@mrclrchtr/supi-code-intelligence 1.3.0 → 1.4.0
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 +70 -32
- package/node_modules/@mrclrchtr/supi-core/README.md +52 -41
- package/node_modules/@mrclrchtr/supi-core/package.json +1 -1
- package/node_modules/@mrclrchtr/supi-core/src/api.ts +13 -13
- package/node_modules/@mrclrchtr/supi-core/src/{config-settings.ts → config/config-settings.ts} +2 -2
- package/node_modules/@mrclrchtr/{supi-lsp/node_modules/@mrclrchtr/supi-core/src → supi-core/src/context}/context-provider-registry.ts +1 -1
- package/node_modules/@mrclrchtr/supi-core/src/extension.ts +1 -1
- package/node_modules/@mrclrchtr/supi-core/src/index.ts +13 -13
- package/node_modules/@mrclrchtr/{supi-lsp/node_modules/@mrclrchtr/supi-core/src → supi-core/src/settings}/settings-registry.ts +1 -1
- package/node_modules/@mrclrchtr/supi-lsp/README.md +58 -39
- package/node_modules/@mrclrchtr/supi-lsp/node_modules/@mrclrchtr/supi-core/README.md +52 -41
- package/node_modules/@mrclrchtr/supi-lsp/node_modules/@mrclrchtr/supi-core/package.json +1 -1
- package/node_modules/@mrclrchtr/supi-lsp/node_modules/@mrclrchtr/supi-core/src/api.ts +13 -13
- package/node_modules/@mrclrchtr/supi-lsp/node_modules/@mrclrchtr/supi-core/src/{config-settings.ts → config/config-settings.ts} +2 -2
- package/node_modules/@mrclrchtr/{supi-core/src → supi-lsp/node_modules/@mrclrchtr/supi-core/src/context}/context-provider-registry.ts +1 -1
- package/node_modules/@mrclrchtr/supi-lsp/node_modules/@mrclrchtr/supi-core/src/extension.ts +1 -1
- package/node_modules/@mrclrchtr/supi-lsp/node_modules/@mrclrchtr/supi-core/src/index.ts +13 -13
- package/node_modules/@mrclrchtr/{supi-core/src → supi-lsp/node_modules/@mrclrchtr/supi-core/src/settings}/settings-registry.ts +1 -1
- package/node_modules/@mrclrchtr/supi-lsp/package.json +5 -3
- package/node_modules/@mrclrchtr/supi-lsp/src/api.ts +16 -3
- package/node_modules/@mrclrchtr/supi-lsp/src/client/client-refresh.ts +1 -1
- package/node_modules/@mrclrchtr/supi-lsp/src/client/client.ts +27 -3
- package/node_modules/@mrclrchtr/supi-lsp/src/client/transport.ts +61 -5
- package/node_modules/@mrclrchtr/supi-lsp/src/config/tsconfig-scope.ts +244 -0
- package/node_modules/@mrclrchtr/supi-lsp/src/{types.ts → config/types.ts} +4 -2
- package/node_modules/@mrclrchtr/supi-lsp/src/coordinates.ts +11 -0
- package/node_modules/@mrclrchtr/supi-lsp/src/diagnostics/diagnostic-augmentation.ts +5 -5
- package/node_modules/@mrclrchtr/supi-lsp/src/diagnostics/diagnostic-context.ts +115 -0
- package/node_modules/@mrclrchtr/supi-lsp/src/diagnostics/diagnostic-display.ts +1 -1
- package/node_modules/@mrclrchtr/supi-lsp/src/diagnostics/diagnostic-summary.ts +3 -2
- package/node_modules/@mrclrchtr/supi-lsp/src/diagnostics/diagnostics.ts +1 -1
- package/node_modules/@mrclrchtr/supi-lsp/src/diagnostics/stale-diagnostics.ts +1 -1
- package/node_modules/@mrclrchtr/supi-lsp/src/diagnostics/suppression-diagnostics.ts +1 -1
- package/node_modules/@mrclrchtr/supi-lsp/src/{workspace-sentinels.ts → diagnostics/workspace-sentinels.ts} +2 -2
- package/node_modules/@mrclrchtr/supi-lsp/src/format.ts +2 -23
- package/node_modules/@mrclrchtr/supi-lsp/src/index.ts +18 -5
- package/node_modules/@mrclrchtr/supi-lsp/src/lsp.ts +72 -120
- package/node_modules/@mrclrchtr/supi-lsp/src/manager/manager-diagnostics.ts +1 -1
- package/node_modules/@mrclrchtr/supi-lsp/src/manager/manager-helpers.ts +4 -2
- package/node_modules/@mrclrchtr/supi-lsp/src/manager/manager-project-info.ts +10 -7
- package/node_modules/@mrclrchtr/supi-lsp/src/manager/manager-workspace-recovery.ts +1 -1
- package/node_modules/@mrclrchtr/supi-lsp/src/manager/manager-workspace-symbol.ts +158 -6
- package/node_modules/@mrclrchtr/supi-lsp/src/manager/manager.ts +202 -43
- package/node_modules/@mrclrchtr/supi-lsp/src/{lsp-state.ts → session/lsp-state.ts} +22 -11
- package/node_modules/@mrclrchtr/supi-lsp/src/{scanner.ts → session/scanner.ts} +3 -3
- package/node_modules/@mrclrchtr/supi-lsp/src/{service-registry.ts → session/service-registry.ts} +104 -12
- package/node_modules/@mrclrchtr/supi-lsp/src/{settings-registration.ts → session/settings-registration.ts} +1 -1
- package/node_modules/@mrclrchtr/supi-lsp/src/session/tree-persist.ts +75 -0
- package/node_modules/@mrclrchtr/supi-lsp/src/summary.ts +1 -1
- package/node_modules/@mrclrchtr/supi-lsp/src/tool/guidance.ts +138 -0
- package/node_modules/@mrclrchtr/supi-lsp/src/tool/names.ts +19 -0
- package/node_modules/@mrclrchtr/supi-lsp/src/{overrides.ts → tool/overrides.ts} +55 -24
- package/node_modules/@mrclrchtr/supi-lsp/src/tool/register-tools.ts +224 -0
- package/node_modules/@mrclrchtr/supi-lsp/src/tool/service-actions.ts +258 -0
- package/node_modules/@mrclrchtr/supi-lsp/src/{ui.ts → ui/ui.ts} +4 -4
- package/node_modules/@mrclrchtr/supi-lsp/src/utils.ts +11 -0
- package/node_modules/@mrclrchtr/supi-tree-sitter/README.md +46 -39
- package/node_modules/@mrclrchtr/supi-tree-sitter/package.json +1 -1
- package/node_modules/@mrclrchtr/supi-tree-sitter/src/api.ts +1 -1
- package/node_modules/@mrclrchtr/supi-tree-sitter/src/index.ts +1 -1
- package/node_modules/@mrclrchtr/supi-tree-sitter/src/{runtime.ts → session/runtime.ts} +3 -3
- package/node_modules/@mrclrchtr/supi-tree-sitter/src/{session.ts → session/session.ts} +4 -4
- package/node_modules/@mrclrchtr/supi-tree-sitter/src/{callees.ts → tool/callees.ts} +3 -3
- package/node_modules/@mrclrchtr/supi-tree-sitter/src/{exports.ts → tool/exports.ts} +4 -4
- package/node_modules/@mrclrchtr/supi-tree-sitter/src/{formatting.ts → tool/formatting.ts} +1 -1
- package/node_modules/@mrclrchtr/supi-tree-sitter/src/tool/guidance.ts +22 -0
- package/node_modules/@mrclrchtr/supi-tree-sitter/src/{imports.ts → tool/imports.ts} +4 -4
- package/node_modules/@mrclrchtr/supi-tree-sitter/src/{node-at.ts → tool/node-at.ts} +3 -3
- package/node_modules/@mrclrchtr/supi-tree-sitter/src/{outline.ts → tool/outline.ts} +3 -3
- package/node_modules/@mrclrchtr/supi-tree-sitter/src/tree-sitter.ts +6 -29
- package/package.json +8 -5
- package/src/actions/affected-action.ts +4 -4
- package/src/actions/brief-action.ts +12 -13
- package/src/actions/callees-action.ts +14 -10
- package/src/actions/callers-action.ts +4 -4
- package/src/actions/implementations-action.ts +4 -4
- package/src/code-intelligence.ts +1 -1
- package/src/pattern-structured.ts +20 -22
- package/src/providers/semantic-provider.ts +34 -0
- package/src/providers/structural-provider.ts +14 -0
- package/src/target-resolution.ts +26 -35
- package/src/tool/guidance.ts +21 -0
- package/node_modules/@mrclrchtr/supi-lsp/src/guidance.ts +0 -163
- package/node_modules/@mrclrchtr/supi-lsp/src/search-fallback.ts +0 -98
- package/node_modules/@mrclrchtr/supi-lsp/src/tool-actions.ts +0 -430
- package/node_modules/@mrclrchtr/supi-lsp/src/tree-persist.ts +0 -48
- package/node_modules/@mrclrchtr/supi-lsp/src/tsconfig-scope.ts +0 -156
- package/src/guidance.ts +0 -42
- /package/node_modules/@mrclrchtr/supi-core/src/{config.ts → config/config.ts} +0 -0
- /package/node_modules/@mrclrchtr/supi-core/src/{context-messages.ts → context/context-messages.ts} +0 -0
- /package/node_modules/@mrclrchtr/supi-core/src/{context-tag.ts → context/context-tag.ts} +0 -0
- /package/node_modules/@mrclrchtr/supi-core/src/{settings-command.ts → settings/settings-command.ts} +0 -0
- /package/node_modules/@mrclrchtr/supi-core/src/{settings-ui.ts → settings/settings-ui.ts} +0 -0
- /package/node_modules/@mrclrchtr/supi-lsp/node_modules/@mrclrchtr/supi-core/src/{config.ts → config/config.ts} +0 -0
- /package/node_modules/@mrclrchtr/supi-lsp/node_modules/@mrclrchtr/supi-core/src/{context-messages.ts → context/context-messages.ts} +0 -0
- /package/node_modules/@mrclrchtr/supi-lsp/node_modules/@mrclrchtr/supi-core/src/{context-tag.ts → context/context-tag.ts} +0 -0
- /package/node_modules/@mrclrchtr/supi-lsp/node_modules/@mrclrchtr/supi-core/src/{settings-command.ts → settings/settings-command.ts} +0 -0
- /package/node_modules/@mrclrchtr/supi-lsp/node_modules/@mrclrchtr/supi-core/src/{settings-ui.ts → settings/settings-ui.ts} +0 -0
- /package/node_modules/@mrclrchtr/supi-lsp/src/{capabilities.ts → config/capabilities.ts} +0 -0
- /package/node_modules/@mrclrchtr/supi-lsp/src/{config.ts → config/config.ts} +0 -0
- /package/node_modules/@mrclrchtr/supi-lsp/src/{defaults.json → config/defaults.json} +0 -0
- /package/node_modules/@mrclrchtr/supi-lsp/src/{renderer.ts → ui/renderer.ts} +0 -0
- /package/node_modules/@mrclrchtr/supi-tree-sitter/src/{structure.ts → tool/structure.ts} +0 -0
|
@@ -0,0 +1,244 @@
|
|
|
1
|
+
// tsconfig-aware file scope detection.
|
|
2
|
+
//
|
|
3
|
+
// Determines whether a file is within the compilation scope of its nearest
|
|
4
|
+
// tsconfig.json or jsconfig.json using the TypeScript compiler's own config
|
|
5
|
+
// parsing APIs. Used by the diagnostic filter to suppress LSP errors on files
|
|
6
|
+
// that TypeScript itself would not include in the project.
|
|
7
|
+
|
|
8
|
+
import * as path from "node:path";
|
|
9
|
+
import ts from "typescript";
|
|
10
|
+
|
|
11
|
+
interface ParsedProjectConfig {
|
|
12
|
+
configPath: string;
|
|
13
|
+
configDir: string;
|
|
14
|
+
fileNames: Set<string>;
|
|
15
|
+
explicitFiles: Set<string> | null;
|
|
16
|
+
includeFilePattern: RegExp | null;
|
|
17
|
+
excludePattern: RegExp | null;
|
|
18
|
+
supportedExtensions: Set<string>;
|
|
19
|
+
usesDefaultInclude: boolean;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const nearestConfigCache = new Map<string, string | null>();
|
|
23
|
+
const parsedConfigCache = new Map<string, ParsedProjectConfig | null>();
|
|
24
|
+
|
|
25
|
+
const tsInternal = ts as typeof ts & {
|
|
26
|
+
getFileMatcherPatterns?: (
|
|
27
|
+
configDir: string,
|
|
28
|
+
excludes: readonly string[] | undefined,
|
|
29
|
+
includes: readonly string[] | undefined,
|
|
30
|
+
useCaseSensitiveFileNames: boolean,
|
|
31
|
+
currentDirectory: string,
|
|
32
|
+
) => {
|
|
33
|
+
includeFilePattern?: string;
|
|
34
|
+
excludePattern?: string;
|
|
35
|
+
};
|
|
36
|
+
getSupportedExtensions?: (
|
|
37
|
+
options: ts.CompilerOptions,
|
|
38
|
+
extraFileExtensions?: unknown,
|
|
39
|
+
) => ReadonlyArray<ReadonlyArray<string>>;
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Check whether a file is excluded by its nearest tsconfig.json or jsconfig.json.
|
|
44
|
+
*
|
|
45
|
+
* @param filePath - Project-relative file path (e.g., "packages/foo/__tests__/x.test.ts")
|
|
46
|
+
* @param cwd - Absolute project root directory
|
|
47
|
+
* @returns `true` if the file is excluded from compilation scope
|
|
48
|
+
*/
|
|
49
|
+
export function isFileExcludedByTsconfig(filePath: string, cwd: string): boolean {
|
|
50
|
+
const absolutePath = path.resolve(cwd, filePath);
|
|
51
|
+
const configPath = findNearestProjectConfig(path.dirname(absolutePath), cwd);
|
|
52
|
+
if (!configPath) return false;
|
|
53
|
+
|
|
54
|
+
const parsed = parseProjectConfig(configPath);
|
|
55
|
+
if (!parsed) return false;
|
|
56
|
+
|
|
57
|
+
return !isFileInProjectScope(parsed, absolutePath);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Find the nearest tsconfig.json or jsconfig.json walking upward from `startDir`,
|
|
62
|
+
* stopping at `rootDir`.
|
|
63
|
+
*/
|
|
64
|
+
function findNearestProjectConfig(startDir: string, rootDir: string): string | null {
|
|
65
|
+
let dir = path.resolve(startDir);
|
|
66
|
+
const resolvedRoot = path.resolve(rootDir);
|
|
67
|
+
|
|
68
|
+
while (true) {
|
|
69
|
+
const cacheKey = `${normalizePath(dir)}::${normalizePath(resolvedRoot)}`;
|
|
70
|
+
const cached = nearestConfigCache.get(cacheKey);
|
|
71
|
+
if (cached !== undefined) return cached;
|
|
72
|
+
|
|
73
|
+
const configPath = getLocalProjectConfig(dir);
|
|
74
|
+
if (configPath) {
|
|
75
|
+
const resolvedConfigPath = path.resolve(configPath);
|
|
76
|
+
nearestConfigCache.set(cacheKey, resolvedConfigPath);
|
|
77
|
+
return resolvedConfigPath;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
if (path.relative(resolvedRoot, dir).startsWith("..") || dir === resolvedRoot) {
|
|
81
|
+
nearestConfigCache.set(cacheKey, null);
|
|
82
|
+
return null;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
const parent = path.dirname(dir);
|
|
86
|
+
if (parent === dir) {
|
|
87
|
+
nearestConfigCache.set(cacheKey, null);
|
|
88
|
+
return null;
|
|
89
|
+
}
|
|
90
|
+
dir = parent;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
function getLocalProjectConfig(directory: string): string | null {
|
|
95
|
+
const tsconfigPath = path.join(directory, "tsconfig.json");
|
|
96
|
+
if (ts.sys.fileExists(tsconfigPath)) return tsconfigPath;
|
|
97
|
+
|
|
98
|
+
const jsconfigPath = path.join(directory, "jsconfig.json");
|
|
99
|
+
if (ts.sys.fileExists(jsconfigPath)) return jsconfigPath;
|
|
100
|
+
|
|
101
|
+
return null;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
function parseProjectConfig(configPath: string): ParsedProjectConfig | null {
|
|
105
|
+
const normalizedConfigPath = normalizePath(configPath);
|
|
106
|
+
const cached = parsedConfigCache.get(normalizedConfigPath);
|
|
107
|
+
if (cached !== undefined) return cached;
|
|
108
|
+
|
|
109
|
+
const parsed = ts.getParsedCommandLineOfConfigFile(configPath, {}, createParseConfigHost());
|
|
110
|
+
if (!parsed) {
|
|
111
|
+
parsedConfigCache.set(normalizedConfigPath, null);
|
|
112
|
+
return null;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
const configDir = path.dirname(path.resolve(configPath));
|
|
116
|
+
const explicitFiles = extractExplicitFiles(parsed.raw.files, configDir);
|
|
117
|
+
const usesDefaultInclude = explicitFiles === null && !Array.isArray(parsed.raw.include);
|
|
118
|
+
const { includeFilePattern, excludePattern } = createFileMatchers(
|
|
119
|
+
configDir,
|
|
120
|
+
parsed.raw.include,
|
|
121
|
+
parsed.raw.exclude,
|
|
122
|
+
usesDefaultInclude,
|
|
123
|
+
);
|
|
124
|
+
const supportedExtensions = new Set(getSupportedExtensions(parsed.options));
|
|
125
|
+
if (parsed.options.resolveJsonModule) supportedExtensions.add(".json");
|
|
126
|
+
|
|
127
|
+
const result = {
|
|
128
|
+
configPath: path.resolve(configPath),
|
|
129
|
+
configDir,
|
|
130
|
+
fileNames: new Set(parsed.fileNames.map(normalizePath)),
|
|
131
|
+
explicitFiles,
|
|
132
|
+
includeFilePattern,
|
|
133
|
+
excludePattern,
|
|
134
|
+
supportedExtensions,
|
|
135
|
+
usesDefaultInclude,
|
|
136
|
+
} satisfies ParsedProjectConfig;
|
|
137
|
+
parsedConfigCache.set(normalizedConfigPath, result);
|
|
138
|
+
return result;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
function extractExplicitFiles(rawFiles: unknown, configDir: string): Set<string> | null {
|
|
142
|
+
if (!Array.isArray(rawFiles)) return null;
|
|
143
|
+
return new Set(
|
|
144
|
+
rawFiles
|
|
145
|
+
.filter((entry): entry is string => typeof entry === "string")
|
|
146
|
+
.map((entry) => normalizePath(path.resolve(configDir, entry))),
|
|
147
|
+
);
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
function createFileMatchers(
|
|
151
|
+
configDir: string,
|
|
152
|
+
rawInclude: unknown,
|
|
153
|
+
rawExclude: unknown,
|
|
154
|
+
useDefaultInclude: boolean,
|
|
155
|
+
): {
|
|
156
|
+
includeFilePattern: RegExp | null;
|
|
157
|
+
excludePattern: RegExp | null;
|
|
158
|
+
} {
|
|
159
|
+
const includeSpecs = Array.isArray(rawInclude)
|
|
160
|
+
? rawInclude.filter((entry): entry is string => typeof entry === "string")
|
|
161
|
+
: undefined;
|
|
162
|
+
const excludeSpecs = Array.isArray(rawExclude)
|
|
163
|
+
? rawExclude.filter((entry): entry is string => typeof entry === "string")
|
|
164
|
+
: undefined;
|
|
165
|
+
const matcherPatterns = getFileMatcherPatterns(
|
|
166
|
+
configDir,
|
|
167
|
+
excludeSpecs,
|
|
168
|
+
useDefaultInclude ? ["**/*"] : includeSpecs,
|
|
169
|
+
);
|
|
170
|
+
|
|
171
|
+
return {
|
|
172
|
+
includeFilePattern: matcherPatterns.includeFilePattern
|
|
173
|
+
? new RegExp(matcherPatterns.includeFilePattern)
|
|
174
|
+
: null,
|
|
175
|
+
excludePattern: matcherPatterns.excludePattern
|
|
176
|
+
? new RegExp(matcherPatterns.excludePattern)
|
|
177
|
+
: null,
|
|
178
|
+
};
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
function isFileInProjectScope(parsed: ParsedProjectConfig, absolutePath: string): boolean {
|
|
182
|
+
const normalizedPath = normalizePath(absolutePath);
|
|
183
|
+
if (parsed.fileNames.has(normalizedPath)) return true;
|
|
184
|
+
|
|
185
|
+
const extension = path.extname(absolutePath).toLowerCase();
|
|
186
|
+
if (!parsed.supportedExtensions.has(extension)) return false;
|
|
187
|
+
|
|
188
|
+
if (parsed.explicitFiles) return parsed.explicitFiles.has(normalizedPath);
|
|
189
|
+
if (!isWithinOrEqual(parsed.configDir, absolutePath)) return false;
|
|
190
|
+
if (parsed.excludePattern?.test(normalizedPath)) return false;
|
|
191
|
+
if (parsed.usesDefaultInclude) return true;
|
|
192
|
+
return parsed.includeFilePattern ? parsed.includeFilePattern.test(normalizedPath) : false;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
function getSupportedExtensions(options: ts.CompilerOptions): string[] {
|
|
196
|
+
return tsInternal.getSupportedExtensions
|
|
197
|
+
? [...tsInternal.getSupportedExtensions(options, undefined).flat()]
|
|
198
|
+
: [".ts", ".tsx", ".d.ts", ".cts", ".d.cts", ".mts", ".d.mts"];
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
function getFileMatcherPatterns(
|
|
202
|
+
configDir: string,
|
|
203
|
+
excludeSpecs: readonly string[] | undefined,
|
|
204
|
+
includeSpecs: readonly string[] | undefined,
|
|
205
|
+
): { includeFilePattern?: string; excludePattern?: string } {
|
|
206
|
+
return tsInternal.getFileMatcherPatterns
|
|
207
|
+
? tsInternal.getFileMatcherPatterns(
|
|
208
|
+
configDir,
|
|
209
|
+
excludeSpecs,
|
|
210
|
+
includeSpecs,
|
|
211
|
+
ts.sys.useCaseSensitiveFileNames,
|
|
212
|
+
path.parse(configDir).root,
|
|
213
|
+
)
|
|
214
|
+
: {};
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
function createParseConfigHost(): ts.ParseConfigFileHost {
|
|
218
|
+
return {
|
|
219
|
+
...ts.sys,
|
|
220
|
+
onUnRecoverableConfigFileDiagnostic: () => {
|
|
221
|
+
// Treat invalid configs as unsupported rather than surfacing a secondary
|
|
222
|
+
// filter failure to the agent.
|
|
223
|
+
},
|
|
224
|
+
};
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
function isWithinOrEqual(root: string, target: string): boolean {
|
|
228
|
+
const relative = path.relative(root, target);
|
|
229
|
+
return relative === "" || (!relative.startsWith(`..${path.sep}`) && relative !== "..");
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
function normalizePath(target: string): string {
|
|
233
|
+
const resolved = path.resolve(target).replaceAll("\\", "/");
|
|
234
|
+
return ts.sys.useCaseSensitiveFileNames ? resolved : resolved.toLowerCase();
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
/**
|
|
238
|
+
* Clear cached nearest-config lookups and parsed project config state.
|
|
239
|
+
* Useful for testing and after workspace file changes.
|
|
240
|
+
*/
|
|
241
|
+
export function clearTsconfigCache(): void {
|
|
242
|
+
nearestConfigCache.clear();
|
|
243
|
+
parsedConfigCache.clear();
|
|
244
|
+
}
|
|
@@ -348,16 +348,18 @@ export interface TextDocumentPositionParams {
|
|
|
348
348
|
|
|
349
349
|
// ── JSON-RPC ──────────────────────────────────────────────────────────
|
|
350
350
|
|
|
351
|
+
export type JsonRpcId = number | string;
|
|
352
|
+
|
|
351
353
|
export interface JsonRpcRequest {
|
|
352
354
|
jsonrpc: "2.0";
|
|
353
|
-
id:
|
|
355
|
+
id: JsonRpcId;
|
|
354
356
|
method: string;
|
|
355
357
|
params?: unknown;
|
|
356
358
|
}
|
|
357
359
|
|
|
358
360
|
export interface JsonRpcResponse {
|
|
359
361
|
jsonrpc: "2.0";
|
|
360
|
-
id:
|
|
362
|
+
id: JsonRpcId | null;
|
|
361
363
|
result?: unknown;
|
|
362
364
|
error?: { code: number; message: string; data?: unknown };
|
|
363
365
|
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { Position } from "./config/types.ts";
|
|
2
|
+
|
|
3
|
+
/** Convert public 1-based coordinates into a 0-based LSP position. */
|
|
4
|
+
export function toLspPosition(line: number, character: number): Position {
|
|
5
|
+
return { line: line - 1, character: character - 1 };
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
/** Convert a 0-based LSP position into 1-based display coordinates. */
|
|
9
|
+
export function toOneBasedPosition(position: Position): { line: number; character: number } {
|
|
10
|
+
return { line: position.line + 1, character: position.character + 1 };
|
|
11
|
+
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import
|
|
1
|
+
import type { Diagnostic, Hover, MarkedString, MarkupContent } from "../config/types.ts";
|
|
2
2
|
import type { LspManager } from "../manager/manager.ts";
|
|
3
|
-
import
|
|
3
|
+
import { resolveSessionPath } from "../utils.ts";
|
|
4
4
|
|
|
5
5
|
const AUGMENT_TIMEOUT_MS = 500;
|
|
6
6
|
|
|
@@ -12,13 +12,13 @@ export async function augmentDiagnostics(
|
|
|
12
12
|
filePath: string,
|
|
13
13
|
diags: Diagnostic[],
|
|
14
14
|
manager: LspManager,
|
|
15
|
-
|
|
15
|
+
cwd: string,
|
|
16
16
|
): Promise<string | null> {
|
|
17
17
|
const firstError = diags.find((d) => d.severity === 1);
|
|
18
18
|
if (!firstError) return null;
|
|
19
19
|
|
|
20
|
-
const resolvedPath =
|
|
21
|
-
const client = await manager.getClientForFile(
|
|
20
|
+
const resolvedPath = resolveSessionPath(cwd, filePath);
|
|
21
|
+
const client = await manager.getClientForFile(resolvedPath);
|
|
22
22
|
if (!client) return null;
|
|
23
23
|
|
|
24
24
|
const pos = firstError.range.start;
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
// Diagnostic context formatting for the LSP extension.
|
|
2
|
+
// Extracted from guidance.ts to keep prompt surfaces separate from formatting logic.
|
|
3
|
+
|
|
4
|
+
import type { Diagnostic } from "../config/types.ts";
|
|
5
|
+
import type { OutstandingDiagnosticSummaryEntry } from "../manager/manager-types.ts";
|
|
6
|
+
import { splitSuppressionDiagnostics } from "./suppression-diagnostics.ts";
|
|
7
|
+
|
|
8
|
+
export const MAX_DETAILED_DIAGNOSTICS = 5;
|
|
9
|
+
const MAX_DETAIL_LINES_PER_FILE = 3;
|
|
10
|
+
|
|
11
|
+
interface DetailedDiagnostics {
|
|
12
|
+
file: string;
|
|
13
|
+
diagnostics: Diagnostic[];
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export function formatDiagnosticsContext(
|
|
17
|
+
diagnostics: OutstandingDiagnosticSummaryEntry[],
|
|
18
|
+
maxFiles: number = 3,
|
|
19
|
+
detailed?: DetailedDiagnostics[],
|
|
20
|
+
staleWarning?: string | null,
|
|
21
|
+
): string | null {
|
|
22
|
+
if (diagnostics.length === 0) return null;
|
|
23
|
+
|
|
24
|
+
const totalDiags = diagnostics.reduce((sum, d) => sum + d.total, 0);
|
|
25
|
+
const detailMap = buildDetailMap(totalDiags, detailed);
|
|
26
|
+
|
|
27
|
+
const lines: string[] = [];
|
|
28
|
+
if (staleWarning) lines.push(staleWarning);
|
|
29
|
+
const visible = diagnostics.slice(0, maxFiles);
|
|
30
|
+
|
|
31
|
+
for (const entry of visible) {
|
|
32
|
+
lines.push(`- ${entry.file}: ${formatCounts(entry)}`);
|
|
33
|
+
appendDetailLines(lines, detailMap?.get(entry.file));
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const remaining = diagnostics.length - visible.length;
|
|
37
|
+
if (remaining > 0) {
|
|
38
|
+
lines.push(`- +${remaining} more file${remaining === 1 ? "" : "s"}`);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
appendSuppressionCleanup(
|
|
42
|
+
lines,
|
|
43
|
+
visible.map((entry) => entry.file),
|
|
44
|
+
detailMap,
|
|
45
|
+
);
|
|
46
|
+
|
|
47
|
+
return [
|
|
48
|
+
'<extension-context source="supi-lsp">',
|
|
49
|
+
"Outstanding diagnostics — fix these before proceeding:",
|
|
50
|
+
...lines,
|
|
51
|
+
"</extension-context>",
|
|
52
|
+
].join("\n");
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function buildDetailMap(
|
|
56
|
+
totalDiags: number,
|
|
57
|
+
detailed?: DetailedDiagnostics[],
|
|
58
|
+
): Map<string, Diagnostic[]> | null {
|
|
59
|
+
if (totalDiags > MAX_DETAILED_DIAGNOSTICS || !detailed || detailed.length === 0) return null;
|
|
60
|
+
return new Map(detailed.map((d) => [d.file, d.diagnostics]));
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function appendDetailLines(lines: string[], details?: Diagnostic[]): void {
|
|
64
|
+
if (!details) return;
|
|
65
|
+
for (const d of details.slice(0, MAX_DETAIL_LINES_PER_FILE)) {
|
|
66
|
+
const line = d.range.start.line + 1;
|
|
67
|
+
const char = d.range.start.character + 1;
|
|
68
|
+
const source = d.source ? ` ${d.source}` : "";
|
|
69
|
+
lines.push(` L${line} C${char}${source}: ${d.message}`);
|
|
70
|
+
}
|
|
71
|
+
if (details.length > MAX_DETAIL_LINES_PER_FILE) {
|
|
72
|
+
const extra = details.length - MAX_DETAIL_LINES_PER_FILE;
|
|
73
|
+
lines.push(` +${extra} more`);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function appendSuppressionCleanup(
|
|
78
|
+
lines: string[],
|
|
79
|
+
visibleFiles: string[],
|
|
80
|
+
detailMap: Map<string, Diagnostic[]> | null,
|
|
81
|
+
): void {
|
|
82
|
+
if (!detailMap) return;
|
|
83
|
+
|
|
84
|
+
const suppressionLines: string[] = [];
|
|
85
|
+
for (const file of visibleFiles) {
|
|
86
|
+
const diagnostics = detailMap.get(file);
|
|
87
|
+
if (!diagnostics) continue;
|
|
88
|
+
|
|
89
|
+
const { suppressions } = splitSuppressionDiagnostics(diagnostics, 1);
|
|
90
|
+
if (suppressions.length === 0) continue;
|
|
91
|
+
|
|
92
|
+
suppressionLines.push(`- ${file}`);
|
|
93
|
+
appendDetailLines(suppressionLines, suppressions);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
if (suppressionLines.length === 0) return;
|
|
97
|
+
lines.push("", "Stale suppression comments — clean these up:", ...suppressionLines);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
export function diagnosticsContextFingerprint(content: string | null): string | null {
|
|
101
|
+
return content;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
function formatCounts(entry: OutstandingDiagnosticSummaryEntry): string {
|
|
105
|
+
const counts: string[] = [];
|
|
106
|
+
if (entry.errors > 0) counts.push(pluralize(entry.errors, "error"));
|
|
107
|
+
if (entry.warnings > 0) counts.push(pluralize(entry.warnings, "warning"));
|
|
108
|
+
if (entry.information > 0) counts.push(pluralize(entry.information, "info"));
|
|
109
|
+
if (entry.hints > 0) counts.push(pluralize(entry.hints, "hint"));
|
|
110
|
+
return counts.join(", ");
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
function pluralize(count: number, word: string): string {
|
|
114
|
+
return `${count} ${word}${count === 1 ? "" : "s"}`;
|
|
115
|
+
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
+
import type { Diagnostic } from "../config/types.ts";
|
|
1
2
|
import type { OutstandingDiagnosticSummaryEntry } from "../manager/manager-types.ts";
|
|
2
|
-
import type { Diagnostic } from "../types.ts";
|
|
3
3
|
|
|
4
4
|
export function formatDiagnosticsDisplayContent(
|
|
5
5
|
diagnostics: OutstandingDiagnosticSummaryEntry[],
|
|
@@ -1,7 +1,8 @@
|
|
|
1
|
+
import { type Diagnostic, DiagnosticSeverity } from "../config/types.ts";
|
|
1
2
|
import type { OutstandingDiagnosticSummaryEntry } from "../manager/manager-types.ts";
|
|
2
3
|
import { isGlobMatch } from "../pattern-matcher.ts";
|
|
3
4
|
import { displayRelativeFilePath, shouldIgnoreLspPath } from "../summary.ts";
|
|
4
|
-
import {
|
|
5
|
+
import { uriToFile } from "../utils.ts";
|
|
5
6
|
|
|
6
7
|
export function collectDiagnosticSummaryCounts(
|
|
7
8
|
fileDiags: Map<string, { errors: number; warnings: number }>,
|
|
@@ -52,7 +53,7 @@ export function accumulateOutstandingDiagnostics(
|
|
|
52
53
|
}
|
|
53
54
|
|
|
54
55
|
export function relativeFilePathFromUri(uri: string, cwd: string): string {
|
|
55
|
-
return displayRelativeFilePath(uri
|
|
56
|
+
return displayRelativeFilePath(uriToFile(uri), cwd);
|
|
56
57
|
}
|
|
57
58
|
|
|
58
59
|
function isDiagnosticWithinThreshold(
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// Diagnostic formatting and severity utilities.
|
|
2
2
|
|
|
3
3
|
import * as path from "node:path";
|
|
4
|
-
import type { Diagnostic } from "../types.ts";
|
|
4
|
+
import type { Diagnostic } from "../config/types.ts";
|
|
5
5
|
|
|
6
6
|
/** Map severity number to label. */
|
|
7
7
|
export function severityLabel(severity: number | undefined): string {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import * as fs from "node:fs";
|
|
2
2
|
import * as path from "node:path";
|
|
3
|
-
import { FileChangeType, type FileEvent } from "
|
|
4
|
-
import { fileToUri } from "
|
|
3
|
+
import { FileChangeType, type FileEvent } from "../config/types.ts";
|
|
4
|
+
import { fileToUri } from "../utils.ts";
|
|
5
5
|
|
|
6
6
|
const IGNORED_DIRECTORIES = new Set(["node_modules", ".pnpm", ".git", "dist", "coverage"]);
|
|
7
7
|
const ROOT_LOCKFILES = ["pnpm-lock.yaml", "package-lock.json", "yarn.lock", "bun.lockb"];
|
|
@@ -1,8 +1,6 @@
|
|
|
1
1
|
// LSP result formatting — converts LSP response types into readable text.
|
|
2
2
|
|
|
3
3
|
import * as path from "node:path";
|
|
4
|
-
import type { GrepMatch } from "./search-fallback.ts";
|
|
5
|
-
import { isProjectSource } from "./summary.ts";
|
|
6
4
|
import type {
|
|
7
5
|
CodeAction,
|
|
8
6
|
DocumentSymbol,
|
|
@@ -13,7 +11,8 @@ import type {
|
|
|
13
11
|
MarkupContent,
|
|
14
12
|
SymbolInformation,
|
|
15
13
|
WorkspaceEdit,
|
|
16
|
-
} from "./types.ts";
|
|
14
|
+
} from "./config/types.ts";
|
|
15
|
+
import { isProjectSource } from "./summary.ts";
|
|
17
16
|
import { uriToFile } from "./utils.ts";
|
|
18
17
|
|
|
19
18
|
// ── Hover ─────────────────────────────────────────────────────────────
|
|
@@ -297,26 +296,6 @@ export function formatWorkspaceSymbols(symbols: SymbolInformation[], cwd: string
|
|
|
297
296
|
return lines.join("\n");
|
|
298
297
|
}
|
|
299
298
|
|
|
300
|
-
// ── Search Results ────────────────────────────────────────────────────
|
|
301
|
-
|
|
302
|
-
export function formatSearchResults(
|
|
303
|
-
lspSymbols: SymbolInformation[] | null,
|
|
304
|
-
grepMatches: GrepMatch[] | null,
|
|
305
|
-
cwd: string,
|
|
306
|
-
): string {
|
|
307
|
-
if (lspSymbols && lspSymbols.length > 0) {
|
|
308
|
-
return formatWorkspaceSymbols(lspSymbols, cwd);
|
|
309
|
-
}
|
|
310
|
-
if (grepMatches && grepMatches.length > 0) {
|
|
311
|
-
const lines = [`Text search results (${grepMatches.length}):\n`];
|
|
312
|
-
for (const match of grepMatches) {
|
|
313
|
-
lines.push(`- ${match.file}:${match.line}: ${match.text}`);
|
|
314
|
-
}
|
|
315
|
-
return lines.join("\n");
|
|
316
|
-
}
|
|
317
|
-
return "No symbols or text matches found.";
|
|
318
|
-
}
|
|
319
|
-
|
|
320
299
|
// ── Symbol Kind Names ─────────────────────────────────────────────────
|
|
321
300
|
|
|
322
301
|
const SYMBOL_KIND_NAMES: Record<number, string> = {
|
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
// Public library entrypoint for @mrclrchtr/supi-lsp/api.
|
|
2
|
-
//
|
|
3
|
-
//
|
|
2
|
+
// Consumers should import the published API surface from
|
|
3
|
+
// `@mrclrchtr/supi-lsp/api`, not the package root.
|
|
4
4
|
|
|
5
|
-
export type { SessionLspServiceState } from "./service-registry.ts";
|
|
6
|
-
export { getSessionLspService, SessionLspService } from "./service-registry.ts";
|
|
7
5
|
export type {
|
|
6
|
+
CodeAction,
|
|
8
7
|
Diagnostic,
|
|
9
8
|
DocumentSymbol,
|
|
10
9
|
Hover,
|
|
@@ -12,6 +11,20 @@ export type {
|
|
|
12
11
|
LocationLink,
|
|
13
12
|
Position,
|
|
14
13
|
ProjectServerInfo,
|
|
14
|
+
Range,
|
|
15
15
|
SymbolInformation,
|
|
16
|
+
WorkspaceEdit,
|
|
16
17
|
WorkspaceSymbol,
|
|
17
|
-
} from "./types.ts";
|
|
18
|
+
} from "./config/types.ts";
|
|
19
|
+
export { toLspPosition, toOneBasedPosition } from "./coordinates.ts";
|
|
20
|
+
export type {
|
|
21
|
+
OutstandingDiagnosticSummaryEntry,
|
|
22
|
+
RecoverDiagnosticsResult,
|
|
23
|
+
SessionLspServiceState,
|
|
24
|
+
WorkspaceDiagnosticSummaryEntry,
|
|
25
|
+
} from "./session/service-registry.ts";
|
|
26
|
+
export {
|
|
27
|
+
getSessionLspService,
|
|
28
|
+
SessionLspService,
|
|
29
|
+
waitForSessionLspService,
|
|
30
|
+
} from "./session/service-registry.ts";
|