Skip to content

Commit ab67eca

Browse files
PiratesIRCclaudeMischaPanch
authored
feat: add mSL (mIRC Scripting Language) support (#1315)
* feat: add mSL (mIRC Scripting Language) support Add language server support for mIRC Scripting Language (.mrc files). mSL is used in mIRC and AdiIRC IRC clients for scripting bots, games, and automation. The implementation uses a custom Python-based LSP server (pygls) that parses aliases, events, menus, dialogs, and CTCP handlers. Dependencies (pygls, lsprotocol) are installed in an isolated venv on first use. Includes test repo, integration tests, and documentation updates. * style: apply ruff formatting to msl_language_server.py * fix: remove INITIALIZE handler that crashes pygls 2.x pygls 2.x handles the initialize request internally. Overriding it via @server.feature(lsp.INITIALIZE) causes the LSP subprocess to crash. Removing the handler lets pygls auto-advertise capabilities based on registered features (document_symbol, workspace_symbol). * fix: update embedded LSP script for pygls 2.x pygls 2.x moved LanguageServer from pygls.server to pygls.lsp.server. Update import and bump requirement from pygls>=1.3.0 to pygls>=2.0.0. * refactor: ship mSL LSP as package module, not runtime disk write Per maintainer feedback: the mSL LSP is a Python script, so it should ship as a module inside the package rather than being written to disk at runtime. Changes: - Extract embedded LSP script to msl_lsp_server.py (proper module) - Simplify MslLanguageServer: remove venv creation, disk writes, _create_msl_lsp_files(). DependencyProvider now just returns sys.executable and launches the sibling module directly. - Add pygls>=2.0.0 and lsprotocol>=2023.0.0 to pyproject.toml deps * fix: regenerate uv.lock after upstream v1.1.0 merge * feat(msl): add references, definitions, and expand test coverage - Add textDocument/references handler for cross-file alias reference finding - Add textDocument/definition handler for go-to-definition support - Handle $ prefix on cursor position for mSL identifiers - Fix pre-existing mypy error in agent.py (dict[str, object] -> dict[str, str | int]) - Add MSL to _LANGUAGE_PYTEST_MARKERS in conftest.py - Add MSL parametrized cases to test_find_symbol_stable and test_find_symbol_references_stable - Expand test repo with cross-file reference examples (main.mrc <-> utils.mrc) - Add within-file and cross-file reference tests to test_msl_basic.py - Update docs and CHANGELOG to reflect expanded capabilities Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * style: apply ruff formatting to msl_lsp_server.py Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(msl): add hover handler, fix cross-file references via filesystem scan Root causes of 4 CI test failures: - textDocument/hover was not implemented (test_find_symbol_stable needs it) - references/workspace_symbol/definition only searched opened documents, missing files not explicitly opened by the client - pygls.uris.to_fs_path returns lowercase drive letters on Windows, causing URI mismatch with the framework's repository path comparison Fixes: - Add textDocument/hover handler returning definition snippets in Markdown - Add _get_all_mrc_files() to scan workspace filesystem for all .mrc files - Read workspace roots from server.workspace (pygls 2.x API) instead of broken monkey-patching of server.lsp - Use pathlib.Path.resolve() to normalize drive letter casing on Windows - Simplify __main__ to just server.start_io() All 12 tests pass locally (10 solidlsp + 2 serena_agent integration). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * MSL language: removed unnecessary Dep. provider * Changelog * deps --------- Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> Co-authored-by: Michael Panchenko <michael.panchenko@oraios-ai.de>
1 parent 90c30e0 commit ab67eca

14 files changed

Lines changed: 842 additions & 1 deletion

File tree

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,9 @@ Status of the `main` branch. Changes prior to the next official version change w
55
* General:
66
- Support environment variable `SERENA_USAGE_REPORTING` (set to `false` to disable usage reporting)
77

8+
* Language Servers:
9+
- Add mSL (mIRC Scripting Language) support (custom pygls-based language server; symbols, references, definitions)
10+
811
# 1.1.1
912

1013
* General:
@@ -16,6 +19,7 @@ Status of the `main` branch. Changes prior to the next official version change w
1619
* Language Servers:
1720
- Fix Dart LSP returning only symbol name as body instead of full method body.
1821

22+
1923
# 1.1.0
2024

2125
* General:

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ Serena incorporates a powerful abstraction layer for the integration of language
7171
The underlying language servers are typically open-source projects or at least freely available for use.
7272

7373
When using Serena's language server backend, we provide **support for over 40 programming languages**, including
74-
AL, Ansible, Bash, C#, C/C++, Clojure, Crystal, Dart, Elixir, Elm, Erlang, Fortran, F#, GLSL, Go, Groovy, Haskell, Haxe, HLSL, Java, JavaScript, Julia, Kotlin, Lean 4, Lua, Luau, Markdown, MATLAB, Nix, OCaml, Perl, PHP, PowerShell, Python, R, Ruby, Rust, Scala, Solidity, Swift, TOML, TypeScript, WGSL, YAML, and Zig.
74+
AL, Ansible, Bash, C#, C/C++, Clojure, Crystal, Dart, Elixir, Elm, Erlang, Fortran, F#, GLSL, Go, Groovy, Haskell, Haxe, HLSL, Java, JavaScript, Julia, Kotlin, Lean 4, Lua, Luau, Markdown, MATLAB, mSL, Nix, OCaml, Perl, PHP, PowerShell, Python, R, Ruby, Rust, Scala, Solidity, Swift, TOML, TypeScript, WGSL, YAML, and Zig.
7575

7676
### The Serena JetBrains Plugin
7777

docs/01-about/020_programming-languages.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,9 @@ Some languages require additional installations or setup steps, as noted.
8484
* **Luau**
8585
* **Markdown**
8686
(must explicitly enable language `markdown`, primarily useful for documentation-heavy projects)
87+
* **mSL** (mIRC Scripting Language)
88+
(auto-installed; no external dependencies required — uses a custom pygls-based LSP server shipped with Serena;
89+
supports document symbols, workspace symbols, references, and go-to-definition for aliases, events, menus, dialogs, and CTCP handlers in `.mrc` files)
8790
* **Nix**
8891
(requires nixd installation)
8992
* **OCaml**

pyproject.toml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,8 @@ dependencies = [
3838
"beautifulsoup4==4.14.2",
3939
"oraios-pywebview==6.2",
4040
"pystray==0.19.5",
41+
"pygls==2.1.1", # used to implement an msl language server
42+
"lsprotocol==2025.0.0",
4143
# Transitive deps pinned for security (dependabot alerts).
4244
# Exact pins because uvx installs from git, ignoring the lock file.
4345
"urllib3==2.6.3",
@@ -344,6 +346,7 @@ markers = [
344346
"lean4: language server running for Lean 4",
345347
"solidity: language server running for Solidity (uses @nomicfoundation/solidity-language-server)",
346348
"ansible: language server running for Ansible (uses @ansible/ansible-language-server)",
349+
"msl: language server running for mSL (mIRC Scripting Language)",
347350
]
348351

349352
[tool.codespell]
Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
"""
2+
Provides mSL (mIRC Scripting Language) specific instantiation of the LanguageServer class.
3+
Uses a custom Python-based LSP server (pygls) for parsing .mrc files.
4+
5+
The LSP server script is shipped as ``msl_lsp_server.py`` alongside this module
6+
and launched as a subprocess using the current Python interpreter.
7+
"""
8+
9+
import logging
10+
import os
11+
import pathlib
12+
import sys
13+
import threading
14+
15+
from solidlsp.ls import (
16+
SolidLanguageServer,
17+
)
18+
from solidlsp.ls_config import LanguageServerConfig
19+
from solidlsp.lsp_protocol_handler.lsp_types import InitializeParams
20+
from solidlsp.lsp_protocol_handler.server import ProcessLaunchInfo
21+
from solidlsp.settings import SolidLSPSettings
22+
23+
log = logging.getLogger(__name__)
24+
25+
_MSL_LSP_SCRIPT = os.path.join(os.path.dirname(__file__), "msl_lsp_server.py")
26+
27+
28+
class MslLanguageServer(SolidLanguageServer):
29+
"""
30+
Provides mSL (mIRC Scripting Language) specific instantiation of the LanguageServer class.
31+
Uses a Python-based LSP server for parsing .mrc files (aliases, events, menus, dialogs).
32+
"""
33+
34+
def __init__(self, config: LanguageServerConfig, repository_root_path: str, solidlsp_settings: SolidLSPSettings):
35+
"""
36+
Creates an MslLanguageServer instance. This class is not meant to be instantiated directly.
37+
Use LanguageServer.create() instead.
38+
"""
39+
process_launch_info = ProcessLaunchInfo(cmd=[sys.executable, _MSL_LSP_SCRIPT], cwd=repository_root_path)
40+
super().__init__(
41+
config,
42+
repository_root_path,
43+
process_launch_info=process_launch_info,
44+
language_id="msl",
45+
solidlsp_settings=solidlsp_settings,
46+
)
47+
self.server_ready = threading.Event()
48+
49+
@staticmethod
50+
def _get_initialize_params(repository_absolute_path: str) -> InitializeParams:
51+
"""Returns the initialize params for the mSL Language Server."""
52+
root_uri = pathlib.Path(repository_absolute_path).as_uri()
53+
initialize_params = {
54+
"locale": "en",
55+
"capabilities": {
56+
"textDocument": {
57+
"synchronization": {"didSave": True, "dynamicRegistration": True},
58+
"documentSymbol": {
59+
"dynamicRegistration": True,
60+
"hierarchicalDocumentSymbolSupport": True,
61+
"symbolKind": {"valueSet": list(range(1, 27))},
62+
},
63+
"hover": {"dynamicRegistration": True, "contentFormat": ["markdown", "plaintext"]},
64+
"references": {"dynamicRegistration": True},
65+
"definition": {"dynamicRegistration": True},
66+
},
67+
"workspace": {
68+
"workspaceFolders": True,
69+
"symbol": {"dynamicRegistration": True},
70+
},
71+
},
72+
"processId": os.getpid(),
73+
"rootPath": repository_absolute_path,
74+
"rootUri": root_uri,
75+
"workspaceFolders": [{"uri": root_uri, "name": os.path.basename(repository_absolute_path)}],
76+
}
77+
return initialize_params # type: ignore
78+
79+
def _start_server(self) -> None:
80+
"""Starts the mSL Language Server."""
81+
82+
def window_log_message(msg: dict) -> None:
83+
log.info(f"LSP: window/logMessage: {msg}")
84+
self.server_ready.set()
85+
86+
def do_nothing(params: dict) -> None:
87+
pass
88+
89+
self.server.on_notification("window/logMessage", window_log_message)
90+
self.server.on_notification("textDocument/publishDiagnostics", do_nothing)
91+
92+
log.info("Starting mSL server process")
93+
self.server.start()
94+
initialize_params = self._get_initialize_params(self.repository_root_path)
95+
96+
log.info("Sending initialize request to mSL LSP server")
97+
init_response = self.server.send.initialize(initialize_params)
98+
log.debug(f"Received initialize response: {init_response}")
99+
100+
self.server.notify.initialized({})
101+
102+
# Wait briefly for server readiness
103+
if not self.server_ready.wait(timeout=2.0):
104+
log.info("Timeout waiting for mSL server ready signal, proceeding anyway")
105+
self.server_ready.set()
106+
107+
log.info("mSL server initialization complete")

0 commit comments

Comments
 (0)