Skip to content

Commit

Permalink
feat: Upgrade to Volar 2.2 (#863)
Browse files Browse the repository at this point in the history
* feat: Upgrade to Volar 2.2

* format

* Add "Reload Project" command

* Update Volar

* fix: some tests

* fix: add missing astro language plugin

* bump services

* Update organize-imports.test.ts

* Update languageServerPlugin.ts

* Update languageServerPlugin.ts

* Update languageServerPlugin.ts

* Create five-kings-talk.md

---------

Co-authored-by: Princesseuh <3019731+Princesseuh@users.noreply.github.com>
  • Loading branch information
johnsoncodehk and Princesseuh committed May 7, 2024
1 parent 7ff9cf5 commit 65d3425
Show file tree
Hide file tree
Showing 22 changed files with 315 additions and 260 deletions.
8 changes: 8 additions & 0 deletions .changeset/five-kings-talk.md
@@ -0,0 +1,8 @@
---
"@astrojs/check": minor
"@astrojs/language-server": minor
"@astrojs/ts-plugin": minor
"astro-vscode": minor
---

Upgrades the language server to use Volar 2.2. This changes should have no negative impacts on the experience.
4 changes: 3 additions & 1 deletion packages/astro-check/src/index.ts
Expand Up @@ -40,7 +40,9 @@ export async function check(flags: Partial<Flags>): Promise<boolean | void> {
// Dynamically get the list of extensions to watch from the files already included in the project
const checkedExtensions = Array.from(
new Set(
checker.linter.languageHost.getScriptFileNames().map((fileName) => path.extname(fileName))
checker.linter.language
.typescript!.projectHost.getScriptFileNames()
.map((fileName) => path.extname(fileName))
)
);
createWatcher(workspaceRoot, checkedExtensions)
Expand Down
26 changes: 13 additions & 13 deletions packages/language-server/package.json
Expand Up @@ -23,19 +23,19 @@
"dependencies": {
"@astrojs/compiler": "^2.7.0",
"@jridgewell/sourcemap-codec": "^1.4.15",
"@volar/kit": "~2.1.5",
"@volar/language-core": "~2.1.5",
"@volar/language-server": "~2.1.5",
"@volar/language-service": "~2.1.5",
"@volar/typescript": "~2.1.5",
"@volar/kit": "~2.2.1",
"@volar/language-core": "~2.2.1",
"@volar/language-server": "~2.2.1",
"@volar/language-service": "~2.2.1",
"@volar/typescript": "~2.2.1",
"fast-glob": "^3.2.12",
"volar-service-css": "0.0.34",
"volar-service-emmet": "0.0.34",
"volar-service-html": "0.0.34",
"volar-service-prettier": "0.0.34",
"volar-service-typescript": "0.0.34",
"volar-service-typescript-twoslash-queries": "0.0.34",
"vscode-html-languageservice": "^5.1.2",
"volar-service-css": "0.0.43",
"volar-service-emmet": "0.0.43",
"volar-service-html": "0.0.43",
"volar-service-prettier": "0.0.43",
"volar-service-typescript": "0.0.43",
"volar-service-typescript-twoslash-queries": "0.0.43",
"vscode-html-languageservice": "^5.2.0",
"vscode-uri": "^3.0.8"
},
"devDependencies": {
Expand All @@ -45,7 +45,7 @@
"@types/chai": "^4.3.5",
"@types/mocha": "^10.0.1",
"@types/node": "^18.17.8",
"@volar/test-utils": "~2.1.5",
"@volar/test-utils": "~2.2.1",
"astro": "^4.3.5",
"chai": "^4.3.7",
"mocha": "^10.2.0",
Expand Down
6 changes: 4 additions & 2 deletions packages/language-server/src/check.ts
Expand Up @@ -62,7 +62,9 @@ export class AstroCheck {
| undefined;
}): Promise<CheckResult> {
let files = (
fileNames !== undefined ? fileNames : this.linter.languageHost.getScriptFileNames()
fileNames !== undefined
? fileNames
: this.linter.language.typescript!.projectHost.getScriptFileNames()
).filter((file) => {
// We don't have the same understanding of Svelte and Vue files as their own respective tools (vue-tsc, svelte-check)
// So we don't want to check them here
Expand Down Expand Up @@ -104,7 +106,7 @@ export class AstroCheck {
console.info(errorText);
}

const fileSnapshot = this.linter.languageHost.getScriptSnapshot(file);
const fileSnapshot = this.linter.language.typescript!.projectHost.getScriptSnapshot(file);
const fileContent = fileSnapshot?.getText(0, fileSnapshot.getLength());

result.fileResult.push({
Expand Down
19 changes: 12 additions & 7 deletions packages/language-server/src/core/index.ts
Expand Up @@ -22,21 +22,26 @@ export function getLanguageModule(
ts: typeof import('typescript')
): LanguagePlugin<AstroVirtualCode> {
return {
createVirtualCode(fileId, languageId, snapshot) {
getLanguageId(scriptId) {
if (scriptId.endsWith('.astro')) {
return 'astro';
}
},
createVirtualCode(scriptId, languageId, snapshot) {
if (languageId === 'astro') {
const fileName = fileId.includes('://')
? URI.parse(fileId).fsPath.replace(/\\/g, '/')
: fileId;
const fileName = scriptId.includes('://')
? URI.parse(scriptId).fsPath.replace(/\\/g, '/')
: scriptId;
return new AstroVirtualCode(fileName, snapshot);
}
},
updateVirtualCode(_fileId, astroCode, snapshot) {
updateVirtualCode(_scriptId, astroCode, snapshot) {
astroCode.update(snapshot);
return astroCode;
},
typescript: {
extraFileExtensions: [{ extension: 'astro', isMixedContent: true, scriptKind: 7 }],
getScript(astroCode) {
getServiceScript(astroCode) {
for (const code of forEachEmbeddedCode(astroCode)) {
if (code.id === 'tsx') {
return {
Expand All @@ -48,7 +53,7 @@ export function getLanguageModule(
}
return undefined;
},
getExtraScripts(fileName, astroCode) {
getExtraServiceScripts(fileName, astroCode) {
const result: ExtraServiceScript[] = [];
for (const code of forEachEmbeddedCode(astroCode)) {
if (code.id.endsWith('.mjs') || code.id.endsWith('.mts')) {
Expand Down
17 changes: 11 additions & 6 deletions packages/language-server/src/core/svelte.ts
Expand Up @@ -11,21 +11,26 @@ import { framework2tsx } from './utils.js';

export function getSvelteLanguageModule(): LanguagePlugin<SvelteVirtualCode> {
return {
createVirtualCode(fileId, languageId, snapshot) {
getLanguageId(scriptId) {
if (scriptId.endsWith('.svelte')) {
return 'svelte';
}
},
createVirtualCode(scriptId, languageId, snapshot) {
if (languageId === 'svelte') {
const fileName = fileId.includes('://')
? URI.parse(fileId).fsPath.replace(/\\/g, '/')
: fileId;
const fileName = scriptId.includes('://')
? URI.parse(scriptId).fsPath.replace(/\\/g, '/')
: scriptId;
return new SvelteVirtualCode(fileName, snapshot);
}
},
updateVirtualCode(_fileId, svelteCode, snapshot) {
updateVirtualCode(_scriptId, svelteCode, snapshot) {
svelteCode.update(snapshot);
return svelteCode;
},
typescript: {
extraFileExtensions: [{ extension: 'svelte', isMixedContent: true, scriptKind: 7 }],
getScript(svelteCode) {
getServiceScript(svelteCode) {
for (const code of forEachEmbeddedCode(svelteCode)) {
if (code.id === 'tsx') {
return {
Expand Down
17 changes: 11 additions & 6 deletions packages/language-server/src/core/vue.ts
Expand Up @@ -11,21 +11,26 @@ import { framework2tsx } from './utils.js';

export function getVueLanguageModule(): LanguagePlugin<VueVirtualCode> {
return {
createVirtualCode(fileId, languageId, snapshot) {
getLanguageId(scriptId) {
if (scriptId.endsWith('.vue')) {
return 'vue';
}
},
createVirtualCode(scriptId, languageId, snapshot) {
if (languageId === 'vue') {
const fileName = fileId.includes('://')
? URI.parse(fileId).fsPath.replace(/\\/g, '/')
: fileId;
const fileName = scriptId.includes('://')
? URI.parse(scriptId).fsPath.replace(/\\/g, '/')
: scriptId;
return new VueVirtualCode(fileName, snapshot);
}
},
updateVirtualCode(_fileId, vueCode, snapshot) {
updateVirtualCode(_scriptId, vueCode, snapshot) {
vueCode.update(snapshot);
return vueCode;
},
typescript: {
extraFileExtensions: [{ extension: 'vue', isMixedContent: true, scriptKind: 7 }],
getScript(vueCode) {
getServiceScript(vueCode) {
for (const code of forEachEmbeddedCode(vueCode)) {
if (code.id === 'tsx') {
return {
Expand Down
111 changes: 49 additions & 62 deletions packages/language-server/src/languageServerPlugin.ts
Expand Up @@ -2,6 +2,7 @@ import {
Connection,
LanguagePlugin,
MessageType,
ServiceEnvironment,
ShowMessageNotification,
VirtualCode,
} from '@volar/language-server/node';
Expand All @@ -18,76 +19,57 @@ import { create as createEmmetService } from 'volar-service-emmet';
import { create as createPrettierService } from 'volar-service-prettier';
import { create as createTypeScriptTwoSlashService } from 'volar-service-typescript-twoslash-queries';

import type { ServerOptions } from '@volar/language-server/lib/server.js';
import { create as createAstroService } from './plugins/astro.js';
import { create as createHtmlService } from './plugins/html.js';
import { create as createTypescriptAddonsService } from './plugins/typescript-addons/index.js';
import { create as createTypeScriptServices } from './plugins/typescript/index.js';

export function createServerOptions(
export function getLanguagePlugins(
connection: Connection,
ts: typeof import('typescript')
): ServerOptions {
return {
watchFileExtensions: [
'js',
'cjs',
'mjs',
'ts',
'cts',
'mts',
'jsx',
'tsx',
'json',
'astro',
'vue',
'svelte',
],
getServicePlugins() {
return [
createHtmlService(),
createCssService(),
createEmmetService(),
...createTypeScriptServices(ts),
createTypeScriptTwoSlashService(ts),
createTypescriptAddonsService(),
createAstroService(ts),
getPrettierService(),
];
},
getLanguagePlugins(serviceEnv, projectContext) {
const languagePlugins: LanguagePlugin<VirtualCode>[] = [
getVueLanguageModule(),
getSvelteLanguageModule(),
];

if (projectContext.typescript) {
const rootPath = projectContext.typescript.configFileName
? projectContext.typescript.configFileName.split('/').slice(0, -1).join('/')
: serviceEnv.typescript!.uriToFileName(serviceEnv.workspaceFolder);
const nearestPackageJson = ts.findConfigFile(rootPath, ts.sys.fileExists, 'package.json');

const astroInstall = getAstroInstall([rootPath], {
nearestPackageJson: nearestPackageJson,
readDirectory: ts.sys.readDirectory,
});

if (astroInstall === 'not-found') {
connection.sendNotification(ShowMessageNotification.type, {
message: `Couldn't find Astro in workspace "${rootPath}". Experience might be degraded. For the best experience, please make sure Astro is installed into your project and restart the language server.`,
type: MessageType.Warning,
});
}
ts: typeof import('typescript'),
serviceEnv: ServiceEnvironment,
tsconfig: string | undefined
) {
const languagePlugins: LanguagePlugin<VirtualCode>[] = [
getVueLanguageModule(),
getSvelteLanguageModule(),
];

const rootPath = tsconfig
? tsconfig.split('/').slice(0, -1).join('/')
: serviceEnv.typescript!.uriToFileName(serviceEnv.workspaceFolder);
const nearestPackageJson = ts.findConfigFile(rootPath, ts.sys.fileExists, 'package.json');

const astroInstall = getAstroInstall([rootPath], {
nearestPackageJson: nearestPackageJson,
readDirectory: ts.sys.readDirectory,
});

if (astroInstall === 'not-found') {
connection.sendNotification(ShowMessageNotification.type, {
message: `Couldn't find Astro in workspace "${rootPath}". Experience might be degraded. For the best experience, please make sure Astro is installed into your project and restart the language server.`,
type: MessageType.Warning,
});
}

languagePlugins.unshift(
getLanguageModule(typeof astroInstall === 'string' ? undefined : astroInstall, ts)
);
}
languagePlugins.unshift(
getLanguageModule(typeof astroInstall === 'string' ? undefined : astroInstall, ts)
);

return languagePlugins;
},
};
return languagePlugins;
}

export function getLanguageServicePlugins(connection: Connection, ts: typeof import('typescript')) {
return [
createHtmlService(),
createCssService(),
createEmmetService(),
...createTypeScriptServices(ts),
createTypeScriptTwoSlashService(ts),
createTypescriptAddonsService(),
createAstroService(ts),
getPrettierService(),
];
function getPrettierService() {
let prettier: ReturnType<typeof importPrettier>;
let prettierPluginPath: ReturnType<typeof getPrettierPluginPath>;
Expand All @@ -113,7 +95,12 @@ export function createServerOptions(
{
documentSelector: ['astro'],
getFormattingOptions: async (prettierInstance, document, formatOptions, context) => {
const filePath = URI.parse(document.uri).fsPath;
const documentUri = context.decodeEmbeddedDocumentUri(document.uri)?.[0] ?? document.uri;
const filePath = URI.parse(documentUri).fsPath;

if (!filePath) {
return {};
}

let configOptions = null;
try {
Expand Down
26 changes: 22 additions & 4 deletions packages/language-server/src/nodeServer.ts
@@ -1,10 +1,10 @@
import {
createConnection,
createServer,
createTypeScriptProjectProviderFactory,
createTypeScriptProjectProvider,
loadTsdkByPath,
} from '@volar/language-server/node';
import { createServerOptions } from './languageServerPlugin.js';
import { getLanguagePlugins, getLanguageServicePlugins } from './languageServerPlugin.js';

const connection = createConnection();
const server = createServer(connection);
Expand All @@ -24,11 +24,29 @@ connection.onInitialize((params) => {

return server.initialize(
params,
createTypeScriptProjectProviderFactory(typescript, diagnosticMessages),
createServerOptions(connection, typescript)
getLanguageServicePlugins(connection, typescript),
createTypeScriptProjectProvider(typescript, diagnosticMessages, (env, project) =>
getLanguagePlugins(connection, typescript, env, project.configFileName)
)
);
});

connection.onInitialized(() => {
server.initialized();
server.watchFiles([
`**/*.{${[
'js',
'cjs',
'mjs',
'ts',
'cts',
'mts',
'jsx',
'tsx',
'json',
'astro',
'vue',
'svelte',
].join(',')}}`,
]);
});

0 comments on commit 65d3425

Please sign in to comment.