Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore: Update volar-service-prettier #823

Merged
merged 5 commits into from Mar 13, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
7 changes: 7 additions & 0 deletions .changeset/thin-dragons-yell.md
@@ -0,0 +1,7 @@
---
"astro-vscode": patch
"@astrojs/check": patch
"@astrojs/language-server": patch
---

chore: Update `volar-service-prettier`. This is only an internal refactor and there should be no visible changes.
12 changes: 6 additions & 6 deletions packages/language-server/package.json
Expand Up @@ -29,12 +29,12 @@
"@volar/language-service": "~2.1.2",
"@volar/typescript": "~2.1.2",
"fast-glob": "^3.2.12",
"volar-service-css": "0.0.31",
"volar-service-emmet": "0.0.31",
"volar-service-html": "0.0.31",
"volar-service-prettier": "0.0.31-patch.1",
"volar-service-typescript": "0.0.31",
"volar-service-typescript-twoslash-queries": "0.0.31",
"volar-service-css": "0.0.32",
"volar-service-emmet": "0.0.32",
"volar-service-html": "0.0.32",
"volar-service-prettier": "0.0.32",
"volar-service-typescript": "0.0.32",
"volar-service-typescript-twoslash-queries": "0.0.32",
"vscode-html-languageservice": "^5.1.2",
"vscode-uri": "^3.0.8"
},
Expand Down
4 changes: 2 additions & 2 deletions packages/language-server/src/check.ts
Expand Up @@ -11,7 +11,7 @@ import { getVueLanguageModule } from './core/vue.js';
import { getAstroInstall } from './utils.js';

import { create as createAstroService } from './plugins/astro.js';
import { create as createTypeScriptService } from './plugins/typescript/index.js';
import { create as createTypeScriptServices } from './plugins/typescript/index.js';

// Export those for downstream consumers
export { Diagnostic, DiagnosticSeverity };
Expand Down Expand Up @@ -142,7 +142,7 @@ export class AstroCheck {
getSvelteLanguageModule(),
getVueLanguageModule(),
];
const services = [createTypeScriptService(this.ts), createAstroService(this.ts)];
const services = [...createTypeScriptServices(this.ts), createAstroService(this.ts)];

if (tsconfigPath) {
this.linter = kit.createTypeScriptChecker(languages, services, tsconfigPath);
Expand Down
99 changes: 58 additions & 41 deletions packages/language-server/src/languageServerPlugin.ts
Expand Up @@ -5,6 +5,7 @@ import {
ShowMessageNotification,
VirtualCode,
} from '@volar/language-server/node';
import { URI } from 'vscode-uri';
import { getLanguageModule } from './core';
import { getSvelteLanguageModule } from './core/svelte.js';
import { getVueLanguageModule } from './core/vue.js';
Expand All @@ -21,7 +22,7 @@ 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 createTypeScriptService } from './plugins/typescript/index.js';
import { create as createTypeScriptServices } from './plugins/typescript/index.js';

