mirror of
https://github.com/tiennm99/serena.git
synced 2026-10-14 00:16:57 +00:00
Add Wolfram Language support via official LSPServer paclet (#1108)
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>
This commit is contained in:
12 files changed
+309
-13
No files matched your search
@@ -6,17 +6,17 @@ project_name: "project_name"
|
||||
# bsl clojure cpp cpp_ccls crystal
|
||||
# csharp csharp_omnisharp cue dart deno
|
||||
# elixir elm erlang fortran fsharp
|
||||
# gdscript go groovy haskell haxe
|
||||
# hlsl html java json julia
|
||||
# kotlin latex lean4 lua luau
|
||||
# markdown matlab msl nextflow nix
|
||||
# ocaml pascal perl php php_phpactor
|
||||
# php_phpantom powershell python python_basedpyright python_jedi
|
||||
# python_pyrefly python_ty qml r rego
|
||||
# ruby ruby_solargraph rust scala scss
|
||||
# solidity svelte swift systemverilog terraform
|
||||
# toml typescript typescript_vts vue yaml
|
||||
# zig
|
||||
# gdscript gleam go groovy haskell
|
||||
# haxe hlsl html java json
|
||||
# julia kotlin latex lean4 lua
|
||||
# luau markdown matlab msl nextflow
|
||||
# nix ocaml pascal perl php
|
||||
# php_phpactor php_phpantom powershell python python_basedpyright
|
||||
# python_jedi python_pyrefly python_ty qml r
|
||||
# rego ruby ruby_solargraph rust scala
|
||||
# scss solidity svelte swift systemverilog
|
||||
# terraform toml typescript typescript_vts vue
|
||||
# wolfram yaml zig
|
||||
# (This list may be outdated; generated with scripts/print_language_list.py;
|
||||
# For the current list, see values of the LanguageServerId enum here:
|
||||
# https://github.com/oraios/serena/blob/main/src/solidlsp/ls_config.py)
|
||||
|
||||
@@ -0,0 +1,183 @@
|
||||
"""Wolfram Language server integration using the official WolframResearch LSPServer paclet."""
|
||||
|
||||
import glob
|
||||
import logging
|
||||
import os
|
||||
import platform
|
||||
import shutil
|
||||
|
||||
from solidlsp.ls import LanguageServerDependencyProvider, LanguageServerDependencyProviderSinglePath, SolidLanguageServer
|
||||
from solidlsp.ls_config import LanguageServerConfig
|
||||
from solidlsp.settings import SolidLSPSettings
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
WOLFRAM_PATH_ENV_VAR = "WOLFRAM_PATH"
|
||||
|
||||
|
||||
class WolframLanguageServer(SolidLanguageServer):
|
||||
"""
|
||||
Wolfram Language server using the official WolframResearch LSPServer paclet
|
||||
(https://github.com/WolframResearch/LSPServer), which is bundled with Wolfram
|
||||
installations and communicates via stdio.
|
||||
|
||||
Requires Wolfram Mathematica 13.0+ or Wolfram Engine 12.1+.
|
||||
The WolframKernel executable is discovered via the ``ls_path`` entry in
|
||||
``ls_specific_settings.wolfram``, the ``WOLFRAM_PATH`` environment variable
|
||||
(pointing to the executable or the installation directory), the system PATH,
|
||||
or common installation locations.
|
||||
"""
|
||||
|
||||
def __init__(self, config: LanguageServerConfig, repository_root_path: str, solidlsp_settings: SolidLSPSettings):
|
||||
super().__init__(config, repository_root_path, None, "wolfram", solidlsp_settings)
|
||||
|
||||
def _create_dependency_provider(self) -> LanguageServerDependencyProvider:
|
||||
return self.DependencyProvider(self._custom_settings, self._ls_resources_dir)
|
||||
|
||||
class DependencyProvider(LanguageServerDependencyProviderSinglePath):
|
||||
def _get_or_install_core_dependency(self) -> str:
|
||||
"""
|
||||
Discover the WolframKernel executable.
|
||||
|
||||
:return: path to the WolframKernel executable
|
||||
:raises FileNotFoundError: if WolframKernel cannot be found
|
||||
"""
|
||||
return _find_wolfram_kernel()
|
||||
|
||||
def _create_launch_command(self, core_path: str) -> list[str]:
|
||||
return [core_path, "-noprompt", "-noinit", "-run", 'Needs["LSPServer`"];LSPServer`StartServer[]']
|
||||
|
||||
def _create_base_initialize_params(self) -> dict:
|
||||
return {
|
||||
"capabilities": {
|
||||
"workspace": {"workspaceFolders": True},
|
||||
"textDocument": {
|
||||
"definition": {"dynamicRegistration": True},
|
||||
"references": {"dynamicRegistration": True},
|
||||
"documentSymbol": {
|
||||
"dynamicRegistration": True,
|
||||
"hierarchicalDocumentSymbolSupport": True,
|
||||
},
|
||||
"hover": {"dynamicRegistration": True, "contentFormat": ["markdown", "plaintext"]},
|
||||
"formatting": {"dynamicRegistration": True},
|
||||
"publishDiagnostics": {"relatedInformation": True},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
def _start_server(self) -> None:
|
||||
def do_nothing(params: dict) -> None:
|
||||
return
|
||||
|
||||
def window_log_message(msg: dict) -> None:
|
||||
log.info(f"Wolfram LSP: window/logMessage: {msg}")
|
||||
|
||||
self.server.on_request("client/registerCapability", do_nothing)
|
||||
self.server.on_notification("window/logMessage", window_log_message)
|
||||
self.server.on_notification("$/progress", do_nothing)
|
||||
self.server.on_notification("textDocument/publishDiagnostics", do_nothing)
|
||||
|
||||
log.info("Starting Wolfram LSPServer process")
|
||||
self.server.start()
|
||||
|
||||
initialize_params = self._create_initialize_params()
|
||||
init_response = self.server.send.initialize(initialize_params)
|
||||
|
||||
log.info(f"Wolfram LSP capabilities: {list(init_response.get('capabilities', {}).keys())}")
|
||||
|
||||
self.server.notify.initialized({})
|
||||
log.info("Wolfram LSPServer initialized and ready.")
|
||||
|
||||
|
||||
def _find_wolfram_kernel() -> str:
|
||||
"""Find the WolframKernel executable via WOLFRAM_PATH, the system PATH, or common install locations."""
|
||||
# 1. WOLFRAM_PATH environment variable (executable or installation directory)
|
||||
env_path = os.environ.get(WOLFRAM_PATH_ENV_VAR)
|
||||
if env_path:
|
||||
if os.path.isfile(env_path) and os.access(env_path, os.X_OK):
|
||||
log.info(f"Using WolframKernel from {WOLFRAM_PATH_ENV_VAR}: {env_path}")
|
||||
return env_path
|
||||
kernel_in_dir = _find_kernel_in_install_dir(env_path)
|
||||
if kernel_in_dir:
|
||||
log.info(f"Using WolframKernel from {WOLFRAM_PATH_ENV_VAR} directory: {kernel_in_dir}")
|
||||
return kernel_in_dir
|
||||
|
||||
# 2. System PATH
|
||||
kernel_path = shutil.which("WolframKernel")
|
||||
if kernel_path:
|
||||
log.info(f"Using WolframKernel from PATH: {kernel_path}")
|
||||
return kernel_path
|
||||
|
||||
# 3. Common installation locations
|
||||
system = platform.system()
|
||||
search_locations: list[str] = []
|
||||
|
||||
if system == "Darwin":
|
||||
search_locations = [
|
||||
"/Applications/Mathematica.app/Contents/MacOS/WolframKernel",
|
||||
"/Applications/Wolfram.app/Contents/MacOS/WolframKernel",
|
||||
"/Applications/Wolfram Engine.app/Contents/MacOS/WolframKernel",
|
||||
]
|
||||
for pattern in [
|
||||
"/Applications/Mathematica*.app/Contents/MacOS/WolframKernel",
|
||||
"/Applications/Wolfram*.app/Contents/MacOS/WolframKernel",
|
||||
]:
|
||||
search_locations.extend(sorted(glob.glob(pattern), reverse=True))
|
||||
|
||||
elif system == "Linux":
|
||||
search_locations = [
|
||||
"/usr/local/bin/WolframKernel",
|
||||
"/usr/bin/WolframKernel",
|
||||
]
|
||||
for pattern in [
|
||||
"/usr/local/Wolfram/Mathematica/*/Executables/WolframKernel",
|
||||
"/usr/local/Wolfram/WolframEngine/*/Executables/WolframKernel",
|
||||
"/opt/Wolfram/Mathematica/*/Executables/WolframKernel",
|
||||
]:
|
||||
search_locations.extend(sorted(glob.glob(pattern), reverse=True))
|
||||
|
||||
elif system == "Windows":
|
||||
for pattern in [
|
||||
"C:\\Program Files\\Wolfram Research\\Mathematica\\*\\WolframKernel.exe",
|
||||
"C:\\Program Files\\Wolfram Research\\Wolfram Engine\\*\\WolframKernel.exe",
|
||||
]:
|
||||
search_locations.extend(sorted(glob.glob(pattern), reverse=True))
|
||||
|
||||
for location in search_locations:
|
||||
if os.path.isfile(location) and os.access(location, os.X_OK):
|
||||
log.info(f"Found WolframKernel at: {location}")
|
||||
return location
|
||||
|
||||
raise FileNotFoundError(
|
||||
"WolframKernel not found. Please either:\n"
|
||||
f"1. Set the {WOLFRAM_PATH_ENV_VAR} environment variable to your Wolfram installation\n"
|
||||
"2. Add WolframKernel to your system PATH\n"
|
||||
"3. Configure ls_path in ls_specific_settings.wolfram\n"
|
||||
"4. Install Wolfram Mathematica (13.0+) or Wolfram Engine (12.1+) from https://www.wolfram.com/"
|
||||
)
|
||||
|
||||
|
||||
def _find_kernel_in_install_dir(install_dir: str) -> str | None:
|
||||
"""Try to locate WolframKernel within a Wolfram installation directory."""
|
||||
system = platform.system()
|
||||
|
||||
if system == "Darwin":
|
||||
candidates = [
|
||||
os.path.join(install_dir, "Contents", "MacOS", "WolframKernel"),
|
||||
os.path.join(install_dir, "MacOS", "WolframKernel"),
|
||||
]
|
||||
elif system == "Windows":
|
||||
candidates = [
|
||||
os.path.join(install_dir, "WolframKernel.exe"),
|
||||
]
|
||||
else:
|
||||
candidates = [
|
||||
os.path.join(install_dir, "Executables", "WolframKernel"),
|
||||
os.path.join(install_dir, "WolframKernel"),
|
||||
]
|
||||
|
||||
for candidate in candidates:
|
||||
if os.path.isfile(candidate) and os.access(candidate, os.X_OK):
|
||||
return candidate
|
||||
|
||||
return None
|
||||
@@ -196,6 +196,11 @@ class LanguageServerId(str, Enum):
|
||||
it reports no symbols for them, so they are not treated as source files here).
|
||||
Automatically downloads the language server JAR; requires Java 17+ (JAVA_HOME or 'java' on PATH).
|
||||
"""
|
||||
WOLFRAM = "wolfram"
|
||||
"""Wolfram Language server using the official WolframResearch LSPServer paclet.
|
||||
Requires Wolfram Mathematica 13.0+ or Wolfram Engine 12.1+.
|
||||
Set WOLFRAM_PATH environment variable or configure ls_path in ls_specific_settings.
|
||||
"""
|
||||
# Experimental or deprecated Language Servers
|
||||
TYPESCRIPT_VTS = "typescript_vts"
|
||||
"""Use the typescript language server through the natively bundled vscode extension via https://github.com/yioneko/vtsls"""
|
||||
@@ -598,6 +603,8 @@ class LanguageServerId(str, Enum):
|
||||
# only scripts: the language server does have a service for .config files, but it provides
|
||||
# no symbols for them, so treating them as source files would only pollute the symbol index
|
||||
return FilenameMatcher(".nf")
|
||||
case self.WOLFRAM:
|
||||
return FilenameMatcher(".wl", ".wls")
|
||||
case self.HTML:
|
||||
return FilenameMatcher(".html", ".htm")
|
||||
case self.SCSS:
|
||||
@@ -901,6 +908,10 @@ class LanguageServerId(str, Enum):
|
||||
from solidlsp.language_servers.nextflow_language_server import NextflowLanguageServer
|
||||
|
||||
return NextflowLanguageServer
|
||||
case self.WOLFRAM:
|
||||
from solidlsp.language_servers.wolfram_language_server import WolframLanguageServer
|
||||
|
||||
return WolframLanguageServer
|
||||
case self.HTML:
|
||||
from solidlsp.language_servers.vscode_html_language_server import VsCodeHtmlLanguageServer
|
||||
|
||||
|
||||
Reference in new issue
Block a user