mirror of
https://github.com/catlog22/Claude-Code-Workflow.git
synced 2026-03-05 16:13:08 +08:00
codex-lens: ship default LSP config and fix entrypoint
This commit is contained in:
@@ -14,6 +14,7 @@ authors = [
|
||||
]
|
||||
dependencies = [
|
||||
"typer~=0.9.0",
|
||||
"click>=8.0.0,<9",
|
||||
"rich~=13.0.0",
|
||||
"pydantic~=2.0.0",
|
||||
"tree-sitter~=0.20.0",
|
||||
@@ -31,7 +32,7 @@ dependencies = [
|
||||
# Semantic search using fastembed (ONNX-based, lightweight ~200MB)
|
||||
semantic = [
|
||||
"numpy~=1.26.0",
|
||||
"fastembed~=0.2.0",
|
||||
"fastembed~=0.2.1",
|
||||
"hnswlib~=0.8.0",
|
||||
]
|
||||
|
||||
@@ -39,7 +40,7 @@ semantic = [
|
||||
# Install with: pip install codexlens[semantic-gpu]
|
||||
semantic-gpu = [
|
||||
"numpy~=1.26.0",
|
||||
"fastembed~=0.2.0",
|
||||
"fastembed~=0.2.1",
|
||||
"hnswlib~=0.8.0",
|
||||
"onnxruntime-gpu~=1.15.0", # CUDA support
|
||||
]
|
||||
@@ -48,7 +49,7 @@ semantic-gpu = [
|
||||
# Install with: pip install codexlens[semantic-directml]
|
||||
semantic-directml = [
|
||||
"numpy~=1.26.0",
|
||||
"fastembed~=0.2.0",
|
||||
"fastembed~=0.2.1",
|
||||
"hnswlib~=0.8.0",
|
||||
"onnxruntime-directml~=1.15.0", # DirectML support
|
||||
]
|
||||
@@ -105,10 +106,13 @@ lsp = [
|
||||
]
|
||||
|
||||
[project.scripts]
|
||||
codexlens-lsp = "codexlens.lsp:main"
|
||||
codexlens-lsp = "codexlens.lsp.server:main"
|
||||
|
||||
[project.urls]
|
||||
Homepage = "https://github.com/openai/codex-lens"
|
||||
|
||||
[tool.setuptools]
|
||||
package-dir = { "" = "src" }
|
||||
|
||||
[tool.setuptools.package-data]
|
||||
"codexlens.lsp" = ["lsp-servers.json"]
|
||||
|
||||
88
codex-lens/src/codexlens/lsp/lsp-servers.json
Normal file
88
codex-lens/src/codexlens/lsp/lsp-servers.json
Normal file
@@ -0,0 +1,88 @@
|
||||
{
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"version": "1.0.0",
|
||||
"description": "Default language server configuration for codex-lens standalone LSP client",
|
||||
"servers": [
|
||||
{
|
||||
"languageId": "python",
|
||||
"displayName": "Pyright",
|
||||
"extensions": ["py", "pyi"],
|
||||
"command": ["pyright-langserver", "--stdio"],
|
||||
"enabled": true,
|
||||
"initializationOptions": {
|
||||
"pythonPath": "",
|
||||
"pythonPlatform": "",
|
||||
"pythonVersion": "3.13"
|
||||
},
|
||||
"settings": {
|
||||
"python.analysis": {
|
||||
"typeCheckingMode": "standard",
|
||||
"diagnosticMode": "workspace",
|
||||
"exclude": ["**/node_modules", "**/__pycache__", "build", "dist"],
|
||||
"include": ["src/**", "tests/**"],
|
||||
"stubPath": "typings"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"languageId": "typescript",
|
||||
"displayName": "TypeScript Language Server",
|
||||
"extensions": ["ts", "tsx"],
|
||||
"command": ["typescript-language-server", "--stdio"],
|
||||
"enabled": true,
|
||||
"initializationOptions": {},
|
||||
"settings": {}
|
||||
},
|
||||
{
|
||||
"languageId": "javascript",
|
||||
"displayName": "TypeScript Language Server (for JS)",
|
||||
"extensions": ["js", "jsx", "mjs", "cjs"],
|
||||
"command": ["typescript-language-server", "--stdio"],
|
||||
"enabled": true,
|
||||
"initializationOptions": {},
|
||||
"settings": {}
|
||||
},
|
||||
{
|
||||
"languageId": "go",
|
||||
"displayName": "Gopls",
|
||||
"extensions": ["go"],
|
||||
"command": ["gopls", "serve"],
|
||||
"enabled": true,
|
||||
"initializationOptions": {},
|
||||
"settings": {}
|
||||
},
|
||||
{
|
||||
"languageId": "rust",
|
||||
"displayName": "Rust Analyzer",
|
||||
"extensions": ["rs"],
|
||||
"command": ["rust-analyzer"],
|
||||
"enabled": false,
|
||||
"initializationOptions": {},
|
||||
"settings": {}
|
||||
},
|
||||
{
|
||||
"languageId": "c",
|
||||
"displayName": "Clangd",
|
||||
"extensions": ["c", "h"],
|
||||
"command": ["clangd"],
|
||||
"enabled": false,
|
||||
"initializationOptions": {},
|
||||
"settings": {}
|
||||
},
|
||||
{
|
||||
"languageId": "cpp",
|
||||
"displayName": "Clangd",
|
||||
"extensions": ["cpp", "hpp", "cc", "cxx"],
|
||||
"command": ["clangd"],
|
||||
"enabled": false,
|
||||
"initializationOptions": {},
|
||||
"settings": {}
|
||||
}
|
||||
],
|
||||
"defaults": {
|
||||
"rootDir": ".",
|
||||
"timeout": 30000,
|
||||
"restartInterval": 5000,
|
||||
"maxRestarts": 3
|
||||
}
|
||||
}
|
||||
@@ -14,6 +14,7 @@ Features:
|
||||
from __future__ import annotations
|
||||
|
||||
import asyncio
|
||||
import importlib.resources as resources
|
||||
import json
|
||||
import logging
|
||||
import os
|
||||
@@ -117,7 +118,6 @@ class StandaloneLspManager:
|
||||
1. Explicit config_file parameter
|
||||
2. {workspace_root}/lsp-servers.json
|
||||
3. {workspace_root}/.codexlens/lsp-servers.json
|
||||
4. Package default (codexlens/lsp-servers.json)
|
||||
"""
|
||||
search_paths = []
|
||||
|
||||
@@ -127,7 +127,6 @@ class StandaloneLspManager:
|
||||
search_paths.extend([
|
||||
self.workspace_root / self.DEFAULT_CONFIG_FILE,
|
||||
self.workspace_root / ".codexlens" / self.DEFAULT_CONFIG_FILE,
|
||||
Path(__file__).parent.parent.parent.parent / self.DEFAULT_CONFIG_FILE, # package root
|
||||
])
|
||||
|
||||
for path in search_paths:
|
||||
@@ -135,21 +134,73 @@ class StandaloneLspManager:
|
||||
return path
|
||||
|
||||
return None
|
||||
|
||||
def _load_builtin_config(self) -> Optional[dict[str, Any]]:
|
||||
"""Load the built-in default lsp-servers.json shipped with the package."""
|
||||
try:
|
||||
text = (
|
||||
resources.files("codexlens.lsp")
|
||||
.joinpath(self.DEFAULT_CONFIG_FILE)
|
||||
.read_text(encoding="utf-8")
|
||||
)
|
||||
except Exception as exc:
|
||||
logger.error(
|
||||
"Failed to load built-in %s template from package: %s",
|
||||
self.DEFAULT_CONFIG_FILE,
|
||||
exc,
|
||||
)
|
||||
return None
|
||||
|
||||
try:
|
||||
return json.loads(text)
|
||||
except Exception as exc:
|
||||
logger.error(
|
||||
"Built-in %s template shipped with the package is invalid JSON: %s",
|
||||
self.DEFAULT_CONFIG_FILE,
|
||||
exc,
|
||||
)
|
||||
return None
|
||||
|
||||
def _load_config(self) -> None:
|
||||
"""Load language server configuration from JSON file."""
|
||||
self._configs.clear()
|
||||
self._extension_map.clear()
|
||||
|
||||
config_path = self._find_config_file()
|
||||
|
||||
if not config_path:
|
||||
logger.warning(f"No {self.DEFAULT_CONFIG_FILE} found, using empty config")
|
||||
return
|
||||
|
||||
try:
|
||||
with open(config_path, "r", encoding="utf-8") as f:
|
||||
data = json.load(f)
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to load config from {config_path}: {e}")
|
||||
return
|
||||
data = self._load_builtin_config()
|
||||
if data is None:
|
||||
logger.warning(
|
||||
"No %s found and built-in defaults could not be loaded; using empty config",
|
||||
self.DEFAULT_CONFIG_FILE,
|
||||
)
|
||||
return
|
||||
|
||||
root_config_path = self.workspace_root / self.DEFAULT_CONFIG_FILE
|
||||
codexlens_config_path = (
|
||||
self.workspace_root / ".codexlens" / self.DEFAULT_CONFIG_FILE
|
||||
)
|
||||
|
||||
logger.info(
|
||||
"No %s found at %s or %s; using built-in defaults shipped with codex-lens. "
|
||||
"To customize, copy the template to one of those locations and restart. "
|
||||
"Language servers are spawned on-demand when first needed. "
|
||||
"Ensure the language server commands in the config are installed and on PATH.",
|
||||
self.DEFAULT_CONFIG_FILE,
|
||||
root_config_path,
|
||||
codexlens_config_path,
|
||||
)
|
||||
config_source = "built-in defaults"
|
||||
else:
|
||||
try:
|
||||
with open(config_path, "r", encoding="utf-8") as f:
|
||||
data = json.load(f)
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to load config from {config_path}: {e}")
|
||||
return
|
||||
|
||||
config_source = str(config_path)
|
||||
|
||||
# Parse defaults
|
||||
defaults = data.get("defaults", {})
|
||||
@@ -186,7 +237,11 @@ class StandaloneLspManager:
|
||||
for ext in config.extensions:
|
||||
self._extension_map[ext.lower()] = language_id
|
||||
|
||||
logger.info(f"Loaded {len(self._configs)} language server configs from {config_path}")
|
||||
logger.info(
|
||||
"Loaded %d language server configs from %s",
|
||||
len(self._configs),
|
||||
config_source,
|
||||
)
|
||||
|
||||
def get_language_id(self, file_path: str) -> Optional[str]:
|
||||
"""Get language ID for a file based on extension.
|
||||
|
||||
27
codex-lens/tests/lsp/test_packaging_metadata.py
Normal file
27
codex-lens/tests/lsp/test_packaging_metadata.py
Normal file
@@ -0,0 +1,27 @@
|
||||
"""Packaging metadata tests for codex-lens (LSP/semantic extras)."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
def _read_pyproject() -> str:
|
||||
repo_root = Path(__file__).resolve().parents[2]
|
||||
return (repo_root / "pyproject.toml").read_text(encoding="utf-8")
|
||||
|
||||
|
||||
def test_lsp_script_entrypoint_points_to_server_main() -> None:
|
||||
pyproject = _read_pyproject()
|
||||
assert 'codexlens-lsp = "codexlens.lsp.server:main"' in pyproject
|
||||
|
||||
|
||||
def test_semantic_extras_do_not_pin_yanked_fastembed_020() -> None:
|
||||
pyproject = _read_pyproject()
|
||||
assert "fastembed~=0.2.0" not in pyproject
|
||||
assert "fastembed~=0.2.1" in pyproject
|
||||
|
||||
|
||||
def test_click_dependency_is_explicitly_guarded() -> None:
|
||||
pyproject = _read_pyproject()
|
||||
assert "click>=8.0.0,<9" in pyproject
|
||||
|
||||
31
codex-lens/tests/lsp/test_standalone_manager_defaults.py
Normal file
31
codex-lens/tests/lsp/test_standalone_manager_defaults.py
Normal file
@@ -0,0 +1,31 @@
|
||||
"""Tests for StandaloneLspManager default config behavior."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import asyncio
|
||||
import logging
|
||||
from pathlib import Path
|
||||
|
||||
import pytest
|
||||
|
||||
from codexlens.lsp.standalone_manager import StandaloneLspManager
|
||||
|
||||
|
||||
def test_loads_builtin_defaults_when_no_config_found(
|
||||
tmp_path: Path, caplog: pytest.LogCaptureFixture
|
||||
) -> None:
|
||||
manager = StandaloneLspManager(workspace_root=str(tmp_path))
|
||||
|
||||
with caplog.at_level(logging.INFO):
|
||||
asyncio.run(manager.start())
|
||||
|
||||
assert manager._configs # type: ignore[attr-defined]
|
||||
assert manager.get_language_id(str(tmp_path / "example.py")) == "python"
|
||||
|
||||
expected_root = str(tmp_path / "lsp-servers.json")
|
||||
expected_codexlens = str(tmp_path / ".codexlens" / "lsp-servers.json")
|
||||
|
||||
assert "using built-in defaults" in caplog.text.lower()
|
||||
assert expected_root in caplog.text
|
||||
assert expected_codexlens in caplog.text
|
||||
|
||||
Reference in New Issue
Block a user