export function createServerOptions(
connection: Connection,
Expand All @@ -47,8 +48,8 @@ export function createServerOptions(
createHtmlService(),
createCssService(),
createEmmetService(),
createTypeScriptService(ts),
createTypeScriptTwoSlashService(),
...createTypeScriptServices(ts),
createTypeScriptTwoSlashService(ts),
createTypescriptAddonsService(),
createAstroService(ts),
getPrettierService(),
Expand Down Expand Up @@ -90,49 +91,65 @@ export function createServerOptions(
function getPrettierService() {
let prettier: ReturnType<typeof importPrettier>;
let prettierPluginPath: ReturnType<typeof getPrettierPluginPath>;
return createPrettierService({
getPrettier(env) {
const workspacePath = env.typescript!.uriToFileName(env.workspaceFolder);
prettier = importPrettier(workspacePath);
prettierPluginPath = getPrettierPluginPath(workspacePath);
if (!prettier || !prettierPluginPath) {
connection.sendNotification(ShowMessageNotification.type, {
message:
"Couldn't load `prettier` or `prettier-plugin-astro`. Formatting will not work. Please make sure those two packages are installed into your project.",
type: MessageType.Warning,
});
}
return prettier;
},
languages: ['astro'],
ignoreIdeOptions: true,
useIdeOptionsFallback: true,
resolveConfigOptions: {
// This seems to be broken since Prettier 3, and it'll always use its cumbersome cache. Hopefully it works one day.
useCache: false,
},
additionalOptions: async (resolvedConfig) => {
async function getAstroPrettierPlugin() {
return createPrettierService(
context => {
const workspaceUri = URI.parse(context.env.workspaceFolder);
if (workspaceUri.scheme === 'file') {
prettier = importPrettier(workspaceUri.fsPath);
prettierPluginPath = getPrettierPluginPath(workspaceUri.fsPath);
if (!prettier || !prettierPluginPath) {
return [];
connection.sendNotification(ShowMessageNotification.type, {
message:
"Couldn't load `prettier` or `prettier-plugin-astro`. Formatting will not work. Please make sure those two packages are installed into your project.",
type: MessageType.Warning,
});
}
return prettier;
}
},
{
documentSelector: ['astro'],
getFormattingOptions: async (prettier, document, formatOptions, context) => {
const filePath = URI.parse(document.uri).fsPath;
const configOptions = await prettier.resolveConfig(filePath, {
// This seems to be broken since Prettier 3, and it'll always use its cumbersome cache. Hopefully it works one day.
useCache: false,
});
const editorOptions = await context.env.getConfiguration<{}>?.('prettier', document.uri);

const hasPluginLoadedAlready =
(await prettier.getSupportInfo()).languages.some((l: any) => l.name === 'astro') ||
resolvedConfig.plugins?.includes('prettier-plugin-astro'); // getSupportInfo doesn't seems to work very well in Prettier 3 for plugins
// Return a config with the following cascade:
// - Prettier config file should always win if it exists, if it doesn't:
// - Prettier config from the VS Code extension is used, if it doesn't exist:
// - Use the editor's basic configuration settings
const resolvedConfig = {
filepath: filePath,
tabWidth: formatOptions.tabSize,
useTabs: !formatOptions.insertSpaces,
...editorOptions,
...configOptions,
};

return hasPluginLoadedAlready ? [] : [prettierPluginPath];
}
return {
...resolvedConfig,
plugins: [
...await getAstroPrettierPlugin(),
...resolvedConfig.plugins ?? [],
],
parser: 'astro',
};

const plugins = [...(await getAstroPrettierPlugin()), ...(resolvedConfig.plugins ?? [])];
async function getAstroPrettierPlugin() {
if (!prettier || !prettierPluginPath) {
return [];
}

return {
...resolvedConfig,
plugins: plugins,
parser: 'astro',
};
},
allowImportError: true,
});
const hasPluginLoadedAlready =
(await prettier.getSupportInfo()).languages.some((l: any) => l.name === 'astro') ||
resolvedConfig.plugins?.includes('prettier-plugin-astro'); // getSupportInfo doesn't seems to work very well in Prettier 3 for plugins

return hasPluginLoadedAlready ? [] : [prettierPluginPath];
}
},
});
}
}
111 changes: 58 additions & 53 deletions packages/language-server/src/plugins/typescript/index.ts
@@ -1,71 +1,76 @@
import type { ServicePlugin, ServicePluginInstance } from '@volar/language-server';
import { create as createTypeScriptService } from 'volar-service-typescript';
import { create as createTypeScriptServices } from 'volar-service-typescript';
import { AstroVirtualCode } from '../../core/index.js';
import { enhancedProvideCodeActions, enhancedResolveCodeAction } from './codeActions.js';
import { enhancedProvideCompletionItems, enhancedResolveCompletionItem } from './completions.js';
import { enhancedProvideSemanticDiagnostics } from './diagnostics.js';

export const create = (ts: typeof import('typescript')): ServicePlugin => {
const tsServicePlugin = createTypeScriptService(ts as typeof import('typescript'));
return {
...tsServicePlugin,
create(context): ServicePluginInstance {
const typeScriptPlugin = tsServicePlugin.create(context);
export const create = (ts: typeof import('typescript')): ServicePlugin[] => {
const tsServicePlugins = createTypeScriptServices(ts as typeof import('typescript'), {});
return tsServicePlugins.map<ServicePlugin>(plugin => {
if (plugin.name === 'typescript-semantic') {
return {
...typeScriptPlugin,
async provideCompletionItems(document, position, completionContext, token) {
const originalCompletions = await typeScriptPlugin.provideCompletionItems!(
document,
position,
completionContext,
token
);
if (!originalCompletions) return null;
...plugin,
create(context): ServicePluginInstance {
const typeScriptPlugin = plugin.create(context);
return {
...typeScriptPlugin,
async provideCompletionItems(document, position, completionContext, token) {
const originalCompletions = await typeScriptPlugin.provideCompletionItems!(
document,
position,
completionContext,
token
);
if (!originalCompletions) return null;

return enhancedProvideCompletionItems(originalCompletions);
},
async resolveCompletionItem(item, token) {
const resolvedCompletionItem = await typeScriptPlugin.resolveCompletionItem!(item, token);
if (!resolvedCompletionItem) return item;
return enhancedProvideCompletionItems(originalCompletions);
},
async resolveCompletionItem(item, token) {
const resolvedCompletionItem = await typeScriptPlugin.resolveCompletionItem!(item, token);
if (!resolvedCompletionItem) return item;

return enhancedResolveCompletionItem(resolvedCompletionItem, context);
},
async provideCodeActions(document, range, codeActionContext, token) {
const originalCodeActions = await typeScriptPlugin.provideCodeActions!(
document,
range,
codeActionContext,
token
);
if (!originalCodeActions) return null;
return enhancedResolveCompletionItem(resolvedCompletionItem, context);
},
async provideCodeActions(document, range, codeActionContext, token) {
const originalCodeActions = await typeScriptPlugin.provideCodeActions!(
document,
range,
codeActionContext,
token
);
if (!originalCodeActions) return null;

return enhancedProvideCodeActions(originalCodeActions, context);
},
async resolveCodeAction(codeAction, token) {
const resolvedCodeAction = await typeScriptPlugin.resolveCodeAction!(codeAction, token);
if (!resolvedCodeAction) return codeAction;
return enhancedProvideCodeActions(originalCodeActions, context);
},
async resolveCodeAction(codeAction, token) {
const resolvedCodeAction = await typeScriptPlugin.resolveCodeAction!(codeAction, token);
if (!resolvedCodeAction) return codeAction;

return enhancedResolveCodeAction(resolvedCodeAction, context);
},
async provideSemanticDiagnostics(document, token) {
const [_, source] = context.documents.getVirtualCodeByUri(document.uri);
const code = source?.generated?.code;
let tsxLineCount = undefined;
return enhancedResolveCodeAction(resolvedCodeAction, context);
},
async provideSemanticDiagnostics(document, token) {
const [_, source] = context.documents.getVirtualCodeByUri(document.uri);
const code = source?.generated?.code;
let tsxLineCount = undefined;

if (code instanceof AstroVirtualCode) {
// If we have compiler errors, our TSX isn't valid so don't bother showing TS errors
if (code.hasCompilationErrors) return null;
if (code instanceof AstroVirtualCode) {
// If we have compiler errors, our TSX isn't valid so don't bother showing TS errors
if (code.hasCompilationErrors) return null;

// We'll use this to filter out diagnostics that are outside the mapped range of the TSX
tsxLineCount = code.astroMeta.tsxRanges.body.end.line;
}
// We'll use this to filter out diagnostics that are outside the mapped range of the TSX
tsxLineCount = code.astroMeta.tsxRanges.body.end.line;
}

const diagnostics = await typeScriptPlugin.provideSemanticDiagnostics!(document, token);
if (!diagnostics) return null;
const diagnostics = await typeScriptPlugin.provideSemanticDiagnostics!(document, token);
if (!diagnostics) return null;

return enhancedProvideSemanticDiagnostics(diagnostics, tsxLineCount);
return enhancedProvideSemanticDiagnostics(diagnostics, tsxLineCount);
},
};
},
};
},
};
}
return plugin;
});
};