fix: allow project creation when no LSP-supported languages are detected

Previously, ProjectConfig.autogenerate() raised a ValueError when no
recognized source files were found (e.g. empty dir, JetBrains project,
or project with only gitignored files). This prevented users from
creating Serena projects in valid scenarios, especially with the
JetBrains backend where language servers are not used at all.

Now, autogenerate() creates the project with an empty languages list
and logs a warning explaining that symbol tools will be unavailable
for the LSP backend. Users can add languages later via the dashboard
or by editing the project configuration manually.

Fixes #1053

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
zerone0xandClaude committed 2026-03-10 16:44:27 +01:00
1 parent 3f047f9a66
commit 9e9b5edc53
2 files changed
+47 -41

No files matched your search

+32 -32
View File
@@ -292,40 +292,40 @@ class ProjectConfig(SharedConfig):
language_composition = determine_programming_language_composition(str(project_root))
log.info("Language composition: %s", language_composition)
if len(language_composition) == 0:
language_values = ", ".join([lang.value for lang in Language])
raise ValueError(
f"No source files found in {project_root}\n\n"
f"To use Serena with this project, you need to either\n"
f" 1. specify a programming language by adding parameters --language <language>\n"
f" when creating the project via the Serena CLI command OR\n"
f" 2. add source files in one of the supported languages first.\n\n"
f"Supported languages are: {language_values}\n"
f"Read the documentation for more information."
log.warning(
"No source files for supported language servers were found in %s. "
"Creating project with no configured languages. "
"Symbol-related tools (e.g. find_symbol, get_symbols_overview) will not work "
"when using the LSP backend. You can add languages later via the Serena dashboard "
"or by manually editing the project configuration.",
project_root,
)
# sort languages by number of files found
languages_and_percentages = sorted(
language_composition.items(), key=lambda item: (item[1], item[0].get_priority()), reverse=True
)
# find the language with the highest percentage and enable it
top_language_pair = languages_and_percentages[0]
other_language_pairs = languages_and_percentages[1:]
languages_to_use: list[str] = [top_language_pair[0].value]
# if in interactive mode, ask the user which other languages to enable
if len(other_language_pairs) > 0 and interactive:
print(
"Detected and enabled main language '%s' (%.2f%% of source files)."
% (top_language_pair[0].value, top_language_pair[1])
languages_to_use: list[str] = []
else:
# sort languages by number of files found
languages_and_percentages = sorted(
language_composition.items(), key=lambda item: (item[1], item[0].get_priority()), reverse=True
)
print(f"Additionally detected {len(other_language_pairs)} other language(s).\n")
print("Note: Enable only languages you need symbolic retrieval/editing capabilities for.")
print(" Additional language servers use resources and some languages may require additional")
print(" system-level installations/configuration (see Serena documentation).")
print("\nWhich additional languages do you want to enable?")
for lang, perc in other_language_pairs:
enable = ask_yes_no("Enable %s (%.2f%% of source files)?" % (lang.value, perc), default=False)
if enable:
languages_to_use.append(lang.value)
print()
# find the language with the highest percentage and enable it
top_language_pair = languages_and_percentages[0]
other_language_pairs = languages_and_percentages[1:]
languages_to_use = [top_language_pair[0].value]
# if in interactive mode, ask the user which other languages to enable
if len(other_language_pairs) > 0 and interactive:
print(
"Detected and enabled main language '%s' (%.2f%% of source files)."
% (top_language_pair[0].value, top_language_pair[1])
)
print(f"Additionally detected {len(other_language_pairs)} other language(s).\n")
print("Note: Enable only languages you need symbolic retrieval/editing capabilities for.")
print(" Additional language servers use resources and some languages may require additional")
print(" system-level installations/configuration (see Serena documentation).")
print("\nWhich additional languages do you want to enable?")
for lang, perc in other_language_pairs:
enable = ask_yes_no("Enable %s (%.2f%% of source files)?" % (lang.value, perc), default=False)
if enable:
languages_to_use.append(lang.value)
print()
log.info("Using languages: %s", languages_to_use)
else:
languages_to_use = [lang.value for lang in languages]
+15 -9
View File
@@ -37,12 +37,18 @@ class TestProjectConfigAutogenerate:
shutil.rmtree(self.test_dir)
def test_autogenerate_empty_directory(self):
"""Test that autogenerate raises ValueError with helpful message for empty directory."""
with pytest.raises(ValueError) as exc_info:
"""Test that autogenerate succeeds with empty languages list for an empty directory."""
config = ProjectConfig.autogenerate(self.project_path, self.serena_config, save_to_disk=False)
assert config.project_name == self.project_path.name
assert config.languages == []
def test_autogenerate_empty_directory_logs_warning(self, caplog):
"""Test that autogenerate logs a warning when no language files are found."""
with caplog.at_level(logging.WARNING):
ProjectConfig.autogenerate(self.project_path, self.serena_config, save_to_disk=False)
error_message = str(exc_info.value)
assert "No source files found" in error_message
assert any("No source files for supported language servers were found" in msg for msg in caplog.messages)
def test_autogenerate_with_python_files(self):
"""Test successful autogeneration with Python source files."""
@@ -105,7 +111,7 @@ class TestProjectConfigAutogenerate:
assert "Project root not found" in str(exc_info.value)
def test_autogenerate_with_gitignored_files_only(self):
"""Test autogenerate behavior when only gitignored files exist."""
"""Test autogenerate creates a project with empty languages when only gitignored files exist."""
# Create a .gitignore that ignores all Python files
gitignore = self.project_path / ".gitignore"
gitignore.write_text("*.py\n")
@@ -113,11 +119,11 @@ class TestProjectConfigAutogenerate:
# Create Python files that will be ignored
(self.project_path / "ignored.py").write_text("print('ignored')")
# Should still raise ValueError as no source files are detected
with pytest.raises(ValueError) as exc_info:
ProjectConfig.autogenerate(self.project_path, self.serena_config, save_to_disk=False)
# Should succeed with empty languages (gitignored files are not counted)
config = ProjectConfig.autogenerate(self.project_path, self.serena_config, save_to_disk=False)
assert "No source files found" in str(exc_info.value)
assert config.project_name == self.project_path.name
assert config.languages == []
def test_autogenerate_custom_project_name(self):
"""Test autogenerate with custom project name."""