codesearch-mcp 0.2.3-alpha → 0.3.0-alpha

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.
Files changed (141) hide show
  1. package/CHANGELOG.md +31 -1
  2. package/README.md +46 -26
  3. package/dist/app.js +5 -19
  4. package/dist/src/chunking/AstChunker.d.ts +12 -0
  5. package/dist/src/chunking/AstChunker.js +30 -0
  6. package/dist/src/chunking/CharacterChunkStrategy.d.ts +8 -0
  7. package/dist/src/chunking/CharacterChunkStrategy.js +15 -0
  8. package/dist/src/chunking/DeclarationChunkStrategy.d.ts +15 -0
  9. package/dist/src/chunking/DeclarationChunkStrategy.js +87 -0
  10. package/dist/src/chunking/FragmentMerger.d.ts +11 -0
  11. package/dist/src/chunking/FragmentMerger.js +61 -0
  12. package/dist/src/chunking/ImportOnlyFilter.d.ts +5 -0
  13. package/dist/src/chunking/ImportOnlyFilter.js +14 -0
  14. package/dist/src/chunking/LangchainTextSplitter.d.ts +21 -0
  15. package/dist/src/chunking/LangchainTextSplitter.js +133 -0
  16. package/dist/src/chunking/MinSizeSplitPolicy.d.ts +9 -0
  17. package/dist/src/chunking/MinSizeSplitPolicy.js +18 -0
  18. package/dist/src/chunking/RunPacker.d.ts +12 -0
  19. package/dist/src/chunking/RunPacker.js +84 -0
  20. package/dist/src/chunking/create-chunker.d.ts +3 -0
  21. package/dist/src/chunking/create-chunker.js +15 -0
  22. package/dist/src/chunking/interfaces.d.ts +27 -0
  23. package/dist/src/chunking/interfaces.js +2 -0
  24. package/dist/src/commands/ConfigCommand.js +1 -9
  25. package/dist/src/commands/EvalCommand.d.ts +4 -3
  26. package/dist/src/commands/EvalCommand.js +85 -52
  27. package/dist/src/commands/IndexCommand.js +0 -22
  28. package/dist/src/commands/McpCommand.js +2 -8
  29. package/dist/src/commands/WebCommand.js +2 -5
  30. package/dist/src/config-error.d.ts +0 -4
  31. package/dist/src/config-error.js +0 -4
  32. package/dist/src/config-store.d.ts +1 -54
  33. package/dist/src/config-store.js +8 -69
  34. package/dist/src/config.d.ts +8 -17
  35. package/dist/src/config.js +35 -34
  36. package/dist/src/constants.d.ts +2 -28
  37. package/dist/src/constants.js +6 -28
  38. package/dist/src/container.d.ts +6 -0
  39. package/dist/src/container.js +12 -3
  40. package/dist/src/db/schema.d.ts +17 -0
  41. package/dist/src/db/schema.js +1 -6
  42. package/dist/src/declarations/DeclarationTree.d.ts +19 -0
  43. package/dist/src/declarations/DeclarationTree.js +66 -0
  44. package/dist/src/declarations/DeclarationTreeBuilder.d.ts +22 -0
  45. package/dist/src/declarations/DeclarationTreeBuilder.js +179 -0
  46. package/dist/src/declarations/SourceLines.d.ts +37 -0
  47. package/dist/src/declarations/SourceLines.js +415 -0
  48. package/dist/src/dependency-traversal.d.ts +0 -6
  49. package/dist/src/dependency-traversal.js +0 -6
  50. package/dist/src/enriched-prompt.d.ts +0 -6
  51. package/dist/src/enriched-prompt.js +0 -6
  52. package/dist/src/error-record.d.ts +12 -0
  53. package/dist/src/error-record.js +248 -0
  54. package/dist/src/eval-corpus.d.ts +16 -0
  55. package/dist/src/eval-corpus.js +169 -0
  56. package/dist/src/eval-results.d.ts +6 -3
  57. package/dist/src/eval-results.js +309 -26
  58. package/dist/src/file-utils.d.ts +0 -5
  59. package/dist/src/file-utils.js +0 -5
  60. package/dist/src/graph.d.ts +0 -25
  61. package/dist/src/graph.js +0 -28
  62. package/dist/src/hnsw-settings.d.ts +3 -0
  63. package/dist/src/hnsw-settings.js +24 -0
  64. package/dist/src/interfaces/IAstParser.d.ts +2 -39
  65. package/dist/src/interfaces/IChunker.d.ts +3 -12
  66. package/dist/src/interfaces/ICorpusInspector.d.ts +7 -0
  67. package/dist/src/interfaces/ICorpusInspector.js +2 -0
  68. package/dist/src/interfaces/IDatabase.d.ts +2 -22
  69. package/dist/src/interfaces/IEmbedder.d.ts +0 -9
  70. package/dist/src/interfaces/ILogger.d.ts +1 -11
  71. package/dist/src/interfaces/IMcpToolHandler.d.ts +0 -5
  72. package/dist/src/interfaces/IMonorepoResolver.d.ts +0 -22
  73. package/dist/src/interfaces/IProjectDiscovery.d.ts +0 -17
  74. package/dist/src/interfaces/IRetrievalEvaluator.d.ts +134 -12
  75. package/dist/src/interfaces/ISearcher.d.ts +16 -5
  76. package/dist/src/interrupt.js +0 -2
  77. package/dist/src/owner-path.d.ts +9 -10
  78. package/dist/src/owner-path.js +11 -23
  79. package/dist/src/project-scope.d.ts +0 -12
  80. package/dist/src/project-scope.js +0 -12
  81. package/dist/src/project-selector.d.ts +0 -5
  82. package/dist/src/project-selector.js +0 -11
  83. package/dist/src/retrieval-eval.d.ts +14 -6
  84. package/dist/src/retrieval-eval.js +345 -59
  85. package/dist/src/search/result-shaping.d.ts +4 -0
  86. package/dist/src/search/result-shaping.js +19 -0
  87. package/dist/src/services/AstParserService.d.ts +2 -62
  88. package/dist/src/services/AstParserService.js +25 -137
  89. package/dist/src/services/DatabaseService.d.ts +2 -7
  90. package/dist/src/services/DatabaseService.js +42 -29
  91. package/dist/src/services/EmbedderFactory.d.ts +0 -4
  92. package/dist/src/services/EmbedderFactory.js +0 -5
  93. package/dist/src/services/IndexerService.d.ts +0 -16
  94. package/dist/src/services/IndexerService.js +16 -45
  95. package/dist/src/services/InfinityEmbedder.d.ts +0 -21
  96. package/dist/src/services/InfinityEmbedder.js +0 -15
  97. package/dist/src/services/LoggerService.d.ts +7 -5
  98. package/dist/src/services/LoggerService.js +37 -41
  99. package/dist/src/services/McpServer.d.ts +22 -14
  100. package/dist/src/services/McpServer.js +70 -37
  101. package/dist/src/services/MonorepoAwareDiscoveryService.d.ts +1 -45
  102. package/dist/src/services/MonorepoAwareDiscoveryService.js +1 -51
  103. package/dist/src/services/NxMonorepoResolver.d.ts +0 -29
  104. package/dist/src/services/NxMonorepoResolver.js +5 -56
  105. package/dist/src/services/OpenAICompatibleEmbedder.d.ts +0 -6
  106. package/dist/src/services/OpenAICompatibleEmbedder.js +0 -14
  107. package/dist/src/services/ProjectDiscoveryService.d.ts +0 -4
  108. package/dist/src/services/ProjectDiscoveryService.js +0 -4
  109. package/dist/src/services/SearcherService.d.ts +9 -116
  110. package/dist/src/services/SearcherService.js +95 -176
  111. package/dist/src/services/VoyageEmbedder.d.ts +0 -6
  112. package/dist/src/services/VoyageEmbedder.js +0 -6
  113. package/dist/src/services/WebServerService.d.ts +1 -53
  114. package/dist/src/services/WebServerService.js +11 -57
  115. package/dist/src/services/http-retry.d.ts +0 -7
  116. package/dist/src/services/http-retry.js +1 -10
  117. package/dist/src/services/infinity-provisioning.d.ts +0 -74
  118. package/dist/src/services/infinity-provisioning.js +5 -88
  119. package/dist/src/services/legacy-ollama-index.d.ts +0 -16
  120. package/dist/src/services/legacy-ollama-index.js +0 -23
  121. package/dist/src/services/reranker.d.ts +0 -9
  122. package/dist/src/services/reranker.js +46 -12
  123. package/dist/src/setup.d.ts +11 -23
  124. package/dist/src/setup.js +98 -71
  125. package/dist/src/symbol-name.d.ts +0 -12
  126. package/dist/src/symbol-name.js +0 -13
  127. package/dist/src/symbol-rows.d.ts +0 -14
  128. package/dist/src/symbol-rows.js +0 -14
  129. package/dist/src/tool-call-log.d.ts +2 -0
  130. package/dist/src/tool-call-log.js +24 -0
  131. package/dist/src/tools/GetDependenciesTool.js +1 -1
  132. package/dist/src/tools/GetDependentsTool.js +1 -1
  133. package/dist/src/tools/SearchCodebaseTool.js +6 -3
  134. package/dist/src/tools/format-helpers.d.ts +0 -6
  135. package/dist/src/tools/format-helpers.js +87 -14
  136. package/dist/src/web/dashboard.d.ts +0 -8
  137. package/dist/src/web/dashboard.js +7 -10
  138. package/package.json +2 -2
  139. package/dist/src/services/AstChunker.d.ts +0 -42
  140. package/dist/src/services/AstChunker.js +0 -158
  141. package/docs/web-dashboard.md +0 -206
