Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ Serena incorporates a powerful abstraction layer for the integration of language
The underlying language servers are typically open-source projects or at least freely available for use.

When using Serena's language server backend, we provide **support for over 40 programming languages**, including
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.
AL, Ansible, Bash, C#, C/C++, Clojure, Crystal, Dart, Elixir, Elm, Erlang, Fortran, F#, Gleam, 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.

### The Serena JetBrains Plugin

Expand Down
2 changes: 2 additions & 0 deletions docs/01-about/020_programming-languages.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,8 @@ Some languages require additional installations or setup steps, as noted.
(requires Elixir installation; Expert language server is downloaded automatically)
* **Elm**
(requires Elm compiler)
* **Gleam**
(requires [Gleam](https://gleam.run/getting-started/installing/) compiler; uses the built-in language server via `gleam lsp`)
* **Erlang**
(requires installation of beam and [erlang_ls](https://github.com/erlang-ls/erlang_ls); experimental, might be slow or hang)
* **F#**
Expand Down
1 change: 1 addition & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -326,6 +326,7 @@ markers = [
"luau: language server running for Luau",
"nix: language server running for Nix",
"dart: language server running for Dart",
"gleam: language server running for Gleam",
"erlang: language server running for Erlang",
"ocaml: language server running for OCaml and Reason",
"scala: language server running for Scala",
Expand Down
135 changes: 135 additions & 0 deletions src/solidlsp/language_servers/gleam_language_server.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
"""
Provides Gleam specific instantiation of the LanguageServer class.
Uses the language server built into the Gleam compiler (gleam lsp).
"""

import logging
import os
import pathlib
import shutil
import threading

from overrides import override

from solidlsp.ls import SolidLanguageServer
from solidlsp.ls_config import LanguageServerConfig
from solidlsp.lsp_protocol_handler.lsp_types import InitializeParams
from solidlsp.lsp_protocol_handler.server import ProcessLaunchInfo
from solidlsp.settings import SolidLSPSettings

log = logging.getLogger(__name__)


class GleamLanguageServer(SolidLanguageServer):
"""
Provides Gleam specific instantiation of the LanguageServer class.

Uses the language server built into the Gleam compiler, invoked via ``gleam lsp``.
Requires the Gleam compiler to be installed and available on PATH.
See https://gleam.run/getting-started/installing/ for installation instructions.
"""

def __init__(self, config: LanguageServerConfig, repository_root_path: str, solidlsp_settings: SolidLSPSettings):
gleam_path = shutil.which("gleam")
if gleam_path is None:
raise RuntimeError(
"Gleam is not installed or not in PATH.\n"
"Please install Gleam from https://gleam.run/getting-started/installing/\n"
"and make sure the 'gleam' binary is available on your PATH."
)
super().__init__(
config,
repository_root_path,
ProcessLaunchInfo(cmd=[gleam_path, "lsp"], cwd=repository_root_path),
"gleam",
solidlsp_settings,
)
self.server_ready = threading.Event()

@override
def is_ignored_dirname(self, dirname: str) -> bool:
return super().is_ignored_dirname(dirname) or dirname in ["build", "_gleam_artefacts"]

@staticmethod
def _get_initialize_params(repository_absolute_path: str) -> InitializeParams:
root_uri = pathlib.Path(repository_absolute_path).as_uri()
initialize_params = {
"locale": "en",
"capabilities": {
"textDocument": {
"synchronization": {"didSave": True, "dynamicRegistration": True},
"definition": {"dynamicRegistration": True, "linkSupport": True},
"references": {"dynamicRegistration": True},
"documentSymbol": {
"dynamicRegistration": True,
"hierarchicalDocumentSymbolSupport": True,
"symbolKind": {"valueSet": list(range(1, 27))},
},
"completion": {
"dynamicRegistration": True,
"completionItem": {
"snippetSupport": True,
"documentationFormat": ["markdown", "plaintext"],
},
},
"hover": {
"dynamicRegistration": True,
"contentFormat": ["markdown", "plaintext"],
},
"codeAction": {
"dynamicRegistration": True,
"codeActionLiteralSupport": {
"codeActionKind": {
"valueSet": ["quickfix", "refactor", "source"],
}
},
},
},
"workspace": {
"workspaceFolders": True,
"didChangeConfiguration": {"dynamicRegistration": True},
"configuration": True,
},
},
"processId": os.getpid(),
"rootPath": repository_absolute_path,
"rootUri": root_uri,
"workspaceFolders": [
{
"uri": root_uri,
"name": os.path.basename(repository_absolute_path),
}
],
}
return initialize_params # type: ignore[return-value]

def _start_server(self) -> None:
"""Start the Gleam language server process."""

def register_capability_handler(_params: dict) -> None:
return

def window_log_message(msg: dict) -> None:
log.info(f"LSP: window/logMessage: {msg}")

def do_nothing(_params: dict) -> None:
return

self.server.on_request("client/registerCapability", register_capability_handler)
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 Gleam language server process")
self.server.start()
initialize_params = self._get_initialize_params(self.repository_root_path)

log.info("Sending initialize request from LSP client to LSP server and awaiting response")
init_response = self.server.send.initialize(initialize_params)

capabilities = init_response["capabilities"]
log.info(f"Gleam language server capabilities: {list(capabilities.keys())}")
assert "textDocumentSync" in capabilities, "textDocumentSync capability missing"

self.server.notify.initialized({})
self.server_ready.set()
7 changes: 7 additions & 0 deletions src/solidlsp/ls_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ class Language(str, Enum):
Supports .luau files. Configure via .luaurc in the project root.
"""
NIX = "nix"
GLEAM = "gleam"
ERLANG = "erlang"
OCAML = "ocaml"
AL = "al"
Expand Down Expand Up @@ -262,6 +263,8 @@ def get_source_fn_matcher(self) -> FilenameMatcher:
return FilenameMatcher("*.luau")
case self.NIX:
return FilenameMatcher("*.nix")
case self.GLEAM:
return FilenameMatcher("*.gleam")
case self.ERLANG:
return FilenameMatcher("*.erl", "*.hrl", "*.escript", "*.config", "*.app", "*.app.src")
case self.OCAML:
Expand Down Expand Up @@ -468,6 +471,10 @@ def get_ls_class(self) -> type["SolidLanguageServer"]:

return LuauLanguageServer

case self.GLEAM:
from solidlsp.language_servers.gleam_language_server import GleamLanguageServer

return GleamLanguageServer
case self.ERLANG:
from solidlsp.language_servers.erlang_language_server import ErlangLanguageServer

Expand Down