diff --git a/packages/vue-language-server/src/common.ts b/packages/vue-language-server/src/common.ts index ba3d725fcd..eb0aaa396e 100644 --- a/packages/vue-language-server/src/common.ts +++ b/packages/vue-language-server/src/common.ts @@ -33,8 +33,7 @@ export function createLanguageServer(connection: vscode.Connection, runtimeEnv: async function onInitialize(params: vscode.InitializeParams) { - // @ts-expect-error - const options: shared.ServerInitializationOptions = params.initializationOptions; + const options: shared.ServerInitializationOptions = params.initializationOptions as any; let folders: string[] = []; let rootUri: URI; @@ -76,6 +75,7 @@ export function createLanguageServer(connection: vscode.Connection, runtimeEnv: return options.documentFeatures?.documentFormatting?.defaultPrintWidth ?? 100; }, lsConfigs?.getSettings, + folders[0], ); (await import('./features/documentFeatures')).register(connection, documents, noStateLs); diff --git a/packages/vue-language-service-types/LICENSE b/packages/vue-language-service-types/LICENSE new file mode 100644 index 0000000000..b55e47a7e8 --- /dev/null +++ b/packages/vue-language-service-types/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2021-present Johnson Chu + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/packages/vue-language-service-types/package.json b/packages/vue-language-service-types/package.json new file mode 100644 index 0000000000..4c67435def --- /dev/null +++ b/packages/vue-language-service-types/package.json @@ -0,0 +1,20 @@ +{ + "name": "@volar/vue-language-service-types", + "version": "0.32.1", + "main": "out/index.js", + "license": "MIT", + "files": [ + "out/**/*.js", + "out/**/*.d.ts" + ], + "repository": { + "type": "git", + "url": "https://github.com/johnsoncodehk/volar.git", + "directory": "packages/vue-language-service-types" + }, + "dependencies": { + "@volar/vue-typescript": "0.32.1", + "vscode-languageserver-protocol": "^3.17.0-next.12", + "vscode-languageserver-textdocument": "^1.0.3" + } +} diff --git a/packages/vue-language-service-types/src/index.ts b/packages/vue-language-service-types/src/index.ts new file mode 100644 index 0000000000..1b3f2d5aa1 --- /dev/null +++ b/packages/vue-language-service-types/src/index.ts @@ -0,0 +1,80 @@ +import type { EmbeddedDocumentSourceMap } from '@volar/vue-typescript'; +import type * as vscode from 'vscode-languageserver-protocol'; +import type { TextDocument } from 'vscode-languageserver-textdocument'; + +type NotNullableResult = T | Thenable; +type NullableResult = NotNullableResult; + +export type SemanticToken = [number, number, number, number, number]; + +export interface ExecuteCommandContext { + token: vscode.CancellationToken; + workDoneProgress: { + begin(title: string, percentage?: number, message?: string, cancellable?: boolean): void; + report(percentage: number): void; + report(message: string): void; + report(percentage: number, message: string): void; + done(): void; + }; + sendNotification

(type: vscode.NotificationType

, params: P): Promise; + applyEdit(paramOrEdit: vscode.ApplyWorkspaceEditParams | vscode.WorkspaceEdit): Promise; +} + +export type EmbeddedLanguagePlugin = { + doValidation?(document: TextDocument, options: { + semantic?: boolean; + syntactic?: boolean; + suggestion?: boolean; + declaration?: boolean; + }): NullableResult; + doComplete?(document: TextDocument, position: vscode.Position, context?: vscode.CompletionContext): NullableResult, + doCompleteResolve?(item: vscode.CompletionItem, newPosition?: vscode.Position): NotNullableResult, + doHover?(document: TextDocument, position: vscode.Position): NullableResult, + findDefinition?(document: TextDocument, position: vscode.Position): NullableResult; + findTypeDefinition?(document: TextDocument, position: vscode.Position): NullableResult; + findImplementations?(document: TextDocument, position: vscode.Position): NullableResult; + findReferences?(document: TextDocument, position: vscode.Position): NullableResult; + findDocumentHighlights?(document: TextDocument, position: vscode.Position): NullableResult; + findDocumentLinks?(document: TextDocument): NullableResult; + findDocumentSymbols?(document: TextDocument): NullableResult; + findDocumentSemanticTokens?(document: TextDocument, range?: vscode.Range, cancleToken?: vscode.CancellationToken): NullableResult; + findWorkspaceSymbols?(query: string): NullableResult; + doCodeActions?(document: TextDocument, range: vscode.Range, context: vscode.CodeActionContext): NullableResult; + doCodeActionResolve?(codeAction: vscode.CodeAction): NotNullableResult; + doCodeLens?(document: TextDocument): NullableResult; + doCodeLensResolve?(codeLens: vscode.CodeLens): NotNullableResult; + doExecuteCommand?(command: string, args: any[], context: ExecuteCommandContext): NotNullableResult; + findDocumentColors?(document: TextDocument): NullableResult; + getColorPresentations?(document: TextDocument, color: vscode.Color, range: vscode.Range): NullableResult; + doRenamePrepare?(document: TextDocument, position: vscode.Position): NullableResult>; + doRename?(document: TextDocument, position: vscode.Position, newName: string): NullableResult; + doFileRename?(oldUri: string, newUri: string): NullableResult; + getFoldingRanges?(document: TextDocument): NullableResult; + getSelectionRanges?(document: TextDocument, positions: vscode.Position[]): NullableResult; + getSignatureHelp?(document: TextDocument, position: vscode.Position, context?: vscode.SignatureHelpContext): NullableResult; + format?(document: TextDocument, range: vscode.Range | undefined, options: vscode.FormattingOptions): NullableResult; + + callHierarchy?: { + doPrepare(document: TextDocument, position: vscode.Position): NullableResult; + getIncomingCalls(item: vscode.CallHierarchyItem): NotNullableResult; + getOutgoingCalls(item: vscode.CallHierarchyItem): NotNullableResult; + }, + + // html + findLinkedEditingRanges?(document: TextDocument, position: vscode.Position): NullableResult; + doAutoInsert?(document: TextDocument, position: vscode.Position, context: { + lastChange: { + range: vscode.Range; + rangeOffset: number; + rangeLength: number; + text: string; + }, + }): NullableResult; + + /** + * TODO: only support to doCompleteResolve for now + */ + resolveEmbeddedRange?(range: vscode.Range, sourceMap: EmbeddedDocumentSourceMap): vscode.Range | undefined; + + // findMatchingTagPosition?(document: TextDocument, position: vscode.Position, htmlDocument: HTMLDocument): vscode.Position | null; +}; diff --git a/packages/vue-language-service-types/tsconfig.build.json b/packages/vue-language-service-types/tsconfig.build.json new file mode 100644 index 0000000000..5a53e9db28 --- /dev/null +++ b/packages/vue-language-service-types/tsconfig.build.json @@ -0,0 +1,19 @@ +{ + "extends": "../../tsconfig.base.json", + "compilerOptions": { + "outDir": "out", + "rootDir": "src", + }, + "include": [ + "src" + ], + "exclude": [ + "node_modules", + ".vscode-test" + ], + "references": [ + { + "path": "../vue-typescript/tsconfig.build.json" + }, + ], +} \ No newline at end of file diff --git a/packages/vue-language-service/package.json b/packages/vue-language-service/package.json index 3c882f94f1..444f633b86 100644 --- a/packages/vue-language-service/package.json +++ b/packages/vue-language-service/package.json @@ -26,6 +26,7 @@ "@volar/transforms": "0.32.1", "@volar/typescript-language-service": "0.32.1", "@volar/vue-code-gen": "0.32.1", + "@volar/vue-language-service-types": "0.32.1", "@volar/vue-typescript": "0.32.1", "@vscode/emmet-helper": "^2.8.3", "@vue/reactivity": "^3.2.27", diff --git a/packages/vue-language-service/src/commonPlugins/css.ts b/packages/vue-language-service/src/commonPlugins/css.ts index d8f5b61260..e27009d53e 100644 --- a/packages/vue-language-service/src/commonPlugins/css.ts +++ b/packages/vue-language-service/src/commonPlugins/css.ts @@ -1,4 +1,4 @@ -import { EmbeddedLanguagePlugin } from '../utils/definePlugin'; +import { EmbeddedLanguagePlugin } from '@volar/vue-language-service-types'; import type * as css from 'vscode-css-languageservice'; import type { TextDocument } from 'vscode-languageserver-textdocument'; import * as shared from '@volar/shared'; diff --git a/packages/vue-language-service/src/commonPlugins/directiveComment.ts b/packages/vue-language-service/src/commonPlugins/directiveComment.ts index 1c103be9e1..66cd67b039 100644 --- a/packages/vue-language-service/src/commonPlugins/directiveComment.ts +++ b/packages/vue-language-service/src/commonPlugins/directiveComment.ts @@ -1,4 +1,4 @@ -import { EmbeddedLanguagePlugin } from '../utils/definePlugin'; +import { EmbeddedLanguagePlugin } from '@volar/vue-language-service-types'; import * as ts2 from '@volar/typescript-language-service'; import { isTsDocument } from './typescript'; diff --git a/packages/vue-language-service/src/commonPlugins/emmet.ts b/packages/vue-language-service/src/commonPlugins/emmet.ts index 155b2c5a4d..9045c6287c 100644 --- a/packages/vue-language-service/src/commonPlugins/emmet.ts +++ b/packages/vue-language-service/src/commonPlugins/emmet.ts @@ -1,4 +1,4 @@ -import { EmbeddedLanguagePlugin } from '../utils/definePlugin'; +import { EmbeddedLanguagePlugin } from '@volar/vue-language-service-types'; import * as emmet from '@vscode/emmet-helper'; export const triggerCharacters = []; // TODO diff --git a/packages/vue-language-service/src/commonPlugins/html.ts b/packages/vue-language-service/src/commonPlugins/html.ts index ca6620f136..94cae2724b 100644 --- a/packages/vue-language-service/src/commonPlugins/html.ts +++ b/packages/vue-language-service/src/commonPlugins/html.ts @@ -1,4 +1,4 @@ -import { EmbeddedLanguagePlugin } from '../utils/definePlugin'; +import { EmbeddedLanguagePlugin } from '@volar/vue-language-service-types'; import * as html from 'vscode-html-languageservice'; import { TextDocument } from 'vscode-languageserver-textdocument'; diff --git a/packages/vue-language-service/src/commonPlugins/jsDoc.ts b/packages/vue-language-service/src/commonPlugins/jsDoc.ts index 935ed5f0cc..919f7a2254 100644 --- a/packages/vue-language-service/src/commonPlugins/jsDoc.ts +++ b/packages/vue-language-service/src/commonPlugins/jsDoc.ts @@ -1,4 +1,4 @@ -import { EmbeddedLanguagePlugin } from '../utils/definePlugin'; +import { EmbeddedLanguagePlugin } from '@volar/vue-language-service-types'; import * as ts2 from '@volar/typescript-language-service'; import { isTsDocument } from './typescript'; diff --git a/packages/vue-language-service/src/commonPlugins/json.ts b/packages/vue-language-service/src/commonPlugins/json.ts index 8e7214c94c..84dd251c96 100644 --- a/packages/vue-language-service/src/commonPlugins/json.ts +++ b/packages/vue-language-service/src/commonPlugins/json.ts @@ -1,6 +1,6 @@ import * as json from 'vscode-json-languageservice'; import { TextDocument } from 'vscode-languageserver-textdocument'; -import { EmbeddedLanguagePlugin } from '../utils/definePlugin'; +import { EmbeddedLanguagePlugin } from '@volar/vue-language-service-types'; import * as vscode from 'vscode-languageserver-protocol'; // https://github.com/microsoft/vscode/blob/09850876e652688fb142e2e19fd00fd38c0bc4ba/extensions/json-language-features/server/src/jsonServer.ts#L150 diff --git a/packages/vue-language-service/src/commonPlugins/prettier.ts b/packages/vue-language-service/src/commonPlugins/prettier.ts index 065b029f5f..904461b67d 100644 --- a/packages/vue-language-service/src/commonPlugins/prettier.ts +++ b/packages/vue-language-service/src/commonPlugins/prettier.ts @@ -1,6 +1,6 @@ import * as prettier from 'prettier'; import * as vscode from 'vscode-languageserver-protocol'; -import { EmbeddedLanguagePlugin } from '../utils/definePlugin'; +import { EmbeddedLanguagePlugin } from '@volar/vue-language-service-types'; export default function (allowLanguageIds: prettier.BuiltInParserName[]): EmbeddedLanguagePlugin { @@ -18,7 +18,7 @@ export default function (allowLanguageIds: prettier.BuiltInParserName[]): Embedd }); if (newStyleText === document.getText()) - return; + return []; const cssEdit = vscode.TextEdit.replace( vscode.Range.create( diff --git a/packages/vue-language-service/src/commonPlugins/prettyhtml.ts b/packages/vue-language-service/src/commonPlugins/prettyhtml.ts index 991822f15e..b807e863d1 100644 --- a/packages/vue-language-service/src/commonPlugins/prettyhtml.ts +++ b/packages/vue-language-service/src/commonPlugins/prettyhtml.ts @@ -1,6 +1,6 @@ import * as prettyhtml from '@starptech/prettyhtml'; import * as vscode from 'vscode-languageserver-protocol'; -import { EmbeddedLanguagePlugin } from '../utils/definePlugin'; +import { EmbeddedLanguagePlugin } from '@volar/vue-language-service-types'; export default function (host: { getPrintWidth: (uri: string) => number | Promise, @@ -13,14 +13,14 @@ export default function (host: { if (document.languageId !== 'html') return; - let newHtml = prettyhtml(document.getText(), { + const newHtml = prettyhtml(document.getText(), { tabWidth: options.tabSize, useTabs: !options.insertSpaces, printWidth: await host.getPrintWidth(document.uri), }).contents; if (newHtml === document.getText()) - return; + return []; const htmlEdit = vscode.TextEdit.replace( vscode.Range.create( diff --git a/packages/vue-language-service/src/commonPlugins/pug.ts b/packages/vue-language-service/src/commonPlugins/pug.ts index fb404fb424..aa6051e52a 100644 --- a/packages/vue-language-service/src/commonPlugins/pug.ts +++ b/packages/vue-language-service/src/commonPlugins/pug.ts @@ -1,4 +1,4 @@ -import { EmbeddedLanguagePlugin } from '../utils/definePlugin'; +import { EmbeddedLanguagePlugin } from '@volar/vue-language-service-types'; import * as html from 'vscode-html-languageservice'; import { TextDocument } from 'vscode-languageserver-textdocument'; import * as pug from '@volar/pug-language-service'; diff --git a/packages/vue-language-service/src/commonPlugins/pugBeautify.ts b/packages/vue-language-service/src/commonPlugins/pugBeautify.ts index bca4507f33..a90f7f0e90 100644 --- a/packages/vue-language-service/src/commonPlugins/pugBeautify.ts +++ b/packages/vue-language-service/src/commonPlugins/pugBeautify.ts @@ -1,5 +1,5 @@ import * as vscode from 'vscode-languageserver-protocol'; -import { EmbeddedLanguagePlugin } from '../utils/definePlugin'; +import { EmbeddedLanguagePlugin } from '@volar/vue-language-service-types'; const pugBeautify = require('@johnsoncodehk/pug-beautify'); @@ -28,7 +28,7 @@ export default function (): EmbeddedLanguagePlugin { }); if (newPugCode === document.getText()) - return; + return []; const pugEdit = vscode.TextEdit.replace( vscode.Range.create( diff --git a/packages/vue-language-service/src/commonPlugins/sassFormatter.ts b/packages/vue-language-service/src/commonPlugins/sassFormatter.ts index 2838d9bae9..90f68844a3 100644 --- a/packages/vue-language-service/src/commonPlugins/sassFormatter.ts +++ b/packages/vue-language-service/src/commonPlugins/sassFormatter.ts @@ -1,6 +1,6 @@ import { SassFormatter } from 'sass-formatter'; import * as vscode from 'vscode-languageserver-protocol'; -import { EmbeddedLanguagePlugin } from '../utils/definePlugin'; +import { EmbeddedLanguagePlugin } from '@volar/vue-language-service-types'; export default function (): EmbeddedLanguagePlugin { @@ -21,7 +21,7 @@ export default function (): EmbeddedLanguagePlugin { const newStyleText = SassFormatter.Format(document.getText(), _options); if (newStyleText === document.getText()) - return; + return []; const cssEdit = vscode.TextEdit.replace( vscode.Range.create( diff --git a/packages/vue-language-service/src/commonPlugins/typescript.ts b/packages/vue-language-service/src/commonPlugins/typescript.ts index 6babc4663f..b704a17b24 100644 --- a/packages/vue-language-service/src/commonPlugins/typescript.ts +++ b/packages/vue-language-service/src/commonPlugins/typescript.ts @@ -1,5 +1,5 @@ import { TextDocument } from 'vscode-languageserver-textdocument'; -import { EmbeddedLanguagePlugin } from '../utils/definePlugin'; +import { EmbeddedLanguagePlugin } from '@volar/vue-language-service-types'; import type * as ts from 'typescript/lib/tsserverlibrary'; import * as ts2 from '@volar/typescript-language-service'; diff --git a/packages/vue-language-service/src/documentFeatures/autoInsert.ts b/packages/vue-language-service/src/documentFeatures/autoInsert.ts index 04532fdc72..258a5cca17 100644 --- a/packages/vue-language-service/src/documentFeatures/autoInsert.ts +++ b/packages/vue-language-service/src/documentFeatures/autoInsert.ts @@ -1,7 +1,7 @@ import type { DocumentServiceRuntimeContext } from '../types'; import { documentArgFeatureWorker } from '../utils/featureWorkers'; import type { TextDocument } from 'vscode-languageserver-textdocument'; -import { EmbeddedLanguagePlugin } from '../utils/definePlugin'; +import { EmbeddedLanguagePlugin } from '@volar/vue-language-service-types'; import * as vscode from 'vscode-languageserver-protocol'; export function register(context: DocumentServiceRuntimeContext) { diff --git a/packages/vue-language-service/src/documentFeatures/format.ts b/packages/vue-language-service/src/documentFeatures/format.ts index 381848db93..c2e169a3e9 100644 --- a/packages/vue-language-service/src/documentFeatures/format.ts +++ b/packages/vue-language-service/src/documentFeatures/format.ts @@ -136,7 +136,7 @@ export function register(context: DocumentServiceRuntimeContext) { const edits = await plugin.format(document, undefined, options); - if (!edits || edits.length === 0) + if (!edits) continue; return edits; diff --git a/packages/vue-language-service/src/documentService.ts b/packages/vue-language-service/src/documentService.ts index b3d8e628e9..02335ae909 100644 --- a/packages/vue-language-service/src/documentService.ts +++ b/packages/vue-language-service/src/documentService.ts @@ -19,11 +19,12 @@ import * as format from './documentFeatures/format'; import * as linkedEditingRanges from './documentFeatures/linkedEditingRanges'; import * as selectionRanges from './documentFeatures/selectionRanges'; import { DocumentServiceRuntimeContext, LanguageServiceHost } from './types'; -import { EmbeddedLanguagePlugin } from './utils/definePlugin'; import * as sharedServices from './utils/sharedLs'; import useAutoWrapParenthesesPlugin from './vuePlugins/autoWrapParentheses'; import useVuePlugin from './vuePlugins/vue'; import type * as _ from 'vscode-languageserver-protocol'; +import { loadCustomPlugins } from './languageService'; +import { EmbeddedLanguagePlugin } from '@volar/vue-language-service-types'; export interface DocumentService extends ReturnType { } @@ -33,6 +34,7 @@ export function getDocumentService( getFormatOptions: LanguageServiceHost['getFormatOptions'], getPrintWidth: (uri: string) => Promise, getSettings: ( (section: string, scopeUri?: string) => Promise) | undefined, + rootPath: string, ) { const vueDocuments = new WeakMap(); @@ -41,6 +43,7 @@ export function getDocumentService( // language support plugins const _getSettings: (section: string, scopeUri?: string | undefined) => Promise = async (section, scopeUri) => getSettings?.(section, scopeUri); + const customPlugins = loadCustomPlugins(rootPath); const vuePlugin = useVuePlugin({ getSettings: _getSettings, getVueDocument, @@ -73,9 +76,18 @@ export function getDocumentService( // formatter plugins const cssFormatPlugin = usePrettierPlugin(['css', 'less', 'scss', 'postcss']); - const htmlFormatPlugin = patchHtmlFormat(useHtmlFormatPlugin({ getPrintWidth })); + const htmlFormatPlugin = useHtmlFormatPlugin({ getPrintWidth }); const pugFormatPlugin = usePugFormatPlugin(); const sassFormatPlugin = useSassFormatPlugin(); + const formatPlugns = [ + ...customPlugins, + cssFormatPlugin, + htmlFormatPlugin, + pugFormatPlugin, + sassFormatPlugin, + jsonPlugin, + tsPlugin, + ].map(patchHtmlFormat); const context: DocumentServiceRuntimeContext = { compilerOptions: {}, @@ -84,6 +96,7 @@ export function getDocumentService( getVueDocument, getPlugins() { return [ + ...customPlugins, vuePlugin, htmlPlugin, pugPlugin, @@ -94,14 +107,7 @@ export function getDocumentService( ]; }, getFormatPlugins() { - return [ - cssFormatPlugin, - htmlFormatPlugin, - pugFormatPlugin, - sassFormatPlugin, - jsonPlugin, - tsPlugin, - ]; + return formatPlugns; }, updateTsLs(document) { if (isTsDocument(document)) { @@ -162,22 +168,27 @@ function patchHtmlFormat(htmlPlugin: EmbeddedLanguagePlugin) { htmlPlugin.format = async (document, range, options) => { - const prefixes = ''; + if (document.languageId === 'html') { + + const prefixes = ''; - const patchDocument = TextDocument.create(document.uri, document.languageId, document.version, prefixes + document.getText() + suffixes); - const result = await originalFormat?.(patchDocument, range, options); + const patchDocument = TextDocument.create(document.uri, document.languageId, document.version, prefixes + document.getText() + suffixes); + const result = await originalFormat?.(patchDocument, range, options); - if (result) { - for (const edit of result) { - if (document.offsetAt(edit.range.start) === 0 && document.offsetAt(edit.range.end) === document.getText().length) { - edit.newText = edit.newText.trim(); - edit.newText = edit.newText.substring(prefixes.length, edit.newText.length - suffixes.length); + if (result) { + for (const edit of result) { + if (document.offsetAt(edit.range.start) === 0 && document.offsetAt(edit.range.end) === document.getText().length) { + edit.newText = edit.newText.trim(); + edit.newText = edit.newText.substring(prefixes.length, edit.newText.length - suffixes.length); + } } } + + return result; } - return result; + return originalFormat?.(document, range, options); }; } diff --git a/packages/vue-language-service/src/index.ts b/packages/vue-language-service/src/index.ts index c2e9154618..ecfa8a58f6 100644 --- a/packages/vue-language-service/src/index.ts +++ b/packages/vue-language-service/src/index.ts @@ -1,8 +1,9 @@ +export * from '@volar/vue-language-service-types'; export * from '@volar/vue-typescript'; export * from './documentService'; -export * from './languageService'; -export { SemanticToken } from './utils/definePlugin'; -export { margeWorkspaceEdits } from './languageFuatures/rename'; export { executePluginCommand, ExecutePluginCommandArgs } from './languageFuatures/executeCommand'; -export { convertTagNameCasingCommand, ConvertTagNameCasingCommandArgs } from './vuePlugins/tagNameCasingConversions'; +export { margeWorkspaceEdits } from './languageFuatures/rename'; +export * from './languageService'; export * from './types'; +export { convertTagNameCasingCommand, ConvertTagNameCasingCommandArgs } from './vuePlugins/tagNameCasingConversions'; + diff --git a/packages/vue-language-service/src/languageFuatures/autoInsert.ts b/packages/vue-language-service/src/languageFuatures/autoInsert.ts index 6eb5e1822c..3cd8567a5d 100644 --- a/packages/vue-language-service/src/languageFuatures/autoInsert.ts +++ b/packages/vue-language-service/src/languageFuatures/autoInsert.ts @@ -1,7 +1,7 @@ import * as vscode from 'vscode-languageserver-protocol'; import type { LanguageServiceRuntimeContext } from '../types'; import { languageFeatureWorker } from '../utils/featureWorkers'; -import { EmbeddedLanguagePlugin } from '../utils/definePlugin'; +import { EmbeddedLanguagePlugin } from '@volar/vue-language-service-types'; export function register(context: LanguageServiceRuntimeContext) { diff --git a/packages/vue-language-service/src/languageFuatures/documentSemanticTokens.ts b/packages/vue-language-service/src/languageFuatures/documentSemanticTokens.ts index d5596ed15a..c42900eb76 100644 --- a/packages/vue-language-service/src/languageFuatures/documentSemanticTokens.ts +++ b/packages/vue-language-service/src/languageFuatures/documentSemanticTokens.ts @@ -1,6 +1,6 @@ import * as shared from '@volar/shared'; import * as vscode from 'vscode-languageserver-protocol'; -import { SemanticToken } from '../utils/definePlugin'; +import { SemanticToken } from '@volar/vue-language-service-types'; import type { LanguageServiceRuntimeContext } from '../types'; import { languageFeatureWorker } from '../utils/featureWorkers'; diff --git a/packages/vue-language-service/src/languageFuatures/executeCommand.ts b/packages/vue-language-service/src/languageFuatures/executeCommand.ts index 72b9347e8f..eee615a3b2 100644 --- a/packages/vue-language-service/src/languageFuatures/executeCommand.ts +++ b/packages/vue-language-service/src/languageFuatures/executeCommand.ts @@ -1,5 +1,5 @@ import type { LanguageServiceRuntimeContext } from '../types'; -import { ExecuteCommandContext } from '../utils/definePlugin'; +import { ExecuteCommandContext } from '@volar/vue-language-service-types'; import * as vscode from 'vscode-languageserver-protocol'; export const executePluginCommand = 'volar.executtePluginCommand'; diff --git a/packages/vue-language-service/src/languageService.ts b/packages/vue-language-service/src/languageService.ts index 2e73bd4dc6..e14b185191 100644 --- a/packages/vue-language-service/src/languageService.ts +++ b/packages/vue-language-service/src/languageService.ts @@ -35,7 +35,7 @@ import * as signatureHelp from './languageFuatures/signatureHelp'; import * as diagnostics from './languageFuatures/validation'; import * as workspaceSymbol from './languageFuatures/workspaceSymbols'; import { LanguageServiceHost, LanguageServiceRuntimeContext } from './types'; -import { EmbeddedLanguagePlugin } from './utils/definePlugin'; +import { EmbeddedLanguagePlugin } from '@volar/vue-language-service-types'; import useAutoDotValuePlugin from './vuePlugins/autoCompleteRefs'; import useHtmlPugConversionsPlugin from './vuePlugins/htmlPugConversions'; import useReferencesCodeLensPlugin from './vuePlugins/referencesCodeLens'; @@ -117,6 +117,7 @@ export function createLanguageService( // plugins const _getSettings: (section: string, scopeUri?: string | undefined) => Promise = async (section, scopeUri) => getSettings?.(section, scopeUri); + const customPlugins = loadCustomPlugins(vueHost.getCurrentDirectory()).map(plugin => defineLanguageServicePlugin(plugin)); const vuePlugin = defineLanguageServicePlugin( useVuePlugin({ getSettings: _getSettings, @@ -247,6 +248,7 @@ export function createLanguageService( const allPlugins = new Map(); for (const plugin of [ + ...customPlugins, vuePlugin, cssPlugin, vueTemplateHtmlPlugin, @@ -273,6 +275,7 @@ export function createLanguageService( getTextDocument: tsRuntime.getHostDocument, getPlugins: lsType => { let plugins = [ + ...customPlugins, vuePlugin, cssPlugin, vueTemplateHtmlPlugin, @@ -502,3 +505,15 @@ export function createLanguageService( return new Proxy(api, handler); } } + +export function loadCustomPlugins(dir: string) { + try { + const configPath = require.resolve('./volar.config.js', { paths: [dir] }); + const config: { plugins?: EmbeddedLanguagePlugin[] } = require(configPath); + return config.plugins ?? [] + } + catch (err) { + console.error('load volar.config.js failed:', err); + return []; + } +} diff --git a/packages/vue-language-service/src/types.ts b/packages/vue-language-service/src/types.ts index e843bf3973..d07a261dfa 100644 --- a/packages/vue-language-service/src/types.ts +++ b/packages/vue-language-service/src/types.ts @@ -3,7 +3,7 @@ import type * as css from 'vscode-css-languageservice'; import type { TextDocument } from 'vscode-css-languageservice'; import type * as json from 'vscode-json-languageservice'; import { LanguageServicePlugin } from './languageService'; -import { EmbeddedLanguagePlugin } from './utils/definePlugin'; +import { EmbeddedLanguagePlugin } from '@volar/vue-language-service-types'; export type LanguageServiceHost = LanguageServiceHostBase & { schemaRequestService?: json.SchemaRequestService, diff --git a/packages/vue-language-service/src/utils/definePlugin.ts b/packages/vue-language-service/src/utils/definePlugin.ts index 90af0ae58c..8fb72f9d73 100644 --- a/packages/vue-language-service/src/utils/definePlugin.ts +++ b/packages/vue-language-service/src/utils/definePlugin.ts @@ -1,84 +1,5 @@ -import { TextDocument } from 'vscode-languageserver-textdocument'; -import * as vscode from 'vscode-languageserver-protocol'; import { Embedded, EmbeddedDocumentSourceMap } from '@volar/vue-typescript'; -type NotNullableResult = T | Thenable; -type NullableResult = NotNullableResult; - -export type SemanticToken = [number, number, number, number, number]; - -export interface ExecuteCommandContext { - token: vscode.CancellationToken; - workDoneProgress: { - begin(title: string, percentage?: number, message?: string, cancellable?: boolean): void; - report(percentage: number): void; - report(message: string): void; - report(percentage: number, message: string): void; - done(): void; - }; - sendNotification

(type: vscode.NotificationType

, params: P): Promise; - applyEdit(paramOrEdit: vscode.ApplyWorkspaceEditParams | vscode.WorkspaceEdit): Promise; -} - -export type EmbeddedLanguagePlugin = { - doValidation?(document: TextDocument, options: { - semantic?: boolean; - syntactic?: boolean; - suggestion?: boolean; - declaration?: boolean; - }): NullableResult; - doComplete?(document: TextDocument, position: vscode.Position, context?: vscode.CompletionContext): NullableResult, - doCompleteResolve?(item: vscode.CompletionItem, newPosition?: vscode.Position): NotNullableResult, - doHover?(document: TextDocument, position: vscode.Position): NullableResult, - findDefinition?(document: TextDocument, position: vscode.Position): NullableResult; - findTypeDefinition?(document: TextDocument, position: vscode.Position): NullableResult; - findImplementations?(document: TextDocument, position: vscode.Position): NullableResult; - findReferences?(document: TextDocument, position: vscode.Position): NullableResult; - findDocumentHighlights?(document: TextDocument, position: vscode.Position): NullableResult; - findDocumentLinks?(document: TextDocument): NullableResult; - findDocumentSymbols?(document: TextDocument): NullableResult; - findDocumentSemanticTokens?(document: TextDocument, range?: vscode.Range, cancleToken?: vscode.CancellationToken): NullableResult; - findWorkspaceSymbols?(query: string): NullableResult; - doCodeActions?(document: TextDocument, range: vscode.Range, context: vscode.CodeActionContext): NullableResult; - doCodeActionResolve?(codeAction: vscode.CodeAction): NotNullableResult; - doCodeLens?(document: TextDocument): NullableResult; - doCodeLensResolve?(codeLens: vscode.CodeLens): NotNullableResult; - doExecuteCommand?(command: string, args: any[], context: ExecuteCommandContext): NotNullableResult; - findDocumentColors?(document: TextDocument): NullableResult; - getColorPresentations?(document: TextDocument, color: vscode.Color, range: vscode.Range): NullableResult; - doRenamePrepare?(document: TextDocument, position: vscode.Position): NullableResult>; - doRename?(document: TextDocument, position: vscode.Position, newName: string): NullableResult; - doFileRename?(oldUri: string, newUri: string): NullableResult; - getFoldingRanges?(document: TextDocument): NullableResult; - getSelectionRanges?(document: TextDocument, positions: vscode.Position[]): NullableResult; - getSignatureHelp?(document: TextDocument, position: vscode.Position, context?: vscode.SignatureHelpContext): NullableResult; - format?(document: TextDocument, range: vscode.Range | undefined, options: vscode.FormattingOptions): NullableResult; - - callHierarchy?: { - doPrepare(document: TextDocument, position: vscode.Position): NullableResult; - getIncomingCalls(item: vscode.CallHierarchyItem): NotNullableResult; - getOutgoingCalls(item: vscode.CallHierarchyItem): NotNullableResult; - }, - - // html - findLinkedEditingRanges?(document: TextDocument, position: vscode.Position): NullableResult; - doAutoInsert?(document: TextDocument, position: vscode.Position, context: { - lastChange: { - range: vscode.Range; - rangeOffset: number; - rangeLength: number; - text: string; - }, - }): NullableResult; - - /** - * TODO: only support to doCompleteResolve for now - */ - resolveEmbeddedRange?(range: vscode.Range, sourceMap: EmbeddedDocumentSourceMap): vscode.Range | undefined; - - // findMatchingTagPosition?(document: TextDocument, position: vscode.Position, htmlDocument: HTMLDocument): vscode.Position | null; -}; - export async function visitEmbedded(embeddeds: Embedded[], cb: (sourceMap: EmbeddedDocumentSourceMap) => Promise) { for (const embedded of embeddeds) { diff --git a/packages/vue-language-service/src/utils/featureWorkers.ts b/packages/vue-language-service/src/utils/featureWorkers.ts index 8d550d3eb2..20708fa43f 100644 --- a/packages/vue-language-service/src/utils/featureWorkers.ts +++ b/packages/vue-language-service/src/utils/featureWorkers.ts @@ -1,8 +1,9 @@ import type { EmbeddedDocumentSourceMap } from '@volar/vue-typescript'; import type { TextDocument } from 'vscode-languageserver-textdocument'; import { LanguageServicePlugin } from '../languageService'; -import { EmbeddedLanguagePlugin, visitEmbedded } from './definePlugin'; +import { visitEmbedded } from './definePlugin'; import type { DocumentServiceRuntimeContext, LanguageServiceRuntimeContext } from '../types'; +import { EmbeddedLanguagePlugin } from '@volar/vue-language-service-types'; export async function documentFeatureWorker( context: DocumentServiceRuntimeContext, diff --git a/packages/vue-language-service/src/vuePlugins/autoCompleteRefs.ts b/packages/vue-language-service/src/vuePlugins/autoCompleteRefs.ts index 78772ec998..835f120aec 100644 --- a/packages/vue-language-service/src/vuePlugins/autoCompleteRefs.ts +++ b/packages/vue-language-service/src/vuePlugins/autoCompleteRefs.ts @@ -5,7 +5,7 @@ import * as ts2 from '@volar/typescript-language-service'; import type * as ts from 'typescript/lib/tsserverlibrary'; import { hyphenate } from '@vue/shared'; import { isTsDocument } from '../commonPlugins/typescript'; -import { EmbeddedLanguagePlugin } from '../utils/definePlugin'; +import { EmbeddedLanguagePlugin } from '@volar/vue-language-service-types'; export default function (host: { getSettings: (section: string, scopeUri?: string | undefined) => Promise, diff --git a/packages/vue-language-service/src/vuePlugins/autoWrapParentheses.ts b/packages/vue-language-service/src/vuePlugins/autoWrapParentheses.ts index a69cfcd738..629c07e907 100644 --- a/packages/vue-language-service/src/vuePlugins/autoWrapParentheses.ts +++ b/packages/vue-language-service/src/vuePlugins/autoWrapParentheses.ts @@ -1,6 +1,6 @@ import type { TextDocument } from 'vscode-languageserver-textdocument'; import * as vscode from 'vscode-languageserver-protocol'; -import { EmbeddedLanguagePlugin } from '../utils/definePlugin'; +import { EmbeddedLanguagePlugin } from '@volar/vue-language-service-types'; import { VueDocument } from '@volar/vue-typescript'; import { isCharacterTyping } from './autoCompleteRefs'; import * as shared from '@volar/shared'; diff --git a/packages/vue-language-service/src/vuePlugins/htmlPugConversions.ts b/packages/vue-language-service/src/vuePlugins/htmlPugConversions.ts index 7475e95611..2cff96bbff 100644 --- a/packages/vue-language-service/src/vuePlugins/htmlPugConversions.ts +++ b/packages/vue-language-service/src/vuePlugins/htmlPugConversions.ts @@ -1,6 +1,6 @@ import { VueDocument } from '@volar/vue-typescript'; import * as vscode from 'vscode-languageserver-protocol'; -import { EmbeddedLanguagePlugin } from '../utils/definePlugin'; +import { EmbeddedLanguagePlugin } from '@volar/vue-language-service-types'; import { htmlToPug, pugToHtml } from '@volar/html2pug'; const toggleConvertCommand = 'htmlPugConversions.toggle'; diff --git a/packages/vue-language-service/src/vuePlugins/refSugarConversions.ts b/packages/vue-language-service/src/vuePlugins/refSugarConversions.ts index fb82c03491..a337be364b 100644 --- a/packages/vue-language-service/src/vuePlugins/refSugarConversions.ts +++ b/packages/vue-language-service/src/vuePlugins/refSugarConversions.ts @@ -4,7 +4,7 @@ import { parseDeclarationRanges, parseDotValueRanges } from '@volar/vue-code-gen import { VueDocument } from '@volar/vue-typescript'; import * as vscode from 'vscode-languageserver-protocol'; import { margeWorkspaceEdits } from '../languageFuatures/rename'; -import { EmbeddedLanguagePlugin, ExecuteCommandContext } from '../utils/definePlugin'; +import { EmbeddedLanguagePlugin, ExecuteCommandContext } from '@volar/vue-language-service-types'; import { isBlacklistNode, isRefType } from './autoCompleteRefs'; import { getAddMissingImportsEdits } from './scriptSetupConversions'; diff --git a/packages/vue-language-service/src/vuePlugins/referencesCodeLens.ts b/packages/vue-language-service/src/vuePlugins/referencesCodeLens.ts index ea0c5daca9..430ee2d27e 100644 --- a/packages/vue-language-service/src/vuePlugins/referencesCodeLens.ts +++ b/packages/vue-language-service/src/vuePlugins/referencesCodeLens.ts @@ -1,6 +1,6 @@ import { VueDocument } from '@volar/vue-typescript'; import * as vscode from 'vscode-languageserver-protocol'; -import { EmbeddedLanguagePlugin } from '../utils/definePlugin'; +import { EmbeddedLanguagePlugin } from '@volar/vue-language-service-types'; import * as shared from '@volar/shared'; const showReferencesCommand = 'volar.show-references'; diff --git a/packages/vue-language-service/src/vuePlugins/scriptSetupConversions.ts b/packages/vue-language-service/src/vuePlugins/scriptSetupConversions.ts index 7f95348470..d76520b8cd 100644 --- a/packages/vue-language-service/src/vuePlugins/scriptSetupConversions.ts +++ b/packages/vue-language-service/src/vuePlugins/scriptSetupConversions.ts @@ -3,7 +3,7 @@ import { parseUnuseScriptSetupRanges, parseUseScriptSetupRanges } from '@volar/v import type { TextRange } from '@volar/vue-code-gen/out/types'; import { VueDocument } from '@volar/vue-typescript'; import * as vscode from 'vscode-languageserver-protocol'; -import { EmbeddedLanguagePlugin, ExecuteCommandContext } from '../utils/definePlugin'; +import { EmbeddedLanguagePlugin, ExecuteCommandContext } from '@volar/vue-language-service-types'; enum Commands { USE_SETUP_SUGAR = 'scriptSetupConversions.use', diff --git a/packages/vue-language-service/src/vuePlugins/tagNameCasingConversions.ts b/packages/vue-language-service/src/vuePlugins/tagNameCasingConversions.ts index 4caa00a967..c457a0a8e6 100644 --- a/packages/vue-language-service/src/vuePlugins/tagNameCasingConversions.ts +++ b/packages/vue-language-service/src/vuePlugins/tagNameCasingConversions.ts @@ -1,6 +1,6 @@ import { VueDocument } from '@volar/vue-typescript'; import * as vscode from 'vscode-languageserver-protocol'; -import { EmbeddedLanguagePlugin } from '../utils/definePlugin'; +import { EmbeddedLanguagePlugin } from '@volar/vue-language-service-types'; import { hyphenate } from '@vue/shared'; export const convertTagNameCasingCommand = 'tagNameCasingConversions'; diff --git a/packages/vue-language-service/src/vuePlugins/vue.ts b/packages/vue-language-service/src/vuePlugins/vue.ts index 039daff665..a43d45cba2 100644 --- a/packages/vue-language-service/src/vuePlugins/vue.ts +++ b/packages/vue-language-service/src/vuePlugins/vue.ts @@ -1,4 +1,4 @@ -import { EmbeddedLanguagePlugin } from '../utils/definePlugin'; +import { EmbeddedLanguagePlugin } from '@volar/vue-language-service-types'; import * as html from 'vscode-html-languageservice'; import { TextDocument } from 'vscode-languageserver-textdocument'; import { VueDocument } from '@volar/vue-typescript'; diff --git a/packages/vue-language-service/src/vuePlugins/vueTemplateLanguage.ts b/packages/vue-language-service/src/vuePlugins/vueTemplateLanguage.ts index f4541139b8..ad58cd1280 100644 --- a/packages/vue-language-service/src/vuePlugins/vueTemplateLanguage.ts +++ b/packages/vue-language-service/src/vuePlugins/vueTemplateLanguage.ts @@ -11,7 +11,7 @@ import type * as ts2 from '@volar/typescript-language-service'; import type { Data } from '@volar/typescript-language-service/src/services/completion'; import type { LanguageServiceHost } from '../types'; import { untrack } from '../utils/untrack'; -import { EmbeddedLanguagePlugin } from '../utils/definePlugin'; +import { EmbeddedLanguagePlugin } from '@volar/vue-language-service-types'; export const semanticTokenTypes = [ 'componentTag', diff --git a/packages/vue-language-service/tsconfig.build.json b/packages/vue-language-service/tsconfig.build.json index 558aaef469..4303b815ff 100644 --- a/packages/vue-language-service/tsconfig.build.json +++ b/packages/vue-language-service/tsconfig.build.json @@ -33,6 +33,9 @@ { "path": "../vue-code-gen/tsconfig.build.json" }, + { + "path": "../vue-language-service-types/tsconfig.build.json" + }, { "path": "../vue-typescript/tsconfig.build.json" }, diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index dcd28d040a..66f36562e8 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -255,6 +255,7 @@ importers: '@volar/transforms': 0.32.1 '@volar/typescript-language-service': 0.32.1 '@volar/vue-code-gen': 0.32.1 + '@volar/vue-language-service-types': 0.32.1 '@volar/vue-typescript': 0.32.1 '@vscode/emmet-helper': ^2.8.3 '@vue/reactivity': ^3.2.27 @@ -278,6 +279,7 @@ importers: '@volar/transforms': link:../transforms '@volar/typescript-language-service': link:../typescript-language-service '@volar/vue-code-gen': link:../vue-code-gen + '@volar/vue-language-service-types': link:../vue-language-service-types '@volar/vue-typescript': link:../vue-typescript '@vscode/emmet-helper': 2.8.4 '@vue/reactivity': 3.2.31 @@ -294,6 +296,16 @@ importers: '@types/prettier': 1.19.1 typescript: 4.6.2 + packages/vue-language-service-types: + specifiers: + '@volar/vue-typescript': 0.32.1 + vscode-languageserver-protocol: ^3.17.0-next.12 + vscode-languageserver-textdocument: ^1.0.3 + dependencies: + '@volar/vue-typescript': link:../vue-typescript + vscode-languageserver-protocol: 3.17.0-next.14 + vscode-languageserver-textdocument: 1.0.4 + packages/vue-language-service/testCases: specifiers: vue: ^3.2.27