summaryrefslogtreecommitdiff
path: root/tools
diff options
context:
space:
mode:
Diffstat (limited to 'tools')
-rw-r--r--tools/prettier-plugin-astro/index.js56
-rw-r--r--tools/prettier-plugin-astro/test/astro-prettier.test.js4
-rw-r--r--tools/prettier-plugin-astro/test/package.json2
-rw-r--r--tools/vscode/languages/astro-language-configuration.json68
-rw-r--r--tools/vscode/package.json188
-rw-r--r--tools/vscode/packages/client/package.json20
-rw-r--r--tools/vscode/packages/client/src/html/autoClose.ts153
-rw-r--r--tools/vscode/packages/client/tsconfig.json18
-rw-r--r--tools/vscode/packages/server/src/core/config/ConfigManager.ts16
-rw-r--r--tools/vscode/packages/server/src/core/documents/Document.ts272
-rw-r--r--tools/vscode/packages/server/src/core/documents/DocumentManager.ts150
-rw-r--r--tools/vscode/packages/server/src/core/documents/parseAstro.ts99
-rw-r--r--tools/vscode/packages/server/src/core/documents/parseHtml.ts230
-rw-r--r--tools/vscode/packages/server/src/core/documents/utils.ts156
-rw-r--r--tools/vscode/packages/server/src/index.ts10
-rw-r--r--tools/vscode/packages/server/src/plugins/PluginHost.ts214
-rw-r--r--tools/vscode/packages/server/src/plugins/astro/AstroPlugin.ts2
-rw-r--r--tools/vscode/packages/server/src/plugins/html/HTMLPlugin.ts13
-rw-r--r--tools/vscode/packages/server/src/plugins/interfaces.ts210
-rw-r--r--tools/vscode/packages/server/src/plugins/typescript/LanguageServiceManager.ts2
-rw-r--r--tools/vscode/packages/server/src/plugins/typescript/SnapshotManager.ts544
-rw-r--r--tools/vscode/packages/server/src/plugins/typescript/TypeScriptPlugin.ts110
-rw-r--r--tools/vscode/packages/server/src/plugins/typescript/astro-sys.ts62
-rw-r--r--tools/vscode/packages/server/src/plugins/typescript/features/CompletionsProvider.ts2
-rw-r--r--tools/vscode/packages/server/src/plugins/typescript/languageService.ts61
-rw-r--r--tools/vscode/packages/server/src/plugins/typescript/utils.ts270
-rw-r--r--tools/vscode/packages/server/src/utils.ts83
-rw-r--r--tools/vscode/packages/server/tsconfig.json14
-rw-r--r--tools/vscode/syntaxes/astro.tmLanguage.json1406
-rw-r--r--tools/vscode/tsconfig.base.json31
-rw-r--r--tools/vscode/tsconfig.json23
31 files changed, 2113 insertions, 2376 deletions
diff --git a/tools/prettier-plugin-astro/index.js b/tools/prettier-plugin-astro/index.js
index 54163459c..8bfdcd3e8 100644
--- a/tools/prettier-plugin-astro/index.js
+++ b/tools/prettier-plugin-astro/index.js
@@ -42,7 +42,7 @@ module.exports.parsers = {
return node.end;
},
astFormat: 'astro-expression',
- }
+ },
};
const findExpressionsInAST = (node, collect = []) => {
@@ -50,24 +50,24 @@ const findExpressionsInAST = (node, collect = []) => {
return collect.concat(node);
}
if (node.children) {
- collect.push(...[].concat(...node.children.map(child => findExpressionsInAST(child))));
+ collect.push(...[].concat(...node.children.map((child) => findExpressionsInAST(child))));
}
return collect;
-}
+};
-const formatExpression = ({ expression: { codeChunks, children }}, text, options) => {
+const formatExpression = ({ expression: { codeChunks, children } }, text, options) => {
if (children.length === 0) {
const codeStart = codeChunks[0]; // If no children, there should only exist a single chunk.
if (codeStart && [`'`, `"`].includes(codeStart[0])) {
- return `<script $ lang="ts">${codeChunks.join('')}</script>`
+ return `<script $ lang="ts">${codeChunks.join('')}</script>`;
}
return `{${codeChunks.join('')}}`;
}
return `<script $ lang="ts">${text}</script>`;
-}
+};
-const isAstroScript = (node) => node.type === 'concat' && node.parts[0] === '<script' && node.parts[1].type === 'indent' && node.parts[1].contents.parts.find(v => v === '$');
+const isAstroScript = (node) => node.type === 'concat' && node.parts[0] === '<script' && node.parts[1].type === 'indent' && node.parts[1].contents.parts.find((v) => v === '$');
const walkDoc = (doc) => {
let inAstroScript = false;
@@ -77,38 +77,38 @@ const walkDoc = (doc) => {
inAstroScript = true;
parent.contents = { type: 'concat', parts: ['{'] };
}
- return node.parts.map(part => recurse(part, { parent: node }));
+ return node.parts.map((part) => recurse(part, { parent: node }));
}
if (inAstroScript) {
if (node.type === 'break-parent') {
- parent.parts = parent.parts.filter(part => !['break-parent', 'line'].includes(part.type));
+ parent.parts = parent.parts.filter((part) => !['break-parent', 'line'].includes(part.type));
}
if (node.type === 'indent') {
- parent.parts = parent.parts.map(part => {
+ parent.parts = parent.parts.map((part) => {
if (part.type !== 'indent') return part;
return {
type: 'concat',
- parts: [part.contents]
- }
- })
+ parts: [part.contents],
+ };
+ });
}
if (typeof node === 'string' && node.endsWith(';')) {
- parent.parts = parent.parts.map(part => {
+ parent.parts = parent.parts.map((part) => {
if (typeof part === 'string' && part.endsWith(';')) return part.slice(0, -1);
return part;
});
}
if (node === '</script>') {
- parent.parts = parent.parts.map(part => part === '</script>' ? '}' : part);
+ parent.parts = parent.parts.map((part) => (part === '</script>' ? '}' : part));
inAstroScript = false;
}
}
if (['group', 'indent'].includes(node.type)) {
return recurse(node.contents, { parent: node });
}
- }
+ };
recurse(doc, { parent: null });
-}
+};
/** @type {Record<string, import('prettier').Printer>} */
module.exports.printers = {
@@ -129,18 +129,20 @@ module.exports.printers = {
if (node.type === 'Fragment' && node.isRoot) {
const expressions = findExpressionsInAST(node);
if (expressions.length > 0) {
- const parts = [].concat(...expressions.map((expr, i, all) => {
- const prev = all[i - 1];
- const start = node.text.slice((prev?.end ?? node.start) - node.start, expr.start - node.start);
- const exprText = formatExpression(expr, node.text.slice(expr.start - node.start + 1, expr.end - node.start - 1), options);
+ const parts = [].concat(
+ ...expressions.map((expr, i, all) => {
+ const prev = all[i - 1];
+ const start = node.text.slice((prev?.end ?? node.start) - node.start, expr.start - node.start);
+ const exprText = formatExpression(expr, node.text.slice(expr.start - node.start + 1, expr.end - node.start - 1), options);
- if (i === all.length - 1) {
- const end = node.text.slice(expr.end - node.start);
- return [start, exprText, end]
- }
+ if (i === all.length - 1) {
+ const end = node.text.slice(expr.end - node.start);
+ return [start, exprText, end];
+ }
- return [start, exprText]
- }));
+ return [start, exprText];
+ })
+ );
const html = parts.join('\n');
const doc = textToDoc(html, { parser: 'html' });
walkDoc(doc);
diff --git a/tools/prettier-plugin-astro/test/astro-prettier.test.js b/tools/prettier-plugin-astro/test/astro-prettier.test.js
index 99ece2a30..cf0f0c87c 100644
--- a/tools/prettier-plugin-astro/test/astro-prettier.test.js
+++ b/tools/prettier-plugin-astro/test/astro-prettier.test.js
@@ -2,10 +2,10 @@ import { suite } from 'uvu';
import * as assert from 'uvu/assert';
import { format } from './test-utils.js';
import { promises as fs } from 'fs';
-import { fileURLToPath } from 'url'
+import { fileURLToPath } from 'url';
const Prettier = suite('Prettier formatting');
-const readFile = (path) => fs.readFile(fileURLToPath(new URL(`./fixtures${path}`, import.meta.url))).then(res => res.toString())
+const readFile = (path) => fs.readFile(fileURLToPath(new URL(`./fixtures${path}`, import.meta.url))).then((res) => res.toString());
/**
* Utility to get `[src, out]` files
diff --git a/tools/prettier-plugin-astro/test/package.json b/tools/prettier-plugin-astro/test/package.json
index 472002573..3dbc1ca59 100644
--- a/tools/prettier-plugin-astro/test/package.json
+++ b/tools/prettier-plugin-astro/test/package.json
@@ -1,3 +1,3 @@
{
- "type": "module"
+ "type": "module"
}
diff --git a/tools/vscode/languages/astro-language-configuration.json b/tools/vscode/languages/astro-language-configuration.json
index a6e9cae46..fef8c8c7b 100644
--- a/tools/vscode/languages/astro-language-configuration.json
+++ b/tools/vscode/languages/astro-language-configuration.json
@@ -1,36 +1,36 @@
{
- "comments": {
- "blockComment": [ "<!--", "-->" ]
- },
- "brackets": [
- ["---", "---"],
- ["<!--", "-->"],
- ["<", ">"],
- ["{", "}"],
- ["(", ")"]
- ],
- "autoClosingPairs": [
- { "open": "{", "close": "}"},
- { "open": "[", "close": "]"},
- { "open": "(", "close": ")" },
- { "open": "'", "close": "'" },
- { "open": "\"", "close": "\"" },
- { "open": "<!--", "close": "-->", "notIn": [ "comment", "string" ]},
- { "open": "/**", "close": " */", "notIn": ["string"] }
- ],
- "autoCloseBefore": ";:.,=}])>` \n\t",
- "surroundingPairs": [
- { "open": "'", "close": "'" },
- { "open": "\"", "close": "\"" },
- { "open": "{", "close": "}"},
- { "open": "[", "close": "]"},
- { "open": "(", "close": ")" },
- { "open": "<", "close": ">" }
- ],
- "folding": {
- "markers": {
- "start": "^\\s*<!--\\s*#region\\b.*-->",
- "end": "^\\s*<!--\\s*#endregion\\b.*-->"
- }
- }
+ "comments": {
+ "blockComment": ["<!--", "-->"]
+ },
+ "brackets": [
+ ["---", "---"],
+ ["<!--", "-->"],
+ ["<", ">"],
+ ["{", "}"],
+ ["(", ")"]
+ ],
+ "autoClosingPairs": [
+ { "open": "{", "close": "}" },
+ { "open": "[", "close": "]" },
+ { "open": "(", "close": ")" },
+ { "open": "'", "close": "'" },
+ { "open": "\"", "close": "\"" },
+ { "open": "<!--", "close": "-->", "notIn": ["comment", "string"] },
+ { "open": "/**", "close": " */", "notIn": ["string"] }
+ ],
+ "autoCloseBefore": ";:.,=}])>` \n\t",
+ "surroundingPairs": [
+ { "open": "'", "close": "'" },
+ { "open": "\"", "close": "\"" },
+ { "open": "{", "close": "}" },
+ { "open": "[", "close": "]" },
+ { "open": "(", "close": ")" },
+ { "open": "<", "close": ">" }
+ ],
+ "folding": {
+ "markers": {
+ "start": "^\\s*<!--\\s*#region\\b.*-->",
+ "end": "^\\s*<!--\\s*#endregion\\b.*-->"
+ }
+ }
}
diff --git a/tools/vscode/package.json b/tools/vscode/package.json
index 75e1eb507..f663d8bcb 100644
--- a/tools/vscode/package.json
+++ b/tools/vscode/package.json
@@ -1,96 +1,96 @@
{
- "name": "@astro.build/vscode",
- "displayName": "Astro",
- "description": "Language support for Astro",
- "icon": "assets/icon.png",
- "galleryBanner": {
- "color": "#FF5D01",
- "theme": "dark"
- },
- "version": "0.3.0",
- "author": "Astro",
- "publisher": "astro-build",
- "license": "MIT",
- "scripts": {
- "vscode:prepublish": "npm run build",
- "bootstrap": "cd packages/client && yarn && cd ../server && yarn",
- "build": "node scripts/build.mjs",
- "watch": "node scripts/watch.mjs"
- },
- "engines": {
- "vscode": "^1.52.0"
- },
- "activationEvents": [
- "onLanguage:astro"
- ],
- "dependencies": {
- "vscode-html-languageservice": "^3.0.3",
- "vscode-emmet-helper": "2.1.2"
- },
- "devDependencies": {
- "esbuild": "0.10.0",
- "@astro-vscode/client": "file:./packages/client",
- "@astro-vscode/server": "file:./packages/server"
- },
- "main": "./dist/index.js",
- "files": [
- "dist/",
- "languages/",
- "syntaxes/"
- ],
- "repository": {
- "type": "git",
- "directory": "vscode",
- "url": "https://github.com/snowpackjs/astro"
- },
- "contributes": {
- "configuration": {
- "type": "object",
- "title": "Astro configuration",
- "properties": {
- "astro.trace.server": {
- "scope": "window",
- "type": "string",
- "enum": [
- "off",
- "messages",
- "verbose"
- ],
- "default": "off",
- "description": "Traces the communication between VS Code and the language server."
- }
- }
- },
- "languages": [
- {
- "id": "astro",
- "extensions": [
- ".astro"
- ],
- "aliases": [
- "Astro"
- ],
- "configuration": "./languages/astro-language-configuration.json"
- }
- ],
- "grammars": [
- {
- "language": "astro",
- "scopeName": "text.html.astro",
- "path": "./syntaxes/astro.tmLanguage.json",
- "injectTo": [
- "text.html.markdown"
- ],
- "embeddedLanguages": {
- "text.html.astro": "astro",
- "text.html": "html",
- "source.css": "css",
- "source.scss": "scss",
- "source.sass": "sass",
- "source.tsx": "typescriptreact",
- "meta.embedded.block.frontmatter": "typescriptreact"
- }
- }
- ]
- }
+ "name": "@astro.build/vscode",
+ "displayName": "Astro",
+ "description": "Language support for Astro",
+ "icon": "assets/icon.png",
+ "galleryBanner": {
+ "color": "#FF5D01",
+ "theme": "dark"
+ },
+ "version": "0.3.0",
+ "author": "Astro",
+ "publisher": "astro-build",
+ "license": "MIT",
+ "scripts": {
+ "vscode:prepublish": "npm run build",
+ "bootstrap": "cd packages/client && yarn && cd ../server && yarn",
+ "build": "node scripts/build.mjs",
+ "watch": "node scripts/watch.mjs"
+ },
+ "engines": {
+ "vscode": "^1.52.0"
+ },
+ "activationEvents": [
+ "onLanguage:astro"
+ ],
+ "dependencies": {
+ "vscode-html-languageservice": "^3.0.3",
+ "vscode-emmet-helper": "2.1.2"
+ },
+ "devDependencies": {
+ "esbuild": "0.10.0",
+ "@astro-vscode/client": "file:./packages/client",
+ "@astro-vscode/server": "file:./packages/server"
+ },
+ "main": "./dist/index.js",
+ "files": [
+ "dist/",
+ "languages/",
+ "syntaxes/"
+ ],
+ "repository": {
+ "type": "git",
+ "directory": "vscode",
+ "url": "https://github.com/snowpackjs/astro"
+ },
+ "contributes": {
+ "configuration": {
+ "type": "object",
+ "title": "Astro configuration",
+ "properties": {
+ "astro.trace.server": {
+ "scope": "window",
+ "type": "string",
+ "enum": [
+ "off",
+ "messages",
+ "verbose"
+ ],
+ "default": "off",
+ "description": "Traces the communication between VS Code and the language server."
+ }
+ }
+ },
+ "languages": [
+ {
+ "id": "astro",
+ "extensions": [
+ ".astro"
+ ],
+ "aliases": [
+ "Astro"
+ ],
+ "configuration": "./languages/astro-language-configuration.json"
+ }
+ ],
+ "grammars": [
+ {
+ "language": "astro",
+ "scopeName": "text.html.astro",
+ "path": "./syntaxes/astro.tmLanguage.json",
+ "injectTo": [
+ "text.html.markdown"
+ ],
+ "embeddedLanguages": {
+ "text.html.astro": "astro",
+ "text.html": "html",
+ "source.css": "css",
+ "source.scss": "scss",
+ "source.sass": "sass",
+ "source.tsx": "typescriptreact",
+ "meta.embedded.block.frontmatter": "typescriptreact"
+ }
+ }
+ ]
+ }
}
diff --git a/tools/vscode/packages/client/package.json b/tools/vscode/packages/client/package.json
index 3a693790d..991f538dc 100644
--- a/tools/vscode/packages/client/package.json
+++ b/tools/vscode/packages/client/package.json
@@ -1,12 +1,12 @@
{
- "name": "@astro-vscode/client",
- "version": "0.1.0",
- "author": "Skypack",
- "license": "MIT",
- "dependencies": {
- "vscode-languageclient": "next"
- },
- "devDependencies": {
- "@types/vscode": "latest"
- }
+ "name": "@astro-vscode/client",
+ "version": "0.1.0",
+ "author": "Skypack",
+ "license": "MIT",
+ "dependencies": {
+ "vscode-languageclient": "next"
+ },
+ "devDependencies": {
+ "@types/vscode": "latest"
+ }
}
diff --git a/tools/vscode/packages/client/src/html/autoClose.ts b/tools/vscode/packages/client/src/html/autoClose.ts
index 0dbce66c8..a3b90f615 100644
--- a/tools/vscode/packages/client/src/html/autoClose.ts
+++ b/tools/vscode/packages/client/src/html/autoClose.ts
@@ -12,97 +12,78 @@ import { TextDocumentContentChangeEvent } from 'vscode-languageserver-protocol';
/** */
export function activateTagClosing(
- tagProvider: (document: TextDocument, position: Position) => Thenable<string>,
- supportedLanguages: { [id: string]: boolean },
- configName: string
+ tagProvider: (document: TextDocument, position: Position) => Thenable<string>,
+ supportedLanguages: { [id: string]: boolean },
+ configName: string
): Disposable {
- const disposables: Disposable[] = [];
- workspace.onDidChangeTextDocument(
- (event) => onDidChangeTextDocument(event.document, event.contentChanges),
- null,
- disposables
- );
+ const disposables: Disposable[] = [];
+ workspace.onDidChangeTextDocument((event) => onDidChangeTextDocument(event.document, event.contentChanges), null, disposables);
- let isEnabled = false;
- updateEnabledState();
- window.onDidChangeActiveTextEditor(updateEnabledState, null, disposables);
+ let isEnabled = false;
+ updateEnabledState();
+ window.onDidChangeActiveTextEditor(updateEnabledState, null, disposables);
- let timeout: NodeJS.Timer | undefined = void 0;
+ let timeout: NodeJS.Timer | undefined = void 0;
- /** Check if this feature is enabled */
- function updateEnabledState() {
- isEnabled = false;
- const editor = window.activeTextEditor;
- if (!editor) {
- return;
- }
- const document = editor.document;
- if (!supportedLanguages[document.languageId]) {
- return;
- }
- if (!workspace.getConfiguration(void 0, document.uri).get<boolean>(configName)) {
- return;
- }
- isEnabled = true;
+ /** Check if this feature is enabled */
+ function updateEnabledState() {
+ isEnabled = false;
+ const editor = window.activeTextEditor;
+ if (!editor) {
+ return;
+ }
+ const document = editor.document;
+ if (!supportedLanguages[document.languageId]) {
+ return;
}
+ if (!workspace.getConfiguration(void 0, document.uri).get<boolean>(configName)) {
+ return;
+ }
+ isEnabled = true;
+ }
- /** Handle text document changes */
- function onDidChangeTextDocument(
- document: TextDocument,
- changes: readonly TextDocumentContentChangeEvent[]
- ) {
- if (!isEnabled) {
- return;
- }
- const activeDocument = window.activeTextEditor && window.activeTextEditor.document;
- if (document !== activeDocument || changes.length === 0) {
- return;
- }
- if (typeof timeout !== 'undefined') {
- clearTimeout(timeout);
- }
- const lastChange = changes[changes.length - 1];
- const lastCharacter = lastChange.text[lastChange.text.length - 1];
- if (
- ('range' in lastChange && (lastChange.rangeLength ?? 0) > 0) ||
- (lastCharacter !== '>' && lastCharacter !== '/')
- ) {
- return;
- }
- const rangeStart =
- 'range' in lastChange
- ? lastChange.range.start
- : new Position(0, document.getText().length);
- const version = document.version;
- timeout = setTimeout(() => {
- const position = new Position(
- rangeStart.line,
- rangeStart.character + lastChange.text.length
- );
- tagProvider(document, position).then((text) => {
- if (text && isEnabled) {
- const activeEditor = window.activeTextEditor;
- if (activeEditor) {
- const activeDocument = activeEditor.document;
- if (document === activeDocument && activeDocument.version === version) {
- const selections = activeEditor.selections;
- if (
- selections.length &&
- selections.some((s) => s.active.isEqual(position))
- ) {
- activeEditor.insertSnippet(
- new SnippetString(text),
- selections.map((s) => s.active)
- );
- } else {
- activeEditor.insertSnippet(new SnippetString(text), position);
- }
- }
- }
- }
- });
- timeout = void 0;
- }, 100);
+ /** Handle text document changes */
+ function onDidChangeTextDocument(document: TextDocument, changes: readonly TextDocumentContentChangeEvent[]) {
+ if (!isEnabled) {
+ return;
+ }
+ const activeDocument = window.activeTextEditor && window.activeTextEditor.document;
+ if (document !== activeDocument || changes.length === 0) {
+ return;
}
- return Disposable.from(...disposables);
+ if (typeof timeout !== 'undefined') {
+ clearTimeout(timeout);
+ }
+ const lastChange = changes[changes.length - 1];
+ const lastCharacter = lastChange.text[lastChange.text.length - 1];
+ if (('range' in lastChange && (lastChange.rangeLength ?? 0) > 0) || (lastCharacter !== '>' && lastCharacter !== '/')) {
+ return;
+ }
+ const rangeStart = 'range' in lastChange ? lastChange.range.start : new Position(0, document.getText().length);
+ const version = document.version;
+ timeout = setTimeout(() => {
+ const position = new Position(rangeStart.line, rangeStart.character + lastChange.text.length);
+ tagProvider(document, position).then((text) => {
+ if (text && isEnabled) {
+ const activeEditor = window.activeTextEditor;
+ if (activeEditor) {
+ const activeDocument = activeEditor.document;
+ if (document === activeDocument && activeDocument.version === version) {
+ const selections = activeEditor.selections;
+ if (selections.length && selections.some((s) => s.active.isEqual(position))) {
+ activeEditor.insertSnippet(
+ new SnippetString(text),
+ selections.map((s) => s.active)
+ );
+ } else {
+ activeEditor.insertSnippet(new SnippetString(text), position);
+ }
+ }
+ }
+ }
+ });
+ timeout = void 0;
+ }, 100);
+ }
+ return Disposable.from(...disposables);
}
diff --git a/tools/vscode/packages/client/tsconfig.json b/tools/vscode/packages/client/tsconfig.json
index 51e95f536..417801227 100644
--- a/tools/vscode/packages/client/tsconfig.json
+++ b/tools/vscode/packages/client/tsconfig.json
@@ -1,12 +1,10 @@
{
- "extends": "../../tsconfig.base.json",
- "compilerOptions": {
- "outDir": "dist",
- "rootDir": "src",
- },
- "include": ["src"],
- "exclude": ["node_modules"],
- "references": [
- { "path": "../server" }
- ]
+ "extends": "../../tsconfig.base.json",
+ "compilerOptions": {
+ "outDir": "dist",
+ "rootDir": "src"
+ },
+ "include": ["src"],
+ "exclude": ["node_modules"],
+ "references": [{ "path": "../server" }]
}
diff --git a/tools/vscode/packages/server/src/core/config/ConfigManager.ts b/tools/vscode/packages/server/src/core/config/ConfigManager.ts
index 4c1c23b13..1e795ab96 100644
--- a/tools/vscode/packages/server/src/core/config/ConfigManager.ts
+++ b/tools/vscode/packages/server/src/core/config/ConfigManager.ts
@@ -1,13 +1,13 @@
import { VSCodeEmmetConfig } from 'vscode-emmet-helper';
export class ConfigManager {
- private emmetConfig: VSCodeEmmetConfig = {};
-
- updateEmmetConfig(config: VSCodeEmmetConfig): void {
- this.emmetConfig = config || {};
- }
+ private emmetConfig: VSCodeEmmetConfig = {};
- getEmmetConfig(): VSCodeEmmetConfig {
- return this.emmetConfig;
- }
+ updateEmmetConfig(config: VSCodeEmmetConfig): void {
+ this.emmetConfig = config || {};
+ }
+
+ getEmmetConfig(): VSCodeEmmetConfig {
+ return this.emmetConfig;
+ }
}
diff --git a/tools/vscode/packages/server/src/core/documents/Document.ts b/tools/vscode/packages/server/src/core/documents/Document.ts
index 4f90813ee..93217e891 100644
--- a/tools/vscode/packages/server/src/core/documents/Document.ts
+++ b/tools/vscode/packages/server/src/core/documents/Document.ts
@@ -7,153 +7,147 @@ import { parseHtml } from './parseHtml';
import { parseAstro, AstroDocument } from './parseAstro';
export class Document implements TextDocument {
-
- private content: string;
-
- languageId = 'astro';
- version = 0;
- html!: HTMLDocument;
- astro!: AstroDocument;
-
- constructor(public uri: string, text: string) {
- this.content = text;
- this.updateDocInfo();
+ private content: string;
+
+ languageId = 'astro';
+ version = 0;
+ html!: HTMLDocument;
+ astro!: AstroDocument;
+
+ constructor(public uri: string, text: string) {
+ this.content = text;
+ this.updateDocInfo();
+ }
+
+ private updateDocInfo() {
+ this.html = parseHtml(this.content);
+ this.astro = parseAstro(this.content);
+ }
+
+ setText(text: string) {
+ this.content = text;
+ this.version++;
+ this.updateDocInfo();
+ }
+
+ /**
+ * Update the text between two positions.
+ * @param text The new text slice
+ * @param start Start offset of the new text
+ * @param end End offset of the new text
+ */
+ update(text: string, start: number, end: number): void {
+ const content = this.getText();
+ this.setText(content.slice(0, start) + text + content.slice(end));
+ }
+
+ getText(): string {
+ return this.content;
+ }
+
+ /**
+ * Get the line and character based on the offset
+ * @param offset The index of the position
+ */
+ positionAt(offset: number): Position {
+ offset = clamp(offset, 0, this.getTextLength());
+
+ const lineOffsets = this.getLineOffsets();
+ let low = 0;
+ let high = lineOffsets.length;
+ if (high === 0) {
+ return Position.create(0, offset);
}
- private updateDocInfo() {
- this.html = parseHtml(this.content);
- this.astro = parseAstro(this.content);
+ while (low < high) {
+ const mid = Math.floor((low + high) / 2);
+ if (lineOffsets[mid] > offset) {
+ high = mid;
+ } else {
+ low = mid + 1;
+ }
}
- setText(text: string) {
- this.content = text;
- this.version++;
- this.updateDocInfo();
+ // low is the least x for which the line offset is larger than the current offset
+ // or array.length if no line offset is larger than the current offset
+ const line = low - 1;
+ return Position.create(line, offset - lineOffsets[line]);
+ }
+
+ /**
+ * Get the index of the line and character position
+ * @param position Line and character position
+ */
+ offsetAt(position: Position): number {
+ const lineOffsets = this.getLineOffsets();
+
+ if (position.line >= lineOffsets.length) {
+ return this.getTextLength();
+ } else if (position.line < 0) {
+ return 0;
}
- /**
- * Update the text between two positions.
- * @param text The new text slice
- * @param start Start offset of the new text
- * @param end End offset of the new text
- */
- update(text: string, start: number, end: number): void {
- const content = this.getText();
- this.setText(content.slice(0, start) + text + content.slice(end));
+ const lineOffset = lineOffsets[position.line];
+ const nextLineOffset = position.line + 1 < lineOffsets.length ? lineOffsets[position.line + 1] : this.getTextLength();
+
+ return clamp(nextLineOffset, lineOffset, lineOffset + position.character);
+ }
+
+ getLineUntilOffset(offset: number): string {
+ const { line, character } = this.positionAt(offset);
+ return this.lines[line].slice(0, character);
+ }
+
+ private getLineOffsets() {
+ const lineOffsets = [];
+ const text = this.getText();
+ let isLineStart = true;
+
+ for (let i = 0; i < text.length; i++) {
+ if (isLineStart) {
+ lineOffsets.push(i);
+ isLineStart = false;
+ }
+ const ch = text.charAt(i);
+ isLineStart = ch === '\r' || ch === '\n';
+ if (ch === '\r' && i + 1 < text.length && text.charAt(i + 1) === '\n') {
+ i++;
+ }
}
- getText(): string {
- return this.content
- }
-
- /**
- * Get the line and character based on the offset
- * @param offset The index of the position
- */
- positionAt(offset: number): Position {
- offset = clamp(offset, 0, this.getTextLength());
-
- const lineOffsets = this.getLineOffsets();
- let low = 0;
- let high = lineOffsets.length;
- if (high === 0) {
- return Position.create(0, offset);
- }
-
- while (low < high) {
- const mid = Math.floor((low + high) / 2);
- if (lineOffsets[mid] > offset) {
- high = mid;
- } else {
- low = mid + 1;
- }
- }
-
- // low is the least x for which the line offset is larger than the current offset
- // or array.length if no line offset is larger than the current offset
- const line = low - 1;
- return Position.create(line, offset - lineOffsets[line]);
- }
-
- /**
- * Get the index of the line and character position
- * @param position Line and character position
- */
- offsetAt(position: Position): number {
- const lineOffsets = this.getLineOffsets();
-
- if (position.line >= lineOffsets.length) {
- return this.getTextLength();
- } else if (position.line < 0) {
- return 0;
- }
-
- const lineOffset = lineOffsets[position.line];
- const nextLineOffset =
- position.line + 1 < lineOffsets.length
- ? lineOffsets[position.line + 1]
- : this.getTextLength();
-
- return clamp(nextLineOffset, lineOffset, lineOffset + position.character);
- }
-
- getLineUntilOffset(offset: number): string {
- const { line, character } = this.positionAt(offset);
- return this.lines[line].slice(0, character);
- }
-
- private getLineOffsets() {
- const lineOffsets = [];
- const text = this.getText();
- let isLineStart = true;
-
- for (let i = 0; i < text.length; i++) {
- if (isLineStart) {
- lineOffsets.push(i);
- isLineStart = false;
- }
- const ch = text.charAt(i);
- isLineStart = ch === '\r' || ch === '\n';
- if (ch === '\r' && i + 1 < text.length && text.charAt(i + 1) === '\n') {
- i++;
- }
- }
-
- if (isLineStart && text.length > 0) {
- lineOffsets.push(text.length);
- }
-
- return lineOffsets;
- }
-
- /**
- * Get the length of the document's content
- */
- getTextLength(): number {
- return this.getText().length;
- }
-
- /**
- * Returns the file path if the url scheme is file
- */
- getFilePath(): string | null {
- return urlToPath(this.uri);
- }
-
- /**
- * Get URL file path.
- */
- getURL() {
- return this.uri;
- }
-
-
- get lines(): string[] {
- return this.getText().split(/\r?\n/);
- }
-
- get lineCount(): number {
- return this.lines.length;
+ if (isLineStart && text.length > 0) {
+ lineOffsets.push(text.length);
}
+ return lineOffsets;
+ }
+
+ /**
+ * Get the length of the document's content
+ */
+ getTextLength(): number {
+ return this.getText().length;
+ }
+
+ /**
+ * Returns the file path if the url scheme is file
+ */
+ getFilePath(): string | null {
+ return urlToPath(this.uri);
+ }
+
+ /**
+ * Get URL file path.
+ */
+ getURL() {
+ return this.uri;
+ }
+
+ get lines(): string[] {
+ return this.getText().split(/\r?\n/);
+ }
+
+ get lineCount(): number {
+ return this.lines.length;
+ }
}
diff --git a/tools/vscode/packages/server/src/core/documents/DocumentManager.ts b/tools/vscode/packages/server/src/core/documents/DocumentManager.ts
index 6195514d8..7c9c168c1 100644
--- a/tools/vscode/packages/server/src/core/documents/DocumentManager.ts
+++ b/tools/vscode/packages/server/src/core/documents/DocumentManager.ts
@@ -1,104 +1,94 @@
import { EventEmitter } from 'events';
-import {
- TextDocumentContentChangeEvent,
- TextDocumentItem
-} from 'vscode-languageserver';
+import { TextDocumentContentChangeEvent, TextDocumentItem } from 'vscode-languageserver';
import { Document } from './Document';
import { normalizeUri } from '../../utils';
export type DocumentEvent = 'documentOpen' | 'documentChange' | 'documentClose';
export class DocumentManager {
- private emitter = new EventEmitter();
- private openedInClient = new Set<string>();
- private documents: Map<string, Document> = new Map();
- private locked = new Set<string>();
- private deleteCandidates = new Set<string>();
-
- constructor(
- private createDocument: (textDocument: { uri: string, text: string }) => Document
- ) {}
-
- get(uri: string) {
- return this.documents.get(normalizeUri(uri));
+ private emitter = new EventEmitter();
+ private openedInClient = new Set<string>();
+ private documents: Map<string, Document> = new Map();
+ private locked = new Set<string>();
+ private deleteCandidates = new Set<string>();
+
+ constructor(private createDocument: (textDocument: { uri: string; text: string }) => Document) {}
+
+ get(uri: string) {
+ return this.documents.get(normalizeUri(uri));
+ }
+
+ openDocument(textDocument: TextDocumentItem) {
+ let document: Document;
+ if (this.documents.has(textDocument.uri)) {
+ document = this.get(textDocument.uri) as Document;
+ document.setText(textDocument.text);
+ } else {
+ document = this.createDocument(textDocument);
+ this.documents.set(normalizeUri(textDocument.uri), document);
+ this.notify('documentOpen', document);
}
- openDocument(textDocument: TextDocumentItem) {
- let document: Document;
- if (this.documents.has(textDocument.uri)) {
- document = this.get(textDocument.uri) as Document;
- document.setText(textDocument.text);
- } else {
- document = this.createDocument(textDocument);
- this.documents.set(normalizeUri(textDocument.uri), document);
- this.notify('documentOpen', document);
- }
+ this.notify('documentChange', document);
- this.notify('documentChange', document);
+ return document;
+ }
- return document;
- }
+ closeDocument(uri: string) {
+ uri = normalizeUri(uri);
- closeDocument(uri: string) {
- uri = normalizeUri(uri);
+ const document = this.documents.get(uri);
+ if (!document) {
+ throw new Error('Cannot call methods on an unopened document');
+ }
- const document = this.documents.get(uri);
- if (!document) {
- throw new Error('Cannot call methods on an unopened document');
- }
+ this.notify('documentClose', document);
- this.notify('documentClose', document);
+ // Some plugin may prevent a document from actually being closed.
+ if (!this.locked.has(uri)) {
+ this.documents.delete(uri);
+ } else {
+ this.deleteCandidates.add(uri);
+ }
- // Some plugin may prevent a document from actually being closed.
- if (!this.locked.has(uri)) {
- this.documents.delete(uri);
- } else {
- this.deleteCandidates.add(uri);
- }
+ this.openedInClient.delete(uri);
+ }
- this.openedInClient.delete(uri);
+ updateDocument(uri: string, changes: TextDocumentContentChangeEvent[]) {
+ const document = this.documents.get(normalizeUri(uri));
+ if (!document) {
+ throw new Error('Cannot call methods on an unopened document');
}
- updateDocument(
- uri: string,
- changes: TextDocumentContentChangeEvent[]
- ) {
- const document = this.documents.get(normalizeUri(uri));
- if (!document) {
- throw new Error('Cannot call methods on an unopened document');
- }
-
- for (const change of changes) {
- let start = 0;
- let end = 0;
- if ('range' in change) {
- start = document.offsetAt(change.range.start);
- end = document.offsetAt(change.range.end);
- } else {
- end = document.getTextLength();
- }
-
- document.update(change.text, start, end);
- }
-
- this.notify('documentChange', document);
+ for (const change of changes) {
+ let start = 0;
+ let end = 0;
+ if ('range' in change) {
+ start = document.offsetAt(change.range.start);
+ end = document.offsetAt(change.range.end);
+ } else {
+ end = document.getTextLength();
+ }
+
+ document.update(change.text, start, end);
}
- markAsOpenedInClient(uri: string) {
- this.openedInClient.add(normalizeUri(uri));
- }
+ this.notify('documentChange', document);
+ }
- getAllOpenedByClient() {
- return Array.from(this.documents.entries()).filter((doc) =>
- this.openedInClient.has(doc[0])
- );
- }
+ markAsOpenedInClient(uri: string) {
+ this.openedInClient.add(normalizeUri(uri));
+ }
- on(name: DocumentEvent, listener: (document: Document) => void) {
- this.emitter.on(name, listener);
- }
+ getAllOpenedByClient() {
+ return Array.from(this.documents.entries()).filter((doc) => this.openedInClient.has(doc[0]));
+ }
- private notify(name: DocumentEvent, document: Document) {
- this.emitter.emit(name, document);
- }
+ on(name: DocumentEvent, listener: (document: Document) => void) {
+ this.emitter.on(name, listener);
+ }
+
+ private notify(name: DocumentEvent, document: Document) {
+ this.emitter.emit(name, document);
+ }
}
diff --git a/tools/vscode/packages/server/src/core/documents/parseAstro.ts b/tools/vscode/packages/server/src/core/documents/parseAstro.ts
index e4f71721a..71c7764d8 100644
--- a/tools/vscode/packages/server/src/core/documents/parseAstro.ts
+++ b/tools/vscode/packages/server/src/core/documents/parseAstro.ts
@@ -1,74 +1,77 @@
import { getFirstNonWhitespaceIndex } from './utils';
interface Frontmatter {
- state: null | 'open' | 'closed';
- startOffset: null | number;
- endOffset: null | number;
+ state: null | 'open' | 'closed';
+ startOffset: null | number;
+ endOffset: null | number;
}
interface Content {
- firstNonWhitespaceOffset: null | number;
+ firstNonWhitespaceOffset: null | number;
}
export interface AstroDocument {
- frontmatter: Frontmatter
- content: Content;
+ frontmatter: Frontmatter;
+ content: Content;
}
/** Parses a document to collect metadata about Astro features */
export function parseAstro(content: string): AstroDocument {
- const frontmatter = getFrontmatter(content)
- return {
- frontmatter,
- content: getContent(content, frontmatter)
- }
+ const frontmatter = getFrontmatter(content);
+ return {
+ frontmatter,
+ content: getContent(content, frontmatter),
+ };
}
/** Get frontmatter metadata */
function getFrontmatter(content: string): Frontmatter {
- /** Quickly check how many `---` blocks are in the document */
- function getFrontmatterState(): Frontmatter['state'] {
- const parts = content.trim().split('---').length;
- switch (parts) {
- case 1: return null;
- case 2: return 'open';
- default: return 'closed';
- }
+ /** Quickly check how many `---` blocks are in the document */
+ function getFrontmatterState(): Frontmatter['state'] {
+ const parts = content.trim().split('---').length;
+ switch (parts) {
+ case 1:
+ return null;
+ case 2:
+ return 'open';
+ default:
+ return 'closed';
}
- const state = getFrontmatterState();
+ }
+ const state = getFrontmatterState();
- /** Construct a range containing the document's frontmatter */
- function getFrontmatterOffsets(): [number|null, number|null] {
- const startOffset = content.indexOf('---');
- if (startOffset === -1) return [null, null];
- const endOffset = content.slice(startOffset + 3).indexOf('---') + 3;
- if (endOffset === -1) return [startOffset, null];
- return [startOffset, endOffset];
- }
- const [startOffset, endOffset] = getFrontmatterOffsets();
+ /** Construct a range containing the document's frontmatter */
+ function getFrontmatterOffsets(): [number | null, number | null] {
+ const startOffset = content.indexOf('---');
+ if (startOffset === -1) return [null, null];
+ const endOffset = content.slice(startOffset + 3).indexOf('---') + 3;
+ if (endOffset === -1) return [startOffset, null];
+ return [startOffset, endOffset];
+ }
+ const [startOffset, endOffset] = getFrontmatterOffsets();
- return {
- state,
- startOffset,
- endOffset
- };
+ return {
+ state,
+ startOffset,
+ endOffset,
+ };
}
/** Get content metadata */
function getContent(content: string, frontmatter: Frontmatter): Content {
- switch (frontmatter.state) {
- case null: {
- const offset = getFirstNonWhitespaceIndex(content);
- return { firstNonWhitespaceOffset: offset === -1 ? null : offset }
- }
- case 'open': {
- return { firstNonWhitespaceOffset: null }
- }
- case 'closed': {
- const { endOffset } = frontmatter;
- const end = (endOffset ?? 0) + 3;
- const offset = getFirstNonWhitespaceIndex(content.slice(end))
- return { firstNonWhitespaceOffset: end + offset }
- }
+ switch (frontmatter.state) {
+ case null: {
+ const offset = getFirstNonWhitespaceIndex(content);
+ return { firstNonWhitespaceOffset: offset === -1 ? null : offset };
+ }
+ case 'open': {
+ return { firstNonWhitespaceOffset: null };
+ }
+ case 'closed': {
+ const { endOffset } = frontmatter;
+ const end = (endOffset ?? 0) + 3;
+ const offset = getFirstNonWhitespaceIndex(content.slice(end));
+ return { firstNonWhitespaceOffset: end + offset };
}
+ }
}
diff --git a/tools/vscode/packages/server/src/core/documents/parseHtml.ts b/tools/vscode/packages/server/src/core/documents/parseHtml.ts
index 86af06008..f5de5f292 100644
--- a/tools/vscode/packages/server/src/core/documents/parseHtml.ts
+++ b/tools/vscode/packages/server/src/core/documents/parseHtml.ts
@@ -1,12 +1,4 @@
-import {
- getLanguageService,
- HTMLDocument,
- TokenType,
- ScannerState,
- Scanner,
- Node,
- Position
-} from 'vscode-html-languageservice';
+import { getLanguageService, HTMLDocument, TokenType, ScannerState, Scanner, Node, Position } from 'vscode-html-languageservice';
import { Document } from './Document';
import { isInsideExpression } from './utils';
@@ -16,154 +8,134 @@ const parser = getLanguageService();
* Parses text as HTML
*/
export function parseHtml(text: string): HTMLDocument {
- const preprocessed = preprocess(text);
+ const preprocessed = preprocess(text);
- // We can safely only set getText because only this is used for parsing
- const parsedDoc = parser.parseHTMLDocument(<any>{ getText: () => preprocessed });
+ // We can safely only set getText because only this is used for parsing
+ const parsedDoc = parser.parseHTMLDocument(<any>{ getText: () => preprocessed });
- return parsedDoc;
+ return parsedDoc;
}
-const createScanner = parser.createScanner as (
- input: string,
- initialOffset?: number,
- initialState?: ScannerState
-) => Scanner;
+const createScanner = parser.createScanner as (input: string, initialOffset?: number, initialState?: ScannerState) => Scanner;
/**
* scan the text and remove any `>` or `<` that cause the tag to end short,
*/
function preprocess(text: string) {
- let scanner = createScanner(text);
- let token = scanner.scan();
- let currentStartTagStart: number | null = null;
+ let scanner = createScanner(text);
+ let token = scanner.scan();
+ let currentStartTagStart: number | null = null;
- while (token !== TokenType.EOS) {
- const offset = scanner.getTokenOffset();
+ while (token !== TokenType.EOS) {
+ const offset = scanner.getTokenOffset();
- if (token === TokenType.StartTagOpen) {
- currentStartTagStart = offset;
- }
-
- if (token === TokenType.StartTagClose) {
- if (shouldBlankStartOrEndTagLike(offset)) {
- blankStartOrEndTagLike(offset);
- } else {
- currentStartTagStart = null;
- }
- }
+ if (token === TokenType.StartTagOpen) {
+ currentStartTagStart = offset;
+ }
- if (token === TokenType.StartTagSelfClose) {
- currentStartTagStart = null;
- }
+ if (token === TokenType.StartTagClose) {
+ if (shouldBlankStartOrEndTagLike(offset)) {
+ blankStartOrEndTagLike(offset);
+ } else {
+ currentStartTagStart = null;
+ }
+ }
- // <Foo checked={a < 1}>
- // https://github.com/microsoft/vscode-html-languageservice/blob/71806ef57be07e1068ee40900ef8b0899c80e68a/src/parser/htmlScanner.ts#L327
- if (
- token === TokenType.Unknown &&
- scanner.getScannerState() === ScannerState.WithinTag &&
- scanner.getTokenText() === '<' &&
- shouldBlankStartOrEndTagLike(offset)
- ) {
- blankStartOrEndTagLike(offset);
- }
+ if (token === TokenType.StartTagSelfClose) {
+ currentStartTagStart = null;
+ }
- token = scanner.scan();
+ // <Foo checked={a < 1}>
+ // https://github.com/microsoft/vscode-html-languageservice/blob/71806ef57be07e1068ee40900ef8b0899c80e68a/src/parser/htmlScanner.ts#L327
+ if (token === TokenType.Unknown && scanner.getScannerState() === ScannerState.WithinTag && scanner.getTokenText() === '<' && shouldBlankStartOrEndTagLike(offset)) {
+ blankStartOrEndTagLike(offset);
}
- return text;
+ token = scanner.scan();
+ }
- function shouldBlankStartOrEndTagLike(offset: number) {
- // not null rather than falsy, otherwise it won't work on first tag(0)
- return (
- currentStartTagStart !== null &&
- isInsideExpression(text, currentStartTagStart, offset)
- );
- }
+ return text;
- function blankStartOrEndTagLike(offset: number) {
- text = text.substring(0, offset) + ' ' + text.substring(offset + 1);
- scanner = createScanner(text, offset, ScannerState.WithinTag);
- }
+ function shouldBlankStartOrEndTagLike(offset: number) {
+ // not null rather than falsy, otherwise it won't work on first tag(0)
+ return currentStartTagStart !== null && isInsideExpression(text, currentStartTagStart, offset);
+ }
+
+ function blankStartOrEndTagLike(offset: number) {
+ text = text.substring(0, offset) + ' ' + text.substring(offset + 1);
+ scanner = createScanner(text, offset, ScannerState.WithinTag);
+ }
}
export interface AttributeContext {
- name: string;
- inValue: boolean;
- valueRange?: [number, number];
+ name: string;
+ inValue: boolean;
+ valueRange?: [number, number];
}
-export function getAttributeContextAtPosition(
- document: Document,
- position: Position
-): AttributeContext | null {
- const offset = document.offsetAt(position);
- const { html } = document;
- const tag = html.findNodeAt(offset);
-
- if (!inStartTag(offset, tag) || !tag.attributes) {
- return null;
- }
+export function getAttributeContextAtPosition(document: Document, position: Position): AttributeContext | null {
+ const offset = document.offsetAt(position);
+ const { html } = document;
+ const tag = html.findNodeAt(offset);
- const text = document.getText();
- const beforeStartTagEnd =
- text.substring(0, tag.start) + preprocess(text.substring(tag.start, tag.startTagEnd));
-
- const scanner = createScanner(beforeStartTagEnd, tag.start);
-
- let token = scanner.scan();
- let currentAttributeName: string | undefined;
- const inTokenRange = () =>
- scanner.getTokenOffset() <= offset && offset <= scanner.getTokenEnd();
- while (token != TokenType.EOS) {
- // adopted from https://github.com/microsoft/vscode-html-languageservice/blob/2f7ae4df298ac2c299a40e9024d118f4a9dc0c68/src/services/htmlCompletion.ts#L402
- if (token === TokenType.AttributeName) {
- currentAttributeName = scanner.getTokenText();
-
- if (inTokenRange()) {
- return {
- name: currentAttributeName,
- inValue: false
- };
- }
- } else if (token === TokenType.DelimiterAssign) {
- if (scanner.getTokenEnd() === offset && currentAttributeName) {
- const nextToken = scanner.scan();
-
- return {
- name: currentAttributeName,
- inValue: true,
- valueRange: [
- offset,
- nextToken === TokenType.AttributeValue ? scanner.getTokenEnd() : offset
- ]
- };
- }
- } else if (token === TokenType.AttributeValue) {
- if (inTokenRange() && currentAttributeName) {
- let start = scanner.getTokenOffset();
- let end = scanner.getTokenEnd();
- const char = text[start];
-
- if (char === '"' || char === "'") {
- start++;
- end--;
- }
-
- return {
- name: currentAttributeName,
- inValue: true,
- valueRange: [start, end]
- };
- }
- currentAttributeName = undefined;
+ if (!inStartTag(offset, tag) || !tag.attributes) {
+ return null;
+ }
+
+ const text = document.getText();
+ const beforeStartTagEnd = text.substring(0, tag.start) + preprocess(text.substring(tag.start, tag.startTagEnd));
+
+ const scanner = createScanner(beforeStartTagEnd, tag.start);
+
+ let token = scanner.scan();
+ let currentAttributeName: string | undefined;
+ const inTokenRange = () => scanner.getTokenOffset() <= offset && offset <= scanner.getTokenEnd();
+ while (token != TokenType.EOS) {
+ // adopted from https://github.com/microsoft/vscode-html-languageservice/blob/2f7ae4df298ac2c299a40e9024d118f4a9dc0c68/src/services/htmlCompletion.ts#L402
+ if (token === TokenType.AttributeName) {
+ currentAttributeName = scanner.getTokenText();
+
+ if (inTokenRange()) {
+ return {
+ name: currentAttributeName,
+ inValue: false,
+ };
+ }
+ } else if (token === TokenType.DelimiterAssign) {
+ if (scanner.getTokenEnd() === offset && currentAttributeName) {
+ const nextToken = scanner.scan();
+
+ return {
+ name: currentAttributeName,
+ inValue: true,
+ valueRange: [offset, nextToken === TokenType.AttributeValue ? scanner.getTokenEnd() : offset],
+ };
+ }
+ } else if (token === TokenType.AttributeValue) {
+ if (inTokenRange() && currentAttributeName) {
+ let start = scanner.getTokenOffset();
+ let end = scanner.getTokenEnd();
+ const char = text[start];
+
+ if (char === '"' || char === "'") {
+ start++;
+ end--;
}
- token = scanner.scan();
+
+ return {
+ name: currentAttributeName,
+ inValue: true,
+ valueRange: [start, end],
+ };
+ }
+ currentAttributeName = undefined;
}
+ token = scanner.scan();
+ }
- return null;
+ return null;
}
function inStartTag(offset: number, node: Node) {
- return offset > node.start && node.startTagEnd != undefined && offset < node.startTagEnd;
+ return offset > node.start && node.startTagEnd != undefined && offset < node.startTagEnd;
}
diff --git a/tools/vscode/packages/server/src/core/documents/utils.ts b/tools/vscode/packages/server/src/core/documents/utils.ts
index 6c69014d5..3d12f35a3 100644
--- a/tools/vscode/packages/server/src/core/documents/utils.ts
+++ b/tools/vscode/packages/server/src/core/documents/utils.ts
@@ -5,63 +5,52 @@ import { clamp } from '../../utils';
* Gets word range at position.
* Delimiter is by default a whitespace, but can be adjusted.
*/
-export function getWordRangeAt(
- str: string,
- pos: number,
- delimiterRegex = { left: /\S+$/, right: /\s/ }
-): { start: number; end: number } {
- let start = str.slice(0, pos).search(delimiterRegex.left);
- if (start < 0) {
- start = pos;
- }
-
- let end = str.slice(pos).search(delimiterRegex.right);
- if (end < 0) {
- end = str.length;
- } else {
- end = end + pos;
- }
-
- return { start, end };
+export function getWordRangeAt(str: string, pos: number, delimiterRegex = { left: /\S+$/, right: /\s/ }): { start: number; end: number } {
+ let start = str.slice(0, pos).search(delimiterRegex.left);
+ if (start < 0) {
+ start = pos;
+ }
+
+ let end = str.slice(pos).search(delimiterRegex.right);
+ if (end < 0) {
+ end = str.length;
+ } else {
+ end = end + pos;
+ }
+
+ return { start, end };
}
/**
* Gets word at position.
* Delimiter is by default a whitespace, but can be adjusted.
*/
-export function getWordAt(
- str: string,
- pos: number,
- delimiterRegex = { left: /\S+$/, right: /\s/ }
-): string {
- const { start, end } = getWordRangeAt(str, pos, delimiterRegex);
- return str.slice(start, end);
+export function getWordAt(str: string, pos: number, delimiterRegex = { left: /\S+$/, right: /\s/ }): string {
+ const { start, end } = getWordRangeAt(str, pos, delimiterRegex);
+ return str.slice(start, end);
}
/**
* Gets index of first-non-whitespace character.
*/
export function getFirstNonWhitespaceIndex(str: string): number {
- return str.length - str.trimStart().length;
+ return str.length - str.trimStart().length;
}
/** checks if a position is currently inside of an expression */
export function isInsideExpression(html: string, tagStart: number, position: number) {
- const charactersInNode = html.substring(tagStart, position);
- return charactersInNode.lastIndexOf('{') > charactersInNode.lastIndexOf('}');
+ const charactersInNode = html.substring(tagStart, position);
+ return charactersInNode.lastIndexOf('{') > charactersInNode.lastIndexOf('}');
}
/**
- * Returns if a given offset is inside of the document frontmatter
+ * Returns if a given offset is inside of the document frontmatter
*/
-export function isInsideFrontmatter(
- text: string,
- offset: number
-): boolean {
- let start = text.slice(0, offset).trim().split('---').length;
- let end = text.slice(offset).trim().split('---').length;
-
- return start > 1 && start < 3 && end >= 1;
+export function isInsideFrontmatter(text: string, offset: number): boolean {
+ let start = text.slice(0, offset).trim().split('---').length;
+ let end = text.slice(offset).trim().split('---').length;
+
+ return start > 1 && start < 3 && end >= 1;
}
/**
@@ -70,28 +59,28 @@ export function isInsideFrontmatter(
* @param text The text for which the position should be retrived
*/
export function positionAt(offset: number, text: string): Position {
- offset = clamp(offset, 0, text.length);
-
- const lineOffsets = getLineOffsets(text);
- let low = 0;
- let high = lineOffsets.length;
- if (high === 0) {
- return Position.create(0, offset);
- }
-
- while (low < high) {
- const mid = Math.floor((low + high) / 2);
- if (lineOffsets[mid] > offset) {
- high = mid;
- } else {
- low = mid + 1;
- }
+ offset = clamp(offset, 0, text.length);
+
+ const lineOffsets = getLineOffsets(text);
+ let low = 0;
+ let high = lineOffsets.length;
+ if (high === 0) {
+ return Position.create(0, offset);
+ }
+
+ while (low < high) {
+ const mid = Math.floor((low + high) / 2);
+ if (lineOffsets[mid] > offset) {
+ high = mid;
+ } else {
+ low = mid + 1;
}
+ }
- // low is the least x for which the line offset is larger than the current offset
- // or array.length if no line offset is larger than the current offset
- const line = low - 1;
- return Position.create(line, offset - lineOffsets[line]);
+ // low is the least x for which the line offset is larger than the current offset
+ // or array.length if no line offset is larger than the current offset
+ const line = low - 1;
+ return Position.create(line, offset - lineOffsets[line]);
}
/**
@@ -100,40 +89,39 @@ export function positionAt(offset: number, text: string): Position {
* @param text The text for which the offset should be retrived
*/
export function offsetAt(position: Position, text: string): number {
- const lineOffsets = getLineOffsets(text);
+ const lineOffsets = getLineOffsets(text);
- if (position.line >= lineOffsets.length) {
- return text.length;
- } else if (position.line < 0) {
- return 0;
- }
+ if (position.line >= lineOffsets.length) {
+ return text.length;
+ } else if (position.line < 0) {
+ return 0;
+ }
- const lineOffset = lineOffsets[position.line];
- const nextLineOffset =
- position.line + 1 < lineOffsets.length ? lineOffsets[position.line + 1] : text.length;
+ const lineOffset = lineOffsets[position.line];
+ const nextLineOffset = position.line + 1 < lineOffsets.length ? lineOffsets[position.line + 1] : text.length;
- return clamp(nextLineOffset, lineOffset, lineOffset + position.character);
+ return clamp(nextLineOffset, lineOffset, lineOffset + position.character);
}
function getLineOffsets(text: string) {
- const lineOffsets = [];
- let isLineStart = true;
-
- for (let i = 0; i < text.length; i++) {
- if (isLineStart) {
- lineOffsets.push(i);
- isLineStart = false;
- }
- const ch = text.charAt(i);
- isLineStart = ch === '\r' || ch === '\n';
- if (ch === '\r' && i + 1 < text.length && text.charAt(i + 1) === '\n') {
- i++;
- }
- }
+ const lineOffsets = [];
+ let isLineStart = true;
- if (isLineStart && text.length > 0) {
- lineOffsets.push(text.length);
+ for (let i = 0; i < text.length; i++) {
+ if (isLineStart) {
+ lineOffsets.push(i);
+ isLineStart = false;
+ }
+ const ch = text.charAt(i);
+ isLineStart = ch === '\r' || ch === '\n';
+ if (ch === '\r' && i + 1 < text.length && text.charAt(i + 1) === '\n') {
+ i++;
}
+ }
+
+ if (isLineStart && text.length > 0) {
+ lineOffsets.push(text.length);
+ }
- return lineOffsets;
+ return lineOffsets;
}
diff --git a/tools/vscode/packages/server/src/index.ts b/tools/vscode/packages/server/src/index.ts
index f72ad550b..528d3cb9d 100644
--- a/tools/vscode/packages/server/src/index.ts
+++ b/tools/vscode/packages/server/src/index.ts
@@ -71,10 +71,12 @@ export function startServer() {
connection.onDidChangeTextDocument((evt) => docManager.updateDocument(evt.textDocument.uri, evt.contentChanges));
connection.onDidChangeWatchedFiles((evt) => {
- const params = evt.changes.map(change => ({
- fileName: urlToPath(change.uri),
- changeType: change.type
- })).filter(change => !!change.fileName)
+ const params = evt.changes
+ .map((change) => ({
+ fileName: urlToPath(change.uri),
+ changeType: change.type,
+ }))
+ .filter((change) => !!change.fileName);
pluginHost.onWatchFileChanges(params);
});
diff --git a/tools/vscode/packages/server/src/plugins/PluginHost.ts b/tools/vscode/packages/server/src/plugins/PluginHost.ts
index 72f098ca1..037dd6e07 100644
--- a/tools/vscode/packages/server/src/plugins/PluginHost.ts
+++ b/tools/vscode/packages/server/src/plugins/PluginHost.ts
@@ -1,11 +1,4 @@
-
-import {
- CompletionContext,
- CompletionItem,
- CompletionList,
- Position,
- TextDocumentIdentifier,
-} from 'vscode-languageserver';
+import { CompletionContext, CompletionItem, CompletionList, Position, TextDocumentIdentifier } from 'vscode-languageserver';
import type { DocumentManager } from '../core/documents';
import type * as d from './interfaces';
import { flatten } from '../utils';
@@ -13,154 +6,107 @@ import { FoldingRange } from 'vscode-languageserver-types';
// eslint-disable-next-line no-shadow
enum ExecuteMode {
- None,
- FirstNonNull,
- Collect
+ None,
+ FirstNonNull,
+ Collect,
}
export class PluginHost {
- private plugins: d.Plugin[] = [];
-
- constructor(private documentsManager: DocumentManager) {}
+ private plugins: d.Plugin[] = [];
- register(plugin: d.Plugin) {
- this.plugins.push(plugin);
- }
+ constructor(private documentsManager: DocumentManager) {}
- async getCompletions(
- textDocument: TextDocumentIdentifier,
- position: Position,
- completionContext?: CompletionContext
- ): Promise<CompletionList> {
- const document = this.getDocument(textDocument.uri);
- if (!document) {
- throw new Error('Cannot call methods on an unopened document');
- }
+ register(plugin: d.Plugin) {
+ this.plugins.push(plugin);
+ }
- const completions = (
- await this.execute<CompletionList>(
- 'getCompletions',
- [document, position, completionContext],
- ExecuteMode.Collect
- )
- ).filter((completion) => completion != null);
-
- let flattenedCompletions = flatten(completions.map((completion) => completion.items));
- const isIncomplete = completions.reduce(
- (incomplete, completion) => incomplete || completion.isIncomplete,
- false as boolean
- );
-
- return CompletionList.create(flattenedCompletions, isIncomplete);
+ async getCompletions(textDocument: TextDocumentIdentifier, position: Position, completionContext?: CompletionContext): Promise<CompletionList> {
+ const document = this.getDocument(textDocument.uri);
+ if (!document) {
+ throw new Error('Cannot call methods on an unopened document');
}
- async resolveCompletion(
- textDocument: TextDocumentIdentifier,
- completionItem: d.AppCompletionItem
- ): Promise<CompletionItem> {
- const document = this.getDocument(textDocument.uri);
+ const completions = (await this.execute<CompletionList>('getCompletions', [document, position, completionContext], ExecuteMode.Collect)).filter(
+ (completion) => completion != null
+ );
- if (!document) {
- throw new Error('Cannot call methods on an unopened document');
- }
-
- const result = await this.execute<CompletionItem>(
- 'resolveCompletion',
- [document, completionItem],
- ExecuteMode.FirstNonNull
- );
+ let flattenedCompletions = flatten(completions.map((completion) => completion.items));
+ const isIncomplete = completions.reduce((incomplete, completion) => incomplete || completion.isIncomplete, false as boolean);
- return result ?? completionItem;
- }
+ return CompletionList.create(flattenedCompletions, isIncomplete);
+ }
- async doTagComplete(
- textDocument: TextDocumentIdentifier,
- position: Position
- ): Promise<string | null> {
- const document = this.getDocument(textDocument.uri);
- if (!document) {
- throw new Error('Cannot call methods on an unopened document');
- }
+ async resolveCompletion(textDocument: TextDocumentIdentifier, completionItem: d.AppCompletionItem): Promise<CompletionItem> {
+ const document = this.getDocument(textDocument.uri);
- return this.execute<string | null>(
- 'doTagComplete',
- [document, position],
- ExecuteMode.FirstNonNull
- );
+ if (!document) {
+ throw new Error('Cannot call methods on an unopened document');
}
- async getFoldingRanges(
- textDocument: TextDocumentIdentifier
- ): Promise<FoldingRange[]|null> {
- const document = this.getDocument(textDocument.uri);
- if (!document) {
- throw new Error('Cannot call methods on an unopened document');
- }
+ const result = await this.execute<CompletionItem>('resolveCompletion', [document, completionItem], ExecuteMode.FirstNonNull);
- const foldingRanges = flatten(await this.execute<FoldingRange[]>(
- 'getFoldingRanges',
- [document],
- ExecuteMode.Collect
- )).filter((completion) => completion != null)
+ return result ?? completionItem;
+ }
- return foldingRanges;
+ async doTagComplete(textDocument: TextDocumentIdentifier, position: Position): Promise<string | null> {
+ const document = this.getDocument(textDocument.uri);
+ if (!document) {
+ throw new Error('Cannot call methods on an unopened document');
}
- onWatchFileChanges(onWatchFileChangesParams: any[]): void {
- for (const support of this.plugins) {
- support.onWatchFileChanges?.(onWatchFileChangesParams);
- }
- }
+ return this.execute<string | null>('doTagComplete', [document, position], ExecuteMode.FirstNonNull);
+ }
- private getDocument(uri: string) {
- return this.documentsManager.get(uri);
+ async getFoldingRanges(textDocument: TextDocumentIdentifier): Promise<FoldingRange[] | null> {
+ const document = this.getDocument(textDocument.uri);
+ if (!document) {
+ throw new Error('Cannot call methods on an unopened document');
}
- private execute<T>(
- name: keyof d.LSProvider,
- args: any[],
- mode: ExecuteMode.FirstNonNull
- ): Promise<T | null>;
- private execute<T>(
- name: keyof d.LSProvider,
- args: any[],
- mode: ExecuteMode.Collect
- ): Promise<T[]>;
- private execute(name: keyof d.LSProvider, args: any[], mode: ExecuteMode.None): Promise<void>;
- private async execute<T>(
- name: keyof d.LSProvider,
- args: any[],
- mode: ExecuteMode
- ): Promise<(T | null) | T[] | void> {
- const plugins = this.plugins.filter((plugin) => typeof plugin[name] === 'function');
-
- switch (mode) {
- case ExecuteMode.FirstNonNull:
- for (const plugin of plugins) {
- const res = await this.tryExecutePlugin(plugin, name, args, null);
- if (res != null) {
- return res;
- }
- }
- return null;
- case ExecuteMode.Collect:
- return Promise.all(
- plugins.map((plugin) => this.tryExecutePlugin(plugin, name, args, []))
- );
- case ExecuteMode.None:
- await Promise.all(
- plugins.map((plugin) => this.tryExecutePlugin(plugin, name, args, null))
- );
- return;
- }
- }
+ const foldingRanges = flatten(await this.execute<FoldingRange[]>('getFoldingRanges', [document], ExecuteMode.Collect)).filter((completion) => completion != null);
+
+ return foldingRanges;
+ }
- private async tryExecutePlugin(plugin: any, fnName: string, args: any[], failValue: any) {
- try {
- return await plugin[fnName](...args);
- } catch (e) {
- console.error(e);
- return failValue;
+ onWatchFileChanges(onWatchFileChangesParams: any[]): void {
+ for (const support of this.plugins) {
+ support.onWatchFileChanges?.(onWatchFileChangesParams);
+ }
+ }
+
+ private getDocument(uri: string) {
+ return this.documentsManager.get(uri);
+ }
+
+ private execute<T>(name: keyof d.LSProvider, args: any[], mode: ExecuteMode.FirstNonNull): Promise<T | null>;
+ private execute<T>(name: keyof d.LSProvider, args: any[], mode: ExecuteMode.Collect): Promise<T[]>;
+ private execute(name: keyof d.LSProvider, args: any[], mode: ExecuteMode.None): Promise<void>;
+ private async execute<T>(name: keyof d.LSProvider, args: any[], mode: ExecuteMode): Promise<(T | null) | T[] | void> {
+ const plugins = this.plugins.filter((plugin) => typeof plugin[name] === 'function');
+
+ switch (mode) {
+ case ExecuteMode.FirstNonNull:
+ for (const plugin of plugins) {
+ const res = await this.tryExecutePlugin(plugin, name, args, null);
+ if (res != null) {
+ return res;
+ }
}
+ return null;
+ case ExecuteMode.Collect:
+ return Promise.all(plugins.map((plugin) => this.tryExecutePlugin(plugin, name, args, [])));
+ case ExecuteMode.None:
+ await Promise.all(plugins.map((plugin) => this.tryExecutePlugin(plugin, name, args, null)));
+ return;
+ }
+ }
+
+ private async tryExecutePlugin(plugin: any, fnName: string, args: any[], failValue: any) {
+ try {
+ return await plugin[fnName](...args);
+ } catch (e) {
+ console.error(e);
+ return failValue;
}
+ }
}
diff --git a/tools/vscode/packages/server/src/plugins/astro/AstroPlugin.ts b/tools/vscode/packages/server/src/plugins/astro/AstroPlugin.ts
index 0696504fc..6baf407a5 100644
--- a/tools/vscode/packages/server/src/plugins/astro/AstroPlugin.ts
+++ b/tools/vscode/packages/server/src/plugins/astro/AstroPlugin.ts
@@ -49,7 +49,7 @@ export class AstroPlugin implements CompletionsProvider, FoldingRangeProvider {
endLine: end.line,
endCharacter: end.character,
kind: FoldingRangeKind.Imports,
- }
+ },
];
}
diff --git a/tools/vscode/packages/server/src/plugins/html/HTMLPlugin.ts b/tools/vscode/packages/server/src/plugins/html/HTMLPlugin.ts
index 5114eda1c..7e0ab4861 100644
--- a/tools/vscode/packages/server/src/plugins/html/HTMLPlugin.ts
+++ b/tools/vscode/packages/server/src/plugins/html/HTMLPlugin.ts
@@ -34,15 +34,7 @@ export class HTMLPlugin implements CompletionsProvider, FoldingRangeProvider {
isIncomplete: true,
items: [],
};
- this.lang.setCompletionParticipants([
- getEmmetCompletionParticipants(
- document,
- position,
- 'html',
- this.configManager.getEmmetConfig(),
- emmetResults
- )
- ]);
+ this.lang.setCompletionParticipants([getEmmetCompletionParticipants(document, position, 'html', this.configManager.getEmmetConfig(), emmetResults)]);
const results = this.lang.doComplete(document, position, html);
const items = this.toCompletionItems(results.items);
@@ -54,14 +46,13 @@ export class HTMLPlugin implements CompletionsProvider, FoldingRangeProvider {
);
}
- getFoldingRanges(document: Document): FoldingRange[]|null {
+ getFoldingRanges(document: Document): FoldingRange[] | null {
const html = this.documents.get(document);
if (!html) {
return null;
}
return this.lang.getFoldingRanges(document);
-
}
doTagComplete(document: Document, position: Position): string | null {
diff --git a/tools/vscode/packages/server/src/plugins/interfaces.ts b/tools/vscode/packages/server/src/plugins/interfaces.ts
index 31aafdc3e..b68100de1 100644
--- a/tools/vscode/packages/server/src/plugins/interfaces.ts
+++ b/tools/vscode/packages/server/src/plugins/interfaces.ts
@@ -1,217 +1,167 @@
+import { CompletionContext, FileChangeType, LinkedEditingRanges, SemanticTokens, SignatureHelpContext, TextDocumentContentChangeEvent } from 'vscode-languageserver';
import {
- CompletionContext,
- FileChangeType,
- LinkedEditingRanges,
- SemanticTokens,
- SignatureHelpContext,
- TextDocumentContentChangeEvent
-} from 'vscode-languageserver';
-import {
- CodeAction,
- CodeActionContext,
- Color,
- ColorInformation,
- ColorPresentation,
- CompletionItem,
- CompletionList,
- DefinitionLink,
- Diagnostic,
- FormattingOptions,
- Hover,
- Location,
- Position,
- Range,
- ReferenceContext,
- SymbolInformation,
- TextDocumentIdentifier,
- TextEdit,
- WorkspaceEdit,
- SelectionRange,
- SignatureHelp,
- FoldingRange
+ CodeAction,
+ CodeActionContext,
+ Color,
+ ColorInformation,
+ ColorPresentation,
+ CompletionItem,
+ CompletionList,
+ DefinitionLink,
+ Diagnostic,
+ FormattingOptions,
+ Hover,
+ Location,
+ Position,
+ Range,
+ ReferenceContext,
+ SymbolInformation,
+ TextDocumentIdentifier,
+ TextEdit,
+ WorkspaceEdit,
+ SelectionRange,
+ SignatureHelp,
+ FoldingRange,
} from 'vscode-languageserver-types';
import { Document } from '../core/documents';
export type Resolvable<T> = T | Promise<T>;
export interface AppCompletionItem<T extends TextDocumentIdentifier = any> extends CompletionItem {
- data?: T;
+ data?: T;
}
export interface AppCompletionList<T extends TextDocumentIdentifier = any> extends CompletionList {
- items: Array<AppCompletionItem<T>>;
+ items: Array<AppCompletionItem<T>>;
}
export interface DiagnosticsProvider {
- getDiagnostics(document: Document): Resolvable<Diagnostic[]>;
+ getDiagnostics(document: Document): Resolvable<Diagnostic[]>;
}
export interface HoverProvider {
- doHover(document: Document, position: Position): Resolvable<Hover | null>;
+ doHover(document: Document, position: Position): Resolvable<Hover | null>;
}
export interface FoldingRangeProvider {
- getFoldingRanges(document: Document): Resolvable<FoldingRange[]|null>;
+ getFoldingRanges(document: Document): Resolvable<FoldingRange[] | null>;
}
export interface CompletionsProvider<T extends TextDocumentIdentifier = any> {
- getCompletions(
- document: Document,
- position: Position,
- completionContext?: CompletionContext
- ): Resolvable<AppCompletionList<T> | null>;
+ getCompletions(document: Document, position: Position, completionContext?: CompletionContext): Resolvable<AppCompletionList<T> | null>;
- resolveCompletion?(
- document: Document,
- completionItem: AppCompletionItem<T>
- ): Resolvable<AppCompletionItem<T>>;
+ resolveCompletion?(document: Document, completionItem: AppCompletionItem<T>): Resolvable<AppCompletionItem<T>>;
}
export interface FormattingProvider {
- formatDocument(document: Document, options: FormattingOptions): Resolvable<TextEdit[]>;
+ formatDocument(document: Document, options: FormattingOptions): Resolvable<TextEdit[]>;
}
export interface TagCompleteProvider {
- doTagComplete(document: Document, position: Position): Resolvable<string | null>;
+ doTagComplete(document: Document, position: Position): Resolvable<string | null>;
}
export interface DocumentColorsProvider {
- getDocumentColors(document: Document): Resolvable<ColorInformation[]>;
+ getDocumentColors(document: Document): Resolvable<ColorInformation[]>;
}
export interface ColorPresentationsProvider {
- getColorPresentations(
- document: Document,
- range: Range,
- color: Color
- ): Resolvable<ColorPresentation[]>;
+ getColorPresentations(document: Document, range: Range, color: Color): Resolvable<ColorPresentation[]>;
}
export interface DocumentSymbolsProvider {
- getDocumentSymbols(document: Document): Resolvable<SymbolInformation[]>;
+ getDocumentSymbols(document: Document): Resolvable<SymbolInformation[]>;
}
export interface DefinitionsProvider {
- getDefinitions(document: Document, position: Position): Resolvable<DefinitionLink[]>;
+ getDefinitions(document: Document, position: Position): Resolvable<DefinitionLink[]>;
}
export interface BackwardsCompatibleDefinitionsProvider {
- getDefinitions(
- document: Document,
- position: Position
- ): Resolvable<DefinitionLink[] | Location[]>;
+ getDefinitions(document: Document, position: Position): Resolvable<DefinitionLink[] | Location[]>;
}
export interface CodeActionsProvider {
- getCodeActions(
- document: Document,
- range: Range,
- context: CodeActionContext
- ): Resolvable<CodeAction[]>;
- executeCommand?(
- document: Document,
- command: string,
- args?: any[]
- ): Resolvable<WorkspaceEdit | string | null>;
+ getCodeActions(document: Document, range: Range, context: CodeActionContext): Resolvable<CodeAction[]>;
+ executeCommand?(document: Document, command: string, args?: any[]): Resolvable<WorkspaceEdit | string | null>;
}
export interface FileRename {
- oldUri: string;
- newUri: string;
+ oldUri: string;
+ newUri: string;
}
export interface UpdateImportsProvider {
- updateImports(fileRename: FileRename): Resolvable<WorkspaceEdit | null>;
+ updateImports(fileRename: FileRename): Resolvable<WorkspaceEdit | null>;
}
export interface RenameProvider {
- rename(
- document: Document,
- position: Position,
- newName: string
- ): Resolvable<WorkspaceEdit | null>;
- prepareRename(document: Document, position: Position): Resolvable<Range | null>;
+ rename(document: Document, position: Position, newName: string): Resolvable<WorkspaceEdit | null>;
+ prepareRename(document: Document, position: Position): Resolvable<Range | null>;
}
export interface FindReferencesProvider {
- findReferences(
- document: Document,
- position: Position,
- context: ReferenceContext
- ): Promise<Location[] | null>;
+ findReferences(document: Document, position: Position, context: ReferenceContext): Promise<Location[] | null>;
}
export interface SignatureHelpProvider {
- getSignatureHelp(
- document: Document,
- position: Position,
- context: SignatureHelpContext | undefined
- ): Resolvable<SignatureHelp | null>;
+ getSignatureHelp(document: Document, position: Position, context: SignatureHelpContext | undefined): Resolvable<SignatureHelp | null>;
}
export interface SelectionRangeProvider {
- getSelectionRange(document: Document, position: Position): Resolvable<SelectionRange | null>;
+ getSelectionRange(document: Document, position: Position): Resolvable<SelectionRange | null>;
}
export interface SemanticTokensProvider {
- getSemanticTokens(textDocument: Document, range?: Range): Resolvable<SemanticTokens | null>;
+ getSemanticTokens(textDocument: Document, range?: Range): Resolvable<SemanticTokens | null>;
}
export interface LinkedEditingRangesProvider {
- getLinkedEditingRanges(
- document: Document,
- position: Position
- ): Resolvable<LinkedEditingRanges | null>;
+ getLinkedEditingRanges(document: Document, position: Position): Resolvable<LinkedEditingRanges | null>;
}
export interface OnWatchFileChangesPara {
- fileName: string;
- changeType: FileChangeType;
+ fileName: string;
+ changeType: FileChangeType;
}
export interface OnWatchFileChanges {
- onWatchFileChanges(onWatchFileChangesParas: OnWatchFileChangesPara[]): void;
+ onWatchFileChanges(onWatchFileChangesParas: OnWatchFileChangesPara[]): void;
}
export interface UpdateTsOrJsFile {
- updateTsOrJsFile(fileName: string, changes: TextDocumentContentChangeEvent[]): void;
+ updateTsOrJsFile(fileName: string, changes: TextDocumentContentChangeEvent[]): void;
}
type ProviderBase = DiagnosticsProvider &
- HoverProvider &
- CompletionsProvider &
- FormattingProvider &
- FoldingRangeProvider &
- TagCompleteProvider &
- DocumentColorsProvider &
- ColorPresentationsProvider &
- DocumentSymbolsProvider &
- UpdateImportsProvider &
- CodeActionsProvider &
- FindReferencesProvider &
- RenameProvider &
- SignatureHelpProvider &
- SemanticTokensProvider &
- LinkedEditingRangesProvider;
+ HoverProvider &
+ CompletionsProvider &
+ FormattingProvider &
+ FoldingRangeProvider &
+ TagCompleteProvider &
+ DocumentColorsProvider &
+ ColorPresentationsProvider &
+ DocumentSymbolsProvider &
+ UpdateImportsProvider &
+ CodeActionsProvider &
+ FindReferencesProvider &
+ RenameProvider &
+ SignatureHelpProvider &
+ SemanticTokensProvider &
+ LinkedEditingRangesProvider;
export type LSProvider = ProviderBase & BackwardsCompatibleDefinitionsProvider;
export interface LSPProviderConfig {
- /**
- * Whether or not completion lists that are marked as imcomplete
- * should be filtered server side.
- */
- filterIncompleteCompletions: boolean;
- /**
- * Whether or not getDefinitions supports the LocationLink interface.
- */
- definitionLinkSupport: boolean;
-}
-
-export type Plugin = Partial<
- ProviderBase &
- DefinitionsProvider &
- OnWatchFileChanges &
- SelectionRangeProvider &
- UpdateTsOrJsFile
->;
+ /**
+ * Whether or not completion lists that are marked as imcomplete
+ * should be filtered server side.
+ */
+ filterIncompleteCompletions: boolean;
+ /**
+ * Whether or not getDefinitions supports the LocationLink interface.
+ */
+ definitionLinkSupport: boolean;
+}
+
+export type Plugin = Partial<ProviderBase & DefinitionsProvider & OnWatchFileChanges & SelectionRangeProvider & UpdateTsOrJsFile>;
diff --git a/tools/vscode/packages/server/src/plugins/typescript/LanguageServiceManager.ts b/tools/vscode/packages/server/src/plugins/typescript/LanguageServiceManager.ts
index 60dec606c..529ab2b4c 100644
--- a/tools/vscode/packages/server/src/plugins/typescript/LanguageServiceManager.ts
+++ b/tools/vscode/packages/server/src/plugins/typescript/LanguageServiceManager.ts
@@ -38,7 +38,7 @@ export class LanguageServiceManager {
const url = urlToPath(curr) as string;
if (fileName.startsWith(url) && curr.length < url.length) return url;
return found;
- }, '')
+ }, '');
}
private createDocument = (fileName: string, content: string) => {
diff --git a/tools/vscode/packages/server/src/plugins/typescript/SnapshotManager.ts b/tools/vscode/packages/server/src/plugins/typescript/SnapshotManager.ts
index aac26d96e..47d44838d 100644
--- a/tools/vscode/packages/server/src/plugins/typescript/SnapshotManager.ts
+++ b/tools/vscode/packages/server/src/plugins/typescript/SnapshotManager.ts
@@ -6,328 +6,298 @@ import { pathToUrl } from '../../utils';
import { getScriptKindFromFileName, isAstroFilePath, toVirtualAstroFilePath } from './utils';
export interface TsFilesSpec {
- include?: readonly string[];
- exclude?: readonly string[];
+ include?: readonly string[];
+ exclude?: readonly string[];
}
export class SnapshotManager {
- private documents: Map<string, DocumentSnapshot> = new Map();
- private lastLogged = new Date(new Date().getTime() - 60_001);
-
- private readonly watchExtensions = [
- ts.Extension.Dts,
- ts.Extension.Js,
- ts.Extension.Jsx,
- ts.Extension.Ts,
- ts.Extension.Tsx,
- ts.Extension.Json
- ];
-
- constructor(
- private projectFiles: string[],
- private fileSpec: TsFilesSpec,
- private workspaceRoot: string
- ) {
-
- }
+ private documents: Map<string, DocumentSnapshot> = new Map();
+ private lastLogged = new Date(new Date().getTime() - 60_001);
- updateProjectFiles() {
- const { include, exclude } = this.fileSpec;
-
- if (include?.length === 0) return;
+ private readonly watchExtensions = [ts.Extension.Dts, ts.Extension.Js, ts.Extension.Jsx, ts.Extension.Ts, ts.Extension.Tsx, ts.Extension.Json];
- const projectFiles = ts.sys.readDirectory(
- this.workspaceRoot,
- this.watchExtensions,
- exclude,
- include
- );
+ constructor(private projectFiles: string[], private fileSpec: TsFilesSpec, private workspaceRoot: string) {}
- this.projectFiles = Array.from(new Set([...this.projectFiles, ...projectFiles]));
- }
+ updateProjectFiles() {
+ const { include, exclude } = this.fileSpec;
- updateProjectFile(fileName: string, changes?: TextDocumentContentChangeEvent[]): void {
- const previousSnapshot = this.get(fileName);
-
- if (changes) {
- if (!(previousSnapshot instanceof TypeScriptDocumentSnapshot)) {
- return;
- }
- previousSnapshot.update(changes);
- } else {
- const newSnapshot = createDocumentSnapshot(fileName);
-
- if (previousSnapshot) {
- newSnapshot.version = previousSnapshot.version + 1;
- } else {
- // ensure it's greater than initial version
- // so that ts server picks up the change
- newSnapshot.version += 1;
- }
- this.set(fileName, newSnapshot);
- }
- }
+ if (include?.length === 0) return;
- has(fileName: string) {
- return this.projectFiles.includes(fileName) || this.getFileNames().includes(fileName);
- }
+ const projectFiles = ts.sys.readDirectory(this.workspaceRoot, this.watchExtensions, exclude, include);
- get(fileName: string) {
- return this.documents.get(fileName);
- }
-
- set(fileName: string, snapshot: DocumentSnapshot) {
- // const prev = this.get(fileName);
- this.logStatistics();
- return this.documents.set(fileName, snapshot);
- }
-
- delete(fileName: string) {
- this.projectFiles = this.projectFiles.filter((s) => s !== fileName);
- return this.documents.delete(fileName);
- }
+ this.projectFiles = Array.from(new Set([...this.projectFiles, ...projectFiles]));
+ }
- getFileNames() {
- return Array.from(this.documents.keys()).map(fileName => toVirtualAstroFilePath(fileName));
- }
+ updateProjectFile(fileName: string, changes?: TextDocumentContentChangeEvent[]): void {
+ const previousSnapshot = this.get(fileName);
- getProjectFileNames() {
- return [...this.projectFiles];
- }
-
- private logStatistics() {
- const date = new Date();
- // Don't use setInterval because that will keep tests running forever
- if (date.getTime() - this.lastLogged.getTime() > 60_000) {
- this.lastLogged = date;
-
- const projectFiles = this.getProjectFileNames();
- const allFiles = Array.from(new Set([...projectFiles, ...this.getFileNames()]));
- console.log(
- 'SnapshotManager File Statistics:\n' +
- `Project files: ${projectFiles.length}\n` +
- `Astro files: ${
- allFiles.filter((name) => name.endsWith('.astro')).length
- }\n` +
- `From node_modules: ${
- allFiles.filter((name) => name.includes('node_modules')).length
- }\n` +
- `Total: ${allFiles.length}`
- );
- }
- }
+ if (changes) {
+ if (!(previousSnapshot instanceof TypeScriptDocumentSnapshot)) {
+ return;
+ }
+ previousSnapshot.update(changes);
+ } else {
+ const newSnapshot = createDocumentSnapshot(fileName);
+
+ if (previousSnapshot) {
+ newSnapshot.version = previousSnapshot.version + 1;
+ } else {
+ // ensure it's greater than initial version
+ // so that ts server picks up the change
+ newSnapshot.version += 1;
+ }
+ this.set(fileName, newSnapshot);
+ }
+ }
+
+ has(fileName: string) {
+ return this.projectFiles.includes(fileName) || this.getFileNames().includes(fileName);
+ }
+
+ get(fileName: string) {
+ return this.documents.get(fileName);
+ }
+
+ set(fileName: string, snapshot: DocumentSnapshot) {
+ // const prev = this.get(fileName);
+ this.logStatistics();
+ return this.documents.set(fileName, snapshot);
+ }
+
+ delete(fileName: string) {
+ this.projectFiles = this.projectFiles.filter((s) => s !== fileName);
+ return this.documents.delete(fileName);
+ }
+
+ getFileNames() {
+ return Array.from(this.documents.keys()).map((fileName) => toVirtualAstroFilePath(fileName));
+ }
+
+ getProjectFileNames() {
+ return [...this.projectFiles];
+ }
+
+ private logStatistics() {
+ const date = new Date();
+ // Don't use setInterval because that will keep tests running forever
+ if (date.getTime() - this.lastLogged.getTime() > 60_000) {
+ this.lastLogged = date;
+
+ const projectFiles = this.getProjectFileNames();
+ const allFiles = Array.from(new Set([...projectFiles, ...this.getFileNames()]));
+ console.log(
+ 'SnapshotManager File Statistics:\n' +
+ `Project files: ${projectFiles.length}\n` +
+ `Astro files: ${allFiles.filter((name) => name.endsWith('.astro')).length}\n` +
+ `From node_modules: ${allFiles.filter((name) => name.includes('node_modules')).length}\n` +
+ `Total: ${allFiles.length}`
+ );
+ }
+ }
}
export interface DocumentSnapshot extends ts.IScriptSnapshot {
- version: number;
- filePath: string;
- scriptKind: ts.ScriptKind;
- positionAt(offset: number): Position;
- /**
- * Instantiates a source mapper.
- * `destroyFragment` needs to be called when
- * it's no longer needed / the class should be cleaned up
- * in order to prevent memory leaks.
- */
- getFragment(): Promise<DocumentFragmentSnapshot>;
- /**
- * Needs to be called when source mapper
- * is no longer needed / the class should be cleaned up
- * in order to prevent memory leaks.
- */
- destroyFragment(): void;
- /**
- * Convenience function for getText(0, getLength())
- */
- getFullText(): string;
+ version: number;
+ filePath: string;
+ scriptKind: ts.ScriptKind;
+ positionAt(offset: number): Position;
+ /**
+ * Instantiates a source mapper.
+ * `destroyFragment` needs to be called when
+ * it's no longer needed / the class should be cleaned up
+ * in order to prevent memory leaks.
+ */
+ getFragment(): Promise<DocumentFragmentSnapshot>;
+ /**
+ * Needs to be called when source mapper
+ * is no longer needed / the class should be cleaned up
+ * in order to prevent memory leaks.
+ */
+ destroyFragment(): void;
+ /**
+ * Convenience function for getText(0, getLength())
+ */
+ getFullText(): string;
}
export const createDocumentSnapshot = (filePath: string, createDocument?: (_filePath: string, text: string) => Document): DocumentSnapshot => {
- const text = ts.sys.readFile(filePath) ?? '';
+ const text = ts.sys.readFile(filePath) ?? '';
- if (isAstroFilePath(filePath)) {
- if (!createDocument) throw new Error('Astro documents require the "createDocument" utility to be provided');
- const snapshot = new AstroDocumentSnapshot(createDocument(filePath, text));
- return snapshot;
- }
-
- return new TypeScriptDocumentSnapshot(0, filePath, text);
+ if (isAstroFilePath(filePath)) {
+ if (!createDocument) throw new Error('Astro documents require the "createDocument" utility to be provided');
+ const snapshot = new AstroDocumentSnapshot(createDocument(filePath, text));
+ return snapshot;
+ }
-}
+ return new TypeScriptDocumentSnapshot(0, filePath, text);
+};
class AstroDocumentSnapshot implements DocumentSnapshot {
-
- version = this.doc.version;
- scriptKind = ts.ScriptKind.Unknown;
-
- constructor(private doc: Document) {}
-
- async getFragment(): Promise<DocumentFragmentSnapshot> {
- return new DocumentFragmentSnapshot(this.doc);
- }
+ version = this.doc.version;
+ scriptKind = ts.ScriptKind.Unknown;
- async destroyFragment() {
- return;
- }
+ constructor(private doc: Document) {}
- get text() {
- return this.doc.getText();
- }
+ async getFragment(): Promise<DocumentFragmentSnapshot> {
+ return new DocumentFragmentSnapshot(this.doc);
+ }
- get filePath() {
- return this.doc.getFilePath() || '';
- }
+ async destroyFragment() {
+ return;
+ }
- getText(start: number, end: number) {
- return this.text.substring(start, end);
- }
+ get text() {
+ return this.doc.getText();
+ }
- getLength() {
- return this.text.length;
- }
+ get filePath() {
+ return this.doc.getFilePath() || '';
+ }
- getFullText() {
- return this.text;
- }
+ getText(start: number, end: number) {
+ return this.text.substring(start, end);
+ }
- getChangeRange() {
- return undefined;
- }
+ getLength() {
+ return this.text.length;
+ }
- positionAt(offset: number) {
- return positionAt(offset, this.text);
- }
+ getFullText() {
+ return this.text;
+ }
- getLineContainingOffset(offset: number) {
- const chunks = this.getText(0, offset).split('\n');
- return chunks[chunks.length - 1];
- }
-
- offsetAt(position: Position) {
- return offsetAt(position, this.text);
- }
-
-}
+ getChangeRange() {
+ return undefined;
+ }
-class DocumentFragmentSnapshot implements Omit<DocumentSnapshot, 'getFragment'|'destroyFragment'> {
-
- version: number;
- filePath: string;
- url: string;
- text: string;
-
- scriptKind = ts.ScriptKind.TSX;
- scriptInfo = null;
-
- constructor(
- private doc: Document
- ) {
- const filePath = doc.getFilePath();
- if (!filePath) throw new Error('Cannot create a document fragment from a non-local document');
- const text = doc.getText();
- this.version = doc.version;
- this.filePath = toVirtualAstroFilePath(filePath);
- this.url = toVirtualAstroFilePath(filePath);
- this.text = this.transformContent(text);
- }
+ positionAt(offset: number) {
+ return positionAt(offset, this.text);
+ }
- /** @internal */
- private transformContent(content: string) {
- return content.replace(/---/g, '///');
- }
+ getLineContainingOffset(offset: number) {
+ const chunks = this.getText(0, offset).split('\n');
+ return chunks[chunks.length - 1];
+ }
- getText(start: number, end: number) {
- return this.text.substring(start, end);
- }
-
- getLength() {
- return this.text.length;
- }
-
- getFullText() {
- return this.text;
- }
-
- getChangeRange() {
- return undefined;
- }
-
- positionAt(offset: number) {
- return positionAt(offset, this.text);
- }
-
- getLineContainingOffset(offset: number) {
- const chunks = this.getText(0, offset).split('\n');
- return chunks[chunks.length - 1];
- }
+ offsetAt(position: Position) {
+ return offsetAt(position, this.text);
+ }
+}
- offsetAt(position: Position): number {
- return offsetAt(position, this.text);
- }
+class DocumentFragmentSnapshot implements Omit<DocumentSnapshot, 'getFragment' | 'destroyFragment'> {
+ version: number;
+ filePath: string;
+ url: string;
+ text: string;
+
+ scriptKind = ts.ScriptKind.TSX;
+ scriptInfo = null;
+
+ constructor(private doc: Document) {
+ const filePath = doc.getFilePath();
+ if (!filePath) throw new Error('Cannot create a document fragment from a non-local document');
+ const text = doc.getText();
+ this.version = doc.version;
+ this.filePath = toVirtualAstroFilePath(filePath);
+ this.url = toVirtualAstroFilePath(filePath);
+ this.text = this.transformContent(text);
+ }
+
+ /** @internal */
+ private transformContent(content: string) {
+ return content.replace(/---/g, '///');
+ }
+
+ getText(start: number, end: number) {
+ return this.text.substring(start, end);
+ }
+
+ getLength() {
+ return this.text.length;
+ }
+
+ getFullText() {
+ return this.text;
+ }
+
+ getChangeRange() {
+ return undefined;
+ }
+
+ positionAt(offset: number) {
+ return positionAt(offset, this.text);
+ }
+
+ getLineContainingOffset(offset: number) {
+ const chunks = this.getText(0, offset).split('\n');
+ return chunks[chunks.length - 1];
+ }
+
+ offsetAt(position: Position): number {
+ return offsetAt(position, this.text);
+ }
}
class TypeScriptDocumentSnapshot implements DocumentSnapshot {
-
- scriptKind = getScriptKindFromFileName(this.filePath);
- scriptInfo = null;
- url: string;
-
-
- constructor(public version: number, public readonly filePath: string, private text: string) {
- this.url = pathToUrl(filePath)
- }
-
- getText(start: number, end: number) {
- return this.text.substring(start, end);
- }
-
- getLength() {
- return this.text.length;
- }
-
- getFullText() {
- return this.text;
- }
-
- getChangeRange() {
- return undefined;
- }
-
- positionAt(offset: number) {
- return positionAt(offset, this.text);
- }
-
- offsetAt(position: Position): number {
- return offsetAt(position, this.text);
- }
-
- async getFragment(): Promise<DocumentFragmentSnapshot> {
- return this as unknown as any;
- }
-
- destroyFragment() {
- // nothing to clean up
- }
-
- getLineContainingOffset(offset: number) {
- const chunks = this.getText(0, offset).split('\n');
- return chunks[chunks.length - 1];
- }
-
- update(changes: TextDocumentContentChangeEvent[]): void {
- for (const change of changes) {
- let start = 0;
- let end = 0;
- if ('range' in change) {
- start = this.offsetAt(change.range.start);
- end = this.offsetAt(change.range.end);
- } else {
- end = this.getLength();
- }
-
- this.text = this.text.slice(0, start) + change.text + this.text.slice(end);
- }
-
- this.version++;
- }
+ scriptKind = getScriptKindFromFileName(this.filePath);
+ scriptInfo = null;
+ url: string;
+
+ constructor(public version: number, public readonly filePath: string, private text: string) {
+ this.url = pathToUrl(filePath);
+ }
+
+ getText(start: number, end: number) {
+ return this.text.substring(start, end);
+ }
+
+ getLength() {
+ return this.text.length;
+ }
+
+ getFullText() {
+ return this.text;
+ }
+
+ getChangeRange() {
+ return undefined;
+ }
+
+ positionAt(offset: number) {
+ return positionAt(offset, this.text);
+ }
+
+ offsetAt(position: Position): number {
+ return offsetAt(position, this.text);
+ }
+
+ async getFragment(): Promise<DocumentFragmentSnapshot> {
+ return (this as unknown) as any;
+ }
+
+ destroyFragment() {
+ // nothing to clean up
+ }
+
+ getLineContainingOffset(offset: number) {
+ const chunks = this.getText(0, offset).split('\n');
+ return chunks[chunks.length - 1];
+ }
+
+ update(changes: TextDocumentContentChangeEvent[]): void {
+ for (const change of changes) {
+ let start = 0;
+ let end = 0;
+ if ('range' in change) {
+ start = this.offsetAt(change.range.start);
+ end = this.offsetAt(change.range.end);
+ } else {
+ end = this.getLength();
+ }
+
+ this.text = this.text.slice(0, start) + change.text + this.text.slice(end);
+ }
+
+ this.version++;
+ }
}
diff --git a/tools/vscode/packages/server/src/plugins/typescript/TypeScriptPlugin.ts b/tools/vscode/packages/server/src/plugins/typescript/TypeScriptPlugin.ts
index 018e8bfda..aab758bdb 100644
--- a/tools/vscode/packages/server/src/plugins/typescript/TypeScriptPlugin.ts
+++ b/tools/vscode/packages/server/src/plugins/typescript/TypeScriptPlugin.ts
@@ -1,11 +1,7 @@
import type { Document, DocumentManager } from '../../core/documents';
import type { ConfigManager } from '../../core/config';
import type { CompletionsProvider, AppCompletionItem, AppCompletionList } from '../interfaces';
-import {
- CompletionContext,
- Position,
- FileChangeType
-} from 'vscode-languageserver';
+import { CompletionContext, Position, FileChangeType } from 'vscode-languageserver';
import * as ts from 'typescript';
import { CompletionsProviderImpl, CompletionEntryWithIdentifer } from './features/CompletionsProvider';
import { LanguageServiceManager } from './LanguageServiceManager';
@@ -13,77 +9,61 @@ import { SnapshotManager } from './SnapshotManager';
import { getScriptKindFromFileName } from './utils';
export class TypeScriptPlugin implements CompletionsProvider {
- private readonly docManager: DocumentManager;
- private readonly configManager: ConfigManager;
- private readonly languageServiceManager: LanguageServiceManager;
+ private readonly docManager: DocumentManager;
+ private readonly configManager: ConfigManager;
+ private readonly languageServiceManager: LanguageServiceManager;
- private readonly completionProvider: CompletionsProviderImpl;
+ private readonly completionProvider: CompletionsProviderImpl;
- constructor(
- docManager: DocumentManager,
- configManager: ConfigManager,
- workspaceUris: string[]
- ) {
- this.docManager = docManager;
- this.configManager = configManager;
- this.languageServiceManager = new LanguageServiceManager(docManager, configManager, workspaceUris);
-
- this.completionProvider = new CompletionsProviderImpl(this.languageServiceManager);
- }
+ constructor(docManager: DocumentManager, configManager: ConfigManager, workspaceUris: string[]) {
+ this.docManager = docManager;
+ this.configManager = configManager;
+ this.languageServiceManager = new LanguageServiceManager(docManager, configManager, workspaceUris);
- async getCompletions(
- document: Document,
- position: Position,
- completionContext?: CompletionContext
- ): Promise<AppCompletionList<CompletionEntryWithIdentifer> | null> {
- const completions = await this.completionProvider.getCompletions(
- document,
- position,
- completionContext
- );
+ this.completionProvider = new CompletionsProviderImpl(this.languageServiceManager);
+ }
- return completions;
- }
+ async getCompletions(document: Document, position: Position, completionContext?: CompletionContext): Promise<AppCompletionList<CompletionEntryWithIdentifer> | null> {
+ const completions = await this.completionProvider.getCompletions(document, position, completionContext);
- async resolveCompletion(
- document: Document,
- completionItem: AppCompletionItem<CompletionEntryWithIdentifer>
- ): Promise<AppCompletionItem<CompletionEntryWithIdentifer>> {
- return this.completionProvider.resolveCompletion(document, completionItem);
- }
+ return completions;
+ }
- async onWatchFileChanges(onWatchFileChangesParams: any[]): Promise<void> {
- const doneUpdateProjectFiles = new Set<SnapshotManager>();
+ async resolveCompletion(document: Document, completionItem: AppCompletionItem<CompletionEntryWithIdentifer>): Promise<AppCompletionItem<CompletionEntryWithIdentifer>> {
+ return this.completionProvider.resolveCompletion(document, completionItem);
+ }
- for (const { fileName, changeType } of onWatchFileChangesParams) {
- const scriptKind = getScriptKindFromFileName(fileName);
+ async onWatchFileChanges(onWatchFileChangesParams: any[]): Promise<void> {
+ const doneUpdateProjectFiles = new Set<SnapshotManager>();
- if (scriptKind === ts.ScriptKind.Unknown) {
- // We don't deal with svelte files here
- continue;
- }
+ for (const { fileName, changeType } of onWatchFileChangesParams) {
+ const scriptKind = getScriptKindFromFileName(fileName);
- const snapshotManager = await this.getSnapshotManager(fileName);
- if (changeType === FileChangeType.Created) {
- if (!doneUpdateProjectFiles.has(snapshotManager)) {
- snapshotManager.updateProjectFiles();
- doneUpdateProjectFiles.add(snapshotManager);
- }
- } else if (changeType === FileChangeType.Deleted) {
- snapshotManager.delete(fileName);
- return;
- }
+ if (scriptKind === ts.ScriptKind.Unknown) {
+ // We don't deal with svelte files here
+ continue;
+ }
- snapshotManager.updateProjectFile(fileName);
+ const snapshotManager = await this.getSnapshotManager(fileName);
+ if (changeType === FileChangeType.Created) {
+ if (!doneUpdateProjectFiles.has(snapshotManager)) {
+ snapshotManager.updateProjectFiles();
+ doneUpdateProjectFiles.add(snapshotManager);
}
- }
+ } else if (changeType === FileChangeType.Deleted) {
+ snapshotManager.delete(fileName);
+ return;
+ }
- /**
- *
- * @internal
- */
- public async getSnapshotManager(fileName: string) {
- return this.languageServiceManager.getSnapshotManager(fileName);
+ snapshotManager.updateProjectFile(fileName);
}
-}
+ }
+ /**
+ *
+ * @internal
+ */
+ public async getSnapshotManager(fileName: string) {
+ return this.languageServiceManager.getSnapshotManager(fileName);
+ }
+}
diff --git a/tools/vscode/packages/server/src/plugins/typescript/astro-sys.ts b/tools/vscode/packages/server/src/plugins/typescript/astro-sys.ts
index 0459528c5..36d009eb6 100644
--- a/tools/vscode/packages/server/src/plugins/typescript/astro-sys.ts
+++ b/tools/vscode/packages/server/src/plugins/typescript/astro-sys.ts
@@ -6,37 +6,37 @@ import { ensureRealAstroFilePath, isAstroFilePath, isVirtualAstroFilePath, toRea
* This should only be accessed by TS Astro module resolution.
*/
export function createAstroSys(getSnapshot: (fileName: string) => DocumentSnapshot) {
- const AstroSys: ts.System = {
- ...ts.sys,
- fileExists(path: string) {
- if (isAstroFilePath(path) || isVirtualAstroFilePath(path)) {
- console.log('fileExists', path, ts.sys.fileExists(ensureRealAstroFilePath(path)));
- }
- return ts.sys.fileExists(ensureRealAstroFilePath(path));
- },
- readFile(path: string) {
- if (isAstroFilePath(path) || isVirtualAstroFilePath(path)) {
- console.log('readFile', path);
- }
- const snapshot = getSnapshot(path);
- return snapshot.getFullText();
- },
- readDirectory(path, extensions, exclude, include, depth) {
- const extensionsWithAstro = (extensions ?? []).concat(...['.astro']);
- const result = ts.sys.readDirectory(path, extensionsWithAstro, exclude, include, depth);;
- return result;
- }
- };
+ const AstroSys: ts.System = {
+ ...ts.sys,
+ fileExists(path: string) {
+ if (isAstroFilePath(path) || isVirtualAstroFilePath(path)) {
+ console.log('fileExists', path, ts.sys.fileExists(ensureRealAstroFilePath(path)));
+ }
+ return ts.sys.fileExists(ensureRealAstroFilePath(path));
+ },
+ readFile(path: string) {
+ if (isAstroFilePath(path) || isVirtualAstroFilePath(path)) {
+ console.log('readFile', path);
+ }
+ const snapshot = getSnapshot(path);
+ return snapshot.getFullText();
+ },
+ readDirectory(path, extensions, exclude, include, depth) {
+ const extensionsWithAstro = (extensions ?? []).concat(...['.astro']);
+ const result = ts.sys.readDirectory(path, extensionsWithAstro, exclude, include, depth);
+ return result;
+ },
+ };
- if (ts.sys.realpath) {
- const realpath = ts.sys.realpath;
- AstroSys.realpath = function (path) {
- if (isVirtualAstroFilePath(path)) {
- return realpath(toRealAstroFilePath(path)) + '.ts';
- }
- return realpath(path);
- };
- }
+ if (ts.sys.realpath) {
+ const realpath = ts.sys.realpath;
+ AstroSys.realpath = function (path) {
+ if (isVirtualAstroFilePath(path)) {
+ return realpath(toRealAstroFilePath(path)) + '.ts';
+ }
+ return realpath(path);
+ };
+ }
- return AstroSys;
+ return AstroSys;
}
diff --git a/tools/vscode/packages/server/src/plugins/typescript/features/CompletionsProvider.ts b/tools/vscode/packages/server/src/plugins/typescript/features/CompletionsProvider.ts
index ebbc16e31..348f3e4ae 100644
--- a/tools/vscode/packages/server/src/plugins/typescript/features/CompletionsProvider.ts
+++ b/tools/vscode/packages/server/src/plugins/typescript/features/CompletionsProvider.ts
@@ -99,7 +99,7 @@ export class CompletionsProviderImpl implements CompletionsProvider<CompletionEn
data: {
...comp,
uri,
- position
+ position,
},
};
}
diff --git a/tools/vscode/packages/server/src/plugins/typescript/languageService.ts b/tools/vscode/packages/server/src/plugins/typescript/languageService.ts
index 4de703b2a..098c335e7 100644
--- a/tools/vscode/packages/server/src/plugins/typescript/languageService.ts
+++ b/tools/vscode/packages/server/src/plugins/typescript/languageService.ts
@@ -53,30 +53,22 @@ async function createLanguageService(tsconfigPath: string, workspaceRoot: string
return ts.sys.readDirectory(path, [...extensions, '.vue', '.svelte', '.astro', '.js', '.jsx'], exclude, include, depth);
},
};
-
+
let configJson = (tsconfigPath && ts.readConfigFile(tsconfigPath, ts.sys.readFile).config) || getDefaultJsConfig();
if (!configJson.extends) {
- configJson = Object.assign(
- {
- exclude: getDefaultExclude()
- },
- configJson
- );
+ configJson = Object.assign(
+ {
+ exclude: getDefaultExclude(),
+ },
+ configJson
+ );
}
- const project = ts.parseJsonConfigFileContent(
- configJson,
- parseConfigHost,
- workspaceRoot,
- {},
- basename(tsconfigPath),
- undefined,
- [
- { extension: '.vue', isMixedContent: true, scriptKind: ts.ScriptKind.Deferred },
- { extension: '.svelte', isMixedContent: true, scriptKind: ts.ScriptKind.Deferred },
- { extension: '.astro', isMixedContent: true, scriptKind: ts.ScriptKind.Deferred }
- ]
- );
+ const project = ts.parseJsonConfigFileContent(configJson, parseConfigHost, workspaceRoot, {}, basename(tsconfigPath), undefined, [
+ { extension: '.vue', isMixedContent: true, scriptKind: ts.ScriptKind.Deferred },
+ { extension: '.svelte', isMixedContent: true, scriptKind: ts.ScriptKind.Deferred },
+ { extension: '.astro', isMixedContent: true, scriptKind: ts.ScriptKind.Deferred },
+ ]);
let projectVersion = 0;
const snapshotManager = new SnapshotManager(project.fileNames, { exclude: ['node_modules', 'dist'], include: ['astro'] }, workspaceRoot || process.cwd());
@@ -100,15 +92,15 @@ async function createLanguageService(tsconfigPath: string, workspaceRoot: string
getProjectVersion: () => `${projectVersion}`,
getScriptFileNames: () => Array.from(new Set([...snapshotManager.getFileNames(), ...snapshotManager.getProjectFileNames()])),
getScriptSnapshot,
- getScriptVersion: (fileName: string) => getScriptSnapshot(fileName).version.toString()
+ getScriptVersion: (fileName: string) => getScriptSnapshot(fileName).version.toString(),
};
const languageService = ts.createLanguageService(host);
const languageServiceProxy = new Proxy(languageService, {
get(target, prop) {
return Reflect.get(target, prop);
- }
- })
+ },
+ });
return {
tsconfigPath,
@@ -141,19 +133,16 @@ async function createLanguageService(tsconfigPath: string, workspaceRoot: string
}
function getScriptSnapshot(fileName: string): DocumentSnapshot {
- fileName = ensureRealAstroFilePath(fileName);
-
- let doc = snapshotManager.get(fileName);
- if (doc) {
- return doc;
- }
-
- doc = createDocumentSnapshot(
- fileName,
- docContext.createDocument,
- );
- snapshotManager.set(fileName, doc);
+ fileName = ensureRealAstroFilePath(fileName);
+
+ let doc = snapshotManager.get(fileName);
+ if (doc) {
return doc;
+ }
+
+ doc = createDocumentSnapshot(fileName, docContext.createDocument);
+ snapshotManager.set(fileName, doc);
+ return doc;
}
}
@@ -168,7 +157,7 @@ function getDefaultJsConfig(): {
compilerOptions: {
maxNodeModuleJsDepth: 2,
allowSyntheticDefaultImports: true,
- allowJs: true
+ allowJs: true,
},
include: ['astro'],
};
diff --git a/tools/vscode/packages/server/src/plugins/typescript/utils.ts b/tools/vscode/packages/server/src/plugins/typescript/utils.ts
index 058868474..1f42e7d0a 100644
--- a/tools/vscode/packages/server/src/plugins/typescript/utils.ts
+++ b/tools/vscode/packages/server/src/plugins/typescript/utils.ts
@@ -3,180 +3,172 @@ import { CompletionItemKind, DiagnosticSeverity } from 'vscode-languageserver';
import { dirname } from 'path';
import { pathToUrl } from '../../utils';
-export function scriptElementKindToCompletionItemKind(
- kind: ts.ScriptElementKind
-): CompletionItemKind {
- switch (kind) {
- case ts.ScriptElementKind.primitiveType:
- case ts.ScriptElementKind.keyword:
- return CompletionItemKind.Keyword;
- case ts.ScriptElementKind.constElement:
- return CompletionItemKind.Constant;
- case ts.ScriptElementKind.letElement:
- case ts.ScriptElementKind.variableElement:
- case ts.ScriptElementKind.localVariableElement:
- case ts.ScriptElementKind.alias:
- return CompletionItemKind.Variable;
- case ts.ScriptElementKind.memberVariableElement:
- case ts.ScriptElementKind.memberGetAccessorElement:
- case ts.ScriptElementKind.memberSetAccessorElement:
- return CompletionItemKind.Field;
- case ts.ScriptElementKind.functionElement:
- return CompletionItemKind.Function;
- case ts.ScriptElementKind.memberFunctionElement:
- case ts.ScriptElementKind.constructSignatureElement:
- case ts.ScriptElementKind.callSignatureElement:
- case ts.ScriptElementKind.indexSignatureElement:
- return CompletionItemKind.Method;
- case ts.ScriptElementKind.enumElement:
- return CompletionItemKind.Enum;
- case ts.ScriptElementKind.moduleElement:
- case ts.ScriptElementKind.externalModuleName:
- return CompletionItemKind.Module;
- case ts.ScriptElementKind.classElement:
- case ts.ScriptElementKind.typeElement:
- return CompletionItemKind.Class;
- case ts.ScriptElementKind.interfaceElement:
- return CompletionItemKind.Interface;
- case ts.ScriptElementKind.warning:
- case ts.ScriptElementKind.scriptElement:
- return CompletionItemKind.File;
- case ts.ScriptElementKind.directory:
- return CompletionItemKind.Folder;
- case ts.ScriptElementKind.string:
- return CompletionItemKind.Constant;
- }
- return CompletionItemKind.Property;
+export function scriptElementKindToCompletionItemKind(kind: ts.ScriptElementKind): CompletionItemKind {
+ switch (kind) {
+ case ts.ScriptElementKind.primitiveType:
+ case ts.ScriptElementKind.keyword:
+ return CompletionItemKind.Keyword;
+ case ts.ScriptElementKind.constElement:
+ return CompletionItemKind.Constant;
+ case ts.ScriptElementKind.letElement:
+ case ts.ScriptElementKind.variableElement:
+ case ts.ScriptElementKind.localVariableElement:
+ case ts.ScriptElementKind.alias:
+ return CompletionItemKind.Variable;
+ case ts.ScriptElementKind.memberVariableElement:
+ case ts.ScriptElementKind.memberGetAccessorElement:
+ case ts.ScriptElementKind.memberSetAccessorElement:
+ return CompletionItemKind.Field;
+ case ts.ScriptElementKind.functionElement:
+ return CompletionItemKind.Function;
+ case ts.ScriptElementKind.memberFunctionElement:
+ case ts.ScriptElementKind.constructSignatureElement:
+ case ts.ScriptElementKind.callSignatureElement:
+ case ts.ScriptElementKind.indexSignatureElement:
+ return CompletionItemKind.Method;
+ case ts.ScriptElementKind.enumElement:
+ return CompletionItemKind.Enum;
+ case ts.ScriptElementKind.moduleElement:
+ case ts.ScriptElementKind.externalModuleName:
+ return CompletionItemKind.Module;
+ case ts.ScriptElementKind.classElement:
+ case ts.ScriptElementKind.typeElement:
+ return CompletionItemKind.Class;
+ case ts.ScriptElementKind.interfaceElement:
+ return CompletionItemKind.Interface;
+ case ts.ScriptElementKind.warning:
+ case ts.ScriptElementKind.scriptElement:
+ return CompletionItemKind.File;
+ case ts.ScriptElementKind.directory:
+ return CompletionItemKind.Folder;
+ case ts.ScriptElementKind.string:
+ return CompletionItemKind.Constant;
+ }
+ return CompletionItemKind.Property;
}
-export function getCommitCharactersForScriptElement(
- kind: ts.ScriptElementKind
-): string[] | undefined {
- const commitCharacters: string[] = [];
- switch (kind) {
- case ts.ScriptElementKind.memberGetAccessorElement:
- case ts.ScriptElementKind.memberSetAccessorElement:
- case ts.ScriptElementKind.constructSignatureElement:
- case ts.ScriptElementKind.callSignatureElement:
- case ts.ScriptElementKind.indexSignatureElement:
- case ts.ScriptElementKind.enumElement:
- case ts.ScriptElementKind.interfaceElement:
- commitCharacters.push('.');
- break;
-
- case ts.ScriptElementKind.moduleElement:
- case ts.ScriptElementKind.alias:
- case ts.ScriptElementKind.constElement:
- case ts.ScriptElementKind.letElement:
- case ts.ScriptElementKind.variableElement:
- case ts.ScriptElementKind.localVariableElement:
- case ts.ScriptElementKind.memberVariableElement:
- case ts.ScriptElementKind.classElement:
- case ts.ScriptElementKind.functionElement:
- case ts.ScriptElementKind.memberFunctionElement:
- commitCharacters.push('.', ',');
- commitCharacters.push('(');
- break;
- }
-
- return commitCharacters.length === 0 ? undefined : commitCharacters;
+export function getCommitCharactersForScriptElement(kind: ts.ScriptElementKind): string[] | undefined {
+ const commitCharacters: string[] = [];
+ switch (kind) {
+ case ts.ScriptElementKind.memberGetAccessorElement:
+ case ts.ScriptElementKind.memberSetAccessorElement:
+ case ts.ScriptElementKind.constructSignatureElement:
+ case ts.ScriptElementKind.callSignatureElement:
+ case ts.ScriptElementKind.indexSignatureElement:
+ case ts.ScriptElementKind.enumElement:
+ case ts.ScriptElementKind.interfaceElement:
+ commitCharacters.push('.');
+ break;
+
+ case ts.ScriptElementKind.moduleElement:
+ case ts.ScriptElementKind.alias:
+ case ts.ScriptElementKind.constElement:
+ case ts.ScriptElementKind.letElement:
+ case ts.ScriptElementKind.variableElement:
+ case ts.ScriptElementKind.localVariableElement:
+ case ts.ScriptElementKind.memberVariableElement:
+ case ts.ScriptElementKind.classElement:
+ case ts.ScriptElementKind.functionElement:
+ case ts.ScriptElementKind.memberFunctionElement:
+ commitCharacters.push('.', ',');
+ commitCharacters.push('(');
+ break;
+ }
+
+ return commitCharacters.length === 0 ? undefined : commitCharacters;
}
export function mapSeverity(category: ts.DiagnosticCategory): DiagnosticSeverity {
- switch (category) {
- case ts.DiagnosticCategory.Error:
- return DiagnosticSeverity.Error;
- case ts.DiagnosticCategory.Warning:
- return DiagnosticSeverity.Warning;
- case ts.DiagnosticCategory.Suggestion:
- return DiagnosticSeverity.Hint;
- case ts.DiagnosticCategory.Message:
- return DiagnosticSeverity.Information;
- }
-
- return DiagnosticSeverity.Error;
+ switch (category) {
+ case ts.DiagnosticCategory.Error:
+ return DiagnosticSeverity.Error;
+ case ts.DiagnosticCategory.Warning:
+ return DiagnosticSeverity.Warning;
+ case ts.DiagnosticCategory.Suggestion:
+ return DiagnosticSeverity.Hint;
+ case ts.DiagnosticCategory.Message:
+ return DiagnosticSeverity.Information;
+ }
+
+ return DiagnosticSeverity.Error;
}
export function getScriptKindFromFileName(fileName: string): ts.ScriptKind {
- const ext = fileName.substr(fileName.lastIndexOf('.'));
- switch (ext.toLowerCase()) {
- case ts.Extension.Js:
- return ts.ScriptKind.JS;
- case ts.Extension.Jsx:
- return ts.ScriptKind.JSX;
- case ts.Extension.Ts:
- return ts.ScriptKind.TS;
- case ts.Extension.Tsx:
- return ts.ScriptKind.TSX;
- case ts.Extension.Json:
- return ts.ScriptKind.JSON;
- default:
- return ts.ScriptKind.Unknown;
- }
+ const ext = fileName.substr(fileName.lastIndexOf('.'));
+ switch (ext.toLowerCase()) {
+ case ts.Extension.Js:
+ return ts.ScriptKind.JS;
+ case ts.Extension.Jsx:
+ return ts.ScriptKind.JSX;
+ case ts.Extension.Ts:
+ return ts.ScriptKind.TS;
+ case ts.Extension.Tsx:
+ return ts.ScriptKind.TSX;
+ case ts.Extension.Json:
+ return ts.ScriptKind.JSON;
+ default:
+ return ts.ScriptKind.Unknown;
+ }
}
export function isAstroFilePath(filePath: string) {
- return filePath.endsWith('.astro');
+ return filePath.endsWith('.astro');
}
export function isVirtualAstroFilePath(filePath: string) {
- return filePath.endsWith('.astro.ts');
+ return filePath.endsWith('.astro.ts');
}
export function toVirtualAstroFilePath(filePath: string) {
- return `${filePath}.ts`;
+ return `${filePath}.ts`;
}
export function toRealAstroFilePath(filePath: string) {
- return filePath.slice(0, -'.ts'.length);
+ return filePath.slice(0, -'.ts'.length);
}
export function ensureRealAstroFilePath(filePath: string) {
- return isVirtualAstroFilePath(filePath) ? toRealAstroFilePath(filePath) : filePath;
+ return isVirtualAstroFilePath(filePath) ? toRealAstroFilePath(filePath) : filePath;
}
export function findTsConfigPath(fileName: string, rootUris: string[]) {
- const searchDir = dirname(fileName);
- const path =
- ts.findConfigFile(searchDir, ts.sys.fileExists, 'tsconfig.json') ||
- ts.findConfigFile(searchDir, ts.sys.fileExists, 'jsconfig.json') ||
- '';
- // Don't return config files that exceed the current workspace context.
- return !!path && rootUris.some((rootUri) => isSubPath(rootUri, path)) ? path : '';
+ const searchDir = dirname(fileName);
+ const path = ts.findConfigFile(searchDir, ts.sys.fileExists, 'tsconfig.json') || ts.findConfigFile(searchDir, ts.sys.fileExists, 'jsconfig.json') || '';
+ // Don't return config files that exceed the current workspace context.
+ return !!path && rootUris.some((rootUri) => isSubPath(rootUri, path)) ? path : '';
}
/** */
export function isSubPath(uri: string, possibleSubPath: string): boolean {
- return pathToUrl(possibleSubPath).startsWith(uri);
+ return pathToUrl(possibleSubPath).startsWith(uri);
}
-
/** Substitutes */
export function substituteWithWhitespace(result: string, start: number, end: number, oldContent: string, before: string, after: string) {
- let accumulatedWS = 0;
- result += before;
- for (let i = start + before.length; i < end; i++) {
- let ch = oldContent[i];
- if (ch === '\n' || ch === '\r') {
- // only write new lines, skip the whitespace
- accumulatedWS = 0;
- result += ch;
- } else {
- accumulatedWS++;
- }
- }
- result = append(result, ' ', accumulatedWS - after.length);
- result += after;
- return result;
+ let accumulatedWS = 0;
+ result += before;
+ for (let i = start + before.length; i < end; i++) {
+ let ch = oldContent[i];
+ if (ch === '\n' || ch === '\r') {
+ // only write new lines, skip the whitespace
+ accumulatedWS = 0;
+ result += ch;
+ } else {
+ accumulatedWS++;
+ }
+ }
+ result = append(result, ' ', accumulatedWS - after.length);
+ result += after;
+ return result;
}
function append(result: string, str: string, n: number): string {
- while (n > 0) {
- if (n & 1) {
- result += str;
- }
- n >>= 1;
- str += str;
- }
- return result;
+ while (n > 0) {
+ if (n & 1) {
+ result += str;
+ }
+ n >>= 1;
+ str += str;
+ }
+ return result;
}
diff --git a/tools/vscode/packages/server/src/utils.ts b/tools/vscode/packages/server/src/utils.ts
index c764aae13..f9f1acf34 100644
--- a/tools/vscode/packages/server/src/utils.ts
+++ b/tools/vscode/packages/server/src/utils.ts
@@ -4,68 +4,61 @@ import { Node } from 'vscode-html-languageservice';
/** Normalizes a document URI */
export function normalizeUri(uri: string): string {
- return URI.parse(uri).toString();
+ return URI.parse(uri).toString();
}
/** Turns a URL into a normalized FS Path */
export function urlToPath(stringUrl: string): string | null {
- const url = URI.parse(stringUrl);
- if (url.scheme !== 'file') {
- return null;
- }
- return url.fsPath.replace(/\\/g, '/');
+ const url = URI.parse(stringUrl);
+ if (url.scheme !== 'file') {
+ return null;
+ }
+ return url.fsPath.replace(/\\/g, '/');
}
/** Converts a path to a URL */
export function pathToUrl(path: string) {
- return URI.file(path).toString();
+ return URI.file(path).toString();
}
-
/**
-*
-* The language service is case insensitive, and would provide
-* hover info for Svelte components like `Option` which have
-* the same name like a html tag.
-*/
+ *
+ * The language service is case insensitive, and would provide
+ * hover info for Svelte components like `Option` which have
+ * the same name like a html tag.
+ */
export function isPossibleComponent(node: Node): boolean {
- return !!node.tag?.[0].match(/[A-Z]/);
+ return !!node.tag?.[0].match(/[A-Z]/);
}
/**
-*
-* The language service is case insensitive, and would provide
-* hover info for Svelte components like `Option` which have
-* the same name like a html tag.
-*/
+ *
+ * The language service is case insensitive, and would provide
+ * hover info for Svelte components like `Option` which have
+ * the same name like a html tag.
+ */
export function isPossibleClientComponent(node: Node): boolean {
- return isPossibleComponent(node) && (node.tag?.indexOf(':') ?? -1) > -1;
+ return isPossibleComponent(node) && (node.tag?.indexOf(':') ?? -1) > -1;
}
/** Flattens an array */
export function flatten<T>(arr: T[][]): T[] {
- return arr.reduce((all, item) => [...all, ...item], []);
+ return arr.reduce((all, item) => [...all, ...item], []);
}
/** Clamps a number between min and max */
export function clamp(num: number, min: number, max: number): number {
- return Math.max(min, Math.min(max, num));
+ return Math.max(min, Math.min(max, num));
}
/** Checks if a position is inside range */
export function isInRange(positionToTest: Position, range: Range): boolean {
- return (
- isBeforeOrEqualToPosition(range.end, positionToTest) &&
- isBeforeOrEqualToPosition(positionToTest, range.start)
- );
+ return isBeforeOrEqualToPosition(range.end, positionToTest) && isBeforeOrEqualToPosition(positionToTest, range.start);
}
/** */
export function isBeforeOrEqualToPosition(position: Position, positionToTest: Position): boolean {
- return (
- positionToTest.line < position.line ||
- (positionToTest.line === position.line && positionToTest.character <= position.character)
- );
+ return positionToTest.line < position.line || (positionToTest.line === position.line && positionToTest.character <= position.character);
}
/**
@@ -76,23 +69,19 @@ export function isBeforeOrEqualToPosition(position: Position, positionToTest: Po
* @param determineIfSame The function which determines if the previous invocation should be canceld or not
* @param miliseconds Number of miliseconds to debounce
*/
-export function debounceSameArg<T>(
- fn: (arg: T) => void,
- shouldCancelPrevious: (newArg: T, prevArg?: T) => boolean,
- miliseconds: number
-): (arg: T) => void {
- let timeout: any;
- let prevArg: T | undefined;
+export function debounceSameArg<T>(fn: (arg: T) => void, shouldCancelPrevious: (newArg: T, prevArg?: T) => boolean, miliseconds: number): (arg: T) => void {
+ let timeout: any;
+ let prevArg: T | undefined;
- return (arg: T) => {
- if (shouldCancelPrevious(arg, prevArg)) {
- clearTimeout(timeout);
- }
+ return (arg: T) => {
+ if (shouldCancelPrevious(arg, prevArg)) {
+ clearTimeout(timeout);
+ }
- prevArg = arg;
- timeout = setTimeout(() => {
- fn(arg);
- prevArg = undefined;
- }, miliseconds);
- };
+ prevArg = arg;
+ timeout = setTimeout(() => {
+ fn(arg);
+ prevArg = undefined;
+ }, miliseconds);
+ };
}
diff --git a/tools/vscode/packages/server/tsconfig.json b/tools/vscode/packages/server/tsconfig.json
index 6af42d0b2..7b2ff1ea2 100644
--- a/tools/vscode/packages/server/tsconfig.json
+++ b/tools/vscode/packages/server/tsconfig.json
@@ -1,9 +1,9 @@
{
- "extends": "../../tsconfig.base.json",
- "compilerOptions": {
- "outDir": "dist",
- "rootDir": "src",
- },
- "include": ["src"],
- "exclude": ["node_modules"],
+ "extends": "../../tsconfig.base.json",
+ "compilerOptions": {
+ "outDir": "dist",
+ "rootDir": "src"
+ },
+ "include": ["src"],
+ "exclude": ["node_modules"]
}
diff --git a/tools/vscode/syntaxes/astro.tmLanguage.json b/tools/vscode/syntaxes/astro.tmLanguage.json
index 9c09d0e71..225b023ca 100644
--- a/tools/vscode/syntaxes/astro.tmLanguage.json
+++ b/tools/vscode/syntaxes/astro.tmLanguage.json
@@ -1,704 +1,706 @@
{
- "fileTypes": [
- "astro"
- ],
- "foldingStartMarker": "(?x)\n(<(?i:head|body|table|thead|tbody|tfoot|tr|div|select|fieldset|style|script|ul|ol|li|form|dl)\\b.*?>\n|<!--(?!.*--\\s*>)\n|^<!--\\ \\#tminclude\\ (?>.*?-->)$\n|<\\?(?:php)?.*\\b(if|for(each)?|while)\\b.+:\n|\\{\\{?(if|foreach|capture|literal|foreach|php|section|strip)\n|\\{\\s*($|\\?>\\s*$|\/\/|\/\\*(.*\\*\/\\s*$|(?!.*?\\*\/)))\n)",
- "foldingStopMarker": "(?x)\n(<\/(?i:head|body|table|thead|tbody|tfoot|tr|div|select|fieldset|style|script|ul|ol|li|form|dl)>\n|^(?!.*?<!--).*?--\\s*>\n|^<!--\\ end\\ tminclude\\ -->$\n|<\\?(?:php)?.*\\bend(if|for(each)?|while)\\b\n|\\{\\{?\/(if|foreach|capture|literal|foreach|php|section|strip)\n|^[^{]*\\}\n)",
- "keyEquivalent": "^~H",
- "name": "Astro",
- "patterns": [
- {
- "include": "#astro-expressions"
- },
- {
- "begin": "(<)([a-zA-Z0-9:-]++)(?=[^>]*><\/\\2>)",
- "beginCaptures": {
- "1": {
- "name": "punctuation.definition.tag.begin.html"
- },
- "2": {
- "name": "entity.name.tag.html"
- }
- },
- "end": "(>)(<)(\/)(\\2)(>)",
- "endCaptures": {
- "1": {
- "name": "punctuation.definition.tag.end.html"
- },
- "2": {
- "name": "punctuation.definition.tag.begin.html meta.scope.between-tag-pair.html"
- },
- "3": {
- "name": "punctuation.definition.tag.begin.html"
- },
- "4": {
- "name": "entity.name.tag.html"
- },
- "5": {
- "name": "punctuation.definition.tag.end.html"
- }
- },
- "name": "meta.tag.any.html",
- "patterns": [
- {
- "include": "#tag-stuff"
- }
- ]
- },
- {
- "begin": "(<\\?)(xml)",
- "captures": {
- "1": {
- "name": "punctuation.definition.tag.html"
- },
- "2": {
- "name": "entity.name.tag.xml.html"
- }
- },
- "end": "(\\?>)",
- "name": "meta.tag.preprocessor.xml.html",
- "patterns": [
- {
- "include": "#tag-generic-attribute"
- },
- {
- "include": "#string-double-quoted"
- },
- {
- "include": "#string-single-quoted"
- }
- ]
- },
- {
- "begin": "<!--",
- "captures": [
- {
- "name": "punctuation.definition.comment.html"
- }
- ],
- "end": "--\\s*>",
- "name": "comment.block.html",
- "patterns": [
- {
- "match": "--",
- "name": "invalid.illegal.bad-comments-or-CDATA.html"
- }
- ]
- },
- {
- "begin": "<!",
- "captures": [
- {
- "name": "punctuation.definition.tag.html"
- }
- ],
- "end": ">",
- "name": "meta.tag.sgml.html",
- "patterns": [
- {
- "begin": "(?i:DOCTYPE|doctype)",
- "captures": {
- "1": {
- "name": "entity.name.tag.doctype.html"
- }
- },
- "end": "(?=>)",
- "name": "meta.tag.sgml.doctype.html",
- "patterns": [
- {
- "match": "\"[^\">]*\"",
- "name": "string.quoted.double.doctype.identifiers-and-DTDs.html"
- }
- ]
- },
- {
- "begin": "\\[CDATA\\[",
- "end": "]](?=>)",
- "name": "constant.other.inline-data.html"
- },
- {
- "match": "(\\s*)(?!--|>)\\S(\\s*)",
- "name": "invalid.illegal.bad-comments-or-CDATA.html"
- }
- ]
- },
- {
- "begin": "(?:^\\s+)?(<)((?i:style))\\b(?=[^>]*lang=(['\"])css\\1?)",
- "captures": {
- "1": {
- "name": "punctuation.definition.tag.begin.html"
- },
- "2": {
- "name": "entity.name.tag.style.html"
- },
- "3": {
- "name": "punctuation.definition.tag.html"
- }
- },
- "end": "(<\/)((?i:style))(>)(?:\\s*\\n)?",
- "name": "source.css.embedded.html",
- "patterns": [
- {
- "include": "#tag-stuff"
- },
- {
- "begin": "(>)",
- "beginCaptures": {
- "1": {
- "name": "punctuation.definition.tag.end.html"
- }
- },
- "end": "(?=<\/(?i:style))",
- "patterns": [
- {
- "include": "source.css"
- }
- ]
- }
- ]
- },
- {
- "begin": "(?:^\\s+)?(<)((?i:style))\\b(?=[^>]*lang=(['\"])sass\\1?)",
- "captures": {
- "1": {
- "name": "punctuation.definition.tag.begin.html"
- },
- "2": {
- "name": "entity.name.tag.style.html"
- },
- "3": {
- "name": "punctuation.definition.tag.html"
- }
- },
- "end": "(<\/)((?i:style))(>)(?:\\s*\\n)?",
- "name": "source.sass.embedded.html",
- "patterns": [
- {
- "include": "#tag-stuff"
- },
- {
- "begin": "(>)",
- "beginCaptures": {
- "1": {
- "name": "punctuation.definition.tag.end.html"
- }
- },
- "end": "(?=<\/(?i:style))",
- "patterns": [
- {
- "include": "source.sass"
- }
- ]
- }
- ]
- },
- {
- "begin": "(?:^\\s+)?(<)((?i:style))\\b(?=[^>]*lang=(['\"])scss\\1?)",
- "captures": {
- "1": {
- "name": "punctuation.definition.tag.begin.html"
- },
- "2": {
- "name": "entity.name.tag.style.html"
- },
- "3": {
- "name": "punctuation.definition.tag.html"
- }
- },
- "end": "(<\/)((?i:style))(>)(?:\\s*\\n)?",
- "name": "source.scss.embedded.html",
- "patterns": [
- {
- "include": "#tag-stuff"
- },
- {
- "begin": "(>)",
- "beginCaptures": {
- "1": {
- "name": "punctuation.definition.tag.end.html"
- }
- },
- "end": "(?=<\/(?i:style))",
- "patterns": [
- {
- "include": "source.css.scss"
- }
- ]
- }
- ]
- },
- {
- "begin": "(?:^\\s+)?(<)((?i:style))\\b(?![^>]*\/>)",
- "captures": {
- "1": {
- "name": "punctuation.definition.tag.begin.html"
- },
- "2": {
- "name": "entity.name.tag.style.html"
- },
- "3": {
- "name": "punctuation.definition.tag.html"
- }
- },
- "end": "(<\/)((?i:style))(>)(?:\\s*\\n)?",
- "__DEFAULT_STYLE_NAME_START__": null,"name": "source.css.embedded.html","__DEFAULT_STYLE_NAME_END__": null,
- "patterns": [
- {
- "include": "#tag-stuff"
- },
- {
- "begin": "(>)",
- "beginCaptures": {
- "1": {
- "name": "punctuation.definition.tag.end.html"
- }
- },
- "end": "(?=<\/(?i:style))",
- "patterns": [
- {
- "__DEFAULT_STYLE_INCLUDE_START__": null,"include": "source.css","__DEFAULT_STYLE_INCLUDE_END__": null
- }
- ]
- }
- ]
- },
- {
- "begin": "(?:^\\s+)?(<)((?i:script))\\b(?=[^>]*lang=(['\"])tsx\\1?)",
- "beginCaptures": {
- "1": {
- "name": "punctuation.definition.tag.begin.html"
- },
- "2": {
- "name": "entity.name.tag.script.html"
- }
- },
- "end": "(?<=<\/(script|SCRIPT))(>)(?:\\s*\\n)?",
- "endCaptures": {
- "2": {
- "name": "punctuation.definition.tag.html"
- }
- },
- "name": "source.tsx.embedded.html",
- "patterns": [
- {
- "include": "#tag-stuff"
- },
- {
- "begin": "(?<!<\/(?:script|SCRIPT))(>)",
- "captures": {
- "1": {
- "name": "punctuation.definition.tag.begin.html"
- },
- "2": {
- "name": "entity.name.tag.script.html"
- }
- },
- "end": "(<\/)((?i:script))",
- "patterns": [
- {
- "include": "source.tsx"
- }
- ]
- }
- ]
- },
- {
- "begin": "(?:^\\s+)?(<)((?i:script))\\b(?=[^>]*lang=(['\"])ts\\1?)",
- "beginCaptures": {
- "1": {
- "name": "punctuation.definition.tag.begin.html"
- },
- "2": {
- "name": "entity.name.tag.script.html"
- }
- },
- "end": "(?<=<\/(script|SCRIPT))(>)(?:\\s*\\n)?",
- "endCaptures": {
- "2": {
- "name": "punctuation.definition.tag.html"
- }
- },
- "name": "source.tsx.embedded.html",
- "patterns": [
- {
- "include": "#tag-stuff"
- },
- {
- "begin": "(?<!<\/(?:script|SCRIPT))(>)",
- "captures": {
- "1": {
- "name": "punctuation.definition.tag.begin.html"
- },
- "2": {
- "name": "entity.name.tag.script.html"
- }
- },
- "end": "(<\/)((?i:script))",
- "patterns": [
- {
- "include": "source.tsx"
- }
- ]
- }
- ]
- },
- {
- "begin": "(<)((?i:script))\\b(?![^>]*\/>)(?![^>]*(?i:type.?=.?text\/((?!javascript|babel|ecmascript).*)))",
- "beginCaptures": {
- "1": {
- "name": "punctuation.definition.tag.begin.html"
- },
- "2": {
- "name": "entity.name.tag.script.html"
- }
- },
- "end": "(?<=<\/(script|SCRIPT))(>)(?:\\s*\\n)?",
- "endCaptures": {
- "2": {
- "name": "punctuation.definition.tag.html"
- }
- },
- "name": "source.tsx.embedded.html",
- "patterns": [
- {
- "include": "#tag-stuff"
- },
- {
- "begin": "(?<!<\/(?:script|SCRIPT))(>)",
- "captures": {
- "1": {
- "name": "punctuation.definition.tag.begin.html"
- },
- "2": {
- "name": "entity.name.tag.script.html"
- }
- },
- "end": "(<\/)((?i:script))",
- "patterns": [
- {
- "captures": {
- "1": {
- "name": "punctuation.definition.comment.js"
- }
- },
- "match": "(\/\/).*?((?=<\/script)|$\\n?)",
- "name": "comment.line.double-slash.js"
- },
- {
- "begin": "\/\\*",
- "captures": [
- {
- "name": "punctuation.definition.comment.js"
- }
- ],
- "end": "\\*\/|(?=<\/script)",
- "name": "comment.block.js"
- },
- {
- "include": "source.tsx"
- }
- ]
- }
- ]
- },
- {
- "begin": "(<\/?)((?i:body|head|html)\\b)",
- "captures": {
- "1": {
- "name": "punctuation.definition.tag.begin.html"
- },
- "2": {
- "name": "entity.name.tag.structure.any.html"
- }
- },
- "end": "(>)",
- "endCaptures": {
- "1": {
- "name": "punctuation.definition.tag.end.html"
- }
- },
- "name": "meta.tag.structure.any.html",
- "patterns": [
- {
- "include": "#tag-stuff"
- }
- ]
- },
- {
- "begin": "(<\/?)((?i:address|blockquote|dd|div|dl|dt|fieldset|form|frame|frameset|h1|h2|h3|h4|h5|h6|iframe|noframes|object|ol|p|ul|applet|center|dir|hr|menu|pre)\\b)",
- "beginCaptures": {
- "1": {
- "name": "punctuation.definition.tag.begin.html"
- },
- "2": {
- "name": "entity.name.tag.block.any.html"
- }
- },
- "end": "(>)",
- "endCaptures": {
- "1": {
- "name": "punctuation.definition.tag.end.html"
- }
- },
- "name": "meta.tag.block.any.html",
- "patterns": [
- {
- "include": "#tag-stuff"
- }
- ]
- },
- {
- "begin": "(<\/?)((?i:a|abbr|acronym|area|b|base|basefont|bdo|big|br|button|caption|cite|code|col|colgroup|del|dfn|em|font|head|html|i|img|input|ins|isindex|kbd|label|legend|li|link|map|meta|noscript|optgroup|option|param|q|s|samp|script|select|small|span|strike|strong|style|sub|sup|table|tbody|td|textarea|tfoot|th|thead|title|tr|tt|u|var)\\b)",
- "beginCaptures": {
- "1": {
- "name": "punctuation.definition.tag.begin.html"
- },
- "2": {
- "name": "entity.name.tag.inline.any.html"
- }
- },
- "end": "((?: ?\/)?>)",
- "endCaptures": {
- "1": {
- "name": "punctuation.definition.tag.end.html"
- }
- },
- "name": "meta.tag.inline.any.html",
- "patterns": [
- {
- "include": "#tag-stuff"
- }
- ]
- },
- {
- "begin": "(<\/?)([a-zA-Z0-9:-]+)",
- "beginCaptures": {
- "1": {
- "name": "punctuation.definition.tag.begin.html"
- },
- "2": {
- "name": "entity.name.tag.other.html"
- }
- },
- "end": "(/?>)",
- "endCaptures": {
- "1": {
- "name": "punctuation.definition.tag.end.html"
- }
- },
- "name": "meta.tag.other.html",
- "patterns": [
- {
- "include": "#tag-stuff"
- }
- ]
- },
- {
- "include": "#entities"
- },
- {
- "include": "#frontmatter"
- },
- {
- "match": "<>",
- "name": "invalid.illegal.incomplete.html"
- },
- {
- "match": "<",
- "name": "invalid.illegal.bad-angle-bracket.html"
- }
- ],
- "repository": {
- "frontmatter": {
- "begin": "\\A(-{3})\\s*$",
- "beginCaptures": {
- "1": {
- "name": "comment.block.html"
- }
- },
- "contentName": "meta.embedded.block.frontmatter",
- "patterns": [
- {
- "include": "source.tsx"
- }
- ],
- "end": "(^|\\G)(-{3})|\\.{3}\\s*$",
- "endCaptures": {
- "2": {
- "name": "comment.block.html"
- }
- }
- },
- "entities": {
- "patterns": [
- {
- "captures": {
- "1": {
- "name": "punctuation.definition.entity.html"
- },
- "3": {
- "name": "punctuation.definition.entity.html"
- }
- },
- "match": "(&)([a-zA-Z0-9]+|#[0-9]+|#x[0-9a-fA-F]+)(;)",
- "name": "constant.character.entity.html"
- },
- {
- "match": "&",
- "name": "invalid.illegal.bad-ampersand.html"
- }
- ]
- },
- "string-double-quoted": {
- "begin": "\"",
- "beginCaptures": [
- {
- "name": "punctuation.definition.string.begin.html"
- }
- ],
- "end": "\"",
- "endCaptures": [
- {
- "name": "punctuation.definition.string.end.html"
- }
- ],
- "name": "string.quoted.double.html",
- "patterns": [
- {
- "include": "#entities"
- }
- ]
- },
- "string-single-quoted": {
- "begin": "'",
- "beginCaptures": [
- {
- "name": "punctuation.definition.string.begin.html"
- }
- ],
- "end": "'",
- "endCaptures": [
- {
- "name": "punctuation.definition.string.end.html"
- }
- ],
- "name": "string.quoted.single.html",
- "patterns": [
- {
- "include": "#entities"
- }
- ]
- },
- "tag-generic-attribute": {
- "match": "\\b([a-zA-Z\\-:]+)",
- "name": "entity.other.attribute-name.html"
- },
- "tag-id-attribute": {
- "begin": "\\b(id)\\b\\s*(=)",
- "captures": {
- "1": {
- "name": "entity.other.attribute-name.id.html"
- },
- "2": {
- "name": "punctuation.separator.key-value.html"
- }
- },
- "end": "(?<='|\")",
- "name": "meta.attribute-with-value.id.html",
- "patterns": [
- {
- "begin": "\"",
- "beginCaptures": [
- {
- "name": "punctuation.definition.string.begin.html"
- }
- ],
- "contentName": "meta.toc-list.id.html",
- "end": "\"",
- "endCaptures": [
- {
- "name": "punctuation.definition.string.end.html"
- }
- ],
- "name": "string.quoted.double.html",
- "patterns": [
- {
- "include": "#astro-expressions"
- },
- {
- "include": "#entities"
- }
- ]
- },
- {
- "begin": "'",
- "beginCaptures": [
- {
- "name": "punctuation.definition.string.begin.html"
- }
- ],
- "contentName": "meta.toc-list.id.html",
- "end": "'",
- "endCaptures": [
- {
- "name": "punctuation.definition.string.end.html"
- }
- ],
- "name": "string.quoted.single.html",
- "patterns": [
- {
- "include": "#astro-expressions"
- },
- {
- "include": "#entities"
- }
- ]
- }
- ]
- },
- "tag-stuff": {
- "patterns": [
- {
- "include": "#tag-id-attribute"
- },
- {
- "include": "#tag-generic-attribute"
- },
- {
- "include": "#string-double-quoted"
- },
- {
- "include": "#string-single-quoted"
- },
- {
- "include": "#astro-expressions"
- }
- ]
- },
- "astro-expressions": {
- "patterns": [
- {
- "begin": "\\{",
- "beginCaptures": [
- {
- "name": "punctuation.definition.generic.begin.html"
- }
- ],
- "end": "\\}",
- "endCaptures": [
- {
- "name": "punctuation.definition.generic.end.html"
- }
- ],
- "name": "expression.embbeded.astro",
- "patterns": [
- {
- "include": "source.tsx"
- }
- ]
- }
- ]
- }
- },
- "scopeName": "text.html.astro"
+ "fileTypes": ["astro"],
+ "foldingStartMarker": "(?x)\n(<(?i:head|body|table|thead|tbody|tfoot|tr|div|select|fieldset|style|script|ul|ol|li|form|dl)\\b.*?>\n|<!--(?!.*--\\s*>)\n|^<!--\\ \\#tminclude\\ (?>.*?-->)$\n|<\\?(?:php)?.*\\b(if|for(each)?|while)\\b.+:\n|\\{\\{?(if|foreach|capture|literal|foreach|php|section|strip)\n|\\{\\s*($|\\?>\\s*$|//|/\\*(.*\\*/\\s*$|(?!.*?\\*/)))\n)",
+ "foldingStopMarker": "(?x)\n(</(?i:head|body|table|thead|tbody|tfoot|tr|div|select|fieldset|style|script|ul|ol|li|form|dl)>\n|^(?!.*?<!--).*?--\\s*>\n|^<!--\\ end\\ tminclude\\ -->$\n|<\\?(?:php)?.*\\bend(if|for(each)?|while)\\b\n|\\{\\{?/(if|foreach|capture|literal|foreach|php|section|strip)\n|^[^{]*\\}\n)",
+ "keyEquivalent": "^~H",
+ "name": "Astro",
+ "patterns": [
+ {
+ "include": "#astro-expressions"
+ },
+ {
+ "begin": "(<)([a-zA-Z0-9:-]++)(?=[^>]*></\\2>)",
+ "beginCaptures": {
+ "1": {
+ "name": "punctuation.definition.tag.begin.html"
+ },
+ "2": {
+ "name": "entity.name.tag.html"
+ }
+ },
+ "end": "(>)(<)(/)(\\2)(>)",
+ "endCaptures": {
+ "1": {
+ "name": "punctuation.definition.tag.end.html"
+ },
+ "2": {
+ "name": "punctuation.definition.tag.begin.html meta.scope.between-tag-pair.html"
+ },
+ "3": {
+ "name": "punctuation.definition.tag.begin.html"
+ },
+ "4": {
+ "name": "entity.name.tag.html"
+ },
+ "5": {
+ "name": "punctuation.definition.tag.end.html"
+ }
+ },
+ "name": "meta.tag.any.html",
+ "patterns": [
+ {
+ "include": "#tag-stuff"
+ }
+ ]
+ },
+ {
+ "begin": "(<\\?)(xml)",
+ "captures": {
+ "1": {
+ "name": "punctuation.definition.tag.html"
+ },
+ "2": {
+ "name": "entity.name.tag.xml.html"
+ }
+ },
+ "end": "(\\?>)",
+ "name": "meta.tag.preprocessor.xml.html",
+ "patterns": [
+ {
+ "include": "#tag-generic-attribute"
+ },
+ {
+ "include": "#string-double-quoted"
+ },
+ {
+ "include": "#string-single-quoted"
+ }
+ ]
+ },
+ {
+ "begin": "<!--",
+ "captures": [
+ {
+ "name": "punctuation.definition.comment.html"
+ }
+ ],
+ "end": "--\\s*>",
+ "name": "comment.block.html",
+ "patterns": [
+ {
+ "match": "--",
+ "name": "invalid.illegal.bad-comments-or-CDATA.html"
+ }
+ ]
+ },
+ {
+ "begin": "<!",
+ "captures": [
+ {
+ "name": "punctuation.definition.tag.html"
+ }
+ ],
+ "end": ">",
+ "name": "meta.tag.sgml.html",
+ "patterns": [
+ {
+ "begin": "(?i:DOCTYPE|doctype)",
+ "captures": {
+ "1": {
+ "name": "entity.name.tag.doctype.html"
+ }
+ },
+ "end": "(?=>)",
+ "name": "meta.tag.sgml.doctype.html",
+ "patterns": [
+ {
+ "match": "\"[^\">]*\"",
+ "name": "string.quoted.double.doctype.identifiers-and-DTDs.html"
+ }
+ ]
+ },
+ {
+ "begin": "\\[CDATA\\[",
+ "end": "]](?=>)",
+ "name": "constant.other.inline-data.html"
+ },
+ {
+ "match": "(\\s*)(?!--|>)\\S(\\s*)",
+ "name": "invalid.illegal.bad-comments-or-CDATA.html"
+ }
+ ]
+ },
+ {
+ "begin": "(?:^\\s+)?(<)((?i:style))\\b(?=[^>]*lang=(['\"])css\\1?)",
+ "captures": {
+ "1": {
+ "name": "punctuation.definition.tag.begin.html"
+ },
+ "2": {
+ "name": "entity.name.tag.style.html"
+ },
+ "3": {
+ "name": "punctuation.definition.tag.html"
+ }
+ },
+ "end": "(</)((?i:style))(>)(?:\\s*\\n)?",
+ "name": "source.css.embedded.html",
+ "patterns": [
+ {
+ "include": "#tag-stuff"
+ },
+ {
+ "begin": "(>)",
+ "beginCaptures": {
+ "1": {
+ "name": "punctuation.definition.tag.end.html"
+ }
+ },
+ "end": "(?=</(?i:style))",
+ "patterns": [
+ {
+ "include": "source.css"
+ }
+ ]
+ }
+ ]
+ },
+ {
+ "begin": "(?:^\\s+)?(<)((?i:style))\\b(?=[^>]*lang=(['\"])sass\\1?)",
+ "captures": {
+ "1": {
+ "name": "punctuation.definition.tag.begin.html"
+ },
+ "2": {
+ "name": "entity.name.tag.style.html"
+ },
+ "3": {
+ "name": "punctuation.definition.tag.html"
+ }
+ },
+ "end": "(</)((?i:style))(>)(?:\\s*\\n)?",
+ "name": "source.sass.embedded.html",
+ "patterns": [
+ {
+ "include": "#tag-stuff"
+ },
+ {
+ "begin": "(>)",
+ "beginCaptures": {
+ "1": {
+ "name": "punctuation.definition.tag.end.html"
+ }
+ },
+ "end": "(?=</(?i:style))",
+ "patterns": [
+ {
+ "include": "source.sass"
+ }
+ ]
+ }
+ ]
+ },
+ {
+ "begin": "(?:^\\s+)?(<)((?i:style))\\b(?=[^>]*lang=(['\"])scss\\1?)",
+ "captures": {
+ "1": {
+ "name": "punctuation.definition.tag.begin.html"
+ },
+ "2": {
+ "name": "entity.name.tag.style.html"
+ },
+ "3": {
+ "name": "punctuation.definition.tag.html"
+ }
+ },
+ "end": "(</)((?i:style))(>)(?:\\s*\\n)?",
+ "name": "source.scss.embedded.html",
+ "patterns": [
+ {
+ "include": "#tag-stuff"
+ },
+ {
+ "begin": "(>)",
+ "beginCaptures": {
+ "1": {
+ "name": "punctuation.definition.tag.end.html"
+ }
+ },
+ "end": "(?=</(?i:style))",
+ "patterns": [
+ {
+ "include": "source.css.scss"
+ }
+ ]
+ }
+ ]
+ },
+ {
+ "begin": "(?:^\\s+)?(<)((?i:style))\\b(?![^>]*/>)",
+ "captures": {
+ "1": {
+ "name": "punctuation.definition.tag.begin.html"
+ },
+ "2": {
+ "name": "entity.name.tag.style.html"
+ },
+ "3": {
+ "name": "punctuation.definition.tag.html"
+ }
+ },
+ "end": "(</)((?i:style))(>)(?:\\s*\\n)?",
+ "__DEFAULT_STYLE_NAME_START__": null,
+ "name": "source.css.embedded.html",
+ "__DEFAULT_STYLE_NAME_END__": null,
+ "patterns": [
+ {
+ "include": "#tag-stuff"
+ },
+ {
+ "begin": "(>)",
+ "beginCaptures": {
+ "1": {
+ "name": "punctuation.definition.tag.end.html"
+ }
+ },
+ "end": "(?=</(?i:style))",
+ "patterns": [
+ {
+ "__DEFAULT_STYLE_INCLUDE_START__": null,
+ "include": "source.css",
+ "__DEFAULT_STYLE_INCLUDE_END__": null
+ }
+ ]
+ }
+ ]
+ },
+ {
+ "begin": "(?:^\\s+)?(<)((?i:script))\\b(?=[^>]*lang=(['\"])tsx\\1?)",
+ "beginCaptures": {
+ "1": {
+ "name": "punctuation.definition.tag.begin.html"
+ },
+ "2": {
+ "name": "entity.name.tag.script.html"
+ }
+ },
+ "end": "(?<=</(script|SCRIPT))(>)(?:\\s*\\n)?",
+ "endCaptures": {
+ "2": {
+ "name": "punctuation.definition.tag.html"
+ }
+ },
+ "name": "source.tsx.embedded.html",
+ "patterns": [
+ {
+ "include": "#tag-stuff"
+ },
+ {
+ "begin": "(?<!</(?:script|SCRIPT))(>)",
+ "captures": {
+ "1": {
+ "name": "punctuation.definition.tag.begin.html"
+ },
+ "2": {
+ "name": "entity.name.tag.script.html"
+ }
+ },
+ "end": "(</)((?i:script))",
+ "patterns": [
+ {
+ "include": "source.tsx"
+ }
+ ]
+ }
+ ]
+ },
+ {
+ "begin": "(?:^\\s+)?(<)((?i:script))\\b(?=[^>]*lang=(['\"])ts\\1?)",
+ "beginCaptures": {
+ "1": {
+ "name": "punctuation.definition.tag.begin.html"
+ },
+ "2": {
+ "name": "entity.name.tag.script.html"
+ }
+ },
+ "end": "(?<=</(script|SCRIPT))(>)(?:\\s*\\n)?",
+ "endCaptures": {
+ "2": {
+ "name": "punctuation.definition.tag.html"
+ }
+ },
+ "name": "source.tsx.embedded.html",
+ "patterns": [
+ {
+ "include": "#tag-stuff"
+ },
+ {
+ "begin": "(?<!</(?:script|SCRIPT))(>)",
+ "captures": {
+ "1": {
+ "name": "punctuation.definition.tag.begin.html"
+ },
+ "2": {
+ "name": "entity.name.tag.script.html"
+ }
+ },
+ "end": "(</)((?i:script))",
+ "patterns": [
+ {
+ "include": "source.tsx"
+ }
+ ]
+ }
+ ]
+ },
+ {
+ "begin": "(<)((?i:script))\\b(?![^>]*/>)(?![^>]*(?i:type.?=.?text/((?!javascript|babel|ecmascript).*)))",
+ "beginCaptures": {
+ "1": {
+ "name": "punctuation.definition.tag.begin.html"
+ },
+ "2": {
+ "name": "entity.name.tag.script.html"
+ }
+ },
+ "end": "(?<=</(script|SCRIPT))(>)(?:\\s*\\n)?",
+ "endCaptures": {
+ "2": {
+ "name": "punctuation.definition.tag.html"
+ }
+ },
+ "name": "source.tsx.embedded.html",
+ "patterns": [
+ {
+ "include": "#tag-stuff"
+ },
+ {
+ "begin": "(?<!</(?:script|SCRIPT))(>)",
+ "captures": {
+ "1": {
+ "name": "punctuation.definition.tag.begin.html"
+ },
+ "2": {
+ "name": "entity.name.tag.script.html"
+ }
+ },
+ "end": "(</)((?i:script))",
+ "patterns": [
+ {
+ "captures": {
+ "1": {
+ "name": "punctuation.definition.comment.js"
+ }
+ },
+ "match": "(//).*?((?=</script)|$\\n?)",
+ "name": "comment.line.double-slash.js"
+ },
+ {
+ "begin": "/\\*",
+ "captures": [
+ {
+ "name": "punctuation.definition.comment.js"
+ }
+ ],
+ "end": "\\*/|(?=</script)",
+ "name": "comment.block.js"
+ },
+ {
+ "include": "source.tsx"
+ }
+ ]
+ }
+ ]
+ },
+ {
+ "begin": "(</?)((?i:body|head|html)\\b)",
+ "captures": {
+ "1": {
+ "name": "punctuation.definition.tag.begin.html"
+ },
+ "2": {
+ "name": "entity.name.tag.structure.any.html"
+ }
+ },
+ "end": "(>)",
+ "endCaptures": {
+ "1": {
+ "name": "punctuation.definition.tag.end.html"
+ }
+ },
+ "name": "meta.tag.structure.any.html",
+ "patterns": [
+ {
+ "include": "#tag-stuff"
+ }
+ ]
+ },
+ {
+ "begin": "(</?)((?i:address|blockquote|dd|div|dl|dt|fieldset|form|frame|frameset|h1|h2|h3|h4|h5|h6|iframe|noframes|object|ol|p|ul|applet|center|dir|hr|menu|pre)\\b)",
+ "beginCaptures": {
+ "1": {
+ "name": "punctuation.definition.tag.begin.html"
+ },
+ "2": {
+ "name": "entity.name.tag.block.any.html"
+ }
+ },
+ "end": "(>)",
+ "endCaptures": {
+ "1": {
+ "name": "punctuation.definition.tag.end.html"
+ }
+ },
+ "name": "meta.tag.block.any.html",
+ "patterns": [
+ {
+ "include": "#tag-stuff"
+ }
+ ]
+ },
+ {
+ "begin": "(</?)((?i:a|abbr|acronym|area|b|base|basefont|bdo|big|br|button|caption|cite|code|col|colgroup|del|dfn|em|font|head|html|i|img|input|ins|isindex|kbd|label|legend|li|link|map|meta|noscript|optgroup|option|param|q|s|samp|script|select|small|span|strike|strong|style|sub|sup|table|tbody|td|textarea|tfoot|th|thead|title|tr|tt|u|var)\\b)",
+ "beginCaptures": {
+ "1": {
+ "name": "punctuation.definition.tag.begin.html"
+ },
+ "2": {
+ "name": "entity.name.tag.inline.any.html"
+ }
+ },
+ "end": "((?: ?/)?>)",
+ "endCaptures": {
+ "1": {
+ "name": "punctuation.definition.tag.end.html"
+ }
+ },
+ "name": "meta.tag.inline.any.html",
+ "patterns": [
+ {
+ "include": "#tag-stuff"
+ }
+ ]
+ },
+ {
+ "begin": "(</?)([a-zA-Z0-9:-]+)",
+ "beginCaptures": {
+ "1": {
+ "name": "punctuation.definition.tag.begin.html"
+ },
+ "2": {
+ "name": "entity.name.tag.other.html"
+ }
+ },
+ "end": "(/?>)",
+ "endCaptures": {
+ "1": {
+ "name": "punctuation.definition.tag.end.html"
+ }
+ },
+ "name": "meta.tag.other.html",
+ "patterns": [
+ {
+ "include": "#tag-stuff"
+ }
+ ]
+ },
+ {
+ "include": "#entities"
+ },
+ {
+ "include": "#frontmatter"
+ },
+ {
+ "match": "<>",
+ "name": "invalid.illegal.incomplete.html"
+ },
+ {
+ "match": "<",
+ "name": "invalid.illegal.bad-angle-bracket.html"
+ }
+ ],
+ "repository": {
+ "frontmatter": {
+ "begin": "\\A(-{3})\\s*$",
+ "beginCaptures": {
+ "1": {
+ "name": "comment.block.html"
+ }
+ },
+ "contentName": "meta.embedded.block.frontmatter",
+ "patterns": [
+ {
+ "include": "source.tsx"
+ }
+ ],
+ "end": "(^|\\G)(-{3})|\\.{3}\\s*$",
+ "endCaptures": {
+ "2": {
+ "name": "comment.block.html"
+ }
+ }
+ },
+ "entities": {
+ "patterns": [
+ {
+ "captures": {
+ "1": {
+ "name": "punctuation.definition.entity.html"
+ },
+ "3": {
+ "name": "punctuation.definition.entity.html"
+ }
+ },
+ "match": "(&)([a-zA-Z0-9]+|#[0-9]+|#x[0-9a-fA-F]+)(;)",
+ "name": "constant.character.entity.html"
+ },
+ {
+ "match": "&",
+ "name": "invalid.illegal.bad-ampersand.html"
+ }
+ ]
+ },
+ "string-double-quoted": {
+ "begin": "\"",
+ "beginCaptures": [
+ {
+ "name": "punctuation.definition.string.begin.html"
+ }
+ ],
+ "end": "\"",
+ "endCaptures": [
+ {
+ "name": "punctuation.definition.string.end.html"
+ }
+ ],
+ "name": "string.quoted.double.html",
+ "patterns": [
+ {
+ "include": "#entities"
+ }
+ ]
+ },
+ "string-single-quoted": {
+ "begin": "'",
+ "beginCaptures": [
+ {
+ "name": "punctuation.definition.string.begin.html"
+ }
+ ],
+ "end": "'",
+ "endCaptures": [
+ {
+ "name": "punctuation.definition.string.end.html"
+ }
+ ],
+ "name": "string.quoted.single.html",
+ "patterns": [
+ {
+ "include": "#entities"
+ }
+ ]
+ },
+ "tag-generic-attribute": {
+ "match": "\\b([a-zA-Z\\-:]+)",
+ "name": "entity.other.attribute-name.html"
+ },
+ "tag-id-attribute": {
+ "begin": "\\b(id)\\b\\s*(=)",
+ "captures": {
+ "1": {
+ "name": "entity.other.attribute-name.id.html"
+ },
+ "2": {
+ "name": "punctuation.separator.key-value.html"
+ }
+ },
+ "end": "(?<='|\")",
+ "name": "meta.attribute-with-value.id.html",
+ "patterns": [
+ {
+ "begin": "\"",
+ "beginCaptures": [
+ {
+ "name": "punctuation.definition.string.begin.html"
+ }
+ ],
+ "contentName": "meta.toc-list.id.html",
+ "end": "\"",
+ "endCaptures": [
+ {
+ "name": "punctuation.definition.string.end.html"
+ }
+ ],
+ "name": "string.quoted.double.html",
+ "patterns": [
+ {
+ "include": "#astro-expressions"
+ },
+ {
+ "include": "#entities"
+ }
+ ]
+ },
+ {
+ "begin": "'",
+ "beginCaptures": [
+ {
+ "name": "punctuation.definition.string.begin.html"
+ }
+ ],
+ "contentName": "meta.toc-list.id.html",
+ "end": "'",
+ "endCaptures": [
+ {
+ "name": "punctuation.definition.string.end.html"
+ }
+ ],
+ "name": "string.quoted.single.html",
+ "patterns": [
+ {
+ "include": "#astro-expressions"
+ },
+ {
+ "include": "#entities"
+ }
+ ]
+ }
+ ]
+ },
+ "tag-stuff": {
+ "patterns": [
+ {
+ "include": "#tag-id-attribute"
+ },
+ {
+ "include": "#tag-generic-attribute"
+ },
+ {
+ "include": "#string-double-quoted"
+ },
+ {
+ "include": "#string-single-quoted"
+ },
+ {
+ "include": "#astro-expressions"
+ }
+ ]
+ },
+ "astro-expressions": {
+ "patterns": [
+ {
+ "begin": "\\{",
+ "beginCaptures": [
+ {
+ "name": "punctuation.definition.generic.begin.html"
+ }
+ ],
+ "end": "\\}",
+ "endCaptures": [
+ {
+ "name": "punctuation.definition.generic.end.html"
+ }
+ ],
+ "name": "expression.embbeded.astro",
+ "patterns": [
+ {
+ "include": "source.tsx"
+ }
+ ]
+ }
+ ]
+ }
+ },
+ "scopeName": "text.html.astro"
}
diff --git a/tools/vscode/tsconfig.base.json b/tools/vscode/tsconfig.base.json
index 8b7556d77..0f560706f 100644
--- a/tools/vscode/tsconfig.base.json
+++ b/tools/vscode/tsconfig.base.json
@@ -1,18 +1,17 @@
-
{
- "compilerOptions": {
- "target": "es2019",
- "lib": ["ESNext"],
- "module": "commonjs",
- "moduleResolution": "node",
- "sourceMap": true,
- "composite": true,
- "declaration": true,
- "strict": true,
- "skipLibCheck": true,
- "baseUrl": "./",
- "paths": {
- "@astro-vscode/*": ["packages/*/src"]
- },
- },
+ "compilerOptions": {
+ "target": "es2019",
+ "lib": ["ESNext"],
+ "module": "commonjs",
+ "moduleResolution": "node",
+ "sourceMap": true,
+ "composite": true,
+ "declaration": true,
+ "strict": true,
+ "skipLibCheck": true,
+ "baseUrl": "./",
+ "paths": {
+ "@astro-vscode/*": ["packages/*/src"]
+ }
+ }
}
diff --git a/tools/vscode/tsconfig.json b/tools/vscode/tsconfig.json
index 56c433fea..1791849bb 100644
--- a/tools/vscode/tsconfig.json
+++ b/tools/vscode/tsconfig.json
@@ -1,14 +1,13 @@
-
{
- "extends": "./tsconfig.base.json",
- "files": [],
- "include": [],
- "references": [
- {
- "path": "./packages/client"
- },
- {
- "path": "./packages/server"
- }
- ]
+ "extends": "./tsconfig.base.json",
+ "files": [],
+ "include": [],
+ "references": [
+ {
+ "path": "./packages/client"
+ },
+ {
+ "path": "./packages/server"
+ }
+ ]
}