Conversation
MischaPanch
left a comment
There was a problem hiding this comment.
Thanks for the contribution! A good step towards adding nim support, but a bit of "polishing" work is still needed. I haven't reviewed all details yet (in particular, the logic of the while loop or the config writing), and will do so once these first comments are addressed.
test/solidlsp/nim/test_nim_basic.py
Outdated
| # Test finding references to the Person type | ||
| references = language_server.request_references("main.nim", 20, 3) # Person type definition | ||
|
|
||
| if references: |
There was a problem hiding this comment.
Since this is the most common point of failure, pls make sure cross-file references finding actually works by ensuring the expected references are found, not just their existence. Pls remove all assert isinstance and assert len(...) > 0, these are too generic and don't really add value. There's a dedicated section about this in the guide (memory) on how to add support for new languages
|
|
||
|
|
||
| @pytest.mark.nim | ||
| class TestNimLanguageServerBasics: |
There was a problem hiding this comment.
tests on windows time out - is the language server not supported for windows?
| self._log(payload) | ||
|
|
||
| # Create simplified message for nimlangserver (only Content-Length header) | ||
| import json |
There was a problem hiding this comment.
pls use global imports (here and below)
| self.logger("client", "logger", f"Failed to write to stdin: {e}") | ||
| return | ||
|
|
||
| def _read_ls_process_stderr(self) -> None: |
There was a problem hiding this comment.
pls use @OVERRIDES for all overridden methods (importes from overrides, not typing)
| max_errors = 10 | ||
|
|
||
| try: | ||
| while self.process and self.process.stderr: |
There was a problem hiding this comment.
I suppose it is this what causes the hanging on windows - we shouldn't have a while loop forever, pls add some condition to terminate, even in case of failure
| def _create_nim_config_if_needed(self): | ||
| """Create or supplement Nim configuration to help nimsuggest work better.""" | ||
| try: | ||
| nim_cfg_path = os.path.join(self.repository_root_path, "nim.cfg") |
There was a problem hiding this comment.
should we really create these files in the user's repository? I suppose this will even create files in nim/test_repo during test runs.
If there's really no way around it, pls factor out _NIMSUGGEST_CONTENT and other such things as multiline strings.
| nimble_files = list(pathlib.Path(self.repository_root_path).glob("*.nimble")) | ||
| if nimble_files: | ||
| # Check common project structures to determine paths | ||
| has_src = os.path.exists(os.path.join(self.repository_root_path, "src")) |
There was a problem hiding this comment.
this seems very fragile - why is that needed? I suppose project structure is not enforced in nim, right?
| cls._solidlsp_settings = solidlsp_settings | ||
|
|
||
| # First check if nimlangserver is already installed via nimble | ||
| nimble_bin = os.path.expanduser("~/.nimble/bin") |
There was a problem hiding this comment.
is this OS independent?
| except Exception as e: | ||
| raise RuntimeError( | ||
| f"Failed to install nimlangserver via nimble: {e}\nPlease try installing manually with: nimble install nimlangserver" | ||
| ) |
There was a problem hiding this comment.
pls use raise ... from e
| """ | ||
| Provides Nim specific instantiation of the LanguageServer class using nimlangserver. | ||
| Contains various configurations and settings specific to Nim language. | ||
| """ |
There was a problem hiding this comment.
There's a whole bunch of tricks used here that are typically not needed in an LS implementation, pls expand the docstring and mention the most unexpected ones (like the custom handler, the while loop, the writing of config files etc) and why they are needed
|
Thank you for the feedback, I will work on all your suggestions. This pull request may have been a little premature, I have been using this implementation successfully but there are a lot of shortcomings with the nim-langserver that I'm not happy with and trying to work around rather than waiting for Nimony. |
d7ad14c to
10960e9
Compare
|
@a5m0 thanks for the updates to the PR. Could you pls merge main into this and resolve the conflicts? It would also help with the review if you answered on my previous comments and briefly mention you addressed them. |
Rebuild Nim language support on current upstream (origin/main) using the latest architecture patterns (LanguageServerProcess, self-wiring Language enum, updated SolidLanguageServer init signature). Includes a custom NimLanguageServerProcess that sends only Content-Length headers — nimlangserver 1.12.0 still cannot parse the Content-Type header that the standard implementation sends. - NIM enum in Language with source matchers and factory wiring - NimLanguageServer with nimsuggest config generation, port health tracking, and retry-with-backoff for document symbol requests - Test suite (8 tests): symbols, types, references, goto-def, completions across main.nim, utils.nim, types.nim - nim pytest marker in pyproject.toml
- Remove silent-pass guards in tests; use hard assertions - Fix incorrect line numbers in goto_definition and completion tests - Fix completion test to match actual SolidLanguageServer return format - Use shutil.which() for cross-platform nimlangserver discovery - Also check ~/.nimble/bin when nimble bin isn't on PATH - Extract config file content to module-level constants - Expand class and method docstrings explaining design decisions
Nim tests were failing with RuntimeError on CI runners (Windows, Ubuntu, macOS) because the Nim toolchain is not pre-installed. Add skipif decorator to gracefully skip when neither nim nor nimlangserver is found in PATH.
Increase pyright startup timeout from 5s to 30s in test_create_with_index_flag, and add Kotlin to the flaky language server skip list in test_find_symbol, both of which were timing out on Windows CI runners.
… and test improvements - Extract _create_server_process() factory method in SolidLanguageServer so NimLanguageServer can override it cleanly instead of the stop-and-replace hack that created then immediately discarded a LanguageServerProcess - Use threading.Event for _has_port_error and _nimsuggest_functional flags to ensure thread-safe reads/writes between notification handlers and request threads - Upgrade config file creation logs from info to warning with .gitignore suggestion - Reduce retry delays from [5, 15, 30]s to [2, 5, 10]s - Replace hardcoded line/column numbers in goto_definition and completions tests with dynamic content lookup - Add trailing newlines to all Nim test repo files - Add unit tests for _send_payload (Content-Length without Content-Type) and _create_nim_config_if_needed (7 edge cases)
- Extract shared _retry_on_empty helper for hover/references/definition retries, replacing three near-identical retry loops - Add _detect_nim_project_mapping to inject projectMapping into LSP init options, ensuring nimsuggest starts with the correct .nim entry point instead of config.nims (fixes flaky cross-file references/goto-def) - Add request cap (200) and debug logging to goto-definition fallback - Filter goto-definition fallback to .nim files only - Add test_repo.nimble and nim.cfg to test repo for proper project detection - Fix broken ../src path in test repo config.nims - Add hover test, delete test_nim_unit.py (tested private methods)
10960e9 to
bf85479
Compare
|
Rebased on latest origin/main, resolved conflicts and addressed comments, thanks! |
|
|
||
| log = logging.getLogger(__name__) | ||
|
|
||
| ENCODING = "utf-8" |
There was a problem hiding this comment.
encoding should be read off the LS's field _encoding
| is_document_symbol = "range" in first | ||
| location = first.get("location") | ||
| is_symbol_information = not is_document_symbol and isinstance(location, dict) and "range" in location | ||
| if not is_document_symbol and not is_symbol_information: |
There was a problem hiding this comment.
what is this case semantically? Why can we just return here?
|
|
||
| # If the range already spans multiple lines, assume it is correct |
There was a problem hiding this comment.
The logic here doesn't seem very robust. It would be better to use a proper parser, like https://github.com/alaviss/tree-sitter-nim, to get the actual symbol range. I understand that it may be too much to ask from you, but in this state it's an experimental implementation that can only be a temporary step before a proper solution.
The best would be if there was an actually functioning LS for nim, but I guess that's outside the realm of possibilities, right?
| return last_content_line | ||
|
|
||
| @override | ||
| def request_document_symbols(self, relative_file_path: str, file_buffer: "LSPFileBuffer | None" = None) -> "DocumentSymbols": |
There was a problem hiding this comment.
You override both the public and the private method, is that really needed?
| """Override to add a goto-definition fallback for Nim. | ||
|
|
||
| nimsuggest's ``textDocument/references`` can return incomplete results, | ||
| especially for cross-file references. When the standard path returns |
There was a problem hiding this comment.
this is really a major bug in the LS, have you considered fixing it there?
|
|
||
| overview = self.request_overview("") | ||
|
|
||
| cap_reached = False |
There was a problem hiding this comment.
I can't actually review this complicated logic. I guess having something for nim is better than having nothing, so if you say there's no better way than to hack around on almost every corner (repeating requests, adjusting selection ranges) and implementing essential LS functionality here in python, then I'll trust you on that. But we will have to flag nim as experimental, write a short document on the sorry status of the nim LS in our documentation (as warning to users) and keep an open issue to improve all of this or to wait for a better LS
There was a problem hiding this comment.
Unfortunately the tooling available is not good (part of what encouraged me to add Nim to Serena), and I agree that the current nim-langserver support should be marked as experimental. I will try to address your new comments, but ultimately I expect Nim support to be sub-par until https://github.com/nim-lang/nimony is ready.
There was a problem hiding this comment.
Ok, I get it. We can merge this best-effort support for Nim. My only criterion (apart from the tests) is that the problems and the shortcuts (and their limitations) taken to address them are clearly documented in the code, and that expectation management towards nim support is done in the user-facing documentation (like we have done for scala).
zerone0x
left a comment
There was a problem hiding this comment.
Pulled the branch locally to make sure nothing obvious regressed. The new NimLanguageServerProcess and config helpers are wired through Language.NIM, the factory override looks clean, and the docs/changelog entries are in place. Ran uv run pytest -m nim — everything skips on this machine because neither Nim nor nimlangserver is installed, but there were no unexpected failures. From my side this looks good; thanks for pushing the full compatibility layer over the finish line.
|
There are still some small things left to be fixed before it can be merged. @a5m0 said
|
Summary
NimLanguageServerclass that handles downloading/installingnimlangserver, sets up environment variables, writes auxiliary Nim/nimsuggest config files, and improves logging/readiness checks so the process starts reliably.Testing
uv run pytest -m nim(passes locally)