@shapeshift-labs/frontier-lang-compiler 0.2.22 → 0.2.24
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 +49 -1
- package/bench/smoke.mjs +74 -1
- package/dist/index.d.ts +63 -0
- package/dist/index.js +1467 -5
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -262,6 +262,10 @@ export const NativeImportRegionTaxonomyKinds = Object.freeze([
|
|
|
262
262
|
'call',
|
|
263
263
|
'type',
|
|
264
264
|
'effect',
|
|
265
|
+
'property',
|
|
266
|
+
'config',
|
|
267
|
+
'content',
|
|
268
|
+
'route',
|
|
265
269
|
'generatedOutput'
|
|
266
270
|
]);
|
|
267
271
|
|
|
@@ -329,6 +333,14 @@ export const NativeImportLanguageProfiles = Object.freeze([
|
|
|
329
333
|
nativeImportLanguageProfile('r', { aliases: ['R'], extensions: ['.r', '.R'], parserAdapters: ['r-parser', 'tree-sitter'], lossKinds: ['declarationOnlyCoverage', 'opaqueNative', 'dynamicRuntime', 'sourceMapApproximation', 'sourcePreservation'] })
|
|
330
334
|
]);
|
|
331
335
|
|
|
336
|
+
export const ExternalSemanticIndexFormats = Object.freeze([
|
|
337
|
+
'frontier-semantic-index',
|
|
338
|
+
'scip',
|
|
339
|
+
'lsif',
|
|
340
|
+
'lsp',
|
|
341
|
+
'semanticdb'
|
|
342
|
+
]);
|
|
343
|
+
|
|
332
344
|
export function normalizeCompileTarget(target) {
|
|
333
345
|
const normalized = String(target ?? 'typescript').toLowerCase();
|
|
334
346
|
const canonical = canonicalTargets[normalized] ?? normalized;
|
|
@@ -544,6 +556,1216 @@ export function compileNativeSource(input, options = {}) {
|
|
|
544
556
|
};
|
|
545
557
|
}
|
|
546
558
|
|
|
559
|
+
export function importExternalSemanticIndex(input) {
|
|
560
|
+
const payload = input?.payload ?? input?.semanticIndex ?? input;
|
|
561
|
+
if (!payload || typeof payload !== 'object') {
|
|
562
|
+
throw new Error('importExternalSemanticIndex requires a payload object');
|
|
563
|
+
}
|
|
564
|
+
const format = normalizeExternalSemanticIndexFormat(input?.format ?? inferExternalSemanticIndexFormat(payload));
|
|
565
|
+
const idPart = idFragment(input?.id ?? input?.sourcePath ?? input?.projectRoot ?? format);
|
|
566
|
+
const context = {
|
|
567
|
+
format,
|
|
568
|
+
idPart,
|
|
569
|
+
language: normalizeExternalSemanticLanguage(input?.language ?? payload.language ?? payload.languageId),
|
|
570
|
+
sourcePath: input?.sourcePath ?? payload.sourcePath ?? payload.uri ?? payload.path,
|
|
571
|
+
sourceHash: input?.sourceHash ?? payload.sourceHash ?? payload.md5,
|
|
572
|
+
projectRoot: input?.projectRoot ?? payload.projectRoot ?? payload.project_root ?? payload.metadata?.projectRoot ?? payload.metadata?.project_root,
|
|
573
|
+
parser: input?.parser ?? `${format}.external-semantic-index`,
|
|
574
|
+
metadata: input?.metadata ?? {}
|
|
575
|
+
};
|
|
576
|
+
const normalized = normalizeExternalSemanticIndexPayload(payload, context);
|
|
577
|
+
const evidence = attachNativeImportLossSummary(
|
|
578
|
+
uniqueByEvidenceId([...(normalized.evidence ?? []), ...(input?.evidence ?? [])]),
|
|
579
|
+
summarizeNativeImportLosses(normalized.losses ?? [], {
|
|
580
|
+
evidence: [...(normalized.evidence ?? []), ...(input?.evidence ?? [])],
|
|
581
|
+
parser: context.parser,
|
|
582
|
+
scanKind: 'external-semantic-index',
|
|
583
|
+
semanticStatus: normalized.semanticStatus
|
|
584
|
+
})
|
|
585
|
+
);
|
|
586
|
+
const losses = normalizeNativeLossRecords(normalized.losses ?? []);
|
|
587
|
+
const semanticIndex = createSemanticIndexRecord({
|
|
588
|
+
id: input?.semanticIndexId ?? normalized.semanticIndexId ?? `index_${idPart}_${idFragment(format)}`,
|
|
589
|
+
repository: normalized.repository,
|
|
590
|
+
documents: normalized.documents,
|
|
591
|
+
symbols: normalized.symbols,
|
|
592
|
+
occurrences: normalized.occurrences,
|
|
593
|
+
relations: normalized.relations,
|
|
594
|
+
facts: normalized.facts,
|
|
595
|
+
evidence,
|
|
596
|
+
metadata: {
|
|
597
|
+
format,
|
|
598
|
+
parser: context.parser,
|
|
599
|
+
source: 'external-semantic-index',
|
|
600
|
+
projectRoot: context.projectRoot,
|
|
601
|
+
semanticStatus: normalized.semanticStatus,
|
|
602
|
+
...normalized.metadata,
|
|
603
|
+
...context.metadata
|
|
604
|
+
}
|
|
605
|
+
});
|
|
606
|
+
const sourceMapMappings = externalSemanticSourceMapMappings(semanticIndex, {
|
|
607
|
+
evidence,
|
|
608
|
+
losses,
|
|
609
|
+
sourcePath: context.sourcePath,
|
|
610
|
+
sourceHash: context.sourceHash
|
|
611
|
+
});
|
|
612
|
+
const sourceMaps = sourceMapMappings.length
|
|
613
|
+
? [createSourceMapRecord({
|
|
614
|
+
id: input?.sourceMapId ?? `source_map_${idPart}_${idFragment(format)}`,
|
|
615
|
+
sourcePath: context.sourcePath,
|
|
616
|
+
sourceHash: context.sourceHash,
|
|
617
|
+
semanticIndexId: semanticIndex.id,
|
|
618
|
+
mappings: sourceMapMappings,
|
|
619
|
+
evidence,
|
|
620
|
+
metadata: {
|
|
621
|
+
format,
|
|
622
|
+
source: 'external-semantic-index',
|
|
623
|
+
projectRoot: context.projectRoot
|
|
624
|
+
}
|
|
625
|
+
})]
|
|
626
|
+
: [];
|
|
627
|
+
const document = createDocument({
|
|
628
|
+
id: input?.documentId ?? `document_${idPart}_${idFragment(format)}`,
|
|
629
|
+
name: input?.documentName ?? context.sourcePath ?? `${format} semantic index`,
|
|
630
|
+
nodes: [],
|
|
631
|
+
rootIds: [],
|
|
632
|
+
metadata: {
|
|
633
|
+
sourceLanguage: context.language,
|
|
634
|
+
sourcePath: context.sourcePath,
|
|
635
|
+
sourceHash: context.sourceHash,
|
|
636
|
+
semanticStatus: normalized.semanticStatus,
|
|
637
|
+
externalSemanticIndexFormat: format
|
|
638
|
+
}
|
|
639
|
+
});
|
|
640
|
+
const universalAst = createUniversalAstEnvelope({
|
|
641
|
+
id: input?.universalAstId ?? `universal_ast_${idPart}_${idFragment(format)}`,
|
|
642
|
+
document,
|
|
643
|
+
nativeSources: [],
|
|
644
|
+
semanticIndex,
|
|
645
|
+
sourceMaps,
|
|
646
|
+
losses,
|
|
647
|
+
evidence,
|
|
648
|
+
metadata: {
|
|
649
|
+
sourceLanguage: context.language,
|
|
650
|
+
sourcePath: context.sourcePath,
|
|
651
|
+
sourceHash: context.sourceHash,
|
|
652
|
+
projectRoot: context.projectRoot,
|
|
653
|
+
externalSemanticIndexFormat: format,
|
|
654
|
+
semanticStatus: normalized.semanticStatus,
|
|
655
|
+
...input?.universalAstMetadata
|
|
656
|
+
}
|
|
657
|
+
});
|
|
658
|
+
const readiness = classifyNativeImportReadiness(losses, {
|
|
659
|
+
evidence,
|
|
660
|
+
parser: context.parser,
|
|
661
|
+
scanKind: 'external-semantic-index',
|
|
662
|
+
semanticStatus: normalized.semanticStatus
|
|
663
|
+
});
|
|
664
|
+
return {
|
|
665
|
+
kind: 'frontier.lang.externalSemanticIndexImport',
|
|
666
|
+
version: 1,
|
|
667
|
+
id: input?.id ?? `external_semantic_index_${idPart}_${idFragment(format)}`,
|
|
668
|
+
format,
|
|
669
|
+
language: context.language,
|
|
670
|
+
sourcePath: context.sourcePath,
|
|
671
|
+
projectRoot: context.projectRoot,
|
|
672
|
+
semanticIndex,
|
|
673
|
+
universalAst,
|
|
674
|
+
sourceMaps,
|
|
675
|
+
losses,
|
|
676
|
+
evidence,
|
|
677
|
+
readiness,
|
|
678
|
+
summary: {
|
|
679
|
+
documents: semanticIndex.documents.length,
|
|
680
|
+
symbols: semanticIndex.symbols.length,
|
|
681
|
+
occurrences: semanticIndex.occurrences.length,
|
|
682
|
+
relations: semanticIndex.relations.length,
|
|
683
|
+
facts: semanticIndex.facts.length,
|
|
684
|
+
sourceMapMappings: sourceMaps.reduce((sum, sourceMap) => sum + (sourceMap.mappings?.length ?? 0), 0),
|
|
685
|
+
losses: losses.length,
|
|
686
|
+
readiness: readiness.readiness
|
|
687
|
+
},
|
|
688
|
+
metadata: {
|
|
689
|
+
format,
|
|
690
|
+
parser: context.parser,
|
|
691
|
+
semanticStatus: normalized.semanticStatus,
|
|
692
|
+
payloadHash: hashSemanticValue(payload),
|
|
693
|
+
...normalized.metadata,
|
|
694
|
+
...context.metadata
|
|
695
|
+
}
|
|
696
|
+
};
|
|
697
|
+
}
|
|
698
|
+
|
|
699
|
+
function normalizeExternalSemanticIndexFormat(format) {
|
|
700
|
+
const normalized = String(format ?? 'frontier-semantic-index').trim().toLowerCase();
|
|
701
|
+
const aliases = {
|
|
702
|
+
frontier: 'frontier-semantic-index',
|
|
703
|
+
'frontier.semantic-index': 'frontier-semantic-index',
|
|
704
|
+
'frontier.lang.semanticindex': 'frontier-semantic-index',
|
|
705
|
+
scipindex: 'scip',
|
|
706
|
+
'sourcegraph-scip': 'scip',
|
|
707
|
+
lsp: 'lsp',
|
|
708
|
+
'language-server-protocol': 'lsp',
|
|
709
|
+
semanticdb: 'semanticdb',
|
|
710
|
+
'scala-semanticdb': 'semanticdb'
|
|
711
|
+
};
|
|
712
|
+
return aliases[normalized] ?? normalized;
|
|
713
|
+
}
|
|
714
|
+
|
|
715
|
+
function inferExternalSemanticIndexFormat(payload) {
|
|
716
|
+
if (payload.kind === 'frontier.lang.semanticIndex') return 'frontier-semantic-index';
|
|
717
|
+
if (Array.isArray(payload) && payload.some((entry) => entry?.type === 'vertex' || entry?.type === 'edge')) return 'lsif';
|
|
718
|
+
if (Array.isArray(payload.vertices) || Array.isArray(payload.edges)) return 'lsif';
|
|
719
|
+
if (Array.isArray(payload.documents) && payload.documents.some((document) => document?.relative_path ?? document?.relativePath)) return 'scip';
|
|
720
|
+
if (payload.metadata?.project_root || payload.metadata?.projectRoot || payload.external_symbols || payload.externalSymbols) return 'scip';
|
|
721
|
+
if (Array.isArray(payload.documents) && payload.documents.some((document) => document?.symbols && document?.occurrences && (document?.uri || document?.md5 || document?.schema))) return 'semanticdb';
|
|
722
|
+
if (Array.isArray(payload.documentSymbols) || Array.isArray(payload.symbols) || payload.semanticTokens || payload.location || payload.range) return 'lsp';
|
|
723
|
+
return 'frontier-semantic-index';
|
|
724
|
+
}
|
|
725
|
+
|
|
726
|
+
function normalizeExternalSemanticIndexPayload(payload, context) {
|
|
727
|
+
if (context.format === 'frontier-semantic-index') return normalizeFrontierSemanticIndexPayload(payload, context);
|
|
728
|
+
if (context.format === 'scip') return normalizeScipPayload(payload, context);
|
|
729
|
+
if (context.format === 'lsif') return normalizeLsifPayload(payload, context);
|
|
730
|
+
if (context.format === 'lsp') return normalizeLspPayload(payload, context);
|
|
731
|
+
if (context.format === 'semanticdb') return normalizeSemanticDbPayload(payload, context);
|
|
732
|
+
return normalizeGenericExternalSemanticIndexPayload(payload, context);
|
|
733
|
+
}
|
|
734
|
+
|
|
735
|
+
function externalSemanticBase(context, metadata = {}) {
|
|
736
|
+
return {
|
|
737
|
+
repository: context.projectRoot ? { root: context.projectRoot } : undefined,
|
|
738
|
+
documents: [],
|
|
739
|
+
symbols: [],
|
|
740
|
+
occurrences: [],
|
|
741
|
+
relations: [],
|
|
742
|
+
facts: [],
|
|
743
|
+
evidence: [externalSemanticEvidence(context, 'passed', `Imported ${context.format} semantic index payload.`)],
|
|
744
|
+
losses: [externalSemanticCoverageLoss(context)],
|
|
745
|
+
semanticStatus: 'external-semantic-index',
|
|
746
|
+
metadata
|
|
747
|
+
};
|
|
748
|
+
}
|
|
749
|
+
|
|
750
|
+
function normalizeFrontierSemanticIndexPayload(payload, context) {
|
|
751
|
+
const result = externalSemanticBase(context, { sourceFormat: payload.kind ?? 'frontier.lang.semanticIndex' });
|
|
752
|
+
result.repository = payload.repository ?? result.repository;
|
|
753
|
+
result.documents = normalizeArray(payload.documents).map((document, index) => externalDocument(document, context, index));
|
|
754
|
+
result.symbols = normalizeArray(payload.symbols).map((symbol, index) => externalSymbol(symbol, context, index));
|
|
755
|
+
result.occurrences = normalizeArray(payload.occurrences).map((occurrence, index) => externalOccurrence(occurrence, context, index));
|
|
756
|
+
result.relations = normalizeArray(payload.relations).map((relation, index) => externalRelation(relation, context, index));
|
|
757
|
+
result.facts = normalizeArray(payload.facts).map((fact, index) => externalFact(fact, context, index));
|
|
758
|
+
result.evidence = uniqueByEvidenceId([...(payload.evidence ?? []), ...result.evidence]);
|
|
759
|
+
if (payload.metadata) result.metadata = { ...result.metadata, ...payload.metadata };
|
|
760
|
+
return withExternalEmptyLoss(result, context);
|
|
761
|
+
}
|
|
762
|
+
|
|
763
|
+
function normalizeScipPayload(payload, context) {
|
|
764
|
+
const result = externalSemanticBase(context, { sourceFormat: 'scip' });
|
|
765
|
+
const metadata = payload.metadata ?? {};
|
|
766
|
+
const projectRoot = context.projectRoot ?? metadata.project_root ?? metadata.projectRoot;
|
|
767
|
+
result.repository = projectRoot ? { root: projectRoot } : undefined;
|
|
768
|
+
const documents = normalizeArray(payload.documents);
|
|
769
|
+
for (const [documentIndex, document] of documents.entries()) {
|
|
770
|
+
const path = document.relative_path ?? document.relativePath ?? document.path ?? context.sourcePath ?? `scip-document-${documentIndex + 1}`;
|
|
771
|
+
const language = normalizeExternalSemanticLanguage(document.language ?? context.language);
|
|
772
|
+
const documentId = document.id ?? `doc_${idFragment(path)}`;
|
|
773
|
+
result.documents.push({
|
|
774
|
+
id: documentId,
|
|
775
|
+
path,
|
|
776
|
+
language,
|
|
777
|
+
sourceHash: document.sourceHash ?? document.md5 ?? context.sourceHash,
|
|
778
|
+
metadata: {
|
|
779
|
+
format: 'scip',
|
|
780
|
+
projectRoot,
|
|
781
|
+
textDocumentEncoding: metadata.text_document_encoding ?? metadata.textDocumentEncoding,
|
|
782
|
+
documentIndex
|
|
783
|
+
}
|
|
784
|
+
});
|
|
785
|
+
const documentSymbols = new Map();
|
|
786
|
+
for (const symbolInfo of [...normalizeArray(document.symbols), ...normalizeArray(payload.external_symbols ?? payload.externalSymbols)]) {
|
|
787
|
+
const symbolId = scipSymbolId(symbolInfo.symbol, context, normalizeArray(document.symbols).includes(symbolInfo) ? documentId : undefined);
|
|
788
|
+
if (!symbolId || documentSymbols.has(symbolId)) continue;
|
|
789
|
+
documentSymbols.set(symbolId, true);
|
|
790
|
+
result.symbols.push({
|
|
791
|
+
id: symbolId,
|
|
792
|
+
scheme: 'scip',
|
|
793
|
+
name: symbolInfo.display_name ?? symbolInfo.displayName ?? nameFromExternalSymbol(symbolInfo.symbol),
|
|
794
|
+
kind: normalizeExternalSymbolKind(symbolInfo.kind),
|
|
795
|
+
language,
|
|
796
|
+
signatureHash: hashSemanticValue([symbolInfo.symbol, symbolInfo.signature_documentation ?? symbolInfo.signatureDocumentation]),
|
|
797
|
+
metadata: {
|
|
798
|
+
format: 'scip',
|
|
799
|
+
rawSymbol: symbolInfo.symbol,
|
|
800
|
+
documentation: symbolInfo.documentation,
|
|
801
|
+
enclosingSymbol: symbolInfo.enclosing_symbol ?? symbolInfo.enclosingSymbol,
|
|
802
|
+
external: !normalizeArray(document.symbols).includes(symbolInfo)
|
|
803
|
+
}
|
|
804
|
+
});
|
|
805
|
+
result.facts.push(...scipSymbolFacts(symbolInfo, symbolId));
|
|
806
|
+
result.relations.push(...scipRelationshipRelations(symbolInfo, symbolId, context));
|
|
807
|
+
}
|
|
808
|
+
for (const [occurrenceIndex, occurrence] of normalizeArray(document.occurrences).entries()) {
|
|
809
|
+
const symbolId = scipSymbolId(occurrence.symbol, context, documentId);
|
|
810
|
+
if (!symbolId) continue;
|
|
811
|
+
const role = scipOccurrenceRole(occurrence.symbol_roles ?? occurrence.symbolRoles);
|
|
812
|
+
if (!documentSymbols.has(symbolId)) {
|
|
813
|
+
documentSymbols.set(symbolId, true);
|
|
814
|
+
result.symbols.push({
|
|
815
|
+
id: symbolId,
|
|
816
|
+
scheme: 'scip',
|
|
817
|
+
name: nameFromExternalSymbol(occurrence.symbol),
|
|
818
|
+
kind: scipSyntaxKind(occurrence.syntax_kind ?? occurrence.syntaxKind),
|
|
819
|
+
language,
|
|
820
|
+
metadata: { format: 'scip', rawSymbol: occurrence.symbol, inferredFromOccurrence: true }
|
|
821
|
+
});
|
|
822
|
+
}
|
|
823
|
+
result.occurrences.push({
|
|
824
|
+
id: occurrence.id ?? `occ_${idFragment(documentId)}_${occurrenceIndex + 1}`,
|
|
825
|
+
documentId,
|
|
826
|
+
symbolId,
|
|
827
|
+
role,
|
|
828
|
+
span: spanFromScipOccurrence(occurrence, path, context.sourceHash),
|
|
829
|
+
metadata: {
|
|
830
|
+
format: 'scip',
|
|
831
|
+
symbolRoles: occurrence.symbol_roles ?? occurrence.symbolRoles,
|
|
832
|
+
roleSet: scipOccurrenceRoleSet(occurrence.symbol_roles ?? occurrence.symbolRoles),
|
|
833
|
+
syntaxKind: occurrence.syntax_kind ?? occurrence.syntaxKind,
|
|
834
|
+
overrideDocumentation: occurrence.override_documentation ?? occurrence.overrideDocumentation
|
|
835
|
+
}
|
|
836
|
+
});
|
|
837
|
+
for (const diagnostic of normalizeArray(occurrence.diagnostics)) {
|
|
838
|
+
const scopedDiagnostic = { ...diagnostic, range: diagnostic.range ?? occurrence.range };
|
|
839
|
+
result.facts.push(externalDiagnosticFact(scopedDiagnostic, context, documentId, path, result.facts.length));
|
|
840
|
+
result.losses.push(externalDiagnosticLoss(scopedDiagnostic, context, path));
|
|
841
|
+
}
|
|
842
|
+
if (scipOccurrenceRoleSet(occurrence.symbol_roles ?? occurrence.symbolRoles).includes('generated')) {
|
|
843
|
+
result.losses.push({
|
|
844
|
+
id: `loss_${idFragment(documentId)}_${occurrenceIndex + 1}_generated_scip_occurrence`,
|
|
845
|
+
severity: 'warning',
|
|
846
|
+
phase: 'index',
|
|
847
|
+
sourceFormat: 'scip',
|
|
848
|
+
kind: 'generatedCode',
|
|
849
|
+
message: 'SCIP occurrence is marked generated; merge admission should review generated/source ownership before applying patches.',
|
|
850
|
+
span: spanFromScipOccurrence(occurrence, path, context.sourceHash),
|
|
851
|
+
semanticSymbolId: symbolId,
|
|
852
|
+
metadata: { format: 'scip', symbolRoles: occurrence.symbol_roles ?? occurrence.symbolRoles }
|
|
853
|
+
});
|
|
854
|
+
}
|
|
855
|
+
}
|
|
856
|
+
}
|
|
857
|
+
return withExternalEmptyLoss(result, context);
|
|
858
|
+
}
|
|
859
|
+
|
|
860
|
+
function normalizeLsifPayload(payload, context) {
|
|
861
|
+
const result = externalSemanticBase(context, { sourceFormat: 'lsif' });
|
|
862
|
+
const records = Array.isArray(payload) ? payload : [...normalizeArray(payload.vertices), ...normalizeArray(payload.edges)];
|
|
863
|
+
const vertices = new Map(records.filter((record) => record?.type === 'vertex').map((record) => [record.id, record]));
|
|
864
|
+
const edges = records.filter((record) => record?.type === 'edge');
|
|
865
|
+
const documentByVertex = new Map();
|
|
866
|
+
const documentIdByRange = new Map();
|
|
867
|
+
const resultSetByRange = new Map();
|
|
868
|
+
const monikerByOut = new Map();
|
|
869
|
+
const definitionRangeIds = new Set();
|
|
870
|
+
for (const vertex of vertices.values()) {
|
|
871
|
+
if (vertex.label === 'document') {
|
|
872
|
+
const path = uriToPath(vertex.uri) ?? vertex.uri ?? context.sourcePath ?? `lsif-document-${result.documents.length + 1}`;
|
|
873
|
+
const documentId = `doc_${idFragment(vertex.id ?? path)}`;
|
|
874
|
+
documentByVertex.set(vertex.id, { id: documentId, path, language: normalizeExternalSemanticLanguage(vertex.languageId ?? context.language) });
|
|
875
|
+
result.documents.push({
|
|
876
|
+
id: documentId,
|
|
877
|
+
path,
|
|
878
|
+
language: normalizeExternalSemanticLanguage(vertex.languageId ?? context.language),
|
|
879
|
+
metadata: { format: 'lsif', vertexId: vertex.id, uri: vertex.uri }
|
|
880
|
+
});
|
|
881
|
+
}
|
|
882
|
+
if (vertex.label === 'moniker') monikerByOut.set(vertex.id, vertex);
|
|
883
|
+
}
|
|
884
|
+
for (const edge of edges) {
|
|
885
|
+
if (edge.label === 'next') resultSetByRange.set(edge.outV, edge.inV);
|
|
886
|
+
if (edge.label === 'moniker') monikerByOut.set(edge.outV, vertices.get(edge.inV) ?? edge);
|
|
887
|
+
if (edge.label === 'contains') {
|
|
888
|
+
const document = documentByVertex.get(edge.outV);
|
|
889
|
+
if (document) {
|
|
890
|
+
for (const rangeId of normalizeArray(edge.inVs ?? edge.inV)) {
|
|
891
|
+
documentIdByRange.set(rangeId, document.id);
|
|
892
|
+
}
|
|
893
|
+
}
|
|
894
|
+
}
|
|
895
|
+
if (edge.label === 'item' && (edge.property === 'definitions' || edge.property === 'declarations')) {
|
|
896
|
+
for (const rangeId of normalizeArray(edge.inVs ?? edge.inV)) definitionRangeIds.add(rangeId);
|
|
897
|
+
}
|
|
898
|
+
}
|
|
899
|
+
const documentIds = result.documents.map((document) => document.id);
|
|
900
|
+
const defaultDocument = result.documents[0] ?? {
|
|
901
|
+
id: `doc_${idFragment(context.sourcePath ?? 'lsif')}`,
|
|
902
|
+
path: context.sourcePath ?? 'lsif:memory',
|
|
903
|
+
language: context.language
|
|
904
|
+
};
|
|
905
|
+
if (!result.documents.length) result.documents.push(defaultDocument);
|
|
906
|
+
for (const [vertexId, vertex] of vertices.entries()) {
|
|
907
|
+
if (vertex.label !== 'range') continue;
|
|
908
|
+
const resultSetId = resultSetByRange.get(vertexId);
|
|
909
|
+
const moniker = monikerByOut.get(resultSetId) ?? monikerByOut.get(vertexId);
|
|
910
|
+
const symbolId = moniker?.identifier
|
|
911
|
+
? `symbol:lsif:${idFragment(moniker.scheme ?? moniker.kind ?? 'moniker')}:${idFragment(moniker.identifier)}`
|
|
912
|
+
: `symbol:lsif:${idFragment(resultSetId ?? vertexId)}`;
|
|
913
|
+
const documentId = documentIdByRange.get(vertexId) ?? documentIds[0] ?? defaultDocument.id;
|
|
914
|
+
const owningDocument = result.documents.find((document) => document.id === documentId) ?? defaultDocument;
|
|
915
|
+
const span = spanFromLspRange(vertex, owningDocument.path, context.sourceHash, 0);
|
|
916
|
+
if (!result.symbols.some((symbol) => symbol.id === symbolId)) {
|
|
917
|
+
result.symbols.push({
|
|
918
|
+
id: symbolId,
|
|
919
|
+
scheme: 'lsif',
|
|
920
|
+
name: moniker?.identifier ?? `range:${vertexId}`,
|
|
921
|
+
kind: moniker?.kind ?? 'symbol',
|
|
922
|
+
language: owningDocument.language,
|
|
923
|
+
definitionSpan: definitionRangeIds.has(vertexId) ? span : undefined,
|
|
924
|
+
metadata: { format: 'lsif', resultSetId, moniker }
|
|
925
|
+
});
|
|
926
|
+
}
|
|
927
|
+
result.occurrences.push({
|
|
928
|
+
id: `occ_${idFragment(vertexId)}`,
|
|
929
|
+
documentId,
|
|
930
|
+
symbolId,
|
|
931
|
+
role: definitionRangeIds.has(vertexId) ? 'definition' : 'reference',
|
|
932
|
+
span,
|
|
933
|
+
metadata: { format: 'lsif', vertexId, resultSetId }
|
|
934
|
+
});
|
|
935
|
+
}
|
|
936
|
+
for (const edge of edges) {
|
|
937
|
+
if (edge.label === 'textDocument/definition' || edge.label === 'textDocument/references' || edge.label === 'textDocument/declaration') {
|
|
938
|
+
result.relations.push({
|
|
939
|
+
id: `rel_${idFragment(edge.id ?? `${edge.outV}_${edge.inV}_${edge.label}`)}`,
|
|
940
|
+
sourceId: `lsif:${edge.outV}`,
|
|
941
|
+
predicate: edge.label,
|
|
942
|
+
targetId: `lsif:${edge.inV}`,
|
|
943
|
+
metadata: { format: 'lsif', edge }
|
|
944
|
+
});
|
|
945
|
+
}
|
|
946
|
+
}
|
|
947
|
+
return withExternalEmptyLoss(result, context);
|
|
948
|
+
}
|
|
949
|
+
|
|
950
|
+
function normalizeLspPayload(payload, context) {
|
|
951
|
+
const result = externalSemanticBase(context, { sourceFormat: 'lsp' });
|
|
952
|
+
const documents = normalizeLspDocuments(payload, context);
|
|
953
|
+
for (const [documentIndex, document] of documents.entries()) {
|
|
954
|
+
const sourcePath = uriToPath(document.uri) ?? document.sourcePath ?? document.path ?? context.sourcePath ?? `lsp-document-${documentIndex + 1}`;
|
|
955
|
+
const language = normalizeExternalSemanticLanguage(document.languageId ?? document.language ?? context.language);
|
|
956
|
+
const documentId = document.id ?? `doc_${idFragment(sourcePath)}`;
|
|
957
|
+
result.documents.push({
|
|
958
|
+
id: documentId,
|
|
959
|
+
path: sourcePath,
|
|
960
|
+
language,
|
|
961
|
+
sourceHash: document.sourceHash ?? context.sourceHash,
|
|
962
|
+
metadata: { format: 'lsp', uri: document.uri, documentIndex }
|
|
963
|
+
});
|
|
964
|
+
const symbols = normalizeArray(document.documentSymbols ?? document.symbols ?? payload.documentSymbols ?? payload.symbols);
|
|
965
|
+
for (const symbol of symbols) addLspSymbol(result, symbol, {
|
|
966
|
+
context,
|
|
967
|
+
documentId,
|
|
968
|
+
sourcePath,
|
|
969
|
+
language,
|
|
970
|
+
parentName: symbol.containerName
|
|
971
|
+
});
|
|
972
|
+
const semanticTokens = document.semanticTokens ?? payload.semanticTokens;
|
|
973
|
+
if (semanticTokens) addLspSemanticTokens(result, semanticTokens, { context, documentId, sourcePath, language });
|
|
974
|
+
for (const diagnostic of normalizeArray(document.diagnostics ?? payload.diagnostics)) {
|
|
975
|
+
result.facts.push(externalDiagnosticFact(diagnostic, context, documentId, sourcePath, result.facts.length));
|
|
976
|
+
result.losses.push(externalDiagnosticLoss(diagnostic, context, sourcePath));
|
|
977
|
+
}
|
|
978
|
+
}
|
|
979
|
+
return withExternalEmptyLoss(result, context);
|
|
980
|
+
}
|
|
981
|
+
|
|
982
|
+
function normalizeSemanticDbPayload(payload, context) {
|
|
983
|
+
const result = externalSemanticBase(context, { sourceFormat: 'semanticdb' });
|
|
984
|
+
const documents = normalizeArray(payload.documents ?? payload.textDocuments ?? payload);
|
|
985
|
+
for (const [documentIndex, document] of documents.entries()) {
|
|
986
|
+
const sourcePath = uriToPath(document.uri) ?? document.uri ?? document.path ?? context.sourcePath ?? `semanticdb-document-${documentIndex + 1}`;
|
|
987
|
+
const language = normalizeExternalSemanticLanguage(document.language ?? context.language ?? 'scala');
|
|
988
|
+
const documentId = document.id ?? `doc_${idFragment(sourcePath)}`;
|
|
989
|
+
result.documents.push({
|
|
990
|
+
id: documentId,
|
|
991
|
+
path: sourcePath,
|
|
992
|
+
language,
|
|
993
|
+
sourceHash: document.md5 ?? document.sourceHash ?? context.sourceHash,
|
|
994
|
+
metadata: { format: 'semanticdb', schema: document.schema, documentIndex }
|
|
995
|
+
});
|
|
996
|
+
for (const [symbolIndex, symbolInfo] of normalizeArray(document.symbols).entries()) {
|
|
997
|
+
const symbolId = semanticDbSymbolId(symbolInfo.symbol, context, documentId);
|
|
998
|
+
result.symbols.push({
|
|
999
|
+
id: symbolId,
|
|
1000
|
+
scheme: 'semanticdb',
|
|
1001
|
+
name: symbolInfo.display_name ?? symbolInfo.displayName ?? nameFromExternalSymbol(symbolInfo.symbol),
|
|
1002
|
+
kind: normalizeExternalSymbolKind(symbolInfo.kind),
|
|
1003
|
+
language,
|
|
1004
|
+
signatureHash: hashSemanticValue(symbolInfo.signature ?? symbolInfo.signature_documentation ?? symbolInfo.signatureDocumentation ?? symbolInfo),
|
|
1005
|
+
metadata: { format: 'semanticdb', symbolIndex, rawSymbol: symbolInfo.symbol, properties: symbolInfo.properties }
|
|
1006
|
+
});
|
|
1007
|
+
result.facts.push(...semanticDbSymbolFacts(symbolInfo, symbolId));
|
|
1008
|
+
}
|
|
1009
|
+
for (const [occurrenceIndex, occurrence] of normalizeArray(document.occurrences).entries()) {
|
|
1010
|
+
const symbolId = semanticDbSymbolId(occurrence.symbol, context, documentId);
|
|
1011
|
+
if (!result.symbols.some((symbol) => symbol.id === symbolId)) {
|
|
1012
|
+
result.symbols.push({
|
|
1013
|
+
id: symbolId,
|
|
1014
|
+
scheme: 'semanticdb',
|
|
1015
|
+
name: nameFromExternalSymbol(occurrence.symbol),
|
|
1016
|
+
kind: 'symbol',
|
|
1017
|
+
language,
|
|
1018
|
+
metadata: { format: 'semanticdb', inferredFromOccurrence: true, rawSymbol: occurrence.symbol }
|
|
1019
|
+
});
|
|
1020
|
+
}
|
|
1021
|
+
result.occurrences.push({
|
|
1022
|
+
id: occurrence.id ?? `occ_${idFragment(documentId)}_${occurrenceIndex + 1}`,
|
|
1023
|
+
documentId,
|
|
1024
|
+
symbolId,
|
|
1025
|
+
role: semanticDbOccurrenceRole(occurrence.role),
|
|
1026
|
+
span: spanFromSemanticDbRange(occurrence.range, sourcePath, document.md5 ?? context.sourceHash),
|
|
1027
|
+
metadata: { format: 'semanticdb', role: occurrence.role }
|
|
1028
|
+
});
|
|
1029
|
+
}
|
|
1030
|
+
for (const diagnostic of normalizeArray(document.diagnostics)) {
|
|
1031
|
+
result.facts.push(externalDiagnosticFact(diagnostic, context, documentId, sourcePath, result.facts.length));
|
|
1032
|
+
result.losses.push(externalDiagnosticLoss(diagnostic, context, sourcePath));
|
|
1033
|
+
}
|
|
1034
|
+
}
|
|
1035
|
+
return withExternalEmptyLoss(result, context);
|
|
1036
|
+
}
|
|
1037
|
+
|
|
1038
|
+
function normalizeGenericExternalSemanticIndexPayload(payload, context) {
|
|
1039
|
+
const result = externalSemanticBase(context, { sourceFormat: context.format, genericPayload: true });
|
|
1040
|
+
result.losses.push({
|
|
1041
|
+
id: `loss_${context.idPart}_${idFragment(context.format)}_unsupported_payload`,
|
|
1042
|
+
severity: 'warning',
|
|
1043
|
+
phase: 'index',
|
|
1044
|
+
sourceFormat: context.format,
|
|
1045
|
+
kind: 'unsupportedSemantic',
|
|
1046
|
+
message: `External semantic index format ${context.format} is not recognized; payload hash is preserved as evidence only.`,
|
|
1047
|
+
metadata: { format: context.format, payloadHash: hashSemanticValue(payload) }
|
|
1048
|
+
});
|
|
1049
|
+
return result;
|
|
1050
|
+
}
|
|
1051
|
+
|
|
1052
|
+
function normalizeArray(value) {
|
|
1053
|
+
if (value === undefined || value === null) return [];
|
|
1054
|
+
return Array.isArray(value) ? value : [value];
|
|
1055
|
+
}
|
|
1056
|
+
|
|
1057
|
+
function normalizeExternalSemanticLanguage(value) {
|
|
1058
|
+
if (value === undefined || value === null || value === '') return undefined;
|
|
1059
|
+
const raw = typeof value === 'number' ? externalLanguageNameByNumber[value] : String(value);
|
|
1060
|
+
return normalizeNativeLanguageId(raw);
|
|
1061
|
+
}
|
|
1062
|
+
|
|
1063
|
+
function externalDocument(document, context, index) {
|
|
1064
|
+
const path = document.path ?? document.uri ?? document.relative_path ?? document.relativePath ?? context.sourcePath ?? `external-document-${index + 1}`;
|
|
1065
|
+
return {
|
|
1066
|
+
id: document.id ?? `doc_${idFragment(path)}`,
|
|
1067
|
+
path: uriToPath(path) ?? path,
|
|
1068
|
+
language: normalizeExternalSemanticLanguage(document.language ?? document.languageId ?? context.language),
|
|
1069
|
+
sourceHash: document.sourceHash ?? document.md5 ?? context.sourceHash,
|
|
1070
|
+
metadata: { format: context.format, ...document.metadata }
|
|
1071
|
+
};
|
|
1072
|
+
}
|
|
1073
|
+
|
|
1074
|
+
function externalSymbol(symbol, context, index) {
|
|
1075
|
+
const id = symbol.id ?? symbol.symbolId ?? symbol.symbol ?? `symbol:${context.format}:${index + 1}`;
|
|
1076
|
+
return {
|
|
1077
|
+
...symbol,
|
|
1078
|
+
id: String(id),
|
|
1079
|
+
scheme: symbol.scheme ?? context.format,
|
|
1080
|
+
name: symbol.name ?? symbol.display_name ?? symbol.displayName ?? nameFromExternalSymbol(id),
|
|
1081
|
+
kind: normalizeExternalSymbolKind(symbol.kind),
|
|
1082
|
+
language: normalizeExternalSemanticLanguage(symbol.language ?? context.language),
|
|
1083
|
+
definitionSpan: normalizeExternalSpan(symbol.definitionSpan ?? symbol.span, context.sourcePath, context.sourceHash),
|
|
1084
|
+
metadata: { format: context.format, rawSymbol: symbol.symbol, ...symbol.metadata }
|
|
1085
|
+
};
|
|
1086
|
+
}
|
|
1087
|
+
|
|
1088
|
+
function externalOccurrence(occurrence, context, index) {
|
|
1089
|
+
return {
|
|
1090
|
+
...occurrence,
|
|
1091
|
+
id: occurrence.id ?? `occ_${context.idPart}_${index + 1}`,
|
|
1092
|
+
documentId: occurrence.documentId ?? occurrence.document_id ?? `doc_${idFragment(occurrence.path ?? context.sourcePath ?? context.format)}`,
|
|
1093
|
+
symbolId: occurrence.symbolId ?? occurrence.symbol_id ?? occurrence.symbol ?? `symbol:${context.format}:unknown`,
|
|
1094
|
+
role: normalizeExternalOccurrenceRole(occurrence.role),
|
|
1095
|
+
span: normalizeExternalSpan(occurrence.span ?? occurrence.range, occurrence.path ?? context.sourcePath, context.sourceHash),
|
|
1096
|
+
metadata: { format: context.format, ...occurrence.metadata }
|
|
1097
|
+
};
|
|
1098
|
+
}
|
|
1099
|
+
|
|
1100
|
+
function externalRelation(relation, context, index) {
|
|
1101
|
+
return {
|
|
1102
|
+
...relation,
|
|
1103
|
+
id: relation.id ?? `rel_${context.idPart}_${index + 1}`,
|
|
1104
|
+
sourceId: relation.sourceId ?? relation.source_id ?? relation.subjectId ?? relation.subject_id ?? `external:${context.format}`,
|
|
1105
|
+
predicate: relation.predicate ?? relation.label ?? relation.kind ?? 'related',
|
|
1106
|
+
targetId: relation.targetId ?? relation.target_id ?? relation.objectId ?? relation.object_id ?? relation.symbol ?? `external:${context.format}`,
|
|
1107
|
+
metadata: { format: context.format, ...relation.metadata }
|
|
1108
|
+
};
|
|
1109
|
+
}
|
|
1110
|
+
|
|
1111
|
+
function externalFact(fact, context, index) {
|
|
1112
|
+
return {
|
|
1113
|
+
...fact,
|
|
1114
|
+
id: fact.id ?? `fact_${context.idPart}_${index + 1}`,
|
|
1115
|
+
predicate: fact.predicate ?? fact.kind ?? 'externalFact',
|
|
1116
|
+
subjectId: fact.subjectId ?? fact.subject_id ?? fact.symbolId ?? fact.symbol_id ?? `external:${context.format}`,
|
|
1117
|
+
value: fact.value ?? fact.data ?? fact,
|
|
1118
|
+
metadata: { format: context.format, ...fact.metadata }
|
|
1119
|
+
};
|
|
1120
|
+
}
|
|
1121
|
+
|
|
1122
|
+
function externalSemanticEvidence(context, status, summary, metadata = {}) {
|
|
1123
|
+
return {
|
|
1124
|
+
id: `evidence_${context.idPart}_${idFragment(context.format)}_external_semantic_index`,
|
|
1125
|
+
kind: 'import',
|
|
1126
|
+
status,
|
|
1127
|
+
path: context.sourcePath,
|
|
1128
|
+
summary,
|
|
1129
|
+
metadata: {
|
|
1130
|
+
format: context.format,
|
|
1131
|
+
parser: context.parser,
|
|
1132
|
+
projectRoot: context.projectRoot,
|
|
1133
|
+
...metadata
|
|
1134
|
+
}
|
|
1135
|
+
};
|
|
1136
|
+
}
|
|
1137
|
+
|
|
1138
|
+
function externalSemanticCoverageLoss(context) {
|
|
1139
|
+
return {
|
|
1140
|
+
id: `loss_${context.idPart}_${idFragment(context.format)}_partial_semantic_index`,
|
|
1141
|
+
severity: 'info',
|
|
1142
|
+
phase: 'index',
|
|
1143
|
+
sourceFormat: context.format,
|
|
1144
|
+
kind: 'partialSemanticIndex',
|
|
1145
|
+
message: `${context.format} payload imported symbols, occurrences, and facts as external semantic evidence; full parser AST, comments, trivia, and executable semantics still require a native parser adapter.`,
|
|
1146
|
+
semanticIndexId: context.semanticIndexId,
|
|
1147
|
+
metadata: {
|
|
1148
|
+
format: context.format,
|
|
1149
|
+
parser: context.parser,
|
|
1150
|
+
source: 'external-semantic-index'
|
|
1151
|
+
}
|
|
1152
|
+
};
|
|
1153
|
+
}
|
|
1154
|
+
|
|
1155
|
+
function withExternalEmptyLoss(result, context) {
|
|
1156
|
+
if (!result.documents.length) {
|
|
1157
|
+
result.documents.push({
|
|
1158
|
+
id: `doc_${context.idPart}_${idFragment(context.format)}`,
|
|
1159
|
+
path: context.sourcePath ?? `${context.format}:memory`,
|
|
1160
|
+
language: context.language,
|
|
1161
|
+
sourceHash: context.sourceHash,
|
|
1162
|
+
metadata: { format: context.format, inferred: true }
|
|
1163
|
+
});
|
|
1164
|
+
}
|
|
1165
|
+
if (!result.symbols.length && !result.occurrences.length) {
|
|
1166
|
+
result.losses.push({
|
|
1167
|
+
id: `loss_${context.idPart}_${idFragment(context.format)}_empty_semantic_index`,
|
|
1168
|
+
severity: 'warning',
|
|
1169
|
+
phase: 'index',
|
|
1170
|
+
sourceFormat: context.format,
|
|
1171
|
+
kind: 'partialSemanticIndex',
|
|
1172
|
+
message: `${context.format} payload did not contain symbols or occurrences that Frontier can map.`,
|
|
1173
|
+
metadata: { format: context.format }
|
|
1174
|
+
});
|
|
1175
|
+
}
|
|
1176
|
+
attachExternalOwnership(result, context);
|
|
1177
|
+
result.symbols = uniqueRecordsById(result.symbols);
|
|
1178
|
+
result.occurrences = uniqueRecordsById(result.occurrences);
|
|
1179
|
+
result.relations = uniqueRecordsById(result.relations);
|
|
1180
|
+
result.facts = uniqueRecordsById(result.facts);
|
|
1181
|
+
result.losses = uniqueByLossId(result.losses);
|
|
1182
|
+
result.evidence = uniqueByEvidenceId(result.evidence);
|
|
1183
|
+
return result;
|
|
1184
|
+
}
|
|
1185
|
+
|
|
1186
|
+
function attachExternalOwnership(result, context) {
|
|
1187
|
+
const occurrencesBySymbol = new Map();
|
|
1188
|
+
for (const occurrence of result.occurrences) {
|
|
1189
|
+
if (!occurrencesBySymbol.has(occurrence.symbolId)) occurrencesBySymbol.set(occurrence.symbolId, []);
|
|
1190
|
+
occurrencesBySymbol.get(occurrence.symbolId).push(occurrence);
|
|
1191
|
+
result.relations.push({
|
|
1192
|
+
id: `rel_${idFragment(occurrence.documentId)}_${idFragment(occurrence.id)}_${idFragment(occurrence.role)}`,
|
|
1193
|
+
sourceId: occurrence.documentId,
|
|
1194
|
+
predicate: externalRelationPredicateForOccurrence(occurrence),
|
|
1195
|
+
targetId: occurrence.symbolId,
|
|
1196
|
+
metadata: {
|
|
1197
|
+
format: context.format,
|
|
1198
|
+
source: 'external-semantic-index',
|
|
1199
|
+
occurrenceId: occurrence.id,
|
|
1200
|
+
role: occurrence.role
|
|
1201
|
+
}
|
|
1202
|
+
});
|
|
1203
|
+
}
|
|
1204
|
+
result.symbols = result.symbols.map((symbol) => {
|
|
1205
|
+
const occurrences = occurrencesBySymbol.get(symbol.id) ?? [];
|
|
1206
|
+
const definition = occurrences.find((occurrence) => occurrence.role === 'definition') ?? occurrences[0];
|
|
1207
|
+
const sourceSpan = symbol.definitionSpan ?? definition?.span;
|
|
1208
|
+
const regionKind = semanticRegionKindForSymbol(symbol, undefined, undefined);
|
|
1209
|
+
const key = [
|
|
1210
|
+
'external',
|
|
1211
|
+
symbol.language ?? context.language ?? 'unknown',
|
|
1212
|
+
sourceSpan?.path ?? context.sourcePath ?? 'memory',
|
|
1213
|
+
regionKind,
|
|
1214
|
+
symbol.name ?? symbol.id
|
|
1215
|
+
].join('#');
|
|
1216
|
+
const region = {
|
|
1217
|
+
id: `region_${idFragment(key)}`,
|
|
1218
|
+
key,
|
|
1219
|
+
regionKind,
|
|
1220
|
+
granularity: 'symbol',
|
|
1221
|
+
language: symbol.language ?? context.language,
|
|
1222
|
+
documentId: definition?.documentId,
|
|
1223
|
+
sourcePath: sourceSpan?.path ?? context.sourcePath,
|
|
1224
|
+
sourceHash: context.sourceHash,
|
|
1225
|
+
symbolId: symbol.id,
|
|
1226
|
+
symbolName: symbol.name,
|
|
1227
|
+
symbolKind: symbol.kind,
|
|
1228
|
+
sourceSpan,
|
|
1229
|
+
precision: sourceSpan ? 'declaration' : 'unknown',
|
|
1230
|
+
mergePolicy: semanticRegionMergePolicy(regionKind),
|
|
1231
|
+
metadata: {
|
|
1232
|
+
format: context.format,
|
|
1233
|
+
source: 'external-semantic-index'
|
|
1234
|
+
}
|
|
1235
|
+
};
|
|
1236
|
+
result.facts.push({
|
|
1237
|
+
id: `fact_${idFragment(symbol.id)}_ownership_region`,
|
|
1238
|
+
predicate: 'semanticOwnershipRegion',
|
|
1239
|
+
subjectId: symbol.id,
|
|
1240
|
+
value: region
|
|
1241
|
+
}, {
|
|
1242
|
+
id: `fact_${idFragment(symbol.id)}_ownership_region_taxonomy`,
|
|
1243
|
+
predicate: 'semanticOwnershipRegionTaxonomy',
|
|
1244
|
+
subjectId: symbol.id,
|
|
1245
|
+
value: {
|
|
1246
|
+
regionKind: region.regionKind,
|
|
1247
|
+
granularity: region.granularity,
|
|
1248
|
+
key: region.key
|
|
1249
|
+
}
|
|
1250
|
+
});
|
|
1251
|
+
return {
|
|
1252
|
+
...symbol,
|
|
1253
|
+
definitionSpan: symbol.definitionSpan ?? definition?.span,
|
|
1254
|
+
metadata: {
|
|
1255
|
+
...symbol.metadata,
|
|
1256
|
+
ownershipRegionId: symbol.metadata?.ownershipRegionId ?? region.id,
|
|
1257
|
+
ownershipRegionKey: symbol.metadata?.ownershipRegionKey ?? region.key,
|
|
1258
|
+
ownershipRegionKind: symbol.metadata?.ownershipRegionKind ?? region.regionKind
|
|
1259
|
+
}
|
|
1260
|
+
};
|
|
1261
|
+
});
|
|
1262
|
+
}
|
|
1263
|
+
|
|
1264
|
+
function externalRelationPredicateForOccurrence(occurrence) {
|
|
1265
|
+
const role = String(occurrence.role ?? '').toLowerCase();
|
|
1266
|
+
if (role === 'definition' || role === 'declaration') return 'defines';
|
|
1267
|
+
if (role === 'import') return 'imports';
|
|
1268
|
+
if (role === 'write') return 'writes';
|
|
1269
|
+
if (role === 'read') return 'reads';
|
|
1270
|
+
return 'references';
|
|
1271
|
+
}
|
|
1272
|
+
|
|
1273
|
+
function externalSemanticSourceMapMappings(semanticIndex, context) {
|
|
1274
|
+
const symbolsById = new Map((semanticIndex.symbols ?? []).map((symbol) => [symbol.id, symbol]));
|
|
1275
|
+
const evidenceIds = (context.evidence ?? []).map((record) => record.id).filter(Boolean);
|
|
1276
|
+
const lossIds = (context.losses ?? []).map((loss) => loss.id).filter(Boolean);
|
|
1277
|
+
return (semanticIndex.occurrences ?? [])
|
|
1278
|
+
.filter((occurrence) => occurrence.span)
|
|
1279
|
+
.map((occurrence, index) => {
|
|
1280
|
+
const symbol = symbolsById.get(occurrence.symbolId);
|
|
1281
|
+
return {
|
|
1282
|
+
id: `map_${idFragment(occurrence.id ?? `${occurrence.symbolId}_${index + 1}`)}`,
|
|
1283
|
+
semanticSymbolId: occurrence.symbolId,
|
|
1284
|
+
semanticOccurrenceId: occurrence.id,
|
|
1285
|
+
sourceSpan: occurrence.span,
|
|
1286
|
+
evidenceIds,
|
|
1287
|
+
lossIds,
|
|
1288
|
+
ownershipRegionId: symbol?.metadata?.ownershipRegionId,
|
|
1289
|
+
ownershipRegionKey: symbol?.metadata?.ownershipRegionKey,
|
|
1290
|
+
ownershipRegionKind: symbol?.metadata?.ownershipRegionKind,
|
|
1291
|
+
precision: occurrence.span ? 'declaration' : 'unknown',
|
|
1292
|
+
metadata: {
|
|
1293
|
+
source: 'external-semantic-index'
|
|
1294
|
+
}
|
|
1295
|
+
};
|
|
1296
|
+
});
|
|
1297
|
+
}
|
|
1298
|
+
|
|
1299
|
+
function scipSymbolId(symbol, context, documentId) {
|
|
1300
|
+
if (!symbol) return undefined;
|
|
1301
|
+
const raw = String(symbol);
|
|
1302
|
+
if (raw.startsWith('symbol:')) return raw;
|
|
1303
|
+
const scope = /^local\b/i.test(raw) ? `${documentId ?? context.idPart}:` : '';
|
|
1304
|
+
return `symbol:scip:${idFragment(scope + raw)}`;
|
|
1305
|
+
}
|
|
1306
|
+
|
|
1307
|
+
function semanticDbSymbolId(symbol, context, documentId) {
|
|
1308
|
+
if (!symbol) return `symbol:semanticdb:${context.idPart}:unknown`;
|
|
1309
|
+
if (String(symbol).startsWith('symbol:')) return String(symbol);
|
|
1310
|
+
const scope = /^local\d+$/i.test(String(symbol)) ? `${documentId}:` : '';
|
|
1311
|
+
return `symbol:semanticdb:${idFragment(scope + symbol)}`;
|
|
1312
|
+
}
|
|
1313
|
+
|
|
1314
|
+
function nameFromExternalSymbol(symbol) {
|
|
1315
|
+
const value = String(symbol ?? 'symbol');
|
|
1316
|
+
const cleaned = value
|
|
1317
|
+
.replace(/^symbol:[^:]+:/, '')
|
|
1318
|
+
.replace(/[`'"]/g, '')
|
|
1319
|
+
.split(/[\/#.:() +]+/)
|
|
1320
|
+
.filter(Boolean)
|
|
1321
|
+
.at(-1);
|
|
1322
|
+
return cleaned || value;
|
|
1323
|
+
}
|
|
1324
|
+
|
|
1325
|
+
const externalSymbolKindByNumber = Object.freeze({
|
|
1326
|
+
1: 'array',
|
|
1327
|
+
2: 'assertion',
|
|
1328
|
+
3: 'associatedType',
|
|
1329
|
+
4: 'attribute',
|
|
1330
|
+
7: 'class',
|
|
1331
|
+
8: 'constant',
|
|
1332
|
+
9: 'constructor',
|
|
1333
|
+
11: 'enum',
|
|
1334
|
+
12: 'enumMember',
|
|
1335
|
+
13: 'event',
|
|
1336
|
+
15: 'field',
|
|
1337
|
+
16: 'file',
|
|
1338
|
+
17: 'function',
|
|
1339
|
+
21: 'interface',
|
|
1340
|
+
25: 'macro',
|
|
1341
|
+
26: 'method',
|
|
1342
|
+
28: 'message',
|
|
1343
|
+
29: 'module',
|
|
1344
|
+
30: 'namespace',
|
|
1345
|
+
35: 'package',
|
|
1346
|
+
37: 'parameter',
|
|
1347
|
+
41: 'property',
|
|
1348
|
+
42: 'protocol',
|
|
1349
|
+
49: 'struct',
|
|
1350
|
+
53: 'trait',
|
|
1351
|
+
54: 'type',
|
|
1352
|
+
55: 'typeAlias',
|
|
1353
|
+
58: 'typeParameter',
|
|
1354
|
+
61: 'variable',
|
|
1355
|
+
66: 'abstractMethod'
|
|
1356
|
+
});
|
|
1357
|
+
|
|
1358
|
+
const lspSymbolKindByNumber = Object.freeze({
|
|
1359
|
+
1: 'file',
|
|
1360
|
+
2: 'module',
|
|
1361
|
+
3: 'namespace',
|
|
1362
|
+
4: 'package',
|
|
1363
|
+
5: 'class',
|
|
1364
|
+
6: 'method',
|
|
1365
|
+
7: 'property',
|
|
1366
|
+
8: 'field',
|
|
1367
|
+
9: 'constructor',
|
|
1368
|
+
10: 'enum',
|
|
1369
|
+
11: 'interface',
|
|
1370
|
+
12: 'function',
|
|
1371
|
+
13: 'variable',
|
|
1372
|
+
14: 'constant',
|
|
1373
|
+
22: 'enumMember',
|
|
1374
|
+
23: 'struct',
|
|
1375
|
+
26: 'typeParameter'
|
|
1376
|
+
});
|
|
1377
|
+
|
|
1378
|
+
const externalLanguageNameByNumber = Object.freeze({
|
|
1379
|
+
1: 'csharp',
|
|
1380
|
+
2: 'swift',
|
|
1381
|
+
3: 'dart',
|
|
1382
|
+
4: 'kotlin',
|
|
1383
|
+
5: 'scala',
|
|
1384
|
+
6: 'java',
|
|
1385
|
+
15: 'python',
|
|
1386
|
+
16: 'ruby',
|
|
1387
|
+
17: 'elixir',
|
|
1388
|
+
18: 'erlang',
|
|
1389
|
+
19: 'php',
|
|
1390
|
+
22: 'javascript',
|
|
1391
|
+
23: 'typescript',
|
|
1392
|
+
33: 'go',
|
|
1393
|
+
34: 'c',
|
|
1394
|
+
35: 'cpp',
|
|
1395
|
+
38: 'zig',
|
|
1396
|
+
40: 'rust',
|
|
1397
|
+
44: 'haskell',
|
|
1398
|
+
54: 'r',
|
|
1399
|
+
69: 'sql'
|
|
1400
|
+
});
|
|
1401
|
+
|
|
1402
|
+
function normalizeExternalSymbolKind(kind) {
|
|
1403
|
+
if (kind === undefined || kind === null || kind === '') return 'symbol';
|
|
1404
|
+
if (typeof kind === 'number') return externalSymbolKindByNumber[kind] ?? lspSymbolKindByNumber[kind] ?? `kind${kind}`;
|
|
1405
|
+
return String(kind).replace(/^[A-Z_]+_/, '').replace(/^[A-Z]/, (letter) => letter.toLowerCase());
|
|
1406
|
+
}
|
|
1407
|
+
|
|
1408
|
+
function normalizeLspSymbolKind(kind) {
|
|
1409
|
+
if (typeof kind === 'number') return lspSymbolKindByNumber[kind] ?? `kind${kind}`;
|
|
1410
|
+
return normalizeExternalSymbolKind(kind);
|
|
1411
|
+
}
|
|
1412
|
+
|
|
1413
|
+
function scipSyntaxKind(kind) {
|
|
1414
|
+
const normalized = typeof kind === 'number' ? kind : Number(kind);
|
|
1415
|
+
if (normalized === 15 || normalized === 16) return 'function';
|
|
1416
|
+
if (normalized === 19 || normalized === 20) return 'type';
|
|
1417
|
+
if (normalized === 25 || normalized === 26) return 'module';
|
|
1418
|
+
if (normalized === 9 || normalized === 10 || normalized === 12) return 'variable';
|
|
1419
|
+
return 'symbol';
|
|
1420
|
+
}
|
|
1421
|
+
|
|
1422
|
+
function normalizeExternalOccurrenceRole(role) {
|
|
1423
|
+
const value = String(role ?? 'reference').toLowerCase();
|
|
1424
|
+
if (value.includes('def')) return 'definition';
|
|
1425
|
+
if (value.includes('decl')) return 'declaration';
|
|
1426
|
+
if (value.includes('import')) return 'import';
|
|
1427
|
+
if (value.includes('write')) return 'write';
|
|
1428
|
+
if (value.includes('read')) return 'read';
|
|
1429
|
+
return value === '2' ? 'definition' : value === '1' ? 'reference' : 'reference';
|
|
1430
|
+
}
|
|
1431
|
+
|
|
1432
|
+
function scipOccurrenceRole(value) {
|
|
1433
|
+
const role = Number(value ?? 0);
|
|
1434
|
+
if ((role & 0x1) > 0) return 'definition';
|
|
1435
|
+
if ((role & 0x2) > 0) return 'import';
|
|
1436
|
+
if ((role & 0x4) > 0) return 'write';
|
|
1437
|
+
if ((role & 0x8) > 0) return 'read';
|
|
1438
|
+
return 'reference';
|
|
1439
|
+
}
|
|
1440
|
+
|
|
1441
|
+
function scipOccurrenceRoleSet(value) {
|
|
1442
|
+
const role = Number(value ?? 0);
|
|
1443
|
+
const roles = [];
|
|
1444
|
+
if ((role & 0x1) > 0) roles.push('definition');
|
|
1445
|
+
if ((role & 0x2) > 0) roles.push('import');
|
|
1446
|
+
if ((role & 0x4) > 0) roles.push('write');
|
|
1447
|
+
if ((role & 0x8) > 0) roles.push('read');
|
|
1448
|
+
if ((role & 0x10) > 0) roles.push('generated');
|
|
1449
|
+
if ((role & 0x20) > 0) roles.push('test');
|
|
1450
|
+
if ((role & 0x40) > 0) roles.push('forwardDefinition');
|
|
1451
|
+
return roles.length ? roles : ['reference'];
|
|
1452
|
+
}
|
|
1453
|
+
|
|
1454
|
+
function semanticDbOccurrenceRole(value) {
|
|
1455
|
+
const role = String(value ?? 'reference').toLowerCase();
|
|
1456
|
+
if (role === '2' || role.includes('definition')) return 'definition';
|
|
1457
|
+
return 'reference';
|
|
1458
|
+
}
|
|
1459
|
+
|
|
1460
|
+
function scipRelationshipRelations(symbolInfo, symbolId, context) {
|
|
1461
|
+
return normalizeArray(symbolInfo.relationships).flatMap((relationship, index) => {
|
|
1462
|
+
const targetId = scipSymbolId(relationship.symbol, context);
|
|
1463
|
+
if (!targetId) return [];
|
|
1464
|
+
const predicates = [];
|
|
1465
|
+
if (relationship.is_reference ?? relationship.isReference) predicates.push('references');
|
|
1466
|
+
if (relationship.is_implementation ?? relationship.isImplementation) predicates.push('implements');
|
|
1467
|
+
if (relationship.is_type_definition ?? relationship.isTypeDefinition) predicates.push('typeDefinition');
|
|
1468
|
+
if (relationship.is_definition ?? relationship.isDefinition) predicates.push('definitionOf');
|
|
1469
|
+
return (predicates.length ? predicates : ['related']).map((predicate) => ({
|
|
1470
|
+
id: `rel_${idFragment(symbolId)}_${idFragment(targetId)}_${idFragment(predicate)}_${index + 1}`,
|
|
1471
|
+
sourceId: symbolId,
|
|
1472
|
+
predicate,
|
|
1473
|
+
targetId,
|
|
1474
|
+
metadata: { format: 'scip', relationship }
|
|
1475
|
+
}));
|
|
1476
|
+
});
|
|
1477
|
+
}
|
|
1478
|
+
|
|
1479
|
+
function scipSymbolFacts(symbolInfo, symbolId) {
|
|
1480
|
+
const facts = [];
|
|
1481
|
+
if (symbolInfo.documentation) {
|
|
1482
|
+
facts.push({
|
|
1483
|
+
id: `fact_${idFragment(symbolId)}_documentation`,
|
|
1484
|
+
predicate: 'documentation',
|
|
1485
|
+
subjectId: symbolId,
|
|
1486
|
+
value: normalizeArray(symbolInfo.documentation)
|
|
1487
|
+
});
|
|
1488
|
+
}
|
|
1489
|
+
const signature = symbolInfo.signature_documentation ?? symbolInfo.signatureDocumentation;
|
|
1490
|
+
if (signature) {
|
|
1491
|
+
facts.push({
|
|
1492
|
+
id: `fact_${idFragment(symbolId)}_signature`,
|
|
1493
|
+
predicate: 'signature',
|
|
1494
|
+
subjectId: symbolId,
|
|
1495
|
+
value: signature
|
|
1496
|
+
});
|
|
1497
|
+
}
|
|
1498
|
+
for (const [index, relationship] of normalizeArray(symbolInfo.relationships).entries()) {
|
|
1499
|
+
facts.push({
|
|
1500
|
+
id: `fact_${idFragment(symbolId)}_relationship_${index + 1}`,
|
|
1501
|
+
predicate: 'relationship',
|
|
1502
|
+
subjectId: symbolId,
|
|
1503
|
+
value: relationship
|
|
1504
|
+
});
|
|
1505
|
+
}
|
|
1506
|
+
return facts;
|
|
1507
|
+
}
|
|
1508
|
+
|
|
1509
|
+
function semanticDbSymbolFacts(symbolInfo, symbolId) {
|
|
1510
|
+
const facts = [];
|
|
1511
|
+
for (const key of ['signature', 'properties', 'annotations', 'access', 'language']) {
|
|
1512
|
+
if (symbolInfo[key] !== undefined) {
|
|
1513
|
+
facts.push({
|
|
1514
|
+
id: `fact_${idFragment(symbolId)}_${idFragment(key)}`,
|
|
1515
|
+
predicate: key,
|
|
1516
|
+
subjectId: symbolId,
|
|
1517
|
+
value: symbolInfo[key]
|
|
1518
|
+
});
|
|
1519
|
+
}
|
|
1520
|
+
}
|
|
1521
|
+
return facts;
|
|
1522
|
+
}
|
|
1523
|
+
|
|
1524
|
+
function normalizeLspDocuments(payload, context) {
|
|
1525
|
+
if (Array.isArray(payload.documents)) return payload.documents;
|
|
1526
|
+
if (payload.textDocument || payload.uri || payload.documentSymbols || payload.symbols || payload.semanticTokens || payload.diagnostics) {
|
|
1527
|
+
return [{
|
|
1528
|
+
...payload,
|
|
1529
|
+
uri: payload.uri ?? payload.textDocument?.uri,
|
|
1530
|
+
languageId: payload.languageId ?? payload.language ?? context.language,
|
|
1531
|
+
documentSymbols: payload.documentSymbols,
|
|
1532
|
+
symbols: payload.symbols,
|
|
1533
|
+
semanticTokens: payload.semanticTokens,
|
|
1534
|
+
diagnostics: payload.diagnostics
|
|
1535
|
+
}];
|
|
1536
|
+
}
|
|
1537
|
+
return [{ uri: context.sourcePath, languageId: context.language }];
|
|
1538
|
+
}
|
|
1539
|
+
|
|
1540
|
+
function addLspSymbol(result, symbol, input) {
|
|
1541
|
+
const location = symbol.location ?? {};
|
|
1542
|
+
const range = symbol.range ?? location.range ?? symbol.selectionRange;
|
|
1543
|
+
const sourcePath = uriToPath(location.uri ?? symbol.uri) ?? input.sourcePath;
|
|
1544
|
+
const symbolName = symbol.name ?? symbol.containerName ?? `symbol_${result.symbols.length + 1}`;
|
|
1545
|
+
const symbolId = symbol.id ?? `symbol:lsp:${idFragment(input.language ?? 'unknown')}:${idFragment([input.parentName, symbolName].filter(Boolean).join('.'))}`;
|
|
1546
|
+
const ownershipSpan = spanFromLspRange(range, sourcePath, input.context.sourceHash, 0);
|
|
1547
|
+
const selectionSpan = spanFromLspRange(symbol.selectionRange ?? range, sourcePath, input.context.sourceHash, 0);
|
|
1548
|
+
if (!result.symbols.some((entry) => entry.id === symbolId)) {
|
|
1549
|
+
result.symbols.push({
|
|
1550
|
+
id: symbolId,
|
|
1551
|
+
scheme: 'lsp',
|
|
1552
|
+
name: symbolName,
|
|
1553
|
+
kind: normalizeLspSymbolKind(symbol.kind),
|
|
1554
|
+
language: input.language,
|
|
1555
|
+
definitionSpan: ownershipSpan,
|
|
1556
|
+
signatureHash: hashSemanticValue([symbolName, symbol.kind, symbol.detail]),
|
|
1557
|
+
metadata: {
|
|
1558
|
+
format: 'lsp',
|
|
1559
|
+
detail: symbol.detail,
|
|
1560
|
+
tags: symbol.tags,
|
|
1561
|
+
deprecated: symbol.deprecated,
|
|
1562
|
+
containerName: symbol.containerName,
|
|
1563
|
+
parentName: input.parentName
|
|
1564
|
+
}
|
|
1565
|
+
});
|
|
1566
|
+
}
|
|
1567
|
+
result.occurrences.push({
|
|
1568
|
+
id: `occ_${idFragment(symbolId)}_${result.occurrences.length + 1}`,
|
|
1569
|
+
documentId: input.documentId,
|
|
1570
|
+
symbolId,
|
|
1571
|
+
role: 'definition',
|
|
1572
|
+
span: selectionSpan,
|
|
1573
|
+
metadata: { format: 'lsp', range, selectionRange: symbol.selectionRange }
|
|
1574
|
+
});
|
|
1575
|
+
for (const child of normalizeArray(symbol.children)) {
|
|
1576
|
+
addLspSymbol(result, child, {
|
|
1577
|
+
...input,
|
|
1578
|
+
parentName: [input.parentName, symbolName].filter(Boolean).join('.')
|
|
1579
|
+
});
|
|
1580
|
+
}
|
|
1581
|
+
}
|
|
1582
|
+
|
|
1583
|
+
function addLspSemanticTokens(result, semanticTokens, input) {
|
|
1584
|
+
const data = normalizeArray(semanticTokens.data);
|
|
1585
|
+
const legend = semanticTokens.legend ?? {};
|
|
1586
|
+
let line = 0;
|
|
1587
|
+
let character = 0;
|
|
1588
|
+
for (let index = 0; index + 4 < data.length; index += 5) {
|
|
1589
|
+
line += Number(data[index] ?? 0);
|
|
1590
|
+
character = Number(data[index] ?? 0) === 0 ? character + Number(data[index + 1] ?? 0) : Number(data[index + 1] ?? 0);
|
|
1591
|
+
const length = Number(data[index + 2] ?? 0);
|
|
1592
|
+
const tokenType = legend.tokenTypes?.[Number(data[index + 3] ?? 0)] ?? `tokenType${data[index + 3] ?? 0}`;
|
|
1593
|
+
const span = {
|
|
1594
|
+
path: input.sourcePath,
|
|
1595
|
+
startLine: line + 1,
|
|
1596
|
+
startColumn: character + 1,
|
|
1597
|
+
endLine: line + 1,
|
|
1598
|
+
endColumn: character + length + 1
|
|
1599
|
+
};
|
|
1600
|
+
result.facts.push({
|
|
1601
|
+
id: `fact_${idFragment(input.documentId)}_semantic_token_${index / 5 + 1}`,
|
|
1602
|
+
predicate: 'semanticToken',
|
|
1603
|
+
subjectId: input.documentId,
|
|
1604
|
+
value: {
|
|
1605
|
+
tokenType,
|
|
1606
|
+
tokenModifiers: semanticTokenModifiers(Number(data[index + 4] ?? 0), legend.tokenModifiers),
|
|
1607
|
+
span
|
|
1608
|
+
},
|
|
1609
|
+
metadata: { format: 'lsp' }
|
|
1610
|
+
});
|
|
1611
|
+
}
|
|
1612
|
+
}
|
|
1613
|
+
|
|
1614
|
+
function semanticTokenModifiers(bitset, modifiers = []) {
|
|
1615
|
+
const result = [];
|
|
1616
|
+
for (let index = 0; index < modifiers.length; index += 1) {
|
|
1617
|
+
if ((bitset & (1 << index)) > 0) result.push(modifiers[index]);
|
|
1618
|
+
}
|
|
1619
|
+
return result;
|
|
1620
|
+
}
|
|
1621
|
+
|
|
1622
|
+
function externalDiagnosticFact(diagnostic, context, documentId, sourcePath, index) {
|
|
1623
|
+
return {
|
|
1624
|
+
id: diagnostic.id ? `fact_${idFragment(diagnostic.id)}_diagnostic` : `fact_${context.idPart}_${idFragment(sourcePath)}_diagnostic_${index + 1}`,
|
|
1625
|
+
predicate: `${context.format}.diagnostic`,
|
|
1626
|
+
subjectId: documentId,
|
|
1627
|
+
value: {
|
|
1628
|
+
severity: diagnostic.severity,
|
|
1629
|
+
code: diagnostic.code,
|
|
1630
|
+
message: diagnostic.message,
|
|
1631
|
+
source: diagnostic.source,
|
|
1632
|
+
tags: diagnostic.tags,
|
|
1633
|
+
range: diagnostic.range,
|
|
1634
|
+
span: normalizeExternalSpan(diagnostic.range ?? diagnostic.span, sourcePath, context.sourceHash)
|
|
1635
|
+
},
|
|
1636
|
+
metadata: {
|
|
1637
|
+
format: context.format,
|
|
1638
|
+
source: 'external-semantic-index'
|
|
1639
|
+
}
|
|
1640
|
+
};
|
|
1641
|
+
}
|
|
1642
|
+
|
|
1643
|
+
function externalDiagnosticLoss(diagnostic, context, sourcePath) {
|
|
1644
|
+
const severity = externalDiagnosticSeverity(diagnostic.severity);
|
|
1645
|
+
return {
|
|
1646
|
+
id: diagnostic.id ?? `loss_${context.idPart}_${idFragment(diagnostic.code ?? diagnostic.message ?? severity)}_${idFragment(sourcePath)}`,
|
|
1647
|
+
severity,
|
|
1648
|
+
phase: 'index',
|
|
1649
|
+
sourceFormat: context.format,
|
|
1650
|
+
kind: severity === 'error' ? 'unsupportedSemantic' : 'partialSemanticIndex',
|
|
1651
|
+
message: String(diagnostic.message ?? `${context.format} diagnostic reported ${severity}.`),
|
|
1652
|
+
span: normalizeExternalSpan(diagnostic.range ?? diagnostic.span, sourcePath, context.sourceHash),
|
|
1653
|
+
metadata: {
|
|
1654
|
+
format: context.format,
|
|
1655
|
+
code: diagnostic.code,
|
|
1656
|
+
source: diagnostic.source,
|
|
1657
|
+
tags: diagnostic.tags
|
|
1658
|
+
}
|
|
1659
|
+
};
|
|
1660
|
+
}
|
|
1661
|
+
|
|
1662
|
+
function externalDiagnosticSeverity(value) {
|
|
1663
|
+
if (value === undefined || value === null || value === '') return 'error';
|
|
1664
|
+
const raw = String(value).toLowerCase();
|
|
1665
|
+
if (raw === '1' || raw.includes('error')) return 'error';
|
|
1666
|
+
if (raw === '3' || raw.includes('info') || raw.includes('hint')) return 'info';
|
|
1667
|
+
return 'warning';
|
|
1668
|
+
}
|
|
1669
|
+
|
|
1670
|
+
function spanFromScipOccurrence(occurrence, sourcePath, sourceHash) {
|
|
1671
|
+
if (occurrence.single_line_range || occurrence.singleLineRange) {
|
|
1672
|
+
const range = occurrence.single_line_range ?? occurrence.singleLineRange;
|
|
1673
|
+
return {
|
|
1674
|
+
sourceId: sourceHash,
|
|
1675
|
+
path: sourcePath,
|
|
1676
|
+
startLine: Number(range.line ?? 0) + 1,
|
|
1677
|
+
startColumn: Number(range.start_character ?? range.startCharacter ?? 0) + 1,
|
|
1678
|
+
endLine: Number(range.line ?? 0) + 1,
|
|
1679
|
+
endColumn: Number(range.end_character ?? range.endCharacter ?? 0) + 1
|
|
1680
|
+
};
|
|
1681
|
+
}
|
|
1682
|
+
if (occurrence.multi_line_range || occurrence.multiLineRange) {
|
|
1683
|
+
const range = occurrence.multi_line_range ?? occurrence.multiLineRange;
|
|
1684
|
+
return {
|
|
1685
|
+
sourceId: sourceHash,
|
|
1686
|
+
path: sourcePath,
|
|
1687
|
+
startLine: Number(range.start_line ?? range.startLine ?? 0) + 1,
|
|
1688
|
+
startColumn: Number(range.start_character ?? range.startCharacter ?? 0) + 1,
|
|
1689
|
+
endLine: Number(range.end_line ?? range.endLine ?? 0) + 1,
|
|
1690
|
+
endColumn: Number(range.end_character ?? range.endCharacter ?? 0) + 1
|
|
1691
|
+
};
|
|
1692
|
+
}
|
|
1693
|
+
const range = occurrence.range;
|
|
1694
|
+
if (Array.isArray(range) && range.length >= 3) {
|
|
1695
|
+
const startLine = Number(range[0] ?? 0);
|
|
1696
|
+
const startColumn = Number(range[1] ?? 0);
|
|
1697
|
+
const endLine = range.length >= 4 ? Number(range[2] ?? startLine) : startLine;
|
|
1698
|
+
const endColumn = range.length >= 4 ? Number(range[3] ?? startColumn) : Number(range[2] ?? startColumn);
|
|
1699
|
+
return {
|
|
1700
|
+
sourceId: sourceHash,
|
|
1701
|
+
path: sourcePath,
|
|
1702
|
+
startLine: startLine + 1,
|
|
1703
|
+
startColumn: startColumn + 1,
|
|
1704
|
+
endLine: endLine + 1,
|
|
1705
|
+
endColumn: endColumn + 1
|
|
1706
|
+
};
|
|
1707
|
+
}
|
|
1708
|
+
return undefined;
|
|
1709
|
+
}
|
|
1710
|
+
|
|
1711
|
+
function spanFromSemanticDbRange(range, sourcePath, sourceHash) {
|
|
1712
|
+
if (!range) return undefined;
|
|
1713
|
+
return {
|
|
1714
|
+
sourceId: sourceHash,
|
|
1715
|
+
path: sourcePath,
|
|
1716
|
+
startLine: Number(range.start_line ?? range.startLine ?? 0) + 1,
|
|
1717
|
+
startColumn: Number(range.start_character ?? range.startCharacter ?? 0) + 1,
|
|
1718
|
+
endLine: Number(range.end_line ?? range.endLine ?? 0) + 1,
|
|
1719
|
+
endColumn: Number(range.end_character ?? range.endCharacter ?? 0) + 1
|
|
1720
|
+
};
|
|
1721
|
+
}
|
|
1722
|
+
|
|
1723
|
+
function spanFromLspRange(range, sourcePath, sourceHash, base = 0) {
|
|
1724
|
+
if (!range) return undefined;
|
|
1725
|
+
const source = range.start && range.end ? range : { start: range, end: range.end ?? range };
|
|
1726
|
+
return {
|
|
1727
|
+
sourceId: sourceHash,
|
|
1728
|
+
path: sourcePath,
|
|
1729
|
+
startLine: Number(source.start?.line ?? source.startLine ?? 0) + (base === 0 ? 1 : 0),
|
|
1730
|
+
startColumn: Number(source.start?.character ?? source.startColumn ?? 0) + (base === 0 ? 1 : 0),
|
|
1731
|
+
endLine: Number(source.end?.line ?? source.endLine ?? source.start?.line ?? 0) + (base === 0 ? 1 : 0),
|
|
1732
|
+
endColumn: Number(source.end?.character ?? source.endColumn ?? source.start?.character ?? 0) + (base === 0 ? 1 : 0)
|
|
1733
|
+
};
|
|
1734
|
+
}
|
|
1735
|
+
|
|
1736
|
+
function normalizeExternalSpan(value, sourcePath, sourceHash) {
|
|
1737
|
+
if (!value) return undefined;
|
|
1738
|
+
if (Array.isArray(value)) {
|
|
1739
|
+
return spanFromScipOccurrence({ range: value }, sourcePath, sourceHash);
|
|
1740
|
+
}
|
|
1741
|
+
if (value.start || value.end || value.line !== undefined) return spanFromLspRange(value, sourcePath, sourceHash, 0);
|
|
1742
|
+
if (value.startLine !== undefined || value.start_line !== undefined) {
|
|
1743
|
+
return {
|
|
1744
|
+
sourceId: value.sourceId ?? sourceHash,
|
|
1745
|
+
path: value.path ?? sourcePath,
|
|
1746
|
+
start: value.start,
|
|
1747
|
+
end: value.end,
|
|
1748
|
+
startLine: Number(value.startLine ?? value.start_line),
|
|
1749
|
+
startColumn: value.startColumn ?? value.start_character,
|
|
1750
|
+
endLine: value.endLine ?? value.end_line,
|
|
1751
|
+
endColumn: value.endColumn ?? value.end_character
|
|
1752
|
+
};
|
|
1753
|
+
}
|
|
1754
|
+
return undefined;
|
|
1755
|
+
}
|
|
1756
|
+
|
|
1757
|
+
function uriToPath(uri) {
|
|
1758
|
+
if (typeof uri !== 'string') return undefined;
|
|
1759
|
+
if (uri.startsWith('file://')) {
|
|
1760
|
+
try {
|
|
1761
|
+
return decodeURIComponent(new URL(uri).pathname);
|
|
1762
|
+
} catch {
|
|
1763
|
+
return uri.replace(/^file:\/\//, '');
|
|
1764
|
+
}
|
|
1765
|
+
}
|
|
1766
|
+
return uri;
|
|
1767
|
+
}
|
|
1768
|
+
|
|
547
1769
|
export function projectFrontierAst(document, target = 'typescript', options = {}) {
|
|
548
1770
|
const normalized = normalizeCompileTarget(target);
|
|
549
1771
|
const projector = projectors[normalized];
|
|
@@ -2801,10 +4023,23 @@ function scanJavaScriptLike(input) {
|
|
|
2801
4023
|
const declarations = [];
|
|
2802
4024
|
let currentClass;
|
|
2803
4025
|
let classDepth = 0;
|
|
4026
|
+
let currentObject;
|
|
4027
|
+
const lexicalState = { inBlockComment: false, inTemplateString: false };
|
|
2804
4028
|
for (const { line, number } of sourceLines(input.sourceText)) {
|
|
2805
|
-
const
|
|
4029
|
+
const scanLine = jsDeclarationScanLine(line, lexicalState);
|
|
4030
|
+
const trimmed = scanLine.trim();
|
|
4031
|
+
if (!trimmed || jsCommentOnlyLine(trimmed)) continue;
|
|
2806
4032
|
const declarationLine = trimmed.replace(/^(?:export\s+)?(?:declare\s+)?/, '');
|
|
2807
4033
|
let match;
|
|
4034
|
+
if (currentObject) {
|
|
4035
|
+
const routeRecord = jsRouteRecordDeclaration(input, number, trimmed, currentObject);
|
|
4036
|
+
if (routeRecord) {
|
|
4037
|
+
declarations.push(routeRecord);
|
|
4038
|
+
} else {
|
|
4039
|
+
const property = jsObjectPropertyDeclaration(input, number, trimmed, currentObject);
|
|
4040
|
+
if (property) declarations.push(property);
|
|
4041
|
+
}
|
|
4042
|
+
}
|
|
2808
4043
|
if ((match = trimmed.match(/^import\s+(?:.+?\s+from\s+)?['"]([^'"]+)['"]/))) {
|
|
2809
4044
|
declarations.push(nativeImportDeclaration(input, number, match[1], 'ImportDeclaration', 'module'));
|
|
2810
4045
|
} else if ((match = trimmed.match(/^import\s*\(\s*['"]([^'"]+)['"]\s*\)/))) {
|
|
@@ -2832,11 +4067,20 @@ function scanJavaScriptLike(input) {
|
|
|
2832
4067
|
} else if ((match = declarationLine.match(/^(?:const|let|var)\s+([A-Za-z_$][\w$]*)\s*=\s*(?:async\s*)?\(?([^=;]*)\)?\s*=>/))) {
|
|
2833
4068
|
declarations.push(nativeDeclaration(input, number, 'VariableFunctionDeclaration', 'function', match[1], { parameters: splitParameters(match[2]) }, true));
|
|
2834
4069
|
} else if ((match = declarationLine.match(/^(?:const|let|var)\s+([A-Za-z_$][\w$]*)\b/))) {
|
|
2835
|
-
|
|
4070
|
+
const initializerKind = jsInitializerKind(declarationLine);
|
|
4071
|
+
const regionKind = jsRegionKindForDeclarationName(match[1], declarationLine);
|
|
4072
|
+
declarations.push(nativeDeclaration(input, number, 'VariableDeclaration', jsVariableSymbolKind(regionKind, initializerKind), match[1], {
|
|
4073
|
+
initializerKind
|
|
4074
|
+
}, jsVariableHasBody(initializerKind, declarationLine), {
|
|
4075
|
+
regionKind,
|
|
4076
|
+
metadata: { initializerKind }
|
|
4077
|
+
}));
|
|
4078
|
+
currentObject = jsObjectRegionContext(match[1], declarationLine, number, regionKind);
|
|
2836
4079
|
} else if ((match = trimmed.match(/^(?:module\.)?exports\.([A-Za-z_$][\w$]*)\s*=\s*(?:async\s+)?function\*?\s*\(([^)]*)\)/))) {
|
|
2837
4080
|
declarations.push(nativeDeclaration(input, number, 'CommonJsFunctionExport', 'function', match[1], { parameters: splitParameters(match[2]) }, true));
|
|
2838
4081
|
} else if ((match = trimmed.match(/^(?:module\.)?exports\.([A-Za-z_$][\w$]*)\s*=/))) {
|
|
2839
|
-
|
|
4082
|
+
const regionKind = jsRegionKindForDeclarationName(match[1], trimmed);
|
|
4083
|
+
declarations.push(nativeDeclaration(input, number, 'CommonJsExport', 'variable', match[1], { export: 'commonjs' }, false, { regionKind }));
|
|
2840
4084
|
} else if (currentClass && (match = declarationLine.match(/^(?:(?:public|private|protected|static|async|override|readonly|abstract|accessor|get|set)\s+)*(?:async\s+)?(?:get\s+|set\s+)?([A-Za-z_$][\w$]*)\s*\(([^)]*)\)\s*(?::\s*[^={]+)?(?:\{|=>|$)/)) && !jsControlKeyword(match[1])) {
|
|
2841
4085
|
declarations.push(nativeDeclaration(input, number, 'MethodDefinition', 'method', `${currentClass}.${match[1]}`, {
|
|
2842
4086
|
methodName: match[1],
|
|
@@ -2857,10 +4101,224 @@ function scanJavaScriptLike(input) {
|
|
|
2857
4101
|
classDepth = 0;
|
|
2858
4102
|
}
|
|
2859
4103
|
}
|
|
4104
|
+
if (currentObject) {
|
|
4105
|
+
if (number !== currentObject.startLine) currentObject.depth += jsContainerDelta(trimmed);
|
|
4106
|
+
if (currentObject.depth <= 0) currentObject = undefined;
|
|
4107
|
+
}
|
|
2860
4108
|
}
|
|
2861
4109
|
return declarations;
|
|
2862
4110
|
}
|
|
2863
4111
|
|
|
4112
|
+
function jsCommentOnlyLine(trimmed) {
|
|
4113
|
+
return trimmed.startsWith('//') || trimmed.startsWith('/*') || trimmed.startsWith('*');
|
|
4114
|
+
}
|
|
4115
|
+
|
|
4116
|
+
function jsDeclarationScanLine(line, state) {
|
|
4117
|
+
let text = String(line ?? '');
|
|
4118
|
+
if (state.inTemplateString) {
|
|
4119
|
+
const close = findUnescapedBacktick(text, 0);
|
|
4120
|
+
if (close < 0) return '';
|
|
4121
|
+
text = text.slice(close + 1);
|
|
4122
|
+
state.inTemplateString = false;
|
|
4123
|
+
}
|
|
4124
|
+
if (state.inBlockComment) {
|
|
4125
|
+
const close = text.indexOf('*/');
|
|
4126
|
+
if (close < 0) return '';
|
|
4127
|
+
text = text.slice(close + 2);
|
|
4128
|
+
state.inBlockComment = false;
|
|
4129
|
+
}
|
|
4130
|
+
let output = '';
|
|
4131
|
+
let quote;
|
|
4132
|
+
let escaped = false;
|
|
4133
|
+
for (let index = 0; index < text.length; index += 1) {
|
|
4134
|
+
const char = text[index];
|
|
4135
|
+
const next = text[index + 1];
|
|
4136
|
+
if (quote) {
|
|
4137
|
+
output += char;
|
|
4138
|
+
if (escaped) {
|
|
4139
|
+
escaped = false;
|
|
4140
|
+
} else if (char === '\\') {
|
|
4141
|
+
escaped = true;
|
|
4142
|
+
} else if (char === quote) {
|
|
4143
|
+
quote = undefined;
|
|
4144
|
+
}
|
|
4145
|
+
continue;
|
|
4146
|
+
}
|
|
4147
|
+
if (char === '/' && next === '/') break;
|
|
4148
|
+
if (char === '/' && next === '*') {
|
|
4149
|
+
const close = text.indexOf('*/', index + 2);
|
|
4150
|
+
if (close < 0) {
|
|
4151
|
+
state.inBlockComment = true;
|
|
4152
|
+
break;
|
|
4153
|
+
}
|
|
4154
|
+
index = close + 1;
|
|
4155
|
+
continue;
|
|
4156
|
+
}
|
|
4157
|
+
if (char === '\'' || char === '"') {
|
|
4158
|
+
quote = char;
|
|
4159
|
+
output += char;
|
|
4160
|
+
continue;
|
|
4161
|
+
}
|
|
4162
|
+
if (char === '`') {
|
|
4163
|
+
const close = findUnescapedBacktick(text, index + 1);
|
|
4164
|
+
if (close < 0) {
|
|
4165
|
+
state.inTemplateString = true;
|
|
4166
|
+
output += '``';
|
|
4167
|
+
break;
|
|
4168
|
+
}
|
|
4169
|
+
output += text.slice(index, close + 1);
|
|
4170
|
+
index = close;
|
|
4171
|
+
continue;
|
|
4172
|
+
}
|
|
4173
|
+
output += char;
|
|
4174
|
+
}
|
|
4175
|
+
return output;
|
|
4176
|
+
}
|
|
4177
|
+
|
|
4178
|
+
function findUnescapedBacktick(text, startIndex) {
|
|
4179
|
+
let escaped = false;
|
|
4180
|
+
for (let index = startIndex; index < text.length; index += 1) {
|
|
4181
|
+
const char = text[index];
|
|
4182
|
+
if (escaped) {
|
|
4183
|
+
escaped = false;
|
|
4184
|
+
} else if (char === '\\') {
|
|
4185
|
+
escaped = true;
|
|
4186
|
+
} else if (char === '`') {
|
|
4187
|
+
return index;
|
|
4188
|
+
}
|
|
4189
|
+
}
|
|
4190
|
+
return -1;
|
|
4191
|
+
}
|
|
4192
|
+
|
|
4193
|
+
function jsObjectRegionContext(name, declarationLine, lineNumber, regionKind) {
|
|
4194
|
+
const initializerKind = jsInitializerKind(declarationLine);
|
|
4195
|
+
if (initializerKind !== 'object' && initializerKind !== 'array') return undefined;
|
|
4196
|
+
const depth = jsContainerDelta(declarationLine);
|
|
4197
|
+
if (depth <= 0) return undefined;
|
|
4198
|
+
return {
|
|
4199
|
+
name,
|
|
4200
|
+
regionKind: regionKind ?? jsRegionKindForDeclarationName(name, declarationLine),
|
|
4201
|
+
initializerKind,
|
|
4202
|
+
depth,
|
|
4203
|
+
startLine: lineNumber
|
|
4204
|
+
};
|
|
4205
|
+
}
|
|
4206
|
+
|
|
4207
|
+
function jsInitializerKind(line) {
|
|
4208
|
+
const initializer = String(line ?? '').split('=').slice(1).join('=').trim();
|
|
4209
|
+
if (!initializer) return 'unknown';
|
|
4210
|
+
if (/^(?:async\s+)?function\b/.test(initializer) || /=>/.test(initializer)) return 'function';
|
|
4211
|
+
if (initializer.startsWith('{')) return 'object';
|
|
4212
|
+
if (initializer.startsWith('[')) return 'array';
|
|
4213
|
+
if (/^new\s+/.test(initializer)) return 'instance';
|
|
4214
|
+
if (/^['"`]/.test(initializer)) return 'string';
|
|
4215
|
+
if (/^(?:true|false)\b/.test(initializer)) return 'boolean';
|
|
4216
|
+
if (/^[0-9]/.test(initializer)) return 'number';
|
|
4217
|
+
return 'expression';
|
|
4218
|
+
}
|
|
4219
|
+
|
|
4220
|
+
function jsVariableHasBody(initializerKind, declarationLine) {
|
|
4221
|
+
return initializerKind === 'function'
|
|
4222
|
+
|| initializerKind === 'object'
|
|
4223
|
+
|| initializerKind === 'array'
|
|
4224
|
+
|| /\{/.test(String(declarationLine ?? ''));
|
|
4225
|
+
}
|
|
4226
|
+
|
|
4227
|
+
function jsVariableSymbolKind(regionKind, initializerKind) {
|
|
4228
|
+
if (regionKind === 'route') return 'route';
|
|
4229
|
+
if (initializerKind === 'function') return 'function';
|
|
4230
|
+
return 'variable';
|
|
4231
|
+
}
|
|
4232
|
+
|
|
4233
|
+
function jsRegionKindForDeclarationName(name, source = '') {
|
|
4234
|
+
const raw = `${name ?? ''} ${source ?? ''}`;
|
|
4235
|
+
const text = raw.replace(/([a-z0-9])([A-Z])/g, '$1 $2').toLowerCase();
|
|
4236
|
+
const compact = raw.toLowerCase();
|
|
4237
|
+
if (/\b(routes?|router|screens?|pages?|navigation|navitems?|menuitems?)\b/.test(text) || /(route|router|screen|page|navigation|navitem|menuitem)/.test(compact)) return 'route';
|
|
4238
|
+
if (/\b(config|settings|options|flags?|schema|manifest|registry|catalog)\b/.test(text) || /(config|settings|options|flags|schema|manifest|registry|catalog)/.test(compact)) return 'config';
|
|
4239
|
+
if (/\b(content|copy|docs?|legal|messages?|strings?|i18n|locale|translations?)\b/.test(text) || /(content|copy|docs|legal|messages|strings|i18n|locale|translations)/.test(compact)) return 'content';
|
|
4240
|
+
return undefined;
|
|
4241
|
+
}
|
|
4242
|
+
|
|
4243
|
+
function jsObjectPropertyDeclaration(input, lineNumber, trimmed, context) {
|
|
4244
|
+
if (/^[}\])]/.test(trimmed) || trimmed.startsWith('...')) return undefined;
|
|
4245
|
+
const methodMatch = trimmed.match(/^(?:(?:async|get|set)\s+)?(['"]?)([A-Za-z_$][\w$-]*)\1\s*\(([^)]*)\)\s*(?:[:\w\s<>\[\]]*)?(?:\{|=>|,|$)/);
|
|
4246
|
+
if (methodMatch && !jsControlKeyword(methodMatch[2])) {
|
|
4247
|
+
const name = `${context.name}.${methodMatch[2]}`;
|
|
4248
|
+
return nativeDeclaration(input, lineNumber, 'ObjectMethod', 'function', name, {
|
|
4249
|
+
owner: context.name,
|
|
4250
|
+
propertyName: methodMatch[2],
|
|
4251
|
+
parameters: splitParameters(methodMatch[3])
|
|
4252
|
+
}, true, {
|
|
4253
|
+
regionKind: jsPropertyRegionKind(context, methodMatch[2], 'function'),
|
|
4254
|
+
metadata: { owner: context.name, propertyName: methodMatch[2], initializerKind: 'function' }
|
|
4255
|
+
});
|
|
4256
|
+
}
|
|
4257
|
+
const propertyMatch = trimmed.match(/^(?:(['"])([^'"]+)\1|([A-Za-z_$][\w$-]*))\s*:\s*(.+?)(?:,)?$/);
|
|
4258
|
+
if (!propertyMatch) return undefined;
|
|
4259
|
+
const propertyName = propertyMatch[2] ?? propertyMatch[3];
|
|
4260
|
+
if (!propertyName || jsControlKeyword(propertyName)) return undefined;
|
|
4261
|
+
const value = propertyMatch[4].trim();
|
|
4262
|
+
const initializerKind = jsPropertyInitializerKind(value);
|
|
4263
|
+
const functionLike = initializerKind === 'function';
|
|
4264
|
+
const name = `${context.name}.${propertyName}`;
|
|
4265
|
+
return nativeDeclaration(input, lineNumber, functionLike ? 'ObjectFunctionProperty' : 'ObjectProperty', functionLike ? 'function' : 'property', name, {
|
|
4266
|
+
owner: context.name,
|
|
4267
|
+
propertyName,
|
|
4268
|
+
initializerKind
|
|
4269
|
+
}, functionLike || initializerKind === 'object' || initializerKind === 'array', {
|
|
4270
|
+
regionKind: jsPropertyRegionKind(context, propertyName, value),
|
|
4271
|
+
metadata: {
|
|
4272
|
+
owner: context.name,
|
|
4273
|
+
propertyName,
|
|
4274
|
+
initializerKind
|
|
4275
|
+
}
|
|
4276
|
+
});
|
|
4277
|
+
}
|
|
4278
|
+
|
|
4279
|
+
function jsRouteRecordDeclaration(input, lineNumber, trimmed, context) {
|
|
4280
|
+
if (context.regionKind !== 'route') return undefined;
|
|
4281
|
+
const match = trimmed.match(/\b(?:path|route|href|url)\s*:\s*(['"`])([^'"`]+)\1/);
|
|
4282
|
+
if (!match) return undefined;
|
|
4283
|
+
const routePath = match[2];
|
|
4284
|
+
return nativeDeclaration(input, lineNumber, 'RouteRecord', 'route', `${context.name}.${routePath}`, {
|
|
4285
|
+
owner: context.name,
|
|
4286
|
+
routePath
|
|
4287
|
+
}, true, {
|
|
4288
|
+
regionKind: 'route',
|
|
4289
|
+
metadata: { owner: context.name, routePath, initializerKind: 'object' }
|
|
4290
|
+
});
|
|
4291
|
+
}
|
|
4292
|
+
|
|
4293
|
+
function jsPropertyInitializerKind(value) {
|
|
4294
|
+
const text = String(value ?? '').trim();
|
|
4295
|
+
if (/^(?:async\s*)?(?:function\b|\([^)]*\)\s*=>|[A-Za-z_$][\w$]*\s*=>)/.test(text)) return 'function';
|
|
4296
|
+
if (text.startsWith('{')) return 'object';
|
|
4297
|
+
if (text.startsWith('[')) return 'array';
|
|
4298
|
+
if (/^['"`]/.test(text)) return 'string';
|
|
4299
|
+
if (/^(?:true|false)\b/.test(text)) return 'boolean';
|
|
4300
|
+
if (/^[0-9]/.test(text)) return 'number';
|
|
4301
|
+
return 'expression';
|
|
4302
|
+
}
|
|
4303
|
+
|
|
4304
|
+
function jsPropertyRegionKind(context, propertyName, value) {
|
|
4305
|
+
const named = jsRegionKindForDeclarationName(propertyName, value);
|
|
4306
|
+
if (named) return named;
|
|
4307
|
+
if (context.regionKind === 'route') return 'route';
|
|
4308
|
+
if (context.regionKind === 'content') return 'content';
|
|
4309
|
+
if (context.regionKind === 'config') return 'config';
|
|
4310
|
+
return 'property';
|
|
4311
|
+
}
|
|
4312
|
+
|
|
4313
|
+
function jsContainerDelta(source) {
|
|
4314
|
+
let delta = 0;
|
|
4315
|
+
for (const char of String(source ?? '')) {
|
|
4316
|
+
if (char === '{' || char === '[') delta += 1;
|
|
4317
|
+
if (char === '}' || char === ']') delta -= 1;
|
|
4318
|
+
}
|
|
4319
|
+
return delta;
|
|
4320
|
+
}
|
|
4321
|
+
|
|
2864
4322
|
function scanPython(input) {
|
|
2865
4323
|
const declarations = [];
|
|
2866
4324
|
for (const { line, number } of sourceLines(input.sourceText)) {
|
|
@@ -3560,7 +5018,7 @@ function rMetaName(source) {
|
|
|
3560
5018
|
return match?.[1] ?? 'dynamic';
|
|
3561
5019
|
}
|
|
3562
5020
|
|
|
3563
|
-
function nativeDeclaration(input, lineNumber, languageKind, symbolKind, name, fields = {}, hasBody = false) {
|
|
5021
|
+
function nativeDeclaration(input, lineNumber, languageKind, symbolKind, name, fields = {}, hasBody = false, options = {}) {
|
|
3564
5022
|
const nodeId = `native_${idFragment(languageKind)}_${lineNumber}_${idFragment(name)}`;
|
|
3565
5023
|
return {
|
|
3566
5024
|
nodeId,
|
|
@@ -3571,7 +5029,9 @@ function nativeDeclaration(input, lineNumber, languageKind, symbolKind, name, fi
|
|
|
3571
5029
|
symbolId: `symbol:${input.language}:${idFragment(name)}`,
|
|
3572
5030
|
span: spanForLine(input, lineNumber),
|
|
3573
5031
|
fields,
|
|
3574
|
-
metadata: { scan: 'lightweight-declaration', hasBody },
|
|
5032
|
+
metadata: { scan: 'lightweight-declaration', hasBody, ...options.metadata },
|
|
5033
|
+
...(options.regionKind ? { regionKind: options.regionKind } : {}),
|
|
5034
|
+
...(options.role ? { role: options.role } : {}),
|
|
3575
5035
|
...(hasBody ? { loss: opaqueBodyLoss(input, lineNumber, nodeId, name) } : {})
|
|
3576
5036
|
};
|
|
3577
5037
|
}
|
|
@@ -5431,6 +6891,8 @@ function semanticPatchHintForRegion(region, readiness, options = {}) {
|
|
|
5431
6891
|
|
|
5432
6892
|
function semanticRegionKindForDeclaration(declaration) {
|
|
5433
6893
|
if (declaration.role === 'import' || declaration.importPath) return 'import';
|
|
6894
|
+
if (declaration.regionKind) return normalizeNativeImportRegionKind(declaration.regionKind);
|
|
6895
|
+
if (declaration.metadata?.ownershipRegionKind) return normalizeNativeImportRegionKind(declaration.metadata.ownershipRegionKind);
|
|
5434
6896
|
const kind = declaration.symbolKind ?? declaration.kind ?? declaration.nativeNode?.kind;
|
|
5435
6897
|
if (semanticKindIsType(kind)) return 'type';
|
|
5436
6898
|
if (semanticKindCanOwnBody(kind, declaration.span ?? declaration.nativeNode?.span)) return 'body';
|