mirror of
https://github.com/tiennm99/serena.git
synced 2026-10-14 22:17:35 +00:00
Project: Make SerenaConfig compulsory, simplifying LanguageServerManager creation
This commit is contained in:
1 parent
59f344cc00
commit
1af824c025
8 files changed
+41
-68
No files matched your search
+1
-15
@@ -799,21 +799,7 @@ class SerenaAgent:
|
||||
"""
|
||||
Starts/resets the language server manager for the current project
|
||||
"""
|
||||
tool_timeout = self.serena_config.tool_timeout
|
||||
if tool_timeout is None or tool_timeout < 0:
|
||||
ls_timeout = None
|
||||
else:
|
||||
if tool_timeout < 10:
|
||||
raise ValueError(f"Tool timeout must be at least 10 seconds, but is {tool_timeout} seconds")
|
||||
ls_timeout = tool_timeout - 5 # the LS timeout is for a single call, it should be smaller than the tool timeout
|
||||
|
||||
# instantiate and start the necessary language servers
|
||||
self.get_active_project_or_raise().create_language_server_manager(
|
||||
log_level=self.serena_config.log_level,
|
||||
ls_timeout=ls_timeout,
|
||||
trace_lsp_communication=self.serena_config.trace_lsp_communication,
|
||||
ls_specific_settings=self.serena_config.ls_specific_settings,
|
||||
)
|
||||
self.get_active_project_or_raise().create_language_server_manager()
|
||||
|
||||
def add_language(self, language: Language) -> None:
|
||||
"""
|
||||
|
||||
+1
-3
@@ -640,9 +640,7 @@ class ProjectCommands(AutoRegisteringGroup):
|
||||
serena_config = SerenaConfig.from_config_file()
|
||||
proj = registered_project.get_project_instance(serena_config=serena_config)
|
||||
click.echo(f"Indexing symbols in {proj} …")
|
||||
ls_mgr = proj.create_language_server_manager(
|
||||
log_level=lvl, ls_timeout=timeout, ls_specific_settings=serena_config.ls_specific_settings
|
||||
)
|
||||
ls_mgr = proj.create_language_server_manager()
|
||||
try:
|
||||
log_file = os.path.join(proj.project_root, ".serena", "logs", "indexing.txt")
|
||||
|
||||
|
||||
@@ -488,7 +488,7 @@ class RegisteredProject(ToStringMixin):
|
||||
"""
|
||||
return self.project_root.samefile(Path(path).resolve())
|
||||
|
||||
def get_project_instance(self, serena_config: "SerenaConfig | None") -> "Project":
|
||||
def get_project_instance(self, serena_config: "SerenaConfig") -> "Project":
|
||||
"""
|
||||
Returns the project instance for this registered project, loading it if necessary.
|
||||
"""
|
||||
|
||||
+18
-19
@@ -10,7 +10,6 @@ from sensai.util.logging import LogTime
|
||||
from sensai.util.string import ToStringMixin
|
||||
|
||||
from serena.config.serena_config import (
|
||||
DEFAULT_TOOL_TIMEOUT,
|
||||
ProjectConfig,
|
||||
get_serena_managed_in_project_dir,
|
||||
)
|
||||
@@ -117,13 +116,16 @@ class MemoriesManager:
|
||||
class Project(ToStringMixin):
|
||||
def __init__(
|
||||
self,
|
||||
*,
|
||||
project_root: str,
|
||||
project_config: ProjectConfig,
|
||||
serena_config: "SerenaConfig",
|
||||
is_newly_created: bool = False,
|
||||
serena_config: "SerenaConfig | None" = None,
|
||||
):
|
||||
assert serena_config is not None
|
||||
self.project_root = project_root
|
||||
self.project_config = project_config
|
||||
self._serena_config = serena_config
|
||||
self.memories_manager = MemoriesManager(project_root)
|
||||
self.language_server_manager: LanguageServerManager | None = None
|
||||
self._is_newly_created = is_newly_created
|
||||
@@ -137,7 +139,6 @@ class Project(ToStringMixin):
|
||||
f.write(f"/{SolidLanguageServer.CACHE_FOLDER_NAME}\n")
|
||||
|
||||
# prepare ignore spec asynchronously, ensuring immediate project activation.
|
||||
self._serena_config = serena_config
|
||||
self.__ignored_patterns: list[str]
|
||||
self.__ignore_spec: pathspec.PathSpec
|
||||
self._ignore_spec_available = threading.Event()
|
||||
@@ -147,7 +148,7 @@ class Project(ToStringMixin):
|
||||
with LogTime(f"Gathering ignore spec for project {self.project_config.project_name}", logger=log):
|
||||
|
||||
# gather ignored paths from the global configuration, project configuration, and gitignore files
|
||||
global_ignored_paths = self._serena_config.ignored_paths if self._serena_config else []
|
||||
global_ignored_paths = self._serena_config.ignored_paths
|
||||
ignored_patterns = list(global_ignored_paths) + list(self.project_config.ignored_paths)
|
||||
if len(global_ignored_paths) > 0:
|
||||
log.info(f"Using {len(global_ignored_paths)} ignored paths from the global configuration.")
|
||||
@@ -189,7 +190,7 @@ class Project(ToStringMixin):
|
||||
def load(
|
||||
cls,
|
||||
project_root: str | Path,
|
||||
serena_config: "SerenaConfig | None",
|
||||
serena_config: "SerenaConfig",
|
||||
autogenerate: bool = True,
|
||||
) -> "Project":
|
||||
project_root = Path(project_root).resolve()
|
||||
@@ -464,23 +465,21 @@ class Project(ToStringMixin):
|
||||
source_file_path=relative_file_path,
|
||||
)
|
||||
|
||||
def create_language_server_manager(
|
||||
self,
|
||||
log_level: int = logging.INFO,
|
||||
ls_timeout: float | None = DEFAULT_TOOL_TIMEOUT - 5,
|
||||
trace_lsp_communication: bool = False,
|
||||
ls_specific_settings: dict[Language, Any] | None = None,
|
||||
) -> LanguageServerManager:
|
||||
def create_language_server_manager(self) -> LanguageServerManager:
|
||||
"""
|
||||
Creates the language server manager for the project, starting one language server per configured programming language.
|
||||
|
||||
:param log_level: the log level for the language server
|
||||
:param ls_timeout: the timeout for the language server
|
||||
:param trace_lsp_communication: whether to trace LSP communication
|
||||
:param ls_specific_settings: optional LS specific configuration of the language server,
|
||||
see docstrings in the inits of subclasses of SolidLanguageServer to see what values may be passed.
|
||||
:return: the language server manager, which is also stored in the project instance
|
||||
"""
|
||||
# determine timeout to use for LS calls
|
||||
tool_timeout = self._serena_config.tool_timeout
|
||||
if tool_timeout is None or tool_timeout < 0:
|
||||
ls_timeout = None
|
||||
else:
|
||||
if tool_timeout < 10:
|
||||
raise ValueError(f"Tool timeout must be at least 10 seconds, but is {tool_timeout} seconds")
|
||||
ls_timeout = tool_timeout - 5 # the LS timeout is for a single call, it should be smaller than the tool timeout
|
||||
|
||||
# if there is an existing instance, stop its language servers first
|
||||
if self.language_server_manager is not None:
|
||||
log.info("Stopping existing language server manager ...")
|
||||
@@ -493,8 +492,8 @@ class Project(ToStringMixin):
|
||||
encoding=self.project_config.encoding,
|
||||
ignored_patterns=self._ignored_patterns,
|
||||
ls_timeout=ls_timeout,
|
||||
ls_specific_settings=ls_specific_settings,
|
||||
trace_lsp_communication=trace_lsp_communication,
|
||||
ls_specific_settings=self._serena_config.ls_specific_settings,
|
||||
trace_lsp_communication=self._serena_config.trace_lsp_communication,
|
||||
)
|
||||
self.language_server_manager = LanguageServerManager.from_languages(self.project_config.languages, factory)
|
||||
return self.language_server_manager
|
||||
|
||||
+3
-2
@@ -10,7 +10,7 @@ from typing import Any
|
||||
import pytest
|
||||
from sensai.util.logging import configure
|
||||
|
||||
from serena.config.serena_config import SerenaPaths
|
||||
from serena.config.serena_config import SerenaConfig, SerenaPaths
|
||||
from serena.constants import SERENA_MANAGED_DIR_NAME
|
||||
from serena.project import Project
|
||||
from serena.util.file_system import GitignoreParser
|
||||
@@ -117,7 +117,8 @@ def start_default_ls_context(language: Language) -> Iterator[SolidLanguageServer
|
||||
|
||||
def _create_default_project(language: Language) -> Project:
|
||||
repo_path = str(get_repo_path(language))
|
||||
return Project.load(repo_path, serena_config=None)
|
||||
serena_config = SerenaConfig()
|
||||
return Project.load(repo_path, serena_config=serena_config)
|
||||
|
||||
|
||||
@pytest.fixture(scope="session")
|
||||
|
||||
@@ -20,9 +20,7 @@ def _create_test_project(
|
||||
ignored_paths=project_ignored_paths or [],
|
||||
ignore_all_files_in_gitignore=False,
|
||||
)
|
||||
serena_config: SerenaConfig | None = None
|
||||
if global_ignored_paths:
|
||||
serena_config = SerenaConfig(gui_log_window=False, web_dashboard=False, ignored_paths=global_ignored_paths)
|
||||
serena_config = SerenaConfig(gui_log_window=False, web_dashboard=False, ignored_paths=global_ignored_paths)
|
||||
return Project(
|
||||
project_root=str(project_root),
|
||||
project_config=config,
|
||||
@@ -84,21 +82,6 @@ class TestGlobalIgnoredPaths:
|
||||
# Non-ignored files still accessible
|
||||
assert not project.is_ignored_path(str(self.project_path / "node_modules" / "pkg" / "index.js"))
|
||||
|
||||
def test_default_global_ignored_paths_backward_compatible(self) -> None:
|
||||
"""Project created without global_ignored_paths parameter works as before."""
|
||||
config = ProjectConfig(
|
||||
project_name="test_project",
|
||||
languages=[Language.PYTHON],
|
||||
ignored_paths=["build"],
|
||||
ignore_all_files_in_gitignore=False,
|
||||
)
|
||||
project = Project(
|
||||
project_root=str(self.project_path),
|
||||
project_config=config,
|
||||
)
|
||||
assert project.is_ignored_path(str(self.project_path / "build" / "output.js"))
|
||||
assert not project.is_ignored_path(str(self.project_path / "node_modules" / "pkg" / "index.js"))
|
||||
|
||||
def test_duplicate_patterns_across_global_and_project(self) -> None:
|
||||
"""Duplicate patterns across global and project do not cause errors."""
|
||||
project = _create_test_project(
|
||||
@@ -161,7 +144,8 @@ class TestRegisteredProjectGlobalIgnoredPaths:
|
||||
project_root=str(self.project_path),
|
||||
project_config=config,
|
||||
)
|
||||
project = registered.get_project_instance(serena_config=None)
|
||||
serena_config = SerenaConfig(gui_log_window=False, web_dashboard=False, ignored_paths=[])
|
||||
project = registered.get_project_instance(serena_config=serena_config)
|
||||
assert not project.is_ignored_path(str(self.project_path / "node_modules" / "pkg.js"))
|
||||
|
||||
def test_from_project_root_passes_global_ignored_paths(self) -> None:
|
||||
|
||||
@@ -191,6 +191,12 @@ def _make_config_with_project(
|
||||
global_backend: LanguageBackend = LanguageBackend.LSP,
|
||||
) -> tuple[SerenaConfig, str]:
|
||||
"""Create a SerenaConfig with a single registered project and return (config, project_name)."""
|
||||
config = SerenaConfig(
|
||||
gui_log_window=False,
|
||||
web_dashboard=False,
|
||||
log_level=logging.ERROR,
|
||||
language_backend=global_backend,
|
||||
)
|
||||
project = Project(
|
||||
project_root=str(Path(__file__).parent.parent / "resources" / "repos" / "python" / "test_repo"),
|
||||
project_config=ProjectConfig(
|
||||
@@ -198,12 +204,7 @@ def _make_config_with_project(
|
||||
languages=[Language.PYTHON],
|
||||
language_backend=language_backend,
|
||||
),
|
||||
)
|
||||
config = SerenaConfig(
|
||||
gui_log_window=False,
|
||||
web_dashboard=False,
|
||||
log_level=logging.ERROR,
|
||||
language_backend=global_backend,
|
||||
serena_config=config,
|
||||
)
|
||||
config.projects = [RegisteredProject.from_project_instance(project)]
|
||||
return config, project_name
|
||||
@@ -259,6 +260,7 @@ class TestEffectiveLanguageBackend:
|
||||
languages=[Language.PYTHON],
|
||||
language_backend=LanguageBackend.JETBRAINS,
|
||||
),
|
||||
serena_config=config,
|
||||
)
|
||||
config.projects.append(RegisteredProject.from_project_instance(jb_project))
|
||||
|
||||
@@ -281,6 +283,7 @@ class TestEffectiveLanguageBackend:
|
||||
languages=[Language.PYTHON],
|
||||
language_backend=LanguageBackend.LSP,
|
||||
),
|
||||
serena_config=config,
|
||||
)
|
||||
config.projects.append(RegisteredProject.from_project_instance(lsp_project2))
|
||||
|
||||
@@ -303,6 +306,7 @@ class TestEffectiveLanguageBackend:
|
||||
languages=[Language.PYTHON],
|
||||
language_backend=None,
|
||||
),
|
||||
serena_config=config,
|
||||
)
|
||||
config.projects.append(RegisteredProject.from_project_instance(proj2))
|
||||
|
||||
|
||||
@@ -21,7 +21,8 @@ from test.solidlsp import clojure as clj
|
||||
|
||||
@pytest.fixture
|
||||
def serena_config():
|
||||
"""Create an in-memory configuration for tests with test repositories pre-registered."""
|
||||
config = SerenaConfig(gui_log_window=False, web_dashboard=False, log_level=logging.ERROR)
|
||||
|
||||
# Create test projects for all supported languages
|
||||
test_projects = []
|
||||
for language in [
|
||||
@@ -47,16 +48,16 @@ def serena_config():
|
||||
project_name=project_name,
|
||||
languages=[language],
|
||||
ignored_paths=[],
|
||||
excluded_tools=set(),
|
||||
excluded_tools=[],
|
||||
read_only=False,
|
||||
ignore_all_files_in_gitignore=True,
|
||||
initial_prompt="",
|
||||
encoding="utf-8",
|
||||
),
|
||||
serena_config=config,
|
||||
)
|
||||
test_projects.append(RegisteredProject.from_project_instance(project))
|
||||
|
||||
config = SerenaConfig(gui_log_window=False, web_dashboard=False, log_level=logging.ERROR)
|
||||
config.projects = test_projects
|
||||
return config
|
||||
|
||||
|
||||
Reference in new issue
Block a user