The knowledge that a process launched with start_new_session=True is its own
group leader (and thus that its PGID equals its PID) was previously held by
StdioLanguageServer, i.e. the consumer had to know how the launcher works in
order to terminate the process correctly.
ManagedSubprocess now owns the process group ID alongside the Popen instance,
deriving it at construction time, and provides the termination logic; the
launcher returns instances of it. StdioLanguageServer merely holds one and
accesses the process through its interface.
LanguageServerSubprocessLauncher now returns ManagedSubprocess instances and
is renamed to ManagedSubprocessLauncher accordingly.
find_referencing_symbols degraded to file-level attribution for any
reference located inside a Go struct body, interface body or const
group: request_containing_symbol found no admissible container, so
request_referencing_symbols logged "Could not find containing symbol
... Returning file symbol instead" and fell back to the file symbol,
losing the answer the tool exists to give.
Root cause: the candidate-container filter admitted only Python's
container kinds {Method, Function, Class} (plus Variable via the
one-liner-exempt path). The language server is not at fault:
hierarchicalDocumentSymbolSupport is declared and the DocumentSymbol
tree is complete, but Go structs arrive as SymbolKind.Struct,
interfaces as Interface and const-group members as Constant - none of
which were admitted, so every candidate was rejected.
Fix, mirroring the existing structure of the filter:
- Struct and Interface join the multi-line container whitelist,
- Constant joins Variable on the one-liner-exempt path (const-group
members are one-liners by nature, exactly like the module-level
variables that path already serves).
The Python suite shows no attribution change
(test/solidlsp/python/test_symbol_retrieval.py: 80 passed). Languages
whose containers use the added kinds (e.g. Rust and C structs,
TypeScript interfaces) get the same admission; verified for Go and
Python only.
Regression coverage: new Go fixture containment_sample.go with a
const-iota group, a struct field and an interface method all
referencing a named type; four tests assert containing-symbol and
referencing-symbol attribution, and all four fail against the
pre-change ls.py (verified). Full go-marker suite: 21 passed,
1 skipped.
An independently-started LSP process is already its own POSIX process group leader
(start_new_session=True), so cleanup can signal that known group directly via os.killpg
instead of walking the tree with psutil.Process.children(recursive=True), which requires
system-wide process-table enumeration that can be denied even for processes we started
and own (#1818).
Adds support for Wolfram Language (.wl, .wls files) using the official
WolframResearch LSPServer paclet, which communicates via stdio.
Requires Wolfram Mathematica 13.0+ or Wolfram Engine 12.1+.
The WolframKernel is located via WOLFRAM_PATH, the system PATH, common
install locations, or ls_path in ls_specific_settings.
Includes language server implementation, test repo, test suite
(skipped gracefully when WolframKernel is unavailable), and
documentation updates.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
* fix(gitignore): escape directory names before interpolating into pattern position
GitignoreParser rescopes patterns from nested .gitignore files by prefixing the
containing directory's relative path, but interpolates that path into pattern
position without escaping it. A directory whose name contains a gitignore
metacharacter (*, ?, [, ], !, #) is then read as glob syntax instead of a
literal name, so a directory like a stray "***" venv can turn a scoped
pattern into one matching most or all of the project.
Fixes#1806
* fix(gitignore): build patterns with '/' only, never os.sep/os.path.join
The escaping added in the prior commit inserts literal backslash escape
characters before pattern metacharacters. On Windows, os.sep is also
backslash, so os.path.join(rel_dir_pattern, line) mixed path-separator
backslashes with escape backslashes, and the pre-existing trailing
`.replace(os.sep, "/")` normalization step then converted ALL of them
indiscriminately -- turning an escaped 'a\[1\]' into 'a/[1/]', which no
longer matches the literal directory name.
Verified with ntpath (Windows path semantics) on this Linux session:
simulating a Windows relpath through the old os.sep/os.path.join/replace
chain produces 'a/[1/]/mod.py', which pathspec does not match against
'a[1]/mod.py'; the new '/'-only construction produces 'a\[1\]/mod.py',
which matches. Fixed by normalizing rel_dir to '/' immediately and joining
every pattern component with a literal '/', removing the now-unneeded (and
on Windows actively harmful) blanket separator replace.
Also marks the two directory-creation regression tests that use '*'/'?'
literally in a directory name as Windows-skipped (those characters are
illegal in Windows filenames, so the directories cannot be created there --
a platform limitation of the test, not the fix), and adds a pure-function
test for _escape_gitignore_path_component so the '*'/'?' escaping logic
still has cross-platform coverage.
Found via this PR's own Windows CI run (jobs 91787560091/91787560092/91787560183
on run 30843966565), which failed test_gitignore_dir_name_with_metachars_anchored_pattern
with an assertion failure (not the OSError the other two tests hit), before any
maintainer had looked at the PR.
The Nextflow language server does not scan the workspace when it says it does.
`LanguageService.initialize` -- which the "Initializing" progress notification we
wait for wraps -- only marks the workspace as unscanned and clears the AST cache.
The scan itself happens in `LanguageService.update0`, behind a 1s debounce, and
only on a round that finds no pending file change; a round that does find one
re-defers it via `updateLater`. The first `didOpen` of a session therefore pushes
the scan out by at least one further round, so a references request issued right
after it sees an AST cache holding nothing but the file it just opened and answers
with an empty list.
Force the scan rather than waiting for it. `completion` is one of the two requests
that call `updateNow` before consulting their provider, and
`DebouncingExecutor.executeNow` cancels the pending debounce and runs the update
synchronously on the request thread, so the response is not sent until that round
has finished. Two rounds suffice whatever the workspace size: the first drains the
pending file changes -- which is what re-defers the scan -- and the second finds
nothing pending, so it compiles the whole workspace before replying.
Polling the workspace symbol index instead would be unsound: `LanguageService.symbol`
neither awaits the update nor holds the monitor that `update` synchronises on. On a
generated 1050-file workspace, polling it made the server raise
`java.util.ConcurrentModificationException` from inside the scan, non-deterministically
(6, 3 and 0 times across three runs).
The existing cross-file reference test did not catch the empty results because the
`language_server` fixture is module-scoped: earlier tests in the class had already
opened main.nf and forced the server to compile it, so the assertion passed for a
reason that does not hold in a real session. Run alone, it failed. The added test
starts its own server and opens nothing but the defining file.
Verified on the 1050-file workspace, where the symbol has exactly 100 references:
without the fix 0 are found, with it all 100, in 2.5s.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Metals serves one build per workspace folder, and Serena sends only the
repository root. Where the builds live below the root, Metals falls back to
its own search (BuildTools.searchForBuildTool), which looks one level down
and takes the *first* match — so in a monorepo every build but that one is
served with no build target, and cross-file references silently come back
empty.
Detect the build roots instead and send them all, one Metals service each.
`ls_specific_settings.scala.project_roots` names them explicitly where the
detection guesses wrong, `project_root_scan_depth` bounds the search.
The configured `ls_workspace_folders` are not usable for this: they are about
what SolidLSP indexes and are shared by every language server of a project, so
in a polyglot monorepo no single value suits both Metals and, say, tsserver.
Where the repository root is itself a build root, nothing changes.
The Nextflow language server schedules a debounced (1s) AST update on every
didOpen/didChange, and LanguageService.references is the only request that does
not await it -- documentSymbol, codeLens, documentLink and semanticTokensFull
all do. A references request issued inside that window races the recompile of
the file it asks about and can return an empty list, which is what the macOS CI
runner hit. Send a documentSymbol request for the same file first, which blocks
server-side until the pending update has been applied.
Also compare reference paths using the platform separator in the tests, since
relativePath is OS-native and the hardcoded forward slashes failed on Windows.
* scala: answer Metals' build-import prompt
Metals asks, via window/showMessageRequest, whether to import a workspace it
has not seen before. Serena registered no handler, so the request came back
`method 'window/showMessageRequest' not handled on client` and Metals logged
"Unexpected error initializing server" and gave up. No build server, no build
target, and every cross-file query answered by the fallback presentation
compiler — which cannot see past the file it is given.
A client cannot decline the question instead: Metals' `disableShowMessageRequest`
is server-side configuration, and its no-op fallback answers "Not now", which
imports nothing either. Answering is the only route to a build.
So answer the three prompts that lead to a build server, and dismiss anything
else with `null` — a prompt we do not recognise is one whose consequences we
cannot judge, and two of Metals' others offer to kill a process and to open a
window. "Don't show again" is never chosen; Metals persists that in the
project's own state.
Since answering yes lets Metals run the project's build tool,
`ls_specific_settings.scala.auto_import_build: false` declines instead.
* scala: name the build-tool choice as a gap, and test the setting
`Messages.ChooseBuildTool` ("Multiple build definitions found. Which would
you like to use?") offers the build tools' own executable names, and precedes
the import prompt wherever a workspace holds more than one kind of build. It
is dismissed like anything else unrecognised, so such a workspace is still
not imported — a deliberate choice, since picking one is a guess of a
different order, but one the comment and the setup guide should admit to
rather than claim every prompt on the path is answered.
Also: cover the route from `ls_specific_settings` to `auto_import_build`,
which nothing exercised, and correct the `ImportBuildChanges` message in the
fixtures, which had the notification variant's trailing full stop rather than
the request's own text.
* feat(kotlin): update language server to 262.9593.0
Note: download URL structure differs depending on version
---------
Co-authored-by: Dominik Jain <dominik.jain@oraios-ai.de>
Add a Deno language server backed by the Deno CLI's built-in `deno lsp`,
serving TypeScript/JavaScript in Deno projects. Unlike the plain
typescript-language-server it understands Deno module resolution
(npm:/jsr:/https: imports) and the `Deno.*` global namespace.
It is experimental and must be selected explicitly via
`language_servers: [deno]`; it overlaps the TypeScript server on file
extensions, so it is not auto-detected. Requires the `deno` CLI on PATH.
Tests run in CI: the `deno` marker joins the other-langs batch, which now
installs Deno via denoland/setup-deno. Off-CI and wherever the CLI is
absent they skip through the central conftest guard, following the existing
pattern for toolchain-gated language servers. Hover is covered explicitly,
including hover on a `Deno.*` global, which is the capability this server
adds over the TypeScript one.