feed-the-machine 1.3.0 → 1.4.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.
Files changed (45) hide show
  1. package/ftm-audit/SKILL.md +383 -57
  2. package/ftm-brainstorm/SKILL.md +119 -51
  3. package/ftm-config/SKILL.md +1 -1
  4. package/ftm-council/SKILL.md +259 -31
  5. package/ftm-dashboard/SKILL.md +10 -10
  6. package/ftm-debug/SKILL.md +861 -54
  7. package/ftm-diagram/SKILL.md +1 -1
  8. package/ftm-executor/SKILL.md +6 -6
  9. package/ftm-git/SKILL.md +208 -22
  10. package/ftm-inbox/bin/start.sh +1 -1
  11. package/ftm-inbox/bin/status.sh +1 -1
  12. package/ftm-inbox/bin/stop.sh +1 -1
  13. package/ftm-intent/SKILL.md +0 -1
  14. package/ftm-map/SKILL.md +46 -14
  15. package/ftm-map/scripts/db.py +439 -118
  16. package/ftm-map/scripts/index.py +128 -54
  17. package/ftm-map/scripts/parser.py +89 -320
  18. package/ftm-map/scripts/queries/go-tags.scm +20 -0
  19. package/ftm-map/scripts/queries/javascript-tags.scm +19 -7
  20. package/ftm-map/scripts/queries/python-tags.scm +22 -8
  21. package/ftm-map/scripts/queries/ruby-tags.scm +19 -0
  22. package/ftm-map/scripts/queries/rust-tags.scm +37 -0
  23. package/ftm-map/scripts/queries/typescript-tags.scm +20 -8
  24. package/ftm-map/scripts/query.py +176 -24
  25. package/ftm-map/scripts/ranker.py +377 -0
  26. package/ftm-map/scripts/requirements.txt +3 -0
  27. package/ftm-map/scripts/setup.sh +11 -0
  28. package/ftm-map/scripts/test_db.py +355 -115
  29. package/ftm-map/scripts/test_parser.py +169 -101
  30. package/ftm-map/scripts/test_query.py +178 -61
  31. package/ftm-map/scripts/test_ranker.py +199 -0
  32. package/ftm-map/scripts/views.py +107 -61
  33. package/ftm-mind/SKILL.md +861 -11
  34. package/ftm-mind/references/event-registry.md +20 -0
  35. package/ftm-pause/SKILL.md +256 -37
  36. package/ftm-resume/SKILL.md +380 -75
  37. package/ftm-retro/SKILL.md +164 -27
  38. package/ftm-upgrade/SKILL.md +4 -4
  39. package/hooks/ftm-blackboard-enforcer.sh +2 -4
  40. package/install.sh +6 -1
  41. package/package.json +1 -1
  42. package/ftm-map/scripts/tests/fixtures/__init__.py +0 -0
  43. package/ftm-map/scripts/tests/fixtures/sample_project/api.ts +0 -16
  44. package/ftm-map/scripts/tests/fixtures/sample_project/auth.py +0 -15
  45. package/ftm-map/scripts/tests/fixtures/sample_project/utils.js +0 -16
@@ -1,17 +1,25 @@
1
1
  """
2
- Tree-sitter based parser for extracting symbols and relationships from source code.
3
- Uses tree-sitter-language-pack for multi-language support and per-language .scm query files.
2
+ Tree-sitter based parser for extracting definition and reference tags from source code.
3
+
4
+ Uses Aider-style @name.definition.* / @name.reference.* capture convention in
5
+ per-language .scm query files for structured tag extraction. Falls back to Pygments
6
+ lexer for reference extraction when tree-sitter queries lack reference patterns.
4
7
  """
5
8
  import hashlib
6
9
  import os
7
10
  import sys
8
- from dataclasses import dataclass
11
+ from collections import namedtuple
9
12
  from pathlib import Path
10
13
  from typing import Optional
11
14
 
12
15
  import tree_sitter as ts
