Skip to content

Commit 41a5736

Browse files
committed
update
1 parent 279c36e commit 41a5736

File tree

11 files changed

+217
-97
lines changed

11 files changed

+217
-97
lines changed

AGENTS.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44

55
- Lint & format: `ruff check --fix && ruff format`
66
- Type checking: `ty check <dir_or_file>`
7-
- Run tests: `pytest`
7+
- Run tests: `uv run pytest`
88
- Use `uv` for Python related commands.
99

1010
## Code Style Guidelines

docs/schemas/draft/relation.md

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,22 @@ It leverages the [Call Hierarchy API](call_hierarchy.md) to trace paths.
2121
| `chains` | `CallHierarchyItem[][]` | List of paths connecting source to target. Each path is a sequence of items.|
2222
| `max_depth` | `number` | The maximum depth used for the search. |
2323

24+
## Implementation Guide
25+
26+
This API is implemented by orchestrating standard LSP `Call Hierarchy` requests.
27+
28+
### Algorithm: Bidirectional Search
29+
30+
1. **Resolve Symbols**:
31+
* Use `textDocument/definition` or `textDocument/documentSymbol` to resolve the `source` and `target` locations to valid LSP `CallHierarchyItem`s using `textDocument/prepareCallHierarchy`.
32+
2. **Breadth-First Search (BFS)**:
33+
* Perform `callHierarchy/outgoingCalls` from the `source` item.
34+
* (Optional optimization) Simultaneously perform `callHierarchy/incomingCalls` from the `target` item.
35+
* Maintain a `visited` set to detect and break recursive cycles.
36+
3. **Path Reconstruction**:
37+
* When the search frontiers meet (or one reaches the other end), reconstruction the full path.
38+
* Filter out paths that exceed `max_depth`.
39+
2440
## Example Usage
2541

