carrick 0.3.117 → 0.3.118

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "carrick",
3
- "version": "0.3.117",
3
+ "version": "0.3.118",
4
4
  "carrickCache": {
5
5
  "cache_version": 22,
6
6
  "intent_cache_version": 2
@@ -113,11 +113,11 @@
113
113
  "zod": "^3.23.0"
114
114
  },
115
115
  "optionalDependencies": {
116
- "@carrick-tools/cli-darwin-arm64": "0.3.117",
117
- "@carrick-tools/cli-darwin-x64": "0.3.117",
118
- "@carrick-tools/cli-linux-arm64": "0.3.117",
119
- "@carrick-tools/cli-linux-x64": "0.3.117",
120
- "@carrick-tools/cli-win32-x64": "0.3.117"
116
+ "@carrick-tools/cli-darwin-arm64": "0.3.118",
117
+ "@carrick-tools/cli-darwin-x64": "0.3.118",
118
+ "@carrick-tools/cli-linux-arm64": "0.3.118",
119
+ "@carrick-tools/cli-linux-x64": "0.3.118",
120
+ "@carrick-tools/cli-win32-x64": "0.3.118"
121
121
  },
122
122
  "devDependencies": {
123
123
  "@types/node": "^24.13.3"
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "carrick",
3
3
  "description": "Claude Code plugin for Carrick, which indexes TypeScript codebases across service and repository boundaries. After each edit it adds the file's routes, calls, and cross-service type mismatches to the session, and it registers Carrick's language server. It pairs with the Carrick MCP server, which lets agents search functions by intent rather than name.",
4
- "version": "0.3.117",
4
+ "version": "0.3.118",
5
5
  "author": {
6
6
  "name": "Carrick",
7
7
  "email": "hello@carrick.tools"
@@ -2896,16 +2896,21 @@ class DeclarationReader {
2896
2896
  }
2897
2897
  /**
2898
2898
  * What a factory builds says nothing: the type itself, or any branch of a
2899
- * conditional return type, does.
2899
+ * conditional return type, does. A conditional whose branches lead back to
2900
+ * itself (a recursive alias) is read once: `seen` holds the conditionals
2901
+ * on the way down, and one met again says nothing new.
2900
2902
  */
2901
- returnSaysNothing(type) {
2903
+ returnSaysNothing(type, seen = new Set()) {
2902
2904
  if (this.saysNothing(type))
2903
2905
  return true;
2904
2906
  return this.parts(type).some(part => {
2905
2907
  if (!(part.flags & ts.TypeFlags.Conditional))
2906
2908
  return false;
2907
2909
  const node = part.root.node;
2908
- return [node.trueType, node.falseType].some(branch => this.returnSaysNothing(this.checker.getTypeFromTypeNode(branch)));
2910
+ if (seen.has(node))
2911
+ return false;
2912
+ seen.add(node);
2913
+ return [node.trueType, node.falseType].some(branch => this.returnSaysNothing(this.checker.getTypeFromTypeNode(branch), seen));
2909
2914
  });
2910
2915
  }
2911
2916
  /** `any` (an unresolved type included) or `unknown`. */
@@ -2,7 +2,7 @@
2
2
  * Per-position compiler rows for a service (carrick#2405, move 2 of the fact
3
3
  * layer). Record-only: nothing in a scan reads the file yet.
4
4
  *
5
- * For every source file the scanner sends, one row for each position of four
5
+ * For every source file the scanner sends, one row for each position of six
6
6
  * kinds, each holding what the compiler knows there: the symbol, the
7
7
  * declaration it is attributed to (file and line), the package that declares
8
8
  * it and the type text.
@@ -13,11 +13,22 @@
13
13
  * | `member` | property or element access | the name or the argument | the access |
14
14
  * | `compare` | `=== !== == != < > <= >= instanceof in` | the left operand | the left operand |
15
15
  * | `ident` | an identifier in expression position | itself | itself |
16
+ * | `impl` | a `call` or `member` whose declaration has no body | an implementation of it | nothing |
17
+ * | `jsx` | a JSX opening or self-closing element's tag | the tag's name | the tag |
16
18
  *
17
- * A row's key is `(start, end, kind)`: TypeScript `getStart()`/`getEnd()` of
18
- * the node, in UTF-16 code units from the start of the file, which is what
19
- * the scanner's `sidecar_position` gives for an SWC span. Every path written
20
- * is relative to the scanned repo's root, with `/` separators.
19
+ * An `impl` row (version 2, carrick#2430) follows the `call` or `member` row
20
+ * it belongs to and has its span: one per declaration that go-to-implementation
21
+ * names for that row's declaration, when that declaration is an interface's
22
+ * or type literal's method, a function-typed property signature, an abstract
23
+ * member, or a function-typed class property or parameter with no
24
+ * initializer. Its `decl_*` name the implementing declaration. Past
25
+ * {@link IMPL_CAP} the row is one, with {@link FLAG_TOO_MANY} and nothing else.
26
+ *
27
+ * A row's key is `(start, end, kind)`, except that several `impl` rows share
28
+ * one: TypeScript `getStart()`/`getEnd()` of the node, in UTF-16 code units
29
+ * from the start of the file, which is what the scanner's `sidecar_position`
30
+ * gives for an SWC span. Every path written is relative to the scanned repo's
31
+ * root, with `/` separators.
21
32
  *
22
33
  * Each file is read in the program of the project that owns it
23
34
  * (`ProjectLoader.projectKeyFor`, carrick#1604), one owner's program at a
@@ -33,7 +44,7 @@ import { ts } from 'ts-morph';
33
44
  import type { ProjectLoader } from './project-loader.js';
34
45
  import type { PositionRowsCounts } from './types.js';
35
46
  /** The format's version, written in the header. */
36
- export declare const POSITION_ROWS_VERSION = 1;
47
+ export declare const POSITION_ROWS_VERSION = 2;
37
48
  /** The type is `any`, declared or inferred. Never set with the error type. */
38
49
  export declare const FLAG_ANY = 1;
39
50
  /** The type is the compiler's error type: something did not resolve. */
@@ -42,7 +53,13 @@ export declare const FLAG_ERROR_TYPE = 2;
42
53
  export declare const FLAG_CHECKER_THREW = 4;
43
54
  /** The declaration is outside the checkout, not the default library and in no installed package. */
44
55
  export declare const FLAG_DECLARATION_UNNAMED = 8;
45
- export type PositionKind = 'call' | 'member' | 'compare' | 'ident';
56
+ /** An `impl` row standing for more implementations than {@link IMPL_CAP}; it names none. */
57
+ export declare const FLAG_TOO_MANY = 16;
58
+ /** The most implementations one declaration's `impl` rows list. */
59
+ export declare const IMPL_CAP = 16;
60
+ /** The kinds {@link kindOf} gives a node; `impl` and `jsx` rows are written beside them. */
61
+ export type NodeKind = 'call' | 'member' | 'compare' | 'ident';
62
+ export type PositionKind = NodeKind | 'impl' | 'jsx';
46
63
  /** `[kind, start, end, symbol, decl_file, decl_line, decl_package, type_text, flags]` */
47
64
  export type PositionRow = [
48
65
  PositionKind,
@@ -75,4 +92,4 @@ export interface PositionRowsOptions {
75
92
  */
76
93
  export declare function writePositionRows(options: PositionRowsOptions): PositionRowsCounts;
77
94
  /** The kind of row a node gives, if any (exported for the editor-parity test, carrick#2418). */
78
- export declare function kindOf(node: ts.Node): PositionKind | undefined;
95
+ export declare function kindOf(node: ts.Node): NodeKind | undefined;
@@ -2,7 +2,7 @@
2
2
  * Per-position compiler rows for a service (carrick#2405, move 2 of the fact
3
3
  * layer). Record-only: nothing in a scan reads the file yet.
4
4
  *
5
- * For every source file the scanner sends, one row for each position of four
5
+ * For every source file the scanner sends, one row for each position of six
6
6
  * kinds, each holding what the compiler knows there: the symbol, the
7
7
  * declaration it is attributed to (file and line), the package that declares
8
8
  * it and the type text.
@@ -13,11 +13,22 @@
13
13
  * | `member` | property or element access | the name or the argument | the access |
14
14
  * | `compare` | `=== !== == != < > <= >= instanceof in` | the left operand | the left operand |
15
15
  * | `ident` | an identifier in expression position | itself | itself |
16
+ * | `impl` | a `call` or `member` whose declaration has no body | an implementation of it | nothing |
17
+ * | `jsx` | a JSX opening or self-closing element's tag | the tag's name | the tag |
16
18
  *
17
- * A row's key is `(start, end, kind)`: TypeScript `getStart()`/`getEnd()` of
18
- * the node, in UTF-16 code units from the start of the file, which is what
19
- * the scanner's `sidecar_position` gives for an SWC span. Every path written
20
- * is relative to the scanned repo's root, with `/` separators.
19
+ * An `impl` row (version 2, carrick#2430) follows the `call` or `member` row
20
+ * it belongs to and has its span: one per declaration that go-to-implementation
21
+ * names for that row's declaration, when that declaration is an interface's
22
+ * or type literal's method, a function-typed property signature, an abstract
23
+ * member, or a function-typed class property or parameter with no
24
+ * initializer. Its `decl_*` name the implementing declaration. Past
25
+ * {@link IMPL_CAP} the row is one, with {@link FLAG_TOO_MANY} and nothing else.
26
+ *
27
+ * A row's key is `(start, end, kind)`, except that several `impl` rows share
28
+ * one: TypeScript `getStart()`/`getEnd()` of the node, in UTF-16 code units
29
+ * from the start of the file, which is what the scanner's `sidecar_position`
30
+ * gives for an SWC span. Every path written is relative to the scanned repo's
31
+ * root, with `/` separators.
21
32
  *
22
33
  * Each file is read in the program of the project that owns it
23
34
  * (`ProjectLoader.projectKeyFor`, carrick#1604), one owner's program at a
@@ -38,7 +49,7 @@ import { WriteGuard, installedPackageDirectory, isDeclarationFileName, moduleExp
38
49
  import { attributedDeclaration, installedPackageOf } from './declaration-attribution.js';
39
50
  import { typeToStringAsDeclared } from './print-type.js';
40
51
  /** The format's version, written in the header. */
41
- export const POSITION_ROWS_VERSION = 1;
52
+ export const POSITION_ROWS_VERSION = 2;
42
53
  /** The type is `any`, declared or inferred. Never set with the error type. */
43
54
  export const FLAG_ANY = 1;
44
55
  /** The type is the compiler's error type: something did not resolve. */
@@ -47,6 +58,10 @@ export const FLAG_ERROR_TYPE = 2;
47
58
  export const FLAG_CHECKER_THREW = 4;
48
59
  /** The declaration is outside the checkout, not the default library and in no installed package. */
49
60
  export const FLAG_DECLARATION_UNNAMED = 8;
61
+ /** An `impl` row standing for more implementations than {@link IMPL_CAP}; it names none. */
62
+ export const FLAG_TOO_MANY = 16;
63
+ /** The most implementations one declaration's `impl` rows list. */
64
+ export const IMPL_CAP = 16;
50
65
  const COMPARE_OPERATORS = new Set([
51
66
  ts.SyntaxKind.EqualsEqualsEqualsToken,
52
67
  ts.SyntaxKind.ExclamationEqualsEqualsToken,
@@ -79,6 +94,8 @@ export function writePositionRows(options) {
79
94
  resolved: 0,
80
95
  any: 0,
81
96
  error_type: 0,
97
+ impl: 0,
98
+ jsx: 0,
82
99
  bytes: 0,
83
100
  load_ms: 0,
84
101
  walk_ms: 0,
@@ -127,11 +144,16 @@ export function writePositionRows(options) {
127
144
  }
128
145
  read.set(index, sourceFile?.getFilePath());
129
146
  }
130
- // Built once, after every file the group adds.
131
- const program = project.getProgram().compilerObject;
147
+ // Built once, after every file the group adds. The language service's
148
+ // program is the one walked, so go-to-implementation answers over it
149
+ // and no second program is built.
150
+ const service = project.getLanguageService().compilerObject;
151
+ const program = service.getProgram();
152
+ if (!program)
153
+ throw new Error('the language service built no program');
132
154
  counts.load_ms += performance.now() - loadStarted;
133
155
  const walkStarted = performance.now();
134
- const walker = new PositionWalker(program, paths, loader);
156
+ const walker = new PositionWalker(program, service, paths, loader);
135
157
  const missed = [];
136
158
  for (const index of indexes) {
137
159
  if (!read.has(index))
@@ -151,22 +173,23 @@ export function writePositionRows(options) {
151
173
  counts.any += 1;
152
174
  if (row[8] & FLAG_ERROR_TYPE)
153
175
  counts.error_type += 1;
176
+ if (row[0] === 'impl')
177
+ counts.impl += 1;
178
+ if (row[0] === 'jsx')
179
+ counts.jsx += 1;
154
180
  }
155
181
  lines[index] = fileLine({ f: paths.relative(files[index]), h: hashes.get(index), r: rows });
156
182
  options.onFile?.();
157
183
  }
158
184
  counts.walk_ms += performance.now() - walkStarted;
159
- return missed;
185
+ return { missed, program };
160
186
  };
161
187
  const hashes = new Map();
162
188
  for (const [key, indexes] of groups) {
163
189
  const project = loader.getProjectFor(key);
164
- const missed = walkGroup(project, indexes, hashes);
190
+ const { missed, program } = walkGroup(project, indexes, hashes);
165
191
  if (missed.length > 0) {
166
- const declarations = project
167
- .getProgram()
168
- .compilerObject.getRootFileNames()
169
- .filter(isDeclarationFileName);
192
+ const declarations = program.getRootFileNames().filter(isDeclarationFileName);
170
193
  untaken.set(key, { indexes: missed, declarations });
171
194
  }
172
195
  // One owner's program at a time.
@@ -184,7 +207,7 @@ export function writePositionRows(options) {
184
207
  }
185
208
  }
186
209
  counts.load_ms += performance.now() - loadStarted;
187
- for (const index of walkGroup(project, indexes, hashes)) {
210
+ for (const index of walkGroup(project, indexes, hashes).missed) {
188
211
  lines[index] = fileLine({ f: paths.relative(files[index]), h: hashes.get(index), skip: 'not_in_program', r: [] });
189
212
  options.onFile?.();
190
213
  }
@@ -344,11 +367,21 @@ function toSlashes(p) {
344
367
  /** The rows of one program's files. */
345
368
  class PositionWalker {
346
369
  program;
370
+ service;
347
371
  paths;
348
372
  loader;
349
373
  checker;
350
- constructor(program, paths, loader) {
374
+ /**
375
+ * Each bodyless declaration's implementations, as `impl` rows with no span
376
+ * yet (`start` and `end` are 0): go-to-implementation searches the whole
377
+ * program, so it is asked once per declaration, never per call.
378
+ */
379
+ implementations = new Map();
380
+ /** The same for a member whose symbol has no declaration, keyed by symbol. */
381
+ undeclaredImplementations = new Map();
382
+ constructor(program, service, paths, loader) {
351
383
  this.program = program;
384
+ this.service = service;
352
385
  this.paths = paths;
353
386
  this.loader = loader;
354
387
  this.checker = program.getTypeChecker();
@@ -357,8 +390,17 @@ class PositionWalker {
357
390
  const rows = [];
358
391
  const visit = (node) => {
359
392
  const kind = kindOf(node);
360
- if (kind)
361
- rows.push(this.row(kind, node, sourceFile));
393
+ if (kind) {
394
+ const row = this.row(kind, node, sourceFile);
395
+ rows.push(row);
396
+ if ((kind === 'call' || kind === 'member') && !(row[8] & FLAG_CHECKER_THREW)) {
397
+ for (const impl of this.implRows(kind, node))
398
+ rows.push(['impl', row[1], row[2], ...tail(impl)]);
399
+ }
400
+ }
401
+ if (ts.isJsxOpeningElement(node) || ts.isJsxSelfClosingElement(node)) {
402
+ rows.push(this.row('jsx', node.tagName, sourceFile));
403
+ }
362
404
  ts.forEachChild(node, visit);
363
405
  };
364
406
  ts.forEachChild(sourceFile, visit);
@@ -370,7 +412,7 @@ class PositionWalker {
370
412
  try {
371
413
  const symbol = this.symbolOf(kind, node);
372
414
  const typed = kind === 'compare' ? node.left : node;
373
- const type = this.checker.getTypeAtLocation(typed);
415
+ const type = (kind === 'jsx' && this.intrinsicProps(node)) || this.checker.getTypeAtLocation(typed);
374
416
  let flags = 0;
375
417
  if (type.flags & ts.TypeFlags.Any) {
376
418
  flags |= type.intrinsicName === 'error' ? FLAG_ERROR_TYPE : FLAG_ANY;
@@ -389,13 +431,9 @@ class PositionWalker {
389
431
  const symbolName = ts.symbolName(symbol);
390
432
  const isModule = declaration.kind === ts.SyntaxKind.SourceFile && symbolName.startsWith('"');
391
433
  const declaringFile = declaration.getSourceFile();
392
- const declFile = this.program.isSourceFileDefaultLibrary(declaringFile)
393
- ? `lib:${path.basename(declaringFile.fileName)}`
394
- : this.paths.declaringFile(declaringFile.fileName);
434
+ const { declFile, declLine, declPackage } = this.located(declaringFile, declaration.getStart(declaringFile));
395
435
  if (declFile === null)
396
436
  flags |= FLAG_DECLARATION_UNNAMED;
397
- const declLine = declaringFile.getLineAndCharacterOfPosition(declaration.getStart(declaringFile)).line + 1;
398
- const declPackage = this.paths.declaringPackage(declaringFile.fileName, (file) => this.loader.packageOf(file));
399
437
  const name = isModule ? declFile : symbolName;
400
438
  return [kind, start, end, name, declFile, declLine, declPackage, typeText, flags];
401
439
  }
@@ -403,6 +441,121 @@ class PositionWalker {
403
441
  return [kind, start, end, null, null, null, null, null, FLAG_CHECKER_THREW];
404
442
  }
405
443
  }
444
+ /**
445
+ * An intrinsic element's type: the attributes `JSX.IntrinsicElements`
446
+ * gives its tag. The compiler types an intrinsic tag name itself as the
447
+ * error type, though the name resolves. A tag is intrinsic when it is an
448
+ * identifier that starts with a lowercase letter or holds a `-`, as the
449
+ * language reads it.
450
+ */
451
+ intrinsicProps(tag) {
452
+ if (!ts.isIdentifier(tag) || !/^[a-z]|-/.test(tag.text))
453
+ return undefined;
454
+ const element = tag.parent;
455
+ if (!ts.isJsxOpeningElement(element) && !ts.isJsxSelfClosingElement(element))
456
+ return undefined;
457
+ return this.checker.getContextualType(element.attributes);
458
+ }
459
+ /** How rows name a position in a program file: its file, 1-based line and package. */
460
+ located(declaringFile, position) {
461
+ const declFile = this.program.isSourceFileDefaultLibrary(declaringFile)
462
+ ? `lib:${path.basename(declaringFile.fileName)}`
463
+ : this.paths.declaringFile(declaringFile.fileName);
464
+ const declLine = declaringFile.getLineAndCharacterOfPosition(position).line + 1;
465
+ const declPackage = this.paths.declaringPackage(declaringFile.fileName, (file) => this.loader.packageOf(file));
466
+ return { declFile, declLine, declPackage };
467
+ }
468
+ /**
469
+ * The `impl` rows of a `call` or `member` row (spans unset): none unless the
470
+ * row's declaration has no body of its own ({@link isBodyless}). Its own
471
+ * search, apart from the row's: a throw here writes one `impl` row flagged
472
+ * {@link FLAG_CHECKER_THREW} and leaves the row as it was.
473
+ */
474
+ implRows(kind, node) {
475
+ let declaration;
476
+ try {
477
+ const symbol = this.symbolOf(kind, node);
478
+ if (symbol && (symbol.declarations ?? []).length === 0)
479
+ return this.undeclaredImplRows(kind, node, symbol);
480
+ declaration = symbol ? attributedDeclaration(() => this.program, symbol.declarations ?? []) : undefined;
481
+ if (!declaration || !isBodyless(declaration, this.checker))
482
+ return [];
483
+ }
484
+ catch {
485
+ return [];
486
+ }
487
+ let rows = this.implementations.get(declaration);
488
+ if (!rows) {
489
+ try {
490
+ rows = this.implementationsOf(declaration);
491
+ }
492
+ catch {
493
+ rows = [['impl', 0, 0, null, null, null, null, null, FLAG_CHECKER_THREW]];
494
+ }
495
+ this.implementations.set(declaration, rows);
496
+ }
497
+ return rows;
498
+ }
499
+ /**
500
+ * The `impl` rows of a member whose symbol has no declaration (a mapped
501
+ * type's member): there is no declaration name to ask at, so the answer is
502
+ * asked at the use site's name, once per symbol. Same shape as
503
+ * {@link implementationsOf}; nothing of the symbol's own is filtered out.
504
+ */
505
+ undeclaredImplRows(kind, node, symbol) {
506
+ const cached = this.undeclaredImplementations.get(symbol);
507
+ if (cached)
508
+ return cached;
509
+ let rows;
510
+ try {
511
+ const named = nameNodeOf(kind, node);
512
+ rows = named ? this.implementationRows(named, []) : [];
513
+ }
514
+ catch {
515
+ rows = [['impl', 0, 0, null, null, null, null, null, FLAG_CHECKER_THREW]];
516
+ }
517
+ this.undeclaredImplementations.set(symbol, rows);
518
+ return rows;
519
+ }
520
+ /**
521
+ * What go-to-implementation names for `declaration`, asked at its name:
522
+ * every declaration the language service answers, except the symbol's own
523
+ * (an overloaded method answers with its other signatures), in file and
524
+ * position order.
525
+ */
526
+ implementationsOf(declaration) {
527
+ const name = ts.getNameOfDeclaration(declaration) ?? declaration;
528
+ const own = (this.checker.getSymbolAtLocation(name) ?? declaration.symbol)
529
+ ?.declarations ?? [declaration];
530
+ return this.implementationRows(name, own);
531
+ }
532
+ /** Go-to-implementation at `name`, minus results inside `own`, as sorted `impl` rows. */
533
+ implementationRows(name, own) {
534
+ const declaringFile = name.getSourceFile();
535
+ const found = new Map();
536
+ for (const location of this.service.getImplementationAtPosition(declaringFile.fileName, name.getStart(declaringFile)) ?? []) {
537
+ const file = this.program.getSourceFile(location.fileName);
538
+ if (!file)
539
+ continue;
540
+ const start = location.textSpan.start;
541
+ if (own.some((d) => d.getSourceFile() === file && d.pos <= start && start < d.end))
542
+ continue;
543
+ found.set(`${location.fileName}\u0000${start}`, { file, start });
544
+ }
545
+ if (found.size === 0)
546
+ return [];
547
+ if (found.size > IMPL_CAP)
548
+ return [['impl', 0, 0, null, null, null, null, null, FLAG_TOO_MANY]];
549
+ const rows = [...found.values()].map(({ file, start }) => {
550
+ const { declFile, declLine, declPackage } = this.located(file, start);
551
+ const named = nodeStartingAt(file, start);
552
+ const symbol = named ? this.checker.getSymbolAtLocation(named) : undefined;
553
+ const flags = declFile === null ? FLAG_DECLARATION_UNNAMED : 0;
554
+ return { start, row: ['impl', 0, 0, symbol ? ts.symbolName(symbol) : null, declFile, declLine, declPackage, null, flags] };
555
+ });
556
+ rows.sort((a, b) => compareNames(a.row[4], b.row[4]) || a.start - b.start);
557
+ return rows.map(({ row }) => row);
558
+ }
406
559
  /**
407
560
  * The declaration a member with none is attributed to (carrick#2448): the
408
561
  * mapped type node, or the alias naming it, of the receiver's type. A
@@ -550,6 +703,54 @@ class PositionWalker {
550
703
  return this.host;
551
704
  }
552
705
  }
706
+ /** A row's fields after its kind and span. */
707
+ function tail(row) {
708
+ return [row[3], row[4], row[5], row[6], row[7], row[8]];
709
+ }
710
+ /** Order two `decl_file` names, `null` last. */
711
+ function compareNames(a, b) {
712
+ if (a === b)
713
+ return 0;
714
+ if (a === null)
715
+ return 1;
716
+ if (b === null)
717
+ return -1;
718
+ return a < b ? -1 : 1;
719
+ }
720
+ /**
721
+ * Whether a declaration has no body for a call to run: an interface's or type
722
+ * literal's method, a function-typed property signature, an abstract method or
723
+ * property, or a function-typed class property or parameter with no
724
+ * initializer. Its implementations are what go-to-implementation names.
725
+ */
726
+ function isBodyless(declaration, checker) {
727
+ const functionTyped = () => checker.getTypeAtLocation(declaration).getNonNullableType().getCallSignatures().length > 0;
728
+ if (ts.isMethodSignature(declaration))
729
+ return true;
730
+ if (ts.isPropertySignature(declaration))
731
+ return functionTyped();
732
+ if (ts.isMethodDeclaration(declaration) || ts.isPropertyDeclaration(declaration)) {
733
+ if (ts.getCombinedModifierFlags(declaration) & ts.ModifierFlags.Abstract)
734
+ return true;
735
+ return ts.isPropertyDeclaration(declaration) && !declaration.initializer && functionTyped();
736
+ }
737
+ if (ts.isParameter(declaration))
738
+ return !declaration.initializer && functionTyped();
739
+ return false;
740
+ }
741
+ /** The innermost node of `file` that starts at `position` (an implementation's name). */
742
+ function nodeStartingAt(file, position) {
743
+ let found;
744
+ const visit = (node) => {
745
+ if (position < node.getStart(file) || position >= node.getEnd())
746
+ return;
747
+ if (node.getStart(file) === position)
748
+ found = node;
749
+ ts.forEachChild(node, visit);
750
+ };
751
+ ts.forEachChild(file, visit);
752
+ return found;
753
+ }
553
754
  /** The kind of row a node gives, if any (exported for the editor-parity test, carrick#2418). */
554
755
  export function kindOf(node) {
555
756
  if (ts.isCallExpression(node) || ts.isNewExpression(node) || ts.isTaggedTemplateExpression(node)) {
@@ -576,6 +777,12 @@ function nameNodeOf(kind, node) {
576
777
  return referenceName(node.left);
577
778
  case 'ident':
578
779
  return node;
780
+ case 'impl':
781
+ return undefined;
782
+ case 'jsx':
783
+ // The tag: `<Badge>`, `<ui.Button>`, `<this.Panel>`; a namespaced
784
+ // name (`<svg:path>`) names nothing.
785
+ return ts.isJsxNamespacedName(node) ? undefined : (referenceName(node) ?? node);
579
786
  }
580
787
  }
581
788
  /**
@@ -765,6 +765,10 @@ export interface PositionRowsCounts {
765
765
  any: number;
766
766
  /** Rows whose type is the compiler's error type (flag 2). */
767
767
  error_type: number;
768
+ /** `impl` rows: implementations of a bodyless declaration, or one past the cap (carrick#2430). */
769
+ impl: number;
770
+ /** `jsx` rows: one per JSX opening or self-closing element. */
771
+ jsx: number;
768
772
  /** The rows file's size on disk, compressed. */
769
773
  bytes: number;
770
774
  /** Time spent building programs and reading files into them. */