13
16
  from tree_sitter_language_pack import get_language, get_parser
14
17
 
18
+ # Tag namedtuple: the single output type for all extraction
19
+ # kind is "def" or "ref"
20
+ # rel_fname is relative path, fname is absolute path
21
+ Tag = namedtuple("Tag", ["rel_fname", "fname", "line", "name", "kind"])
22
+
15
23
  QUERIES_DIR = os.path.join(os.path.dirname(__file__), "queries")
16
24
 
17
25
  # Map file extensions to tree-sitter language names
@@ -35,67 +43,6 @@ EXTENSION_MAP = {
35
43
  ".sh": "bash",
36
44
  }
37
45
 
38
- # Node types that represent definitions in generic AST walk, keyed by node type
39
- DEFINITION_TYPES = {
40
- # TypeScript / JavaScript
41
- "function_declaration": "function",
42
- "method_definition": "method",
43
- "class_declaration": "class",
44
- "arrow_function": "function",
45
- "lexical_declaration": "variable",
46
- "variable_declaration": "variable",
47
- "interface_declaration": "class",
48
- "type_alias_declaration": "type",
49
- "enum_declaration": "class",
50
- # Python
51
- "function_definition": "function",
52
- "class_definition": "class",
53
- "decorated_definition": None, # unwrap to inner definition
54
- # Imports
55
- "import_statement": "import",
56
- "import_from_statement": "import",
57
- }
58
-
59
- # Node types that carry a symbol name field
60
- NAME_TYPES = frozenset({
61
- "identifier",
62
- "property_identifier",
63
- "type_identifier",
64
- })
65
-
66
- # Node types representing call expressions per language
67
- CALL_TYPES = frozenset({
68
- "call_expression", # JS/TS/Go/Rust
69
- "call", # Ruby
70
- })
71
-
72
- # Node types representing import statements
73
- IMPORT_TYPES = frozenset({
74
- "import_statement",
75
- "import_from_statement",
76
- })
77
-
78
-
79
- @dataclass
80
- class Symbol:
81
- name: str
82
- kind: str # function, class, method, variable, import, type
83
- file_path: str
84
- start_line: int
85
- end_line: int
86
- signature: str = ""
87
- doc_comment: str = ""
88
- content_hash: str = ""
89
-
90
-
91
- @dataclass
92
- class Relationship:
93
- source_name: str
94
- target_name: str
95
- kind: str # calls, imports, extends, implements, uses
96
- source_file: str
97
- target_file: str = "" # may be unknown for cross-file refs
98
-
99
46
 
100
47
  # ---------------------------------------------------------------------------
101
48
  # Public API
@@ -112,55 +59,44 @@ def compute_content_hash(content: str) -> str:
112
59
  return hashlib.sha256(content.encode()).hexdigest()[:16]
113
60
 
114
61
 
115
- def parse_file(file_path: str) -> list[Symbol]:
116
- """Parse a source file and extract symbols.
62
+ def get_tags(fname: str, rel_fname: str = None) -> list[Tag]:
63
+ """Extract definition and reference tags from a source file.
64
+
65
+ Uses tree-sitter query files with @name.definition.* and @name.reference.*
66
+ capture naming convention (Aider-style).
117
67
 