2642
### Scenario 1: How does `handle_request` reach `db.query`?
Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
# Test Relation API
2+
3+
The Test Relation API bridges the gap between production code and test code. It helps Agents perform targeted verification ("I changed X, what should I run?") and understand test failures ("Test Y failed, what does it cover?").
4+
5+
## TestRelationRequest
6+
7+
| Field | Type | Default | Description |
8+
| :---------- | :---------------------- | :---------- | :-------------------------------------------------------- |
9+
| `locate` | [`Locate`](locate.md) | Required | The symbol to analyze (source function or test case). |
10+
| `direction` | `"to_test" \| "to_source"` | `"to_test"` | Find tests for source, or find source for tests. |
11+
12+
## TestRelationResponse
13+
14+
| Field | Type | Description |
15+
| :-------------- | :------------------- | :--------------------------------------------- |
16+
| `symbol` | `SymbolInfo` | The resolved input symbol. |
17+
| `direction` | `string` | The direction used. |
18+
| `related_items` | `TestRelationItem[]` | List of found related items (tests or source). |
19+
20+
### TestRelationItem
21+
22+
| Field | Type | Description |
23+
| :---------- | :------- | :---------------------------------------------------------------------- |
24+
| `name` | `string` | Name of the related item (e.g., `test_add`). |
25+
| `kind` | `string` | Symbol kind (e.g., `function`, `class`). |
26+
| `file_path` | `string` | Path to the file. |
27+
| `range` | `Range` | Location in the file. |
28+
| `strategy` | `string` | How the relation was found: `reference`, `convention`, or `import`. |
29+
30+
## Implementation Guide
31+
32+
This API uses a multi-strategy approach to find relations, combining LSP capabilities with heuristic rules.
33+
34+
### Direction: `to_test` (Source -> Test)
35+
36+
1. **LSP References (Strongest Signal)**:
37+
* Call `textDocument/references` on the source symbol.
38+
* Filter results: Keep files located in `tests/` directories or matching pattern `*test*`/`*spec*`.
39+
* Mark as `strategy: reference`.
40+
2. **Naming Convention (Heuristic)**:
41+
* Infer potential test names (e.g., `add` -> `test_add`, `TestAdd`).
42+
* Use `workspace/symbol` to search for these names.
43+
* Mark as `strategy: convention`.
44+
3. **Module Imports (Broadest Signal)**:
45+
* Identify which test files import the source module.
46+
* Mark as `strategy: import`.
47+
48+
### Direction: `to_source` (Test -> Source)
49+
50+
1. **LSP Outgoing Calls (Strongest Signal)**:
51+
* Analyze the test function body.
52+
* Use `textDocument/definition` on symbols used within the test.
53+
* Filter results: Keep symbols defined in the project's source directory (exclude external libs).
54+
* Mark as `strategy: reference`.
55+
2. **Naming Convention (Heuristic)**:
56+
* Infer source name from test name (e.g., `test_add` -> `add`).
57+
* Use `workspace/symbol` to search.
58+
* Mark as `strategy: convention`.
59+
60+
## Example Usage
61+
62+
### Scenario 1: Finding tests for a modified function
63+
64+
#### Request
65+
66+
```json
67+
{
68+
"locate": {
69+
"file_path": "src/calculator.py",
70+
"scope": {
71+
"symbol_path": ["add"]
72+
}
73+
},
74+
"direction": "to_test"
75+
}
76+
```
77+
78+
#### Markdown Rendered for LLM
79+
80+
```markdown
81+
# Test Relation for `add` (to_test)
82+
83+
Found 2 related item(s):
84+
85+
- **test_add_basic** (`function`)
86+
- File: `tests/test_calculator.py`
87+
- Line: 15
88+
- Strategy: `convention`
89+
- **test_add_edge_cases** (`function`)
90+
- File: `tests/test_calculator.py`
91+
- Line: 28
92+
- Strategy: `reference`
93+
```
94+
95+
### Scenario 2: Finding source code for a failing test
96+
97+
#### Request
98+
99+
```json
100+
{
101+
"locate": {
102+
"file_path": "tests/test_auth.py",
103+
"scope": {
104+
"symbol_path": ["TestLogin", "test_invalid_password"]
105+
}
106+
},
107+
"direction": "to_source"
108+
}
109+
```
110+
111+
#### Markdown Rendered for LLM
112+
113+
```markdown
114+
# Test Relation for `test_invalid_password` (to_source)
115+
116+
Found 1 related item(s):
117+
118+
- **AuthService.login** (`method`)
119+
- File: `src/services/auth.py`
120+
- Line: 42
121+
- Strategy: `reference`
122+
```

python/src/lsap/locate.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
from lsprotocol.types import Range as LSPRange
1919

2020
from lsap.exception import NotFoundError
21-
from lsap.utils.content import DocumentReader
21+
from lsap.utils.document import DocumentReader
2222
from lsap.utils.symbol import iter_symbols
2323

2424
from .abc import Capability

python/src/lsap/reference.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
from lsprotocol.types import Position as LSPPosition
1818
from lsprotocol.types import Range as LSPRange
1919

20-
from lsap.utils.content import DocumentReader
20+
from lsap.utils.document import DocumentReader
2121
from lsap.utils.pagination import paginate
2222
from lsap.utils.symbol import symbol_at
2323

python/src/lsap/symbol.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313

1414
from lsap.abc import Capability
1515
from lsap.locate import LocateCapability
16-
from lsap.utils.content import DocumentReader
16+
from lsap.utils.document import DocumentReader
1717
from lsap.utils.symbol import symbol_at
1818

1919
from .symbol_outline import SymbolOutlineCapability

python/src/lsap/utils/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
from .cache import PaginationCache
2-
from .content import DocumentReader, Snippet
2+
from .document import DocumentReader, Snippet
33
from .pagination import paginate
44

55
__all__ = ["DocumentReader", "Snippet", "PaginationCache", "paginate"]

python/tests/test_content.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
from lsprotocol.types import Position, Range
22

3-
from lsap.utils.content import DocumentReader
3+
from lsap.utils.document import DocumentReader
44

55

66
def test_read_single_line():

src/lsap_schema/__init__.py

