shifty-lsp 0.1.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- shifty_lsp-0.1.1/.github/workflows/ci.yml +67 -0
- shifty_lsp-0.1.1/.gitignore +8 -0
- shifty_lsp-0.1.1/LICENSE +28 -0
- shifty_lsp-0.1.1/Makefile +55 -0
- shifty_lsp-0.1.1/PKG-INFO +10 -0
- shifty_lsp-0.1.1/README.md +277 -0
- shifty_lsp-0.1.1/editors/vscode/extension.js +54 -0
- shifty_lsp-0.1.1/editors/vscode/language-configuration.json +13 -0
- shifty_lsp-0.1.1/editors/vscode/package.json +52 -0
- shifty_lsp-0.1.1/editors/vscode/shifty-lsp-0.1.0.vsix +0 -0
- shifty_lsp-0.1.1/iaq_sensor_deployment.ttl +744 -0
- shifty_lsp-0.1.1/lsp/shifty-lsp.lua +7 -0
- shifty_lsp-0.1.1/lua/shifty_lsp/init.lua +94 -0
- shifty_lsp-0.1.1/plugin/shifty_lsp.lua +11 -0
- shifty_lsp-0.1.1/pyproject.toml +21 -0
- shifty_lsp-0.1.1/shifty_lsp/__init__.py +3 -0
- shifty_lsp-0.1.1/shifty_lsp/server.py +1109 -0
- shifty_lsp-0.1.1/test_e2e.py +304 -0
- shifty_lsp-0.1.1/uv.lock +183 -0
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main, master]
|
|
6
|
+
pull_request:
|
|
7
|
+
|
|
8
|
+
jobs:
|
|
9
|
+
test:
|
|
10
|
+
runs-on: ubuntu-latest
|
|
11
|
+
steps:
|
|
12
|
+
- uses: actions/checkout@v4
|
|
13
|
+
|
|
14
|
+
- uses: astral-sh/setup-uv@v5
|
|
15
|
+
|
|
16
|
+
- name: Set up Python
|
|
17
|
+
run: uv python install 3.12
|
|
18
|
+
|
|
19
|
+
- name: Install dependencies
|
|
20
|
+
run: make dev
|
|
21
|
+
|
|
22
|
+
- name: Run end-to-end LSP test
|
|
23
|
+
run: make test
|
|
24
|
+
|
|
25
|
+
build:
|
|
26
|
+
runs-on: ubuntu-latest
|
|
27
|
+
needs: test
|
|
28
|
+
steps:
|
|
29
|
+
- uses: actions/checkout@v4
|
|
30
|
+
|
|
31
|
+
- uses: astral-sh/setup-uv@v5
|
|
32
|
+
|
|
33
|
+
- uses: actions/setup-node@v4
|
|
34
|
+
with:
|
|
35
|
+
node-version: 20
|
|
36
|
+
|
|
37
|
+
- name: Build wheel + sdist
|
|
38
|
+
run: make wheel
|
|
39
|
+
|
|
40
|
+
- name: Build VS Code extension
|
|
41
|
+
run: make vsix
|
|
42
|
+
|
|
43
|
+
- name: Upload artifacts
|
|
44
|
+
uses: actions/upload-artifact@v4
|
|
45
|
+
with:
|
|
46
|
+
name: packages
|
|
47
|
+
path: |
|
|
48
|
+
dist/
|
|
49
|
+
editors/vscode/*.vsix
|
|
50
|
+
|
|
51
|
+
publish:
|
|
52
|
+
# Publish to PyPI when a version tag is pushed (requires a
|
|
53
|
+
# repository secret PYPI_TOKEN, or configure trusted publishing).
|
|
54
|
+
if: startsWith(github.ref, 'refs/tags/v')
|
|
55
|
+
runs-on: ubuntu-latest
|
|
56
|
+
needs: build
|
|
57
|
+
permissions:
|
|
58
|
+
id-token: write # PyPI trusted publishing
|
|
59
|
+
steps:
|
|
60
|
+
- uses: actions/checkout@v4
|
|
61
|
+
- uses: astral-sh/setup-uv@v5
|
|
62
|
+
|
|
63
|
+
- name: Build packages
|
|
64
|
+
run: make wheel
|
|
65
|
+
|
|
66
|
+
- name: Publish to PyPI
|
|
67
|
+
run: uv publish
|
shifty_lsp-0.1.1/LICENSE
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
BSD 3-Clause License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026, Gabe Fierro
|
|
4
|
+
|
|
5
|
+
Redistribution and use in source and binary forms, with or without
|
|
6
|
+
modification, are permitted provided that the following conditions are met:
|
|
7
|
+
|
|
8
|
+
1. Redistributions of source code must retain the above copyright notice, this
|
|
9
|
+
list of conditions and the following disclaimer.
|
|
10
|
+
|
|
11
|
+
2. Redistributions in binary form must reproduce the above copyright notice,
|
|
12
|
+
this list of conditions and the following disclaimer in the documentation
|
|
13
|
+
and/or other materials provided with the distribution.
|
|
14
|
+
|
|
15
|
+
3. Neither the name of the copyright holder nor the names of its
|
|
16
|
+
contributors may be used to endorse or promote products derived from
|
|
17
|
+
this software without specific prior written permission.
|
|
18
|
+
|
|
19
|
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
20
|
+
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
21
|
+
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
22
|
+
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
|
23
|
+
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
24
|
+
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
|
25
|
+
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
|
26
|
+
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
|
27
|
+
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
28
|
+
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
# shifty-lsp build process
|
|
2
|
+
#
|
|
3
|
+
# Local dev:
|
|
4
|
+
# make dev # set up .venv and install the package (editable)
|
|
5
|
+
# make test # run the end-to-end LSP test
|
|
6
|
+
# make build # build the Python wheel + sdist and the VS Code .vsix
|
|
7
|
+
# make clean # remove all build artifacts
|
|
8
|
+
|
|
9
|
+
PYTHON ?= python3
|
|
10
|
+
VENV := .venv
|
|
11
|
+
VENV_PY := $(VENV)/bin/python
|
|
12
|
+
VSCODE_DIR := editors/vscode
|
|
13
|
+
VERSION := $(shell grep -m1 '^version' pyproject.toml | cut -d'"' -f2)
|
|
14
|
+
VSIX := $(VSCODE_DIR)/shifty-lsp-$(VERSION).vsix
|
|
15
|
+
|
|
16
|
+
.PHONY: all dev test build wheel vsix lint clean publish
|
|
17
|
+
|
|
18
|
+
all: build
|
|
19
|
+
|
|
20
|
+
# --- local development -----------------------------------------------------
|
|
21
|
+
|
|
22
|
+
$(VENV_PY):
|
|
23
|
+
uv venv $(VENV)
|
|
24
|
+
|
|
25
|
+
dev: $(VENV_PY)
|
|
26
|
+
uv pip install -e .
|
|
27
|
+
|
|
28
|
+
test: dev
|
|
29
|
+
$(VENV_PY) test_e2e.py
|
|
30
|
+
|
|
31
|
+
# --- build artifacts ---------------------------------------------------------
|
|
32
|
+
|
|
33
|
+
build: wheel vsix
|
|
34
|
+
|
|
35
|
+
wheel: dev
|
|
36
|
+
rm -rf dist
|
|
37
|
+
uv build
|
|
38
|
+
|
|
39
|
+
$(VSCODE_DIR)/node_modules: $(VSCODE_DIR)/package.json
|
|
40
|
+
cd $(VSCODE_DIR) && npm install --no-fund --no-audit
|
|
41
|
+
@touch $@
|
|
42
|
+
|
|
43
|
+
vsix: $(VSIX)
|
|
44
|
+
|
|
45
|
+
$(VSIX): $(VSCODE_DIR)/node_modules $(VSCODE_DIR)/extension.js $(VSCODE_DIR)/package.json
|
|
46
|
+
cd $(VSCODE_DIR) && npx @vscode/vsce package --allow-missing-repository
|
|
47
|
+
|
|
48
|
+
# --- release -----------------------------------------------------------------
|
|
49
|
+
|
|
50
|
+
publish: build
|
|
51
|
+
uv publish
|
|
52
|
+
@echo "Don't forget: cd $(VSCODE_DIR) && npx @vscode/vsce publish"
|
|
53
|
+
|
|
54
|
+
clean:
|
|
55
|
+
rm -rf dist build *.egg-info $(VSCODE_DIR)/node_modules $(VSCODE_DIR)/*.vsix
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: shifty-lsp
|
|
3
|
+
Version: 0.1.1
|
|
4
|
+
Summary: Language server for Turtle/RDF ontologies: ontoenv-powered owl:imports resolution and continuous SHACL validation via shifty
|
|
5
|
+
License-File: LICENSE
|
|
6
|
+
Requires-Python: >=3.11
|
|
7
|
+
Requires-Dist: ontoenv<0.7,>=0.6
|
|
8
|
+
Requires-Dist: pygls>=2.0
|
|
9
|
+
Requires-Dist: pyshifty>=0.2
|
|
10
|
+
Requires-Dist: rdflib>=7.0
|
|
@@ -0,0 +1,277 @@
|
|
|
1
|
+
# shifty-lsp
|
|
2
|
+
|
|
3
|
+
A language server for Turtle/RDF ontology files, combining:
|
|
4
|
+
|
|
5
|
+
- **[ontoenv](https://ontoenv.gtf.fyi)** — manages an ontology environment rooted at the
|
|
6
|
+
enclosing Git repository. All `*.ttl` files in the repo are discovered automatically, and
|
|
7
|
+
`owl:imports` declarations are resolved and downloaded into a local cache.
|
|
8
|
+
- **[shifty](https://shifty.gtf.fyi)** — continuous SHACL validation + SHACL-AF inference.
|
|
9
|
+
The document is validated against the transitive closure of its `owl:imports` (used as the
|
|
10
|
+
shapes graph). `sh:Violation`, `sh:Warning`, and `sh:Info` results are published as LSP
|
|
11
|
+
error/warning/information diagnostics.
|
|
12
|
+
|
|
13
|
+
## Features
|
|
14
|
+
|
|
15
|
+
| Feature | LSP method |
|
|
16
|
+
|---|---|
|
|
17
|
+
| Continuous SHACL validation + inference, results as diagnostics | `textDocument/publishDiagnostics` |
|
|
18
|
+
| Turtle syntax errors as diagnostics (correct line) | 〃 |
|
|
19
|
+
| Unresolvable `owl:imports` (404 etc.) as error diagnostics | 〃 |
|
|
20
|
+
| Prefix-aware completion from the ontology environment (`brick:IAQ…`) | `textDocument/completion` |
|
|
21
|
+
| Hover docs (`rdfs:label`/`comment`/types/supertypes) | `textDocument/hover` |
|
|
22
|
+
| Goto definition of ontology IRIs (remote ontologies materialized locally) | `textDocument/definition` |
|
|
23
|
+
| References, repo-wide | `textDocument/references` |
|
|
24
|
+
| Document outline (ontology, classes, shapes, individuals) | `textDocument/documentSymbol` |
|
|
25
|
+
| Quickfix: declare missing prefix (+ `owl:imports`) | `textDocument/codeAction` |
|
|
26
|
+
| Progress reporting during downloads/validation | `window/workDoneProgress` |
|
|
27
|
+
| Validation summary messages | `window/showMessage` |
|
|
28
|
+
|
|
29
|
+
## Install
|
|
30
|
+
|
|
31
|
+
**The server** (Python 3.10+, provides the `shifty-lsp` executable on your PATH):
|
|
32
|
+
|
|
33
|
+
```sh
|
|
34
|
+
# from PyPI (once published)
|
|
35
|
+
uv tool install shifty-lsp
|
|
36
|
+
|
|
37
|
+
# or straight from the repo
|
|
38
|
+
uv tool install git+https://github.com/gtfierro/shifty-lsp
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
**Neovim plugin** (Neovim ≥ 0.11, ships an `lsp/` config):
|
|
42
|
+
|
|
43
|
+
```lua
|
|
44
|
+
vim.pack.add({ "https://github.com/gtfierro/shifty-lsp" })
|
|
45
|
+
require("shifty_lsp").setup() -- keymaps, commands, diagnostics (all optional)
|
|
46
|
+
vim.lsp.enable("shifty-lsp")
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
`setup()` accepts `{ keymaps = false, commands = false, diagnostics = false }` to
|
|
50
|
+
disable any part. The plugin also registers `.ttl` as the `turtle` filetype.
|
|
51
|
+
|
|
52
|
+
**VS Code extension**: a prebuilt `.vsix` lives in `editors/vscode/`. Install with:
|
|
53
|
+
|
|
54
|
+
```sh
|
|
55
|
+
code --install-extension editors/vscode/shifty-lsp-0.1.0.vsix
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
or rebuild it yourself:
|
|
59
|
+
|
|
60
|
+
```sh
|
|
61
|
+
cd editors/vscode && npm install && npx @vscode/vsce package
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
It adds Turtle language support, starts the server for `.ttl` files (configurable via
|
|
65
|
+
the `shifty-lsp.path` setting), and exposes *Shifty: Validate File* and *Shifty: Refresh
|
|
66
|
+
Ontology Environment* commands in the command palette.
|
|
67
|
+
|
|
68
|
+
## How it works
|
|
69
|
+
|
|
70
|
+
1. On `initialize` (or lazily on first file open), the server finds the Git root of the
|
|
71
|
+
workspace (walks up looking for `.git`; falls back to the workspace folder) and
|
|
72
|
+
opens/creates an ontoenv environment there (`.ontoenv/` directory). Opening any file
|
|
73
|
+
automatically loads the environment and triggers validation — no manual setup needed.
|
|
74
|
+
2. On document open/change (debounced ~0.8s, in a background thread) and save:
|
|
75
|
+
- The document is parsed with rdflib (parse errors become diagnostics, positioned at
|
|
76
|
+
the line reported by the parser).
|
|
77
|
+
- The file is registered with ontoenv, which downloads any missing `owl:imports`.
|
|
78
|
+
Imports that cannot be fetched (404, network error, etc.) are published as **error
|
|
79
|
+
diagnostics** on the `owl:imports` line, with the underlying error message.
|
|
80
|
+
- The transitive import closure is materialized as the shapes graph. It is **cached
|
|
81
|
+
per document** and only rebuilt when the file's `owl:imports` set changes or the
|
|
82
|
+
`refreshEnvironment` command runs.
|
|
83
|
+
- `shifty.validate(data, shapes, infer=True)` runs SHACL-AF rules to a fixed point and
|
|
84
|
+
validates. Each result is mapped to diagnostics at **every line where the offending
|
|
85
|
+
node appears**: the `sh:value` node when the report identifies one, otherwise the
|
|
86
|
+
`sh:focusNode`. Matching is prefix-aware (`ex:Foo`, `<full-iri>`, local name) with
|
|
87
|
+
token boundaries.
|
|
88
|
+
- If you keep typing while a slow validation is in flight, the stale run's results
|
|
89
|
+
are discarded rather than overwriting fresher state.
|
|
90
|
+
- When validation completes, a `window/showMessage` summary is sent
|
|
91
|
+
(`shifty: file.ttl: conforms ✓` / `N error(s), M warning(s)`).
|
|
92
|
+
3. On save, ontoenv re-scans the repo so newly added `.ttl` files join the environment
|
|
93
|
+
(this does not force shapes-graph rebuilds — see `refreshEnvironment` for that).
|
|
94
|
+
|
|
95
|
+
## Commands
|
|
96
|
+
|
|
97
|
+
Executable via `workspace/executeCommand` from your editor:
|
|
98
|
+
|
|
99
|
+
- `shifty-lsp.refreshEnvironment` — re-scan the repository, invalidate cached shapes
|
|
100
|
+
graphs, and revalidate all open documents. Use this after editing an ontology that
|
|
101
|
+
*other* files import (the per-document import-set cache can't detect that case).
|
|
102
|
+
- `shifty-lsp.validateFile` — validate a single file (pass the document URI as the first
|
|
103
|
+
argument; defaults to the current document) and publish diagnostics. Returns `true` if
|
|
104
|
+
the file conforms, `false` otherwise.
|
|
105
|
+
|
|
106
|
+
## Completion
|
|
107
|
+
|
|
108
|
+
`textDocument/completion` (triggered by `:`): typing a prefixed name like `brick:IAQ`
|
|
109
|
+
offers every class, property, and individual in that namespace, drawn from the merged
|
|
110
|
+
imports closure in the ontoenv environment. Items carry the term's `rdfs:label` as detail
|
|
111
|
+
and `rdfs:comment` as documentation. Completion works even while the buffer doesn't parse
|
|
112
|
+
(imports are recovered with a regex), and the client filters as you keep typing.
|
|
113
|
+
|
|
114
|
+
## Hover
|
|
115
|
+
|
|
116
|
+
`textDocument/hover`: cursor on any IRI or prefixed name shows a markdown card with the
|
|
117
|
+
term's `rdfs:label`, `rdf:type`, `rdfs:subClassOf` chain, and `rdfs:comment`, pulled from
|
|
118
|
+
the merged ontology environment.
|
|
119
|
+
|
|
120
|
+
## References
|
|
121
|
+
|
|
122
|
+
`textDocument/references`: finds every occurrence of the term under the cursor, both in
|
|
123
|
+
the current file and in all other `.ttl` files in the repository (`.ontoenv/` excluded).
|
|
124
|
+
|
|
125
|
+
## Code actions (quickfix)
|
|
126
|
+
|
|
127
|
+
`textDocument/codeAction`: when a prefixed name is used whose prefix is not declared in
|
|
128
|
+
the file but **is** a known namespace in the ontology environment, offers a quickfix that
|
|
129
|
+
inserts the `@prefix` declaration — plus an `owl:imports` statement when the prefix maps
|
|
130
|
+
to a known ontology. Works even while the buffer doesn't parse (that's usually why the
|
|
131
|
+
prefix is missing).
|
|
132
|
+
|
|
133
|
+
## Document symbols (outline)
|
|
134
|
+
|
|
135
|
+
`textDocument/documentSymbol` returns the ontology declaration plus every typed subject
|
|
136
|
+
in the file (classes, properties, SHACL shapes, individuals) with their `rdf:type` as
|
|
137
|
+
detail. Use it via `gO` (built-in location list), `:Telescope lsp_document_symbols`, or
|
|
138
|
+
outline plugins like aerial.nvim.
|
|
139
|
+
|
|
140
|
+
## Goto definition
|
|
141
|
+
|
|
142
|
+
`textDocument/definition` is supported: with the cursor on an ontology IRI or prefixed
|
|
143
|
+
name (e.g. inside an `owl:imports <...>` statement), jump to that ontology's `.ttl` file.
|
|
144
|
+
Local ontologies open directly; remote ones are materialized from the ontoenv store into
|
|
145
|
+
`.ontoenv/lsp-cache/<name>.ttl` on first use and opened from there.
|
|
146
|
+
|
|
147
|
+
## Editor setup
|
|
148
|
+
|
|
149
|
+
### Neovim (manual setup, without the plugin)
|
|
150
|
+
|
|
151
|
+
```lua
|
|
152
|
+
vim.api.nvim_create_autocmd("FileType", {
|
|
153
|
+
pattern = { "turtle", "ttl" },
|
|
154
|
+
callback = function(args)
|
|
155
|
+
vim.lsp.start({
|
|
156
|
+
name = "shifty-lsp",
|
|
157
|
+
cmd = { "/path/to/shifty-lsp/.venv/bin/shifty-lsp" },
|
|
158
|
+
root_dir = vim.fs.root(0, ".git") or vim.fn.getcwd(),
|
|
159
|
+
})
|
|
160
|
+
|
|
161
|
+
-- inline diagnostics: virtual_lines renders each SHACL message on its own
|
|
162
|
+
-- line(s) below the offending code -- better for long validation messages.
|
|
163
|
+
-- Neovim wraps them to the window width automatically.
|
|
164
|
+
vim.diagnostic.config({
|
|
165
|
+
signs = true,
|
|
166
|
+
underline = true,
|
|
167
|
+
virtual_lines = true,
|
|
168
|
+
-- alternative: end-of-line virtual text, wrapped to the window width.
|
|
169
|
+
-- Requires a wrap_text(msg, width) helper that soft-wraps on word
|
|
170
|
+
-- boundaries and joins with "\n".
|
|
171
|
+
-- virtual_text = {
|
|
172
|
+
-- format = function(d)
|
|
173
|
+
-- local width = vim.api.nvim_win_get_width(0) - d.col - 8
|
|
174
|
+
-- return wrap_text(d.message, math.max(width, 40))
|
|
175
|
+
-- end,
|
|
176
|
+
-- },
|
|
177
|
+
})
|
|
178
|
+
|
|
179
|
+
-- completion: manual via omnifunc (<C-x><C-o>), or wire up nvim-cmp/blink.cmp
|
|
180
|
+
-- with the 'nvim_lsp' source for as-you-type completion
|
|
181
|
+
vim.bo[args.buf].omnifunc = "v:lua.vim.lsp.omnifunc"
|
|
182
|
+
|
|
183
|
+
-- goto definition (jump to an ontology's .ttl from its IRI in owl:imports)
|
|
184
|
+
vim.keymap.set("n", "gd", vim.lsp.buf.definition, { buffer = args.buf, desc = "goto definition" })
|
|
185
|
+
|
|
186
|
+
-- hover: label/comment/types for the term under the cursor
|
|
187
|
+
vim.keymap.set("n", "K", vim.lsp.buf.hover, { buffer = args.buf, desc = "hover docs" })
|
|
188
|
+
|
|
189
|
+
-- references: every use of the term (this file + repo .ttl files)
|
|
190
|
+
vim.keymap.set("n", "gr", vim.lsp.buf.references, { buffer = args.buf, desc = "references" })
|
|
191
|
+
|
|
192
|
+
-- code actions: e.g. "Declare prefix 'sh:' and import ..." quickfix
|
|
193
|
+
vim.keymap.set("n", "<leader>ca", vim.lsp.buf.code_action, { buffer = args.buf, desc = "code action" })
|
|
194
|
+
|
|
195
|
+
-- jump between diagnostics from the cursor position (float shows the message)
|
|
196
|
+
vim.keymap.set("n", "]e", function()
|
|
197
|
+
vim.diagnostic.jump({ count = 1, float = true })
|
|
198
|
+
end, { buffer = args.buf, desc = "next diagnostic" })
|
|
199
|
+
vim.keymap.set("n", "[e", function()
|
|
200
|
+
vim.diagnostic.jump({ count = -1, float = true })
|
|
201
|
+
end, { buffer = args.buf, desc = "prev diagnostic" })
|
|
202
|
+
-- errors only (skip warnings/infos):
|
|
203
|
+
vim.keymap.set("n", "]E", function()
|
|
204
|
+
vim.diagnostic.jump({ count = 1, float = true, severity = vim.diagnostic.severity.ERROR })
|
|
205
|
+
end, { buffer = args.buf, desc = "next error" })
|
|
206
|
+
vim.keymap.set("n", "[E", function()
|
|
207
|
+
vim.diagnostic.jump({ count = -1, float = true, severity = vim.diagnostic.severity.ERROR })
|
|
208
|
+
end, { buffer = args.buf, desc = "prev error" })
|
|
209
|
+
|
|
210
|
+
-- validate the current file, showing the conforms result.
|
|
211
|
+
-- NOTE: async request -- a synchronous buf_request_sync would block the
|
|
212
|
+
-- UI and swallow the server's progress notifications.
|
|
213
|
+
vim.api.nvim_buf_create_user_command(args.buf, "ShiftyValidate", function()
|
|
214
|
+
local client = vim.lsp.get_clients({ bufnr = 0, name = "shifty-lsp" })[1]
|
|
215
|
+
if not client then return end
|
|
216
|
+
client:request("workspace/executeCommand", {
|
|
217
|
+
command = "shifty-lsp.validateFile",
|
|
218
|
+
arguments = { vim.uri_from_bufnr(0) },
|
|
219
|
+
}, function(err, result)
|
|
220
|
+
if err then
|
|
221
|
+
vim.notify("shifty-lsp error: " .. tostring(err.message), vim.log.levels.ERROR)
|
|
222
|
+
return
|
|
223
|
+
end
|
|
224
|
+
vim.notify(result and "\226\156\147 conforms" or "\226\156\151 violations found",
|
|
225
|
+
result and vim.log.levels.INFO or vim.log.levels.ERROR)
|
|
226
|
+
end, 0)
|
|
227
|
+
end, {})
|
|
228
|
+
|
|
229
|
+
-- refresh the ontoenv environment
|
|
230
|
+
vim.api.nvim_buf_create_user_command(args.buf, "ShiftyRefreshEnv", function()
|
|
231
|
+
vim.lsp.buf.execute_command({ command = "shifty-lsp.refreshEnvironment" })
|
|
232
|
+
end, {})
|
|
233
|
+
|
|
234
|
+
vim.keymap.set("n", "<leader>ov", "<cmd>ShiftyValidate<cr>", { buffer = args.buf, desc = "shifty: validate file" })
|
|
235
|
+
vim.keymap.set("n", "<leader>or", "<cmd>ShiftyRefreshEnv<cr>", { buffer = args.buf, desc = "ontoenv: refresh environment" })
|
|
236
|
+
end,
|
|
237
|
+
})
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
Progress during validation (import downloads, shifty runs) is reported via
|
|
241
|
+
`window/workDoneProgress`. Stock Neovim does **not** display this anywhere visible —
|
|
242
|
+
either install [fidget.nvim](https://github.com/j-hui/fidget.nvim) (`opts = {}` is
|
|
243
|
+
enough), or add this autocmd to echo progress to the message area:
|
|
244
|
+
|
|
245
|
+
```lua
|
|
246
|
+
vim.api.nvim_create_autocmd("LspProgress", {
|
|
247
|
+
callback = function(ev)
|
|
248
|
+
local value = ev.data.params.value
|
|
249
|
+
if value.kind == "end" then return end
|
|
250
|
+
local msg = value.title or ""
|
|
251
|
+
if value.message then msg = msg .. ": " .. value.message end
|
|
252
|
+
vim.api.nvim_echo({ { msg, "Comment" } }, false, {})
|
|
253
|
+
end,
|
|
254
|
+
})
|
|
255
|
+
```
|
|
256
|
+
|
|
257
|
+
### VS Code
|
|
258
|
+
|
|
259
|
+
Install the extension from `editors/vscode/` (see [Install](#install)). Alternatively,
|
|
260
|
+
use a generic LSP client extension and point it at the `shifty-lsp` executable for
|
|
261
|
+
`.ttl` files.
|
|
262
|
+
|
|
263
|
+
## Development
|
|
264
|
+
|
|
265
|
+
Everything goes through the Makefile:
|
|
266
|
+
|
|
267
|
+
```sh
|
|
268
|
+
make dev # set up .venv and install the package (editable)
|
|
269
|
+
make test # run the end-to-end LSP test
|
|
270
|
+
make build # build the Python wheel/sdist + the VS Code .vsix
|
|
271
|
+
make clean # remove all build artifacts
|
|
272
|
+
make publish # build + publish to PyPI
|
|
273
|
+
```
|
|
274
|
+
|
|
275
|
+
CI (GitHub Actions, `.github/workflows/ci.yml`) runs the e2e test on every push/PR,
|
|
276
|
+
builds the wheel and `.vsix` as downloadable artifacts, and publishes to PyPI when a
|
|
277
|
+
`v*` tag is pushed (via trusted publishing or a `PYPI_TOKEN` secret).
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
const vscode = require("vscode");
|
|
2
|
+
const { LanguageClient, TransportKind } = require("vscode-languageclient/node");
|
|
3
|
+
|
|
4
|
+
/** @type {LanguageClient | undefined} */
|
|
5
|
+
let client;
|
|
6
|
+
|
|
7
|
+
function activate(context) {
|
|
8
|
+
const config = vscode.workspace.getConfiguration("shifty-lsp");
|
|
9
|
+
const command = config.get("path", "shifty-lsp");
|
|
10
|
+
|
|
11
|
+
client = new LanguageClient(
|
|
12
|
+
"shifty-lsp",
|
|
13
|
+
"shifty-lsp",
|
|
14
|
+
{ command, transport: TransportKind.stdio },
|
|
15
|
+
{
|
|
16
|
+
documentSelector: [{ scheme: "file", language: "turtle" }],
|
|
17
|
+
// The server roots its ontoenv environment at the git repository.
|
|
18
|
+
workspaceFolder: vscode.workspace.workspaceFolders?.[0],
|
|
19
|
+
}
|
|
20
|
+
);
|
|
21
|
+
|
|
22
|
+
context.subscriptions.push(
|
|
23
|
+
vscode.commands.registerCommand("shifty-lsp.validateFile", async () => {
|
|
24
|
+
const editor = vscode.window.activeTextEditor;
|
|
25
|
+
if (!editor) return;
|
|
26
|
+
const result = await client.sendRequest("workspace/executeCommand", {
|
|
27
|
+
command: "shifty-lsp.validateFile",
|
|
28
|
+
arguments: [editor.document.uri.toString()],
|
|
29
|
+
});
|
|
30
|
+
if (result) {
|
|
31
|
+
vscode.window.showInformationMessage("shifty: file conforms ✓");
|
|
32
|
+
} else {
|
|
33
|
+
vscode.window.showWarningMessage(
|
|
34
|
+
"shifty: violations found — see Problems panel"
|
|
35
|
+
);
|
|
36
|
+
}
|
|
37
|
+
}),
|
|
38
|
+
vscode.commands.registerCommand("shifty-lsp.refreshEnvironment", async () => {
|
|
39
|
+
await client.sendRequest("workspace/executeCommand", {
|
|
40
|
+
command: "shifty-lsp.refreshEnvironment",
|
|
41
|
+
arguments: [],
|
|
42
|
+
});
|
|
43
|
+
vscode.window.showInformationMessage("shifty: ontoenv environment refreshed");
|
|
44
|
+
})
|
|
45
|
+
);
|
|
46
|
+
|
|
47
|
+
client.start();
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function deactivate() {
|
|
51
|
+
return client ? client.stop() : undefined;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
module.exports = { activate, deactivate };
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
{
|
|
2
|
+
"comments": {
|
|
3
|
+
"lineComment": "#"
|
|
4
|
+
},
|
|
5
|
+
"brackets": [["<", ">"], ["[", "]"], ["(", ")"], ["{", "}"]],
|
|
6
|
+
"autoClosingPairs": [
|
|
7
|
+
{ "open": "\"", "close": "\"" },
|
|
8
|
+
{ "open": "<", "close": ">" },
|
|
9
|
+
{ "open": "[", "close": "]" },
|
|
10
|
+
{ "open": "(", "close": ")" },
|
|
11
|
+
{ "open": "{", "close": "}" }
|
|
12
|
+
]
|
|
13
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "shifty-lsp",
|
|
3
|
+
"displayName": "shifty-lsp",
|
|
4
|
+
"description": "Turtle/RDF ontology support: SHACL validation via shifty, owl:imports management via ontoenv",
|
|
5
|
+
"version": "0.1.0",
|
|
6
|
+
"publisher": "gtfierro",
|
|
7
|
+
"license": "BSD-3-Clause",
|
|
8
|
+
"engines": {
|
|
9
|
+
"vscode": "^1.85.0"
|
|
10
|
+
},
|
|
11
|
+
"categories": [
|
|
12
|
+
"Programming Languages",
|
|
13
|
+
"Linters"
|
|
14
|
+
],
|
|
15
|
+
"activationEvents": [
|
|
16
|
+
"onLanguage:turtle"
|
|
17
|
+
],
|
|
18
|
+
"main": "./extension.js",
|
|
19
|
+
"contributes": {
|
|
20
|
+
"languages": [
|
|
21
|
+
{
|
|
22
|
+
"id": "turtle",
|
|
23
|
+
"aliases": ["Turtle", "turtle"],
|
|
24
|
+
"extensions": [".ttl"],
|
|
25
|
+
"configuration": "./language-configuration.json"
|
|
26
|
+
}
|
|
27
|
+
],
|
|
28
|
+
"commands": [
|
|
29
|
+
{
|
|
30
|
+
"command": "shifty-lsp.validateFile",
|
|
31
|
+
"title": "Shifty: Validate File"
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
"command": "shifty-lsp.refreshEnvironment",
|
|
35
|
+
"title": "Shifty: Refresh Ontology Environment"
|
|
36
|
+
}
|
|
37
|
+
],
|
|
38
|
+
"configuration": {
|
|
39
|
+
"title": "shifty-lsp",
|
|
40
|
+
"properties": {
|
|
41
|
+
"shifty-lsp.path": {
|
|
42
|
+
"type": "string",
|
|
43
|
+
"default": "shifty-lsp",
|
|
44
|
+
"description": "Path to the shifty-lsp executable"
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
},
|
|
49
|
+
"dependencies": {
|
|
50
|
+
"vscode-languageclient": "^9.0.1"
|
|
51
|
+
}
|
|
52
|
+
}
|
|
Binary file
|