@jafreck/lore 0.3.7 → 0.3.8
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 +23 -122
- package/dist/cli.d.ts +2 -1
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +63 -29
- package/dist/cli.js.map +1 -1
- package/dist/db/read-only.d.ts +22 -43
- package/dist/db/read-only.d.ts.map +1 -1
- package/dist/db/read-only.js +80 -156
- package/dist/db/read-only.js.map +1 -1
- package/dist/db/schema.d.ts +9 -0
- package/dist/db/schema.d.ts.map +1 -1
- package/dist/db/schema.js +164 -85
- package/dist/db/schema.js.map +1 -1
- package/dist/discovery/poller.d.ts +6 -16
- package/dist/discovery/poller.d.ts.map +1 -1
- package/dist/discovery/poller.js +21 -52
- package/dist/discovery/poller.js.map +1 -1
- package/dist/discovery/scip-flush.d.ts +44 -0
- package/dist/discovery/scip-flush.d.ts.map +1 -0
- package/dist/discovery/scip-flush.js +67 -0
- package/dist/discovery/scip-flush.js.map +1 -0
- package/dist/discovery/watcher.d.ts +6 -16
- package/dist/discovery/watcher.d.ts.map +1 -1
- package/dist/discovery/watcher.js +22 -54
- package/dist/discovery/watcher.js.map +1 -1
- package/dist/docs/docs.d.ts +0 -2
- package/dist/docs/docs.d.ts.map +1 -1
- package/dist/docs/docs.js +0 -27
- package/dist/docs/docs.js.map +1 -1
- package/dist/index.d.ts +5 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -2
- package/dist/index.js.map +1 -1
- package/dist/indexer/index.d.ts +19 -10
- package/dist/indexer/index.d.ts.map +1 -1
- package/dist/indexer/index.js +99 -24
- package/dist/indexer/index.js.map +1 -1
- package/dist/indexer/pipeline.d.ts +25 -14
- package/dist/indexer/pipeline.d.ts.map +1 -1
- package/dist/indexer/pipeline.js +8 -7
- package/dist/indexer/pipeline.js.map +1 -1
- package/dist/indexer/stages/docs-index.d.ts +1 -3
- package/dist/indexer/stages/docs-index.d.ts.map +1 -1
- package/dist/indexer/stages/docs-index.js +1 -25
- package/dist/indexer/stages/docs-index.js.map +1 -1
- package/dist/indexer/stages/index.d.ts +4 -2
- package/dist/indexer/stages/index.d.ts.map +1 -1
- package/dist/indexer/stages/index.js +3 -2
- package/dist/indexer/stages/index.js.map +1 -1
- package/dist/indexer/stages/lsp-enrichment.d.ts.map +1 -1
- package/dist/indexer/stages/lsp-enrichment.js +20 -1
- package/dist/indexer/stages/lsp-enrichment.js.map +1 -1
- package/dist/indexer/stages/overlay-cleanup.d.ts +31 -0
- package/dist/indexer/stages/overlay-cleanup.d.ts.map +1 -0
- package/dist/indexer/stages/overlay-cleanup.js +64 -0
- package/dist/indexer/stages/overlay-cleanup.js.map +1 -0
- package/dist/indexer/stages/reverse-deps.d.ts +13 -0
- package/dist/indexer/stages/reverse-deps.d.ts.map +1 -0
- package/dist/indexer/stages/reverse-deps.js +81 -0
- package/dist/indexer/stages/reverse-deps.js.map +1 -0
- package/dist/indexer/stages/{scip-source.d.ts → scip-indexer.d.ts} +38 -9
- package/dist/indexer/stages/scip-indexer.d.ts.map +1 -0
- package/dist/indexer/stages/{scip-source.js → scip-indexer.js} +350 -91
- package/dist/indexer/stages/scip-indexer.js.map +1 -0
- package/dist/indexer/stages/source-index.d.ts +3 -3
- package/dist/indexer/stages/source-index.d.ts.map +1 -1
- package/dist/indexer/stages/source-index.js +116 -69
- package/dist/indexer/stages/source-index.js.map +1 -1
- package/dist/lsp/enrichment.d.ts.map +1 -1
- package/dist/lsp/enrichment.js +1 -18
- package/dist/lsp/enrichment.js.map +1 -1
- package/dist/parsing/extractors/go.d.ts.map +1 -1
- package/dist/parsing/extractors/go.js +0 -38
- package/dist/parsing/extractors/go.js.map +1 -1
- package/dist/parsing/extractors/javascript.d.ts.map +1 -1
- package/dist/parsing/extractors/javascript.js +0 -47
- package/dist/parsing/extractors/javascript.js.map +1 -1
- package/dist/parsing/extractors/python.d.ts.map +1 -1
- package/dist/parsing/extractors/python.js +0 -22
- package/dist/parsing/extractors/python.js.map +1 -1
- package/dist/parsing/extractors/types.d.ts +0 -16
- package/dist/parsing/extractors/types.d.ts.map +1 -1
- package/dist/parsing/extractors/types.js +1 -1
- package/dist/parsing/extractors/types.js.map +1 -1
- package/dist/parsing/extractors/typescript.js +2 -2
- package/dist/parsing/extractors/typescript.js.map +1 -1
- package/dist/resolution/call-graph.d.ts +3 -1
- package/dist/resolution/call-graph.d.ts.map +1 -1
- package/dist/resolution/call-graph.js +15 -13
- package/dist/resolution/call-graph.js.map +1 -1
- package/dist/resolution/resolution-method.d.ts +1 -1
- package/dist/resolution/resolution-method.d.ts.map +1 -1
- package/dist/resolution/resolution-method.js +2 -0
- package/dist/resolution/resolution-method.js.map +1 -1
- package/dist/runtime.d.ts +0 -2
- package/dist/runtime.d.ts.map +1 -1
- package/dist/runtime.js.map +1 -1
- package/dist/scip/compdb.d.ts +38 -0
- package/dist/scip/compdb.d.ts.map +1 -0
- package/dist/scip/compdb.js +138 -0
- package/dist/scip/compdb.js.map +1 -0
- package/dist/scip/enrichment.d.ts +6 -1
- package/dist/scip/enrichment.d.ts.map +1 -1
- package/dist/scip/enrichment.js +59 -2
- package/dist/scip/enrichment.js.map +1 -1
- package/dist/scip/installer.d.ts +59 -0
- package/dist/scip/installer.d.ts.map +1 -0
- package/dist/scip/installer.js +302 -0
- package/dist/scip/installer.js.map +1 -0
- package/dist/scip/registry.d.ts +1 -1
- package/dist/scip/registry.d.ts.map +1 -1
- package/dist/scip/registry.js +33 -5
- package/dist/scip/registry.js.map +1 -1
- package/dist/server/server.d.ts +2 -6
- package/dist/server/server.d.ts.map +1 -1
- package/dist/server/server.js +6 -56
- package/dist/server/server.js.map +1 -1
- package/dist/server/tool-registry.d.ts +2 -2
- package/dist/server/tool-registry.d.ts.map +1 -1
- package/dist/server/tool-registry.js +41 -20
- package/dist/server/tool-registry.js.map +1 -1
- package/dist/server/tools/cohesion.d.ts +46 -0
- package/dist/server/tools/cohesion.d.ts.map +1 -0
- package/dist/server/tools/cohesion.js +100 -0
- package/dist/server/tools/cohesion.js.map +1 -0
- package/dist/server/tools/dependents.d.ts +139 -0
- package/dist/server/tools/dependents.d.ts.map +1 -0
- package/dist/server/tools/dependents.js +354 -0
- package/dist/server/tools/dependents.js.map +1 -0
- package/dist/server/tools/diff.d.ts +73 -0
- package/dist/server/tools/diff.d.ts.map +1 -0
- package/dist/server/tools/diff.js +157 -0
- package/dist/server/tools/diff.js.map +1 -0
- package/dist/server/tools/structure.d.ts +77 -0
- package/dist/server/tools/structure.d.ts.map +1 -0
- package/dist/server/tools/structure.js +309 -0
- package/dist/server/tools/structure.js.map +1 -0
- package/dist/server/tools/test-map.d.ts +7 -0
- package/dist/server/tools/test-map.d.ts.map +1 -1
- package/dist/server/tools/test-map.js +18 -1
- package/dist/server/tools/test-map.js.map +1 -1
- package/dist/server/tools/trace.d.ts +84 -0
- package/dist/server/tools/trace.d.ts.map +1 -0
- package/dist/server/tools/trace.js +317 -0
- package/dist/server/tools/trace.js.map +1 -0
- package/dist/testing/coverage.d.ts +11 -0
- package/dist/testing/coverage.d.ts.map +1 -1
- package/dist/testing/coverage.js +34 -2
- package/dist/testing/coverage.js.map +1 -1
- package/dist/testing/test-mapper.d.ts +1 -1
- package/dist/testing/test-mapper.d.ts.map +1 -1
- package/dist/testing/test-mapper.js +1 -0
- package/dist/testing/test-mapper.js.map +1 -1
- package/package.json +3 -1
- package/dist/indexer/stages/scip-enrichment.d.ts +0 -43
- package/dist/indexer/stages/scip-enrichment.d.ts.map +0 -1
- package/dist/indexer/stages/scip-enrichment.js +0 -172
- package/dist/indexer/stages/scip-enrichment.js.map +0 -1
- package/dist/indexer/stages/scip-source.d.ts.map +0 -1
- package/dist/indexer/stages/scip-source.js.map +0 -1
- package/dist/server/tools/notes.d.ts +0 -165
- package/dist/server/tools/notes.d.ts.map +0 -1
- package/dist/server/tools/notes.js +0 -214
- package/dist/server/tools/notes.js.map +0 -1
- package/dist/server/tools/routes.d.ts +0 -38
- package/dist/server/tools/routes.d.ts.map +0 -1
- package/dist/server/tools/routes.js +0 -38
- package/dist/server/tools/routes.js.map +0 -1
|
@@ -1,19 +1,25 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* @module indexer/stages/scip-
|
|
2
|
+
* @module indexer/stages/scip-indexer
|
|
3
3
|
*
|
|
4
4
|
* Pipeline stage: for SCIP-covered languages, populate `files`, `symbols`,
|
|
5
5
|
* `symbol_refs`, `type_refs`, `symbol_relationships`, and `file_imports`
|
|
6
6
|
* **directly from the SCIP index** — bypassing tree-sitter entirely.
|
|
7
7
|
*
|
|
8
|
-
* This is the
|
|
9
|
-
*
|
|
8
|
+
* This is the single-pass SCIP architecture. SCIP is the source of truth
|
|
9
|
+
* for the symbol table, the call graph, **and** enrichment metadata (type
|
|
10
|
+
* signatures, definition locations). All data is written in one pass.
|
|
11
|
+
*
|
|
12
|
+
* For each SCIP document:
|
|
10
13
|
*
|
|
11
14
|
* 1. **Symbols**: Definition occurrences → `symbols` rows; kinds inferred
|
|
12
15
|
* from SCIP descriptor suffixes; spans from `enclosing_range`.
|
|
16
|
+
* Enrichment columns (`resolved_type_signature`, `resolved_return_type`,
|
|
17
|
+
* `definition_uri`, `definition_path`) are populated inline.
|
|
13
18
|
* 2. **Refs**: Non-definition, non-local reference occurrences →
|
|
14
19
|
* `symbol_refs` rows with both `caller_id` and `callee_id` resolved
|
|
15
20
|
* using containment (which symbol's span encloses this ref?) and
|
|
16
21
|
* definition lookup (where is the referenced SCIP symbol defined?).
|
|
22
|
+
* Enrichment columns are populated inline from the same SCIP data.
|
|
17
23
|
*
|
|
18
24
|
* SCIP refs are inserted **pre-resolved** with `resolution_method =
|
|
19
25
|
* 'scip_definition'`. The downstream resolution stage only processes
|
|
@@ -23,20 +29,32 @@
|
|
|
23
29
|
*
|
|
24
30
|
* Same tables as `SourceIndexStage`: `files`, `symbols`, `symbols_fts`,
|
|
25
31
|
* `symbol_refs`, `type_refs`, `symbol_relationships`, `file_imports`.
|
|
32
|
+
* Additionally populates enrichment columns (type signatures, definition
|
|
33
|
+
* locations) inline during the same pass.
|
|
26
34
|
*
|
|
27
35
|
* ## Pipeline ordering
|
|
28
36
|
*
|
|
29
37
|
* This stage runs **before** `SourceIndexStage`. It stores which
|
|
30
|
-
* languages and files it handled in `context.scipSourcedLanguages
|
|
31
|
-
* `context.scipSourcedFiles` so
|
|
38
|
+
* languages and files it handled in `context.scipSourcedLanguages`,
|
|
39
|
+
* `context.scipSourcedFiles`, and `context.scipCoveredLanguages` so
|
|
40
|
+
* `SourceIndexStage` can skip full extraction (but still compute
|
|
41
|
+
* tree-sitter metrics) and `LspEnrichmentStage` knows not to re-enrich
|
|
42
|
+
* these languages.
|
|
32
43
|
*/
|
|
33
44
|
import * as fs from 'node:fs';
|
|
34
45
|
import * as crypto from 'node:crypto';
|
|
46
|
+
import { tmpdir } from 'node:os';
|
|
35
47
|
import { resolve } from 'node:path';
|
|
48
|
+
import { pathToFileURL } from 'node:url';
|
|
36
49
|
import { fromBinary } from '@bufbuild/protobuf';
|
|
37
50
|
import { IndexSchema, SymbolRole, } from '../../scip/scip_pb.js';
|
|
38
51
|
import { normalizeTypeName } from '../../resolution/call-graph.js';
|
|
39
52
|
import { SCIP_SUPPORTED_LANGUAGES, resolveScipIndexerRegistry } from '../../scip/registry.js';
|
|
53
|
+
import { getLogger } from '../../logger.js';
|
|
54
|
+
import { extractReturnType } from '../../scip/index-reader.js';
|
|
55
|
+
import { getSpecsForLanguage, installScipIndexer } from '../../scip/installer.js';
|
|
56
|
+
import { ensureCompilationDatabase } from '../../scip/compdb.js';
|
|
57
|
+
import { EXT_TO_LANG } from '../../discovery/walker.js';
|
|
40
58
|
import { readFileSync, existsSync } from 'node:fs';
|
|
41
59
|
import { join } from 'node:path';
|
|
42
60
|
import { execFile } from 'node:child_process';
|
|
@@ -89,6 +107,10 @@ function inferKindFromScipSymbol(scipSymbol, docHint) {
|
|
|
89
107
|
return 'constant';
|
|
90
108
|
if (docHint.includes('(property)'))
|
|
91
109
|
return 'property';
|
|
110
|
+
// scip-clang uses term descriptors for C/C++ functions:
|
|
111
|
+
// ` $ funcName(hexhash).` — the (hash) indicates a function, not a variable.
|
|
112
|
+
if (/\([0-9a-f]{8,}\)\.$/.test(scipSymbol))
|
|
113
|
+
return 'function';
|
|
92
114
|
return 'variable';
|
|
93
115
|
}
|
|
94
116
|
// Meta: ends with :
|
|
@@ -110,12 +132,17 @@ function extractNameFromScipSymbol(scipSymbol) {
|
|
|
110
132
|
let cleaned = scipSymbol.replace(/[.#/:]$/, '');
|
|
111
133
|
// For methods, strip the disambiguator: `name(+1).` → `name`
|
|
112
134
|
cleaned = cleaned.replace(/\(\+?\d*\)$/, '');
|
|
135
|
+
// scip-clang uses ` $ name(hash)` for C/C++ symbols — strip the hash.
|
|
136
|
+
// E.g., ` $ parse_analyze_fixedparams(39d222e79bbfb7c0)` → `parse_analyze_fixedparams`
|
|
137
|
+
cleaned = cleaned.replace(/\([0-9a-f]{8,}\)$/, '');
|
|
113
138
|
// Get the last descriptor's name
|
|
114
139
|
// Descriptors are separated by ., #, /, :, or ()
|
|
115
140
|
const parts = cleaned.split(/[.#/:]/);
|
|
116
141
|
let name = parts[parts.length - 1] || '';
|
|
117
142
|
// Remove backtick escaping
|
|
118
143
|
name = name.replace(/`/g, '');
|
|
144
|
+
// Strip leading ` $ ` prefix used by scip-clang
|
|
145
|
+
name = name.replace(/^\s*\$\s*/, '');
|
|
119
146
|
// Handle parameter descriptors like `(paramName)`
|
|
120
147
|
if (name.startsWith('(') && name.endsWith(')')) {
|
|
121
148
|
name = name.slice(1, -1);
|
|
@@ -243,11 +270,14 @@ function inferTypeRefKind(sourceLines, refLine, refChar) {
|
|
|
243
270
|
return 'other';
|
|
244
271
|
}
|
|
245
272
|
// ─── Stage implementation ────────────────────────────────────────────────────
|
|
246
|
-
export class
|
|
247
|
-
name = 'scip-
|
|
273
|
+
export class ScipIndexerStage {
|
|
274
|
+
name = 'scip-indexer';
|
|
248
275
|
async execute(context, mode) {
|
|
249
276
|
if (!context.scip?.enabled)
|
|
250
277
|
return;
|
|
278
|
+
// SCIP only runs during baseline builds — never during overlay updates.
|
|
279
|
+
if (context.layer === 'overlay')
|
|
280
|
+
return;
|
|
251
281
|
const log = context.log;
|
|
252
282
|
const rootDir = context.walkerConfig.rootDir;
|
|
253
283
|
// In update mode, determine which SCIP-supported languages have changed
|
|
@@ -266,19 +296,27 @@ export class ScipSourceStage {
|
|
|
266
296
|
}
|
|
267
297
|
}
|
|
268
298
|
if (staleLanguages.size === 0) {
|
|
269
|
-
log.indexing('scip-
|
|
299
|
+
log.indexing('scip-indexer: no SCIP-supported languages in changed files, skipping');
|
|
270
300
|
return;
|
|
271
301
|
}
|
|
272
|
-
log.indexing('scip-
|
|
302
|
+
log.indexing('scip-indexer: stale languages', { languages: [...staleLanguages] });
|
|
273
303
|
}
|
|
274
|
-
// Load SCIP
|
|
275
|
-
const
|
|
276
|
-
if (
|
|
277
|
-
log.indexing('scip-
|
|
304
|
+
// Load SCIP indexes (one per indexer that succeeds)
|
|
305
|
+
const indexBuffers = await this.loadScipIndexes(context.scip, rootDir, staleLanguages);
|
|
306
|
+
if (indexBuffers.length === 0) {
|
|
307
|
+
log.indexing('scip-indexer: no SCIP index available');
|
|
278
308
|
return;
|
|
279
309
|
}
|
|
280
|
-
|
|
281
|
-
|
|
310
|
+
// Parse all SCIP index buffers and combine their documents
|
|
311
|
+
const allDocuments = [];
|
|
312
|
+
const allExternalSymbols = [];
|
|
313
|
+
for (const buf of indexBuffers) {
|
|
314
|
+
const parsed = fromBinary(IndexSchema, buf);
|
|
315
|
+
allDocuments.push(...parsed.documents);
|
|
316
|
+
allExternalSymbols.push(...parsed.externalSymbols);
|
|
317
|
+
}
|
|
318
|
+
const scipIndex = { documents: allDocuments, externalSymbols: allExternalSymbols };
|
|
319
|
+
log.indexing('scip-indexer: loaded index', {
|
|
282
320
|
documents: scipIndex.documents.length,
|
|
283
321
|
externalSymbols: scipIndex.externalSymbols.length,
|
|
284
322
|
});
|
|
@@ -294,7 +332,7 @@ export class ScipSourceStage {
|
|
|
294
332
|
if (loreLang)
|
|
295
333
|
coveredLanguages.add(loreLang);
|
|
296
334
|
}
|
|
297
|
-
log.indexing('scip-
|
|
335
|
+
log.indexing('scip-indexer: languages covered', { languages: [...coveredLanguages] });
|
|
298
336
|
// Determine the project's SCIP symbol prefix so we can distinguish
|
|
299
337
|
// internal symbols from external ones (stdlib, node_modules, etc.).
|
|
300
338
|
// Internal symbols are those whose SCIP string starts with the same
|
|
@@ -350,17 +388,19 @@ export class ScipSourceStage {
|
|
|
350
388
|
const db = context.db;
|
|
351
389
|
const branch = context.branch;
|
|
352
390
|
// Prepared statements
|
|
353
|
-
const insertFile = db.prepare(`INSERT INTO files (path, branch, language, size_bytes, last_hash, source)
|
|
354
|
-
VALUES (?, ?, ?, ?, ?, ?)`);
|
|
355
|
-
const insertSymbol = db.prepare(`INSERT INTO symbols (file_id, name, kind, start_line, end_line, signature, doc_comment)
|
|
356
|
-
VALUES (?, ?, ?, ?, ?, ?, ?)`);
|
|
357
|
-
const insertCallRef = db.prepare(`INSERT INTO symbol_refs (caller_id, file_id, callee_id, callee_name, call_line, call_character, call_kind, resolution_method)
|
|
391
|
+
const insertFile = db.prepare(`INSERT INTO files (path, branch, language, size_bytes, last_hash, source, layer, generation)
|
|
358
392
|
VALUES (?, ?, ?, ?, ?, ?, ?, ?)`);
|
|
359
|
-
const
|
|
360
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`);
|
|
361
|
-
const
|
|
362
|
-
|
|
363
|
-
|
|
393
|
+
const insertSymbol = db.prepare(`INSERT INTO symbols (file_id, name, kind, start_line, end_line, signature, doc_comment, resolved_type_signature, resolved_return_type, definition_uri, definition_path, layer, generation)
|
|
394
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`);
|
|
395
|
+
const insertCallRef = db.prepare(`INSERT INTO symbol_refs (caller_id, file_id, callee_id, callee_name, call_line, call_character, call_kind, resolution_method, resolved_type_signature, resolved_return_type, definition_uri, definition_path, definition_line, definition_character, layer, generation)
|
|
396
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`);
|
|
397
|
+
const insertTypeRef = db.prepare(`INSERT INTO type_refs (file_id, symbol_id, type_id, type_name, type_name_bare, ref_kind, ref_line, ref_character, resolution_method, resolved_type_signature, definition_uri, definition_path, definition_line, definition_character, layer, generation)
|
|
398
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`);
|
|
399
|
+
const insertImport = db.prepare('INSERT INTO file_imports (file_id, raw_import, resolved_id, layer, generation) VALUES (?, ?, ?, ?, ?)');
|
|
400
|
+
const insertRelationship = db.prepare(`INSERT INTO symbol_relationships (file_id, source_symbol_id, target_symbol_name, relationship_type, line, character, resolution_method, definition_uri, definition_path, definition_line, definition_character, layer, generation)
|
|
401
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`);
|
|
402
|
+
const layer = context.layer;
|
|
403
|
+
const generation = context.generation;
|
|
364
404
|
// Global map: SCIP symbol string → Lore numeric symbol ID (across all files)
|
|
365
405
|
const scipToLoreId = new Map();
|
|
366
406
|
// Pass 1: Create files and symbols
|
|
@@ -395,7 +435,7 @@ export class ScipSourceStage {
|
|
|
395
435
|
db.prepare('DELETE FROM files WHERE id = ?').run(existing.id);
|
|
396
436
|
}
|
|
397
437
|
// Insert file
|
|
398
|
-
const fileInfo = insertFile.run(absPath, branch, loreLang, sizeBytes, hash, source);
|
|
438
|
+
const fileInfo = insertFile.run(absPath, branch, loreLang, sizeBytes, hash, source, layer, generation);
|
|
399
439
|
const fileId = Number(fileInfo.lastInsertRowid);
|
|
400
440
|
fileIdMap.set(absPath, fileId);
|
|
401
441
|
coveredFiles.add(absPath);
|
|
@@ -452,13 +492,18 @@ export class ScipSourceStage {
|
|
|
452
492
|
continue;
|
|
453
493
|
const signature = extractSignatureFromDoc(firstDoc);
|
|
454
494
|
const docComment = symInfo.documentation.slice(1).join('\n').trim() || null;
|
|
455
|
-
|
|
495
|
+
// Compute enrichment data inline (definition + type signature)
|
|
496
|
+
const resolvedTypeSig = signature || null;
|
|
497
|
+
const resolvedReturnType = extractReturnType(resolvedTypeSig);
|
|
498
|
+
const definitionUri = pathToFileURL(absPath).toString();
|
|
499
|
+
const info = insertSymbol.run(fileId, name, kind, defLoc.startLine, defLoc.endLine, signature || null, docComment, resolvedTypeSig, resolvedReturnType, definitionUri, absPath, layer, generation);
|
|
456
500
|
const loreId = Number(info.lastInsertRowid);
|
|
457
501
|
scipToLoreId.set(symInfo.symbol, loreId);
|
|
458
502
|
}
|
|
459
503
|
// Insert imports (from Import-role occurrences)
|
|
460
|
-
// Prefer the actual import path from source; fall back to SCIP package
|
|
461
|
-
|
|
504
|
+
// Prefer the actual import path from source; fall back to SCIP package.
|
|
505
|
+
// Use symbolDefinitions to pre-resolve imports to target file IDs.
|
|
506
|
+
const seenImports = new Map(); // rawImport → resolved file ID
|
|
462
507
|
for (const occ of doc.occurrences) {
|
|
463
508
|
if ((occ.symbolRoles & SymbolRole.Import) !== 0 && occ.symbol) {
|
|
464
509
|
const importLine = occ.range[0] ?? 0;
|
|
@@ -474,9 +519,23 @@ export class ScipSourceStage {
|
|
|
474
519
|
const parts = occ.symbol.split(' ');
|
|
475
520
|
rawImport = parts.length >= 4 ? parts[3] : occ.symbol;
|
|
476
521
|
}
|
|
477
|
-
if (
|
|
478
|
-
|
|
479
|
-
|
|
522
|
+
if (!rawImport)
|
|
523
|
+
continue;
|
|
524
|
+
// Resolve the import's target file via SCIP symbol → definition location
|
|
525
|
+
const defLoc = symbolDefinitions.get(occ.symbol);
|
|
526
|
+
const resolvedFileId = defLoc ? (fileIdMap.get(defLoc.filePath) ?? null) : null;
|
|
527
|
+
if (seenImports.has(rawImport)) {
|
|
528
|
+
// If we already inserted this import without a resolved_id,
|
|
529
|
+
// upgrade it now that we have one.
|
|
530
|
+
if (resolvedFileId && !seenImports.get(rawImport)) {
|
|
531
|
+
seenImports.set(rawImport, resolvedFileId);
|
|
532
|
+
db.prepare('UPDATE file_imports SET resolved_id = ? WHERE file_id = ? AND raw_import = ?')
|
|
533
|
+
.run(resolvedFileId, fileId, rawImport);
|
|
534
|
+
}
|
|
535
|
+
}
|
|
536
|
+
else {
|
|
537
|
+
seenImports.set(rawImport, resolvedFileId);
|
|
538
|
+
insertImport.run(fileId, rawImport, resolvedFileId, layer, generation);
|
|
480
539
|
}
|
|
481
540
|
}
|
|
482
541
|
}
|
|
@@ -516,7 +575,10 @@ export class ScipSourceStage {
|
|
|
516
575
|
// relationships for symbols whose definition is in another file or
|
|
517
576
|
// external package, so defLoc may be undefined.
|
|
518
577
|
const defLoc = symbolDefinitions.get(symInfo.symbol);
|
|
519
|
-
|
|
578
|
+
// Resolve the target's definition location for enrichment
|
|
579
|
+
const targetDef = symbolDefinitions.get(rel.symbol);
|
|
580
|
+
const relDefUri = targetDef ? pathToFileURL(targetDef.filePath).toString() : null;
|
|
581
|
+
insertRelationship.run(fileId, sourceId, targetName, relType, defLoc?.line ?? null, defLoc?.character ?? null, targetId ? 'scip_definition' : 'unresolved', relDefUri, targetDef?.filePath ?? null, targetDef?.line ?? null, targetDef?.character ?? null, layer, generation);
|
|
520
582
|
// If we have both source and target IDs, update the resolved target
|
|
521
583
|
if (targetId) {
|
|
522
584
|
db.prepare('UPDATE symbol_relationships SET target_symbol_id = ? WHERE file_id = ? AND source_symbol_id = ? AND target_symbol_name = ? AND relationship_type = ?').run(targetId, fileId, sourceId, targetName, relType);
|
|
@@ -526,7 +588,7 @@ export class ScipSourceStage {
|
|
|
526
588
|
}
|
|
527
589
|
});
|
|
528
590
|
processDocuments();
|
|
529
|
-
log.indexing('scip-
|
|
591
|
+
log.indexing('scip-indexer: symbols inserted', {
|
|
530
592
|
files: fileIdMap.size,
|
|
531
593
|
symbols: scipToLoreId.size,
|
|
532
594
|
});
|
|
@@ -655,21 +717,49 @@ export class ScipSourceStage {
|
|
|
655
717
|
}
|
|
656
718
|
if (refKind === 'type') {
|
|
657
719
|
const typeRefKind = inferTypeRefKind(sourceLines, line, character);
|
|
658
|
-
|
|
659
|
-
|
|
720
|
+
// Resolve enrichment metadata for the referenced type
|
|
721
|
+
const refDef = symbolDefinitions.get(occ.symbol);
|
|
722
|
+
const refInfo = symbolInfoMap.get(occ.symbol);
|
|
723
|
+
const refSig = refInfo ? extractSignatureFromDoc(refInfo.documentation[0] ?? '') || null : null;
|
|
724
|
+
const refDefUri = refDef ? pathToFileURL(refDef.filePath).toString() : null;
|
|
725
|
+
try {
|
|
726
|
+
insertTypeRef.run(fileId, callerId, calleeId ?? null, calleeName, normalizeTypeName(calleeName), typeRefKind, line, character, method, refSig, refDefUri, refDef?.filePath ?? null, refDef?.line ?? null, refDef?.character ?? null, layer, generation);
|
|
727
|
+
typeRefsInserted++;
|
|
728
|
+
}
|
|
729
|
+
catch {
|
|
730
|
+
// FK constraint failure — skip this ref
|
|
731
|
+
refsNoCaller++;
|
|
732
|
+
}
|
|
660
733
|
}
|
|
661
734
|
else {
|
|
662
735
|
// refKind === 'call'
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
736
|
+
// Skip if callee can't be resolved — FK constraint requires valid callee_id
|
|
737
|
+
if (!calleeId) {
|
|
738
|
+
refsNoCaller++;
|
|
739
|
+
continue;
|
|
740
|
+
}
|
|
741
|
+
// Resolve enrichment metadata for the callee
|
|
742
|
+
const refDef = symbolDefinitions.get(occ.symbol);
|
|
743
|
+
const refInfo = symbolInfoMap.get(occ.symbol);
|
|
744
|
+
const refSig = refInfo ? extractSignatureFromDoc(refInfo.documentation[0] ?? '') || null : null;
|
|
745
|
+
const refReturnType = extractReturnType(refSig);
|
|
746
|
+
const refDefUri = refDef ? pathToFileURL(refDef.filePath).toString() : null;
|
|
747
|
+
try {
|
|
748
|
+
insertCallRef.run(callerId, fileId, calleeId, calleeName, line, character, 'direct', method, refSig, refReturnType, refDefUri, refDef?.filePath ?? null, refDef?.line ?? null, refDef?.character ?? null, layer, generation);
|
|
749
|
+
refsInserted++;
|
|
750
|
+
if (isExternal)
|
|
751
|
+
refsExternal++;
|
|
752
|
+
}
|
|
753
|
+
catch {
|
|
754
|
+
// FK constraint failure — skip this ref
|
|
755
|
+
refsNoCaller++;
|
|
756
|
+
}
|
|
667
757
|
}
|
|
668
758
|
}
|
|
669
759
|
}
|
|
670
760
|
});
|
|
671
761
|
processRefs();
|
|
672
|
-
log.indexing('scip-
|
|
762
|
+
log.indexing('scip-indexer: refs inserted', {
|
|
673
763
|
callRefs: refsInserted,
|
|
674
764
|
typeRefs: typeRefsInserted,
|
|
675
765
|
external: refsExternal,
|
|
@@ -680,6 +770,9 @@ export class ScipSourceStage {
|
|
|
680
770
|
// Communicate coverage to downstream stages
|
|
681
771
|
context.scipSourcedLanguages = coveredLanguages;
|
|
682
772
|
context.scipSourcedFiles = coveredFiles;
|
|
773
|
+
// Also set scipCoveredLanguages so that LspEnrichmentStage skips
|
|
774
|
+
// languages already fully enriched by this single SCIP pass.
|
|
775
|
+
context.scipCoveredLanguages = coveredLanguages;
|
|
683
776
|
// Add SCIP-sourced files to context.files so later stages process them
|
|
684
777
|
for (const doc of scipIndex.documents) {
|
|
685
778
|
const absPath = resolve(rootDir, doc.relativePath);
|
|
@@ -693,62 +786,191 @@ export class ScipSourceStage {
|
|
|
693
786
|
// No persistent resources to clean up
|
|
694
787
|
}
|
|
695
788
|
// ─── SCIP index loading ──────────────────────────────────────────────────
|
|
696
|
-
async
|
|
789
|
+
async loadScipIndexes(settings, rootDir, staleLanguages = null) {
|
|
697
790
|
// Try pre-computed index directory first
|
|
698
791
|
if (settings.indexDir) {
|
|
792
|
+
const precomputed = [];
|
|
699
793
|
// When staleLanguages is set, prefer per-language index files so
|
|
700
794
|
// we only load the languages that actually need re-processing.
|
|
701
795
|
if (staleLanguages) {
|
|
702
796
|
for (const lang of staleLanguages) {
|
|
703
797
|
const candidate = join(rootDir, settings.indexDir, `${lang}.scip`);
|
|
704
798
|
if (existsSync(candidate)) {
|
|
705
|
-
|
|
799
|
+
precomputed.push(readFileSync(candidate));
|
|
706
800
|
}
|
|
707
801
|
}
|
|
708
802
|
}
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
803
|
+
if (precomputed.length === 0) {
|
|
804
|
+
const candidates = [
|
|
805
|
+
join(rootDir, settings.indexDir, 'index.scip'),
|
|
806
|
+
...['typescript', 'javascript', 'python', 'java', 'rust', 'c', 'cpp', 'csharp', 'ruby', 'php', 'go', 'dart'].map(lang => join(rootDir, settings.indexDir, `${lang}.scip`)),
|
|
807
|
+
];
|
|
808
|
+
for (const candidate of candidates) {
|
|
809
|
+
if (existsSync(candidate)) {
|
|
810
|
+
precomputed.push(readFileSync(candidate));
|
|
811
|
+
}
|
|
717
812
|
}
|
|
718
813
|
}
|
|
814
|
+
if (precomputed.length > 0)
|
|
815
|
+
return precomputed;
|
|
719
816
|
}
|
|
720
817
|
// Try running an indexer
|
|
721
|
-
|
|
818
|
+
let resolvedIndexers = resolveScipIndexerRegistry(settings.indexers);
|
|
819
|
+
const log = getLogger();
|
|
820
|
+
// Check if any indexers are available; if not, try auto-installing
|
|
821
|
+
const requestedLanguages = staleLanguages ?? new Set(Object.keys(resolvedIndexers));
|
|
822
|
+
const hasAvailable = [...requestedLanguages].some((lang) => resolvedIndexers[lang]?.available);
|
|
823
|
+
if (!hasAvailable) {
|
|
824
|
+
const attempted = new Set();
|
|
825
|
+
for (const lang of requestedLanguages) {
|
|
826
|
+
for (const spec of getSpecsForLanguage(lang)) {
|
|
827
|
+
if (attempted.has(spec.command))
|
|
828
|
+
continue;
|
|
829
|
+
attempted.add(spec.command);
|
|
830
|
+
log.indexing(`scip-indexer: auto-installing ${spec.command} for ${lang}...`);
|
|
831
|
+
const result = await installScipIndexer(spec);
|
|
832
|
+
if (result.installed) {
|
|
833
|
+
log.indexing(`scip-indexer: installed ${spec.command} at ${result.path}`);
|
|
834
|
+
}
|
|
835
|
+
else {
|
|
836
|
+
log.indexing(`scip-indexer: could not install ${spec.command}: ${result.error ?? 'unknown'}`);
|
|
837
|
+
}
|
|
838
|
+
}
|
|
839
|
+
}
|
|
840
|
+
// Re-resolve after installation
|
|
841
|
+
resolvedIndexers = resolveScipIndexerRegistry(settings.indexers);
|
|
842
|
+
}
|
|
843
|
+
// Determine which SCIP-supported languages actually exist in the project
|
|
844
|
+
// so we don't waste time running irrelevant indexers (e.g., scip-go on a C project).
|
|
845
|
+
const projectLanguages = staleLanguages ?? detectProjectLanguages(resolve(rootDir));
|
|
846
|
+
// Run all available indexers and merge results — don't stop at the first success.
|
|
847
|
+
// Group by shared command to avoid running the same indexer twice (e.g., scip-java for java/scala/kotlin).
|
|
848
|
+
const commandsRun = new Set();
|
|
849
|
+
const indexBuffers = [];
|
|
722
850
|
for (const [lang, indexer] of Object.entries(resolvedIndexers)) {
|
|
723
851
|
if (!indexer.available)
|
|
724
852
|
continue;
|
|
725
|
-
//
|
|
726
|
-
if (
|
|
853
|
+
// Skip languages not present in the project
|
|
854
|
+
if (!projectLanguages.has(lang))
|
|
727
855
|
continue;
|
|
856
|
+
// Don't run the same command twice (e.g., scip-clang for both c and cpp)
|
|
857
|
+
if (commandsRun.has(indexer.command))
|
|
858
|
+
continue;
|
|
859
|
+
commandsRun.add(indexer.command);
|
|
728
860
|
try {
|
|
729
|
-
const outputPath =
|
|
730
|
-
|
|
861
|
+
const outputPath = resolve(rootDir, `.lore-scip-${lang}.scip`);
|
|
862
|
+
let args = indexer.args.map(a => a.replace(/\{output\}/g, outputPath));
|
|
863
|
+
const cwd = resolve(rootDir);
|
|
864
|
+
// For C/C++: ensure a compile_commands.json exists and pass it to scip-clang
|
|
865
|
+
if ((lang === 'c' || lang === 'cpp') && args.some(a => a.includes('{compdb}'))) {
|
|
866
|
+
const compdb = await ensureCompilationDatabase(rootDir, settings.timeoutMs);
|
|
867
|
+
if (!compdb.path) {
|
|
868
|
+
log.indexing(`scip-indexer: no compile_commands.json for ${lang}, skipping`);
|
|
869
|
+
continue;
|
|
870
|
+
}
|
|
871
|
+
args = args.map(a => a.replace(/\{compdb\}/g, compdb.path));
|
|
872
|
+
}
|
|
873
|
+
// For TypeScript: generate a broad tsconfig so scip-typescript
|
|
874
|
+
// indexes ALL .ts files (including tests), not just those in the
|
|
875
|
+
// project's tsconfig "include" (which typically excludes tests).
|
|
876
|
+
let tempTsconfigPath = null;
|
|
877
|
+
if (lang === 'typescript') {
|
|
878
|
+
tempTsconfigPath = createLoreScipTsconfig(rootDir);
|
|
879
|
+
if (tempTsconfigPath) {
|
|
880
|
+
args.push(tempTsconfigPath);
|
|
881
|
+
}
|
|
882
|
+
}
|
|
883
|
+
// scip-clang needs a longer timeout for large C projects
|
|
884
|
+
const indexerTimeout = (lang === 'c' || lang === 'cpp')
|
|
885
|
+
? Math.max(settings.timeoutMs, 600_000)
|
|
886
|
+
: settings.timeoutMs;
|
|
731
887
|
const execFileAsync = promisify(execFile);
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
888
|
+
const executablePath = indexer.resolvedPath ?? indexer.command;
|
|
889
|
+
try {
|
|
890
|
+
await execFileAsync(executablePath, args, {
|
|
891
|
+
cwd,
|
|
892
|
+
timeout: indexerTimeout,
|
|
893
|
+
});
|
|
894
|
+
}
|
|
895
|
+
finally {
|
|
896
|
+
if (tempTsconfigPath) {
|
|
897
|
+
try {
|
|
898
|
+
fs.unlinkSync(tempTsconfigPath);
|
|
899
|
+
}
|
|
900
|
+
catch { /* best effort */ }
|
|
901
|
+
}
|
|
902
|
+
}
|
|
736
903
|
// Check for output
|
|
737
|
-
for (const candidate of [outputPath,
|
|
904
|
+
for (const candidate of [outputPath, resolve(rootDir, 'index.scip')]) {
|
|
738
905
|
if (existsSync(candidate)) {
|
|
739
906
|
const data = readFileSync(candidate);
|
|
740
907
|
try {
|
|
741
908
|
fs.unlinkSync(candidate);
|
|
742
909
|
}
|
|
743
910
|
catch { /* best effort */ }
|
|
744
|
-
|
|
911
|
+
indexBuffers.push(data);
|
|
912
|
+
break;
|
|
745
913
|
}
|
|
746
914
|
}
|
|
747
915
|
}
|
|
748
|
-
catch {
|
|
916
|
+
catch (error) {
|
|
917
|
+
const msg = error instanceof Error ? error.message : String(error);
|
|
918
|
+
log.indexing(`scip-indexer: indexer failed for ${lang}: ${msg}`);
|
|
749
919
|
continue;
|
|
750
920
|
}
|
|
751
921
|
}
|
|
922
|
+
return indexBuffers;
|
|
923
|
+
}
|
|
924
|
+
}
|
|
925
|
+
// ─── Temporary tsconfig for broad SCIP indexing ─────────────────────────────
|
|
926
|
+
/** Fields that only affect build output, not type-checking or SCIP indexing. */
|
|
927
|
+
const TSCONFIG_BUILD_ONLY_FIELDS = [
|
|
928
|
+
'outDir', 'rootDir', 'declaration', 'declarationMap', 'declarationDir',
|
|
929
|
+
'sourceMap', 'inlineSourceMap', 'inlineSources', 'composite',
|
|
930
|
+
'tsBuildInfoFile', 'emitDeclarationOnly',
|
|
931
|
+
];
|
|
932
|
+
/**
|
|
933
|
+
* Generate a temporary tsconfig that includes **all** `.ts`/`.tsx` files
|
|
934
|
+
* in the project, so `scip-typescript` indexes tests and other files
|
|
935
|
+
* excluded by the project's production tsconfig.
|
|
936
|
+
*
|
|
937
|
+
* The file is written to `os.tmpdir()` so the indexed repo is never mutated.
|
|
938
|
+
* Include/exclude globs use absolute paths rooted at `rootDir` so
|
|
939
|
+
* `scip-typescript` resolves source files correctly even though the
|
|
940
|
+
* tsconfig lives elsewhere.
|
|
941
|
+
*
|
|
942
|
+
* Strips build-only compiler options (`outDir`, `rootDir`, `declaration`,
|
|
943
|
+
* etc.) that would conflict with the broad `include` and are irrelevant
|
|
944
|
+
* for SCIP analysis. Preserves all type-checking options (`strict`,
|
|
945
|
+
* `paths`, `baseUrl`, etc.) so SCIP still resolves types correctly.
|
|
946
|
+
*
|
|
947
|
+
* Returns the path to the temp file, or `null` if no tsconfig exists.
|
|
948
|
+
*/
|
|
949
|
+
export function createLoreScipTsconfig(rootDir) {
|
|
950
|
+
const log = getLogger();
|
|
951
|
+
const tsconfigPath = join(rootDir, 'tsconfig.json');
|
|
952
|
+
if (!existsSync(tsconfigPath))
|
|
953
|
+
return null;
|
|
954
|
+
try {
|
|
955
|
+
const raw = JSON.parse(readFileSync(tsconfigPath, 'utf8'));
|
|
956
|
+
const compilerOptions = { ...(raw.compilerOptions ?? {}) };
|
|
957
|
+
// Strip build-only fields
|
|
958
|
+
for (const field of TSCONFIG_BUILD_ONLY_FIELDS) {
|
|
959
|
+
delete compilerOptions[field];
|
|
960
|
+
}
|
|
961
|
+
// Use absolute paths so the tsconfig works from tmpdir
|
|
962
|
+
const absRoot = resolve(rootDir);
|
|
963
|
+
const loreTsconfig = {
|
|
964
|
+
compilerOptions,
|
|
965
|
+
include: [join(absRoot, '**/*.ts'), join(absRoot, '**/*.tsx')],
|
|
966
|
+
exclude: (raw.exclude ?? ['node_modules']).map((e) => join(absRoot, e)),
|
|
967
|
+
};
|
|
968
|
+
const outPath = join(tmpdir(), `lore-scip-${crypto.randomUUID()}.json`);
|
|
969
|
+
fs.writeFileSync(outPath, JSON.stringify(loreTsconfig, null, 2));
|
|
970
|
+
log.debug('scip', `generated broad tsconfig for SCIP: ${outPath}`);
|
|
971
|
+
return outPath;
|
|
972
|
+
}
|
|
973
|
+
catch {
|
|
752
974
|
return null;
|
|
753
975
|
}
|
|
754
976
|
}
|
|
@@ -769,6 +991,10 @@ function classifyScipReference(scipSymbol) {
|
|
|
769
991
|
// Method/function: ends with (). or (+N). (with disambiguator)
|
|
770
992
|
if (/\(\+?\d*\)\.$/.test(scipSymbol))
|
|
771
993
|
return 'call';
|
|
994
|
+
// scip-clang C/C++ functions: term descriptors ending with `(hexhash).`
|
|
995
|
+
// E.g., `$ parse_analyze_fixedparams(39d222e79bbfb7c0).`
|
|
996
|
+
if (/\([0-9a-f]{8,}\)\.$/.test(scipSymbol))
|
|
997
|
+
return 'call';
|
|
772
998
|
// Type: ends with #
|
|
773
999
|
if (scipSymbol.endsWith('#'))
|
|
774
1000
|
return 'type';
|
|
@@ -871,32 +1097,65 @@ const SCIP_LANG_MAP = {
|
|
|
871
1097
|
go: 'go',
|
|
872
1098
|
dart: 'dart',
|
|
873
1099
|
};
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
1100
|
+
/**
|
|
1101
|
+
* Quick scan of the project root to detect which SCIP-supported languages
|
|
1102
|
+
* are present. Checks for telltale file extensions and build files.
|
|
1103
|
+
* Only scans top-level + one directory deep to stay fast.
|
|
1104
|
+
*/
|
|
1105
|
+
function detectProjectLanguages(rootDir) {
|
|
1106
|
+
const found = new Set();
|
|
1107
|
+
const langIndicators = {
|
|
1108
|
+
typescript: ['tsconfig.json', 'package.json'],
|
|
1109
|
+
python: ['setup.py', 'pyproject.toml', 'requirements.txt'],
|
|
1110
|
+
java: ['pom.xml', 'build.gradle', 'build.gradle.kts'],
|
|
1111
|
+
rust: ['Cargo.toml'],
|
|
1112
|
+
c: ['Makefile', 'CMakeLists.txt', 'meson.build', 'configure', 'configure.ac'],
|
|
1113
|
+
cpp: ['CMakeLists.txt', 'meson.build'],
|
|
1114
|
+
csharp: ['.csproj', '.sln'],
|
|
1115
|
+
ruby: ['Gemfile'],
|
|
1116
|
+
go: ['go.mod'],
|
|
1117
|
+
php: ['composer.json'],
|
|
1118
|
+
dart: ['pubspec.yaml'],
|
|
1119
|
+
};
|
|
1120
|
+
// Check for language indicator files at the root
|
|
1121
|
+
for (const [lang, indicators] of Object.entries(langIndicators)) {
|
|
1122
|
+
for (const indicator of indicators) {
|
|
1123
|
+
if (existsSync(join(rootDir, indicator))) {
|
|
1124
|
+
found.add(lang);
|
|
1125
|
+
break;
|
|
1126
|
+
}
|
|
1127
|
+
}
|
|
1128
|
+
}
|
|
1129
|
+
// Quick extension scan: read first-level directory entries
|
|
1130
|
+
try {
|
|
1131
|
+
const entries = fs.readdirSync(rootDir, { withFileTypes: true });
|
|
1132
|
+
for (const entry of entries) {
|
|
1133
|
+
if (entry.isFile()) {
|
|
1134
|
+
const ext = entry.name.slice(entry.name.lastIndexOf('.')).toLowerCase();
|
|
1135
|
+
const lang = EXT_TO_LANG[ext];
|
|
1136
|
+
if (lang && SCIP_SUPPORTED_LANGUAGES.has(lang))
|
|
1137
|
+
found.add(lang);
|
|
1138
|
+
}
|
|
1139
|
+
else if (entry.isDirectory() && entry.name !== 'node_modules' && !entry.name.startsWith('.')) {
|
|
1140
|
+
// One level deep
|
|
1141
|
+
try {
|
|
1142
|
+
const subEntries = fs.readdirSync(join(rootDir, entry.name), { withFileTypes: true });
|
|
1143
|
+
for (const sub of subEntries.slice(0, 50)) { // Limit to avoid scanning huge dirs
|
|
1144
|
+
if (sub.isFile()) {
|
|
1145
|
+
const ext = sub.name.slice(sub.name.lastIndexOf('.')).toLowerCase();
|
|
1146
|
+
const lang = EXT_TO_LANG[ext];
|
|
1147
|
+
if (lang && SCIP_SUPPORTED_LANGUAGES.has(lang))
|
|
1148
|
+
found.add(lang);
|
|
1149
|
+
}
|
|
1150
|
+
}
|
|
1151
|
+
}
|
|
1152
|
+
catch { /* ignore permission errors */ }
|
|
1153
|
+
}
|
|
1154
|
+
}
|
|
1155
|
+
}
|
|
1156
|
+
catch { /* ignore */ }
|
|
1157
|
+
return found;
|
|
1158
|
+
}
|
|
900
1159
|
/**
|
|
901
1160
|
* Determine the Lore language for a SCIP document.
|
|
902
1161
|
*
|
|
@@ -921,4 +1180,4 @@ function inferLoreLanguage(scipLanguage, relativePath) {
|
|
|
921
1180
|
// ─── Test-visible helpers ───────────────────────────────────────────────────
|
|
922
1181
|
// Exported for unit testing only. Not part of the public API.
|
|
923
1182
|
export { estimateSymbolEndLine as _estimateSymbolEndLine, inferTypeRefKind as _inferTypeRefKind, extractImportPathFromSource as _extractImportPathFromSource, inferKindFromScipSymbol as _inferKindFromScipSymbol, inferLoreLanguage as _inferLoreLanguage, classifyScipReference as _classifyScipReference, extractNameFromScipSymbol as _extractNameFromScipSymbol, };
|
|
924
|
-
//# sourceMappingURL=scip-
|
|
1183
|
+
//# sourceMappingURL=scip-indexer.js.map
|