Lines changed: 0 additions & 91 deletions
Original file line numberDiff line numberDiff line change
@@ -7,60 +7,6 @@
77
DefinitionResponse,
88
)
99

10-
# Call hierarchy
11-
from .draft.call_hierarchy import (
12-
CallEdge,
13-
CallHierarchyItem,
14-
CallHierarchyNode,
15-
CallHierarchyRequest,
16-
CallHierarchyResponse,
17-
)
18-
19-
# Completion
20-
from .draft.completion import CompletionItem, CompletionRequest, CompletionResponse
21-
22-
# Diagnostics
23-
from .draft.diagnostics import (
24-
Diagnostic,
25-
FileDiagnosticsRequest,
26-
FileDiagnosticsResponse,
27-
WorkspaceDiagnosticItem,
28-
WorkspaceDiagnosticsRequest,
29-
WorkspaceDiagnosticsResponse,
30-
)
31-
32-
# Inlay hint
33-
from .draft.inlay_hint import (
34-
DecoratedContentResponse,
35-
InlayHintItem,
36-
InlayHintRequest,
37-
InlineValueItem,
38-
InlineValueRequest,
39-
)
40-
41-
# Rename
42-
from .draft.rename import (
43-
RenameDiff,
44-
RenameFileChange,
45-
RenameRequest,
46-
RenameResponse,
47-
)
48-
49-
# Relation
50-
from .draft.relation import (
51-
RelationRequest,
52-
RelationResponse,
53-
)
54-
55-
# Type hierarchy
56-
from .draft.type_hierarchy import (
57-
TypeEdge,
58-
TypeHierarchyItem,
59-
TypeHierarchyNode,
60-
TypeHierarchyRequest,
61-
TypeHierarchyResponse,
62-
)
63-
6410
# Hover
6511
from .hover import HoverRequest, HoverResponse
6612

@@ -113,35 +59,12 @@
11359
"PaginatedResponse",
11460
"Request",
11561
"Response",
116-
# Call hierarchy
117-
"CallEdge",
118-
"CallHierarchyItem",
119-
"CallHierarchyNode",
120-
"CallHierarchyRequest",
121-
"CallHierarchyResponse",
122-
# Completion
123-
"CompletionItem",
124-
"CompletionRequest",
125-
"CompletionResponse",
12662
# Definition
12763
"DefinitionRequest",
12864
"DefinitionResponse",
129-
# Diagnostics
130-
"Diagnostic",
131-
"FileDiagnosticsRequest",
132-
"FileDiagnosticsResponse",
133-
"WorkspaceDiagnosticItem",
134-
"WorkspaceDiagnosticsRequest",
135-
"WorkspaceDiagnosticsResponse",
13665
# Hover
13766
"HoverRequest",
13867
"HoverResponse",
139-
# Inlay hint
140-
"DecoratedContentResponse",
141-
"InlayHintItem",
142-
"InlayHintRequest",
143-
"InlineValueItem",
144-
"InlineValueRequest",
14568
# Locate
14669
"HERE",
14770
"LineScope",
@@ -163,26 +86,12 @@
16386
"ReferenceItem",
16487
"ReferenceRequest",
16588
"ReferenceResponse",
166-
# Rename
167-
"RenameDiff",
168-
"RenameFileChange",
169-
"RenameRequest",
170-
"RenameResponse",
171-
# Relation
172-
"RelationRequest",
173-
"RelationResponse",
17489
# Symbol
17590
"SymbolRequest",
17691
"SymbolResponse",
17792
# Symbol outline
17893
"SymbolOutlineRequest",
17994
"SymbolOutlineResponse",
180-
# Type hierarchy
181-
"TypeEdge",
182-
"TypeHierarchyItem",
183-
"TypeHierarchyNode",
184-
"TypeHierarchyRequest",
185-
"TypeHierarchyResponse",
18695
# Workspace
18796
"WorkspaceSymbolItem",
18897
"WorkspaceSymbolRequest",

0 commit comments

Comments
 (0)