@@ -1,48 +1,26 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.FORMER_OLLAMA_MODELS = exports.DEFAULT_EMBED_MODEL = exports.MODEL_REGISTRY = exports.MAX_EMBED_DIM = void 0;
3
+ exports.FORMER_OLLAMA_MODELS = exports.DEFAULT_EMBED_MODEL = exports.MODEL_REGISTRY = exports.MAX_EMBED_DIM = exports.VOYAGE_RERANK_MODELS = void 0;
4
4
  exports.registryDimension = registryDimension;
5
5
  exports.resolveDimension = resolveDimension;
6
- /**
7
- * pgvector's HNSW / IVFFlat index hard limit. The effective embedding dimension
8
- * must not exceed this; larger cloud models must reduce via the Matryoshka
9
- * `dimensions` request parameter (`embedding.dimensions`).
10
- */
6
+ exports.VOYAGE_RERANK_MODELS = ['rerank-3', 'rerank-3-lite'];
11
7
  exports.MAX_EMBED_DIM = 2000;
12
- /**
13
- * Advisory (NOT exhaustive) registry of known embedding models. Used to resolve
14
- * a model's dimension without a runtime probe and to suggest models in the
15
- * dashboard. Models absent here still work — their dimension is probed at index
16
- * time (see `resolveDimension`).
17
- */
18
8
  exports.MODEL_REGISTRY = {
19
9
  'text-embedding-3-small': { provider: 'openai-compatible', dimensions: 1536, dimConfigurable: true },
20
10
  'text-embedding-3-large': { provider: 'openai-compatible', dimensions: 3072, dimConfigurable: true, maxIndexable: 2000 },
21
11
  'text-embedding-ada-002': { provider: 'openai-compatible', dimensions: 1536 },
22
- 'voyage-code-3': { provider: 'voyage', dimensions: 1024, dimConfigurable: true, maxDim: 2048 },
23
- 'voyage-3': { provider: 'voyage', dimensions: 1024, dimConfigurable: true },
24
- 'voyage-3-lite': { provider: 'voyage', dimensions: 512 },
12
+ 'voyage-code-4': { provider: 'voyage', dimensions: 1024, dimConfigurable: true, maxDim: 2048 },
13
+ 'voyage-4-large': { provider: 'voyage', dimensions: 1024, dimConfigurable: true, maxDim: 2048 },
14
+ 'voyage-4': { provider: 'voyage', dimensions: 1024, dimConfigurable: true, maxDim: 2048 },
15
+ 'voyage-4-lite': { provider: 'voyage', dimensions: 1024, dimConfigurable: true, maxDim: 2048 },
25
16
  'BAAI/bge-m3': { provider: 'infinity', dimensions: 1024 },
26
17
  'jinaai/jina-embeddings-v2-base-code': { provider: 'infinity', dimensions: 768 },
27
18
  };
