diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 31feeedb..cd5f2ff4 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -44,3 +44,25 @@ for you. Note: For the Windows subsystem for Linux (WSL), you may need to adjust the path for the volume. + +## Adding a New Supported Language + +Serena interacts with code through language servers which are included in +the `multilspy` package. It is rather easy to include a new supported language +if an LSP implementation for it exists. You just need to: + +1. create a new subclass of `LanguageServer` +2. add a new value to the `Language` enum +3. make a new `elif` case in the `LanguageServer.create` method +4. write minor tests + +The subclasses are typically easy to write, have a look at the +[PyrightLanguageServer](src/multilspy/language_servers/pyright_language_server/pyright_server.py) +for an example, or at any other implementation to see how non-python +dependencies for language servers are handled there. +There are also some tips from the multilspy admin [here](https://github.com/microsoft/multilspy/issues/5). + +⚠️ Important: The LSP allows for lot of optional fields and symbols, so the language servers may differ +in some details, even if they follow the LSP. Therefore you should include some code of the new +language in `test/resources` and add tests for symbolic read operations on that code. Have a look +at `test/multilspy/test_symbol_retrieval.py` for an example of such tests for the python LS. diff --git a/README.md b/README.md index 390c832f..89a59646 100644 --- a/README.md +++ b/README.md @@ -140,7 +140,8 @@ Serena can read, write and execute code, read logs and the terminal output. } ``` - When using paths containing backslashes on Windows, be sure to escape them correctly (`\\`). + If you are using paths containing backslashes for paths on Windows + (note that you can also just use forward slashes), be sure to escape them correctly (`\\`). That's it! Save the config and then restart Claude Desktop. @@ -482,9 +483,12 @@ Without these projects, Serena would not have been possible (or would have been ## Customizing Serena -It is very easy to extend Serena with your own ideas. Just implement a new Tool by subclassing from -`serena.agent.Tool`. By default, the `SerenaAgent` will immediately have access to it. We look forward -to seeing what the community will come up with! For details on contributing, see [here](/CONTRIBUTING.md). +It is very easy to extend Serena's AI functionality with your own ideas. +Just implement a new Tool by subclassing from +`serena.agent.Tool`. By default, the `SerenaAgent` will immediately have access to it. + +It is also relatively straightforward to add [support for a new language](/CONTRIBUTING.md#adding-a-new-supported-language). We look forward to seeing what the community will come up with! +For details on contributing, see [here](/CONTRIBUTING.md). ## Full List of Tools diff --git a/test/multilspy/test_basic.py b/test/multilspy/test_basic.py index 8b74bcec..e377a17c 100644 --- a/test/multilspy/test_basic.py +++ b/test/multilspy/test_basic.py @@ -15,7 +15,7 @@ from serena.text_utils import LineType class TestLanguageServerBasics: """Test basic functionality of the language server.""" - def test_request_references_user_class(self, language_server: SyncLanguageServer, repo_path: Path): + def test_request_references_user_class(self, language_server: SyncLanguageServer): """Test request_references on the User class.""" # Get references to the User class in models.py file_path = os.path.join("test_repo", "models.py") @@ -28,7 +28,7 @@ class TestLanguageServerBasics: # At least two references should be found (one for the class definition itself) assert len(references) > 1 - def test_request_references_item_class(self, language_server: SyncLanguageServer, repo_path: Path): + def test_request_references_item_class(self, language_server: SyncLanguageServer): """Test request_references on the Item class.""" # Get references to the Item class in models.py file_path = os.path.join("test_repo", "models.py") @@ -42,7 +42,7 @@ class TestLanguageServerBasics: services_references = [ref for ref in references if "services.py" in ref["uri"]] assert len(services_references) > 0 - def test_request_references_function_parameter(self, language_server: SyncLanguageServer, repo_path: Path): + def test_request_references_function_parameter(self, language_server: SyncLanguageServer): """Test request_references on a function parameter.""" # Get references to the id parameter in get_user method file_path = os.path.join("test_repo", "services.py") @@ -52,7 +52,7 @@ class TestLanguageServerBasics: # id parameter should be referenced within the method assert len(references) > 0 - def test_request_references_create_user_method(self, language_server: SyncLanguageServer, repo_path: Path): + def test_request_references_create_user_method(self, language_server: SyncLanguageServer): # Get references to the create_user method in UserService file_path = os.path.join("test_repo", "services.py") # Line 15 contains the create_user method definition @@ -61,7 +61,7 @@ class TestLanguageServerBasics: # Verify that we get valid references assert len(references) > 1 - def test_retrieve_content_around_line(self, language_server: SyncLanguageServer, repo_path: Path): + def test_retrieve_content_around_line(self, language_server: SyncLanguageServer): """Test retrieve_content_around_line functionality with various scenarios.""" file_path = os.path.join("test_repo", "models.py") @@ -167,7 +167,7 @@ class TestLanguageServerBasics: else: assert line.match_type == LineType.AFTER_MATCH - def test_search_files_for_pattern(self, language_server: SyncLanguageServer, repo_path: Path): + def test_search_files_for_pattern(self, language_server: SyncLanguageServer): """Test search_files_for_pattern with various patterns and glob filters.""" # Test 1: Search for class definitions across all files class_pattern = r"class\s+\w+\s*(?:\([^{]*\)|:)"