118
- Returns a list of Symbol objects. Handles parse errors gracefully by
119
- returning partial results and logging warnings to stderr.
68
+ Returns list of Tag namedtuples with kind="def" or kind="ref".
120
69
  """
121
- lang = detect_language(file_path)
70
+ if rel_fname is None:
71
+ rel_fname = fname
72
+
73
+ lang = detect_language(fname)
122
74
  if not lang:
123
75
  return []
124
76
 
125
- source = _read_source(file_path)
77
+ source = _read_source(fname)
126
78
  if source is None:
127
79
  return []
128
80
 
129
- tree = _parse_source(source, lang, file_path)
81
+ tree = _parse_source(source, lang, fname)
130
82
  if tree is None:
131
83
  return []
132
84
 
133
85
  scm_path = os.path.join(QUERIES_DIR, f"{lang}-tags.scm")
134
- if os.path.exists(scm_path):
135
- symbols = _extract_with_query(tree, source, file_path, lang, scm_path)
136
- # Fall back to generic walk if the query produced nothing
137
- if symbols:
138
- return symbols
139
-
140
- return _extract_generic(tree, source, file_path)
141
-
142
-
143
- def extract_relationships(file_path: str) -> list[Relationship]:
144
- """Extract relationships (calls, imports) from a source file.
145
-
146
- Returns a list of Relationship objects.
147
- """
148
- lang = detect_language(file_path)
149
- if not lang:
150
- return []
86
+ if not os.path.exists(scm_path):
87
+ # No query file -- use Pygments fallback for refs only
88
+ return _pygments_ref_fallback(source, fname, rel_fname)
151
89
 
152
- source = _read_source(file_path)
153
- if source is None:
154
- return []
90
+ tags = _extract_tags(tree, source, fname, rel_fname, lang, scm_path)
155
91
 
156
- tree = _parse_source(source, lang, file_path)
157
- if tree is None:
158
- return []
92
+ # If we got defs but no refs, supplement with Pygments fallback for refs
93
+ has_defs = any(t.kind == "def" for t in tags)
94
+ has_refs = any(t.kind == "ref" for t in tags)
95
+ if has_defs and not has_refs:
96
+ ref_tags = _pygments_ref_fallback(source, fname, rel_fname)
97
+ tags.extend(ref_tags)
159
98
 
160
- relationships: list[Relationship] = []
161
- _extract_calls(tree.root_node, source, file_path, relationships)
162
- _extract_imports(tree.root_node, source, file_path, relationships)
163
- return relationships
99
+ return tags
164
100
 
165
101
 
166
102
  # ---------------------------------------------------------------------------
@@ -188,23 +124,15 @@ def _parse_source(source: str, lang: str, file_path: str):
188
124
 
189
125
 
190
126
  # ---------------------------------------------------------------------------
191
- # Query-based extraction
127
+ # Tag extraction via tree-sitter queries
192
128
  # ---------------------------------------------------------------------------
193
129
 
194
- def _extract_with_query(tree, source: str, file_path: str, lang: str, scm_path: str) -> list[Symbol]:
195
- """Extract symbols using a language-specific .scm query file.
196
-
197
- Uses tree-sitter QueryCursor.matches() which returns per-pattern match dicts.
198
- Each match dict contains both the @definition.X node and the @name node so
199
- they are already correlated — no post-hoc joining needed.
200
-
201
- Falls back to empty list on any error so callers can use generic extraction.
202
- """
130
+ def _extract_tags(tree, source: str, fname: str, rel_fname: str, lang: str, scm_path: str) -> list[Tag]:
131
+ """Extract tags using tree-sitter query with @name.definition.* / @name.reference.* convention."""
203
132
  try:
204
133
  with open(scm_path) as fh:
205
134
  query_text = fh.read()
206
- except (IOError, OSError) as exc:
207
- print(f"Warning: Cannot read query {scm_path}: {exc}", file=sys.stderr)
135
+ except (IOError, OSError):
208
136
  return []
209
137
 
210
138
  try:
@@ -212,211 +140,76 @@ def _extract_with_query(tree, source: str, file_path: str, lang: str, scm_path:
212
140
  query = ts.Query(language, query_text)
213
141
  cursor = ts.QueryCursor(query)
214
142
  matches = list(cursor.matches(tree.root_node))
215
- except Exception as exc: # noqa: BLE001
216
- print(f"Warning: Query execution failed for {file_path}: {exc}", file=sys.stderr)
143
+ except Exception: # noqa: BLE001
217
144
  return []
218
145
 
219
- return _process_matches(matches, source, file_path)
146
+ tags = []
147
+ seen = {} # (name, start_byte, end_byte) -> Tag for dedup
220
148
 
149
+ for _pattern_idx, capture_dict in matches:
150
+ for capture_name, nodes in capture_dict.items():
151
+ # Only process @name.definition.* and @name.reference.* captures
152
+ if capture_name.startswith("name.definition."):
153
+ kind = "def"
154
+ elif capture_name.startswith("name.reference."):
155
+ kind = "ref"
156
+ else:
157
+ continue
221
158
 
222
- def _process_matches(matches: list, source: str, file_path: str) -> list[Symbol]:
223
- """Convert QueryCursor.matches() results into Symbol objects.
159
+ for node in nodes:
160
+ name_text = source[node.start_byte:node.end_byte].strip()
161
+ if not name_text:
162
+ continue
224
163
 
