scip-query 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/IMPROVEMENTS.md +143 -0
- package/PLAN.md +320 -0
- package/README.md +1213 -0
- package/dist/chunk-2QZ23IBN.js +55 -0
- package/dist/chunk-2QZ23IBN.js.map +1 -0
- package/dist/chunk-36OMT7ZJ.js +144 -0
- package/dist/chunk-36OMT7ZJ.js.map +1 -0
- package/dist/chunk-3E2X7RIE.js +101 -0
- package/dist/chunk-3E2X7RIE.js.map +1 -0
- package/dist/chunk-3UOUTZQT.js +45 -0
- package/dist/chunk-3UOUTZQT.js.map +1 -0
- package/dist/chunk-3ZZJVBIO.js +88 -0
- package/dist/chunk-3ZZJVBIO.js.map +1 -0
- package/dist/chunk-4TYLS5XX.js +10 -0
- package/dist/chunk-4TYLS5XX.js.map +1 -0
- package/dist/chunk-5FGUEU7N.js +101 -0
- package/dist/chunk-5FGUEU7N.js.map +1 -0
- package/dist/chunk-5WTJAXY2.js +61 -0
- package/dist/chunk-5WTJAXY2.js.map +1 -0
- package/dist/chunk-6NBLIDF4.js +24 -0
- package/dist/chunk-6NBLIDF4.js.map +1 -0
- package/dist/chunk-6SXADWLW.js +43 -0
- package/dist/chunk-6SXADWLW.js.map +1 -0
- package/dist/chunk-6VJ6Q7IE.js +65 -0
- package/dist/chunk-6VJ6Q7IE.js.map +1 -0
- package/dist/chunk-7OZPA5OO.js +258 -0
- package/dist/chunk-7OZPA5OO.js.map +1 -0
- package/dist/chunk-BEPIEVLR.js +76 -0
- package/dist/chunk-BEPIEVLR.js.map +1 -0
- package/dist/chunk-BFSCMC22.js +42 -0
- package/dist/chunk-BFSCMC22.js.map +1 -0
- package/dist/chunk-BP2ATLK2.js +110 -0
- package/dist/chunk-BP2ATLK2.js.map +1 -0
- package/dist/chunk-CM454WL3.js +114 -0
- package/dist/chunk-CM454WL3.js.map +1 -0
- package/dist/chunk-DCKMSTJ4.js +74 -0
- package/dist/chunk-DCKMSTJ4.js.map +1 -0
- package/dist/chunk-DEZKCZXD.js +40 -0
- package/dist/chunk-DEZKCZXD.js.map +1 -0
- package/dist/chunk-DVWGWHFW.js +99 -0
- package/dist/chunk-DVWGWHFW.js.map +1 -0
- package/dist/chunk-EMDQWNYR.js +102 -0
- package/dist/chunk-EMDQWNYR.js.map +1 -0
- package/dist/chunk-FFSWWE5O.js +33 -0
- package/dist/chunk-FFSWWE5O.js.map +1 -0
- package/dist/chunk-FGXRVW7G.js +73 -0
- package/dist/chunk-FGXRVW7G.js.map +1 -0
- package/dist/chunk-FUHJCHS4.js +158 -0
- package/dist/chunk-FUHJCHS4.js.map +1 -0
- package/dist/chunk-GJFURBEW.js +64 -0
- package/dist/chunk-GJFURBEW.js.map +1 -0
- package/dist/chunk-GTILYBH6.js +102 -0
- package/dist/chunk-GTILYBH6.js.map +1 -0
- package/dist/chunk-JJP7KQND.js +1 -0
- package/dist/chunk-JJP7KQND.js.map +1 -0
- package/dist/chunk-JKP5GH6T.js +213 -0
- package/dist/chunk-JKP5GH6T.js.map +1 -0
- package/dist/chunk-KCBMVQL5.js +38 -0
- package/dist/chunk-KCBMVQL5.js.map +1 -0
- package/dist/chunk-KVSW5KYP.js +78 -0
- package/dist/chunk-KVSW5KYP.js.map +1 -0
- package/dist/chunk-LAWMH22O.js +172 -0
- package/dist/chunk-LAWMH22O.js.map +1 -0
- package/dist/chunk-LB7OS35Q.js +72 -0
- package/dist/chunk-LB7OS35Q.js.map +1 -0
- package/dist/chunk-LUSIFBXO.js +57 -0
- package/dist/chunk-LUSIFBXO.js.map +1 -0
- package/dist/chunk-MBVNHJVN.js +44 -0
- package/dist/chunk-MBVNHJVN.js.map +1 -0
- package/dist/chunk-MGNMHKX3.js +15 -0
- package/dist/chunk-MGNMHKX3.js.map +1 -0
- package/dist/chunk-N5KEREIA.js +41 -0
- package/dist/chunk-N5KEREIA.js.map +1 -0
- package/dist/chunk-NDSQYIWT.js +71 -0
- package/dist/chunk-NDSQYIWT.js.map +1 -0
- package/dist/chunk-NUZ4OMU3.js +28 -0
- package/dist/chunk-NUZ4OMU3.js.map +1 -0
- package/dist/chunk-QOV2R2WT.js +170 -0
- package/dist/chunk-QOV2R2WT.js.map +1 -0
- package/dist/chunk-SEFSL2GF.js +78 -0
- package/dist/chunk-SEFSL2GF.js.map +1 -0
- package/dist/chunk-T6ARFSBZ.js +103 -0
- package/dist/chunk-T6ARFSBZ.js.map +1 -0
- package/dist/chunk-TBP6BICL.js +46 -0
- package/dist/chunk-TBP6BICL.js.map +1 -0
- package/dist/chunk-TDNNOR6D.js +97 -0
- package/dist/chunk-TDNNOR6D.js.map +1 -0
- package/dist/chunk-TSPZOMHC.js +195 -0
- package/dist/chunk-TSPZOMHC.js.map +1 -0
- package/dist/chunk-UNTPVD36.js +55 -0
- package/dist/chunk-UNTPVD36.js.map +1 -0
- package/dist/chunk-VRUJH4BO.js +88 -0
- package/dist/chunk-VRUJH4BO.js.map +1 -0
- package/dist/chunk-VZ7AMAFL.js +76 -0
- package/dist/chunk-VZ7AMAFL.js.map +1 -0
- package/dist/chunk-XFXDXEUN.js +24 -0
- package/dist/chunk-XFXDXEUN.js.map +1 -0
- package/dist/chunk-YZAA4LYG.js +169 -0
- package/dist/chunk-YZAA4LYG.js.map +1 -0
- package/dist/chunk-Z73NYSBZ.js +92 -0
- package/dist/chunk-Z73NYSBZ.js.map +1 -0
- package/dist/chunk-ZJRYBOEE.js +125 -0
- package/dist/chunk-ZJRYBOEE.js.map +1 -0
- package/dist/cli.js +5798 -0
- package/dist/cli.js.map +1 -0
- package/dist/db-BxaevAyc.d.ts +683 -0
- package/dist/index.d.ts +254 -0
- package/dist/index.js +1271 -0
- package/dist/index.js.map +1 -0
- package/dist/postinstall.js +167 -0
- package/dist/postinstall.js.map +1 -0
- package/dist/queries/affected.d.ts +14 -0
- package/dist/queries/affected.js +9 -0
- package/dist/queries/affected.js.map +1 -0
- package/dist/queries/bottlenecks.d.ts +18 -0
- package/dist/queries/bottlenecks.js +8 -0
- package/dist/queries/bottlenecks.js.map +1 -0
- package/dist/queries/by-kind.d.ts +20 -0
- package/dist/queries/by-kind.js +10 -0
- package/dist/queries/by-kind.js.map +1 -0
- package/dist/queries/call-graph.d.ts +13 -0
- package/dist/queries/call-graph.js +9 -0
- package/dist/queries/call-graph.js.map +1 -0
- package/dist/queries/change-surface.d.ts +10 -0
- package/dist/queries/change-surface.js +9 -0
- package/dist/queries/change-surface.js.map +1 -0
- package/dist/queries/clean-signature.d.ts +9 -0
- package/dist/queries/clean-signature.js +7 -0
- package/dist/queries/clean-signature.js.map +1 -0
- package/dist/queries/code.d.ts +17 -0
- package/dist/queries/code.js +9 -0
- package/dist/queries/code.js.map +1 -0
- package/dist/queries/complexity-hotspots.d.ts +19 -0
- package/dist/queries/complexity-hotspots.js +9 -0
- package/dist/queries/complexity-hotspots.js.map +1 -0
- package/dist/queries/complexity.d.ts +13 -0
- package/dist/queries/complexity.js +9 -0
- package/dist/queries/complexity.js.map +1 -0
- package/dist/queries/convergence.d.ts +11 -0
- package/dist/queries/convergence.js +9 -0
- package/dist/queries/convergence.js.map +1 -0
- package/dist/queries/coupling.d.ts +17 -0
- package/dist/queries/coupling.js +9 -0
- package/dist/queries/coupling.js.map +1 -0
- package/dist/queries/cycles.d.ts +16 -0
- package/dist/queries/cycles.js +8 -0
- package/dist/queries/cycles.js.map +1 -0
- package/dist/queries/dataflow.d.ts +19 -0
- package/dist/queries/dataflow.js +9 -0
- package/dist/queries/dataflow.js.map +1 -0
- package/dist/queries/dead.d.ts +10 -0
- package/dist/queries/dead.js +9 -0
- package/dist/queries/dead.js.map +1 -0
- package/dist/queries/deep-chains.d.ts +16 -0
- package/dist/queries/deep-chains.js +8 -0
- package/dist/queries/deep-chains.js.map +1 -0
- package/dist/queries/deps.d.ts +9 -0
- package/dist/queries/deps.js +9 -0
- package/dist/queries/deps.js.map +1 -0
- package/dist/queries/diff-impact.d.ts +13 -0
- package/dist/queries/diff-impact.js +9 -0
- package/dist/queries/diff-impact.js.map +1 -0
- package/dist/queries/doc-coverage.d.ts +14 -0
- package/dist/queries/doc-coverage.js +8 -0
- package/dist/queries/doc-coverage.js.map +1 -0
- package/dist/queries/drift.d.ts +25 -0
- package/dist/queries/drift.js +8 -0
- package/dist/queries/drift.js.map +1 -0
- package/dist/queries/extract-candidates.d.ts +25 -0
- package/dist/queries/extract-candidates.js +9 -0
- package/dist/queries/extract-candidates.js.map +1 -0
- package/dist/queries/fan.d.ts +29 -0
- package/dist/queries/fan.js +14 -0
- package/dist/queries/fan.js.map +1 -0
- package/dist/queries/files.d.ts +6 -0
- package/dist/queries/files.js +7 -0
- package/dist/queries/files.js.map +1 -0
- package/dist/queries/health.d.ts +18 -0
- package/dist/queries/health.js +21 -0
- package/dist/queries/health.js.map +1 -0
- package/dist/queries/hierarchy.d.ts +13 -0
- package/dist/queries/hierarchy.js +8 -0
- package/dist/queries/hierarchy.js.map +1 -0
- package/dist/queries/hotspots.d.ts +13 -0
- package/dist/queries/hotspots.js +8 -0
- package/dist/queries/hotspots.js.map +1 -0
- package/dist/queries/imports.d.ts +19 -0
- package/dist/queries/imports.js +12 -0
- package/dist/queries/imports.js.map +1 -0
- package/dist/queries/index.d.ts +47 -0
- package/dist/queries/index.js +207 -0
- package/dist/queries/index.js.map +1 -0
- package/dist/queries/isolated.d.ts +14 -0
- package/dist/queries/isolated.js +9 -0
- package/dist/queries/isolated.js.map +1 -0
- package/dist/queries/members.d.ts +10 -0
- package/dist/queries/members.js +8 -0
- package/dist/queries/members.js.map +1 -0
- package/dist/queries/methods.d.ts +6 -0
- package/dist/queries/methods.js +8 -0
- package/dist/queries/methods.js.map +1 -0
- package/dist/queries/outline.d.ts +10 -0
- package/dist/queries/outline.js +8 -0
- package/dist/queries/outline.js.map +1 -0
- package/dist/queries/passthrough-candidates.d.ts +18 -0
- package/dist/queries/passthrough-candidates.js +9 -0
- package/dist/queries/passthrough-candidates.js.map +1 -0
- package/dist/queries/redundant-reexports.d.ts +22 -0
- package/dist/queries/redundant-reexports.js +8 -0
- package/dist/queries/redundant-reexports.js.map +1 -0
- package/dist/queries/refs.d.ts +6 -0
- package/dist/queries/refs.js +7 -0
- package/dist/queries/refs.js.map +1 -0
- package/dist/queries/similar-chains.d.ts +29 -0
- package/dist/queries/similar-chains.js +8 -0
- package/dist/queries/similar-chains.js.map +1 -0
- package/dist/queries/similar-files.d.ts +19 -0
- package/dist/queries/similar-files.js +8 -0
- package/dist/queries/similar-files.js.map +1 -0
- package/dist/queries/similar-signatures.d.ts +21 -0
- package/dist/queries/similar-signatures.js +8 -0
- package/dist/queries/similar-signatures.js.map +1 -0
- package/dist/queries/similar.d.ts +34 -0
- package/dist/queries/similar.js +11 -0
- package/dist/queries/similar.js.map +1 -0
- package/dist/queries/slice.d.ts +21 -0
- package/dist/queries/slice.js +9 -0
- package/dist/queries/slice.js.map +1 -0
- package/dist/queries/stale-abstractions.d.ts +18 -0
- package/dist/queries/stale-abstractions.js +9 -0
- package/dist/queries/stale-abstractions.js.map +1 -0
- package/dist/queries/stats.d.ts +6 -0
- package/dist/queries/stats.js +7 -0
- package/dist/queries/stats.js.map +1 -0
- package/dist/queries/surface.d.ts +7 -0
- package/dist/queries/surface.js +8 -0
- package/dist/queries/surface.js.map +1 -0
- package/dist/queries/symbols.d.ts +6 -0
- package/dist/queries/symbols.js +9 -0
- package/dist/queries/symbols.js.map +1 -0
- package/dist/queries/system.d.ts +7 -0
- package/dist/queries/system.js +9 -0
- package/dist/queries/system.js.map +1 -0
- package/dist/queries/test-coverage.d.ts +22 -0
- package/dist/queries/test-coverage.js +11 -0
- package/dist/queries/test-coverage.js.map +1 -0
- package/dist/queries/trace.d.ts +6 -0
- package/dist/queries/trace.js +8 -0
- package/dist/queries/trace.js.map +1 -0
- package/dist/queries/wrapper-candidates.d.ts +17 -0
- package/dist/queries/wrapper-candidates.js +9 -0
- package/dist/queries/wrapper-candidates.js.map +1 -0
- package/dist/reindex-worker.js +368 -0
- package/dist/reindex-worker.js.map +1 -0
- package/docs/AGENT_GUIDE.md +359 -0
- package/package.json +70 -0
- package/reports/debloat/2026-04-10-scip-query-self-audit.md +161 -0
- package/skills/concrete-plan/SKILL.md +318 -0
- package/skills/scip-debloat/SKILL.md +413 -0
- package/skills/scip-explore/SKILL.md +235 -0
- package/skills/scip-verify/SKILL.md +323 -0
- package/src/cli.ts +1480 -0
- package/src/config.ts +117 -0
- package/src/db.ts +127 -0
- package/src/gitignore-filter.ts +143 -0
- package/src/index.ts +11 -0
- package/src/postinstall.ts +8 -0
- package/src/queries/affected.ts +86 -0
- package/src/queries/bottlenecks.ts +67 -0
- package/src/queries/by-kind.ts +204 -0
- package/src/queries/call-graph.ts +66 -0
- package/src/queries/change-surface.ts +110 -0
- package/src/queries/clean-signature.ts +22 -0
- package/src/queries/code.ts +101 -0
- package/src/queries/complexity-hotspots.ts +119 -0
- package/src/queries/complexity.ts +152 -0
- package/src/queries/convergence.ts +82 -0
- package/src/queries/coupling.ts +99 -0
- package/src/queries/cycles.ts +78 -0
- package/src/queries/dataflow.ts +128 -0
- package/src/queries/dead.ts +122 -0
- package/src/queries/deep-chains.ts +59 -0
- package/src/queries/deps.ts +46 -0
- package/src/queries/diff-impact.ts +204 -0
- package/src/queries/doc-coverage.ts +86 -0
- package/src/queries/drift.ts +224 -0
- package/src/queries/extract-candidates.ts +167 -0
- package/src/queries/fan.ts +148 -0
- package/src/queries/files.ts +16 -0
- package/src/queries/health.ts +324 -0
- package/src/queries/hierarchy.ts +49 -0
- package/src/queries/hotspots.ts +53 -0
- package/src/queries/imports.ts +95 -0
- package/src/queries/index.ts +45 -0
- package/src/queries/isolated.ts +67 -0
- package/src/queries/members.ts +54 -0
- package/src/queries/methods.ts +27 -0
- package/src/queries/outline.ts +52 -0
- package/src/queries/passthrough-candidates.ts +94 -0
- package/src/queries/redundant-reexports.ts +170 -0
- package/src/queries/refs.ts +27 -0
- package/src/queries/similar-chains.ts +314 -0
- package/src/queries/similar-files.ts +140 -0
- package/src/queries/similar-signatures.ts +151 -0
- package/src/queries/similar.ts +305 -0
- package/src/queries/slice.ts +154 -0
- package/src/queries/stale-abstractions.ts +82 -0
- package/src/queries/stats.ts +22 -0
- package/src/queries/surface.ts +34 -0
- package/src/queries/symbols.ts +39 -0
- package/src/queries/system.ts +86 -0
- package/src/queries/test-coverage.ts +106 -0
- package/src/queries/trace.ts +55 -0
- package/src/queries/wrapper-candidates.ts +112 -0
- package/src/query-support.ts +226 -0
- package/src/reindex/detect.ts +58 -0
- package/src/reindex/index.ts +153 -0
- package/src/reindex/indexers.ts +220 -0
- package/src/reindex/install.ts +125 -0
- package/src/reindex-worker.ts +35 -0
- package/src/setup.ts +202 -0
- package/src/symbol-parser.ts +278 -0
- package/src/types.ts +654 -0
- package/src/watch.ts +274 -0
- package/tests/gitignore-filter.test.ts +48 -0
- package/tests/queries.test.ts +300 -0
- package/tests/symbol-parser.test.ts +157 -0
- package/tsconfig.json +20 -0
- package/tsup.config.ts +40 -0
- package/vitest.config.ts +7 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/queries/hotspots.ts"],"sourcesContent":["import type { ScipDatabase } from '../db.js';\nimport type { HotspotResult } from '../types.js';\nimport { shortenSymbol } from '../symbol-parser.js';\n\n/**\n * Find the most-referenced symbols in the codebase — the choke points\n * where changes have the widest blast radius.\n */\nexport function hotspots(\n db: ScipDatabase,\n opts: { limit?: number; scope?: string } = {},\n): HotspotResult[] {\n const { limit = 30, scope } = opts;\n\n const scopeFilter = scope ? `AND def_d.relative_path LIKE '%${scope}%'` : '';\n\n const rows = db.all<{\n symbol: string;\n ref_count: number;\n file_count: number;\n defined_in: string;\n }>(\n `SELECT\n gs.symbol,\n COUNT(*) AS ref_count,\n COUNT(DISTINCT ref_d.id) AS file_count,\n def_d.relative_path AS defined_in\n FROM mentions m\n JOIN chunks c ON m.chunk_id = c.id\n JOIN documents ref_d ON c.document_id = ref_d.id\n JOIN global_symbols gs ON m.symbol_id = gs.id\n JOIN defn_enclosing_ranges der ON gs.id = der.symbol_id\n JOIN documents def_d ON der.document_id = def_d.id\n WHERE m.role = 0\n ${db.pathExclusionsFor('def_d')}\n ${db.symbolNoiseFor('gs')}\n ${scopeFilter}\n GROUP BY gs.id\n ORDER BY ref_count DESC\n LIMIT ?`,\n limit,\n );\n\n return rows\n .filter((r) => !db.isIgnored(r.defined_in))\n .map((r) => ({\n symbol: r.symbol,\n shortName: shortenSymbol(r.symbol),\n refCount: r.ref_count,\n fileCount: r.file_count,\n definedIn: r.defined_in,\n }));\n}\n"],"mappings":";;;;;AAQO,SAAS,SACd,IACA,OAA2C,CAAC,GAC3B;AACjB,QAAM,EAAE,QAAQ,IAAI,MAAM,IAAI;AAE9B,QAAM,cAAc,QAAQ,kCAAkC,KAAK,OAAO;AAE1E,QAAM,OAAO,GAAG;AAAA,IAMd;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,QAYI,GAAG,kBAAkB,OAAO,CAAC;AAAA,QAC7B,GAAG,eAAe,IAAI,CAAC;AAAA,QACvB,WAAW;AAAA;AAAA;AAAA;AAAA,IAIf;AAAA,EACF;AAEA,SAAO,KACJ,OAAO,CAAC,MAAM,CAAC,GAAG,UAAU,EAAE,UAAU,CAAC,EACzC,IAAI,CAAC,OAAO;AAAA,IACX,QAAQ,EAAE;AAAA,IACV,WAAW,cAAc,EAAE,MAAM;AAAA,IACjC,UAAU,EAAE;AAAA,IACZ,WAAW,EAAE;AAAA,IACb,WAAW,EAAE;AAAA,EACf,EAAE;AACN;","names":[]}
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
import {
|
|
2
|
+
findFirstSymbolMatch,
|
|
3
|
+
getCalleeRowsForSymbol
|
|
4
|
+
} from "./chunk-FUHJCHS4.js";
|
|
5
|
+
import {
|
|
6
|
+
shortenSymbol
|
|
7
|
+
} from "./chunk-QOV2R2WT.js";
|
|
8
|
+
|
|
9
|
+
// src/queries/complexity.ts
|
|
10
|
+
import { readFileSync } from "fs";
|
|
11
|
+
import { join } from "path";
|
|
12
|
+
function complexity(db, symbolPattern) {
|
|
13
|
+
const match = findFirstSymbolMatch(db, symbolPattern);
|
|
14
|
+
if (!match) return null;
|
|
15
|
+
const doc = db.get(
|
|
16
|
+
`SELECT language FROM documents WHERE relative_path = ?`,
|
|
17
|
+
match.relativePath
|
|
18
|
+
);
|
|
19
|
+
const language = doc?.language ?? "unknown";
|
|
20
|
+
const filePath = join(db.config.projectRoot, match.relativePath);
|
|
21
|
+
let source = "";
|
|
22
|
+
try {
|
|
23
|
+
const lines = readFileSync(filePath, "utf-8").split("\n");
|
|
24
|
+
source = lines.slice(match.startLine, match.endLine + 1).join("\n");
|
|
25
|
+
} catch {
|
|
26
|
+
}
|
|
27
|
+
const branches = countBranches(source, language);
|
|
28
|
+
const loc = match.endLine - match.startLine + 1;
|
|
29
|
+
const callees = getCalleeRowsForSymbol(db, match);
|
|
30
|
+
const uniqueCallees = new Set(callees.map((c) => c.symbol));
|
|
31
|
+
const fanInRow = db.get(
|
|
32
|
+
`SELECT COUNT(DISTINCT c.document_id) AS c
|
|
33
|
+
FROM mentions m
|
|
34
|
+
JOIN chunks c ON m.chunk_id = c.id
|
|
35
|
+
WHERE m.symbol_id = ? AND m.role = 0`,
|
|
36
|
+
match.symbolId
|
|
37
|
+
);
|
|
38
|
+
const fanOut = new Set(
|
|
39
|
+
callees.filter((c) => c.file !== match.relativePath).map((c) => c.symbol)
|
|
40
|
+
).size;
|
|
41
|
+
return {
|
|
42
|
+
symbol: match.symbol,
|
|
43
|
+
shortName: shortenSymbol(match.symbol),
|
|
44
|
+
relativePath: match.relativePath,
|
|
45
|
+
startLine: match.startLine,
|
|
46
|
+
endLine: match.endLine,
|
|
47
|
+
loc,
|
|
48
|
+
branches,
|
|
49
|
+
cyclomaticEstimate: branches + 1,
|
|
50
|
+
calleeCount: uniqueCallees.size,
|
|
51
|
+
fanIn: fanInRow?.c ?? 0,
|
|
52
|
+
fanOut
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
function countBranches(source, language) {
|
|
56
|
+
const stripped = stripCommentsAndStrings(source);
|
|
57
|
+
let count = 0;
|
|
58
|
+
const universalPatterns = [
|
|
59
|
+
/\bif\b/g,
|
|
60
|
+
/\belse\s+if\b/g,
|
|
61
|
+
/\belse\b/g,
|
|
62
|
+
/\bfor\b/g,
|
|
63
|
+
/\bwhile\b/g,
|
|
64
|
+
/\bswitch\b/g,
|
|
65
|
+
/\bcase\b/g,
|
|
66
|
+
/\bcatch\b/g,
|
|
67
|
+
/\?\s*[^?]/g,
|
|
68
|
+
// ternary (but not ??)
|
|
69
|
+
/&&/g,
|
|
70
|
+
/\|\|/g
|
|
71
|
+
];
|
|
72
|
+
for (const pattern of universalPatterns) {
|
|
73
|
+
const matches = stripped.match(pattern);
|
|
74
|
+
if (matches) count += matches.length;
|
|
75
|
+
}
|
|
76
|
+
if (language === "python") {
|
|
77
|
+
const pyPatterns = [/\belif\b/g, /\bexcept\b/g, /\bfinally\b/g];
|
|
78
|
+
for (const p of pyPatterns) {
|
|
79
|
+
const m = stripped.match(p);
|
|
80
|
+
if (m) count += m.length;
|
|
81
|
+
}
|
|
82
|
+
} else if (language === "rust") {
|
|
83
|
+
const rustPatterns = [/\bmatch\b/g, /=>/g, /\bloop\b/g];
|
|
84
|
+
for (const p of rustPatterns) {
|
|
85
|
+
const m = stripped.match(p);
|
|
86
|
+
if (m) count += m.length;
|
|
87
|
+
}
|
|
88
|
+
} else if (language === "ruby") {
|
|
89
|
+
const rubyPatterns = [/\belsif\b/g, /\bunless\b/g, /\brescue\b/g, /\bwhen\b/g];
|
|
90
|
+
for (const p of rubyPatterns) {
|
|
91
|
+
const m = stripped.match(p);
|
|
92
|
+
if (m) count += m.length;
|
|
93
|
+
}
|
|
94
|
+
} else if (language === "go") {
|
|
95
|
+
const goPatterns = [/\bselect\b/g, /\bdefer\b/g];
|
|
96
|
+
for (const p of goPatterns) {
|
|
97
|
+
const m = stripped.match(p);
|
|
98
|
+
if (m) count += m.length;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
return count;
|
|
102
|
+
}
|
|
103
|
+
function stripCommentsAndStrings(source) {
|
|
104
|
+
return source.replace(/\/\*[\s\S]*?\*\//g, "").replace(/\/\/.*/g, "").replace(/#.*/g, "").replace(/"(?:[^"\\]|\\.)*"/g, '""').replace(/'(?:[^'\\]|\\.)*'/g, "''").replace(/`(?:[^`\\]|\\.)*`/g, "``");
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
export {
|
|
108
|
+
complexity
|
|
109
|
+
};
|
|
110
|
+
//# sourceMappingURL=chunk-BP2ATLK2.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/queries/complexity.ts"],"sourcesContent":["import { readFileSync } from 'node:fs';\nimport { join } from 'node:path';\nimport type { ScipDatabase } from '../db.js';\nimport { findFirstSymbolMatch, getCalleeRowsForSymbol } from '../query-support.js';\nimport type { ComplexityResult } from '../types.js';\nimport { shortenSymbol } from '../symbol-parser.js';\n\n/**\n * Per-symbol complexity analysis combining source-level branch counting\n * with index-level metrics (fan-in, fan-out, callee count).\n *\n * Branch counting uses language-aware regex. The language is read from\n * the SCIP documents table, so it works for any indexed language.\n */\nexport function complexity(\n db: ScipDatabase,\n symbolPattern: string,\n): ComplexityResult | null {\n const match = findFirstSymbolMatch(db, symbolPattern);\n if (!match) return null;\n\n // Get language\n const doc = db.get<{ language: string | null }>(\n `SELECT language FROM documents WHERE relative_path = ?`,\n match.relativePath,\n );\n const language = doc?.language ?? 'unknown';\n\n // Read source for branch counting\n const filePath = join(db.config.projectRoot, match.relativePath);\n let source = '';\n try {\n const lines = readFileSync(filePath, 'utf-8').split('\\n');\n source = lines.slice(match.startLine, match.endLine + 1).join('\\n');\n } catch {\n // If we can't read the file, just skip branch counting\n }\n\n const branches = countBranches(source, language);\n const loc = match.endLine - match.startLine + 1;\n\n // Callee count\n const callees = getCalleeRowsForSymbol(db, match);\n const uniqueCallees = new Set(callees.map((c) => c.symbol));\n\n // Fan-in\n const fanInRow = db.get<{ c: number }>(\n `SELECT COUNT(DISTINCT c.document_id) AS c\n FROM mentions m\n JOIN chunks c ON m.chunk_id = c.id\n WHERE m.symbol_id = ? AND m.role = 0`,\n match.symbolId,\n );\n\n // Fan-out (callees in other files)\n const fanOut = new Set(\n callees.filter((c) => c.file !== match.relativePath).map((c) => c.symbol),\n ).size;\n\n return {\n symbol: match.symbol,\n shortName: shortenSymbol(match.symbol),\n relativePath: match.relativePath,\n startLine: match.startLine,\n endLine: match.endLine,\n loc,\n branches,\n cyclomaticEstimate: branches + 1,\n calleeCount: uniqueCallees.size,\n fanIn: fanInRow?.c ?? 0,\n fanOut,\n };\n}\n\n/**\n * Count branch points in source code using language-aware regex.\n * Works across all SCIP-supported languages.\n */\nfunction countBranches(source: string, language: string): number {\n // Strip comments and strings to avoid false positives\n const stripped = stripCommentsAndStrings(source);\n let count = 0;\n\n // Universal branch keywords (work across most C-family languages)\n const universalPatterns = [\n /\\bif\\b/g,\n /\\belse\\s+if\\b/g,\n /\\belse\\b/g,\n /\\bfor\\b/g,\n /\\bwhile\\b/g,\n /\\bswitch\\b/g,\n /\\bcase\\b/g,\n /\\bcatch\\b/g,\n /\\?\\s*[^?]/g, // ternary (but not ??)\n /&&/g,\n /\\|\\|/g,\n ];\n\n for (const pattern of universalPatterns) {\n const matches = stripped.match(pattern);\n if (matches) count += matches.length;\n }\n\n // Language-specific patterns\n if (language === 'python') {\n const pyPatterns = [/\\belif\\b/g, /\\bexcept\\b/g, /\\bfinally\\b/g];\n for (const p of pyPatterns) {\n const m = stripped.match(p);\n if (m) count += m.length;\n }\n } else if (language === 'rust') {\n const rustPatterns = [/\\bmatch\\b/g, /=>/g, /\\bloop\\b/g];\n for (const p of rustPatterns) {\n const m = stripped.match(p);\n if (m) count += m.length;\n }\n } else if (language === 'ruby') {\n const rubyPatterns = [/\\belsif\\b/g, /\\bunless\\b/g, /\\brescue\\b/g, /\\bwhen\\b/g];\n for (const p of rubyPatterns) {\n const m = stripped.match(p);\n if (m) count += m.length;\n }\n } else if (language === 'go') {\n const goPatterns = [/\\bselect\\b/g, /\\bdefer\\b/g];\n for (const p of goPatterns) {\n const m = stripped.match(p);\n if (m) count += m.length;\n }\n }\n\n return count;\n}\n\n/**\n * Rough strip of comments and string literals to reduce false positives\n * in branch counting. Not perfect but good enough for estimation.\n */\nfunction stripCommentsAndStrings(source: string): string {\n return source\n // Block comments\n .replace(/\\/\\*[\\s\\S]*?\\*\\//g, '')\n // Line comments\n .replace(/\\/\\/.*/g, '')\n // Python/Ruby line comments\n .replace(/#.*/g, '')\n // Double-quoted strings\n .replace(/\"(?:[^\"\\\\]|\\\\.)*\"/g, '\"\"')\n // Single-quoted strings\n .replace(/'(?:[^'\\\\]|\\\\.)*'/g, \"''\")\n // Template literals\n .replace(/`(?:[^`\\\\]|\\\\.)*`/g, '``');\n}\n"],"mappings":"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","names":[]}
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
import {
|
|
2
|
+
getCalleeRowsForSymbol
|
|
3
|
+
} from "./chunk-FUHJCHS4.js";
|
|
4
|
+
import {
|
|
5
|
+
shortenSymbol
|
|
6
|
+
} from "./chunk-QOV2R2WT.js";
|
|
7
|
+
|
|
8
|
+
// src/queries/extract-candidates.ts
|
|
9
|
+
function extractCandidates(db, opts = {}) {
|
|
10
|
+
const { scope, minLoc = 10, minCallees = 6, limit = 20 } = opts;
|
|
11
|
+
const scopeFilter = scope ? `AND d.relative_path LIKE '%${scope}%'` : "";
|
|
12
|
+
const symbols = db.all(
|
|
13
|
+
`SELECT gs.id, gs.symbol, der.document_id, der.start_line, der.end_line, d.relative_path
|
|
14
|
+
FROM global_symbols gs
|
|
15
|
+
JOIN defn_enclosing_ranges der ON gs.id = der.symbol_id
|
|
16
|
+
JOIN documents d ON der.document_id = d.id
|
|
17
|
+
WHERE 1 = 1
|
|
18
|
+
${db.pathExclusionsFor("d")}
|
|
19
|
+
${db.symbolNoiseFor("gs")}
|
|
20
|
+
AND (der.end_line - der.start_line + 1) >= ?
|
|
21
|
+
${scopeFilter}
|
|
22
|
+
ORDER BY (der.end_line - der.start_line + 1) DESC`,
|
|
23
|
+
minLoc
|
|
24
|
+
);
|
|
25
|
+
const results = [];
|
|
26
|
+
for (const sym of symbols) {
|
|
27
|
+
if (db.isIgnored(sym.relative_path)) continue;
|
|
28
|
+
const basename = sym.relative_path.split("/").pop() ?? "";
|
|
29
|
+
if (basename.includes("types")) continue;
|
|
30
|
+
const calleeChunks = getCalleeRowsForSymbol(db, {
|
|
31
|
+
documentId: sym.document_id,
|
|
32
|
+
startLine: sym.start_line,
|
|
33
|
+
endLine: sym.end_line,
|
|
34
|
+
symbolId: sym.id
|
|
35
|
+
});
|
|
36
|
+
const calleeSet = new Set(calleeChunks.map((c) => c.symbol));
|
|
37
|
+
if (calleeSet.size < minCallees) continue;
|
|
38
|
+
const cooccurrence = /* @__PURE__ */ new Map();
|
|
39
|
+
for (const callee of calleeSet) {
|
|
40
|
+
cooccurrence.set(callee, /* @__PURE__ */ new Set());
|
|
41
|
+
}
|
|
42
|
+
const chunkToCallees = /* @__PURE__ */ new Map();
|
|
43
|
+
for (const cc of calleeChunks) {
|
|
44
|
+
if (!chunkToCallees.has(cc.chunkId)) chunkToCallees.set(cc.chunkId, /* @__PURE__ */ new Set());
|
|
45
|
+
chunkToCallees.get(cc.chunkId).add(cc.symbol);
|
|
46
|
+
}
|
|
47
|
+
for (const callees of chunkToCallees.values()) {
|
|
48
|
+
const arr = [...callees];
|
|
49
|
+
for (let i = 0; i < arr.length; i++) {
|
|
50
|
+
for (let j = i + 1; j < arr.length; j++) {
|
|
51
|
+
cooccurrence.get(arr[i]).add(arr[j]);
|
|
52
|
+
cooccurrence.get(arr[j]).add(arr[i]);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
const visited = /* @__PURE__ */ new Set();
|
|
57
|
+
const clusters = [];
|
|
58
|
+
for (const callee of calleeSet) {
|
|
59
|
+
if (visited.has(callee)) continue;
|
|
60
|
+
const cluster = /* @__PURE__ */ new Set();
|
|
61
|
+
const queue = [callee];
|
|
62
|
+
while (queue.length > 0) {
|
|
63
|
+
const current = queue.pop();
|
|
64
|
+
if (visited.has(current)) continue;
|
|
65
|
+
visited.add(current);
|
|
66
|
+
cluster.add(current);
|
|
67
|
+
for (const neighbor of cooccurrence.get(current) ?? []) {
|
|
68
|
+
if (!visited.has(neighbor)) queue.push(neighbor);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
clusters.push(cluster);
|
|
72
|
+
}
|
|
73
|
+
if (clusters.length < 2) continue;
|
|
74
|
+
const scoredClusters = clusters.filter((c) => c.size >= 2).map((cluster) => {
|
|
75
|
+
const otherCallees = /* @__PURE__ */ new Set();
|
|
76
|
+
for (const c of clusters) {
|
|
77
|
+
if (c !== cluster) {
|
|
78
|
+
for (const s of c) otherCallees.add(s);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
let crossEdges = 0;
|
|
82
|
+
for (const callee of cluster) {
|
|
83
|
+
for (const neighbor of cooccurrence.get(callee) ?? []) {
|
|
84
|
+
if (otherCallees.has(neighbor)) crossEdges++;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
const maxCrossEdges = cluster.size * otherCallees.size;
|
|
88
|
+
const isolation = maxCrossEdges > 0 ? 1 - crossEdges / maxCrossEdges : 1;
|
|
89
|
+
return {
|
|
90
|
+
callees: [...cluster].map(shortenSymbol),
|
|
91
|
+
isolation
|
|
92
|
+
};
|
|
93
|
+
}).filter((c) => c.isolation > 0.5).sort((a, b) => b.isolation - a.isolation);
|
|
94
|
+
if (scoredClusters.length > 0) {
|
|
95
|
+
results.push({
|
|
96
|
+
symbol: sym.symbol,
|
|
97
|
+
shortName: shortenSymbol(sym.symbol),
|
|
98
|
+
relativePath: sym.relative_path,
|
|
99
|
+
startLine: sym.start_line,
|
|
100
|
+
endLine: sym.end_line,
|
|
101
|
+
loc: sym.end_line - sym.start_line + 1,
|
|
102
|
+
totalCallees: calleeSet.size,
|
|
103
|
+
clusters: scoredClusters
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
results.sort((a, b) => b.clusters.length - a.clusters.length || b.loc - a.loc);
|
|
108
|
+
return results.slice(0, limit);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
export {
|
|
112
|
+
extractCandidates
|
|
113
|
+
};
|
|
114
|
+
//# sourceMappingURL=chunk-CM454WL3.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/queries/extract-candidates.ts"],"sourcesContent":["import type { ScipDatabase } from '../db.js';\nimport { getCalleeRowsForSymbol } from '../query-support.js';\nimport type { ExtractCandidate } from '../types.js';\nimport { shortenSymbol } from '../symbol-parser.js';\n\n/**\n * Find functions with natural extraction seams.\n *\n * A large function that references two distinct groups of symbols —\n * where group A's symbols are never co-referenced with group B's —\n * has a natural extraction boundary. The isolated cluster can likely\n * be pulled into its own function.\n *\n * We detect this by:\n * 1. Finding all callees of a function\n * 2. Building a co-occurrence graph (which callees appear in the same chunk)\n * 3. Finding connected components — disconnected components = extraction seams\n * 4. Scoring each cluster by how isolated it is from the rest\n */\nexport function extractCandidates(\n db: ScipDatabase,\n opts: { scope?: string; minLoc?: number; minCallees?: number; limit?: number } = {},\n): ExtractCandidate[] {\n const { scope, minLoc = 10, minCallees = 6, limit = 20 } = opts;\n const scopeFilter = scope ? `AND d.relative_path LIKE '%${scope}%'` : '';\n\n // Find functions large enough to consider\n const symbols = db.all<{\n id: number;\n symbol: string;\n document_id: number;\n start_line: number;\n end_line: number;\n relative_path: string;\n }>(\n `SELECT gs.id, gs.symbol, der.document_id, der.start_line, der.end_line, d.relative_path\n FROM global_symbols gs\n JOIN defn_enclosing_ranges der ON gs.id = der.symbol_id\n JOIN documents d ON der.document_id = d.id\n WHERE 1 = 1\n ${db.pathExclusionsFor('d')}\n ${db.symbolNoiseFor('gs')}\n AND (der.end_line - der.start_line + 1) >= ?\n ${scopeFilter}\n ORDER BY (der.end_line - der.start_line + 1) DESC`,\n minLoc,\n );\n\n const results: ExtractCandidate[] = [];\n\n for (const sym of symbols) {\n if (db.isIgnored(sym.relative_path)) continue;\n\n // Skip pure type files — \"callees\" in a type file are just type references,\n // not function calls. Splitting type files is a cosmetic choice, not an\n // extraction opportunity.\n const basename = sym.relative_path.split('/').pop() ?? '';\n if (basename.includes('types')) continue;\n\n // Get callees with their chunk locations (to build co-occurrence)\n const calleeChunks = getCalleeRowsForSymbol(db, {\n documentId: sym.document_id,\n startLine: sym.start_line,\n endLine: sym.end_line,\n symbolId: sym.id,\n });\n\n // Collect unique callees\n const calleeSet = new Set(calleeChunks.map((c) => c.symbol));\n if (calleeSet.size < minCallees) continue;\n\n // Build co-occurrence graph: two callees are connected if they\n // appear in the same chunk (meaning they're used in proximity)\n const cooccurrence = new Map<string, Set<string>>();\n for (const callee of calleeSet) {\n cooccurrence.set(callee, new Set());\n }\n\n // Group by chunk\n const chunkToCallees = new Map<number, Set<string>>();\n for (const cc of calleeChunks) {\n if (!chunkToCallees.has(cc.chunkId)) chunkToCallees.set(cc.chunkId, new Set());\n chunkToCallees.get(cc.chunkId)!.add(cc.symbol);\n }\n\n // Callees in the same chunk are co-occurring\n for (const callees of chunkToCallees.values()) {\n const arr = [...callees];\n for (let i = 0; i < arr.length; i++) {\n for (let j = i + 1; j < arr.length; j++) {\n cooccurrence.get(arr[i]!)!.add(arr[j]!);\n cooccurrence.get(arr[j]!)!.add(arr[i]!);\n }\n }\n }\n\n // Find connected components via BFS\n const visited = new Set<string>();\n const clusters: Set<string>[] = [];\n\n for (const callee of calleeSet) {\n if (visited.has(callee)) continue;\n const cluster = new Set<string>();\n const queue = [callee];\n while (queue.length > 0) {\n const current = queue.pop()!;\n if (visited.has(current)) continue;\n visited.add(current);\n cluster.add(current);\n for (const neighbor of cooccurrence.get(current) ?? []) {\n if (!visited.has(neighbor)) queue.push(neighbor);\n }\n }\n clusters.push(cluster);\n }\n\n // Only interesting if there are multiple clusters (= extraction seams exist)\n if (clusters.length < 2) continue;\n\n // Score each cluster by isolation:\n // isolation = 1 - (edges to other clusters / total possible edges to other clusters)\n const scoredClusters = clusters\n .filter((c) => c.size >= 2) // single-callee clusters aren't interesting\n .map((cluster) => {\n const otherCallees = new Set<string>();\n for (const c of clusters) {\n if (c !== cluster) {\n for (const s of c) otherCallees.add(s);\n }\n }\n\n // Count cross-cluster edges\n let crossEdges = 0;\n for (const callee of cluster) {\n for (const neighbor of cooccurrence.get(callee) ?? []) {\n if (otherCallees.has(neighbor)) crossEdges++;\n }\n }\n\n const maxCrossEdges = cluster.size * otherCallees.size;\n const isolation = maxCrossEdges > 0 ? 1 - crossEdges / maxCrossEdges : 1;\n\n return {\n callees: [...cluster].map(shortenSymbol),\n isolation,\n };\n })\n .filter((c) => c.isolation > 0.5) // Only report well-isolated clusters\n .sort((a, b) => b.isolation - a.isolation);\n\n if (scoredClusters.length > 0) {\n results.push({\n symbol: sym.symbol,\n shortName: shortenSymbol(sym.symbol),\n relativePath: sym.relative_path,\n startLine: sym.start_line,\n endLine: sym.end_line,\n loc: sym.end_line - sym.start_line + 1,\n totalCallees: calleeSet.size,\n clusters: scoredClusters,\n });\n }\n }\n\n results.sort((a, b) => b.clusters.length - a.clusters.length || b.loc - a.loc);\n return results.slice(0, limit);\n}\n"],"mappings":"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","names":[]}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import {
|
|
2
|
+
cleanSignature
|
|
3
|
+
} from "./chunk-4TYLS5XX.js";
|
|
4
|
+
import {
|
|
5
|
+
shortenSymbol
|
|
6
|
+
} from "./chunk-QOV2R2WT.js";
|
|
7
|
+
|
|
8
|
+
// src/queries/system.ts
|
|
9
|
+
function system(db, modulePattern) {
|
|
10
|
+
const fileRows = db.all(
|
|
11
|
+
`SELECT relative_path FROM documents
|
|
12
|
+
WHERE relative_path LIKE ?
|
|
13
|
+
ORDER BY relative_path`,
|
|
14
|
+
`%${modulePattern}%`
|
|
15
|
+
);
|
|
16
|
+
const files = fileRows.map((r) => r.relative_path).filter((p) => !db.isIgnored(p));
|
|
17
|
+
const symbolRows = db.all(
|
|
18
|
+
`SELECT der.start_line, der.end_line, gs.symbol,
|
|
19
|
+
REPLACE(SUBSTR(gs.documentation, INSTR(gs.documentation, '|') + 1), char(10), ' ') AS sig
|
|
20
|
+
FROM defn_enclosing_ranges der
|
|
21
|
+
JOIN global_symbols gs ON der.symbol_id = gs.id
|
|
22
|
+
JOIN documents d ON der.document_id = d.id
|
|
23
|
+
WHERE d.relative_path LIKE ?
|
|
24
|
+
AND ${db.localSymbolPredicate}
|
|
25
|
+
${db.symbolNoise}
|
|
26
|
+
AND gs.documentation IS NOT NULL
|
|
27
|
+
ORDER BY d.relative_path, der.start_line`,
|
|
28
|
+
`%${modulePattern}%`
|
|
29
|
+
);
|
|
30
|
+
const symbols = symbolRows.map((r) => ({
|
|
31
|
+
startLine: r.start_line,
|
|
32
|
+
endLine: r.end_line,
|
|
33
|
+
symbol: r.symbol,
|
|
34
|
+
shortName: shortenSymbol(r.symbol),
|
|
35
|
+
signature: cleanSignature(r.sig)
|
|
36
|
+
}));
|
|
37
|
+
const depRows = db.all(
|
|
38
|
+
`SELECT DISTINCT d2.relative_path
|
|
39
|
+
FROM mentions m
|
|
40
|
+
JOIN chunks c ON m.chunk_id = c.id
|
|
41
|
+
JOIN documents d1 ON c.document_id = d1.id
|
|
42
|
+
JOIN global_symbols gs ON m.symbol_id = gs.id
|
|
43
|
+
JOIN defn_enclosing_ranges der ON gs.id = der.symbol_id
|
|
44
|
+
JOIN documents d2 ON der.document_id = d2.id
|
|
45
|
+
WHERE d1.relative_path LIKE ?
|
|
46
|
+
AND d2.relative_path NOT LIKE ?
|
|
47
|
+
AND ${db.localSymbolPredicate}
|
|
48
|
+
ORDER BY d2.relative_path`,
|
|
49
|
+
`%${modulePattern}%`,
|
|
50
|
+
`%${modulePattern}%`
|
|
51
|
+
);
|
|
52
|
+
const dependsOn = depRows.map((r) => r.relative_path).filter((p) => !db.isIgnored(p));
|
|
53
|
+
const rdepRows = db.all(
|
|
54
|
+
`SELECT DISTINCT d1.relative_path
|
|
55
|
+
FROM mentions m
|
|
56
|
+
JOIN chunks c ON m.chunk_id = c.id
|
|
57
|
+
JOIN documents d1 ON c.document_id = d1.id
|
|
58
|
+
JOIN global_symbols gs ON m.symbol_id = gs.id
|
|
59
|
+
JOIN defn_enclosing_ranges der ON gs.id = der.symbol_id
|
|
60
|
+
JOIN documents d2 ON der.document_id = d2.id
|
|
61
|
+
WHERE d2.relative_path LIKE ?
|
|
62
|
+
AND d1.relative_path NOT LIKE ?
|
|
63
|
+
ORDER BY d1.relative_path`,
|
|
64
|
+
`%${modulePattern}%`,
|
|
65
|
+
`%${modulePattern}%`
|
|
66
|
+
);
|
|
67
|
+
const dependedOnBy = rdepRows.map((r) => r.relative_path).filter((p) => !db.isIgnored(p));
|
|
68
|
+
return { files, symbols, dependsOn, dependedOnBy };
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export {
|
|
72
|
+
system
|
|
73
|
+
};
|
|
74
|
+
//# sourceMappingURL=chunk-DCKMSTJ4.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/queries/system.ts"],"sourcesContent":["import type { ScipDatabase } from '../db.js';\nimport type { SystemResult, SymbolResult } from '../types.js';\nimport { shortenSymbol } from '../symbol-parser.js';\nimport { cleanSignature } from './clean-signature.js';\n\n/** Full system map for a module path: files, symbols, deps in/out */\nexport function system(db: ScipDatabase, modulePattern: string): SystemResult {\n // Files in this module\n const fileRows = db.all<{ relative_path: string }>(\n `SELECT relative_path FROM documents\n WHERE relative_path LIKE ?\n ORDER BY relative_path`,\n `%${modulePattern}%`,\n );\n const files = fileRows\n .map((r) => r.relative_path)\n .filter((p) => !db.isIgnored(p));\n\n // Exported symbols\n const symbolRows = db.all<{\n start_line: number;\n end_line: number;\n symbol: string;\n sig: string | null;\n }>(\n `SELECT der.start_line, der.end_line, gs.symbol,\n REPLACE(SUBSTR(gs.documentation, INSTR(gs.documentation, '|') + 1), char(10), ' ') AS sig\n FROM defn_enclosing_ranges der\n JOIN global_symbols gs ON der.symbol_id = gs.id\n JOIN documents d ON der.document_id = d.id\n WHERE d.relative_path LIKE ?\n AND ${db.localSymbolPredicate}\n ${db.symbolNoise}\n AND gs.documentation IS NOT NULL\n ORDER BY d.relative_path, der.start_line`,\n `%${modulePattern}%`,\n );\n const symbols: SymbolResult[] = symbolRows.map((r) => ({\n startLine: r.start_line,\n endLine: r.end_line,\n symbol: r.symbol,\n shortName: shortenSymbol(r.symbol),\n signature: cleanSignature(r.sig),\n }));\n\n // Internal dependencies (what this module depends on)\n const depRows = db.all<{ relative_path: string }>(\n `SELECT DISTINCT d2.relative_path\n FROM mentions m\n JOIN chunks c ON m.chunk_id = c.id\n JOIN documents d1 ON c.document_id = d1.id\n JOIN global_symbols gs ON m.symbol_id = gs.id\n JOIN defn_enclosing_ranges der ON gs.id = der.symbol_id\n JOIN documents d2 ON der.document_id = d2.id\n WHERE d1.relative_path LIKE ?\n AND d2.relative_path NOT LIKE ?\n AND ${db.localSymbolPredicate}\n ORDER BY d2.relative_path`,\n `%${modulePattern}%`,\n `%${modulePattern}%`,\n );\n const dependsOn = depRows\n .map((r) => r.relative_path)\n .filter((p) => !db.isIgnored(p));\n\n // Reverse dependencies (who depends on this module)\n const rdepRows = db.all<{ relative_path: string }>(\n `SELECT DISTINCT d1.relative_path\n FROM mentions m\n JOIN chunks c ON m.chunk_id = c.id\n JOIN documents d1 ON c.document_id = d1.id\n JOIN global_symbols gs ON m.symbol_id = gs.id\n JOIN defn_enclosing_ranges der ON gs.id = der.symbol_id\n JOIN documents d2 ON der.document_id = d2.id\n WHERE d2.relative_path LIKE ?\n AND d1.relative_path NOT LIKE ?\n ORDER BY d1.relative_path`,\n `%${modulePattern}%`,\n `%${modulePattern}%`,\n );\n const dependedOnBy = rdepRows\n .map((r) => r.relative_path)\n .filter((p) => !db.isIgnored(p));\n\n return { files, symbols, dependsOn, dependedOnBy };\n}\n"],"mappings":";;;;;;;;AAMO,SAAS,OAAO,IAAkB,eAAqC;AAE5E,QAAM,WAAW,GAAG;AAAA,IAClB;AAAA;AAAA;AAAA,IAGA,IAAI,aAAa;AAAA,EACnB;AACA,QAAM,QAAQ,SACX,IAAI,CAAC,MAAM,EAAE,aAAa,EAC1B,OAAO,CAAC,MAAM,CAAC,GAAG,UAAU,CAAC,CAAC;AAGjC,QAAM,aAAa,GAAG;AAAA,IAMpB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,YAMQ,GAAG,oBAAoB;AAAA,QAC3B,GAAG,WAAW;AAAA;AAAA;AAAA,IAGlB,IAAI,aAAa;AAAA,EACnB;AACA,QAAM,UAA0B,WAAW,IAAI,CAAC,OAAO;AAAA,IACrD,WAAW,EAAE;AAAA,IACb,SAAS,EAAE;AAAA,IACX,QAAQ,EAAE;AAAA,IACV,WAAW,cAAc,EAAE,MAAM;AAAA,IACjC,WAAW,eAAe,EAAE,GAAG;AAAA,EACjC,EAAE;AAGF,QAAM,UAAU,GAAG;AAAA,IACjB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,YASQ,GAAG,oBAAoB;AAAA;AAAA,IAE/B,IAAI,aAAa;AAAA,IACjB,IAAI,aAAa;AAAA,EACnB;AACA,QAAM,YAAY,QACf,IAAI,CAAC,MAAM,EAAE,aAAa,EAC1B,OAAO,CAAC,MAAM,CAAC,GAAG,UAAU,CAAC,CAAC;AAGjC,QAAM,WAAW,GAAG;AAAA,IAClB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAUA,IAAI,aAAa;AAAA,IACjB,IAAI,aAAa;AAAA,EACnB;AACA,QAAM,eAAe,SAClB,IAAI,CAAC,MAAM,EAAE,aAAa,EAC1B,OAAO,CAAC,MAAM,CAAC,GAAG,UAAU,CAAC,CAAC;AAEjC,SAAO,EAAE,OAAO,SAAS,WAAW,aAAa;AACnD;","names":[]}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import {
|
|
2
|
+
shortenSymbol
|
|
3
|
+
} from "./chunk-QOV2R2WT.js";
|
|
4
|
+
|
|
5
|
+
// src/queries/hierarchy.ts
|
|
6
|
+
function hierarchy(db, symbolPattern) {
|
|
7
|
+
const sym = db.get(
|
|
8
|
+
`SELECT symbol, enclosing_symbol FROM global_symbols
|
|
9
|
+
WHERE symbol LIKE ? LIMIT 1`,
|
|
10
|
+
`%${symbolPattern}%`
|
|
11
|
+
);
|
|
12
|
+
if (!sym) return [];
|
|
13
|
+
const chain = [
|
|
14
|
+
{ symbol: sym.symbol, shortName: shortenSymbol(sym.symbol), depth: 0 }
|
|
15
|
+
];
|
|
16
|
+
let current = sym.enclosing_symbol;
|
|
17
|
+
let depth = 1;
|
|
18
|
+
const seen = /* @__PURE__ */ new Set([sym.symbol]);
|
|
19
|
+
while (current && !seen.has(current) && depth < 20) {
|
|
20
|
+
seen.add(current);
|
|
21
|
+
const parent = db.get(
|
|
22
|
+
`SELECT symbol, enclosing_symbol FROM global_symbols WHERE symbol = ?`,
|
|
23
|
+
current
|
|
24
|
+
);
|
|
25
|
+
if (!parent) break;
|
|
26
|
+
chain.push({
|
|
27
|
+
symbol: parent.symbol,
|
|
28
|
+
shortName: shortenSymbol(parent.symbol),
|
|
29
|
+
depth
|
|
30
|
+
});
|
|
31
|
+
current = parent.enclosing_symbol;
|
|
32
|
+
depth++;
|
|
33
|
+
}
|
|
34
|
+
return chain;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export {
|
|
38
|
+
hierarchy
|
|
39
|
+
};
|
|
40
|
+
//# sourceMappingURL=chunk-DEZKCZXD.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/queries/hierarchy.ts"],"sourcesContent":["import type { ScipDatabase } from '../db.js';\nimport type { HierarchyNode } from '../types.js';\nimport { shortenSymbol } from '../symbol-parser.js';\n\n/**\n * Walk the enclosing_symbol chain upward to show a symbol's ancestry.\n * e.g., method → class → module → file\n *\n * Falls back to parsing the SCIP symbol descriptor chain when\n * enclosing_symbol is not populated by the indexer.\n */\nexport function hierarchy(db: ScipDatabase, symbolPattern: string): HierarchyNode[] {\n // Find the symbol\n const sym = db.get<{ symbol: string; enclosing_symbol: string | null }>(\n `SELECT symbol, enclosing_symbol FROM global_symbols\n WHERE symbol LIKE ? LIMIT 1`,\n `%${symbolPattern}%`,\n );\n\n if (!sym) return [];\n\n const chain: HierarchyNode[] = [\n { symbol: sym.symbol, shortName: shortenSymbol(sym.symbol), depth: 0 },\n ];\n\n // Walk enclosing_symbol chain if available\n let current = sym.enclosing_symbol;\n let depth = 1;\n const seen = new Set<string>([sym.symbol]);\n\n while (current && !seen.has(current) && depth < 20) {\n seen.add(current);\n const parent = db.get<{ symbol: string; enclosing_symbol: string | null }>(\n `SELECT symbol, enclosing_symbol FROM global_symbols WHERE symbol = ?`,\n current,\n );\n if (!parent) break;\n\n chain.push({\n symbol: parent.symbol,\n shortName: shortenSymbol(parent.symbol),\n depth,\n });\n current = parent.enclosing_symbol;\n depth++;\n }\n\n return chain;\n}\n"],"mappings":";;;;;AAWO,SAAS,UAAU,IAAkB,eAAwC;AAElF,QAAM,MAAM,GAAG;AAAA,IACb;AAAA;AAAA,IAEA,IAAI,aAAa;AAAA,EACnB;AAEA,MAAI,CAAC,IAAK,QAAO,CAAC;AAElB,QAAM,QAAyB;AAAA,IAC7B,EAAE,QAAQ,IAAI,QAAQ,WAAW,cAAc,IAAI,MAAM,GAAG,OAAO,EAAE;AAAA,EACvE;AAGA,MAAI,UAAU,IAAI;AAClB,MAAI,QAAQ;AACZ,QAAM,OAAO,oBAAI,IAAY,CAAC,IAAI,MAAM,CAAC;AAEzC,SAAO,WAAW,CAAC,KAAK,IAAI,OAAO,KAAK,QAAQ,IAAI;AAClD,SAAK,IAAI,OAAO;AAChB,UAAM,SAAS,GAAG;AAAA,MAChB;AAAA,MACA;AAAA,IACF;AACA,QAAI,CAAC,OAAQ;AAEb,UAAM,KAAK;AAAA,MACT,QAAQ,OAAO;AAAA,MACf,WAAW,cAAc,OAAO,MAAM;AAAA,MACtC;AAAA,IACF,CAAC;AACD,cAAU,OAAO;AACjB;AAAA,EACF;AAEA,SAAO;AACT;","names":[]}
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
// src/reindex/install.ts
|
|
2
|
+
import { execFileSync } from "child_process";
|
|
3
|
+
import { platform } from "os";
|
|
4
|
+
var IS_WINDOWS = platform() === "win32";
|
|
5
|
+
function isBinaryAvailable(name) {
|
|
6
|
+
const cmd = IS_WINDOWS ? "where" : "which";
|
|
7
|
+
try {
|
|
8
|
+
execFileSync(cmd, [name], { stdio: "pipe" });
|
|
9
|
+
return true;
|
|
10
|
+
} catch {
|
|
11
|
+
return false;
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
function isIndexerInstalled(config) {
|
|
15
|
+
return isBinaryAvailable(config.indexerBinary);
|
|
16
|
+
}
|
|
17
|
+
function tryInstallIndexer(config, onStatus) {
|
|
18
|
+
const methods = config.installMethods;
|
|
19
|
+
if (!methods?.length) {
|
|
20
|
+
onStatus(`No auto-install method available for ${config.indexerBinary}.`);
|
|
21
|
+
if (config.installUrl) {
|
|
22
|
+
onStatus(`Install manually from: ${config.installUrl}`);
|
|
23
|
+
}
|
|
24
|
+
return false;
|
|
25
|
+
}
|
|
26
|
+
for (const method of methods) {
|
|
27
|
+
if (!isBinaryAvailable(method.prerequisite)) {
|
|
28
|
+
continue;
|
|
29
|
+
}
|
|
30
|
+
onStatus(`Installing ${config.indexerBinary} via ${method.label}...`);
|
|
31
|
+
try {
|
|
32
|
+
execFileSync(method.binary, method.args, {
|
|
33
|
+
stdio: "inherit",
|
|
34
|
+
timeout: 3e5,
|
|
35
|
+
env: process.env
|
|
36
|
+
});
|
|
37
|
+
if (isIndexerInstalled(config)) {
|
|
38
|
+
onStatus(`Successfully installed ${config.indexerBinary} via ${method.label}`);
|
|
39
|
+
return true;
|
|
40
|
+
}
|
|
41
|
+
onStatus(`${method.label} command completed but ${config.indexerBinary} not found on PATH`);
|
|
42
|
+
} catch (err) {
|
|
43
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
44
|
+
onStatus(`${method.label} install failed: ${msg}`);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
onStatus(`Could not auto-install ${config.indexerBinary}.`);
|
|
48
|
+
if (config.installUrl) {
|
|
49
|
+
onStatus(`Install manually from: ${config.installUrl}`);
|
|
50
|
+
}
|
|
51
|
+
return false;
|
|
52
|
+
}
|
|
53
|
+
function tryInstallScipCli(onStatus) {
|
|
54
|
+
if (platform() === "darwin" && isBinaryAvailable("brew")) {
|
|
55
|
+
onStatus("Installing scip CLI via Homebrew...");
|
|
56
|
+
try {
|
|
57
|
+
execFileSync("brew", ["install", "sourcegraph/scip/scip"], {
|
|
58
|
+
stdio: "inherit",
|
|
59
|
+
timeout: 3e5,
|
|
60
|
+
env: process.env
|
|
61
|
+
});
|
|
62
|
+
if (isBinaryAvailable("scip")) {
|
|
63
|
+
onStatus("Successfully installed scip CLI via Homebrew");
|
|
64
|
+
return true;
|
|
65
|
+
}
|
|
66
|
+
} catch (err) {
|
|
67
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
68
|
+
onStatus(`Homebrew install failed: ${msg}`);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
if (isBinaryAvailable("go")) {
|
|
72
|
+
onStatus("Installing scip CLI via go install...");
|
|
73
|
+
try {
|
|
74
|
+
execFileSync("go", ["install", "github.com/sourcegraph/scip/cmd/scip@latest"], {
|
|
75
|
+
stdio: "inherit",
|
|
76
|
+
timeout: 3e5,
|
|
77
|
+
env: process.env
|
|
78
|
+
});
|
|
79
|
+
if (isBinaryAvailable("scip")) {
|
|
80
|
+
onStatus("Successfully installed scip CLI via go install");
|
|
81
|
+
return true;
|
|
82
|
+
}
|
|
83
|
+
} catch (err) {
|
|
84
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
85
|
+
onStatus(`go install failed: ${msg}`);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
onStatus("Could not auto-install scip CLI.");
|
|
89
|
+
onStatus("Install manually from: https://github.com/sourcegraph/scip/releases");
|
|
90
|
+
return false;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
export {
|
|
94
|
+
isBinaryAvailable,
|
|
95
|
+
isIndexerInstalled,
|
|
96
|
+
tryInstallIndexer,
|
|
97
|
+
tryInstallScipCli
|
|
98
|
+
};
|
|
99
|
+
//# sourceMappingURL=chunk-DVWGWHFW.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/reindex/install.ts"],"sourcesContent":["import { execFileSync } from 'node:child_process';\nimport { platform } from 'node:os';\nimport type { IndexerConfig } from '../types.js';\n\nconst IS_WINDOWS = platform() === 'win32';\n\n/**\n * Check if a binary is available on PATH.\n */\nexport function isBinaryAvailable(name: string): boolean {\n const cmd = IS_WINDOWS ? 'where' : 'which';\n try {\n execFileSync(cmd, [name], { stdio: 'pipe' });\n return true;\n } catch {\n return false;\n }\n}\n\n/**\n * Check if an indexer's binary is available on PATH.\n */\nexport function isIndexerInstalled(config: IndexerConfig): boolean {\n return isBinaryAvailable(config.indexerBinary);\n}\n\n/**\n * Attempt to auto-install an indexer using its configured install methods.\n * Tries each method in order, checking prerequisites first.\n * Returns true if installation succeeded.\n */\nexport function tryInstallIndexer(\n config: IndexerConfig,\n onStatus: (msg: string) => void,\n): boolean {\n const methods = config.installMethods;\n if (!methods?.length) {\n onStatus(`No auto-install method available for ${config.indexerBinary}.`);\n if (config.installUrl) {\n onStatus(`Install manually from: ${config.installUrl}`);\n }\n return false;\n }\n\n for (const method of methods) {\n if (!isBinaryAvailable(method.prerequisite)) {\n continue;\n }\n\n onStatus(`Installing ${config.indexerBinary} via ${method.label}...`);\n try {\n execFileSync(method.binary, method.args, {\n stdio: 'inherit',\n timeout: 300_000,\n env: process.env,\n });\n\n if (isIndexerInstalled(config)) {\n onStatus(`Successfully installed ${config.indexerBinary} via ${method.label}`);\n return true;\n }\n onStatus(`${method.label} command completed but ${config.indexerBinary} not found on PATH`);\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n onStatus(`${method.label} install failed: ${msg}`);\n }\n }\n\n onStatus(`Could not auto-install ${config.indexerBinary}.`);\n if (config.installUrl) {\n onStatus(`Install manually from: ${config.installUrl}`);\n }\n return false;\n}\n\n/**\n * Attempt to auto-install the `scip` CLI binary.\n * Tries brew (macOS), then go install, then prints manual instructions.\n * Returns true if installation succeeded.\n */\nexport function tryInstallScipCli(\n onStatus: (msg: string) => void,\n): boolean {\n // macOS: try Homebrew first\n if (platform() === 'darwin' && isBinaryAvailable('brew')) {\n onStatus('Installing scip CLI via Homebrew...');\n try {\n execFileSync('brew', ['install', 'sourcegraph/scip/scip'], {\n stdio: 'inherit',\n timeout: 300_000,\n env: process.env,\n });\n if (isBinaryAvailable('scip')) {\n onStatus('Successfully installed scip CLI via Homebrew');\n return true;\n }\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n onStatus(`Homebrew install failed: ${msg}`);\n }\n }\n\n // Any platform: try go install\n if (isBinaryAvailable('go')) {\n onStatus('Installing scip CLI via go install...');\n try {\n execFileSync('go', ['install', 'github.com/sourcegraph/scip/cmd/scip@latest'], {\n stdio: 'inherit',\n timeout: 300_000,\n env: process.env,\n });\n if (isBinaryAvailable('scip')) {\n onStatus('Successfully installed scip CLI via go install');\n return true;\n }\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n onStatus(`go install failed: ${msg}`);\n }\n }\n\n onStatus('Could not auto-install scip CLI.');\n onStatus('Install manually from: https://github.com/sourcegraph/scip/releases');\n return false;\n}\n"],"mappings":";AAAA,SAAS,oBAAoB;AAC7B,SAAS,gBAAgB;AAGzB,IAAM,aAAa,SAAS,MAAM;AAK3B,SAAS,kBAAkB,MAAuB;AACvD,QAAM,MAAM,aAAa,UAAU;AACnC,MAAI;AACF,iBAAa,KAAK,CAAC,IAAI,GAAG,EAAE,OAAO,OAAO,CAAC;AAC3C,WAAO;AAAA,EACT,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAKO,SAAS,mBAAmB,QAAgC;AACjE,SAAO,kBAAkB,OAAO,aAAa;AAC/C;AAOO,SAAS,kBACd,QACA,UACS;AACT,QAAM,UAAU,OAAO;AACvB,MAAI,CAAC,SAAS,QAAQ;AACpB,aAAS,wCAAwC,OAAO,aAAa,GAAG;AACxE,QAAI,OAAO,YAAY;AACrB,eAAS,0BAA0B,OAAO,UAAU,EAAE;AAAA,IACxD;AACA,WAAO;AAAA,EACT;AAEA,aAAW,UAAU,SAAS;AAC5B,QAAI,CAAC,kBAAkB,OAAO,YAAY,GAAG;AAC3C;AAAA,IACF;AAEA,aAAS,cAAc,OAAO,aAAa,QAAQ,OAAO,KAAK,KAAK;AACpE,QAAI;AACF,mBAAa,OAAO,QAAQ,OAAO,MAAM;AAAA,QACvC,OAAO;AAAA,QACP,SAAS;AAAA,QACT,KAAK,QAAQ;AAAA,MACf,CAAC;AAED,UAAI,mBAAmB,MAAM,GAAG;AAC9B,iBAAS,0BAA0B,OAAO,aAAa,QAAQ,OAAO,KAAK,EAAE;AAC7E,eAAO;AAAA,MACT;AACA,eAAS,GAAG,OAAO,KAAK,0BAA0B,OAAO,aAAa,oBAAoB;AAAA,IAC5F,SAAS,KAAK;AACZ,YAAM,MAAM,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AAC3D,eAAS,GAAG,OAAO,KAAK,oBAAoB,GAAG,EAAE;AAAA,IACnD;AAAA,EACF;AAEA,WAAS,0BAA0B,OAAO,aAAa,GAAG;AAC1D,MAAI,OAAO,YAAY;AACrB,aAAS,0BAA0B,OAAO,UAAU,EAAE;AAAA,EACxD;AACA,SAAO;AACT;AAOO,SAAS,kBACd,UACS;AAET,MAAI,SAAS,MAAM,YAAY,kBAAkB,MAAM,GAAG;AACxD,aAAS,qCAAqC;AAC9C,QAAI;AACF,mBAAa,QAAQ,CAAC,WAAW,uBAAuB,GAAG;AAAA,QACzD,OAAO;AAAA,QACP,SAAS;AAAA,QACT,KAAK,QAAQ;AAAA,MACf,CAAC;AACD,UAAI,kBAAkB,MAAM,GAAG;AAC7B,iBAAS,8CAA8C;AACvD,eAAO;AAAA,MACT;AAAA,IACF,SAAS,KAAK;AACZ,YAAM,MAAM,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AAC3D,eAAS,4BAA4B,GAAG,EAAE;AAAA,IAC5C;AAAA,EACF;AAGA,MAAI,kBAAkB,IAAI,GAAG;AAC3B,aAAS,uCAAuC;AAChD,QAAI;AACF,mBAAa,MAAM,CAAC,WAAW,6CAA6C,GAAG;AAAA,QAC7E,OAAO;AAAA,QACP,SAAS;AAAA,QACT,KAAK,QAAQ;AAAA,MACf,CAAC;AACD,UAAI,kBAAkB,MAAM,GAAG;AAC7B,iBAAS,gDAAgD;AACzD,eAAO;AAAA,MACT;AAAA,IACF,SAAS,KAAK;AACZ,YAAM,MAAM,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AAC3D,eAAS,sBAAsB,GAAG,EAAE;AAAA,IACtC;AAAA,EACF;AAEA,WAAS,kCAAkC;AAC3C,WAAS,qEAAqE;AAC9E,SAAO;AACT;","names":[]}
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import {
|
|
2
|
+
testFileExclusionSql
|
|
3
|
+
} from "./chunk-FUHJCHS4.js";
|
|
4
|
+
import {
|
|
5
|
+
shortenSymbol
|
|
6
|
+
} from "./chunk-QOV2R2WT.js";
|
|
7
|
+
|
|
8
|
+
// src/queries/complexity-hotspots.ts
|
|
9
|
+
function complexityHotspots(db, opts) {
|
|
10
|
+
const { scope, minLoc = 10, limit = 30 } = opts ?? {};
|
|
11
|
+
const scopeFilter = scope ? `AND d.relative_path LIKE '%${scope}%'` : "";
|
|
12
|
+
const rows = db.all(
|
|
13
|
+
`SELECT
|
|
14
|
+
gs.symbol,
|
|
15
|
+
d.relative_path AS file,
|
|
16
|
+
der.start_line,
|
|
17
|
+
der.end_line,
|
|
18
|
+
(der.end_line - der.start_line + 1) AS loc,
|
|
19
|
+
-- fanIn: distinct files that reference this symbol
|
|
20
|
+
(SELECT COUNT(DISTINCT ref_c.document_id)
|
|
21
|
+
FROM mentions ref_m
|
|
22
|
+
JOIN chunks ref_c ON ref_m.chunk_id = ref_c.id
|
|
23
|
+
WHERE ref_m.symbol_id = gs.id AND ref_m.role = 0
|
|
24
|
+
) AS fan_in,
|
|
25
|
+
-- fanOut: distinct symbols referenced within this definition range
|
|
26
|
+
-- that are defined in different files
|
|
27
|
+
(SELECT COUNT(DISTINCT out_gs.id)
|
|
28
|
+
FROM mentions out_m
|
|
29
|
+
JOIN chunks out_c ON out_m.chunk_id = out_c.id
|
|
30
|
+
JOIN global_symbols out_gs ON out_m.symbol_id = out_gs.id
|
|
31
|
+
JOIN defn_enclosing_ranges out_der ON out_gs.id = out_der.symbol_id
|
|
32
|
+
WHERE out_c.document_id = der.document_id
|
|
33
|
+
AND out_c.start_line >= der.start_line
|
|
34
|
+
AND out_c.end_line <= der.end_line
|
|
35
|
+
AND out_m.role = 0
|
|
36
|
+
AND out_gs.id != gs.id
|
|
37
|
+
AND out_der.document_id != der.document_id
|
|
38
|
+
) AS fan_out,
|
|
39
|
+
-- calleeCount: total distinct callees within definition range
|
|
40
|
+
(SELECT COUNT(DISTINCT callee_gs.id)
|
|
41
|
+
FROM mentions callee_m
|
|
42
|
+
JOIN chunks callee_c ON callee_m.chunk_id = callee_c.id
|
|
43
|
+
JOIN global_symbols callee_gs ON callee_m.symbol_id = callee_gs.id
|
|
44
|
+
WHERE callee_c.document_id = der.document_id
|
|
45
|
+
AND callee_c.start_line >= der.start_line
|
|
46
|
+
AND callee_c.end_line <= der.end_line
|
|
47
|
+
AND callee_m.role = 0
|
|
48
|
+
AND callee_gs.id != gs.id
|
|
49
|
+
) AS callee_count
|
|
50
|
+
FROM global_symbols gs
|
|
51
|
+
JOIN defn_enclosing_ranges der ON gs.id = der.symbol_id
|
|
52
|
+
JOIN documents d ON der.document_id = d.id
|
|
53
|
+
WHERE 1 = 1
|
|
54
|
+
${db.pathExclusionsFor("d")}
|
|
55
|
+
AND ${testFileExclusionSql("d")}
|
|
56
|
+
${db.symbolNoiseFor("gs")}
|
|
57
|
+
AND (der.end_line - der.start_line + 1) >= ?
|
|
58
|
+
${scopeFilter}
|
|
59
|
+
ORDER BY (
|
|
60
|
+
CAST((der.end_line - der.start_line + 1) AS REAL) / 50.0
|
|
61
|
+
* CAST((SELECT COUNT(DISTINCT ref_c2.document_id)
|
|
62
|
+
FROM mentions ref_m2
|
|
63
|
+
JOIN chunks ref_c2 ON ref_m2.chunk_id = ref_c2.id
|
|
64
|
+
WHERE ref_m2.symbol_id = gs.id AND ref_m2.role = 0
|
|
65
|
+
) AS REAL) / 5.0
|
|
66
|
+
* MAX(CAST((SELECT COUNT(DISTINCT out_gs2.id)
|
|
67
|
+
FROM mentions out_m2
|
|
68
|
+
JOIN chunks out_c2 ON out_m2.chunk_id = out_c2.id
|
|
69
|
+
JOIN global_symbols out_gs2 ON out_m2.symbol_id = out_gs2.id
|
|
70
|
+
JOIN defn_enclosing_ranges out_der2 ON out_gs2.id = out_der2.symbol_id
|
|
71
|
+
WHERE out_c2.document_id = der.document_id
|
|
72
|
+
AND out_c2.start_line >= der.start_line
|
|
73
|
+
AND out_c2.end_line <= der.end_line
|
|
74
|
+
AND out_m2.role = 0
|
|
75
|
+
AND out_gs2.id != gs.id
|
|
76
|
+
AND out_der2.document_id != der.document_id
|
|
77
|
+
) AS REAL) / 5.0, 1.0)
|
|
78
|
+
) DESC
|
|
79
|
+
LIMIT ?`,
|
|
80
|
+
minLoc,
|
|
81
|
+
limit
|
|
82
|
+
);
|
|
83
|
+
return rows.filter((r) => !db.isIgnored(r.file)).map((r) => ({
|
|
84
|
+
symbol: r.symbol,
|
|
85
|
+
shortName: shortenSymbol(r.symbol),
|
|
86
|
+
file: r.file,
|
|
87
|
+
startLine: r.start_line,
|
|
88
|
+
endLine: r.end_line,
|
|
89
|
+
loc: r.loc,
|
|
90
|
+
fanIn: r.fan_in,
|
|
91
|
+
fanOut: r.fan_out,
|
|
92
|
+
calleeCount: r.callee_count,
|
|
93
|
+
score: Math.round(
|
|
94
|
+
r.loc / 50 * (r.fan_in / 5) * Math.max(r.fan_out / 5, 1) * 100
|
|
95
|
+
) / 100
|
|
96
|
+
}));
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
export {
|
|
100
|
+
complexityHotspots
|
|
101
|
+
};
|
|
102
|
+
//# sourceMappingURL=chunk-EMDQWNYR.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/queries/complexity-hotspots.ts"],"sourcesContent":["import type { ScipDatabase } from '../db.js';\nimport { testFileExclusionSql } from '../query-support.js';\nimport type { ComplexityHotspot } from '../types.js';\nimport { shortenSymbol } from '../symbol-parser.js';\n\n/**\n * Find complexity hotspots: symbols with a composite score based on\n * LOC, fan-in, fan-out, and callee count.\n *\n * Score = (loc / 50) * (fanIn / 5) * max(fanOut / 5, 1)\n *\n * High scores indicate symbols that are large, widely depended upon,\n * AND reach out to many other modules — the riskiest code to change.\n */\nexport function complexityHotspots(\n db: ScipDatabase,\n opts?: { scope?: string; minLoc?: number; limit?: number },\n): ComplexityHotspot[] {\n const { scope, minLoc = 10, limit = 30 } = opts ?? {};\n const scopeFilter = scope ? `AND d.relative_path LIKE '%${scope}%'` : '';\n\n const rows = db.all<{\n symbol: string;\n file: string;\n start_line: number;\n end_line: number;\n loc: number;\n fan_in: number;\n fan_out: number;\n callee_count: number;\n }>(\n `SELECT\n gs.symbol,\n d.relative_path AS file,\n der.start_line,\n der.end_line,\n (der.end_line - der.start_line + 1) AS loc,\n -- fanIn: distinct files that reference this symbol\n (SELECT COUNT(DISTINCT ref_c.document_id)\n FROM mentions ref_m\n JOIN chunks ref_c ON ref_m.chunk_id = ref_c.id\n WHERE ref_m.symbol_id = gs.id AND ref_m.role = 0\n ) AS fan_in,\n -- fanOut: distinct symbols referenced within this definition range\n -- that are defined in different files\n (SELECT COUNT(DISTINCT out_gs.id)\n FROM mentions out_m\n JOIN chunks out_c ON out_m.chunk_id = out_c.id\n JOIN global_symbols out_gs ON out_m.symbol_id = out_gs.id\n JOIN defn_enclosing_ranges out_der ON out_gs.id = out_der.symbol_id\n WHERE out_c.document_id = der.document_id\n AND out_c.start_line >= der.start_line\n AND out_c.end_line <= der.end_line\n AND out_m.role = 0\n AND out_gs.id != gs.id\n AND out_der.document_id != der.document_id\n ) AS fan_out,\n -- calleeCount: total distinct callees within definition range\n (SELECT COUNT(DISTINCT callee_gs.id)\n FROM mentions callee_m\n JOIN chunks callee_c ON callee_m.chunk_id = callee_c.id\n JOIN global_symbols callee_gs ON callee_m.symbol_id = callee_gs.id\n WHERE callee_c.document_id = der.document_id\n AND callee_c.start_line >= der.start_line\n AND callee_c.end_line <= der.end_line\n AND callee_m.role = 0\n AND callee_gs.id != gs.id\n ) AS callee_count\n FROM global_symbols gs\n JOIN defn_enclosing_ranges der ON gs.id = der.symbol_id\n JOIN documents d ON der.document_id = d.id\n WHERE 1 = 1\n ${db.pathExclusionsFor('d')}\n AND ${testFileExclusionSql('d')}\n ${db.symbolNoiseFor('gs')}\n AND (der.end_line - der.start_line + 1) >= ?\n ${scopeFilter}\n ORDER BY (\n CAST((der.end_line - der.start_line + 1) AS REAL) / 50.0\n * CAST((SELECT COUNT(DISTINCT ref_c2.document_id)\n FROM mentions ref_m2\n JOIN chunks ref_c2 ON ref_m2.chunk_id = ref_c2.id\n WHERE ref_m2.symbol_id = gs.id AND ref_m2.role = 0\n ) AS REAL) / 5.0\n * MAX(CAST((SELECT COUNT(DISTINCT out_gs2.id)\n FROM mentions out_m2\n JOIN chunks out_c2 ON out_m2.chunk_id = out_c2.id\n JOIN global_symbols out_gs2 ON out_m2.symbol_id = out_gs2.id\n JOIN defn_enclosing_ranges out_der2 ON out_gs2.id = out_der2.symbol_id\n WHERE out_c2.document_id = der.document_id\n AND out_c2.start_line >= der.start_line\n AND out_c2.end_line <= der.end_line\n AND out_m2.role = 0\n AND out_gs2.id != gs.id\n AND out_der2.document_id != der.document_id\n ) AS REAL) / 5.0, 1.0)\n ) DESC\n LIMIT ?`,\n minLoc, limit,\n );\n\n return rows\n .filter((r) => !db.isIgnored(r.file))\n .map((r) => ({\n symbol: r.symbol,\n shortName: shortenSymbol(r.symbol),\n file: r.file,\n startLine: r.start_line,\n endLine: r.end_line,\n loc: r.loc,\n fanIn: r.fan_in,\n fanOut: r.fan_out,\n calleeCount: r.callee_count,\n score:\n Math.round(\n (r.loc / 50) * (r.fan_in / 5) * Math.max(r.fan_out / 5, 1) * 100,\n ) / 100,\n }));\n}\n"],"mappings":";;;;;;;;AAcO,SAAS,mBACd,IACA,MACqB;AACrB,QAAM,EAAE,OAAO,SAAS,IAAI,QAAQ,GAAG,IAAI,QAAQ,CAAC;AACpD,QAAM,cAAc,QAAQ,8BAA8B,KAAK,OAAO;AAEtE,QAAM,OAAO,GAAG;AAAA,IAUd;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,QAyCI,GAAG,kBAAkB,GAAG,CAAC;AAAA,YACrB,qBAAqB,GAAG,CAAC;AAAA,QAC7B,GAAG,eAAe,IAAI,CAAC;AAAA;AAAA,QAEvB,WAAW;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAsBf;AAAA,IAAQ;AAAA,EACV;AAEA,SAAO,KACJ,OAAO,CAAC,MAAM,CAAC,GAAG,UAAU,EAAE,IAAI,CAAC,EACnC,IAAI,CAAC,OAAO;AAAA,IACX,QAAQ,EAAE;AAAA,IACV,WAAW,cAAc,EAAE,MAAM;AAAA,IACjC,MAAM,EAAE;AAAA,IACR,WAAW,EAAE;AAAA,IACb,SAAS,EAAE;AAAA,IACX,KAAK,EAAE;AAAA,IACP,OAAO,EAAE;AAAA,IACT,QAAQ,EAAE;AAAA,IACV,aAAa,EAAE;AAAA,IACf,OACE,KAAK;AAAA,MACF,EAAE,MAAM,MAAO,EAAE,SAAS,KAAK,KAAK,IAAI,EAAE,UAAU,GAAG,CAAC,IAAI;AAAA,IAC/D,IAAI;AAAA,EACR,EAAE;AACN;","names":[]}
|