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
18 changes: 18 additions & 0 deletions .github/workflows/pytest.yml
Original file line number Diff line number Diff line change
Expand Up @@ -506,6 +506,24 @@ jobs:
FPC_BIN_DIR=$(dirname "$FPC_EXE")
echo "$FPC_BIN_DIR" >> $GITHUB_PATH
fi
- name: Install COBOL Compiler (GnuCOBOL)
shell: bash
run: |
if [[ "${{ runner.os }}" == "Linux" ]]; then
sudo apt-get update
sudo apt-get install -y gnucobol
# Verify installation
cobc --version || echo "WARNING: GnuCOBOL installation may have failed"
elif [[ "${{ runner.os }}" == "macOS" ]]; then
brew install gnucobol
# Verify installation
cobc --version || echo "WARNING: GnuCOBOL installation may have failed"
elif [[ "${{ runner.os }}" == "Windows" ]]; then
# On Windows, we'll skip COBOL tests as GnuCOBOL installation is complex
# and enterprise COBOL LSPs (IBM Z Open Editor) require VSCode extensions
echo "Skipping COBOL installation on Windows"
echo "SKIP_COBOL_TESTS=true" >> $GITHUB_ENV
fi
- name: Verify FPC installation
shell: bash
run: |
Expand Down
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ Status of the `main` branch. Changes prior to the next official version change w
* Language support:

