@velarscript/cli 0.10.1
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/LICENSE +201 -0
- package/README.md +117 -0
- package/dist/application-package-host.d.ts +5 -0
- package/dist/application-package-host.d.ts.map +1 -0
- package/dist/application-package-host.js +67 -0
- package/dist/application-package-host.js.map +1 -0
- package/dist/bounded-text.d.ts +2 -0
- package/dist/bounded-text.d.ts.map +1 -0
- package/dist/bounded-text.js +13 -0
- package/dist/bounded-text.js.map +1 -0
- package/dist/browser-process-owner.d.ts +15 -0
- package/dist/browser-process-owner.d.ts.map +1 -0
- package/dist/browser-process-owner.js +151 -0
- package/dist/browser-process-owner.js.map +1 -0
- package/dist/browser-test-runner.d.ts +11 -0
- package/dist/browser-test-runner.d.ts.map +1 -0
- package/dist/browser-test-runner.js +1022 -0
- package/dist/browser-test-runner.js.map +1 -0
- package/dist/build-engine-tool.d.ts +3 -0
- package/dist/build-engine-tool.d.ts.map +1 -0
- package/dist/build-engine-tool.js +34 -0
- package/dist/build-engine-tool.js.map +1 -0
- package/dist/build-staging.d.ts +3 -0
- package/dist/build-staging.d.ts.map +1 -0
- package/dist/build-staging.js +3 -0
- package/dist/build-staging.js.map +1 -0
- package/dist/bundled-application-package-host-registry.d.ts +10 -0
- package/dist/bundled-application-package-host-registry.d.ts.map +1 -0
- package/dist/bundled-application-package-host-registry.js +16 -0
- package/dist/bundled-application-package-host-registry.js.map +1 -0
- package/dist/bundled-extension-registry.d.ts +22 -0
- package/dist/bundled-extension-registry.d.ts.map +1 -0
- package/dist/bundled-extension-registry.js +10 -0
- package/dist/bundled-extension-registry.js.map +1 -0
- package/dist/canonical-path.d.ts +3 -0
- package/dist/canonical-path.d.ts.map +1 -0
- package/dist/canonical-path.js +30 -0
- package/dist/canonical-path.js.map +1 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +1274 -0
- package/dist/cli.js.map +1 -0
- package/dist/config.d.ts +26 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +403 -0
- package/dist/config.js.map +1 -0
- package/dist/deployment-verifier.d.ts +34 -0
- package/dist/deployment-verifier.d.ts.map +1 -0
- package/dist/deployment-verifier.js +260 -0
- package/dist/deployment-verifier.js.map +1 -0
- package/dist/dev-server.d.ts +3 -0
- package/dist/dev-server.d.ts.map +1 -0
- package/dist/dev-server.js +464 -0
- package/dist/dev-server.js.map +1 -0
- package/dist/embedded-modules.d.ts +17 -0
- package/dist/embedded-modules.d.ts.map +1 -0
- package/dist/embedded-modules.js +33 -0
- package/dist/embedded-modules.js.map +1 -0
- package/dist/extension-metadata.d.ts +23 -0
- package/dist/extension-metadata.d.ts.map +1 -0
- package/dist/extension-metadata.js +254 -0
- package/dist/extension-metadata.js.map +1 -0
- package/dist/file-integrity.d.ts +7 -0
- package/dist/file-integrity.d.ts.map +1 -0
- package/dist/file-integrity.js +14 -0
- package/dist/file-integrity.js.map +1 -0
- package/dist/framework-host.d.ts +12 -0
- package/dist/framework-host.d.ts.map +1 -0
- package/dist/framework-host.js +46 -0
- package/dist/framework-host.js.map +1 -0
- package/dist/host-error.d.ts +6 -0
- package/dist/host-error.d.ts.map +1 -0
- package/dist/host-error.js +54 -0
- package/dist/host-error.js.map +1 -0
- package/dist/installed-package.d.ts +9 -0
- package/dist/installed-package.d.ts.map +1 -0
- package/dist/installed-package.js +23 -0
- package/dist/installed-package.js.map +1 -0
- package/dist/language-server-bundle-entry.d.ts +2 -0
- package/dist/language-server-bundle-entry.d.ts.map +1 -0
- package/dist/language-server-bundle-entry.js +7 -0
- package/dist/language-server-bundle-entry.js.map +1 -0
- package/dist/language-server-tool.d.ts +3 -0
- package/dist/language-server-tool.d.ts.map +1 -0
- package/dist/language-server-tool.js +30 -0
- package/dist/language-server-tool.js.map +1 -0
- package/dist/language-server.d.ts +9 -0
- package/dist/language-server.d.ts.map +1 -0
- package/dist/language-server.js +1733 -0
- package/dist/language-server.js.map +1 -0
- package/dist/mechanical-fixer.d.ts +30 -0
- package/dist/mechanical-fixer.d.ts.map +1 -0
- package/dist/mechanical-fixer.js +103 -0
- package/dist/mechanical-fixer.js.map +1 -0
- package/dist/module-assets.d.ts +11 -0
- package/dist/module-assets.d.ts.map +1 -0
- package/dist/module-assets.js +90 -0
- package/dist/module-assets.js.map +1 -0
- package/dist/npm.d.ts +23 -0
- package/dist/npm.d.ts.map +1 -0
- package/dist/npm.js +456 -0
- package/dist/npm.js.map +1 -0
- package/dist/official-language-server-extensions.d.ts +2 -0
- package/dist/official-language-server-extensions.d.ts.map +1 -0
- package/dist/official-language-server-extensions.js +38 -0
- package/dist/official-language-server-extensions.js.map +1 -0
- package/dist/official-script-language-service.d.ts +2 -0
- package/dist/official-script-language-service.d.ts.map +1 -0
- package/dist/official-script-language-service.js +10 -0
- package/dist/official-script-language-service.js.map +1 -0
- package/dist/official-tool-assets.d.ts +3 -0
- package/dist/official-tool-assets.d.ts.map +1 -0
- package/dist/official-tool-assets.js +41 -0
- package/dist/official-tool-assets.js.map +1 -0
- package/dist/ownership-graph.d.ts +46 -0
- package/dist/ownership-graph.d.ts.map +1 -0
- package/dist/ownership-graph.js +255 -0
- package/dist/ownership-graph.js.map +1 -0
- package/dist/package-manager.d.ts +20 -0
- package/dist/package-manager.d.ts.map +1 -0
- package/dist/package-manager.js +345 -0
- package/dist/package-manager.js.map +1 -0
- package/dist/packaged-official-tool.d.ts +8 -0
- package/dist/packaged-official-tool.d.ts.map +1 -0
- package/dist/packaged-official-tool.js +32 -0
- package/dist/packaged-official-tool.js.map +1 -0
- package/dist/preview-server.d.ts +11 -0
- package/dist/preview-server.d.ts.map +1 -0
- package/dist/preview-server.js +163 -0
- package/dist/preview-server.js.map +1 -0
- package/dist/production-build.d.ts +57 -0
- package/dist/production-build.d.ts.map +1 -0
- package/dist/production-build.js +332 -0
- package/dist/production-build.js.map +1 -0
- package/dist/production-verifier.d.ts +9 -0
- package/dist/production-verifier.d.ts.map +1 -0
- package/dist/production-verifier.js +268 -0
- package/dist/production-verifier.js.map +1 -0
- package/dist/program-runner.d.ts +7 -0
- package/dist/program-runner.d.ts.map +1 -0
- package/dist/program-runner.js +98 -0
- package/dist/program-runner.js.map +1 -0
- package/dist/project-check.d.ts +31 -0
- package/dist/project-check.d.ts.map +1 -0
- package/dist/project-check.js +95 -0
- package/dist/project-check.js.map +1 -0
- package/dist/project-format.d.ts +6 -0
- package/dist/project-format.d.ts.map +1 -0
- package/dist/project-format.js +21 -0
- package/dist/project-format.js.map +1 -0
- package/dist/project-semantic.d.ts +62 -0
- package/dist/project-semantic.d.ts.map +1 -0
- package/dist/project-semantic.js +1079 -0
- package/dist/project-semantic.js.map +1 -0
- package/dist/project-session.d.ts +36 -0
- package/dist/project-session.d.ts.map +1 -0
- package/dist/project-session.js +339 -0
- package/dist/project-session.js.map +1 -0
- package/dist/project-task-bundle-entry.d.ts +2 -0
- package/dist/project-task-bundle-entry.d.ts.map +1 -0
- package/dist/project-task-bundle-entry.js +18 -0
- package/dist/project-task-bundle-entry.js.map +1 -0
- package/dist/project-task-invocation.d.ts +16 -0
- package/dist/project-task-invocation.d.ts.map +1 -0
- package/dist/project-task-invocation.js +72 -0
- package/dist/project-task-invocation.js.map +1 -0
- package/dist/project-task-tool.d.ts +3 -0
- package/dist/project-task-tool.d.ts.map +1 -0
- package/dist/project-task-tool.js +61 -0
- package/dist/project-task-tool.js.map +1 -0
- package/dist/project.d.ts +71 -0
- package/dist/project.d.ts.map +1 -0
- package/dist/project.js +1750 -0
- package/dist/project.js.map +1 -0
- package/dist/reproduction.d.ts +50 -0
- package/dist/reproduction.d.ts.map +1 -0
- package/dist/reproduction.js +321 -0
- package/dist/reproduction.js.map +1 -0
- package/dist/script-language-service.d.ts +82 -0
- package/dist/script-language-service.d.ts.map +1 -0
- package/dist/script-language-service.js +18 -0
- package/dist/script-language-service.js.map +1 -0
- package/dist/source-limits.d.ts +5 -0
- package/dist/source-limits.d.ts.map +1 -0
- package/dist/source-limits.js +19 -0
- package/dist/source-limits.js.map +1 -0
- package/dist/standalone-build.d.ts +15 -0
- package/dist/standalone-build.d.ts.map +1 -0
- package/dist/standalone-build.js +76 -0
- package/dist/standalone-build.js.map +1 -0
- package/dist/standard-modules.d.ts +22 -0
- package/dist/standard-modules.d.ts.map +1 -0
- package/dist/standard-modules.js +3066 -0
- package/dist/standard-modules.js.map +1 -0
- package/dist/static-deployment.d.ts +48 -0
- package/dist/static-deployment.d.ts.map +1 -0
- package/dist/static-deployment.js +142 -0
- package/dist/static-deployment.js.map +1 -0
- package/dist/test-output.d.ts +20 -0
- package/dist/test-output.d.ts.map +1 -0
- package/dist/test-output.js +119 -0
- package/dist/test-output.js.map +1 -0
- package/dist/test-runner.d.ts +13 -0
- package/dist/test-runner.d.ts.map +1 -0
- package/dist/test-runner.js +226 -0
- package/dist/test-runner.js.map +1 -0
- package/dist/typescript-declarations.d.ts +20 -0
- package/dist/typescript-declarations.d.ts.map +1 -0
- package/dist/typescript-declarations.js +1609 -0
- package/dist/typescript-declarations.js.map +1 -0
- package/dist/uncaught-program-error.d.ts +24 -0
- package/dist/uncaught-program-error.d.ts.map +1 -0
- package/dist/uncaught-program-error.js +105 -0
- package/dist/uncaught-program-error.js.map +1 -0
- package/dist/unowned-errors.d.ts +56 -0
- package/dist/unowned-errors.d.ts.map +1 -0
- package/dist/unowned-errors.js +82 -0
- package/dist/unowned-errors.js.map +1 -0
- package/dist/version.d.ts +3 -0
- package/dist/version.d.ts.map +1 -0
- package/dist/version.js +3 -0
- package/dist/version.js.map +1 -0
- package/dist/workspace-index.d.ts +78 -0
- package/dist/workspace-index.d.ts.map +1 -0
- package/dist/workspace-index.js +493 -0
- package/dist/workspace-index.js.map +1 -0
- package/package.json +45 -0
- package/skill/ai-skill.md +749 -0
|
@@ -0,0 +1,1079 @@
|
|
|
1
|
+
import { dirname, extname, resolve } from "node:path";
|
|
2
|
+
import { bindingNameRestriction, isSourceIdentifierPart, isSourceIdentifierStart, memberNameRestriction, semanticImportAt, semanticModuleReferenceAt, semanticSymbolAt, semanticVisibleSymbolsAt, } from "@velarscript/compiler";
|
|
3
|
+
import { projectImportKey } from "./project.js";
|
|
4
|
+
function bindingRenameRestrictionMessage(project, name) {
|
|
5
|
+
const extensionReservedBindings = new Set(project.compilerExtensions.flatMap((extension) => [...extension.analysis?.reservedBindings ?? []]));
|
|
6
|
+
const restriction = bindingNameRestriction(name, extensionReservedBindings);
|
|
7
|
+
if (!restriction)
|
|
8
|
+
return null;
|
|
9
|
+
if (restriction === "invalid")
|
|
10
|
+
return "The new name is not a valid VelarScript identifier";
|
|
11
|
+
if (restriction === "keyword")
|
|
12
|
+
return "The new name is reserved by VelarScript";
|
|
13
|
+
if (restriction === "source")
|
|
14
|
+
return `The source spelling '${name}' is unavailable in VelarScript`;
|
|
15
|
+
if (restriction === "javascript")
|
|
16
|
+
return `The new name '${name}' is reserved by JavaScript for lexical bindings`;
|
|
17
|
+
if (restriction === "compiler")
|
|
18
|
+
return "The new name uses a reserved compiler prefix '__velar'";
|
|
19
|
+
if (restriction === "core")
|
|
20
|
+
return `The new name '${name}' is a reserved Core binding`;
|
|
21
|
+
return `The new name '${name}' is a reserved extension binding`;
|
|
22
|
+
}
|
|
23
|
+
function memberRenameRestrictionMessage(name, owner) {
|
|
24
|
+
const restriction = memberNameRestriction(name, owner);
|
|
25
|
+
if (!restriction)
|
|
26
|
+
return null;
|
|
27
|
+
if (restriction === "invalid")
|
|
28
|
+
return "The new name is not a valid VelarScript identifier";
|
|
29
|
+
if (restriction === "source")
|
|
30
|
+
return `The source spelling '${name}' is unavailable in VelarScript`;
|
|
31
|
+
if (restriction === "prototype")
|
|
32
|
+
return "VelarScript does not expose prototype manipulation";
|
|
33
|
+
if (restriction === "constructor")
|
|
34
|
+
return "Class member 'constructor' is reserved for the constructor(...) declaration";
|
|
35
|
+
return `Enum member '${name}' is reserved for runtime validation`;
|
|
36
|
+
}
|
|
37
|
+
function memberOwnerKind(target) {
|
|
38
|
+
if (!target.symbol.container)
|
|
39
|
+
return "data";
|
|
40
|
+
const owner = target.module.result.semanticIndex.symbols.find((symbol) => symbol.name === target.symbol.container
|
|
41
|
+
&& (symbol.kind === "class" || symbol.kind === "type" || symbol.kind.startsWith("extension:class:") || symbol.kind.startsWith("extension:type:")));
|
|
42
|
+
return owner?.kind === "class" || owner?.kind.startsWith("extension:class:") ? "class" : "data";
|
|
43
|
+
}
|
|
44
|
+
export function projectDefinitionAt(project, path, offset) {
|
|
45
|
+
const module = moduleAt(project, path);
|
|
46
|
+
if (!module)
|
|
47
|
+
return null;
|
|
48
|
+
const enumMember = enumMemberAt(project, module, offset);
|
|
49
|
+
if (enumMember)
|
|
50
|
+
return locationOf(enumMember);
|
|
51
|
+
const member = accessibleMemberTargetAt(project, module, offset);
|
|
52
|
+
if (member)
|
|
53
|
+
return locationOf(member.symbol);
|
|
54
|
+
const moduleReference = semanticModuleReferenceAt(module.result.semanticIndex, offset);
|
|
55
|
+
if (moduleReference) {
|
|
56
|
+
const targetPath = moduleReference.source.startsWith(".") && extname(moduleReference.source) === ".vel"
|
|
57
|
+
? resolve(dirname(module.inputPath), moduleReference.source)
|
|
58
|
+
: project.velarImports.get(projectImportKey(module.inputPath, moduleReference.source));
|
|
59
|
+
const target = targetPath ? moduleAt(project, targetPath) : null;
|
|
60
|
+
if (target)
|
|
61
|
+
return { path: target.inputPath, span: { start: 0, end: 0 } };
|
|
62
|
+
}
|
|
63
|
+
const target = targetAt(project, module, offset, "definition");
|
|
64
|
+
if (!target)
|
|
65
|
+
return null;
|
|
66
|
+
if (target.kind === "local" && target.symbol.kind === "import") {
|
|
67
|
+
const imported = importForSymbol(module.result.semanticIndex, target.symbol.id);
|
|
68
|
+
const exported = imported ? exportedTarget(project, module, imported) : null;
|
|
69
|
+
if (exported)
|
|
70
|
+
return locationOf(exported.symbol);
|
|
71
|
+
}
|
|
72
|
+
return locationOf(target.symbol);
|
|
73
|
+
}
|
|
74
|
+
export function projectReferencesAt(project, path, offset, includeDeclaration = false) {
|
|
75
|
+
const module = moduleAt(project, path);
|
|
76
|
+
if (!module)
|
|
77
|
+
return [];
|
|
78
|
+
const enumMember = enumMemberTargetAt(project, module, offset);
|
|
79
|
+
if (enumMember)
|
|
80
|
+
return enumMemberLocations(project, enumMember, includeDeclaration);
|
|
81
|
+
const member = accessibleMemberTargetAt(project, module, offset);
|
|
82
|
+
if (member)
|
|
83
|
+
return memberLocations(project, member, includeDeclaration);
|
|
84
|
+
const target = targetAt(project, module, offset, "references");
|
|
85
|
+
if (!target)
|
|
86
|
+
return [];
|
|
87
|
+
const locations = target.kind === "export"
|
|
88
|
+
? exportedLocations(project, target, includeDeclaration)
|
|
89
|
+
: localLocations(target, includeDeclaration);
|
|
90
|
+
return uniqueLocations(locations);
|
|
91
|
+
}
|
|
92
|
+
export function projectPrepareRenameAt(project, path, offset) {
|
|
93
|
+
const module = moduleAt(project, path);
|
|
94
|
+
if (!module)
|
|
95
|
+
return null;
|
|
96
|
+
const enumMember = enumMemberTargetAt(project, module, offset);
|
|
97
|
+
if (enumMember) {
|
|
98
|
+
const selection = wordSpanAt(module.result.source.text, offset) ?? enumMember.selectionSpan;
|
|
99
|
+
return { edits: [{ path: module.inputPath, span: selection }], placeholder: enumMember.name };
|
|
100
|
+
}
|
|
101
|
+
const member = accessibleMemberTargetAt(project, module, offset);
|
|
102
|
+
if (member) {
|
|
103
|
+
if (extensionRenameProtection(project, member) || !renameableMember(member))
|
|
104
|
+
return null;
|
|
105
|
+
const selection = wordSpanAt(module.result.source.text, offset) ?? member.symbol.selectionSpan;
|
|
106
|
+
return { edits: [{ path: module.inputPath, span: selection }], placeholder: member.symbol.name };
|
|
107
|
+
}
|
|
108
|
+
const target = targetAt(project, module, offset, "rename");
|
|
109
|
+
if (!target || !renameable(target.symbol))
|
|
110
|
+
return null;
|
|
111
|
+
const selection = renameSelectionAt(module.result.semanticIndex, offset, target.symbol);
|
|
112
|
+
return { edits: [{ path: module.inputPath, span: selection }], placeholder: target.symbol.name };
|
|
113
|
+
}
|
|
114
|
+
export function projectRenameAt(project, path, offset, newName) {
|
|
115
|
+
const module = moduleAt(project, path);
|
|
116
|
+
if (!module)
|
|
117
|
+
return "No renameable VelarScript symbol at this position";
|
|
118
|
+
const enumMember = enumMemberTargetAt(project, module, offset);
|
|
119
|
+
if (enumMember) {
|
|
120
|
+
const restriction = memberRenameRestrictionMessage(newName, "enum");
|
|
121
|
+
if (restriction)
|
|
122
|
+
return restriction;
|
|
123
|
+
if (enumMember.name === newName)
|
|
124
|
+
return { edits: [], placeholder: enumMember.name };
|
|
125
|
+
if (enumMemberRenameCollides(project, enumMember, newName))
|
|
126
|
+
return "The new name collides with another declaration";
|
|
127
|
+
return { edits: enumMemberLocations(project, enumMember, true), placeholder: enumMember.name };
|
|
128
|
+
}
|
|
129
|
+
const member = accessibleMemberTargetAt(project, module, offset);
|
|
130
|
+
if (member) {
|
|
131
|
+
const restriction = member.symbol.kind === "parameter"
|
|
132
|
+
? bindingRenameRestrictionMessage(project, newName)
|
|
133
|
+
: memberRenameRestrictionMessage(newName, memberOwnerKind(member));
|
|
134
|
+
if (restriction)
|
|
135
|
+
return restriction;
|
|
136
|
+
const protection = extensionRenameProtection(project, member);
|
|
137
|
+
if (protection)
|
|
138
|
+
return protection;
|
|
139
|
+
if (!renameableMember(member))
|
|
140
|
+
return "No renameable VelarScript symbol at this position";
|
|
141
|
+
if (member.symbol.name === newName)
|
|
142
|
+
return { edits: [], placeholder: member.symbol.name };
|
|
143
|
+
if (memberRenameCollides(project, member, newName))
|
|
144
|
+
return "The new name collides with another declaration";
|
|
145
|
+
return { edits: memberRenameEdits(project, member, newName), placeholder: member.symbol.name };
|
|
146
|
+
}
|
|
147
|
+
const target = targetAt(project, module, offset, "rename");
|
|
148
|
+
if (!target || !renameable(target.symbol))
|
|
149
|
+
return "No renameable VelarScript symbol at this position";
|
|
150
|
+
const restriction = bindingRenameRestrictionMessage(project, newName);
|
|
151
|
+
if (restriction)
|
|
152
|
+
return restriction;
|
|
153
|
+
if (target.symbol.name === newName)
|
|
154
|
+
return { edits: [], placeholder: target.symbol.name };
|
|
155
|
+
if (renameCollides(project, target, newName))
|
|
156
|
+
return "The new name collides with another declaration";
|
|
157
|
+
const locations = target.kind === "export"
|
|
158
|
+
? exportedLocations(project, target, true)
|
|
159
|
+
: localLocations(target, true);
|
|
160
|
+
return { edits: uniqueLocations(locations), placeholder: target.symbol.name };
|
|
161
|
+
}
|
|
162
|
+
export function projectDocumentSymbols(project, path) {
|
|
163
|
+
const module = moduleAt(project, path);
|
|
164
|
+
if (!module)
|
|
165
|
+
return [];
|
|
166
|
+
return module.result.semanticIndex.symbols.map((symbol) => ({
|
|
167
|
+
name: symbol.name,
|
|
168
|
+
kind: symbol.kind,
|
|
169
|
+
path: symbol.path,
|
|
170
|
+
span: symbol.span,
|
|
171
|
+
selectionSpan: symbol.selectionSpan,
|
|
172
|
+
type: symbol.type,
|
|
173
|
+
...(symbol.presentationKind ? { presentationKind: symbol.presentationKind } : {}),
|
|
174
|
+
}));
|
|
175
|
+
}
|
|
176
|
+
export function projectWorkspaceSymbols(project, query, maximum = 10_000) {
|
|
177
|
+
const normalized = query.toLowerCase();
|
|
178
|
+
const symbols = [];
|
|
179
|
+
for (const module of project.modules) {
|
|
180
|
+
for (const symbol of module.result.semanticIndex.symbols) {
|
|
181
|
+
if (symbol.kind === "import" || symbol.kind === "parameter" || symbol.kind === "catch")
|
|
182
|
+
continue;
|
|
183
|
+
if (symbol.scopeId !== 0 && !symbol.container)
|
|
184
|
+
continue;
|
|
185
|
+
const name = symbol.name.toLowerCase();
|
|
186
|
+
const match = normalized === "" ? 0 : name.indexOf(normalized);
|
|
187
|
+
if (match < 0)
|
|
188
|
+
continue;
|
|
189
|
+
symbols.push({
|
|
190
|
+
name: symbol.name,
|
|
191
|
+
kind: symbol.kind,
|
|
192
|
+
path: symbol.path,
|
|
193
|
+
span: symbol.span,
|
|
194
|
+
selectionSpan: symbol.selectionSpan,
|
|
195
|
+
type: symbol.type,
|
|
196
|
+
...(symbol.container ? { containerName: symbol.container } : {}),
|
|
197
|
+
...(symbol.presentationKind ? { presentationKind: symbol.presentationKind } : {}),
|
|
198
|
+
score: match === 0 ? name.length === normalized.length ? 0 : 1 : 2,
|
|
199
|
+
});
|
|
200
|
+
if (symbols.length >= maximum)
|
|
201
|
+
break;
|
|
202
|
+
}
|
|
203
|
+
if (symbols.length >= maximum)
|
|
204
|
+
break;
|
|
205
|
+
}
|
|
206
|
+
return symbols
|
|
207
|
+
.sort((left, right) => left.score - right.score || left.name.localeCompare(right.name)
|
|
208
|
+
|| left.path.localeCompare(right.path) || left.selectionSpan.start - right.selectionSpan.start)
|
|
209
|
+
.map(({ score: _score, ...symbol }) => symbol);
|
|
210
|
+
}
|
|
211
|
+
export function projectSemanticTokens(project, path) {
|
|
212
|
+
const module = moduleAt(project, path);
|
|
213
|
+
if (!module)
|
|
214
|
+
return [];
|
|
215
|
+
const tokens = new Map();
|
|
216
|
+
const add = (span, symbol, fallback, declaration, priority) => {
|
|
217
|
+
if (span.end <= span.start)
|
|
218
|
+
return;
|
|
219
|
+
const token = {
|
|
220
|
+
span,
|
|
221
|
+
type: symbol ? semanticTokenType(symbol) : fallback,
|
|
222
|
+
modifiers: symbol ? semanticTokenModifiers(symbol, declaration) : declaration ? ["declaration"] : [],
|
|
223
|
+
};
|
|
224
|
+
const key = `${span.start}:${span.end}`;
|
|
225
|
+
if ((tokens.get(key)?.priority ?? -1) < priority)
|
|
226
|
+
tokens.set(key, { token, priority });
|
|
227
|
+
};
|
|
228
|
+
for (const symbol of module.result.semanticIndex.symbols) {
|
|
229
|
+
const resolved = projectSymbolAt(project, path, Math.min(symbol.selectionSpan.end, symbol.selectionSpan.start + 1)) ?? symbol;
|
|
230
|
+
add(symbol.selectionSpan, resolved, "variable", true, 3);
|
|
231
|
+
}
|
|
232
|
+
for (const reference of module.result.semanticIndex.references) {
|
|
233
|
+
const resolved = projectSymbolAt(project, path, Math.min(reference.span.end, reference.span.start + 1));
|
|
234
|
+
add(reference.span, resolved, "variable", false, 1);
|
|
235
|
+
}
|
|
236
|
+
for (const reference of module.result.semanticIndex.memberReferences) {
|
|
237
|
+
const resolved = projectMemberSymbolAt(project, path, Math.min(reference.span.end, reference.span.start + 1));
|
|
238
|
+
const expression = module.result.semanticIndex.expressions.find((item) => item.selectionSpan
|
|
239
|
+
&& item.selectionSpan.start === reference.span.start && item.selectionSpan.end === reference.span.end);
|
|
240
|
+
add(reference.span, resolved, expression?.callable ? "method" : "property", false, 2);
|
|
241
|
+
}
|
|
242
|
+
return [...tokens.values()].map((item) => item.token).sort((left, right) => left.span.start - right.span.start || left.span.end - right.span.end);
|
|
243
|
+
}
|
|
244
|
+
function semanticTokenType(symbol) {
|
|
245
|
+
if (symbol.kind.startsWith("extension:function:"))
|
|
246
|
+
return "function";
|
|
247
|
+
if (symbol.kind.startsWith("extension:parameter:"))
|
|
248
|
+
return "parameter";
|
|
249
|
+
if (symbol.kind.startsWith("extension:type:"))
|
|
250
|
+
return "type";
|
|
251
|
+
if (symbol.kind.startsWith("extension:class:"))
|
|
252
|
+
return "class";
|
|
253
|
+
switch (symbol.kind) {
|
|
254
|
+
case "type": return "type";
|
|
255
|
+
case "class": return "class";
|
|
256
|
+
case "enum": return "enum";
|
|
257
|
+
case "enum-member": return "enumMember";
|
|
258
|
+
case "function":
|
|
259
|
+
return "function";
|
|
260
|
+
case "method": return "method";
|
|
261
|
+
case "field": return "property";
|
|
262
|
+
case "parameter": return "parameter";
|
|
263
|
+
default: return "variable";
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
function semanticTokenModifiers(symbol, declaration) {
|
|
267
|
+
const modifiers = [];
|
|
268
|
+
if (declaration)
|
|
269
|
+
modifiers.push("declaration");
|
|
270
|
+
if (symbol.kind === "variable" && !symbol.mutable)
|
|
271
|
+
modifiers.push("readonly");
|
|
272
|
+
if (symbol.static)
|
|
273
|
+
modifiers.push("static");
|
|
274
|
+
return modifiers;
|
|
275
|
+
}
|
|
276
|
+
export function projectSymbolAt(project, path, offset) {
|
|
277
|
+
const module = moduleAt(project, path);
|
|
278
|
+
if (!module)
|
|
279
|
+
return null;
|
|
280
|
+
const enumMember = enumMemberAt(project, module, offset);
|
|
281
|
+
if (enumMember)
|
|
282
|
+
return enumMember;
|
|
283
|
+
const symbol = semanticSymbolAt(module.result.semanticIndex, offset);
|
|
284
|
+
if (!symbol)
|
|
285
|
+
return null;
|
|
286
|
+
if (symbol.kind !== "import")
|
|
287
|
+
return symbol;
|
|
288
|
+
const imported = importForSymbol(module.result.semanticIndex, symbol.id);
|
|
289
|
+
return imported ? exportedTarget(project, module, imported)?.symbol ?? symbol : symbol;
|
|
290
|
+
}
|
|
291
|
+
export function projectExpressionAt(project, path, offset) {
|
|
292
|
+
const module = moduleAt(project, path);
|
|
293
|
+
if (!module)
|
|
294
|
+
return null;
|
|
295
|
+
return module.result.semanticIndex.expressions
|
|
296
|
+
.filter((expression) => expression.selectionSpan && contains(expression.selectionSpan, offset))
|
|
297
|
+
.sort((left, right) => (left.selectionSpan.end - left.selectionSpan.start)
|
|
298
|
+
- (right.selectionSpan.end - right.selectionSpan.start))[0] ?? null;
|
|
299
|
+
}
|
|
300
|
+
export function projectMemberSymbolAt(project, path, offset) {
|
|
301
|
+
const module = moduleAt(project, path);
|
|
302
|
+
return module ? accessibleMemberTargetAt(project, module, offset)?.symbol ?? null : null;
|
|
303
|
+
}
|
|
304
|
+
export function projectCompletionsAt(project, path, offset) {
|
|
305
|
+
const module = moduleAt(project, path);
|
|
306
|
+
if (!module)
|
|
307
|
+
return [];
|
|
308
|
+
const extensionCompletion = extensionCompletionAt(project, module, offset);
|
|
309
|
+
if (extensionCompletion)
|
|
310
|
+
return extensionCompletion.completions;
|
|
311
|
+
const memberAccess = memberAccessAt(module.result.source.text, offset);
|
|
312
|
+
if (memberAccess) {
|
|
313
|
+
const owner = memberAccess.ownerOffset === null ? null : projectSymbolAt(project, module.inputPath, memberAccess.ownerOffset);
|
|
314
|
+
const ownerExpression = module.result.semanticIndex.expressions
|
|
315
|
+
.filter((expression) => expression.span.end === memberAccess.ownerEnd)
|
|
316
|
+
.sort((left, right) => right.span.start - left.span.start)[0];
|
|
317
|
+
const members = ownerExpression?.members ?? owner?.members ?? [];
|
|
318
|
+
const documentationOwner = completionOwnerTarget(project, module, memberAccess.ownerOffset, ownerExpression?.type ?? owner?.type ?? null);
|
|
319
|
+
return members.map((member) => {
|
|
320
|
+
const declared = documentationOwner
|
|
321
|
+
? findDeclaredMember(project, documentationOwner.target, member.name, documentationOwner.staticMember, new Set())
|
|
322
|
+
: null;
|
|
323
|
+
return {
|
|
324
|
+
label: member.name,
|
|
325
|
+
detail: member.type,
|
|
326
|
+
kind: member.kind,
|
|
327
|
+
...(declared?.symbol.documentation ? { documentation: declared.symbol.documentation } : {}),
|
|
328
|
+
};
|
|
329
|
+
});
|
|
330
|
+
}
|
|
331
|
+
const object = objectFieldContextAt(module.result.semanticIndex, module.result.source.text, offset);
|
|
332
|
+
if (object) {
|
|
333
|
+
const contextOwner = object.expression.contextType
|
|
334
|
+
? ownerTarget(project, module, object.expression.contextType, "named")
|
|
335
|
+
: null;
|
|
336
|
+
return (object.expression.contextMembers ?? []).filter((member) => !object.used.has(member.name)).map((member) => {
|
|
337
|
+
const declared = contextOwner ? findDeclaredMember(project, contextOwner, member.name, false, new Set()) : null;
|
|
338
|
+
return {
|
|
339
|
+
label: member.name,
|
|
340
|
+
detail: member.type,
|
|
341
|
+
kind: member.kind,
|
|
342
|
+
...(declared?.symbol.documentation ? { documentation: declared.symbol.documentation } : {}),
|
|
343
|
+
};
|
|
344
|
+
});
|
|
345
|
+
}
|
|
346
|
+
return semanticVisibleSymbolsAt(module.result.semanticIndex, offset).map((symbol) => {
|
|
347
|
+
const resolved = projectSymbolAt(project, module.inputPath, symbol.selectionSpan.start) ?? symbol;
|
|
348
|
+
return {
|
|
349
|
+
label: symbol.name,
|
|
350
|
+
detail: symbol.type ?? symbol.kind,
|
|
351
|
+
kind: symbol.kind,
|
|
352
|
+
...(resolved.presentationKind ? { presentationKind: resolved.presentationKind } : {}),
|
|
353
|
+
...(resolved.documentation ? { documentation: resolved.documentation } : {}),
|
|
354
|
+
};
|
|
355
|
+
});
|
|
356
|
+
}
|
|
357
|
+
function completionOwnerTarget(project, module, ownerOffset, displayedType) {
|
|
358
|
+
if (ownerOffset !== null) {
|
|
359
|
+
const direct = targetAt(project, module, ownerOffset, "definition");
|
|
360
|
+
if (direct?.symbol.kind === "class")
|
|
361
|
+
return { target: direct, staticMember: true };
|
|
362
|
+
if (direct?.symbol.kind.startsWith("extension:function:") || direct?.symbol.kind.startsWith("extension:class:")) {
|
|
363
|
+
return { target: direct, staticMember: false };
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
if (!displayedType)
|
|
367
|
+
return null;
|
|
368
|
+
const classTarget = ownerTarget(project, module, displayedType, "class");
|
|
369
|
+
if (classTarget)
|
|
370
|
+
return { target: classTarget, staticMember: false };
|
|
371
|
+
const recordTarget = ownerTarget(project, module, displayedType, "named");
|
|
372
|
+
return recordTarget ? { target: recordTarget, staticMember: false } : null;
|
|
373
|
+
}
|
|
374
|
+
export function projectCompletionContextAt(project, path, offset) {
|
|
375
|
+
const module = moduleAt(project, path);
|
|
376
|
+
if (!module)
|
|
377
|
+
return "ordinary";
|
|
378
|
+
const source = module.result.source.text;
|
|
379
|
+
const extension = extensionCompletionAt(project, module, offset);
|
|
380
|
+
if (extension)
|
|
381
|
+
return `extension:${extension.extensionId}:${extension.context}`;
|
|
382
|
+
if (memberAccessAt(source, offset))
|
|
383
|
+
return "member";
|
|
384
|
+
if (objectFieldContextAt(module.result.semanticIndex, source, offset))
|
|
385
|
+
return "object-field";
|
|
386
|
+
return "ordinary";
|
|
387
|
+
}
|
|
388
|
+
function extensionCompletionAt(project, module, offset) {
|
|
389
|
+
const extensions = project.compilerExtensions.filter((extension) => extension.editor?.project?.complete);
|
|
390
|
+
if (extensions.length === 0)
|
|
391
|
+
return null;
|
|
392
|
+
const visibleSymbols = semanticVisibleSymbolsAt(module.result.semanticIndex, offset).map((symbol) => {
|
|
393
|
+
const resolved = projectSymbolAt(project, module.inputPath, symbol.selectionSpan.start) ?? symbol;
|
|
394
|
+
return {
|
|
395
|
+
label: symbol.name,
|
|
396
|
+
detail: symbol.type ?? symbol.kind,
|
|
397
|
+
kind: symbol.kind,
|
|
398
|
+
...(resolved.presentationKind ? { presentationKind: resolved.presentationKind } : {}),
|
|
399
|
+
...(resolved.documentation ? { documentation: resolved.documentation } : {}),
|
|
400
|
+
};
|
|
401
|
+
});
|
|
402
|
+
const membersAt = (memberOffset) => {
|
|
403
|
+
const target = targetAt(project, module, memberOffset, "definition");
|
|
404
|
+
if (!target)
|
|
405
|
+
return [];
|
|
406
|
+
return target.symbol.members.map((member) => {
|
|
407
|
+
const declared = findDeclaredMember(project, target, member.name, false, new Set());
|
|
408
|
+
return {
|
|
409
|
+
label: member.name,
|
|
410
|
+
detail: member.type,
|
|
411
|
+
kind: member.kind,
|
|
412
|
+
...(declared?.symbol.documentation ? { documentation: declared.symbol.documentation } : {}),
|
|
413
|
+
};
|
|
414
|
+
});
|
|
415
|
+
};
|
|
416
|
+
for (const extension of extensions) {
|
|
417
|
+
const result = extension.editor.project.complete({
|
|
418
|
+
source: module.result.source.text,
|
|
419
|
+
offset,
|
|
420
|
+
visibleSymbols,
|
|
421
|
+
membersAt,
|
|
422
|
+
});
|
|
423
|
+
if (result)
|
|
424
|
+
return { extensionId: extension.id, context: result.context, completions: result.completions };
|
|
425
|
+
}
|
|
426
|
+
return null;
|
|
427
|
+
}
|
|
428
|
+
function objectFieldContextAt(index, source, offset) {
|
|
429
|
+
const expression = index.expressions
|
|
430
|
+
.filter((item) => item.contextMembers && offset > item.span.start && offset <= item.span.end)
|
|
431
|
+
.sort((left, right) => right.span.start - left.span.start)[0];
|
|
432
|
+
if (!expression)
|
|
433
|
+
return null;
|
|
434
|
+
const fragment = source.slice(expression.span.start + 1, Math.min(offset, expression.span.end));
|
|
435
|
+
let quote = null;
|
|
436
|
+
let escaped = false;
|
|
437
|
+
let depth = 0;
|
|
438
|
+
let visible = "";
|
|
439
|
+
for (const character of fragment) {
|
|
440
|
+
if (quote) {
|
|
441
|
+
visible += " ";
|
|
442
|
+
if (!escaped && character === quote)
|
|
443
|
+
quote = null;
|
|
444
|
+
escaped = !escaped && character === "\\";
|
|
445
|
+
if (character !== "\\")
|
|
446
|
+
escaped = false;
|
|
447
|
+
continue;
|
|
448
|
+
}
|
|
449
|
+
if (character === "\"" || character === "'") {
|
|
450
|
+
quote = character;
|
|
451
|
+
visible += " ";
|
|
452
|
+
continue;
|
|
453
|
+
}
|
|
454
|
+
if (character === "{" || character === "[" || character === "(") {
|
|
455
|
+
depth += 1;
|
|
456
|
+
visible += " ";
|
|
457
|
+
continue;
|
|
458
|
+
}
|
|
459
|
+
if (character === "}" || character === "]" || character === ")") {
|
|
460
|
+
depth = Math.max(0, depth - 1);
|
|
461
|
+
visible += " ";
|
|
462
|
+
continue;
|
|
463
|
+
}
|
|
464
|
+
visible += depth === 0 ? character : " ";
|
|
465
|
+
}
|
|
466
|
+
if (quote || depth > 0)
|
|
467
|
+
return null;
|
|
468
|
+
const segments = visible.split(",");
|
|
469
|
+
const current = segments.at(-1) ?? "";
|
|
470
|
+
if (current.includes(":"))
|
|
471
|
+
return null;
|
|
472
|
+
const used = new Set();
|
|
473
|
+
for (const segment of segments) {
|
|
474
|
+
const match = /^\s*([A-Za-z_$][A-Za-z0-9_$]*)/u.exec(segment);
|
|
475
|
+
if (match)
|
|
476
|
+
used.add(match[1]);
|
|
477
|
+
}
|
|
478
|
+
return { expression, used };
|
|
479
|
+
}
|
|
480
|
+
function memberAccessAt(source, offset) {
|
|
481
|
+
let cursor = Math.min(Math.max(0, offset), source.length);
|
|
482
|
+
while (cursor > 0 && isSourceIdentifierPart(source[cursor - 1]))
|
|
483
|
+
cursor -= 1;
|
|
484
|
+
let dot = cursor - 1;
|
|
485
|
+
while (dot >= 0 && /\s/u.test(source[dot]))
|
|
486
|
+
dot -= 1;
|
|
487
|
+
if (source[dot] !== ".")
|
|
488
|
+
return null;
|
|
489
|
+
let ownerEnd = dot;
|
|
490
|
+
if (source[ownerEnd - 1] === "?")
|
|
491
|
+
ownerEnd -= 1;
|
|
492
|
+
while (ownerEnd > 0 && /\s/u.test(source[ownerEnd - 1]))
|
|
493
|
+
ownerEnd -= 1;
|
|
494
|
+
let ownerStart = ownerEnd;
|
|
495
|
+
while (ownerStart > 0 && isSourceIdentifierPart(source[ownerStart - 1]))
|
|
496
|
+
ownerStart -= 1;
|
|
497
|
+
const ownerOffset = ownerStart < ownerEnd && isSourceIdentifierStart(source[ownerStart]) ? ownerStart : null;
|
|
498
|
+
return { ownerOffset, ownerEnd };
|
|
499
|
+
}
|
|
500
|
+
export function projectSignatureAt(project, path, offset) {
|
|
501
|
+
const module = moduleAt(project, path);
|
|
502
|
+
if (!module)
|
|
503
|
+
return null;
|
|
504
|
+
const source = module.result.source.text;
|
|
505
|
+
const call = callAt(source, offset);
|
|
506
|
+
if (!call)
|
|
507
|
+
return null;
|
|
508
|
+
const symbol = projectSymbolAt(project, path, call.calleeOffset);
|
|
509
|
+
if (symbol?.callable && symbol.type)
|
|
510
|
+
return { label: `${symbol.name}${symbol.type}`, activeParameter: call.activeParameter };
|
|
511
|
+
const expression = module.result.semanticIndex.expressions.find((item) => item.selectionSpan && contains(item.selectionSpan, call.calleeOffset));
|
|
512
|
+
if (expression?.memberName && expression.callable) {
|
|
513
|
+
return { label: `${expression.memberName}${expression.type}`, activeParameter: call.activeParameter };
|
|
514
|
+
}
|
|
515
|
+
const member = memberCallAt(project, module, source, call.calleeOffset);
|
|
516
|
+
return member?.kind === "method"
|
|
517
|
+
? { label: `${member.name}${member.type}`, activeParameter: call.activeParameter }
|
|
518
|
+
: null;
|
|
519
|
+
}
|
|
520
|
+
function memberCallAt(project, module, source, calleeOffset) {
|
|
521
|
+
const callee = wordSpanAt(source, calleeOffset);
|
|
522
|
+
if (!callee)
|
|
523
|
+
return null;
|
|
524
|
+
let dot = callee.start - 1;
|
|
525
|
+
while (dot >= 0 && /\s/u.test(source[dot]))
|
|
526
|
+
dot -= 1;
|
|
527
|
+
if (source[dot] !== ".")
|
|
528
|
+
return null;
|
|
529
|
+
let ownerEnd = dot;
|
|
530
|
+
if (source[ownerEnd - 1] === "?")
|
|
531
|
+
ownerEnd -= 1;
|
|
532
|
+
while (ownerEnd > 0 && /\s/u.test(source[ownerEnd - 1]))
|
|
533
|
+
ownerEnd -= 1;
|
|
534
|
+
let ownerStart = ownerEnd;
|
|
535
|
+
while (ownerStart > 0 && isSourceIdentifierPart(source[ownerStart - 1]))
|
|
536
|
+
ownerStart -= 1;
|
|
537
|
+
if (ownerStart === ownerEnd || !isSourceIdentifierStart(source[ownerStart]))
|
|
538
|
+
return null;
|
|
539
|
+
const owner = projectSymbolAt(project, module.inputPath, ownerStart);
|
|
540
|
+
const memberName = source.slice(callee.start, callee.end);
|
|
541
|
+
return owner?.members.find((member) => member.name === memberName) ?? null;
|
|
542
|
+
}
|
|
543
|
+
function targetAt(project, module, offset, purpose) {
|
|
544
|
+
const index = module.result.semanticIndex;
|
|
545
|
+
const imported = semanticImportAt(index, offset);
|
|
546
|
+
if (imported && !imported.namespace) {
|
|
547
|
+
const onImported = contains(imported.importedSpan, offset);
|
|
548
|
+
const aliased = imported.local !== imported.imported;
|
|
549
|
+
if (onImported && (!aliased || !contains(imported.localSpan, offset))) {
|
|
550
|
+
return exportedTarget(project, module, imported);
|
|
551
|
+
}
|
|
552
|
+
const local = index.symbols.find((symbol) => symbol.id === imported.localSymbolId);
|
|
553
|
+
if (local) {
|
|
554
|
+
if (!aliased)
|
|
555
|
+
return exportedTarget(project, module, imported) ?? { kind: "local", module, symbol: local };
|
|
556
|
+
return { kind: "local", module, symbol: local };
|
|
557
|
+
}
|
|
558
|
+
}
|
|
559
|
+
const symbol = semanticSymbolAt(index, offset);
|
|
560
|
+
if (!symbol)
|
|
561
|
+
return null;
|
|
562
|
+
if (symbol.kind === "import") {
|
|
563
|
+
const specifier = importForSymbol(index, symbol.id);
|
|
564
|
+
if (specifier && specifier.local === specifier.imported) {
|
|
565
|
+
return exportedTarget(project, module, specifier) ?? { kind: "local", module, symbol };
|
|
566
|
+
}
|
|
567
|
+
}
|
|
568
|
+
return symbol.exported ? { kind: "export", module, symbol } : { kind: "local", module, symbol };
|
|
569
|
+
}
|
|
570
|
+
function memberTargetAt(project, module, offset) {
|
|
571
|
+
const reference = module.result.semanticIndex.memberReferences.find((item) => contains(item.span, offset));
|
|
572
|
+
if (reference)
|
|
573
|
+
return memberTargetForReference(project, module, reference);
|
|
574
|
+
const declared = semanticSymbolAt(module.result.semanticIndex, offset);
|
|
575
|
+
if (declared && (declared.kind === "field" || declared.kind === "method" || declared.kind === "parameter") && declared.container) {
|
|
576
|
+
return { module, symbol: declared };
|
|
577
|
+
}
|
|
578
|
+
const expression = module.result.semanticIndex.expressions.find((item) => item.selectionSpan && contains(item.selectionSpan, offset));
|
|
579
|
+
return expression ? memberTargetForExpression(project, module, expression) : null;
|
|
580
|
+
}
|
|
581
|
+
function accessibleMemberTargetAt(project, module, offset) {
|
|
582
|
+
const target = memberTargetAt(project, module, offset);
|
|
583
|
+
if (!target?.symbol.private)
|
|
584
|
+
return target;
|
|
585
|
+
if (target.module.inputPath !== module.inputPath || !target.symbol.container)
|
|
586
|
+
return null;
|
|
587
|
+
const owner = module.result.semanticIndex.symbols.find((symbol) => symbol.kind === "class"
|
|
588
|
+
&& symbol.name === target.symbol.container);
|
|
589
|
+
return owner && contains(owner.span, offset) ? target : null;
|
|
590
|
+
}
|
|
591
|
+
function memberTargetForExpression(project, module, expression) {
|
|
592
|
+
if (!expression.memberName || !expression.ownerType || !expression.ownerKind)
|
|
593
|
+
return null;
|
|
594
|
+
return memberTargetForOwner(project, module, expression.memberName, expression.ownerType, expression.ownerKind, expression.ownerIdentity, expression.ownerSymbolKind);
|
|
595
|
+
}
|
|
596
|
+
function memberTargetForReference(project, module, reference) {
|
|
597
|
+
return memberTargetForOwner(project, module, reference.name, reference.ownerType, reference.ownerKind, reference.ownerIdentity, reference.ownerSymbolKind);
|
|
598
|
+
}
|
|
599
|
+
function memberTargetForOwner(project, module, memberName, ownerType, ownerKind, ownerIdentity, ownerSymbolKind) {
|
|
600
|
+
if (ownerKind !== "named" && ownerKind !== "class" && ownerKind !== "typeObject"
|
|
601
|
+
&& ownerKind !== "classConstructor" && ownerKind !== "extension")
|
|
602
|
+
return null;
|
|
603
|
+
if (ownerKind === "extension" && !ownerSymbolKind)
|
|
604
|
+
return null;
|
|
605
|
+
const owner = ownerTarget(project, module, ownerType, ownerKind, ownerIdentity, new Set(), ownerSymbolKind);
|
|
606
|
+
return owner ? findDeclaredMember(project, owner, memberName, ownerKind === "classConstructor", new Set()) : null;
|
|
607
|
+
}
|
|
608
|
+
function ownerTarget(project, module, name, ownerKind, ownerIdentity, visited = new Set(), ownerSymbolKind) {
|
|
609
|
+
const expected = ownerKind === "class" || ownerKind === "classConstructor" ? "class"
|
|
610
|
+
: ownerKind === "extension" && ownerSymbolKind ? ownerSymbolKind : "type";
|
|
611
|
+
const key = `${module.inputPath}:${expected}:${name}`;
|
|
612
|
+
if (visited.has(key))
|
|
613
|
+
return null;
|
|
614
|
+
visited.add(key);
|
|
615
|
+
const identity = ownerIdentity ?? (expected === "class" ? name : null);
|
|
616
|
+
if (identity && (expected === "class" || expected === "type")) {
|
|
617
|
+
const identified = declaredOwnerByIdentity(project, identity, expected);
|
|
618
|
+
if (identified)
|
|
619
|
+
return identified;
|
|
620
|
+
}
|
|
621
|
+
const symbol = module.result.semanticIndex.symbols.find((item) => item.name === name
|
|
622
|
+
&& (item.kind === expected || item.kind === "import"));
|
|
623
|
+
if (!symbol)
|
|
624
|
+
return null;
|
|
625
|
+
if (symbol.kind === "import") {
|
|
626
|
+
const imported = importForSymbol(module.result.semanticIndex, symbol.id);
|
|
627
|
+
const exported = imported ? exportedTarget(project, module, imported) : null;
|
|
628
|
+
return exported && exported.symbol.kind === expected
|
|
629
|
+
? ownerTarget(project, exported.module, exported.symbol.name, ownerKind, ownerIdentity, visited, ownerSymbolKind)
|
|
630
|
+
: null;
|
|
631
|
+
}
|
|
632
|
+
if (symbol.kind === "type" && symbol.typeTarget && symbol.typeTarget !== symbol.name) {
|
|
633
|
+
return ownerTarget(project, module, symbol.typeTarget, "named", undefined, visited) ?? { module, symbol };
|
|
634
|
+
}
|
|
635
|
+
return { module, symbol };
|
|
636
|
+
}
|
|
637
|
+
function declaredOwnerByIdentity(project, identity, kind) {
|
|
638
|
+
for (const candidate of project.modules) {
|
|
639
|
+
const declarations = kind === "class"
|
|
640
|
+
? [...candidate.result.moduleInterface.classes].map(([name, info]) => [name, info.identity])
|
|
641
|
+
: [...candidate.result.moduleInterface.namedTypeIdentities];
|
|
642
|
+
for (const [name, declarationIdentity] of declarations) {
|
|
643
|
+
if (declarationIdentity !== identity)
|
|
644
|
+
continue;
|
|
645
|
+
const symbol = candidate.result.semanticIndex.symbols.find((item) => item.kind === kind && item.name === name);
|
|
646
|
+
if (symbol)
|
|
647
|
+
return { module: candidate, symbol };
|
|
648
|
+
}
|
|
649
|
+
}
|
|
650
|
+
return null;
|
|
651
|
+
}
|
|
652
|
+
function findDeclaredMember(project, owner, name, staticMember, visited, allowPrivate = true) {
|
|
653
|
+
const key = `${owner.symbol.id}:${name}:${staticMember}`;
|
|
654
|
+
if (visited.has(key))
|
|
655
|
+
return null;
|
|
656
|
+
visited.add(key);
|
|
657
|
+
const direct = owner.module.result.semanticIndex.symbols.find((symbol) => symbol.container === owner.symbol.name
|
|
658
|
+
&& symbol.name === name
|
|
659
|
+
&& (allowPrivate || !symbol.private)
|
|
660
|
+
&& (owner.symbol.kind.startsWith("extension:function:") || owner.symbol.kind.startsWith("extension:class:")
|
|
661
|
+
? symbol.kind === "parameter"
|
|
662
|
+
: (symbol.kind === "field" || symbol.kind === "method") && Boolean(symbol.static) === staticMember));
|
|
663
|
+
if (direct)
|
|
664
|
+
return { module: owner.module, symbol: direct };
|
|
665
|
+
if (owner.symbol.kind !== "class")
|
|
666
|
+
return null;
|
|
667
|
+
const base = owner.module.result.moduleInterface.classes.get(owner.symbol.name)?.base;
|
|
668
|
+
if (!base)
|
|
669
|
+
return null;
|
|
670
|
+
const baseOwner = ownerTarget(project, owner.module, base, "class");
|
|
671
|
+
return baseOwner ? findDeclaredMember(project, baseOwner, name, staticMember, visited, false) : null;
|
|
672
|
+
}
|
|
673
|
+
function memberLocations(project, target, includeDeclaration) {
|
|
674
|
+
const targets = memberContractTargets(project, target);
|
|
675
|
+
const targetIds = new Set(targets.map((item) => item.symbol.id));
|
|
676
|
+
const locations = includeDeclaration ? targets.map((item) => locationOf(item.symbol)) : [];
|
|
677
|
+
for (const item of targets)
|
|
678
|
+
locations.push(...referencesFor(item.module, item.symbol));
|
|
679
|
+
for (const module of project.modules) {
|
|
680
|
+
for (const reference of module.result.semanticIndex.memberReferences) {
|
|
681
|
+
if (reference.name !== target.symbol.name)
|
|
682
|
+
continue;
|
|
683
|
+
const resolved = memberTargetForReference(project, module, reference);
|
|
684
|
+
if (resolved && targetIds.has(resolved.symbol.id)) {
|
|
685
|
+
locations.push({ path: module.inputPath, span: reference.span });
|
|
686
|
+
}
|
|
687
|
+
}
|
|
688
|
+
}
|
|
689
|
+
return uniqueLocations(locations);
|
|
690
|
+
}
|
|
691
|
+
function renameableMember(target) {
|
|
692
|
+
if (!target.symbol.container)
|
|
693
|
+
return false;
|
|
694
|
+
if (target.symbol.kind === "method")
|
|
695
|
+
return true;
|
|
696
|
+
if (target.symbol.kind === "parameter") {
|
|
697
|
+
return target.module.result.semanticIndex.symbols.some((symbol) => (symbol.kind.startsWith("extension:function:") || symbol.kind.startsWith("extension:class:"))
|
|
698
|
+
&& symbol.name === target.symbol.container);
|
|
699
|
+
}
|
|
700
|
+
if (target.symbol.kind !== "field")
|
|
701
|
+
return false;
|
|
702
|
+
return target.module.result.semanticIndex.symbols.some((symbol) => (symbol.kind === "type" || symbol.kind === "class")
|
|
703
|
+
&& symbol.name === target.symbol.container);
|
|
704
|
+
}
|
|
705
|
+
function extensionRenameProtection(project, target) {
|
|
706
|
+
const containerKind = target.symbol.container
|
|
707
|
+
? target.module.result.semanticIndex.symbols.find((symbol) => symbol.name === target.symbol.container)?.kind ?? null
|
|
708
|
+
: null;
|
|
709
|
+
for (const extension of project.compilerExtensions) {
|
|
710
|
+
const message = extension.editor?.project?.protectRename?.({
|
|
711
|
+
name: target.symbol.name,
|
|
712
|
+
kind: target.symbol.kind,
|
|
713
|
+
container: target.symbol.container ?? null,
|
|
714
|
+
containerKind,
|
|
715
|
+
});
|
|
716
|
+
if (message)
|
|
717
|
+
return message;
|
|
718
|
+
}
|
|
719
|
+
return null;
|
|
720
|
+
}
|
|
721
|
+
function memberRenameCollides(project, target, newName) {
|
|
722
|
+
const targets = memberContractTargets(project, target);
|
|
723
|
+
const targetIds = new Set(targets.map((item) => item.symbol.id));
|
|
724
|
+
const contractRoot = (target.symbol.kind === "method" || target.symbol.kind === "field") && !target.symbol.static && !target.symbol.private
|
|
725
|
+
? memberContractRootOwner(project, target) : null;
|
|
726
|
+
const owners = contractRoot ? classHierarchyOwners(project, contractRoot)
|
|
727
|
+
: targets.flatMap((item) => classOwnerForMember(item) ? [classOwnerForMember(item)] : []);
|
|
728
|
+
if (owners.length === 0) {
|
|
729
|
+
return target.module.result.semanticIndex.symbols.some((symbol) => !targetIds.has(symbol.id)
|
|
730
|
+
&& symbol.container === target.symbol.container
|
|
731
|
+
&& (symbol.kind === "field" || symbol.kind === "method" || symbol.kind === "parameter")
|
|
732
|
+
&& symbol.name === newName);
|
|
733
|
+
}
|
|
734
|
+
const hierarchyCollision = owners.some((owner) => owner.module.result.semanticIndex.symbols.some((symbol) => !targetIds.has(symbol.id)
|
|
735
|
+
&& symbol.container === owner.symbol.name
|
|
736
|
+
&& (symbol.kind === "parameter" || ((symbol.kind === "field" || symbol.kind === "method")
|
|
737
|
+
&& Boolean(symbol.static) === Boolean(target.symbol.static)))
|
|
738
|
+
&& symbol.name === newName));
|
|
739
|
+
if (hierarchyCollision || !contractRoot)
|
|
740
|
+
return hierarchyCollision;
|
|
741
|
+
for (let ancestor = baseClassOwner(project, contractRoot); ancestor; ancestor = baseClassOwner(project, ancestor)) {
|
|
742
|
+
if (directClassMember(ancestor, newName, false))
|
|
743
|
+
return true;
|
|
744
|
+
}
|
|
745
|
+
return false;
|
|
746
|
+
}
|
|
747
|
+
function memberRenameEdits(project, target, newName) {
|
|
748
|
+
const targets = memberContractTargets(project, target);
|
|
749
|
+
const targetIds = new Set(targets.map((item) => item.symbol.id));
|
|
750
|
+
const edits = targets.map((item) => ({ ...locationOf(item.symbol), replacement: newName }));
|
|
751
|
+
for (const item of targets) {
|
|
752
|
+
edits.push(...referencesFor(item.module, item.symbol).map((location) => ({ ...location, replacement: newName })));
|
|
753
|
+
}
|
|
754
|
+
for (const module of project.modules) {
|
|
755
|
+
for (const reference of module.result.semanticIndex.memberReferences) {
|
|
756
|
+
if (reference.name !== target.symbol.name)
|
|
757
|
+
continue;
|
|
758
|
+
const resolved = memberTargetForReference(project, module, reference);
|
|
759
|
+
if (!resolved || !targetIds.has(resolved.symbol.id))
|
|
760
|
+
continue;
|
|
761
|
+
edits.push({
|
|
762
|
+
path: module.inputPath,
|
|
763
|
+
span: reference.span,
|
|
764
|
+
replacement: reference.shorthand ? `${newName}: ${reference.name}` : newName,
|
|
765
|
+
});
|
|
766
|
+
}
|
|
767
|
+
}
|
|
768
|
+
return uniqueTextEdits(edits);
|
|
769
|
+
}
|
|
770
|
+
function memberContractTargets(project, target) {
|
|
771
|
+
if ((target.symbol.kind !== "method" && target.symbol.kind !== "field") || target.symbol.static || target.symbol.private)
|
|
772
|
+
return [target];
|
|
773
|
+
const rootOwner = memberContractRootOwner(project, target);
|
|
774
|
+
if (!rootOwner)
|
|
775
|
+
return [target];
|
|
776
|
+
const targets = [];
|
|
777
|
+
for (const candidate of classHierarchyOwners(project, rootOwner)) {
|
|
778
|
+
const declared = directClassMember(candidate, target.symbol.name, false, target.symbol.kind);
|
|
779
|
+
if (declared?.symbol.kind === target.symbol.kind)
|
|
780
|
+
targets.push(declared);
|
|
781
|
+
}
|
|
782
|
+
return targets.length > 0 ? targets : [target];
|
|
783
|
+
}
|
|
784
|
+
function memberContractRootOwner(project, target) {
|
|
785
|
+
if (target.symbol.kind !== "field" && target.symbol.kind !== "method")
|
|
786
|
+
return null;
|
|
787
|
+
const memberKind = target.symbol.kind;
|
|
788
|
+
const owner = classOwnerForMember(target);
|
|
789
|
+
if (!owner)
|
|
790
|
+
return null;
|
|
791
|
+
let rootOwner = owner;
|
|
792
|
+
for (let current = baseClassOwner(project, owner); current; current = baseClassOwner(project, current)) {
|
|
793
|
+
const declared = directClassMember(current, target.symbol.name, false, memberKind);
|
|
794
|
+
if (declared?.symbol.kind === memberKind)
|
|
795
|
+
rootOwner = current;
|
|
796
|
+
}
|
|
797
|
+
return rootOwner;
|
|
798
|
+
}
|
|
799
|
+
function classHierarchyOwners(project, rootOwner) {
|
|
800
|
+
const owners = [];
|
|
801
|
+
for (const module of project.modules) {
|
|
802
|
+
for (const symbol of module.result.semanticIndex.symbols) {
|
|
803
|
+
if (symbol.kind !== "class")
|
|
804
|
+
continue;
|
|
805
|
+
const candidate = { module, symbol };
|
|
806
|
+
if (classDescendsFrom(project, candidate, rootOwner))
|
|
807
|
+
owners.push(candidate);
|
|
808
|
+
}
|
|
809
|
+
}
|
|
810
|
+
return owners;
|
|
811
|
+
}
|
|
812
|
+
function classOwnerForMember(target) {
|
|
813
|
+
if (!target.symbol.container)
|
|
814
|
+
return null;
|
|
815
|
+
const owner = target.module.result.semanticIndex.symbols.find((symbol) => symbol.kind === "class" && symbol.name === target.symbol.container);
|
|
816
|
+
return owner ? { module: target.module, symbol: owner } : null;
|
|
817
|
+
}
|
|
818
|
+
function baseClassOwner(project, owner) {
|
|
819
|
+
const base = owner.module.result.moduleInterface.classes.get(owner.symbol.name)?.base;
|
|
820
|
+
return base ? ownerTarget(project, owner.module, base, "class") : null;
|
|
821
|
+
}
|
|
822
|
+
function directClassMember(owner, name, staticMember, expectedKind) {
|
|
823
|
+
const symbol = owner.module.result.semanticIndex.symbols.find((item) => item.container === owner.symbol.name
|
|
824
|
+
&& item.name === name
|
|
825
|
+
&& (!expectedKind || item.kind === expectedKind)
|
|
826
|
+
&& (item.kind === "field" || item.kind === "method")
|
|
827
|
+
&& Boolean(item.static) === staticMember);
|
|
828
|
+
return symbol ? { module: owner.module, symbol } : null;
|
|
829
|
+
}
|
|
830
|
+
function classDescendsFrom(project, candidate, expected) {
|
|
831
|
+
const visited = new Set();
|
|
832
|
+
for (let current = candidate; current; current = baseClassOwner(project, current)) {
|
|
833
|
+
if (current.symbol.id === expected.symbol.id)
|
|
834
|
+
return true;
|
|
835
|
+
if (visited.has(current.symbol.id))
|
|
836
|
+
return false;
|
|
837
|
+
visited.add(current.symbol.id);
|
|
838
|
+
}
|
|
839
|
+
return false;
|
|
840
|
+
}
|
|
841
|
+
function exportedTarget(project, importer, imported, visited = new Set()) {
|
|
842
|
+
const targetModule = importedModule(project, importer, imported);
|
|
843
|
+
if (!targetModule)
|
|
844
|
+
return null;
|
|
845
|
+
const symbol = targetModule.result.semanticIndex.symbols.find((item) => item.exported && item.name === imported.imported);
|
|
846
|
+
if (!symbol)
|
|
847
|
+
return null;
|
|
848
|
+
if (symbol.kind === "import") {
|
|
849
|
+
// An exported import symbol is a named re-export; follow the chain to the
|
|
850
|
+
// module that actually declares the value.
|
|
851
|
+
const forwarded = targetModule.result.semanticIndex.imports.find((item) => item.localSymbolId === symbol.id);
|
|
852
|
+
const key = `${targetModule.inputPath}\0${symbol.id}`;
|
|
853
|
+
if (forwarded && !visited.has(key)) {
|
|
854
|
+
visited.add(key);
|
|
855
|
+
const origin = exportedTarget(project, targetModule, forwarded, visited);
|
|
856
|
+
if (origin)
|
|
857
|
+
return origin;
|
|
858
|
+
}
|
|
859
|
+
}
|
|
860
|
+
return { kind: "export", module: targetModule, symbol };
|
|
861
|
+
}
|
|
862
|
+
function importedModule(project, importer, imported) {
|
|
863
|
+
const target = imported.source.startsWith(".") && extname(imported.source) === ".vel"
|
|
864
|
+
? resolve(dirname(importer.inputPath), imported.source)
|
|
865
|
+
: project.velarImports.get(projectImportKey(importer.inputPath, imported.source));
|
|
866
|
+
return target ? moduleAt(project, target) : null;
|
|
867
|
+
}
|
|
868
|
+
function enumMemberAt(project, module, offset) {
|
|
869
|
+
const source = module.result.source.text;
|
|
870
|
+
const property = wordSpanAt(source, offset);
|
|
871
|
+
if (!property)
|
|
872
|
+
return null;
|
|
873
|
+
let dot = property.start - 1;
|
|
874
|
+
while (dot >= 0 && /\s/u.test(source[dot]))
|
|
875
|
+
dot -= 1;
|
|
876
|
+
if (source[dot] !== ".")
|
|
877
|
+
return null;
|
|
878
|
+
let ownerEnd = dot;
|
|
879
|
+
while (ownerEnd > 0 && /\s/u.test(source[ownerEnd - 1]))
|
|
880
|
+
ownerEnd -= 1;
|
|
881
|
+
let ownerStart = ownerEnd;
|
|
882
|
+
while (ownerStart > 0 && isSourceIdentifierPart(source[ownerStart - 1]))
|
|
883
|
+
ownerStart -= 1;
|
|
884
|
+
if (ownerStart === ownerEnd || !isSourceIdentifierStart(source[ownerStart]))
|
|
885
|
+
return null;
|
|
886
|
+
const owner = projectSymbolAt(project, module.inputPath, ownerStart);
|
|
887
|
+
if (owner?.kind !== "enum")
|
|
888
|
+
return null;
|
|
889
|
+
const target = moduleAt(project, owner.path);
|
|
890
|
+
if (!target)
|
|
891
|
+
return null;
|
|
892
|
+
const name = source.slice(property.start, property.end);
|
|
893
|
+
return target.result.semanticIndex.symbols.find((symbol) => symbol.kind === "enum-member" && symbol.container === owner.name && symbol.name === name) ?? null;
|
|
894
|
+
}
|
|
895
|
+
function enumMemberTargetAt(project, module, offset) {
|
|
896
|
+
const qualified = enumMemberAt(project, module, offset);
|
|
897
|
+
if (qualified)
|
|
898
|
+
return qualified;
|
|
899
|
+
const local = semanticSymbolAt(module.result.semanticIndex, offset);
|
|
900
|
+
return local?.kind === "enum-member" ? local : null;
|
|
901
|
+
}
|
|
902
|
+
function enumMemberLocations(project, target, includeDeclaration) {
|
|
903
|
+
const locations = includeDeclaration ? [locationOf(target)] : [];
|
|
904
|
+
const pattern = new RegExp(`\\b${escapeRegExp(target.name)}\\b`, "gu");
|
|
905
|
+
for (const module of project.modules) {
|
|
906
|
+
const source = module.result.source.text;
|
|
907
|
+
pattern.lastIndex = 0;
|
|
908
|
+
for (let match = pattern.exec(source); match; match = pattern.exec(source)) {
|
|
909
|
+
const member = enumMemberAt(project, module, match.index);
|
|
910
|
+
if (member?.id === target.id && member.path === target.path) {
|
|
911
|
+
locations.push({ path: module.inputPath, span: { start: match.index, end: match.index + target.name.length } });
|
|
912
|
+
}
|
|
913
|
+
}
|
|
914
|
+
}
|
|
915
|
+
return uniqueLocations(locations);
|
|
916
|
+
}
|
|
917
|
+
function enumMemberRenameCollides(project, target, newName) {
|
|
918
|
+
const module = moduleAt(project, target.path);
|
|
919
|
+
return module?.result.semanticIndex.symbols.some((symbol) => symbol.kind === "enum-member"
|
|
920
|
+
&& symbol.id !== target.id
|
|
921
|
+
&& symbol.container === target.container
|
|
922
|
+
&& symbol.name === newName) ?? false;
|
|
923
|
+
}
|
|
924
|
+
function escapeRegExp(value) {
|
|
925
|
+
return value.replace(/[.*+?^${}()|[\]\\]/gu, "\\$&");
|
|
926
|
+
}
|
|
927
|
+
function exportedLocations(project, target, includeDeclaration) {
|
|
928
|
+
const locations = includeDeclaration ? [locationOf(target.symbol)] : [];
|
|
929
|
+
locations.push(...referencesFor(target.module, target.symbol));
|
|
930
|
+
for (const module of project.modules) {
|
|
931
|
+
for (const imported of module.result.semanticIndex.imports) {
|
|
932
|
+
const importedTarget = exportedTarget(project, module, imported);
|
|
933
|
+
if (!importedTarget || importedTarget.symbol.id !== target.symbol.id)
|
|
934
|
+
continue;
|
|
935
|
+
locations.push({ path: module.inputPath, span: imported.importedSpan });
|
|
936
|
+
if (imported.local === imported.imported) {
|
|
937
|
+
const local = module.result.semanticIndex.symbols.find((symbol) => symbol.id === imported.localSymbolId);
|
|
938
|
+
if (local)
|
|
939
|
+
locations.push(...referencesFor(module, local));
|
|
940
|
+
}
|
|
941
|
+
}
|
|
942
|
+
}
|
|
943
|
+
return locations;
|
|
944
|
+
}
|
|
945
|
+
function localLocations(target, includeDeclaration) {
|
|
946
|
+
return [
|
|
947
|
+
...(includeDeclaration ? [locationOf(target.symbol)] : []),
|
|
948
|
+
...referencesFor(target.module, target.symbol),
|
|
949
|
+
];
|
|
950
|
+
}
|
|
951
|
+
function referencesFor(module, symbol) {
|
|
952
|
+
return module.result.semanticIndex.references
|
|
953
|
+
.filter((reference) => reference.symbolId === symbol.id)
|
|
954
|
+
.map((reference) => ({ path: module.inputPath, span: reference.span }));
|
|
955
|
+
}
|
|
956
|
+
function renameCollides(project, target, newName) {
|
|
957
|
+
if (scopeHasName(target.module.result.semanticIndex, target.symbol, newName))
|
|
958
|
+
return true;
|
|
959
|
+
if (target.kind !== "export")
|
|
960
|
+
return false;
|
|
961
|
+
for (const module of project.modules) {
|
|
962
|
+
for (const imported of module.result.semanticIndex.imports) {
|
|
963
|
+
if (imported.local !== imported.imported)
|
|
964
|
+
continue;
|
|
965
|
+
const importedTarget = exportedTarget(project, module, imported);
|
|
966
|
+
if (!importedTarget || importedTarget.symbol.id !== target.symbol.id)
|
|
967
|
+
continue;
|
|
968
|
+
const local = module.result.semanticIndex.symbols.find((symbol) => symbol.id === imported.localSymbolId);
|
|
969
|
+
if (local && scopeHasName(module.result.semanticIndex, local, newName))
|
|
970
|
+
return true;
|
|
971
|
+
}
|
|
972
|
+
}
|
|
973
|
+
return false;
|
|
974
|
+
}
|
|
975
|
+
function scopeHasName(index, target, newName) {
|
|
976
|
+
return index.symbols.some((symbol) => symbol.id !== target.id && symbol.scopeId === target.scopeId && symbol.name === newName);
|
|
977
|
+
}
|
|
978
|
+
function renameSelectionAt(index, offset, fallback) {
|
|
979
|
+
const imported = semanticImportAt(index, offset);
|
|
980
|
+
if (imported) {
|
|
981
|
+
if (contains(imported.localSpan, offset))
|
|
982
|
+
return imported.localSpan;
|
|
983
|
+
if (contains(imported.importedSpan, offset))
|
|
984
|
+
return imported.importedSpan;
|
|
985
|
+
}
|
|
986
|
+
const reference = index.references.find((item) => contains(item.span, offset));
|
|
987
|
+
return reference?.span ?? fallback.selectionSpan;
|
|
988
|
+
}
|
|
989
|
+
function importForSymbol(index, symbolId) {
|
|
990
|
+
return index.imports.find((item) => item.localSymbolId === symbolId) ?? null;
|
|
991
|
+
}
|
|
992
|
+
function moduleAt(project, path) {
|
|
993
|
+
const absolute = resolve(path);
|
|
994
|
+
return project.modules.find((module) => module.inputPath === absolute) ?? null;
|
|
995
|
+
}
|
|
996
|
+
function locationOf(symbol) {
|
|
997
|
+
return { path: symbol.path, span: symbol.selectionSpan };
|
|
998
|
+
}
|
|
999
|
+
function uniqueLocations(locations) {
|
|
1000
|
+
const seen = new Set();
|
|
1001
|
+
return locations.filter((location) => {
|
|
1002
|
+
const key = `${location.path}:${location.span.start}:${location.span.end}`;
|
|
1003
|
+
if (seen.has(key))
|
|
1004
|
+
return false;
|
|
1005
|
+
seen.add(key);
|
|
1006
|
+
return true;
|
|
1007
|
+
}).sort((left, right) => left.path.localeCompare(right.path) || left.span.start - right.span.start);
|
|
1008
|
+
}
|
|
1009
|
+
function uniqueTextEdits(edits) {
|
|
1010
|
+
const seen = new Set();
|
|
1011
|
+
return edits.filter((edit) => {
|
|
1012
|
+
const key = `${edit.path}:${edit.span.start}:${edit.span.end}:${edit.replacement ?? ""}`;
|
|
1013
|
+
if (seen.has(key))
|
|
1014
|
+
return false;
|
|
1015
|
+
seen.add(key);
|
|
1016
|
+
return true;
|
|
1017
|
+
}).sort((left, right) => left.path.localeCompare(right.path) || left.span.start - right.span.start);
|
|
1018
|
+
}
|
|
1019
|
+
function renameable(symbol) {
|
|
1020
|
+
return symbol.kind !== "field" && symbol.kind !== "method" && symbol.kind !== "enum-member";
|
|
1021
|
+
}
|
|
1022
|
+
function contains(span, offset) {
|
|
1023
|
+
return offset >= span.start && offset < span.end;
|
|
1024
|
+
}
|
|
1025
|
+
function wordSpanAt(source, offset) {
|
|
1026
|
+
let start = Math.min(Math.max(0, offset), source.length);
|
|
1027
|
+
if (start === source.length || !isSourceIdentifierPart(source[start]))
|
|
1028
|
+
start -= 1;
|
|
1029
|
+
if (start < 0 || !isSourceIdentifierPart(source[start]))
|
|
1030
|
+
return null;
|
|
1031
|
+
let end = start + 1;
|
|
1032
|
+
while (start > 0 && isSourceIdentifierPart(source[start - 1]))
|
|
1033
|
+
start -= 1;
|
|
1034
|
+
while (end < source.length && isSourceIdentifierPart(source[end]))
|
|
1035
|
+
end += 1;
|
|
1036
|
+
return { start, end };
|
|
1037
|
+
}
|
|
1038
|
+
function callAt(source, offset) {
|
|
1039
|
+
let depth = 0;
|
|
1040
|
+
let activeParameter = 0;
|
|
1041
|
+
let quote = null;
|
|
1042
|
+
for (let index = Math.min(offset, source.length) - 1; index >= 0; index -= 1) {
|
|
1043
|
+
const character = source[index];
|
|
1044
|
+
if (quote) {
|
|
1045
|
+
if (character === quote && source[index - 1] !== "\\")
|
|
1046
|
+
quote = null;
|
|
1047
|
+
continue;
|
|
1048
|
+
}
|
|
1049
|
+
if (character === "\"" || character === "'") {
|
|
1050
|
+
quote = character;
|
|
1051
|
+
continue;
|
|
1052
|
+
}
|
|
1053
|
+
if (character === ")" || character === "]" || character === "}") {
|
|
1054
|
+
depth += 1;
|
|
1055
|
+
continue;
|
|
1056
|
+
}
|
|
1057
|
+
if (character === "(" || character === "[" || character === "{") {
|
|
1058
|
+
if (depth > 0) {
|
|
1059
|
+
depth -= 1;
|
|
1060
|
+
continue;
|
|
1061
|
+
}
|
|
1062
|
+
if (character !== "(")
|
|
1063
|
+
return null;
|
|
1064
|
+
let end = index;
|
|
1065
|
+
while (end > 0 && /\s/u.test(source[end - 1]))
|
|
1066
|
+
end -= 1;
|
|
1067
|
+
let start = end;
|
|
1068
|
+
while (start > 0 && isSourceIdentifierPart(source[start - 1]))
|
|
1069
|
+
start -= 1;
|
|
1070
|
+
return start === end ? null : { calleeOffset: start, activeParameter };
|
|
1071
|
+
}
|
|
1072
|
+
if (character === "," && depth === 0)
|
|
1073
|
+
activeParameter += 1;
|
|
1074
|
+
if ((character === "\n" || character === "\r") && depth === 0)
|
|
1075
|
+
return null;
|
|
1076
|
+
}
|
|
1077
|
+
return null;
|
|
1078
|
+
}
|
|
1079
|
+
//# sourceMappingURL=project-semantic.js.map
|