mirror of
https://github.com/tiennm99/serena.git
synced 2026-10-12 16:15:33 +00:00
Apply configuration updates (with defaults and backward compatibility) at the dictionary level,
allowing old configurations to be properly updated when resaving (outdated entries removed/new entries added in re-saved version)
This commit is contained in:
1 parent
6e26d08d5d
commit
e85afc1ea8
2 files changed
+43
-21
No files matched your search
@@ -223,21 +223,44 @@ class ProjectConfig(ToolInclusionDefinition, ToStringMixin):
|
||||
def rel_path_to_project_yml(cls) -> str:
|
||||
return os.path.join(SERENA_MANAGED_DIR_NAME, cls.SERENA_DEFAULT_PROJECT_FILE)
|
||||
|
||||
@classmethod
|
||||
def load_commented_map(cls, yml_path: str) -> CommentedMap:
|
||||
"""
|
||||
Load the project configuration as a CommentedMap, preserving comments and ensuring
|
||||
completeness of the configuration by applying default values for missing fields
|
||||
and backward compatibility adjustments.
|
||||
|
||||
:param yml_path: the path to the project.yml file
|
||||
:return: a CommentedMap representing a full project configuration
|
||||
"""
|
||||
data = load_yaml(yml_path, preserve_comments=True)
|
||||
|
||||
# apply defaults for new fields
|
||||
data["languages"] = data.get("languages", [])
|
||||
data["ignored_paths"] = data.get("ignored_paths", [])
|
||||
data["excluded_tools"] = data.get("excluded_tools", [])
|
||||
data["included_optional_tools"] = data.get("included_optional_tools", [])
|
||||
data["read_only"] = data.get("read_only", False)
|
||||
data["ignore_all_files_in_gitignore"] = data.get("ignore_all_files_in_gitignore", True)
|
||||
data["initial_prompt"] = data.get("initial_prompt", "")
|
||||
data["encoding"] = data.get("encoding", DEFAULT_SOURCE_FILE_ENCODING)
|
||||
|
||||
# backward compatibility: handle single "language" field
|
||||
if len(data["languages"]) == 0 and "language" in data:
|
||||
data["languages"] = [data["language"]]
|
||||
if "language" in data:
|
||||
del data["language"]
|
||||
|
||||
return data
|
||||
|
||||
@classmethod
|
||||
def _from_dict(cls, data: dict[str, Any]) -> Self:
|
||||
"""
|
||||
Create a ProjectConfig instance from a configuration dictionary
|
||||
Create a ProjectConfig instance from a (full) configuration dictionary
|
||||
"""
|
||||
project_name = data["project_name"]
|
||||
language_strings = data.get("languages", [])
|
||||
|
||||
# backwards compatibility
|
||||
lang_name_mapping = {"javascript": "typescript"}
|
||||
if len(language_strings) == 0 and "language" in data:
|
||||
language_strings = [data["language"]]
|
||||
|
||||
languages: list[Language] = []
|
||||
for language_str in language_strings:
|
||||
for language_str in data["languages"]:
|
||||
try:
|
||||
language_str = language_str.lower()
|
||||
if language_str in lang_name_mapping:
|
||||
@@ -248,15 +271,15 @@ class ProjectConfig(ToolInclusionDefinition, ToStringMixin):
|
||||
raise ValueError(f"Invalid language: {data['language']}.\nValid language_strings are: {[l.value for l in Language]}") from e
|
||||
|
||||
return cls(
|
||||
project_name=project_name,
|
||||
project_name=data["project_name"],
|
||||
languages=languages,
|
||||
ignored_paths=data.get("ignored_paths", []),
|
||||
excluded_tools=data.get("excluded_tools", []),
|
||||
included_optional_tools=data.get("included_optional_tools", []),
|
||||
read_only=data.get("read_only", False),
|
||||
ignore_all_files_in_gitignore=data.get("ignore_all_files_in_gitignore", True),
|
||||
initial_prompt=data.get("initial_prompt", ""),
|
||||
encoding=data.get("encoding", DEFAULT_SOURCE_FILE_ENCODING),
|
||||
ignored_paths=data["ignored_paths"],
|
||||
excluded_tools=data["excluded_tools"],
|
||||
included_optional_tools=data["included_optional_tools"],
|
||||
read_only=data["read_only"],
|
||||
ignore_all_files_in_gitignore=data["ignore_all_files_in_gitignore"],
|
||||
initial_prompt=data["initial_prompt"],
|
||||
encoding=data["encoding"],
|
||||
)
|
||||
|
||||
def to_yaml_dict(self) -> dict:
|
||||
@@ -279,8 +302,7 @@ class ProjectConfig(ToolInclusionDefinition, ToStringMixin):
|
||||
return cls.autogenerate(project_root)
|
||||
else:
|
||||
raise FileNotFoundError(f"Project configuration file not found: {yaml_path}")
|
||||
with open(yaml_path, encoding=SERENA_FILE_ENCODING) as f:
|
||||
yaml_data = yaml.safe_load(f)
|
||||
yaml_data = cls.load_commented_map(str(yaml_path))
|
||||
if "project_name" not in yaml_data:
|
||||
yaml_data["project_name"] = project_root.name
|
||||
return cls._from_dict(yaml_data)
|
||||
|
||||
@@ -12,7 +12,7 @@ from serena.constants import SERENA_FILE_ENCODING, SERENA_MANAGED_DIR_NAME
|
||||
from serena.ls_manager import LanguageServerFactory, LanguageServerManager
|
||||
from serena.text_utils import MatchedConsecutiveLines, search_files
|
||||
from serena.util.file_system import GitignoreParser, match_path
|
||||
from serena.util.general import load_yaml, save_yaml
|
||||
from serena.util.general import save_yaml
|
||||
from solidlsp import SolidLanguageServer
|
||||
from solidlsp.ls_config import Language
|
||||
from solidlsp.ls_utils import FileUtils
|
||||
@@ -116,7 +116,7 @@ class Project(ToStringMixin):
|
||||
"""
|
||||
config_path = os.path.join(self.project_root, self.project_config.rel_path_to_project_yml())
|
||||
log.info("Saving updated project configuration to %s", config_path)
|
||||
config_with_comments = load_yaml(config_path, preserve_comments=True)
|
||||
config_with_comments = ProjectConfig.load_commented_map(config_path)
|
||||
config_with_comments.update(self.project_config.to_yaml_dict())
|
||||
save_yaml(config_path, config_with_comments, preserve_comments=True)
|
||||
|
||||
|
||||
Reference in new issue
Block a user