* **Add support for Lean 4** via built-in `lean --server` with cross-file reference support (requires `lean` and `lake` via [elan](https://github.com/leanprover/elan))
* **Add support for COBOL** via COBOL language servers (requires GnuCOBOL compiler or enterprise language servers like IBM Z Open Editor or Eclipse Che4z; some setups may need `ls_path` configuration)
* **Add support for OCaml** via ocaml-lsp-server with cross-file reference support on OCaml 5.2+ (requires opam; see [setup guide](docs/03-special-guides/ocaml_setup_guide_for_serena.md))
* **Add Phpactor as alternative PHP language server** (specify `php_phpactor` as language; requires PHP 8.1+)
* **Add support for Fortran** via fortls language server (requires `pip install fortls`)
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ that implement the language server protocol (LSP).
The underlying language servers are typically open-source projects (like Serena) or at least freely available for use.

With Serena's LSP library, we provide **support for over 40 programming languages**, including
AL, Ansible, Bash, C#, C/C++, Clojure, Dart, Elixir, Elm, Erlang, Fortran, F# (currently with some bugs), GLSL, Go, Groovy (partial support), Haskell, 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.
AL, Ansible, Bash, C#, C/C++, Clojure, COBOL, Dart, Elixir, Elm, Erlang, Fortran, F# (currently with some bugs), GLSL, Go, Groovy (partial support), Haskell, 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.

> [!IMPORTANT]
> Some language servers require additional dependencies to be installed; see the [Language Support](https://oraios.github.io/serena/01-about/020_programming-languages.html) page for details.
Expand Down
3 changes: 3 additions & 0 deletions docs/01-about/020_programming-languages.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,9 @@ Some languages require additional installations or setup steps, as noted.
for best results, provide a `compile_commands.json` at the repository root;
see the [C/C++ Setup Guide](../03-special-guides/cpp_setup) for details.)
* **Clojure**
* **COBOL**
(requires installation of a COBOL language server such as GnuCOBOL compiler or enterprise language servers like IBM Z Open Editor or Eclipse Che4z COBOL Language Support;
some language servers may require additional setup via `ls_path` configuration)
* **Dart**
* **Elixir**
(requires Elixir installation; Expert language server is downloaded automatically)
Expand Down
1 change: 1 addition & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -337,6 +337,7 @@ markers = [
"lean4: language server running for Lean 4",
"solidity: language server running for Solidity (uses @nomicfoundation/solidity-language-server)",
"ansible: language server running for Ansible (uses @ansible/ansible-language-server)",
"cobol: language server running for COBOL",
]

[tool.codespell]
Expand Down
203 changes: 203 additions & 0 deletions src/solidlsp/language_servers/cobol_language_server.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,203 @@
"""
COBOL Language Server implementation for Serena.

This module provides integration with a COBOL language server
supporting .cob, .cbl, and .cobol file extensions.
"""

import logging
import os
import shutil
from pathlib import Path

from solidlsp.ls import LanguageServerDependencyProviderSinglePath, SolidLanguageServer
from solidlsp.ls_config import LanguageServerConfig
from solidlsp.lsp_protocol_handler.lsp_types import InitializeParams
from solidlsp.settings import SolidLSPSettings

log = logging.getLogger(__name__)


class CobolLanguageServer(SolidLanguageServer):
"""
COBOL Language Server implementation.

Supports COBOL file extensions: .cob, .cbl, .cobol, .CBL, .COB

Configuration:
--------------
You can specify a custom path to the COBOL language server executable
using the 'ls_path' setting in your Serena configuration:

```yaml
language_servers:
cobol:
ls_path: '/path/to/cobol-language-server'
```

Supported Language Servers:
---------------------------
- IBM Z Open Editor language server
- Eclipse Che4z COBOL Language Support
- Other LSP-compliant COBOL language servers
"""

class DependencyProvider(LanguageServerDependencyProviderSinglePath):
"""Handles COBOL language server dependencies and launch command."""

def _get_or_install_core_dependency(self) -> str:
"""
Get or install the COBOL language server.

Returns:
Path to the COBOL language server executable

"""
# First, check if user provided a custom path via ls_path setting
custom_path = self._custom_settings.get("ls_path")
if custom_path and os.path.exists(custom_path):
log.info(f"Using custom COBOL language server at: {custom_path}")
return custom_path

# Check for system-installed COBOL language server
# Common names for COBOL language servers
possible_commands = [
"cobol-language-server",
"cobol-lsp",
"che4z-lsp-for-cobol",
]

for cmd in possible_commands:
system_path = shutil.which(cmd)
if system_path:
log.info(f"Found system-installed COBOL language server: {system_path}")
return system_path

# If no system installation found, check for IBM Z Open Editor
vscode_extensions = os.path.expanduser("~/.vscode/extensions")
if os.path.exists(vscode_extensions):
# Look for IBM Z Open Editor extension
for ext_dir in os.listdir(vscode_extensions):
if "ibm.zopeneditor" in ext_dir.lower():
ext_path = os.path.join(vscode_extensions, ext_dir)
# Look for language server JAR or executable
# This is a placeholder - actual path depends on extension structure
possible_paths = [
os.path.join(ext_path, "server", "cobol-language-server.jar"),
os.path.join(ext_path, "bin", "cobol-language-server"),
]
for path in possible_paths:
if os.path.exists(path):
log.info(f"Found COBOL language server in Z Open Editor: {path}")
return path

# If still not found, provide helpful error message
raise FileNotFoundError(
"COBOL language server not found. Please install one of the following:\n\n"
"1. IBM Z Open Editor (VSCode extension):\n"
" Install from VSCode Marketplace: 'IBM Z Open Editor'\n\n"
"2. Eclipse Che4z COBOL Language Support:\n"
" Install from VSCode Marketplace: 'COBOL Language Support'\n\n"
"3. Or specify a custom path in your Serena configuration:\n"
" language_servers:\n"
" cobol:\n"
" ls_path: '/path/to/your/cobol-language-server'\n"
)

def _create_launch_command(self, core_path: str) -> list[str]:
"""
Create the launch command for the COBOL language server.

Args:
core_path: Path to the COBOL language server executable or JAR

Returns:
Command to launch the language server

"""
# Handle Java-based language servers (like IBM Z Open Editor)
if core_path.endswith(".jar"):
java_path = shutil.which("java")
if not java_path:
raise RuntimeError("Java is required to run the COBOL language server but was not found in PATH")
return [java_path, "-jar", core_path, "--stdio"]

# Handle executable-based language servers
return [core_path, "--stdio"]

def __init__(self, config: LanguageServerConfig, repository_root_path: str, solidlsp_settings: SolidLSPSettings):
"""Initialize the COBOL language server."""
super().__init__(
config,
repository_root_path,
None,
"cobol",
solidlsp_settings,
)

def _create_dependency_provider(self) -> LanguageServerDependencyProviderSinglePath:
"""Create the dependency provider for the COBOL language server."""
return self.DependencyProvider(self._custom_settings, self._ls_resources_dir)

def _get_initialize_params(self) -> InitializeParams:
"""
Get initialization parameters for the COBOL language server.

Returns:
LSP initialization parameters

"""
root_uri = Path(self.repository_root_path).as_uri()
return {
"processId": os.getpid(),
"rootUri": root_uri,
"capabilities": {
"textDocument": {
"synchronization": {
"didSave": True,
"dynamicRegistration": False,
},
"completion": {"completionItem": {"snippetSupport": True}},
"definition": {"dynamicRegistration": False},
"references": {"dynamicRegistration": False},
"documentSymbol": {"dynamicRegistration": False},
"hover": {"dynamicRegistration": False},
},
"workspace": {
"symbol": {"dynamicRegistration": False},
"workspaceFolders": True,
},
},
"workspaceFolders": [
{
"uri": root_uri,
"name": os.path.basename(self.repository_root_path),
}
],
}

def _start_server(self) -> None:
"""
Start the COBOL language server and wait for it to be ready.
"""
log.info("Starting COBOL language server process")
self.server.start()

log.info("Sending initialize request to COBOL language server")
initialize_params = self._get_initialize_params()
init_response = self.server.send.initialize(initialize_params)

log.info("Received initialize response from COBOL language server")
log.debug(f"Server capabilities: {init_response.get('capabilities', {})}")

# Send initialized notification
self.server.notify.initialized({})

log.info("COBOL language server initialized successfully")

# Verify essential capabilities
capabilities = init_response.get("capabilities", {})
if "textDocumentSync" not in capabilities:
log.warning("COBOL language server does not advertise textDocumentSync")
if "documentSymbolProvider" not in capabilities:
log.warning("COBOL language server does not advertise documentSymbolProvider")
7 changes: 7 additions & 0 deletions src/solidlsp/ls_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,7 @@ class Language(str, Enum):
Must be explicitly specified in project.yml. Requires Node.js and npm.
Requires ``ansible`` in PATH for full functionality.
"""
COBOL = "cobol"

@classmethod
def iter_all(cls, include_experimental: bool = False) -> Iterable[Self]:
Expand Down Expand Up @@ -308,6 +309,8 @@ def get_source_fn_matcher(self) -> FilenameMatcher:
return FilenameMatcher("*.sol")
case self.ANSIBLE:
return FilenameMatcher("*.yaml", "*.yml")
case self.COBOL:
return FilenameMatcher("*.cbl", "*.cob", "*.cpy")
case _:
raise ValueError(f"Unhandled language: {self}")

Expand Down Expand Up @@ -519,6 +522,10 @@ def get_ls_class(self) -> type["SolidLanguageServer"]:
from solidlsp.language_servers.ansible_language_server import AnsibleLanguageServer

return AnsibleLanguageServer
case self.COBOL:
from solidlsp.language_servers.cobol_language_server import CobolLanguageServer

return CobolLanguageServer
case _:
raise ValueError(f"Unhandled language: {self}")

Expand Down
19 changes: 19 additions & 0 deletions test/resources/repos/cobol/test_repo/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# COBOL build artifacts
*.exe
*.o
*.so
*.dll
*.cbl.lst
*.cob.lst
*.idy
*.err
*.cpy
*.lst

# IDE files
.vscode/
.idea/

# OS files
.DS_Store
Thumbs.db
21 changes: 21 additions & 0 deletions test/resources/repos/cobol/test_repo/lib/helper.cob
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
*****************************************************************
* HELPER - Helper program for testing cross-file references
* Author: Serena Test Suite
*****************************************************************
IDENTIFICATION DIVISION.
PROGRAM-ID. HELPER.

DATA DIVISION.
LINKAGE SECTION.
01 LS-MESSAGE PIC X(50).

PROCEDURE DIVISION USING LS-MESSAGE.
HELPER-MAIN.
MOVE "Hello from helper program!" TO LS-MESSAGE.
PERFORM FORMAT-MESSAGE.
GOBACK.

FORMAT-MESSAGE.
STRING "Formatted: " DELIMITED BY SIZE
LS-MESSAGE DELIMITED BY SIZE
INTO LS-MESSAGE.
38 changes: 38 additions & 0 deletions test/resources/repos/cobol/test_repo/main.cob
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
*****************************************************************
* CALCULATOR - Main COBOL program for testing
* Author: Serena Test Suite
*****************************************************************
IDENTIFICATION DIVISION.
PROGRAM-ID. CALCULATOR.

DATA DIVISION.
WORKING-STORAGE SECTION.
01 WS-NUM1 PIC 9(4) VALUE 0.
01 WS-NUM2 PIC 9(4) VALUE 0.
01 WS-RESULT PIC 9(8) VALUE 0.
01 WS-GREETING PIC X(50).

PROCEDURE DIVISION.
MAIN-PROCEDURE.
MOVE 10 TO WS-NUM1.
MOVE 20 TO WS-NUM2.

PERFORM ADD-NUMBERS.
DISPLAY "Result of addition: " WS-RESULT.

PERFORM SUBTRACT-NUMBERS.
DISPLAY "Result of subtraction: " WS-RESULT.

PERFORM CALL-HELPER.

STOP RUN.

ADD-NUMBERS.
ADD WS-NUM1 TO WS-NUM2 GIVING WS-RESULT.

SUBTRACT-NUMBERS.
SUBTRACT WS-NUM2 FROM WS-NUM1 GIVING WS-RESULT.

CALL-HELPER.
CALL 'HELPER' USING WS-GREETING.
DISPLAY WS-GREETING.
Loading
Loading