feat: initial iLSP project scaffolding
- Python LSP (pylsp + pylsp_i80 plugin): i80 pypi package completions - Bicep LSP (asyncio TCP proxy → Bicep.LangServer.dll): LRU module injection - Health HTTP endpoint (:2089) for Consul/Nomad checks - Startup catalog fetch from pypi-server.i80.dk + DevOpsMCP (no volume needed) - Multi-stage Dockerfile: downloads Bicep LS at build time, dotnet-runtime-8.0 + python3.12 - Nomad job: static TCP ports 2087/2088, health check on 2089 - Gitea Actions CI: build + push + deploy pipeline - Editor configs: Helix / nvim / LSP4IJ / VS Code
This commit is contained in:
75
ilsp/python_lsp/plugin.py
Normal file
75
ilsp/python_lsp/plugin.py
Normal file
@@ -0,0 +1,75 @@
|
||||
"""
|
||||
pylsp plugin: injects i80/LRU packages into import completions.
|
||||
|
||||
Registered via entry_points group "pylsp" in pyproject.toml.
|
||||
pylsp calls these hooks automatically when the plugin is installed.
|
||||
"""
|
||||
|
||||
import asyncio
|
||||
import logging
|
||||
|
||||
from pylsp import hookimpl
|
||||
|
||||
from .catalog import PypiCatalog
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
# Trigger characters that indicate we're completing an import statement
|
||||
_IMPORT_TRIGGERS = {"import", "from"}
|
||||
|
||||
|
||||
def _is_import_context(document, position) -> bool:
|
||||
"""Return True if the cursor is on an import line."""
|
||||
line_num = position["line"]
|
||||
if line_num >= len(document.lines):
|
||||
return False
|
||||
line = document.lines[line_num].strip()
|
||||
return any(line.startswith(kw) for kw in _IMPORT_TRIGGERS)
|
||||
|
||||
|
||||
@hookimpl
|
||||
def pylsp_completions(config, workspace, document, position):
|
||||
"""Inject i80 pypi packages when completing import statements."""
|
||||
if not _is_import_context(document, position):
|
||||
return []
|
||||
|
||||
# PypiCatalog._packages is populated at startup; safe to read synchronously
|
||||
packages = PypiCatalog._packages
|
||||
if not packages:
|
||||
return []
|
||||
|
||||
return [
|
||||
{
|
||||
"label": pkg["name"],
|
||||
"kind": 9, # Module
|
||||
"detail": "i80 — pypi-server.i80.dk",
|
||||
"sortText": f"{pkg['sort_prefix']}{pkg['name']}",
|
||||
"documentation": {
|
||||
"kind": "markdown",
|
||||
"value": f"**{pkg['name']}**\n\nCustom i80/LRU package from `pypi-server.i80.dk`",
|
||||
},
|
||||
}
|
||||
for pkg in packages
|
||||
]
|
||||
|
||||
|
||||
@hookimpl
|
||||
def pylsp_hover(config, workspace, document, position):
|
||||
"""Show package docs on hover for i80 packages."""
|
||||
word = document.word_at_position(position)
|
||||
if not word:
|
||||
return None
|
||||
|
||||
for pkg in PypiCatalog._packages:
|
||||
if pkg["name"] == word:
|
||||
return {
|
||||
"contents": {
|
||||
"kind": "markdown",
|
||||
"value": (
|
||||
f"**{pkg['name']}** — i80 internal package\n\n"
|
||||
f"Source: `pypi-server.i80.dk`\n\n"
|
||||
f"Install: `pip install {pkg['name']} --index-url https://pypi-server.i80.dk/simple/`"
|
||||
),
|
||||
}
|
||||
}
|
||||
return None
|
||||
Reference in New Issue
Block a user