28
19
  exports.DEFAULT_EMBED_MODEL = 'jinaai/jina-embeddings-v2-base-code';
29
- /**
30
- * Former Ollama registry model names, kept only so config validation can
31
- * recognise an implicit (provider-less) config that still targets Ollama —
32
- * see `detectFormerOllamaConfig` in `config-store.ts`.
33
- */
34
20
  exports.FORMER_OLLAMA_MODELS = ['bge-m3', 'nomic-embed-text', 'mxbai-embed-large'];
35
- /** Advisory dimension for a model, or `undefined` when the model is unknown. */
36
21
  function registryDimension(model) {
37
22
  return exports.MODEL_REGISTRY[model]?.dimensions;
38
23
  }
39
- /**
40
- * Resolve the effective vector dimension: explicit override → registry → probe.
41
- * Validates the result is ≤ MAX_EMBED_DIM, throwing an error that names
42
- * `embedding.dimensions` as the fix. `probeFn` embeds a short string once and
43
- * returns the observed length; only called when neither override nor registry
44
- * yields a value. Pure aside from the injected probe.
45
- */
46
24
  async function resolveDimension(override, model, probeFn, registry = exports.MODEL_REGISTRY) {
47
25
  let dim;
48
26
  if (override != null) {
@@ -1,6 +1,7 @@
1
1
  import { Config } from './config';
2
2
  import { IndexerService } from './services/IndexerService';
3
3
  import { McpServer } from './services/McpServer';
4
+ import { LoggerService } from './services/LoggerService';
4
5
  import type { IDatabase } from './interfaces';
5
6
  import type { IEmbedder } from './interfaces';
6
7
  import type { ISearcher } from './interfaces';
@@ -16,4 +17,9 @@ export interface Container {
16
17
  logger: ILogger;
17
18
  discovery: IProjectDiscovery;
18
19
  }
20
+ export declare function loggerBindingsFor(loggerName?: string): Record<string, unknown> | undefined;
21
+ export declare function createLogger(cfg: {
22
+ level: string;
23
+ dir?: string;
24
+ }, loggerName?: string): LoggerService;
19
25
  export declare function createContainer(loggerName?: string): Container;
@@ -1,24 +1,33 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.loggerBindingsFor = loggerBindingsFor;
4
+ exports.createLogger = createLogger;
3
5
  exports.createContainer = createContainer;
6
+ const crypto_1 = require("crypto");
4
7
  const config_1 = require("./config");
5
8
  const DatabaseService_1 = require("./services/DatabaseService");
6
9
  const EmbedderFactory_1 = require("./services/EmbedderFactory");
7
10
  const AstParserService_1 = require("./services/AstParserService");
8
- const AstChunker_1 = require("./services/AstChunker");
11
+ const create_chunker_1 = require("./chunking/create-chunker");
9
12
  const IndexerService_1 = require("./services/IndexerService");
10
13
  const SearcherService_1 = require("./services/SearcherService");
11
14
  const McpServer_1 = require("./services/McpServer");
12
15
  const LoggerService_1 = require("./services/LoggerService");
13
16
  const MonorepoAwareDiscoveryService_1 = require("./services/MonorepoAwareDiscoveryService");
14
17
  const NxMonorepoResolver_1 = require("./services/NxMonorepoResolver");
18
+ function loggerBindingsFor(loggerName) {
19
+ return loggerName === 'mcp' ? { connectionId: (0, crypto_1.randomUUID)() } : undefined;
20
+ }
21
+ function createLogger(cfg, loggerName) {
22
+ return LoggerService_1.LoggerService.fromConfig(cfg, loggerName, loggerBindingsFor(loggerName));
23
+ }
15
24
  function createContainer(loggerName) {
16
25
  const config = new config_1.Config();
17
- const logger = LoggerService_1.LoggerService.fromConfig(config.logging, loggerName);
26
+ const logger = createLogger(config.logging, loggerName);
18
27
  const db = new DatabaseService_1.DatabaseService(config);
19
28
  const embedder = (0, EmbedderFactory_1.createEmbedder)(config, logger);
20
29
  const ast = new AstParserService_1.AstParserService(logger);
21
- const chunker = new AstChunker_1.AstChunker(config);
30
+ const chunker = (0, create_chunker_1.createChunker)(config);
22
31
  const discovery = new MonorepoAwareDiscoveryService_1.MonorepoAwareDiscoveryService([new NxMonorepoResolver_1.NxMonorepoResolver(logger)], logger);
23
32
  const indexer = new IndexerService_1.IndexerService(db, embedder, ast, chunker, config, logger);
24
33
  const searcher = new SearcherService_1.SearcherService(db, embedder, discovery, config, logger);
@@ -161,6 +161,23 @@ export declare const codeChunk: import("drizzle-orm/pg-core").PgTableWithColumns
161
161
  }, {}, {
162
162
  length: number | undefined;
163
163
  }>;
164
+ endLine: import("drizzle-orm/pg-core").PgColumn<{
165
+ name: "endLine";
166
+ tableName: "code_chunk";
167
+ dataType: "number";
168
+ columnType: "PgInteger";
169
+ data: number;
170
+ driverParam: string | number;
171
+ notNull: false;
172
+ hasDefault: false;
173
+ isPrimaryKey: false;
174
+ isAutoincrement: false;
175
+ hasRuntimeDefault: false;
176
+ enumValues: undefined;
177
+ baseColumn: never;
178
+ identity: undefined;
179
+ generated: undefined;
180
+ }, {}, {}>;
164
181
  };
165
182
  dialect: "pg";
166
183
  }>;
