@macroforge/svelte-language-server 0.1.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +331 -0
- package/bin/server.js +5 -0
- package/dist/src/importPackage.d.ts +16 -0
- package/dist/src/importPackage.js +76 -0
- package/dist/src/importPackage.js.map +1 -0
- package/dist/src/index.d.ts +3 -0
- package/dist/src/index.js +23 -0
- package/dist/src/index.js.map +1 -0
- package/dist/src/lib/DiagnosticsManager.d.ts +20 -0
- package/dist/src/lib/DiagnosticsManager.js +71 -0
- package/dist/src/lib/DiagnosticsManager.js.map +1 -0
- package/dist/src/lib/FallbackWatcher.d.ts +15 -0
- package/dist/src/lib/FallbackWatcher.js +66 -0
- package/dist/src/lib/FallbackWatcher.js.map +1 -0
- package/dist/src/lib/documentHighlight/wordHighlight.d.ts +3 -0
- package/dist/src/lib/documentHighlight/wordHighlight.js +62 -0
- package/dist/src/lib/documentHighlight/wordHighlight.js.map +1 -0
- package/dist/src/lib/documents/Document.d.ts +63 -0
- package/dist/src/lib/documents/Document.js +138 -0
- package/dist/src/lib/documents/Document.js.map +1 -0
- package/dist/src/lib/documents/DocumentBase.d.ts +65 -0
- package/dist/src/lib/documents/DocumentBase.js +69 -0
- package/dist/src/lib/documents/DocumentBase.js.map +1 -0
- package/dist/src/lib/documents/DocumentManager.d.ts +28 -0
- package/dist/src/lib/documents/DocumentManager.js +122 -0
- package/dist/src/lib/documents/DocumentManager.js.map +1 -0
- package/dist/src/lib/documents/DocumentMapper.d.ts +88 -0
- package/dist/src/lib/documents/DocumentMapper.js +258 -0
- package/dist/src/lib/documents/DocumentMapper.js.map +1 -0
- package/dist/src/lib/documents/configLoader.d.ts +80 -0
- package/dist/src/lib/documents/configLoader.js +265 -0
- package/dist/src/lib/documents/configLoader.js.map +1 -0
- package/dist/src/lib/documents/fileCollection.d.ts +41 -0
- package/dist/src/lib/documents/fileCollection.js +87 -0
- package/dist/src/lib/documents/fileCollection.js.map +1 -0
- package/dist/src/lib/documents/index.d.ts +5 -0
- package/dist/src/lib/documents/index.js +22 -0
- package/dist/src/lib/documents/index.js.map +1 -0
- package/dist/src/lib/documents/parseHtml.d.ts +13 -0
- package/dist/src/lib/documents/parseHtml.js +160 -0
- package/dist/src/lib/documents/parseHtml.js.map +1 -0
- package/dist/src/lib/documents/utils.d.ts +105 -0
- package/dist/src/lib/documents/utils.js +410 -0
- package/dist/src/lib/documents/utils.js.map +1 -0
- package/dist/src/lib/foldingRange/indentFolding.d.ts +26 -0
- package/dist/src/lib/foldingRange/indentFolding.js +142 -0
- package/dist/src/lib/foldingRange/indentFolding.js.map +1 -0
- package/dist/src/lib/semanticToken/semanticTokenLegend.d.ts +33 -0
- package/dist/src/lib/semanticToken/semanticTokenLegend.js +37 -0
- package/dist/src/lib/semanticToken/semanticTokenLegend.js.map +1 -0
- package/dist/src/logger.d.ts +9 -0
- package/dist/src/logger.js +28 -0
- package/dist/src/logger.js.map +1 -0
- package/dist/src/ls-config.d.ts +326 -0
- package/dist/src/ls-config.js +386 -0
- package/dist/src/ls-config.js.map +1 -0
- package/dist/src/plugins/PluginHost.d.ts +68 -0
- package/dist/src/plugins/PluginHost.js +447 -0
- package/dist/src/plugins/PluginHost.js.map +1 -0
- package/dist/src/plugins/css/CSSDocument.d.ts +46 -0
- package/dist/src/plugins/css/CSSDocument.js +78 -0
- package/dist/src/plugins/css/CSSDocument.js.map +1 -0
- package/dist/src/plugins/css/CSSPlugin.d.ts +35 -0
- package/dist/src/plugins/css/CSSPlugin.js +407 -0
- package/dist/src/plugins/css/CSSPlugin.js.map +1 -0
- package/dist/src/plugins/css/FileSystemProvider.d.ts +10 -0
- package/dist/src/plugins/css/FileSystemProvider.js +75 -0
- package/dist/src/plugins/css/FileSystemProvider.js.map +1 -0
- package/dist/src/plugins/css/StyleAttributeDocument.d.ts +41 -0
- package/dist/src/plugins/css/StyleAttributeDocument.js +65 -0
- package/dist/src/plugins/css/StyleAttributeDocument.js.map +1 -0
- package/dist/src/plugins/css/features/getIdClassCompletion.d.ts +19 -0
- package/dist/src/plugins/css/features/getIdClassCompletion.js +56 -0
- package/dist/src/plugins/css/features/getIdClassCompletion.js.map +1 -0
- package/dist/src/plugins/css/features/svelte-selectors.d.ts +2 -0
- package/dist/src/plugins/css/features/svelte-selectors.js +18 -0
- package/dist/src/plugins/css/features/svelte-selectors.js.map +1 -0
- package/dist/src/plugins/css/global-vars.d.ts +16 -0
- package/dist/src/plugins/css/global-vars.js +82 -0
- package/dist/src/plugins/css/global-vars.js.map +1 -0
- package/dist/src/plugins/css/service.d.ts +5 -0
- package/dist/src/plugins/css/service.js +66 -0
- package/dist/src/plugins/css/service.js.map +1 -0
- package/dist/src/plugins/documentContext.d.ts +3 -0
- package/dist/src/plugins/documentContext.js +36 -0
- package/dist/src/plugins/documentContext.js.map +1 -0
- package/dist/src/plugins/html/HTMLPlugin.d.ts +36 -0
- package/dist/src/plugins/html/HTMLPlugin.js +363 -0
- package/dist/src/plugins/html/HTMLPlugin.js.map +1 -0
- package/dist/src/plugins/html/dataProvider.d.ts +1 -0
- package/dist/src/plugins/html/dataProvider.js +481 -0
- package/dist/src/plugins/html/dataProvider.js.map +1 -0
- package/dist/src/plugins/index.d.ts +7 -0
- package/dist/src/plugins/index.js +24 -0
- package/dist/src/plugins/index.js.map +1 -0
- package/dist/src/plugins/interfaces.d.ts +132 -0
- package/dist/src/plugins/interfaces.js +3 -0
- package/dist/src/plugins/interfaces.js.map +1 -0
- package/dist/src/plugins/svelte/SvelteDocument.d.ts +98 -0
- package/dist/src/plugins/svelte/SvelteDocument.js +318 -0
- package/dist/src/plugins/svelte/SvelteDocument.js.map +1 -0
- package/dist/src/plugins/svelte/SveltePlugin.d.ts +24 -0
- package/dist/src/plugins/svelte/SveltePlugin.js +306 -0
- package/dist/src/plugins/svelte/SveltePlugin.js.map +1 -0
- package/dist/src/plugins/svelte/features/SvelteTags.d.ts +28 -0
- package/dist/src/plugins/svelte/features/SvelteTags.js +136 -0
- package/dist/src/plugins/svelte/features/SvelteTags.js.map +1 -0
- package/dist/src/plugins/svelte/features/getCodeActions/getQuickfixes.d.ts +11 -0
- package/dist/src/plugins/svelte/features/getCodeActions/getQuickfixes.js +140 -0
- package/dist/src/plugins/svelte/features/getCodeActions/getQuickfixes.js.map +1 -0
- package/dist/src/plugins/svelte/features/getCodeActions/getRefactorings.d.ts +9 -0
- package/dist/src/plugins/svelte/features/getCodeActions/getRefactorings.js +140 -0
- package/dist/src/plugins/svelte/features/getCodeActions/getRefactorings.js.map +1 -0
- package/dist/src/plugins/svelte/features/getCodeActions/index.d.ts +4 -0
- package/dist/src/plugins/svelte/features/getCodeActions/index.js +19 -0
- package/dist/src/plugins/svelte/features/getCodeActions/index.js.map +1 -0
- package/dist/src/plugins/svelte/features/getCompletions.d.ts +4 -0
- package/dist/src/plugins/svelte/features/getCompletions.js +184 -0
- package/dist/src/plugins/svelte/features/getCompletions.js.map +1 -0
- package/dist/src/plugins/svelte/features/getDiagnostics.d.ts +9 -0
- package/dist/src/plugins/svelte/features/getDiagnostics.js +284 -0
- package/dist/src/plugins/svelte/features/getDiagnostics.js.map +1 -0
- package/dist/src/plugins/svelte/features/getHoverInfo.d.ts +7 -0
- package/dist/src/plugins/svelte/features/getHoverInfo.js +93 -0
- package/dist/src/plugins/svelte/features/getHoverInfo.js.map +1 -0
- package/dist/src/plugins/svelte/features/getModifierData.d.ts +7 -0
- package/dist/src/plugins/svelte/features/getModifierData.js +56 -0
- package/dist/src/plugins/svelte/features/getModifierData.js.map +1 -0
- package/dist/src/plugins/svelte/features/getSelectionRanges.d.ts +3 -0
- package/dist/src/plugins/svelte/features/getSelectionRanges.js +46 -0
- package/dist/src/plugins/svelte/features/getSelectionRanges.js.map +1 -0
- package/dist/src/plugins/svelte/features/utils.d.ts +5 -0
- package/dist/src/plugins/svelte/features/utils.js +18 -0
- package/dist/src/plugins/svelte/features/utils.js.map +1 -0
- package/dist/src/plugins/typescript/ComponentInfoProvider.d.ts +27 -0
- package/dist/src/plugins/typescript/ComponentInfoProvider.js +116 -0
- package/dist/src/plugins/typescript/ComponentInfoProvider.js.map +1 -0
- package/dist/src/plugins/typescript/DocumentMapper.d.ts +10 -0
- package/dist/src/plugins/typescript/DocumentMapper.js +25 -0
- package/dist/src/plugins/typescript/DocumentMapper.js.map +1 -0
- package/dist/src/plugins/typescript/DocumentSnapshot.d.ts +173 -0
- package/dist/src/plugins/typescript/DocumentSnapshot.js +623 -0
- package/dist/src/plugins/typescript/DocumentSnapshot.js.map +1 -0
- package/dist/src/plugins/typescript/LSAndTSDocResolver.d.ts +100 -0
- package/dist/src/plugins/typescript/LSAndTSDocResolver.js +301 -0
- package/dist/src/plugins/typescript/LSAndTSDocResolver.js.map +1 -0
- package/dist/src/plugins/typescript/SnapshotManager.d.ts +59 -0
- package/dist/src/plugins/typescript/SnapshotManager.js +238 -0
- package/dist/src/plugins/typescript/SnapshotManager.js.map +1 -0
- package/dist/src/plugins/typescript/TypeScriptPlugin.d.ts +65 -0
- package/dist/src/plugins/typescript/TypeScriptPlugin.js +332 -0
- package/dist/src/plugins/typescript/TypeScriptPlugin.js.map +1 -0
- package/dist/src/plugins/typescript/features/CallHierarchyProvider.d.ts +24 -0
- package/dist/src/plugins/typescript/features/CallHierarchyProvider.js +325 -0
- package/dist/src/plugins/typescript/features/CallHierarchyProvider.js.map +1 -0
- package/dist/src/plugins/typescript/features/CodeActionsProvider.d.ts +72 -0
- package/dist/src/plugins/typescript/features/CodeActionsProvider.js +1030 -0
- package/dist/src/plugins/typescript/features/CodeActionsProvider.js.map +1 -0
- package/dist/src/plugins/typescript/features/CodeLensProvider.d.ts +28 -0
- package/dist/src/plugins/typescript/features/CodeLensProvider.js +205 -0
- package/dist/src/plugins/typescript/features/CodeLensProvider.js.map +1 -0
- package/dist/src/plugins/typescript/features/CompletionProvider.d.ts +57 -0
- package/dist/src/plugins/typescript/features/CompletionProvider.js +791 -0
- package/dist/src/plugins/typescript/features/CompletionProvider.js.map +1 -0
- package/dist/src/plugins/typescript/features/DiagnosticsProvider.d.ts +36 -0
- package/dist/src/plugins/typescript/features/DiagnosticsProvider.js +497 -0
- package/dist/src/plugins/typescript/features/DiagnosticsProvider.js.map +1 -0
- package/dist/src/plugins/typescript/features/DocumentHighlightProvider.d.ts +17 -0
- package/dist/src/plugins/typescript/features/DocumentHighlightProvider.js +211 -0
- package/dist/src/plugins/typescript/features/DocumentHighlightProvider.js.map +1 -0
- package/dist/src/plugins/typescript/features/FindComponentReferencesProvider.d.ts +9 -0
- package/dist/src/plugins/typescript/features/FindComponentReferencesProvider.js +66 -0
- package/dist/src/plugins/typescript/features/FindComponentReferencesProvider.js.map +1 -0
- package/dist/src/plugins/typescript/features/FindFileReferencesProvider.d.ts +9 -0
- package/dist/src/plugins/typescript/features/FindFileReferencesProvider.js +38 -0
- package/dist/src/plugins/typescript/features/FindFileReferencesProvider.js.map +1 -0
- package/dist/src/plugins/typescript/features/FindReferencesProvider.d.ts +20 -0
- package/dist/src/plugins/typescript/features/FindReferencesProvider.js +149 -0
- package/dist/src/plugins/typescript/features/FindReferencesProvider.js.map +1 -0
- package/dist/src/plugins/typescript/features/FoldingRangeProvider.d.ts +28 -0
- package/dist/src/plugins/typescript/features/FoldingRangeProvider.js +247 -0
- package/dist/src/plugins/typescript/features/FoldingRangeProvider.js.map +1 -0
- package/dist/src/plugins/typescript/features/HoverProvider.d.ts +11 -0
- package/dist/src/plugins/typescript/features/HoverProvider.js +75 -0
- package/dist/src/plugins/typescript/features/HoverProvider.js.map +1 -0
- package/dist/src/plugins/typescript/features/ImplementationProvider.d.ts +9 -0
- package/dist/src/plugins/typescript/features/ImplementationProvider.js +47 -0
- package/dist/src/plugins/typescript/features/ImplementationProvider.js.map +1 -0
- package/dist/src/plugins/typescript/features/InlayHintProvider.d.ts +22 -0
- package/dist/src/plugins/typescript/features/InlayHintProvider.js +225 -0
- package/dist/src/plugins/typescript/features/InlayHintProvider.js.map +1 -0
- package/dist/src/plugins/typescript/features/RenameProvider.d.ts +53 -0
- package/dist/src/plugins/typescript/features/RenameProvider.js +423 -0
- package/dist/src/plugins/typescript/features/RenameProvider.js.map +1 -0
- package/dist/src/plugins/typescript/features/SelectionRangeProvider.d.ts +18 -0
- package/dist/src/plugins/typescript/features/SelectionRangeProvider.js +62 -0
- package/dist/src/plugins/typescript/features/SelectionRangeProvider.js.map +1 -0
- package/dist/src/plugins/typescript/features/SemanticTokensProvider.d.ts +15 -0
- package/dist/src/plugins/typescript/features/SemanticTokensProvider.js +116 -0
- package/dist/src/plugins/typescript/features/SemanticTokensProvider.js.map +1 -0
- package/dist/src/plugins/typescript/features/SignatureHelpProvider.d.ts +22 -0
- package/dist/src/plugins/typescript/features/SignatureHelpProvider.js +110 -0
- package/dist/src/plugins/typescript/features/SignatureHelpProvider.js.map +1 -0
- package/dist/src/plugins/typescript/features/TypeDefinitionProvider.d.ts +9 -0
- package/dist/src/plugins/typescript/features/TypeDefinitionProvider.js +35 -0
- package/dist/src/plugins/typescript/features/TypeDefinitionProvider.js.map +1 -0
- package/dist/src/plugins/typescript/features/UpdateImportsProvider.d.ts +11 -0
- package/dist/src/plugins/typescript/features/UpdateImportsProvider.js +109 -0
- package/dist/src/plugins/typescript/features/UpdateImportsProvider.js.map +1 -0
- package/dist/src/plugins/typescript/features/WorkspaceSymbolProvider.d.ts +25 -0
- package/dist/src/plugins/typescript/features/WorkspaceSymbolProvider.js +176 -0
- package/dist/src/plugins/typescript/features/WorkspaceSymbolProvider.js.map +1 -0
- package/dist/src/plugins/typescript/features/getDirectiveCommentCompletions.d.ts +13 -0
- package/dist/src/plugins/typescript/features/getDirectiveCommentCompletions.js +58 -0
- package/dist/src/plugins/typescript/features/getDirectiveCommentCompletions.js.map +1 -0
- package/dist/src/plugins/typescript/features/getJsDocTemplateCompletion.d.ts +4 -0
- package/dist/src/plugins/typescript/features/getJsDocTemplateCompletion.js +57 -0
- package/dist/src/plugins/typescript/features/getJsDocTemplateCompletion.js.map +1 -0
- package/dist/src/plugins/typescript/features/utils.d.ts +81 -0
- package/dist/src/plugins/typescript/features/utils.js +331 -0
- package/dist/src/plugins/typescript/features/utils.js.map +1 -0
- package/dist/src/plugins/typescript/macroforgeAugmenter.d.ts +23 -0
- package/dist/src/plugins/typescript/macroforgeAugmenter.js +41 -0
- package/dist/src/plugins/typescript/macroforgeAugmenter.js.map +1 -0
- package/dist/src/plugins/typescript/module-loader.d.ts +28 -0
- package/dist/src/plugins/typescript/module-loader.js +254 -0
- package/dist/src/plugins/typescript/module-loader.js.map +1 -0
- package/dist/src/plugins/typescript/previewer.d.ts +7 -0
- package/dist/src/plugins/typescript/previewer.js +120 -0
- package/dist/src/plugins/typescript/previewer.js.map +1 -0
- package/dist/src/plugins/typescript/service.d.ts +105 -0
- package/dist/src/plugins/typescript/service.js +1073 -0
- package/dist/src/plugins/typescript/service.js.map +1 -0
- package/dist/src/plugins/typescript/serviceCache.d.ts +90 -0
- package/dist/src/plugins/typescript/serviceCache.js +50 -0
- package/dist/src/plugins/typescript/serviceCache.js.map +1 -0
- package/dist/src/plugins/typescript/svelte-ast-utils.d.ts +77 -0
- package/dist/src/plugins/typescript/svelte-ast-utils.js +100 -0
- package/dist/src/plugins/typescript/svelte-ast-utils.js.map +1 -0
- package/dist/src/plugins/typescript/svelte-sys.d.ts +9 -0
- package/dist/src/plugins/typescript/svelte-sys.js +79 -0
- package/dist/src/plugins/typescript/svelte-sys.js.map +1 -0
- package/dist/src/plugins/typescript/utils.d.ts +48 -0
- package/dist/src/plugins/typescript/utils.js +334 -0
- package/dist/src/plugins/typescript/utils.js.map +1 -0
- package/dist/src/server.d.ts +19 -0
- package/dist/src/server.js +434 -0
- package/dist/src/server.js.map +1 -0
- package/dist/src/svelte-check.d.ts +65 -0
- package/dist/src/svelte-check.js +288 -0
- package/dist/src/svelte-check.js.map +1 -0
- package/dist/src/utils.d.ts +111 -0
- package/dist/src/utils.js +337 -0
- package/dist/src/utils.js.map +1 -0
- package/dist/tsconfig.tsbuildinfo +1 -0
- package/package.json +79 -0
|
@@ -0,0 +1,791 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.CompletionsProviderImpl = void 0;
|
|
7
|
+
const path_1 = require("path");
|
|
8
|
+
const typescript_1 = __importDefault(require("typescript"));
|
|
9
|
+
const vscode_languageserver_1 = require("vscode-languageserver");
|
|
10
|
+
const documents_1 = require("../../../lib/documents");
|
|
11
|
+
const parseHtml_1 = require("../../../lib/documents/parseHtml");
|
|
12
|
+
const utils_1 = require("../../../utils");
|
|
13
|
+
const previewer_1 = require("../previewer");
|
|
14
|
+
const utils_2 = require("../utils");
|
|
15
|
+
const getJsDocTemplateCompletion_1 = require("./getJsDocTemplateCompletion");
|
|
16
|
+
const utils_3 = require("./utils");
|
|
17
|
+
const svelte_ast_utils_1 = require("../svelte-ast-utils");
|
|
18
|
+
class CompletionsProviderImpl {
|
|
19
|
+
constructor(lsAndTsDocResolver, configManager) {
|
|
20
|
+
this.lsAndTsDocResolver = lsAndTsDocResolver;
|
|
21
|
+
this.configManager = configManager;
|
|
22
|
+
/**
|
|
23
|
+
* The language service throws an error if the character is not a valid trigger character.
|
|
24
|
+
* Also, the completions are worse.
|
|
25
|
+
* Therefore, only use the characters the typescript compiler treats as valid.
|
|
26
|
+
*/
|
|
27
|
+
this.validTriggerCharacters = ['.', '"', "'", '`', '/', '@', '<', '#'];
|
|
28
|
+
this.commitCharacters = ['.', ',', ';', '('];
|
|
29
|
+
}
|
|
30
|
+
isValidTriggerCharacter(character) {
|
|
31
|
+
return this.validTriggerCharacters.includes(character);
|
|
32
|
+
}
|
|
33
|
+
async getCompletions(document, position, completionContext, cancellationToken) {
|
|
34
|
+
if ((0, documents_1.isInTag)(position, document.styleInfo)) {
|
|
35
|
+
return null;
|
|
36
|
+
}
|
|
37
|
+
const { lang: langForSyntheticOperations, tsDoc, userPreferences } = await this.lsAndTsDocResolver.getLsForSyntheticOperations(document);
|
|
38
|
+
const filePath = tsDoc.filePath;
|
|
39
|
+
if (!filePath) {
|
|
40
|
+
return null;
|
|
41
|
+
}
|
|
42
|
+
const triggerCharacter = completionContext?.triggerCharacter;
|
|
43
|
+
const triggerKind = completionContext?.triggerKind;
|
|
44
|
+
const validTriggerCharacter = this.isValidTriggerCharacter(triggerCharacter)
|
|
45
|
+
? triggerCharacter
|
|
46
|
+
: undefined;
|
|
47
|
+
const isCustomTriggerCharacter = triggerKind === vscode_languageserver_1.CompletionTriggerKind.TriggerCharacter;
|
|
48
|
+
const isJsDocTriggerCharacter = triggerCharacter === '*';
|
|
49
|
+
const isEventOrSlotLetTriggerCharacter = triggerCharacter === ':';
|
|
50
|
+
// ignore any custom trigger character specified in server capabilities
|
|
51
|
+
// and is not allow by ts
|
|
52
|
+
if (isCustomTriggerCharacter &&
|
|
53
|
+
!validTriggerCharacter &&
|
|
54
|
+
!isJsDocTriggerCharacter &&
|
|
55
|
+
!isEventOrSlotLetTriggerCharacter) {
|
|
56
|
+
return null;
|
|
57
|
+
}
|
|
58
|
+
if (this.canReuseLastCompletion(this.lastCompletion, triggerKind, triggerCharacter, document, position)) {
|
|
59
|
+
this.lastCompletion.position = position;
|
|
60
|
+
return this.lastCompletion.completionList;
|
|
61
|
+
}
|
|
62
|
+
else {
|
|
63
|
+
this.lastCompletion = undefined;
|
|
64
|
+
}
|
|
65
|
+
if (!tsDoc.isInGenerated(position)) {
|
|
66
|
+
return null;
|
|
67
|
+
}
|
|
68
|
+
const originalOffset = document.offsetAt(position);
|
|
69
|
+
let offset = tsDoc.offsetAt(tsDoc.getGeneratedPosition(position));
|
|
70
|
+
if (isJsDocTriggerCharacter) {
|
|
71
|
+
return (0, getJsDocTemplateCompletion_1.getJsDocTemplateCompletion)(tsDoc, langForSyntheticOperations, filePath, offset);
|
|
72
|
+
}
|
|
73
|
+
const svelteNode = tsDoc.svelteNodeAt(originalOffset);
|
|
74
|
+
if (
|
|
75
|
+
// Cursor is somewhere in regular HTML text
|
|
76
|
+
(svelteNode?.type === 'Text' &&
|
|
77
|
+
[
|
|
78
|
+
'Element',
|
|
79
|
+
'InlineComponent',
|
|
80
|
+
'Fragment',
|
|
81
|
+
'SlotTemplate',
|
|
82
|
+
'SnippetBlock',
|
|
83
|
+
'IfBlock',
|
|
84
|
+
'EachBlock',
|
|
85
|
+
'AwaitBlock'
|
|
86
|
+
].includes(svelteNode.parent?.type)) ||
|
|
87
|
+
// Cursor is at <div>|</div> in which case there's no TextNode inbetween
|
|
88
|
+
document.getText().substring(originalOffset - 1, originalOffset + 2) === '></') {
|
|
89
|
+
return null;
|
|
90
|
+
}
|
|
91
|
+
const { lang, lsContainer } = await this.lsAndTsDocResolver.getLSAndTSDoc(document);
|
|
92
|
+
if (cancellationToken?.isCancellationRequested) {
|
|
93
|
+
return null;
|
|
94
|
+
}
|
|
95
|
+
const inScript = (0, utils_2.isInScript)(position, tsDoc);
|
|
96
|
+
const wordInfo = this.getWordAtPosition(document, originalOffset);
|
|
97
|
+
if (!inScript &&
|
|
98
|
+
wordInfo.word[0] === '{' &&
|
|
99
|
+
(wordInfo.word[1] === '#' ||
|
|
100
|
+
wordInfo.word[1] === '@' ||
|
|
101
|
+
wordInfo.word[1] === ':' ||
|
|
102
|
+
wordInfo.word[1] === '/')) {
|
|
103
|
+
// Typing something like {/if}
|
|
104
|
+
return null;
|
|
105
|
+
}
|
|
106
|
+
// Special case: completion at `<Comp.` -> mapped one character too short -> adjust
|
|
107
|
+
if (!inScript &&
|
|
108
|
+
wordInfo.word === '' &&
|
|
109
|
+
document.getText()[originalOffset - 1] === '.' &&
|
|
110
|
+
tsDoc.getFullText()[offset] === '.') {
|
|
111
|
+
offset++;
|
|
112
|
+
}
|
|
113
|
+
const componentInfo = (0, utils_3.getComponentAtPosition)(lang, document, tsDoc, position);
|
|
114
|
+
const attributeContext = componentInfo && (0, parseHtml_1.getAttributeContextAtPosition)(document, position);
|
|
115
|
+
const eventAndSlotLetCompletions = this.getEventAndSlotLetCompletions(componentInfo, attributeContext, wordInfo.defaultTextEditRange);
|
|
116
|
+
if (isEventOrSlotLetTriggerCharacter) {
|
|
117
|
+
return vscode_languageserver_1.CompletionList.create(eventAndSlotLetCompletions, !!tsDoc.parserError);
|
|
118
|
+
}
|
|
119
|
+
const tagCompletions = componentInfo || eventAndSlotLetCompletions.length > 0
|
|
120
|
+
? []
|
|
121
|
+
: this.getCustomElementCompletions(lang, lsContainer, document, tsDoc, position);
|
|
122
|
+
const formatSettings = await this.configManager.getFormatCodeSettingsForFile(document, tsDoc.scriptKind);
|
|
123
|
+
if (cancellationToken?.isCancellationRequested) {
|
|
124
|
+
return null;
|
|
125
|
+
}
|
|
126
|
+
// one or two characters after start tag might be mapped to the component name
|
|
127
|
+
if (svelteNode?.type === 'InlineComponent' &&
|
|
128
|
+
'name' in svelteNode &&
|
|
129
|
+
typeof svelteNode.name === 'string') {
|
|
130
|
+
const name = svelteNode.name;
|
|
131
|
+
const nameEnd = svelteNode.start + 1 + name.length;
|
|
132
|
+
const isWhitespaceAfterStartTag = document.getText().slice(nameEnd, originalOffset).trim() === '' &&
|
|
133
|
+
this.mightBeAtStartTagWhitespace(document, originalOffset);
|
|
134
|
+
if (isWhitespaceAfterStartTag) {
|
|
135
|
+
// We can be sure only to get completions for directives and props here
|
|
136
|
+
// so don't bother with the expensive global completions
|
|
137
|
+
return this.getCompletionListForDirectiveOrProps(attributeContext, componentInfo, wordInfo.defaultTextEditRange, eventAndSlotLetCompletions, tsDoc);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
const response = lang.getCompletionsAtPosition(filePath, offset, {
|
|
141
|
+
...userPreferences,
|
|
142
|
+
triggerCharacter: validTriggerCharacter
|
|
143
|
+
}, formatSettings);
|
|
144
|
+
const commitCharactersOptions = this.getCommitCharactersOptions(response, tsDoc, position);
|
|
145
|
+
let completions = response?.entries || [];
|
|
146
|
+
const customCompletions = eventAndSlotLetCompletions.concat(tagCompletions ?? []);
|
|
147
|
+
if (completions.length === 0 && customCompletions.length === 0) {
|
|
148
|
+
return tsDoc.parserError ? vscode_languageserver_1.CompletionList.create([], true) : null;
|
|
149
|
+
}
|
|
150
|
+
if (completions.length > 500 &&
|
|
151
|
+
svelteNode?.type === 'Element' &&
|
|
152
|
+
completions[0].kind !== typescript_1.default.ScriptElementKind.memberVariableElement) {
|
|
153
|
+
// False global completions inside element start tag
|
|
154
|
+
return null;
|
|
155
|
+
}
|
|
156
|
+
if (completions.length > 500 &&
|
|
157
|
+
svelteNode?.type === 'InlineComponent' &&
|
|
158
|
+
this.mightBeAtStartTagWhitespace(document, originalOffset)) {
|
|
159
|
+
// Very likely false global completions inside component start tag -> narrow
|
|
160
|
+
return this.getCompletionListForDirectiveOrProps(attributeContext, componentInfo, wordInfo.defaultTextEditRange, eventAndSlotLetCompletions, tsDoc);
|
|
161
|
+
}
|
|
162
|
+
// moved here due to perf reasons
|
|
163
|
+
const existingImports = this.getExistingImports(document);
|
|
164
|
+
const fileUrl = (0, utils_1.pathToUrl)(tsDoc.filePath);
|
|
165
|
+
const isCompletionInTag = (0, svelte_ast_utils_1.isInTag)(svelteNode, originalOffset);
|
|
166
|
+
const isHandlerCompletion = svelteNode?.type === 'EventHandler' && svelteNode.parent?.type === 'Element';
|
|
167
|
+
const preferComponents = wordInfo.word[0] === '<' || inScript;
|
|
168
|
+
const completionItems = customCompletions;
|
|
169
|
+
const isValidCompletion = createIsValidCompletion(document, position, !!tsDoc.parserError);
|
|
170
|
+
const addCompletion = (entry, asStore) => {
|
|
171
|
+
if (isValidCompletion(entry)) {
|
|
172
|
+
let completion = this.toCompletionItem(tsDoc, entry, fileUrl, position, isCompletionInTag, commitCharactersOptions, asStore, existingImports, preferComponents);
|
|
173
|
+
if (completion) {
|
|
174
|
+
completionItems.push(this.fixTextEditRange(wordInfo.range, (0, documents_1.mapCompletionItemToOriginal)(tsDoc, completion), isHandlerCompletion, completion.textEdit, tsDoc));
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
};
|
|
178
|
+
// If completion is about a store which is not imported yet, do another
|
|
179
|
+
// completion request at the beginning of the file to get all global
|
|
180
|
+
// import completions and then filter them down to likely matches.
|
|
181
|
+
if (wordInfo.word.charAt(0) === '$') {
|
|
182
|
+
const storeName = wordInfo.word.substring(1);
|
|
183
|
+
const text = '__sveltets_2_store_get(' + storeName;
|
|
184
|
+
if (!tsDoc.getFullText().includes(text)) {
|
|
185
|
+
const pos = (tsDoc.scriptInfo || tsDoc.moduleScriptInfo)?.endPos ?? {
|
|
186
|
+
line: 0,
|
|
187
|
+
character: 0
|
|
188
|
+
};
|
|
189
|
+
const virtualOffset = tsDoc.offsetAt(tsDoc.getGeneratedPosition(pos));
|
|
190
|
+
const storeCompletions = lang.getCompletionsAtPosition(filePath, virtualOffset, {
|
|
191
|
+
...userPreferences,
|
|
192
|
+
triggerCharacter: validTriggerCharacter
|
|
193
|
+
}, formatSettings);
|
|
194
|
+
for (const entry of storeCompletions?.entries || []) {
|
|
195
|
+
if (entry.name.startsWith(storeName)) {
|
|
196
|
+
addCompletion(entry, true);
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
for (const entry of completions) {
|
|
202
|
+
addCompletion(entry, false);
|
|
203
|
+
}
|
|
204
|
+
// Add ./$types imports for SvelteKit since TypeScript is bad at it
|
|
205
|
+
if ((0, path_1.basename)(filePath).startsWith('+')) {
|
|
206
|
+
const $typeImports = new Map();
|
|
207
|
+
for (const c of completionItems) {
|
|
208
|
+
if ((0, utils_3.isKitTypePath)(c.data?.source)) {
|
|
209
|
+
$typeImports.set(c.label, c);
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
for (const $typeImport of $typeImports.values()) {
|
|
213
|
+
// resolve path from filePath to svelte-kit/types
|
|
214
|
+
// src/routes/foo/+page.svelte -> .svelte-kit/types/foo/$types.d.ts
|
|
215
|
+
const routesFolder = document.config?.kit?.files?.routes || 'src/routes';
|
|
216
|
+
const relativeFileName = filePath.split(routesFolder)[1]?.slice(1);
|
|
217
|
+
if (relativeFileName) {
|
|
218
|
+
const relativePath = (0, path_1.dirname)(relativeFileName) === '.' ? '' : `${(0, path_1.dirname)(relativeFileName)}/`;
|
|
219
|
+
const modifiedSource = $typeImport.data.source.split('.svelte-kit/types')[0] +
|
|
220
|
+
// note the missing .d.ts at the end - TS wants it that way for some reason
|
|
221
|
+
`.svelte-kit/types/${routesFolder}/${relativePath}$types`;
|
|
222
|
+
completionItems.push({
|
|
223
|
+
...$typeImport,
|
|
224
|
+
// Ensure it's sorted above the other imports
|
|
225
|
+
sortText: !isNaN(Number($typeImport.sortText))
|
|
226
|
+
? String(Number($typeImport.sortText) - 1)
|
|
227
|
+
: $typeImport.sortText,
|
|
228
|
+
data: {
|
|
229
|
+
...$typeImport.data,
|
|
230
|
+
__is_sveltekit$typeImport: true,
|
|
231
|
+
source: modifiedSource,
|
|
232
|
+
data: undefined
|
|
233
|
+
}
|
|
234
|
+
});
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
const completionList = vscode_languageserver_1.CompletionList.create(completionItems, !!tsDoc.parserError);
|
|
239
|
+
if (commitCharactersOptions.checkCommitCharacters &&
|
|
240
|
+
commitCharactersOptions.defaultCommitCharacters?.length) {
|
|
241
|
+
const clientSupportsItemsDefault = this.configManager
|
|
242
|
+
.getClientCapabilities()
|
|
243
|
+
?.textDocument?.completion?.completionList?.itemDefaults?.includes('commitCharacters');
|
|
244
|
+
if (clientSupportsItemsDefault) {
|
|
245
|
+
completionList.itemDefaults = {
|
|
246
|
+
commitCharacters: commitCharactersOptions.defaultCommitCharacters
|
|
247
|
+
};
|
|
248
|
+
}
|
|
249
|
+
else {
|
|
250
|
+
completionList.items.forEach((item) => {
|
|
251
|
+
item.commitCharacters ??= commitCharactersOptions.defaultCommitCharacters;
|
|
252
|
+
});
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
this.lastCompletion = { key: document.getFilePath() || '', position, completionList };
|
|
256
|
+
return completionList;
|
|
257
|
+
}
|
|
258
|
+
getWordAtPosition(document, offset) {
|
|
259
|
+
const wordRange = (0, documents_1.getWordRangeAt)(document.getText(), offset, {
|
|
260
|
+
left: /[^\s.]+$/,
|
|
261
|
+
right: /[^\w$:]/
|
|
262
|
+
});
|
|
263
|
+
const range = vscode_languageserver_1.Range.create(document.positionAt(wordRange.start), document.positionAt(wordRange.end));
|
|
264
|
+
return {
|
|
265
|
+
wordRange,
|
|
266
|
+
word: document.getText().slice(wordRange.start, wordRange.end),
|
|
267
|
+
range,
|
|
268
|
+
defaultTextEditRange: wordRange.start === wordRange.end ? undefined : range
|
|
269
|
+
};
|
|
270
|
+
}
|
|
271
|
+
mightBeAtStartTagWhitespace(document, originalOffset) {
|
|
272
|
+
return /\s[\s>/]/.test(document.getText().substring(originalOffset - 1, originalOffset + 1));
|
|
273
|
+
}
|
|
274
|
+
canReuseLastCompletion(lastCompletion, triggerKind, triggerCharacter, document, position) {
|
|
275
|
+
return (!!lastCompletion &&
|
|
276
|
+
lastCompletion.key === document.getFilePath() &&
|
|
277
|
+
lastCompletion.position.line === position.line &&
|
|
278
|
+
((Math.abs(lastCompletion.position.character - position.character) < 2 &&
|
|
279
|
+
(triggerKind === vscode_languageserver_1.CompletionTriggerKind.TriggerForIncompleteCompletions ||
|
|
280
|
+
// Special case: `.` is a trigger character, but inside import path completions
|
|
281
|
+
// it shouldn't trigger another completion because we can reuse the old one
|
|
282
|
+
(triggerCharacter === '.' &&
|
|
283
|
+
(0, utils_3.isPartOfImportStatement)(document.getText(), position)))) ||
|
|
284
|
+
// `let:` or `on:` -> up to 3 previous characters allowed
|
|
285
|
+
(Math.abs(lastCompletion.position.character - position.character) < 4 &&
|
|
286
|
+
triggerCharacter === ':' &&
|
|
287
|
+
!!(0, documents_1.getNodeIfIsInStartTag)(document.html, document.offsetAt(position)))));
|
|
288
|
+
}
|
|
289
|
+
getExistingImports(document) {
|
|
290
|
+
const rawImports = (0, utils_1.getRegExpMatches)(scriptImportRegex, document.getText()).map((match) => (match[1] ?? match[2]).split(','));
|
|
291
|
+
const tidiedImports = (0, utils_1.flatten)(rawImports).map((match) => match.trim());
|
|
292
|
+
return new Set(tidiedImports);
|
|
293
|
+
}
|
|
294
|
+
getEventAndSlotLetCompletions(componentInfo, attributeContext, defaultTextEditRange) {
|
|
295
|
+
if (componentInfo === null) {
|
|
296
|
+
return [];
|
|
297
|
+
}
|
|
298
|
+
if (attributeContext?.inValue) {
|
|
299
|
+
return [];
|
|
300
|
+
}
|
|
301
|
+
return [
|
|
302
|
+
...componentInfo
|
|
303
|
+
.getEvents()
|
|
304
|
+
.map((event) => this.componentInfoToCompletionEntry(event, 'on:', undefined, defaultTextEditRange)),
|
|
305
|
+
...componentInfo
|
|
306
|
+
.getSlotLets()
|
|
307
|
+
.map((slot) => this.componentInfoToCompletionEntry(slot, 'let:', undefined, defaultTextEditRange))
|
|
308
|
+
];
|
|
309
|
+
}
|
|
310
|
+
getCustomElementCompletions(lang, lsContainer, document, tsDoc, position) {
|
|
311
|
+
const offset = document.offsetAt(position);
|
|
312
|
+
const tag = (0, documents_1.getNodeIfIsInHTMLStartTag)(document.html, offset);
|
|
313
|
+
if (!tag) {
|
|
314
|
+
return;
|
|
315
|
+
}
|
|
316
|
+
const tagNameEnd = tag.start + 1 + (tag.tag?.length ?? 0);
|
|
317
|
+
if (offset > tagNameEnd) {
|
|
318
|
+
return;
|
|
319
|
+
}
|
|
320
|
+
const program = lang.getProgram();
|
|
321
|
+
const sourceFile = program?.getSourceFile(tsDoc.filePath);
|
|
322
|
+
const typeChecker = program?.getTypeChecker();
|
|
323
|
+
if (!typeChecker || !sourceFile) {
|
|
324
|
+
return;
|
|
325
|
+
}
|
|
326
|
+
const typingsNamespace = lsContainer.getTsConfigSvelteOptions().namespace;
|
|
327
|
+
const typingsNamespaceSymbol = this.findTypingsNamespaceSymbol(typingsNamespace, typeChecker, sourceFile);
|
|
328
|
+
if (!typingsNamespaceSymbol) {
|
|
329
|
+
return;
|
|
330
|
+
}
|
|
331
|
+
const elements = typeChecker
|
|
332
|
+
.getExportsOfModule(typingsNamespaceSymbol)
|
|
333
|
+
.find((symbol) => symbol.name === 'IntrinsicElements');
|
|
334
|
+
if (!elements || !(elements.flags & typescript_1.default.SymbolFlags.Interface)) {
|
|
335
|
+
return;
|
|
336
|
+
}
|
|
337
|
+
let tagNames = typeChecker
|
|
338
|
+
.getDeclaredTypeOfSymbol(elements)
|
|
339
|
+
.getProperties()
|
|
340
|
+
.map((p) => typescript_1.default.symbolName(p));
|
|
341
|
+
if (tagNames.length && tag.tag) {
|
|
342
|
+
tagNames = tagNames.filter((name) => name.startsWith(tag.tag ?? ''));
|
|
343
|
+
}
|
|
344
|
+
const replacementRange = (0, documents_1.toRange)(document, tag.start + 1, tagNameEnd);
|
|
345
|
+
return tagNames.map((name) => ({
|
|
346
|
+
label: name,
|
|
347
|
+
kind: vscode_languageserver_1.CompletionItemKind.Property,
|
|
348
|
+
textEdit: vscode_languageserver_1.TextEdit.replace((0, utils_2.cloneRange)(replacementRange), name),
|
|
349
|
+
commitCharacters: []
|
|
350
|
+
}));
|
|
351
|
+
}
|
|
352
|
+
findTypingsNamespaceSymbol(namespaceExpression, typeChecker, sourceFile) {
|
|
353
|
+
if (!namespaceExpression || typeof namespaceExpression !== 'string') {
|
|
354
|
+
return;
|
|
355
|
+
}
|
|
356
|
+
const [first, ...rest] = namespaceExpression.split('.');
|
|
357
|
+
let symbol = typeChecker
|
|
358
|
+
.getSymbolsInScope(sourceFile, typescript_1.default.SymbolFlags.Namespace)
|
|
359
|
+
.find((symbol) => symbol.name === first);
|
|
360
|
+
for (const part of rest) {
|
|
361
|
+
if (!symbol) {
|
|
362
|
+
return;
|
|
363
|
+
}
|
|
364
|
+
symbol = typeChecker.getExportsOfModule(symbol).find((symbol) => symbol.name === part);
|
|
365
|
+
}
|
|
366
|
+
return symbol;
|
|
367
|
+
}
|
|
368
|
+
componentInfoToCompletionEntry(info, prefix, kind, defaultTextEditRange) {
|
|
369
|
+
const name = prefix + info.name;
|
|
370
|
+
return {
|
|
371
|
+
label: name,
|
|
372
|
+
kind,
|
|
373
|
+
sortText: '-1',
|
|
374
|
+
detail: info.name + ': ' + info.type,
|
|
375
|
+
documentation: info.doc && { kind: vscode_languageserver_1.MarkupKind.Markdown, value: info.doc },
|
|
376
|
+
commitCharacters: [],
|
|
377
|
+
textEdit: defaultTextEditRange
|
|
378
|
+
? vscode_languageserver_1.TextEdit.replace((0, utils_2.cloneRange)(defaultTextEditRange), name)
|
|
379
|
+
: undefined
|
|
380
|
+
};
|
|
381
|
+
}
|
|
382
|
+
getCompletionListForDirectiveOrProps(attributeContext, componentInfo, defaultTextEditRange, eventAndSlotLetCompletions, tsDoc) {
|
|
383
|
+
const props = (!attributeContext?.inValue &&
|
|
384
|
+
componentInfo
|
|
385
|
+
?.getProps()
|
|
386
|
+
.map((entry) => this.componentInfoToCompletionEntry(entry, '', vscode_languageserver_1.CompletionItemKind.Field, defaultTextEditRange))) ||
|
|
387
|
+
[];
|
|
388
|
+
return vscode_languageserver_1.CompletionList.create([...eventAndSlotLetCompletions, ...props], !!tsDoc.parserError);
|
|
389
|
+
}
|
|
390
|
+
toCompletionItem(snapshot, comp, uri, position, isCompletionInTag, commitCharactersOptions, asStore, existingImports, preferComponents) {
|
|
391
|
+
const completionLabelAndInsert = this.getCompletionLabelAndInsert(snapshot, comp);
|
|
392
|
+
if (!completionLabelAndInsert) {
|
|
393
|
+
return null;
|
|
394
|
+
}
|
|
395
|
+
let { label, insertText, isSvelteComp, isRunesCompletion, replacementSpan } = completionLabelAndInsert;
|
|
396
|
+
// TS may suggest another Svelte component even if there already exists an import
|
|
397
|
+
// with the same name, because under the hood every Svelte component is postfixed
|
|
398
|
+
// with `__SvelteComponent`. In this case, filter out this completion by returning null.
|
|
399
|
+
if (isSvelteComp && existingImports.has(label)) {
|
|
400
|
+
return null;
|
|
401
|
+
}
|
|
402
|
+
// Remove wrong quotes, for example when using --css-props
|
|
403
|
+
if (isCompletionInTag &&
|
|
404
|
+
!insertText &&
|
|
405
|
+
label[0] === '"' &&
|
|
406
|
+
label[label.length - 1] === '"') {
|
|
407
|
+
label = label.slice(1, -1);
|
|
408
|
+
}
|
|
409
|
+
else if (asStore) {
|
|
410
|
+
// only modify label, so that the data property is untouched, which is important so the resolving still works
|
|
411
|
+
label = `$${label}`;
|
|
412
|
+
}
|
|
413
|
+
const textEdit = replacementSpan
|
|
414
|
+
? vscode_languageserver_1.TextEdit.replace((0, utils_2.convertRange)(snapshot, replacementSpan), insertText ?? label)
|
|
415
|
+
: undefined;
|
|
416
|
+
const labelDetails = comp.labelDetails ??
|
|
417
|
+
(comp.sourceDisplay
|
|
418
|
+
? {
|
|
419
|
+
description: typescript_1.default.displayPartsToString(comp.sourceDisplay)
|
|
420
|
+
}
|
|
421
|
+
: undefined);
|
|
422
|
+
return {
|
|
423
|
+
label,
|
|
424
|
+
insertText,
|
|
425
|
+
kind: (0, utils_2.scriptElementKindToCompletionItemKind)(comp.kind),
|
|
426
|
+
commitCharacters: this.getCommitCharacters(comp, commitCharactersOptions, isSvelteComp),
|
|
427
|
+
// Make sure svelte component and runes take precedence
|
|
428
|
+
sortText: preferComponents && (isRunesCompletion || isSvelteComp) ? '-1' : comp.sortText,
|
|
429
|
+
preselect: preferComponents && (isRunesCompletion || isSvelteComp) ? true : comp.isRecommended,
|
|
430
|
+
insertTextFormat: comp.isSnippet ? vscode_languageserver_1.InsertTextFormat.Snippet : undefined,
|
|
431
|
+
labelDetails,
|
|
432
|
+
textEdit,
|
|
433
|
+
// pass essential data for resolving completion
|
|
434
|
+
data: {
|
|
435
|
+
name: comp.name,
|
|
436
|
+
source: comp.source,
|
|
437
|
+
data: comp.data,
|
|
438
|
+
uri,
|
|
439
|
+
position
|
|
440
|
+
}
|
|
441
|
+
};
|
|
442
|
+
}
|
|
443
|
+
getCompletionLabelAndInsert(snapshot, comp) {
|
|
444
|
+
let { name, insertText, kindModifiers } = comp;
|
|
445
|
+
const isScriptElement = comp.kind === typescript_1.default.ScriptElementKind.scriptElement;
|
|
446
|
+
const hasModifier = Boolean(comp.kindModifiers);
|
|
447
|
+
const isRunesCompletion = name === '$props' || name === '$state' || name === '$derived' || name === '$effect';
|
|
448
|
+
const isSvelteComp = !isRunesCompletion && (0, utils_2.isGeneratedSvelteComponentName)(name);
|
|
449
|
+
if (isSvelteComp) {
|
|
450
|
+
name = (0, utils_2.changeSvelteComponentName)(name);
|
|
451
|
+
if (this.isExistingSvelteComponentImport(snapshot, name, comp.source)) {
|
|
452
|
+
return null;
|
|
453
|
+
}
|
|
454
|
+
}
|
|
455
|
+
if (isScriptElement && hasModifier) {
|
|
456
|
+
const label = kindModifiers && !name.endsWith(kindModifiers) ? name + kindModifiers : name;
|
|
457
|
+
return {
|
|
458
|
+
insertText: name,
|
|
459
|
+
label,
|
|
460
|
+
isSvelteComp,
|
|
461
|
+
isRunesCompletion
|
|
462
|
+
};
|
|
463
|
+
}
|
|
464
|
+
if (comp.replacementSpan) {
|
|
465
|
+
return {
|
|
466
|
+
label: name,
|
|
467
|
+
isSvelteComp,
|
|
468
|
+
isRunesCompletion,
|
|
469
|
+
insertText: insertText ? (0, utils_2.changeSvelteComponentName)(insertText) : undefined,
|
|
470
|
+
replacementSpan: comp.replacementSpan
|
|
471
|
+
};
|
|
472
|
+
}
|
|
473
|
+
return {
|
|
474
|
+
label: name,
|
|
475
|
+
insertText,
|
|
476
|
+
isSvelteComp,
|
|
477
|
+
isRunesCompletion
|
|
478
|
+
};
|
|
479
|
+
}
|
|
480
|
+
getCommitCharactersOptions(response, tsDoc, position) {
|
|
481
|
+
if ((!(0, utils_2.isInScript)(position, tsDoc) && tsDoc.parserError) || !response) {
|
|
482
|
+
return {
|
|
483
|
+
checkCommitCharacters: false
|
|
484
|
+
};
|
|
485
|
+
}
|
|
486
|
+
const isNewIdentifierLocation = response.isNewIdentifierLocation;
|
|
487
|
+
// TypeScript 5.7+ reused the same array for different completions
|
|
488
|
+
let defaultCommitCharacters = response.defaultCommitCharacters
|
|
489
|
+
? Array.from(response.defaultCommitCharacters)
|
|
490
|
+
: undefined;
|
|
491
|
+
if (!isNewIdentifierLocation) {
|
|
492
|
+
// This actually always exists although it's optional in the type, at least in ts 5.6,
|
|
493
|
+
// so our commit characters are mostly fallback for older ts versions
|
|
494
|
+
if (defaultCommitCharacters) {
|
|
495
|
+
// this is controlled by a vscode setting that isn't available in the ts server so it isn't added to the language service
|
|
496
|
+
defaultCommitCharacters?.push('(');
|
|
497
|
+
}
|
|
498
|
+
else {
|
|
499
|
+
defaultCommitCharacters = this.commitCharacters;
|
|
500
|
+
}
|
|
501
|
+
}
|
|
502
|
+
return {
|
|
503
|
+
checkCommitCharacters: true,
|
|
504
|
+
defaultCommitCharacters,
|
|
505
|
+
isNewIdentifierLocation
|
|
506
|
+
};
|
|
507
|
+
}
|
|
508
|
+
getCommitCharacters(entry, options, isSvelteComp) {
|
|
509
|
+
// Because Svelte components take precedence, we leave out commit characters to not auto complete
|
|
510
|
+
// in weird places (e.g. when you have foo.filter(a => a)) and get autocomplete for component A,
|
|
511
|
+
// then a commit character of `.` would auto import the component which is not what we want
|
|
512
|
+
if (isSvelteComp) {
|
|
513
|
+
return ['>'];
|
|
514
|
+
}
|
|
515
|
+
// https://github.com/microsoft/vscode/blob/d012408e88ffabd6456c367df4d343654da2eb10/extensions/typescript-language-features/src/languageFeatures/completions.ts#L504
|
|
516
|
+
if (!options.checkCommitCharacters) {
|
|
517
|
+
return undefined;
|
|
518
|
+
}
|
|
519
|
+
const commitCharacters = entry.commitCharacters;
|
|
520
|
+
// Ambient JS word based suggestions
|
|
521
|
+
const skipCommitCharacters = entry.kind === typescript_1.default.ScriptElementKind.warning ||
|
|
522
|
+
entry.kind === typescript_1.default.ScriptElementKind.string;
|
|
523
|
+
if (commitCharacters) {
|
|
524
|
+
if (!options.isNewIdentifierLocation && !skipCommitCharacters) {
|
|
525
|
+
return commitCharacters.concat('(');
|
|
526
|
+
}
|
|
527
|
+
return commitCharacters;
|
|
528
|
+
}
|
|
529
|
+
return skipCommitCharacters ? [] : undefined;
|
|
530
|
+
}
|
|
531
|
+
isExistingSvelteComponentImport(snapshot, name, source) {
|
|
532
|
+
const importStatement = new RegExp(`import ${name} from ["'\`][\\s\\S]+\\.svelte["'\`]`);
|
|
533
|
+
return !!source && !!snapshot.getFullText().match(importStatement);
|
|
534
|
+
}
|
|
535
|
+
fixTextEditRange(wordRange, completionItem, isHandlerCompletion, generatedTextEdit, tsDoc) {
|
|
536
|
+
if (isHandlerCompletion && completionItem.label.startsWith('on:')) {
|
|
537
|
+
completionItem.textEdit = vscode_languageserver_1.TextEdit.replace((0, utils_2.cloneRange)(wordRange), completionItem.label);
|
|
538
|
+
return completionItem;
|
|
539
|
+
}
|
|
540
|
+
const { textEdit } = completionItem;
|
|
541
|
+
if (!textEdit || !vscode_languageserver_1.TextEdit.is(textEdit)) {
|
|
542
|
+
return completionItem;
|
|
543
|
+
}
|
|
544
|
+
if (vscode_languageserver_1.TextEdit.is(generatedTextEdit)) {
|
|
545
|
+
(0, utils_3.checkRangeMappingWithGeneratedSemi)(textEdit.range, generatedTextEdit.range, tsDoc);
|
|
546
|
+
}
|
|
547
|
+
const { newText, range: { start } } = textEdit;
|
|
548
|
+
//If the textEdit is out of the word range of the triggered position
|
|
549
|
+
// vscode would refuse to show the completions
|
|
550
|
+
// split those edits into additionalTextEdit to fix it
|
|
551
|
+
if (start.line !== wordRange.start.line || start.character > wordRange.start.character) {
|
|
552
|
+
return completionItem;
|
|
553
|
+
}
|
|
554
|
+
textEdit.newText = newText.substring(wordRange.start.character - start.character);
|
|
555
|
+
textEdit.range.start = {
|
|
556
|
+
line: start.line,
|
|
557
|
+
character: wordRange.start.character
|
|
558
|
+
};
|
|
559
|
+
completionItem.additionalTextEdits = [
|
|
560
|
+
vscode_languageserver_1.TextEdit.replace({
|
|
561
|
+
start,
|
|
562
|
+
end: {
|
|
563
|
+
line: start.line,
|
|
564
|
+
character: wordRange.start.character
|
|
565
|
+
}
|
|
566
|
+
}, newText.substring(0, wordRange.start.character - start.character))
|
|
567
|
+
];
|
|
568
|
+
return completionItem;
|
|
569
|
+
}
|
|
570
|
+
/**
|
|
571
|
+
* TypeScript throws a debug assertion error if the importModuleSpecifierEnding config is
|
|
572
|
+
* 'js' and there's an unknown file extension - which is the case for `.svelte`. Therefore
|
|
573
|
+
* rewrite the importModuleSpecifierEnding for this case to silence the error.
|
|
574
|
+
*/
|
|
575
|
+
fixUserPreferencesForSvelteComponentImport(userPreferences) {
|
|
576
|
+
if (userPreferences.importModuleSpecifierEnding === 'js') {
|
|
577
|
+
return {
|
|
578
|
+
...userPreferences,
|
|
579
|
+
importModuleSpecifierEnding: 'index'
|
|
580
|
+
};
|
|
581
|
+
}
|
|
582
|
+
return userPreferences;
|
|
583
|
+
}
|
|
584
|
+
async resolveCompletion(document, completionItem, cancellationToken) {
|
|
585
|
+
const { data: comp } = completionItem;
|
|
586
|
+
const { tsDoc, lang, userPreferences } = await this.lsAndTsDocResolver.getLSAndTSDoc(document);
|
|
587
|
+
const filePath = tsDoc.filePath;
|
|
588
|
+
const formatCodeOptions = await this.configManager.getFormatCodeSettingsForFile(document, tsDoc.scriptKind);
|
|
589
|
+
if (!comp || !filePath || cancellationToken?.isCancellationRequested) {
|
|
590
|
+
return completionItem;
|
|
591
|
+
}
|
|
592
|
+
const is$typeImport = !!comp.__is_sveltekit$typeImport;
|
|
593
|
+
const errorPreventingUserPreferences = comp.source?.endsWith('.svelte')
|
|
594
|
+
? this.fixUserPreferencesForSvelteComponentImport(userPreferences)
|
|
595
|
+
: userPreferences;
|
|
596
|
+
const detail = lang.getCompletionEntryDetails(filePath, tsDoc.offsetAt(tsDoc.getGeneratedPosition(comp.position)), comp.name, formatCodeOptions, comp.source, errorPreventingUserPreferences, comp.data);
|
|
597
|
+
if (detail) {
|
|
598
|
+
const { detail: itemDetail, documentation: itemDocumentation } = this.getCompletionDocument(tsDoc, detail, is$typeImport);
|
|
599
|
+
// VSCode + tsserver won't have this pop-in effect
|
|
600
|
+
// because tsserver has internal APIs for caching
|
|
601
|
+
// TODO: consider if we should adopt the internal APIs
|
|
602
|
+
if (detail.sourceDisplay && !completionItem.labelDetails) {
|
|
603
|
+
completionItem.labelDetails = {
|
|
604
|
+
description: typescript_1.default.displayPartsToString(detail.sourceDisplay)
|
|
605
|
+
};
|
|
606
|
+
}
|
|
607
|
+
completionItem.detail = itemDetail;
|
|
608
|
+
completionItem.documentation = itemDocumentation;
|
|
609
|
+
}
|
|
610
|
+
const actions = detail?.codeActions;
|
|
611
|
+
const isImport = !!detail?.source;
|
|
612
|
+
if (actions) {
|
|
613
|
+
const edit = [];
|
|
614
|
+
const formatCodeBasis = (0, utils_3.getFormatCodeBasis)(formatCodeOptions);
|
|
615
|
+
for (const action of actions) {
|
|
616
|
+
for (const change of action.changes) {
|
|
617
|
+
edit.push(...this.codeActionChangesToTextEdit(document, tsDoc, change, isImport, comp.position, formatCodeBasis.newLine, is$typeImport));
|
|
618
|
+
}
|
|
619
|
+
}
|
|
620
|
+
completionItem.additionalTextEdits = (completionItem.additionalTextEdits ?? []).concat(edit);
|
|
621
|
+
}
|
|
622
|
+
return completionItem;
|
|
623
|
+
}
|
|
624
|
+
getCompletionDocument(tsDoc, compDetail, is$typeImport) {
|
|
625
|
+
const { sourceDisplay, documentation: tsDocumentation, displayParts, tags } = compDetail;
|
|
626
|
+
let parts = compDetail.codeActions?.map((codeAction) => codeAction.description) ?? [];
|
|
627
|
+
if (sourceDisplay && is$typeImport) {
|
|
628
|
+
const importPath = typescript_1.default.displayPartsToString(sourceDisplay);
|
|
629
|
+
// Take into account Node16 moduleResolution
|
|
630
|
+
parts = parts.map((detail) => detail.replace(importPath, `'./$types${importPath.endsWith('.js') ? '.js' : ''}'`));
|
|
631
|
+
}
|
|
632
|
+
let text = (0, utils_2.changeSvelteComponentName)(typescript_1.default.displayPartsToString(displayParts));
|
|
633
|
+
if (tsDoc.isSvelte5Plus && text.includes('(alias)')) {
|
|
634
|
+
// The info contains both the const and type export along with a bunch of gibberish we want to hide
|
|
635
|
+
if (text.includes('__SvelteComponent_')) {
|
|
636
|
+
// import - remove completely
|
|
637
|
+
text = '';
|
|
638
|
+
}
|
|
639
|
+
else if (text.includes('__sveltets_2_IsomorphicComponent')) {
|
|
640
|
+
// already imported - only keep the last part
|
|
641
|
+
text = text.substring(text.lastIndexOf('import'));
|
|
642
|
+
}
|
|
643
|
+
}
|
|
644
|
+
parts.push(text);
|
|
645
|
+
const markdownDoc = (0, previewer_1.getMarkdownDocumentation)(tsDocumentation, tags);
|
|
646
|
+
const documentation = markdownDoc
|
|
647
|
+
? { value: markdownDoc, kind: vscode_languageserver_1.MarkupKind.Markdown }
|
|
648
|
+
: undefined;
|
|
649
|
+
return {
|
|
650
|
+
documentation,
|
|
651
|
+
detail: parts.filter(Boolean).join('\n\n')
|
|
652
|
+
};
|
|
653
|
+
}
|
|
654
|
+
codeActionChangesToTextEdit(doc, snapshot, changes, isImport, originalTriggerPosition, newLine, is$typeImport) {
|
|
655
|
+
return changes.textChanges.map((change) => this.codeActionChangeToTextEdit(doc, snapshot, change, isImport, originalTriggerPosition, newLine, is$typeImport));
|
|
656
|
+
}
|
|
657
|
+
codeActionChangeToTextEdit(doc, snapshot, change, isImport, originalTriggerPosition, newLine, is$typeImport, isCombinedCodeAction) {
|
|
658
|
+
change.newText = isCombinedCodeAction
|
|
659
|
+
? (0, utils_1.modifyLines)(change.newText, (line) => this.fixImportNewText(line, (0, utils_2.isInScript)(originalTriggerPosition, doc), is$typeImport))
|
|
660
|
+
: this.fixImportNewText(change.newText, (0, utils_2.isInScript)(originalTriggerPosition, doc), is$typeImport);
|
|
661
|
+
const scriptTagInfo = snapshot.scriptInfo || snapshot.moduleScriptInfo;
|
|
662
|
+
// no script tag defined yet, add it.
|
|
663
|
+
if (!scriptTagInfo) {
|
|
664
|
+
if (isCombinedCodeAction) {
|
|
665
|
+
return vscode_languageserver_1.TextEdit.insert(vscode_languageserver_1.Position.create(0, 0), change.newText);
|
|
666
|
+
}
|
|
667
|
+
const config = this.configManager.getConfig();
|
|
668
|
+
// Remove the empty line after the script tag because getNewScriptStartTag will always add one
|
|
669
|
+
let newText = change.newText;
|
|
670
|
+
if (newText[0] === '\r') {
|
|
671
|
+
newText = newText.substring(1);
|
|
672
|
+
}
|
|
673
|
+
if (newText[0] === '\n') {
|
|
674
|
+
newText = newText.substring(1);
|
|
675
|
+
}
|
|
676
|
+
return vscode_languageserver_1.TextEdit.replace(beginOfDocumentRange, `${(0, utils_3.getNewScriptStartTag)(config, newLine)}${newText}</script>${newLine}`);
|
|
677
|
+
}
|
|
678
|
+
const { span } = change;
|
|
679
|
+
const virtualRange = (0, utils_2.convertRange)(snapshot, span);
|
|
680
|
+
let range;
|
|
681
|
+
const isNewImport = isImport && virtualRange.start.character === 0;
|
|
682
|
+
// Since new import always can't be mapped, we'll have special treatment here
|
|
683
|
+
// but only hack this when there is multiple line in script
|
|
684
|
+
if (isNewImport && virtualRange.start.line > 1) {
|
|
685
|
+
range = this.mapRangeForNewImport(snapshot, virtualRange);
|
|
686
|
+
}
|
|
687
|
+
else {
|
|
688
|
+
range = (0, documents_1.mapRangeToOriginal)(snapshot, virtualRange);
|
|
689
|
+
}
|
|
690
|
+
// If range is somehow not mapped in parent,
|
|
691
|
+
// the import is mapped wrong or is outside script tag,
|
|
692
|
+
// use script starting point instead.
|
|
693
|
+
// This happens among other things if the completion is the first import of the file.
|
|
694
|
+
if (range.start.line === -1 ||
|
|
695
|
+
(range.start.line === 0 && range.start.character <= 1 && span.length === 0) ||
|
|
696
|
+
!(0, utils_2.isInScript)(range.start, snapshot)) {
|
|
697
|
+
range = (0, utils_2.convertRange)(doc, {
|
|
698
|
+
start: (0, documents_1.isInTag)(originalTriggerPosition, doc.scriptInfo)
|
|
699
|
+
? snapshot.scriptInfo?.start || scriptTagInfo.start
|
|
700
|
+
: (0, documents_1.isInTag)(originalTriggerPosition, doc.moduleScriptInfo)
|
|
701
|
+
? snapshot.moduleScriptInfo?.start || scriptTagInfo.start
|
|
702
|
+
: scriptTagInfo.start,
|
|
703
|
+
length: span.length
|
|
704
|
+
});
|
|
705
|
+
}
|
|
706
|
+
// prevent newText from being placed like this: <script>import {} from ''
|
|
707
|
+
const editOffset = doc.offsetAt(range.start);
|
|
708
|
+
if ((editOffset === snapshot.scriptInfo?.start ||
|
|
709
|
+
editOffset === snapshot.moduleScriptInfo?.start) &&
|
|
710
|
+
!change.newText.startsWith('\r\n') &&
|
|
711
|
+
!change.newText.startsWith('\n')) {
|
|
712
|
+
change.newText = newLine + change.newText;
|
|
713
|
+
}
|
|
714
|
+
const after = doc.getText().slice(doc.offsetAt(range.end));
|
|
715
|
+
// typescript add empty line after import when the generated ts file
|
|
716
|
+
// doesn't have new line at the start of the file
|
|
717
|
+
if (after.startsWith('\r\n') || after.startsWith('\n')) {
|
|
718
|
+
change.newText = change.newText.trimEnd() + newLine;
|
|
719
|
+
}
|
|
720
|
+
return vscode_languageserver_1.TextEdit.replace(range, change.newText);
|
|
721
|
+
}
|
|
722
|
+
mapRangeForNewImport(snapshot, virtualRange) {
|
|
723
|
+
const sourceMappableRange = this.offsetLinesAndMovetoStartOfLine(virtualRange, -1);
|
|
724
|
+
const mappableRange = (0, documents_1.mapRangeToOriginal)(snapshot, sourceMappableRange);
|
|
725
|
+
return this.offsetLinesAndMovetoStartOfLine(mappableRange, 1);
|
|
726
|
+
}
|
|
727
|
+
offsetLinesAndMovetoStartOfLine({ start, end }, offsetLines) {
|
|
728
|
+
return vscode_languageserver_1.Range.create(vscode_languageserver_1.Position.create(start.line + offsetLines, 0), vscode_languageserver_1.Position.create(end.line + offsetLines, 0));
|
|
729
|
+
}
|
|
730
|
+
fixImportNewText(importText, actionTriggeredInScript, is$typeImport) {
|
|
731
|
+
if (is$typeImport && importText.trim().startsWith('import ')) {
|
|
732
|
+
// Take into account Node16 moduleResolution
|
|
733
|
+
return importText.replace(/(['"])(.+?)['"]/, (_match, quote, path) => `${quote}./$types${path.endsWith('.js') ? '.js' : ''}${quote}`);
|
|
734
|
+
}
|
|
735
|
+
const changedName = (0, utils_2.changeSvelteComponentName)(importText);
|
|
736
|
+
if (importText !== changedName || !actionTriggeredInScript) {
|
|
737
|
+
// For some reason, TS sometimes adds the `type` modifier. Remove it
|
|
738
|
+
// in case of Svelte component imports or if import triggered from markup.
|
|
739
|
+
return changedName.replace(' type ', ' ');
|
|
740
|
+
}
|
|
741
|
+
return importText;
|
|
742
|
+
}
|
|
743
|
+
}
|
|
744
|
+
exports.CompletionsProviderImpl = CompletionsProviderImpl;
|
|
745
|
+
const beginOfDocumentRange = vscode_languageserver_1.Range.create(vscode_languageserver_1.Position.create(0, 0), vscode_languageserver_1.Position.create(0, 0));
|
|
746
|
+
// `import {...} from '..'` or `import ... from '..'`
|
|
747
|
+
// Note: Does not take into account if import is within a comment.
|
|
748
|
+
const scriptImportRegex = /\bimport\s+{([^}]*?)}\s+?from\s+['"`].+?['"`]|\bimport\s+(\w+?)\s+from\s+['"`].+?['"`]/g;
|
|
749
|
+
// Type definitions from svelte-shims.d.ts that shouldn't appear in completion suggestions
|
|
750
|
+
// because they are meant to be used "behind the scenes"
|
|
751
|
+
const svelte2tsxTypes = new Set([
|
|
752
|
+
'Svelte2TsxComponent',
|
|
753
|
+
'Svelte2TsxComponentConstructorParameters',
|
|
754
|
+
'SvelteComponentConstructor',
|
|
755
|
+
'SvelteActionReturnType',
|
|
756
|
+
'SvelteTransitionConfig',
|
|
757
|
+
'SvelteTransitionReturnType',
|
|
758
|
+
'SvelteAnimationReturnType',
|
|
759
|
+
'SvelteWithOptionalProps',
|
|
760
|
+
'SvelteAllProps',
|
|
761
|
+
'SveltePropsAnyFallback',
|
|
762
|
+
'SvelteSlotsAnyFallback',
|
|
763
|
+
'SvelteRestProps',
|
|
764
|
+
'SvelteSlots',
|
|
765
|
+
'SvelteStore'
|
|
766
|
+
]);
|
|
767
|
+
const startsWithUppercase = /^[A-Z]/;
|
|
768
|
+
function createIsValidCompletion(document, position, hasParserError) {
|
|
769
|
+
// Make fallback completions for tags inside the template a bit better
|
|
770
|
+
const isAtStartTag = !(0, documents_1.isInTag)(position, document.scriptInfo) &&
|
|
771
|
+
/<\w*$/.test(document.getText(vscode_languageserver_1.Range.create(position.line, 0, position.line, position.character)));
|
|
772
|
+
const noWrongCompletionAtStartTag = isAtStartTag && hasParserError
|
|
773
|
+
? (value) => startsWithUppercase.test(value.name)
|
|
774
|
+
: () => true;
|
|
775
|
+
const isNoSvelte2tsxCompletion = (value) => {
|
|
776
|
+
if (value.kindModifiers === 'declare') {
|
|
777
|
+
return !value.name.startsWith('__sveltets_') && !svelte2tsxTypes.has(value.name);
|
|
778
|
+
}
|
|
779
|
+
return !value.name.startsWith('$$_');
|
|
780
|
+
};
|
|
781
|
+
const isCompletionInHTMLStartTag = !!(0, documents_1.getNodeIfIsInHTMLStartTag)(document.html, document.offsetAt(position));
|
|
782
|
+
if (!isCompletionInHTMLStartTag) {
|
|
783
|
+
return isNoSvelte2tsxCompletion;
|
|
784
|
+
}
|
|
785
|
+
// TODO with the new transformation this is ts.ScriptElementKind.memberVariableElement
|
|
786
|
+
// which is also true for all properties of any other object -> how reliably filter this out?
|
|
787
|
+
// ---> another /*ignore*/ pragma?
|
|
788
|
+
// ---> OR: make these lower priority if we find out they are inside a html start tag
|
|
789
|
+
return (value) => isNoSvelte2tsxCompletion(value) && noWrongCompletionAtStartTag(value);
|
|
790
|
+
}
|
|
791
|
+
//# sourceMappingURL=CompletionProvider.js.map
|