225
- Each match is a (pattern_index, capture_dict) tuple where capture_dict maps
226
- capture name → list[Node]. Both @definition.X and @name appear in the same
227
- capture_dict for each pattern match, making correlation trivial.
228
- """
229
- symbols: list[Symbol] = []
164
+ key = (name_text, node.start_byte, node.end_byte)
165
+ if key in seen:
166
+ continue
230
167
 
231
- for _pattern_idx, capture_dict in matches:
232
- # Find the definition capture (e.g. "definition.function")
233
- def_key = next(
234
- (k for k in capture_dict if k.startswith("definition.")),
235
- None,
236
- )
237
- if not def_key:
238
- continue
239
-
240
- kind = def_key[len("definition."):]
241
- def_nodes = capture_dict[def_key]
242
- name_nodes = capture_dict.get("name", [])
243
-
244
- if not def_nodes:
245
- continue
246
-
247
- def_node = def_nodes[0]
248
-
249
- # Prefer the @name capture; fall back to identifier child walk
250
- if name_nodes:
251
- sym_name = source[name_nodes[0].start_byte:name_nodes[0].end_byte].strip()
252
- else:
253
- sym_name = _find_name(def_node, source)
254
-
255
- if not sym_name:
256
- continue
257
-
258
- body = source[def_node.start_byte:def_node.end_byte]
259
- sig = _first_line(body)
260
- doc = _find_doc_comment(def_node, source)
261
-
262
- symbols.append(Symbol(
263
- name=sym_name,
264
- kind=kind,
265
- file_path=file_path,
266
- start_line=def_node.start_point[0] + 1,
267
- end_line=def_node.end_point[0] + 1,
268
- signature=sig,
269
- doc_comment=doc,
270
- content_hash=compute_content_hash(body),
271
- ))
272
-
273
- return symbols
168
+ line = node.start_point[0] + 1
169
+ tag = Tag(rel_fname=rel_fname, fname=fname, line=line, name=name_text, kind=kind)
170
+ seen[key] = tag
171
+ tags.append(tag)
172
+
173
+ return tags
274
174
 
275
175
 
276
176
  # ---------------------------------------------------------------------------
277
- # Generic AST walk extraction
177
+ # Pygments fallback for references
278
178
  # ---------------------------------------------------------------------------
279
179
 
280
- def _extract_generic(tree, source: str, file_path: str) -> list[Symbol]:
281
- """Walk the AST and extract symbols without a query file."""
282
- symbols: list[Symbol] = []
283
- _walk_node(tree.root_node, source, file_path, symbols)
284
- return symbols
285
-
286
-
287
- def _walk_node(node, source: str, file_path: str, symbols: list[Symbol]) -> None:
288
- """Recursively walk AST nodes looking for definition nodes."""
289
- node_type = node.type
290
-
291
- if node_type in DEFINITION_TYPES:
292
- kind = DEFINITION_TYPES[node_type]
293
-
294
- if kind is None:
295
- # Decorated definition — unwrap inner nodes only
296
- for child in node.children:
297
- _walk_node(child, source, file_path, symbols)
298
- return
299
-
300
- # Skip bare arrow functions without a variable name context
301
- if node_type == "arrow_function":
302
- for child in node.children:
303
- _walk_node(child, source, file_path, symbols)
304
- return
305
-
306
- name = _find_name(node, source)
307
- if not name:
308
- for child in node.children:
309
- _walk_node(child, source, file_path, symbols)
310
- return
311
-
312
- body = source[node.start_byte:node.end_byte]
313
- sig = _first_line(body)
314
- doc = _find_doc_comment(node, source)
315
-
316
- symbols.append(Symbol(
317
- name=name,
318
- kind=kind,
319
- file_path=file_path,
320
- start_line=node.start_point[0] + 1,
321
- end_line=node.end_point[0] + 1,
322
- signature=sig,
323
- doc_comment=doc,
324
- content_hash=compute_content_hash(body),
325
- ))
326
-
327
- # Always recurse (definitions can be nested)
328
- for child in node.children:
329
- _walk_node(child, source, file_path, symbols)
180
+ def _pygments_ref_fallback(source: str, fname: str, rel_fname: str) -> list[Tag]:
181
+ """Use Pygments to extract reference-like tokens when tree-sitter refs are missing."""
182
+ try:
183
+ from pygments.lexers import get_lexer_for_filename
184
+ from pygments.token import Token
185
+ except ImportError:
186
+ return []
330
187
 
188
+ try:
189
+ lexer = get_lexer_for_filename(fname)
190
+ except Exception: # noqa: BLE001
191
+ return []
331
192
 
332
- # ---------------------------------------------------------------------------
333
- # Relationship extraction
334
- # ---------------------------------------------------------------------------
193
+ tags = []
194
+ line = 1
195
+ for token_type, value in lexer.get_tokens(source):
196
+ # Count newlines for line tracking
197
+ newlines = value.count('\n')
198
+ if token_type in Token.Name and value.strip():
199
+ tags.append(Tag(rel_fname=rel_fname, fname=fname, line=line, name=value.strip(), kind="ref"))
200
+ line += newlines
335
201
 
336
- def _extract_calls(node, source: str, file_path: str, rels: list[Relationship]) -> None:
337
- """Recursively extract function call relationships."""
338
- if node.type in CALL_TYPES:
339
- func_node = node.children[0] if node.children else None
340
- if func_node:
341
- callee_text = source[func_node.start_byte:func_node.end_byte]
342
- # Simplify dotted paths to last component
343
- callee_name = callee_text.split(".")[-1].split("(")[0].strip()
344
- caller_name = _find_enclosing_function(node, source)
345
- if callee_name and caller_name:
346
- rels.append(Relationship(
347
- source_name=caller_name,
348
- target_name=callee_name,
349
- kind="calls",
350
- source_file=file_path,
351
- ))
352
-
353
- for child in node.children:
354
- _extract_calls(child, source, file_path, rels)
355
-
356
-
357
- def _extract_imports(node, source: str, file_path: str, rels: list[Relationship]) -> None:
358
- """Recursively extract import relationships."""
359
- if node.type in IMPORT_TYPES:
360
- module_stem = Path(file_path).stem
361
- _collect_import_names(node, source, module_stem, file_path, rels)
362
-
363
- for child in node.children:
364
- _extract_imports(child, source, file_path, rels)
365
-
366
-
367
- def _collect_import_names(
368
- node,
369
- source: str,
370
- module_stem: str,
371
- file_path: str,
372
- rels: list[Relationship],
373
- ) -> None:
374
- """Walk an import node and emit Relationship objects for each imported name."""
375
- for child in node.children:
376
- child_type = child.type
377
-
378
- if child_type == "dotted_name":
379
- imported = source[child.start_byte:child.end_byte]
380
- rels.append(Relationship(
381
- source_name=module_stem,
382
- target_name=imported,
383
- kind="imports",
384
- source_file=file_path,
385
- ))
386
-
387
- elif child_type in ("import_clause", "named_imports", "import_specifier"):
388
- for grandchild in child.children:
389
- if grandchild.type in NAME_TYPES:
390
- name = source[grandchild.start_byte:grandchild.end_byte].strip()
391
- if name and name not in ("{", "}", ","):
392
- rels.append(Relationship(
393
- source_name=module_stem,
394
- target_name=name,
395
- kind="imports",
396
- source_file=file_path,
397
- ))
398
-
399
- elif child_type == "string":
400
- # import ... from "module-path"
401
- raw = source[child.start_byte:child.end_byte].strip("'\"")
402
- rels.append(Relationship(
403
- source_name=module_stem,
404
- target_name=raw,
405
- kind="imports",
406
- source_file=file_path,
407
- ))
202
+ return tags
408
203
 
409
204
 
410
205
  # ---------------------------------------------------------------------------
411
- # Small AST utilities
206
+ # Small utilities (kept for potential downstream use)
412
207
  # ---------------------------------------------------------------------------
413
208
 
414
- def _find_name(node, source: str) -> str:
415
- """Find the first name-like identifier child of a node."""
416
- for child in node.children:
417
- if child.type in NAME_TYPES:
418
- return source[child.start_byte:child.end_byte]
419
- return ""
209
+ def _first_line(text: str, max_len: int = 200) -> str:
210
+ """Return the first non-empty line of text, truncated to max_len."""
211
+ line = text.split("\n")[0].strip()
212
+ return line[:max_len] + "..." if len(line) > max_len else line
420
213
 
421
214
 
422
215
  def _find_doc_comment(node, source: str) -> str:
@@ -429,27 +222,3 @@ def _find_doc_comment(node, source: str) -> str:
429
222
  text = text.strip(marker)
430
223
  return text.strip()[:500]
431
224
  return ""
432
-
433
-
434
- def _first_line(text: str, max_len: int = 200) -> str:
435
- """Return the first non-empty line of text, truncated to max_len."""
436
- line = text.split("\n")[0].strip()
437
- return line[:max_len] + "..." if len(line) > max_len else line
438
-
439
-
440
- def _find_enclosing_function(node, source: str) -> str:
441
- """Walk up the AST to find the name of the nearest enclosing function."""
442
- enclosing_types = {
443
- "function_declaration",
444
- "function_definition",
445
- "method_definition",
446
- "arrow_function",
447
- }
448
- current = node.parent
449
- while current:
450
- if current.type in enclosing_types:
451
- name = _find_name(current, source)
452
- if name:
453
- return name
454
- current = current.parent
455
- return ""
@@ -0,0 +1,20 @@
1
+ ; Functions
2
+ (function_declaration
3
+ name: (identifier) @name.definition.function) @definition.function
4
+
5
+ ; Methods
6
+ (method_declaration
7
+ name: (field_identifier) @name.definition.method) @definition.method
8
+
9
+ ; Type declarations
10
+ (type_declaration
11
+ (type_spec
12
+ name: (type_identifier) @name.definition.type)) @definition.type
13
+
14
+ ; Call references
15
+ (call_expression
16
+ function: [
17
+ (identifier) @name.reference.call
18
+ (selector_expression
19
+ field: (field_identifier) @name.reference.call)
20
+ ]) @reference.call
@@ -1,23 +1,35 @@
1
1
  ; Functions
2
2
  (function_declaration
3
- name: (identifier) @name) @definition.function
3
+ name: (identifier) @name.definition.function) @definition.function
4
4
 
5
5
  ; Methods
6
6
  (method_definition
7
- name: (property_identifier) @name) @definition.method
7
+ name: (property_identifier) @name.definition.method) @definition.method
8
8
 
9
9
  ; Classes
10
10
  (class_declaration
11
- name: (identifier) @name) @definition.class
11
+ name: (identifier) @name.definition.class) @definition.class
12
12
 
13
- ; Arrow functions assigned to const/let variables
13
+ ; Arrow functions assigned to const/let
14
14
  (lexical_declaration
15
15
  (variable_declarator
16
- name: (identifier) @name
16
+ name: (identifier) @name.definition.function
17
17
  value: (arrow_function))) @definition.function
18
18
 
19
- ; Arrow functions assigned to var variables
19
+ ; Arrow functions assigned to var
20
20
  (variable_declaration
21
21
  (variable_declarator
22
- name: (identifier) @name
22
+ name: (identifier) @name.definition.function
23
23
  value: (arrow_function))) @definition.function
24
+
25
+ ; Call references
26
+ (call_expression
27
+ function: [
28
+ (identifier) @name.reference.call
29
+ (member_expression
30
+ property: (property_identifier) @name.reference.call)
31
+ ]) @reference.call
32
+
33
+ ; New expressions
34
+ (new_expression
35
+ constructor: (identifier) @name.reference.class) @reference.class
@@ -1,17 +1,31 @@
1
- ; Functions
2
- (function_definition
3
- name: (identifier) @name) @definition.function
1
+ ; Module-level constants
2
+ (module
3
+ (expression_statement
4
+ (assignment
5
+ left: (identifier) @name.definition.constant) @definition.constant))
4
6
 
5
7
  ; Classes
6
8
  (class_definition
7
- name: (identifier) @name) @definition.class
9
+ name: (identifier) @name.definition.class) @definition.class
10
+
11
+ ; Functions
12
+ (function_definition
13
+ name: (identifier) @name.definition.function) @definition.function
8
14
 
9
- ; Decorated functions
15
+ ; Decorated definitions (functions)
10
16
  (decorated_definition
11
17
  definition: (function_definition
12
- name: (identifier) @name) @definition.function)
18
+ name: (identifier) @name.definition.function)) @definition.function
13
19
 
14
- ; Decorated classes
20
+ ; Decorated definitions (classes)
15
21
  (decorated_definition
16
22
  definition: (class_definition
17
- name: (identifier) @name) @definition.class)
23
+ name: (identifier) @name.definition.class)) @definition.class
24
+
25
+ ; Call references (direct function calls and attribute method calls)
26
+ (call
27
+ function: [
28
+ (identifier) @name.reference.call
29
+ (attribute
30
+ attribute: (identifier) @name.reference.call)
31
+ ]) @reference.call
@@ -0,0 +1,19 @@
1
+ ; Methods
2
+ (method
3
+ name: (identifier) @name.definition.method) @definition.method
4
+
5
+ ; Singleton methods
6
+ (singleton_method
7
+ name: (identifier) @name.definition.method) @definition.method
8
+
9
+ ; Classes
10
+ (class
11
+ name: (constant) @name.definition.class) @definition.class
12
+
13
+ ; Modules
14
+ (module
15
+ name: (constant) @name.definition.module) @definition.module
16
+
17
+ ; Call references
18
+ (call
19
+ method: (identifier) @name.reference.call) @reference.call
@@ -0,0 +1,37 @@
1
+ ; Functions
2
+ (function_item
3
+ name: (identifier) @name.definition.function) @definition.function
4
+
5
+ ; Structs
6
+ (struct_item
7
+ name: (type_identifier) @name.definition.struct) @definition.struct
8
+
9
+ ; Enums
10
+ (enum_item
11
+ name: (type_identifier) @name.definition.enum) @definition.enum
12
+
13
+ ; Traits
14
+ (trait_item
15
+ name: (type_identifier) @name.definition.trait) @definition.trait
16
+
17
+ ; Impl blocks
18
+ (impl_item
19
+ trait: (type_identifier) @name.definition.impl) @definition.impl
20
+
21
+ ; Modules
22
+ (mod_item
23
+ name: (identifier) @name.definition.module) @definition.module
24
+
25
+ ; Macro definitions
26
+ (macro_definition
27
+ name: (identifier) @name.definition.macro) @definition.macro
28
+
29
+ ; Call references
30
+ (call_expression
31
+ function: [
32
+ (identifier) @name.reference.call
33
+ (field_expression
34
+ field: (field_identifier) @name.reference.call)
35
+ (scoped_identifier
36
+ name: (identifier) @name.reference.call)
37
+ ]) @reference.call