feat(lsp): add missing LspCapability enum values
CI / build (pull_request) Successful in 18s
CI / security (pull_request) Successful in 58s
CI / lint (pull_request) Successful in 3m19s
CI / typecheck (pull_request) Successful in 3m54s
CI / quality (pull_request) Successful in 3m55s
CI / integration_tests (pull_request) Successful in 6m49s
CI / unit_tests (pull_request) Successful in 7m3s
CI / docker (pull_request) Successful in 1m6s
CI / e2e_tests (pull_request) Successful in 11m45s
CI / benchmark-publish (pull_request) Has been skipped
CI / coverage (pull_request) Successful in 12m52s
CI / status-check (pull_request) Successful in 2s
CI / benchmark-regression (pull_request) Successful in 58m46s
CI / build (pull_request) Successful in 18s
CI / security (pull_request) Successful in 58s
CI / lint (pull_request) Successful in 3m19s
CI / typecheck (pull_request) Successful in 3m54s
CI / quality (pull_request) Successful in 3m55s
CI / integration_tests (pull_request) Successful in 6m49s
CI / unit_tests (pull_request) Successful in 7m3s
CI / docker (pull_request) Successful in 1m6s
CI / e2e_tests (pull_request) Successful in 11m45s
CI / benchmark-publish (pull_request) Has been skipped
CI / coverage (pull_request) Successful in 12m52s
CI / status-check (pull_request) Successful in 2s
CI / benchmark-regression (pull_request) Successful in 58m46s
Align LspCapability enum with the spec's 11-capability set
(docs/specification.md lines 20705-20717):
Renamed: TYPE_INFO -> HOVER, SYMBOLS -> DOCUMENT_SYMBOLS,
FORMAT -> FORMATTING
Added: DEFINITIONS, SIGNATURE_HELP, WORKSPACE_SYMBOLS
Updated LspToolAdapter._CAPABILITY_TOOL_MAP (11 entries):
- code_actions suffix -> code-actions (hyphen per spec)
- RENAME gets dedicated schema with new_name parameter
- workspace_symbols gets query-based schema
Updated _input_schema_for() with 4 schema categories extracted
to module-level constants: file-only, position-based, rename
(with new_name), and query-based. Added defensive ValueError for
unmapped capabilities. Added additionalProperties: false.
Extended LspClient.initialize() to advertise all 11 capabilities
in the initialize request (hover, definition, references, rename,
codeAction, formatting, signatureHelp, documentSymbol, workspace
symbol).
Fixed _make_runtime_handler() to handle workspace_symbols as
query-only input (no file_path required), resolving the
schema/handler contract mismatch.
Fixed stale references: type_info -> hover, symbols ->
document_symbols in test fixtures, feature files, CLI docstring.
Updated docs/reference/lsp.md and CHANGELOG.md.
Behave tests (38 scenarios): enum completeness, tool spec generation
with structural validation, input schema per category, all 11
stubbed provider keys, negative tests for invalid capability and
defensive ValueError branch.
ISSUES CLOSED: #834
This commit is contained in:
+9
-2
@@ -2,7 +2,6 @@
|
||||
|
||||
## Unreleased
|
||||
|
||||
<<<<<<< HEAD
|
||||
- Added TDD bug-capture tests for bug #1076 — `use_action()` does not
|
||||
propagate `automation_profile` to Plan. Three Behave BDD scenarios
|
||||
(`@tdd_bug @tdd_bug_1076 @tdd_expected_fail`) verify the full precedence
|
||||
@@ -23,7 +22,7 @@
|
||||
ValueError with a distinctive message and asserts the message appears in the
|
||||
structlog warning log — which currently fails, confirming the bug. The
|
||||
`@tdd_expected_fail` tag inverts this to a CI pass until the fix is merged.
|
||||
(#1093)
|
||||
(#1093)
|
||||
- Added ResourceHandler sandbox and checkpoint lifecycle methods:
|
||||
`create_sandbox` (idempotent, delegates to SandboxManager),
|
||||
`create_checkpoint`, `rollback_to`, and `project_access`. Frozen
|
||||
@@ -33,6 +32,14 @@
|
||||
FsDirectoryHandler uses `shutil.copytree` snapshot and clear-and-restore.
|
||||
Default `project_access` delegates to PermissionService (local mode =
|
||||
always permit). (#836)
|
||||
- Added 5 missing LSP capabilities to `LspCapability` enum: `HOVER`,
|
||||
`DEFINITIONS`, `SIGNATURE_HELP`, `DOCUMENT_SYMBOLS`, `WORKSPACE_SYMBOLS`.
|
||||
Renamed `TYPE_INFO` -> `HOVER`, `SYMBOLS` -> `DOCUMENT_SYMBOLS`,
|
||||
`FORMAT` -> `FORMATTING` for spec alignment. Updated tool adapter with
|
||||
11 capability mappings, RENAME schema with `new_name` parameter,
|
||||
workspace-symbols query schema, and defensive schema validation.
|
||||
Extended `initialize()` to advertise all 11 capabilities. Fixed
|
||||
`workspace_symbols` runtime handler to accept query-only input. (#834)
|
||||
- Added TDD bug-capture E2E tests for bug #1028 — ACMS indexing pipeline not
|
||||
wired into CLI. Four Robot Framework E2E tests prove ContextTierService starts
|
||||
empty on every CLI invocation. Tests use ``@tdd_expected_fail`` until the bug
|
||||
|
||||
+15
-11
@@ -20,16 +20,19 @@ The registry is available for configuration and validation purposes even in loca
|
||||
|
||||
Enum of LSP features that can be exposed as tools:
|
||||
|
||||
| Value | Description |
|
||||
|----------------|------------------------------------------|
|
||||
| `DIAGNOSTICS` | Retrieve diagnostics (errors/warnings) |
|
||||
| `TYPE_INFO` | Get type information at a cursor position|
|
||||
| `SYMBOLS` | List document and workspace symbols |
|
||||
| `COMPLETIONS` | Get code completion suggestions |
|
||||
| `REFERENCES` | Find all references to a symbol |
|
||||
| `RENAME` | Rename a symbol across the workspace |
|
||||
| `CODE_ACTIONS` | Retrieve available code actions |
|
||||
| `FORMAT` | Format a document or selection |
|
||||
| Value | Description |
|
||||
|--------------------|------------------------------------------------|
|
||||
| `DIAGNOSTICS` | Retrieve diagnostics (errors/warnings) |
|
||||
| `HOVER` | Get type information and documentation |
|
||||
| `COMPLETIONS` | Get code completion suggestions |
|
||||
| `DEFINITIONS` | Go to the definition of a symbol |
|
||||
| `REFERENCES` | Find all references to a symbol |
|
||||
| `RENAME` | Rename a symbol across the workspace |
|
||||
| `CODE_ACTIONS` | Retrieve available code actions for a range |
|
||||
| `FORMATTING` | Format a document or selection |
|
||||
| `SIGNATURE_HELP` | Get function signature information at a call |
|
||||
| `DOCUMENT_SYMBOLS` | List all symbols in a file |
|
||||
| `WORKSPACE_SYMBOLS`| Search for symbols across the workspace |
|
||||
|
||||
### LspServerConfig
|
||||
|
||||
@@ -101,7 +104,8 @@ languages: ["python"]
|
||||
capabilities:
|
||||
- diagnostics
|
||||
- completions
|
||||
- type_info
|
||||
- hover
|
||||
- definitions
|
||||
- references
|
||||
- rename
|
||||
env:
|
||||
|
||||
@@ -1228,7 +1228,7 @@ Feature: Consolidated Misc
|
||||
Scenario: Register LSP server with capabilities
|
||||
Given a clean LSP registry
|
||||
Given an LSP server config named "local/pyright" for language "python" with command "pyright-langserver"
|
||||
And the LSP server config has capabilities "diagnostics,completions,type_info"
|
||||
And the LSP server config has capabilities "diagnostics,completions,hover"
|
||||
When the LSP server is registered
|
||||
Then the LSP server "local/pyright" should have 3 capabilities
|
||||
|
||||
|
||||
@@ -0,0 +1,122 @@
|
||||
@lsp @capability
|
||||
Feature: LspCapability Enum Completeness
|
||||
As a developer integrating LSP servers
|
||||
I want the LspCapability enum to include all spec-defined capabilities
|
||||
So that the tool adapter generates correct tool specs
|
||||
|
||||
# ── Enum completeness ──────────────────────────────────────
|
||||
|
||||
Scenario: LspCapability enum has exactly 11 members
|
||||
Given the LspCapability enum for lsp_cap
|
||||
Then the lsp_cap enum should have 11 members
|
||||
|
||||
Scenario Outline: LspCapability enum includes <member>
|
||||
Given the LspCapability enum for lsp_cap
|
||||
Then the lsp_cap enum should include "<member>"
|
||||
|
||||
Examples:
|
||||
| member |
|
||||
| diagnostics |
|
||||
| hover |
|
||||
| completions |
|
||||
| definitions |
|
||||
| references |
|
||||
| rename |
|
||||
| code_actions |
|
||||
| formatting |
|
||||
| signature_help |
|
||||
| document_symbols |
|
||||
| workspace_symbols |
|
||||
|
||||
# ── Tool adapter generates specs for all capabilities ──────
|
||||
|
||||
Scenario: Tool adapter has mappings for all 11 capabilities
|
||||
Given the lsp_cap capability tool map
|
||||
Then the lsp_cap tool map should have 11 entries
|
||||
|
||||
Scenario Outline: Tool adapter generates tool spec for <capability>
|
||||
Given an LspServerConfig with capability "<capability>" for lsp_cap
|
||||
When I generate tool specs for lsp_cap
|
||||
Then the lsp_cap generated specs should not be empty
|
||||
And the lsp_cap tool spec should have keys "name" and "input_schema"
|
||||
And the lsp_cap tool name should end with "<tool_suffix>"
|
||||
|
||||
Examples:
|
||||
| capability | tool_suffix |
|
||||
| diagnostics | diagnostics |
|
||||
| hover | hover |
|
||||
| completions | completions |
|
||||
| definitions | definition |
|
||||
| references | references |
|
||||
| rename | rename |
|
||||
| code_actions | code-actions |
|
||||
| formatting | format |
|
||||
| signature_help | signature |
|
||||
| document_symbols | symbols |
|
||||
| workspace_symbols | workspace-symbols |
|
||||
|
||||
# ── Input schemas per category ─────────────────────────────
|
||||
|
||||
Scenario Outline: File-only capability <cap> has file_path schema
|
||||
Given the lsp_cap input schema for "<cap>"
|
||||
Then the lsp_cap schema should require "file_path"
|
||||
And the lsp_cap schema should not require "line"
|
||||
|
||||
Examples:
|
||||
| cap |
|
||||
| diagnostics |
|
||||
| formatting |
|
||||
| document_symbols |
|
||||
|
||||
Scenario Outline: Position capability <cap> has file+line+column schema
|
||||
Given the lsp_cap input schema for "<cap>"
|
||||
Then the lsp_cap schema should require "file_path"
|
||||
And the lsp_cap schema should require "line"
|
||||
And the lsp_cap schema should require "column"
|
||||
|
||||
Examples:
|
||||
| cap |
|
||||
| hover |
|
||||
| completions |
|
||||
| definitions |
|
||||
| references |
|
||||
| code_actions |
|
||||
| signature_help |
|
||||
|
||||
Scenario: Rename capability has file+line+column+new_name schema
|
||||
Given the lsp_cap input schema for "rename"
|
||||
Then the lsp_cap schema should require "file_path"
|
||||
And the lsp_cap schema should require "line"
|
||||
And the lsp_cap schema should require "column"
|
||||
And the lsp_cap schema should require "new_name"
|
||||
|
||||
Scenario: Workspace symbols has query schema
|
||||
Given the lsp_cap input schema for "workspace_symbols"
|
||||
Then the lsp_cap schema should require "query"
|
||||
And the lsp_cap schema should not require "file_path"
|
||||
|
||||
# ── Stubbed server capabilities include all keys ───────────
|
||||
|
||||
Scenario: Stubbed server capabilities include all LSP providers
|
||||
Given the lsp_cap stubbed server capabilities
|
||||
Then the lsp_cap stubbed caps should include "hoverProvider"
|
||||
And the lsp_cap stubbed caps should include "definitionProvider"
|
||||
And the lsp_cap stubbed caps should include "signatureHelpProvider"
|
||||
And the lsp_cap stubbed caps should include "documentSymbolProvider"
|
||||
And the lsp_cap stubbed caps should include "workspaceSymbolProvider"
|
||||
And the lsp_cap stubbed caps should include "completionProvider"
|
||||
And the lsp_cap stubbed caps should include "referencesProvider"
|
||||
And the lsp_cap stubbed caps should include "renameProvider"
|
||||
And the lsp_cap stubbed caps should include "codeActionProvider"
|
||||
And the lsp_cap stubbed caps should include "documentFormattingProvider"
|
||||
And the lsp_cap stubbed caps should include "diagnosticProvider"
|
||||
|
||||
# ── Negative tests ─────────────────────────────────────────
|
||||
|
||||
Scenario: Invalid capability value raises ValueError
|
||||
When I create an LspCapability with value "nonexistent" for lsp_cap
|
||||
Then the lsp_cap creation should raise a ValueError
|
||||
|
||||
Scenario: _input_schema_for raises ValueError for unknown capability
|
||||
When I call _input_schema_for with a fake capability for lsp_cap
|
||||
Then the lsp_cap schema call should raise a ValueError
|
||||
@@ -0,0 +1,184 @@
|
||||
"""Step definitions for lsp_capability_enum.feature."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from behave import given, then, when
|
||||
|
||||
from cleveragents.lsp.models import LspCapability, LspServerConfig
|
||||
from cleveragents.lsp.tool_adapter import (
|
||||
_CAPABILITY_TOOL_MAP,
|
||||
LspToolAdapter,
|
||||
_input_schema_for,
|
||||
)
|
||||
|
||||
# ── Given steps ──────────────────────────────────────────────
|
||||
|
||||
|
||||
@given("the LspCapability enum for lsp_cap")
|
||||
def step_load_enum_lsp_cap(context: object) -> None:
|
||||
"""Load the LspCapability enum."""
|
||||
context.lsp_cap_enum = LspCapability # type: ignore[attr-defined]
|
||||
|
||||
|
||||
@given("the lsp_cap capability tool map")
|
||||
def step_load_tool_map_lsp_cap(context: object) -> None:
|
||||
"""Load the capability tool map."""
|
||||
context.lsp_cap_tool_map = _CAPABILITY_TOOL_MAP # type: ignore[attr-defined]
|
||||
|
||||
|
||||
@given('an LspServerConfig with capability "{capability}" for lsp_cap')
|
||||
def step_config_with_capability_lsp_cap(
|
||||
context: object,
|
||||
capability: str,
|
||||
) -> None:
|
||||
"""Create an LspServerConfig with a single capability."""
|
||||
cap = LspCapability(capability)
|
||||
context.lsp_cap_config = LspServerConfig( # type: ignore[attr-defined]
|
||||
name="test/server",
|
||||
languages=["python"],
|
||||
command="test-server",
|
||||
capabilities=[cap],
|
||||
)
|
||||
|
||||
|
||||
@given('the lsp_cap input schema for "{capability}"')
|
||||
def step_input_schema_lsp_cap(context: object, capability: str) -> None:
|
||||
"""Get the input schema for a capability."""
|
||||
cap = LspCapability(capability)
|
||||
context.lsp_cap_schema = _input_schema_for(cap) # type: ignore[attr-defined]
|
||||
|
||||
|
||||
@given("the lsp_cap stubbed server capabilities")
|
||||
def step_stubbed_caps_lsp_cap(context: object) -> None:
|
||||
"""Load the stubbed server capabilities."""
|
||||
from cleveragents.lsp.server import _STUBBED_CAPABILITIES
|
||||
|
||||
context.lsp_cap_stubbed = _STUBBED_CAPABILITIES # type: ignore[attr-defined]
|
||||
|
||||
|
||||
# ── When steps ───────────────────────────────────────────────
|
||||
|
||||
|
||||
@when("I generate tool specs for lsp_cap")
|
||||
def step_generate_specs_lsp_cap(context: object) -> None:
|
||||
"""Generate tool specs from the config."""
|
||||
config = context.lsp_cap_config # type: ignore[attr-defined]
|
||||
adapter = LspToolAdapter()
|
||||
context.lsp_cap_specs = adapter.generate_tool_specs(config) # type: ignore[attr-defined]
|
||||
|
||||
|
||||
# ── Then steps ───────────────────────────────────────────────
|
||||
|
||||
|
||||
@then("the lsp_cap enum should have {count:d} members")
|
||||
def step_enum_count_lsp_cap(context: object, count: int) -> None:
|
||||
"""Assert the enum has the expected number of members."""
|
||||
members = list(context.lsp_cap_enum) # type: ignore[attr-defined]
|
||||
assert len(members) == count, (
|
||||
f"Expected {count} members, got {len(members)}: {[m.value for m in members]}"
|
||||
)
|
||||
|
||||
|
||||
@then('the lsp_cap enum should include "{member}"')
|
||||
def step_enum_includes_lsp_cap(context: object, member: str) -> None:
|
||||
"""Assert the enum includes the given member value."""
|
||||
values = [m.value for m in context.lsp_cap_enum] # type: ignore[attr-defined]
|
||||
assert member in values, f"'{member}' not in enum values: {values}"
|
||||
|
||||
|
||||
@then("the lsp_cap tool map should have {count:d} entries")
|
||||
def step_tool_map_count_lsp_cap(context: object, count: int) -> None:
|
||||
"""Assert the tool map has the expected number of entries."""
|
||||
tool_map = context.lsp_cap_tool_map # type: ignore[attr-defined]
|
||||
assert len(tool_map) == count, f"Expected {count} entries, got {len(tool_map)}"
|
||||
|
||||
|
||||
@then("the lsp_cap generated specs should not be empty")
|
||||
def step_specs_not_empty_lsp_cap(context: object) -> None:
|
||||
"""Assert generated specs are not empty."""
|
||||
specs = context.lsp_cap_specs # type: ignore[attr-defined]
|
||||
assert len(specs) > 0, "Expected non-empty tool specs"
|
||||
|
||||
|
||||
@then('the lsp_cap tool spec should have keys "name" and "input_schema"')
|
||||
def step_spec_has_keys_lsp_cap(context: object) -> None:
|
||||
"""Assert generated specs have required keys."""
|
||||
specs = context.lsp_cap_specs # type: ignore[attr-defined]
|
||||
for spec in specs:
|
||||
assert "name" in spec, f"Spec missing 'name': {spec}"
|
||||
assert "input_schema" in spec, f"Spec missing 'input_schema': {spec}"
|
||||
|
||||
|
||||
@then('the lsp_cap tool name should end with "{suffix}"')
|
||||
def step_tool_name_ends_with_lsp_cap(context: object, suffix: str) -> None:
|
||||
"""Assert at least one tool name ends with the suffix."""
|
||||
specs = context.lsp_cap_specs # type: ignore[attr-defined]
|
||||
names = [s["name"] for s in specs]
|
||||
assert any(n.endswith(f"/{suffix}") for n in names), (
|
||||
f"No tool name ends with '/{suffix}': {names}"
|
||||
)
|
||||
|
||||
|
||||
@then('the lsp_cap schema should require "{field}"')
|
||||
def step_schema_requires_lsp_cap(context: object, field: str) -> None:
|
||||
"""Assert the schema requires the given field."""
|
||||
schema = context.lsp_cap_schema # type: ignore[attr-defined]
|
||||
required = schema.get("required", [])
|
||||
assert field in required, f"'{field}' not in required: {required}"
|
||||
|
||||
|
||||
@then('the lsp_cap schema should not require "{field}"')
|
||||
def step_schema_not_requires_lsp_cap(context: object, field: str) -> None:
|
||||
"""Assert the schema does not require the given field."""
|
||||
schema = context.lsp_cap_schema # type: ignore[attr-defined]
|
||||
required = schema.get("required", [])
|
||||
assert field not in required, f"'{field}' should not be in required: {required}"
|
||||
|
||||
|
||||
@then('the lsp_cap stubbed caps should include "{key}"')
|
||||
def step_stubbed_includes_lsp_cap(context: object, key: str) -> None:
|
||||
"""Assert the stubbed capabilities dict includes the key."""
|
||||
caps = context.lsp_cap_stubbed # type: ignore[attr-defined]
|
||||
assert key in caps, f"'{key}' not in stubbed capabilities: {list(caps)}"
|
||||
|
||||
|
||||
# ── When/Then steps: negative tests ─────────────────────────
|
||||
|
||||
|
||||
@when('I create an LspCapability with value "{value}" for lsp_cap')
|
||||
def step_create_invalid_capability_lsp_cap(context: object, value: str) -> None:
|
||||
"""Attempt to create an LspCapability with an invalid value."""
|
||||
context.lsp_cap_error = None # type: ignore[attr-defined]
|
||||
try:
|
||||
LspCapability(value)
|
||||
except ValueError as exc:
|
||||
context.lsp_cap_error = exc # type: ignore[attr-defined]
|
||||
|
||||
|
||||
@then("the lsp_cap creation should raise a ValueError")
|
||||
def step_should_raise_value_error_lsp_cap(context: object) -> None:
|
||||
"""Assert that a ValueError was raised."""
|
||||
err = context.lsp_cap_error # type: ignore[attr-defined]
|
||||
assert err is not None, "Expected ValueError but none was raised"
|
||||
|
||||
|
||||
@when("I call _input_schema_for with a fake capability for lsp_cap")
|
||||
def step_call_schema_fake_cap_lsp_cap(context: object) -> None:
|
||||
"""Call _input_schema_for with a value not in any category tuple."""
|
||||
from enum import StrEnum
|
||||
|
||||
class _FakeCap(StrEnum):
|
||||
FAKE = "fake_capability"
|
||||
|
||||
context.lsp_cap_schema_error = None # type: ignore[attr-defined]
|
||||
try:
|
||||
_input_schema_for(_FakeCap.FAKE) # type: ignore[arg-type]
|
||||
except ValueError as exc:
|
||||
context.lsp_cap_schema_error = exc # type: ignore[attr-defined]
|
||||
|
||||
|
||||
@then("the lsp_cap schema call should raise a ValueError")
|
||||
def step_schema_should_raise_lsp_cap(context: object) -> None:
|
||||
"""Assert _input_schema_for raised ValueError."""
|
||||
err = context.lsp_cap_schema_error # type: ignore[attr-defined]
|
||||
assert err is not None, "Expected ValueError from _input_schema_for but none raised"
|
||||
@@ -38,7 +38,7 @@ languages:
|
||||
capabilities:
|
||||
- diagnostics
|
||||
- completions
|
||||
- type_info
|
||||
- hover
|
||||
"""
|
||||
|
||||
_VALID_YAML_2 = """\
|
||||
@@ -51,7 +51,7 @@ languages:
|
||||
- cpp
|
||||
capabilities:
|
||||
- diagnostics
|
||||
- symbols
|
||||
- document_symbols
|
||||
"""
|
||||
|
||||
_VALID_YAML_ENV = """\
|
||||
|
||||
@@ -27,7 +27,7 @@ name: local/pyright
|
||||
command: pyright-langserver
|
||||
args: ["--stdio"]
|
||||
languages: ["python"]
|
||||
capabilities: ["diagnostics", "completions", "type_info"]
|
||||
capabilities: ["diagnostics", "completions", "hover"]
|
||||
```
|
||||
|
||||
## Error Handling
|
||||
|
||||
@@ -232,6 +232,32 @@ class LspClient:
|
||||
"completion": {
|
||||
"completionItem": {"snippetSupport": False},
|
||||
},
|
||||
"hover": {"contentFormat": ["plaintext", "markdown"]},
|
||||
"definition": {"linkSupport": False},
|
||||
"references": {},
|
||||
"rename": {"prepareSupport": False},
|
||||
"codeAction": {
|
||||
"codeActionLiteralSupport": {
|
||||
"codeActionKind": {
|
||||
"valueSet": [
|
||||
"quickfix",
|
||||
"refactor",
|
||||
"source",
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
"formatting": {},
|
||||
"signatureHelp": {
|
||||
"signatureInformation": {
|
||||
"parameterInformation": {
|
||||
"labelOffsetSupport": True,
|
||||
},
|
||||
},
|
||||
},
|
||||
"documentSymbol": {
|
||||
"hierarchicalDocumentSymbolSupport": True,
|
||||
},
|
||||
"synchronization": {
|
||||
"didSave": True,
|
||||
"willSave": False,
|
||||
@@ -239,6 +265,7 @@ class LspClient:
|
||||
},
|
||||
"workspace": {
|
||||
"workspaceFolders": True,
|
||||
"symbol": {"symbolKind": {}},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
@@ -27,13 +27,16 @@ class LspCapability(StrEnum):
|
||||
"""
|
||||
|
||||
DIAGNOSTICS = "diagnostics"
|
||||
TYPE_INFO = "type_info"
|
||||
SYMBOLS = "symbols"
|
||||
HOVER = "hover"
|
||||
COMPLETIONS = "completions"
|
||||
DEFINITIONS = "definitions"
|
||||
REFERENCES = "references"
|
||||
RENAME = "rename"
|
||||
CODE_ACTIONS = "code_actions"
|
||||
FORMAT = "format"
|
||||
FORMATTING = "formatting"
|
||||
SIGNATURE_HELP = "signature_help"
|
||||
DOCUMENT_SYMBOLS = "document_symbols"
|
||||
WORKSPACE_SYMBOLS = "workspace_symbols"
|
||||
|
||||
|
||||
class LspServerConfig(BaseModel):
|
||||
|
||||
@@ -35,18 +35,18 @@ _CAPABILITY_TOOL_MAP: dict[LspCapability, tuple[str, str]] = {
|
||||
"diagnostics",
|
||||
"Retrieve diagnostics (errors/warnings) for a file",
|
||||
),
|
||||
LspCapability.TYPE_INFO: (
|
||||
"type_info",
|
||||
"Get type information at a cursor position",
|
||||
),
|
||||
LspCapability.SYMBOLS: (
|
||||
"symbols",
|
||||
"List document and workspace symbols",
|
||||
LspCapability.HOVER: (
|
||||
"hover",
|
||||
"Get type information and documentation at a position",
|
||||
),
|
||||
LspCapability.COMPLETIONS: (
|
||||
"completions",
|
||||
"Get code completion suggestions at a position",
|
||||
),
|
||||
LspCapability.DEFINITIONS: (
|
||||
"definition",
|
||||
"Go to the definition of a symbol",
|
||||
),
|
||||
LspCapability.REFERENCES: (
|
||||
"references",
|
||||
"Find all references to a symbol",
|
||||
@@ -56,13 +56,25 @@ _CAPABILITY_TOOL_MAP: dict[LspCapability, tuple[str, str]] = {
|
||||
"Rename a symbol across the workspace",
|
||||
),
|
||||
LspCapability.CODE_ACTIONS: (
|
||||
"code_actions",
|
||||
"code-actions",
|
||||
"Retrieve available code actions for a range",
|
||||
),
|
||||
LspCapability.FORMAT: (
|
||||
LspCapability.FORMATTING: (
|
||||
"format",
|
||||
"Format a document or selection",
|
||||
),
|
||||
LspCapability.SIGNATURE_HELP: (
|
||||
"signature",
|
||||
"Get function signature information at a call site",
|
||||
),
|
||||
LspCapability.DOCUMENT_SYMBOLS: (
|
||||
"symbols",
|
||||
"List all symbols in a file",
|
||||
),
|
||||
LspCapability.WORKSPACE_SYMBOLS: (
|
||||
"workspace-symbols",
|
||||
"Search for symbols across the entire workspace",
|
||||
),
|
||||
}
|
||||
|
||||
|
||||
@@ -109,6 +121,17 @@ def _make_runtime_handler(
|
||||
"""
|
||||
|
||||
def _handler(**kwargs: Any) -> Any:
|
||||
# workspace_symbols is query-based, not file-based
|
||||
if capability == LspCapability.WORKSPACE_SYMBOLS:
|
||||
query: str = kwargs.get("query", "")
|
||||
if not query:
|
||||
return {"error": "query is required"}
|
||||
raise LspNotAvailableError(
|
||||
f"LSP capability '{capability.value}' is not yet implemented "
|
||||
f"for server '{server_name}'",
|
||||
details={"server": server_name, "capability": capability.value},
|
||||
)
|
||||
|
||||
file_path: str = kwargs.get("file_path", "")
|
||||
if not file_path:
|
||||
return {"error": "file_path is required"}
|
||||
@@ -208,6 +231,24 @@ class LspToolAdapter:
|
||||
return specs
|
||||
|
||||
|
||||
# Schema category constants (hoisted for zero-alloc lookup).
|
||||
_FILE_ONLY_CAPABILITIES = (
|
||||
LspCapability.DIAGNOSTICS,
|
||||
LspCapability.FORMATTING,
|
||||
LspCapability.DOCUMENT_SYMBOLS,
|
||||
)
|
||||
_POSITION_BASED_CAPABILITIES = (
|
||||
LspCapability.HOVER,
|
||||
LspCapability.COMPLETIONS,
|
||||
LspCapability.DEFINITIONS,
|
||||
LspCapability.REFERENCES,
|
||||
LspCapability.CODE_ACTIONS,
|
||||
LspCapability.SIGNATURE_HELP,
|
||||
)
|
||||
_RENAME_CAPABILITY = (LspCapability.RENAME,)
|
||||
_QUERY_BASED_CAPABILITIES = (LspCapability.WORKSPACE_SYMBOLS,)
|
||||
|
||||
|
||||
def _input_schema_for(capability: LspCapability) -> dict[str, Any]:
|
||||
"""Return a minimal JSON Schema for the given capability.
|
||||
|
||||
@@ -216,30 +257,70 @@ def _input_schema_for(capability: LspCapability) -> dict[str, Any]:
|
||||
|
||||
Returns:
|
||||
A JSON Schema dict.
|
||||
|
||||
Raises:
|
||||
ValueError: If no schema is defined for the capability.
|
||||
"""
|
||||
base: dict[str, Any] = {"type": "object", "properties": {}}
|
||||
if capability in (
|
||||
LspCapability.DIAGNOSTICS,
|
||||
LspCapability.FORMAT,
|
||||
LspCapability.SYMBOLS,
|
||||
):
|
||||
base: dict[str, Any] = {
|
||||
"type": "object",
|
||||
"properties": {},
|
||||
"additionalProperties": False,
|
||||
}
|
||||
|
||||
if capability in _FILE_ONLY_CAPABILITIES:
|
||||
base["properties"] = {
|
||||
"file_path": {"type": "string", "description": "Path to the file"},
|
||||
"file_path": {
|
||||
"type": "string",
|
||||
"description": "Path to the file",
|
||||
},
|
||||
}
|
||||
base["required"] = ["file_path"]
|
||||
elif capability in (
|
||||
LspCapability.COMPLETIONS,
|
||||
LspCapability.TYPE_INFO,
|
||||
LspCapability.REFERENCES,
|
||||
LspCapability.RENAME,
|
||||
LspCapability.CODE_ACTIONS,
|
||||
):
|
||||
elif capability in _POSITION_BASED_CAPABILITIES:
|
||||
base["properties"] = {
|
||||
"file_path": {"type": "string", "description": "Path to the file"},
|
||||
"line": {"type": "integer", "description": "1-based line number"},
|
||||
"column": {"type": "integer", "description": "1-based column number"},
|
||||
"file_path": {
|
||||
"type": "string",
|
||||
"description": "Path to the file",
|
||||
},
|
||||
"line": {
|
||||
"type": "integer",
|
||||
"description": "1-based line number",
|
||||
},
|
||||
"column": {
|
||||
"type": "integer",
|
||||
"description": "1-based column number",
|
||||
},
|
||||
}
|
||||
base["required"] = ["file_path", "line", "column"]
|
||||
elif capability in _RENAME_CAPABILITY:
|
||||
base["properties"] = {
|
||||
"file_path": {
|
||||
"type": "string",
|
||||
"description": "Path to the file",
|
||||
},
|
||||
"line": {
|
||||
"type": "integer",
|
||||
"description": "1-based line number",
|
||||
},
|
||||
"column": {
|
||||
"type": "integer",
|
||||
"description": "1-based column number",
|
||||
},
|
||||
"new_name": {
|
||||
"type": "string",
|
||||
"description": "New name for the symbol",
|
||||
},
|
||||
}
|
||||
base["required"] = ["file_path", "line", "column", "new_name"]
|
||||
elif capability in _QUERY_BASED_CAPABILITIES:
|
||||
base["properties"] = {
|
||||
"query": {
|
||||
"type": "string",
|
||||
"description": "Symbol search query",
|
||||
},
|
||||
}
|
||||
base["required"] = ["query"]
|
||||
else:
|
||||
raise ValueError(f"No input schema defined for capability: {capability}")
|
||||
return base
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user