@@ -2,10 +2,6 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.indexMeta = exports.codeCall = exports.codeSymbol = exports.codeDependency = exports.codeChunk = void 0;
4
4
  const pg_core_1 = require("drizzle-orm/pg-core");
5
- // NOTE: the `dimensions` literal below is nominal only. `dataType()` is invoked
6
- // solely by drizzle-kit DDL generation, which this project does not use — the
7
- // `code_chunk` table is created by raw SQL in DatabaseService at the dimension
8
- // resolved from the active model, and the real width is recorded in index_meta.
9
5
  const vector = (0, pg_core_1.customType)({
10
6
  dataType(config) { return `vector(${config?.dimensions ?? 1024})`; },
11
7
  });
@@ -19,6 +15,7 @@ exports.codeChunk = (0, pg_core_1.pgTable)('code_chunk', {
19
15
  lastModified: (0, pg_core_1.doublePrecision)('lastModified'),
20
16
  contentHash: (0, pg_core_1.varchar)('contentHash', { length: 64 }),
21
17
  ownerPath: (0, pg_core_1.varchar)('ownerPath'),
18
+ endLine: (0, pg_core_1.integer)('endLine'),
22
19
  });
23
20
  exports.codeDependency = (0, pg_core_1.pgTable)('code_dependency', {
24
21
  id: (0, pg_core_1.uuid)('id').primaryKey().defaultRandom(),
@@ -53,8 +50,6 @@ exports.codeCall = (0, pg_core_1.pgTable)('code_call', {
53
50
  line: (0, pg_core_1.integer)('line'),
54
51
  col: (0, pg_core_1.integer)('col'),
55
52
  });
56
- // Single-row source of truth for which model built the current index. The
57
- // `id` column is a boolean pinned to `true` so at most one row can exist.
58
53
  exports.indexMeta = (0, pg_core_1.pgTable)('index_meta', {
59
54
  id: (0, pg_core_1.boolean)('id').primaryKey().default(true),
60
55
  provider: (0, pg_core_1.text)('provider').notNull(),
@@ -0,0 +1,19 @@
1
+ import type { SymbolKind } from '../interfaces/IAstParser';
2
+ import type { LineRange } from './SourceLines';
3
+ export interface DeclarationNode {
4
+ start: number;
5
+ end: number;
6
+ nameLine: number;
7
+ qualifiedName: string;
8
+ kind: SymbolKind;
9
+ children: DeclarationNode[];
10
+ }
11
+ export declare class DeclarationTree {
12
+ static readonly EMPTY: DeclarationTree;
13
+ readonly roots: DeclarationNode[];
14
+ constructor(roots: DeclarationNode[]);
15
+ contains(line: number): boolean;
16
+ smallestContaining(lines: number[]): DeclarationNode | null;
17
+ within(range: LineRange): DeclarationNode[];
18
+ private static sorted;
19
+ }
@@ -0,0 +1,66 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.DeclarationTree = void 0;
4
+ class DeclarationTree {
5
+ static EMPTY = new DeclarationTree([]);
6
+ roots;
7
+ constructor(roots) {
8
+ this.roots = DeclarationTree.sorted(roots);
9
+ }
10
+ contains(line) {
11
+ return this.roots.some(r => r.start <= line && line <= r.end);
12
+ }
13
+ smallestContaining(lines) {
14
+ if (lines.length === 0)
15
+ return null;
16
+ const lo = Math.min(...lines);
17
+ const hi = Math.max(...lines);
18
+ const covers = (n) => n.start <= lo && n.end >= hi;
19
+ const span = (n) => n.end - n.start;
20
+ let fewest = Infinity;
21
+ const measure = (nodes) => {
22
+ for (const node of nodes) {
23
+ if (!covers(node))
24
+ continue;
25
+ fewest = Math.min(fewest, span(node));
26
+ measure(node.children);
27
+ }
28
+ };
29
+ measure(this.roots);
30
+ let best = null;
31
+ const pick = (nodes) => {
32
+ let anyTied = false;
33
+ for (const node of nodes) {
34
+ if (!covers(node))
35
+ continue;
36
+ const nestedTied = pick(node.children);
37
+ const tied = span(node) === fewest;
38
+ if (tied && !nestedTied && (best === null || node.nameLine < best.nameLine))
39
+ best = node;
40
+ anyTied ||= tied || nestedTied;
41
+ }
42
+ return anyTied;
43
+ };
44
+ pick(this.roots);
45
+ return best;
46
+ }
47
+ within(range) {
48
+ const out = [];
49
+ const visit = (nodes) => {
50
+ for (const node of nodes) {
51
+ if (node.end < range.start || node.start > range.end)
52
+ continue;
53
+ out.push(node);
54
+ visit(node.children);
55
+ }
56
+ };
57
+ visit(this.roots);
58
+ return out;
59
+ }
60
+ static sorted(nodes) {
61
+ return [...nodes]
62
+ .sort((a, b) => a.start - b.start || b.end - a.end)
63
+ .map(n => ({ ...n, children: DeclarationTree.sorted(n.children) }));
64
+ }
65
+ }
66
+ exports.DeclarationTree = DeclarationTree;
@@ -0,0 +1,22 @@
1
+ import { Node, type SourceFile } from 'ts-morph';
2
+ import type { SymbolEntry } from '../interfaces/IAstParser';
3
+ import { DeclarationTree } from './DeclarationTree';
4
+ export interface DeclarationCandidate {
5
+ entry: SymbolEntry;
6
+ declNode: Node;
7
+ }
8
+ export declare class DeclarationTreeBuilder {
9
+ build(sourceFile: SourceFile, candidates: DeclarationCandidate[]): DeclarationTree;
10
+ flat(candidates: DeclarationCandidate[]): DeclarationTree;
11
+ leadingStart(sourceFile: SourceFile, declNode: Node, lines?: LineIndex): number;
12
+ private safeLeadingStart;
13
+ private triviaNode;
14
+ private overloadSets;
15
+ private nearestCandidateAncestor;
16
+ }
17
+ declare class LineIndex {
18
+ private readonly starts;
19
+ constructor(text: string);
20
+ lineAt(pos: number): number;
21
+ }
22
+ export {};
@@ -0,0 +1,179 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.DeclarationTreeBuilder = void 0;
4
+ const ts_morph_1 = require("ts-morph");
5
+ const DeclarationTree_1 = require("./DeclarationTree");
6
+ class DeclarationTreeBuilder {
7
+ build(sourceFile, candidates) {
8
+ const lines = new LineIndex(sourceFile.getFullText());
9
+ const siblings = new SiblingIndex();
10
+ const groups = this.overloadSets(candidates.filter(c => c.entry.kind !== 'method_signature'), siblings);
11
+ const nodeOf = new Map();
12
+ const entries = [];
13
+ for (const group of groups) {
14
+ const first = group[0];
15
+ const last = group[group.length - 1];
16
+ const named = group.find(c => c.entry.hasBody) ?? first;
17
+ const node = {
18
+ start: this.safeLeadingStart(sourceFile, siblings.firstSignature(first.declNode), lines),
19
+ end: lines.lineAt(last.declNode.getEnd()),
20
+ nameLine: named.entry.line ?? first.declNode.getStartLineNumber(),
21
+ qualifiedName: named.entry.container ? `${named.entry.container}.${named.entry.name}` : named.entry.name,
22
+ kind: named.entry.kind,
23
+ children: [],
24
+ };
25
+ for (const c of group)
26
+ nodeOf.set(c.declNode, node);
27
+ entries.push({ first: first.declNode, node });
28
+ }
29
+ const roots = [];
30
+ for (const { first, node } of entries) {
31
+ const parent = this.nearestCandidateAncestor(first, nodeOf);
32
+ if (parent && parent !== node) {
33
+ node.start = Math.max(node.start, parent.start);
34
+ parent.children.push(node);
35
+ }
36
+ else {
37
+ roots.push(node);
38
+ }
39
+ }
40
+ return new DeclarationTree_1.DeclarationTree(roots);
41
+ }
42
+ flat(candidates) {
43
+ const siblings = new SiblingIndex();
44
+ return new DeclarationTree_1.DeclarationTree(this.overloadSets(candidates, siblings).map(group => {
45
+ const first = group[0];
46
+ const named = group.find(c => c.entry.hasBody) ?? first;
47
+ return {
48
+ start: siblings.firstSignature(first.declNode).getStartLineNumber(),
49
+ end: group[group.length - 1].declNode.getEndLineNumber(),
50
+ nameLine: named.entry.line ?? first.declNode.getStartLineNumber(),
51
+ qualifiedName: named.entry.container ? `${named.entry.container}.${named.entry.name}` : named.entry.name,
52
+ kind: named.entry.kind,
53
+ children: [],
54
+ };
55
+ }));
56
+ }
57
+ leadingStart(sourceFile, declNode, lines = new LineIndex(sourceFile.getFullText())) {
58
+ const triviaNode = this.triviaNode(declNode);
59
+ let boundary = Math.min(lines.lineAt(declNode.getStart()), lines.lineAt(triviaNode.getStart()));
60
+ const ranges = ts_morph_1.ts.getLeadingCommentRanges(sourceFile.getFullText(), triviaNode.getPos()) ?? [];
61
+ for (let i = ranges.length - 1; i >= 0; i--) {
62
+ const endLine = lines.lineAt(Math.max(ranges[i].pos, ranges[i].end - 1));
63
+ if (endLine < boundary - 1)
64
+ break;
65
+ boundary = Math.min(boundary, lines.lineAt(ranges[i].pos));
66
+ }
67
+ return boundary;
68
+ }
69
+ safeLeadingStart(sourceFile, declNode, lines) {
70
+ try {
71
+ return this.leadingStart(sourceFile, declNode, lines);
72
+ }
73
+ catch {
74
+ return declNode.getStartLineNumber();
75
+ }
76
+ }
77
+ triviaNode(declNode) {
78
+ if (!ts_morph_1.Node.isVariableDeclaration(declNode))
79
+ return declNode;
80
+ const list = declNode.getParent();
81
+ const statement = list?.getParent();
82
+ if (!ts_morph_1.Node.isVariableDeclarationList(list) || !ts_morph_1.Node.isVariableStatement(statement))
83
+ return declNode;
84
+ return list.getDeclarations()[0] === declNode ? statement : declNode;
85
+ }
86
+ overloadSets(candidates, siblings) {
87
+ const key = (c) => JSON.stringify([c.entry.container, c.entry.name, c.entry.kind, c.entry.isStatic]);
88
+ const sorted = [...candidates].sort((a, b) => a.declNode.getStart() - b.declNode.getStart());
89
+ const sets = [];
90
+ const openSet = new Map();
91
+ for (const c of sorted) {
92
+ const k = key(c);
93
+ const open = openSet.get(k);
94
+ const prev = open?.[open.length - 1];
95
+ if (open && prev && siblings.adjacent(prev.declNode, c.declNode)) {
96
+ open.push(c);
97
+ continue;
98
+ }
99
+ const set = [c];
100
+ sets.push(set);
101
+ openSet.set(k, set);
102
+ }
103
+ return sets;
104
+ }
105
+ nearestCandidateAncestor(declNode, nodeOf) {
106
+ for (let current = declNode.getParent(); current; current = current.getParent()) {
107
+ const found = nodeOf.get(current);
108
+ if (found)
109
+ return found;
110
+ }
111
+ return undefined;
112
+ }
113
+ }
114
+ exports.DeclarationTreeBuilder = DeclarationTreeBuilder;
115
+ class SiblingIndex {
116
+ positions = new Map();
117
+ read = new Set();
118
+ statements = new Map();
119
+ adjacent(a, b) {
120
+ const pa = this.position(a.compilerNode);
121
+ const pb = this.position(b.compilerNode);
122
+ return !!pa && !!pb && pa.list === pb.list && pb.index === pa.index + 1;
123
+ }
124
+ firstSignature(declNode) {
125
+ if (!ts_morph_1.Node.isFunctionDeclaration(declNode) || !declNode.compilerNode.body)
126
+ return declNode;
127
+ const name = declNode.getName();
128
+ const position = this.position(declNode.compilerNode);
129
+ if (!name || !position)
130
+ return declNode;
131
+ const isSignature = (n) => ts_morph_1.ts.isFunctionDeclaration(n) && !n.body && n.name?.text === name;
132
+ let i = position.index;
133
+ while (i > 0 && isSignature(position.list[i - 1]))
134
+ i--;
135
+ if (i === position.index)
136
+ return declNode;
137
+ return this.statement(declNode.getParentOrThrow(), position.list[i]) ?? declNode;
138
+ }
139
+ position(node) {
140
+ const parent = node.parent;
141
+ if (parent && !this.read.has(parent)) {
142
+ this.read.add(parent);
143
+ ts_morph_1.ts.forEachChild(parent, () => undefined, list => {
144
+ list.forEach((child, index) => this.positions.set(child, { list, index }));
145
+ return undefined;
146
+ });
147
+ }
148
+ return this.positions.get(node);
149
+ }
150
+ statement(parent, compilerNode) {
151
+ if (!ts_morph_1.Node.isStatemented(parent))
152
+ return undefined;
153
+ let byCompilerNode = this.statements.get(parent);
154
+ if (!byCompilerNode) {
155
+ byCompilerNode = new Map(parent.getStatements().map(s => [s.compilerNode, s]));
156
+ this.statements.set(parent, byCompilerNode);
157
+ }
158
+ return byCompilerNode.get(compilerNode);
159
+ }
160
+ }
161
+ class LineIndex {
162
+ starts = [0];
163
+ constructor(text) {
164
+ for (let i = text.indexOf('\n'); i !== -1; i = text.indexOf('\n', i + 1))
165
+ this.starts.push(i + 1);
166
+ }
167
+ lineAt(pos) {
168
+ let lo = 0;
169
+ let hi = this.starts.length - 1;
170
+ while (lo < hi) {
171
+ const mid = (lo + hi + 1) >> 1;
172
+ if (this.starts[mid] <= pos)
173
+ lo = mid;
174
+ else
175
+ hi = mid - 1;
176
+ }
177
+ return lo + 1;
178
+ }
179
+ }
@@ -0,0 +1,37 @@
1
+ export interface LineRange {
2
+ start: number;
3
+ end: number;
4
+ }
5
+ export declare class SourceLines {
6
+ readonly text: string;
7
+ private readonly lines;
8
+ private readonly stripped;
9
+ private readonly code;
10
+ private readonly startsInCode;
11
+ private importLines?;
12
+ constructor(text: string, { jsx }?: {
13
+ jsx?: boolean;
14
+ });
15
+ get count(): number;
16
+ line(n: number): string;
17
+ slice(start: number, end: number): string;
18
+ length(start: number, end: number): number;
19
+ isBlank(n: number): boolean;
20
+ isCommentOnly(n: number): boolean;
21
+ isDirective(n: number): boolean;
22
+ isPunctuationOnly(n: number): boolean;
23
+ isSignificant(n: number): boolean;
24
+ significantLength(start: number, end: number): number;
25
+ importRegions(start: number, end: number): LineRange[];
26
+ private importStatementEnd;
27
+ private startsImport;
28
+ private nextCode;
29
+ private static bracketDepth;
30
+ static isImportOrReexportOnly(content: string): boolean;
31
+ isImportOnly(start: number, end: number): boolean;
32
+ private importStatementLines;
33
+ private static isImportLine;
34
+ private static statements;
35
+ private static opensElement;
36
+ private static scanCode;
37
+ }