opencode-sql-lsp-server 0.1.0__tar.gz → 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.
Files changed (17) hide show
  1. opencode_sql_lsp_server-0.1.1/LICENSE +21 -0
  2. {opencode_sql_lsp_server-0.1.0 → opencode_sql_lsp_server-0.1.1}/PKG-INFO +5 -3
  3. {opencode_sql_lsp_server-0.1.0 → opencode_sql_lsp_server-0.1.1}/pyproject.toml +8 -3
  4. {opencode_sql_lsp_server-0.1.0 → opencode_sql_lsp_server-0.1.1}/src/opencode_sql_lsp_server/__init__.py +1 -1
  5. {opencode_sql_lsp_server-0.1.0 → opencode_sql_lsp_server-0.1.1}/src/opencode_sql_lsp_server/cli.py +0 -1
  6. {opencode_sql_lsp_server-0.1.0 → opencode_sql_lsp_server-0.1.1}/src/opencode_sql_lsp_server/config.py +17 -3
  7. opencode_sql_lsp_server-0.1.1/src/opencode_sql_lsp_server/server.py +361 -0
  8. {opencode_sql_lsp_server-0.1.0 → opencode_sql_lsp_server-0.1.1}/src/opencode_sql_lsp_server.egg-info/PKG-INFO +5 -3
  9. {opencode_sql_lsp_server-0.1.0 → opencode_sql_lsp_server-0.1.1}/src/opencode_sql_lsp_server.egg-info/SOURCES.txt +1 -0
  10. opencode_sql_lsp_server-0.1.0/src/opencode_sql_lsp_server/server.py +0 -108
  11. {opencode_sql_lsp_server-0.1.0 → opencode_sql_lsp_server-0.1.1}/README.md +0 -0
  12. {opencode_sql_lsp_server-0.1.0 → opencode_sql_lsp_server-0.1.1}/setup.cfg +0 -0
  13. {opencode_sql_lsp_server-0.1.0 → opencode_sql_lsp_server-0.1.1}/src/opencode_sql_lsp_server/sqlfluff_adapter.py +0 -0
  14. {opencode_sql_lsp_server-0.1.0 → opencode_sql_lsp_server-0.1.1}/src/opencode_sql_lsp_server.egg-info/dependency_links.txt +0 -0
  15. {opencode_sql_lsp_server-0.1.0 → opencode_sql_lsp_server-0.1.1}/src/opencode_sql_lsp_server.egg-info/entry_points.txt +0 -0
  16. {opencode_sql_lsp_server-0.1.0 → opencode_sql_lsp_server-0.1.1}/src/opencode_sql_lsp_server.egg-info/requires.txt +0 -0
  17. {opencode_sql_lsp_server-0.1.0 → opencode_sql_lsp_server-0.1.1}/src/opencode_sql_lsp_server.egg-info/top_level.txt +0 -0
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -1,22 +1,24 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: opencode-sql-lsp-server
3
- Version: 0.1.0
3
+ Version: 0.1.1
4
4
  Summary: SQL Language Server for OpenCode with Trino/StarRocks dialect support
5
5
  Author: OpenCode SQL LSP port
6
- License: MIT
6
+ License-Expression: MIT
7
+ Project-URL: Homepage, https://opencode.ai
7
8
  Keywords: opencode,lsp,sql,sqlfluff,trino,starrocks
8
9
  Classifier: Development Status :: 3 - Alpha
9
10
  Classifier: Environment :: Console
10
11
  Classifier: Intended Audience :: Developers
11
- Classifier: License :: OSI Approved :: MIT License
12
12
  Classifier: Programming Language :: Python :: 3
13
13
  Classifier: Programming Language :: Python :: 3 :: Only
14
14
  Classifier: Topic :: Software Development :: Quality Assurance
15
15
  Requires-Python: >=3.10
16
16
  Description-Content-Type: text/markdown
17
+ License-File: LICENSE
17
18
  Requires-Dist: pygls>=1.3.1
18
19
  Requires-Dist: lsprotocol>=2023.0.1
19
20
  Requires-Dist: sqlfluff>=3.0.0
21
+ Dynamic: license-file
20
22
 
21
23
  # opencode-sql-lsp-server
22
24
 
@@ -4,28 +4,33 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "opencode-sql-lsp-server"
7
- version = "0.1.0"
7
+ version = "0.1.1"
8
8
  description = "SQL Language Server for OpenCode with Trino/StarRocks dialect support"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.10"
11
- license = { text = "MIT" }
11
+ license = "MIT"
12
12
  authors = [{ name = "OpenCode SQL LSP port" }]
13
13
  keywords = ["opencode", "lsp", "sql", "sqlfluff", "trino", "starrocks"]
14
14
  classifiers = [
15
15
  "Development Status :: 3 - Alpha",
16
16
  "Environment :: Console",
17
17
  "Intended Audience :: Developers",
18
- "License :: OSI Approved :: MIT License",
19
18
  "Programming Language :: Python :: 3",
20
19
  "Programming Language :: Python :: 3 :: Only",
21
20
  "Topic :: Software Development :: Quality Assurance",
22
21
  ]
22
+
23
+ license-files = ["LICENSE"]
24
+
23
25
  dependencies = [
24
26
  "pygls>=1.3.1",
25
27
  "lsprotocol>=2023.0.1",
26
28
  "sqlfluff>=3.0.0",
27
29
  ]
28
30
 
31
+ [project.urls]
32
+ Homepage = "https://opencode.ai"
33
+
29
34
  [project.scripts]
30
35
  opencode-sql-lsp = "opencode_sql_lsp_server.cli:main"
31
36
 
@@ -1,2 +1,2 @@
1
1
  __all__ = ["__version__"]
2
- __version__ = "0.1.0"
2
+ __version__ = "0.1.1"
@@ -23,7 +23,6 @@ def main() -> None:
23
23
  if not args.stdio:
24
24
  raise SystemExit("Only --stdio transport is supported")
25
25
 
26
- # pygls gets rootUri from initialize; we optionally allow an override.
27
26
  if args.workspace:
28
27
  server.set_workspace_root(args.workspace)
29
28
 
@@ -7,17 +7,29 @@ from pathlib import Path
7
7
  from typing import Any
8
8
 
9
9
 
10
+ class SqlLspConfigLoadError(RuntimeError):
11
+ pass
12
+
13
+
10
14
  @dataclass(frozen=True)
11
15
  class SqlLspConfig:
12
16
  default_dialect: str
13
17
  overrides: dict[str, str]
14
18
 
19
+ @staticmethod
20
+ def default() -> "SqlLspConfig":
21
+ return SqlLspConfig(default_dialect="starrocks", overrides={})
22
+
15
23
  @staticmethod
16
24
  def load(root: Path) -> "SqlLspConfig":
17
25
  cfg_path = root / ".opencode" / "sql-lsp.json"
18
26
  if not cfg_path.exists():
19
- return SqlLspConfig(default_dialect="starrocks", overrides={})
20
- data: Any = json.loads(cfg_path.read_text(encoding="utf-8"))
27
+ return SqlLspConfig.default()
28
+
29
+ try:
30
+ data: Any = json.loads(cfg_path.read_text(encoding="utf-8"))
31
+ except Exception as e:
32
+ raise SqlLspConfigLoadError(f"Failed to load config {cfg_path}: {e}") from e
21
33
  default_dialect = data.get("defaultDialect")
22
34
  if not isinstance(default_dialect, str) or not default_dialect.strip():
23
35
  default_dialect = "starrocks"
@@ -35,7 +47,9 @@ class SqlLspConfig:
35
47
  return SqlLspConfig(default_dialect=default_dialect, overrides=overrides)
36
48
 
37
49
  def dialect_for_path(self, relative_path: str) -> str:
50
+ relative_path_posix = relative_path.replace("\\", "/")
38
51
  for pattern, dialect in self.overrides.items():
39
- if fnmatch(relative_path, pattern):
52
+ pattern_posix = pattern.replace("\\", "/")
53
+ if fnmatch(relative_path_posix, pattern_posix):
40
54
  return dialect
41
55
  return self.default_dialect
@@ -0,0 +1,361 @@
1
+ from __future__ import annotations
2
+
3
+ import asyncio
4
+ from dataclasses import dataclass
5
+ from pathlib import Path
6
+
7
+ from lsprotocol.types import (
8
+ Diagnostic,
9
+ DiagnosticSeverity,
10
+ DocumentFormattingParams,
11
+ InitializeParams,
12
+ INITIALIZE,
13
+ PublishDiagnosticsParams,
14
+ TEXT_DOCUMENT_DID_CHANGE,
15
+ TEXT_DOCUMENT_DID_OPEN,
16
+ TEXT_DOCUMENT_DID_SAVE,
17
+ TEXT_DOCUMENT_FORMATTING,
18
+ Position,
19
+ Range,
20
+ TextEdit,
21
+ )
22
+ from pygls.lsp.server import LanguageServer
23
+ from pygls.uris import to_fs_path
24
+
25
+ from pygls.exceptions import PyglsError
26
+
27
+ from .config import SqlLspConfig
28
+ from .sqlfluff_adapter import format_sql, lint_issues
29
+
30
+
31
+ _DID_CHANGE_DEBOUNCE_S = 0.25
32
+ _MAX_LINT_LINES = 5_000
33
+ _MAX_LINT_BYTES = 200_000
34
+
35
+
36
+ @dataclass
37
+ class _ConfigCacheEntry:
38
+ mtime_ns: int | None
39
+ config: SqlLspConfig
40
+ last_error_mtime_ns: int | None = None
41
+
42
+
43
+ @dataclass
44
+ class _DocState:
45
+ version: int | None = None
46
+ pending_timer: asyncio.TimerHandle | None = None
47
+ pending_task: asyncio.Task[None] | None = None
48
+
49
+
50
+ class OpenCodeSqlLanguageServer(LanguageServer):
51
+ def __init__(self) -> None:
52
+ super().__init__("opencode-sql-lsp", "0.1.1", max_workers=2)
53
+ self._workspace_roots: list[Path] = []
54
+ self._config_cache: dict[Path, _ConfigCacheEntry] = {}
55
+ self._doc_state: dict[str, _DocState] = {}
56
+
57
+ def set_workspace_root(self, root: str | None) -> None:
58
+ self.set_workspace_roots([root] if root else [])
59
+
60
+ def set_workspace_roots(self, roots: list[str]) -> None:
61
+ self._workspace_roots = [Path(r).resolve() for r in roots if r]
62
+ self._config_cache.clear()
63
+
64
+ def _best_root_for_uri(self, doc_uri: str) -> Path | None:
65
+ if not self._workspace_roots:
66
+ return None
67
+ try:
68
+ fs_path = to_fs_path(doc_uri)
69
+ if not fs_path:
70
+ return None
71
+ p = Path(fs_path).resolve()
72
+ except Exception:
73
+ return None
74
+
75
+ best: Path | None = None
76
+ best_len = -1
77
+ for r in self._workspace_roots:
78
+ try:
79
+ p.relative_to(r)
80
+ except Exception:
81
+ continue
82
+ l = len(str(r))
83
+ if l > best_len:
84
+ best = r
85
+ best_len = l
86
+ return best
87
+
88
+ def _config_for_root(self, root: Path) -> SqlLspConfig:
89
+ cfg_path = root / ".opencode" / "sql-lsp.json"
90
+ try:
91
+ st = cfg_path.stat()
92
+ mtime_ns: int | None = st.st_mtime_ns
93
+ except FileNotFoundError:
94
+ mtime_ns = None
95
+ except Exception as e:
96
+ self.report_server_error(e, PyglsError)
97
+ return SqlLspConfig.default()
98
+
99
+ cached = self._config_cache.get(root)
100
+ if cached and cached.mtime_ns == mtime_ns:
101
+ return cached.config
102
+
103
+ if mtime_ns is None:
104
+ cfg = SqlLspConfig.default()
105
+ self._config_cache[root] = _ConfigCacheEntry(mtime_ns=None, config=cfg)
106
+ return cfg
107
+
108
+ try:
109
+ cfg = SqlLspConfig.load(root)
110
+ except Exception as e:
111
+ if cached and cached.config:
112
+ if cached.last_error_mtime_ns != mtime_ns:
113
+ cached.last_error_mtime_ns = mtime_ns
114
+ self.report_server_error(e, PyglsError)
115
+ return cached.config
116
+ self.report_server_error(e, PyglsError)
117
+ cfg = SqlLspConfig.default()
118
+
119
+ self._config_cache[root] = _ConfigCacheEntry(mtime_ns=mtime_ns, config=cfg)
120
+ return cfg
121
+
122
+ def dialect_for_document(self, doc_uri: str) -> str:
123
+ root = self._best_root_for_uri(doc_uri)
124
+ if not root:
125
+ return SqlLspConfig.default().default_dialect
126
+ cfg = self._config_for_root(root)
127
+ try:
128
+ fs_path = to_fs_path(doc_uri)
129
+ if not fs_path:
130
+ return cfg.default_dialect
131
+ p = Path(fs_path).resolve()
132
+ rel = p.relative_to(root)
133
+ return cfg.dialect_for_path(str(rel))
134
+ except Exception:
135
+ return cfg.default_dialect
136
+
137
+
138
+ server = OpenCodeSqlLanguageServer()
139
+
140
+
141
+ @server.feature(INITIALIZE)
142
+ def initialize(ls: OpenCodeSqlLanguageServer, params: InitializeParams):
143
+ roots: list[str] = []
144
+ try:
145
+ wf = getattr(params, "workspace_folders", None)
146
+ if isinstance(wf, list):
147
+ for f in wf:
148
+ uri = getattr(f, "uri", None)
149
+ if isinstance(uri, str) and uri:
150
+ try:
151
+ fs_path = to_fs_path(uri)
152
+ if fs_path:
153
+ roots.append(fs_path)
154
+ except Exception:
155
+ continue
156
+ except Exception:
157
+ pass
158
+
159
+ root_uri = getattr(params, "root_uri", None)
160
+ if isinstance(root_uri, str) and root_uri:
161
+ try:
162
+ fs_path = to_fs_path(root_uri)
163
+ if fs_path:
164
+ roots.append(fs_path)
165
+ except Exception:
166
+ pass
167
+
168
+ deduped: list[str] = []
169
+ seen: set[str] = set()
170
+ for r in roots:
171
+ if r not in seen:
172
+ seen.add(r)
173
+ deduped.append(r)
174
+ ls.set_workspace_roots(deduped)
175
+
176
+
177
+ def _is_large_document(source: str) -> bool:
178
+ if len(source.encode("utf-8", errors="ignore")) > _MAX_LINT_BYTES:
179
+ return True
180
+ return source.count("\n") > _MAX_LINT_LINES
181
+
182
+
183
+ def _publish_skipped_diagnostics(ls: OpenCodeSqlLanguageServer, uri: str) -> None:
184
+ ls.text_document_publish_diagnostics(
185
+ PublishDiagnosticsParams(
186
+ uri=uri,
187
+ diagnostics=[
188
+ Diagnostic(
189
+ range=Range(
190
+ start=Position(line=0, character=0),
191
+ end=Position(line=0, character=0),
192
+ ),
193
+ message="Lint skipped (file too large)",
194
+ severity=DiagnosticSeverity.Warning,
195
+ source="opencode-sql-lsp",
196
+ )
197
+ ],
198
+ )
199
+ )
200
+
201
+
202
+ def _safe_position(doc, line_1: int, character: int) -> Position:
203
+ if not getattr(doc, "lines", None):
204
+ return Position(line=0, character=0)
205
+ max_line = max(0, len(doc.lines) - 1)
206
+ line_idx = min(max(0, line_1 - 1), max_line)
207
+ line_text = doc.lines[line_idx] if doc.lines else ""
208
+ char_idx = min(max(0, character), len(line_text))
209
+ return Position(line=line_idx, character=char_idx)
210
+
211
+
212
+ def _issue_range(doc, issue) -> Range:
213
+ start = _safe_position(doc, issue.line, issue.character)
214
+ end_char = start.character + 1
215
+ if getattr(doc, "lines", None):
216
+ line_text = doc.lines[start.line]
217
+ end_char = min(end_char, len(line_text))
218
+ end = Position(line=start.line, character=end_char)
219
+ return Range(start=start, end=end)
220
+
221
+
222
+ async def _run_lint_and_publish(
223
+ ls: OpenCodeSqlLanguageServer,
224
+ uri: str,
225
+ expected_version: int | None,
226
+ ) -> None:
227
+ state = ls._doc_state.setdefault(uri, _DocState())
228
+ try:
229
+ doc = ls.workspace.get_text_document(uri)
230
+ except Exception as e:
231
+ ls.report_server_error(e, PyglsError)
232
+ return
233
+
234
+ source = doc.source
235
+ dialect = ls.dialect_for_document(uri)
236
+ loop = asyncio.get_running_loop()
237
+
238
+ try:
239
+ issues = await loop.run_in_executor(
240
+ ls.thread_pool, lambda: lint_issues(source, dialect=dialect)
241
+ )
242
+ except Exception as e:
243
+ ls.report_server_error(e, PyglsError)
244
+ issues = []
245
+
246
+ if expected_version is not None and state.version != expected_version:
247
+ return
248
+
249
+ diagnostics: list[Diagnostic] = []
250
+ for issue in issues:
251
+ diagnostics.append(
252
+ Diagnostic(
253
+ range=_issue_range(doc, issue),
254
+ message=issue.message,
255
+ severity=DiagnosticSeverity.Error,
256
+ source="sqlfluff",
257
+ )
258
+ )
259
+
260
+ ls.text_document_publish_diagnostics(
261
+ PublishDiagnosticsParams(uri=uri, diagnostics=diagnostics)
262
+ )
263
+
264
+
265
+ def _schedule_diagnostics(
266
+ ls: OpenCodeSqlLanguageServer,
267
+ uri: str,
268
+ version: int | None,
269
+ *,
270
+ debounce_s: float,
271
+ ) -> None:
272
+ state = ls._doc_state.setdefault(uri, _DocState())
273
+ state.version = version
274
+
275
+ if state.pending_timer is not None:
276
+ state.pending_timer.cancel()
277
+ state.pending_timer = None
278
+
279
+ if state.pending_task is not None and not state.pending_task.done():
280
+ pass
281
+
282
+ loop = asyncio.get_running_loop()
283
+
284
+ def kickoff() -> None:
285
+ state.pending_timer = None
286
+ state.pending_task = asyncio.create_task(
287
+ _run_lint_and_publish(ls, uri, expected_version=version)
288
+ )
289
+
290
+ if debounce_s <= 0:
291
+ kickoff()
292
+ else:
293
+ state.pending_timer = loop.call_later(debounce_s, kickoff)
294
+
295
+
296
+ @server.feature(TEXT_DOCUMENT_DID_OPEN)
297
+ def did_open(ls: OpenCodeSqlLanguageServer, params):
298
+ uri = params.text_document.uri
299
+ version = getattr(params.text_document, "version", None)
300
+ try:
301
+ doc = ls.workspace.get_text_document(uri)
302
+ except Exception as e:
303
+ ls.report_server_error(e, PyglsError)
304
+ else:
305
+ if _is_large_document(doc.source):
306
+ _publish_skipped_diagnostics(ls, uri)
307
+ return
308
+ _schedule_diagnostics(ls, uri, version, debounce_s=0.0)
309
+
310
+
311
+ @server.feature(TEXT_DOCUMENT_DID_CHANGE)
312
+ def did_change(ls: OpenCodeSqlLanguageServer, params):
313
+ uri = params.text_document.uri
314
+ version = getattr(params.text_document, "version", None)
315
+ try:
316
+ doc = ls.workspace.get_text_document(uri)
317
+ except Exception as e:
318
+ ls.report_server_error(e, PyglsError)
319
+ else:
320
+ if _is_large_document(doc.source):
321
+ _publish_skipped_diagnostics(ls, uri)
322
+ return
323
+ _schedule_diagnostics(ls, uri, version, debounce_s=_DID_CHANGE_DEBOUNCE_S)
324
+
325
+
326
+ @server.feature(TEXT_DOCUMENT_DID_SAVE)
327
+ def did_save(ls: OpenCodeSqlLanguageServer, params):
328
+ uri = params.text_document.uri
329
+ version = getattr(params.text_document, "version", None)
330
+ try:
331
+ doc = ls.workspace.get_text_document(uri)
332
+ except Exception as e:
333
+ ls.report_server_error(e, PyglsError)
334
+ else:
335
+ if _is_large_document(doc.source):
336
+ _publish_skipped_diagnostics(ls, uri)
337
+ return
338
+ _schedule_diagnostics(ls, uri, version, debounce_s=0.0)
339
+
340
+
341
+ @server.feature(TEXT_DOCUMENT_FORMATTING)
342
+ def formatting(ls: OpenCodeSqlLanguageServer, params: DocumentFormattingParams):
343
+ uri = params.text_document.uri
344
+ try:
345
+ doc = ls.workspace.get_text_document(uri)
346
+ except Exception as e:
347
+ ls.report_server_error(e, PyglsError)
348
+ return []
349
+
350
+ dialect = ls.dialect_for_document(uri)
351
+ try:
352
+ formatted = format_sql(doc.source, dialect=dialect)
353
+ except Exception:
354
+ return []
355
+ last_line = max(0, len(doc.lines) - 1)
356
+ last_char = len(doc.lines[last_line]) if doc.lines else 0
357
+ edit_range = Range(
358
+ start=Position(line=0, character=0),
359
+ end=Position(line=last_line, character=last_char),
360
+ )
361
+ return [TextEdit(range=edit_range, new_text=formatted)]
@@ -1,22 +1,24 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: opencode-sql-lsp-server
3
- Version: 0.1.0
3
+ Version: 0.1.1
4
4
  Summary: SQL Language Server for OpenCode with Trino/StarRocks dialect support
5
5
  Author: OpenCode SQL LSP port
6
- License: MIT
6
+ License-Expression: MIT
7
+ Project-URL: Homepage, https://opencode.ai
7
8
  Keywords: opencode,lsp,sql,sqlfluff,trino,starrocks
8
9
  Classifier: Development Status :: 3 - Alpha
9
10
  Classifier: Environment :: Console
10
11
  Classifier: Intended Audience :: Developers
11
- Classifier: License :: OSI Approved :: MIT License
12
12
  Classifier: Programming Language :: Python :: 3
13
13
  Classifier: Programming Language :: Python :: 3 :: Only
14
14
  Classifier: Topic :: Software Development :: Quality Assurance
15
15
  Requires-Python: >=3.10
16
16
  Description-Content-Type: text/markdown
17
+ License-File: LICENSE
17
18
  Requires-Dist: pygls>=1.3.1
18
19
  Requires-Dist: lsprotocol>=2023.0.1
19
20
  Requires-Dist: sqlfluff>=3.0.0
21
+ Dynamic: license-file
20
22
 
21
23
  # opencode-sql-lsp-server
22
24
 
@@ -1,3 +1,4 @@
1
+ LICENSE
1
2
  README.md
2
3
  pyproject.toml
3
4
  src/opencode_sql_lsp_server/__init__.py
@@ -1,108 +0,0 @@
1
- from __future__ import annotations
2
-
3
- from pathlib import Path
4
- from typing import Optional
5
-
6
- from lsprotocol.types import (
7
- Diagnostic,
8
- DiagnosticSeverity,
9
- DocumentFormattingParams,
10
- InitializeParams,
11
- INITIALIZE,
12
- PublishDiagnosticsParams,
13
- TEXT_DOCUMENT_DID_CHANGE,
14
- TEXT_DOCUMENT_DID_OPEN,
15
- TEXT_DOCUMENT_FORMATTING,
16
- Position,
17
- Range,
18
- TextEdit,
19
- )
20
- from pygls.lsp.server import LanguageServer
21
-
22
- from .config import SqlLspConfig
23
- from .sqlfluff_adapter import format_sql, lint_issues
24
-
25
-
26
- class OpenCodeSqlLanguageServer(LanguageServer):
27
- def __init__(self) -> None:
28
- super().__init__("opencode-sql-lsp", "0.1.0")
29
- self._workspace_root: Optional[Path] = None
30
- self._config: Optional[SqlLspConfig] = None
31
-
32
- def set_workspace_root(self, root: Optional[str]) -> None:
33
- self._workspace_root = Path(root).resolve() if root else None
34
- self._config = (
35
- SqlLspConfig.load(self._workspace_root) if self._workspace_root else None
36
- )
37
-
38
- def dialect_for_document(self, doc_uri: str) -> str:
39
- if not self._workspace_root or not self._config:
40
- return "starrocks"
41
- try:
42
- p = Path(self.uri_to_path(doc_uri)).resolve()
43
- rel = p.relative_to(self._workspace_root)
44
- return self._config.dialect_for_path(str(rel))
45
- except Exception:
46
- return self._config.default_dialect
47
-
48
-
49
- server = OpenCodeSqlLanguageServer()
50
-
51
-
52
- @server.feature(INITIALIZE)
53
- def initialize(ls: OpenCodeSqlLanguageServer, params: InitializeParams):
54
- root_uri = getattr(params, "root_uri", None)
55
- if isinstance(root_uri, str) and root_uri:
56
- try:
57
- ls.set_workspace_root(ls.uri_to_path(root_uri))
58
- except Exception:
59
- pass
60
-
61
-
62
- def _publish_diagnostics(ls: OpenCodeSqlLanguageServer, uri: str) -> None:
63
- doc = ls.workspace.get_text_document(uri)
64
- dialect = ls.dialect_for_document(uri)
65
- issues = lint_issues(doc.source, dialect=dialect)
66
- diagnostics: list[Diagnostic] = []
67
- for issue in issues:
68
- start = Position(line=issue.line - 1, character=issue.character)
69
- end = Position(line=issue.line - 1, character=issue.character + 1)
70
- diagnostics.append(
71
- Diagnostic(
72
- range=Range(start=start, end=end),
73
- message=issue.message,
74
- severity=DiagnosticSeverity.Error,
75
- source="sqlglot",
76
- )
77
- )
78
- ls.text_document_publish_diagnostics(
79
- PublishDiagnosticsParams(uri=uri, diagnostics=diagnostics)
80
- )
81
-
82
-
83
- @server.feature(TEXT_DOCUMENT_DID_OPEN)
84
- def did_open(ls: OpenCodeSqlLanguageServer, params):
85
- _publish_diagnostics(ls, params.text_document.uri)
86
-
87
-
88
- @server.feature(TEXT_DOCUMENT_DID_CHANGE)
89
- def did_change(ls: OpenCodeSqlLanguageServer, params):
90
- _publish_diagnostics(ls, params.text_document.uri)
91
-
92
-
93
- @server.feature(TEXT_DOCUMENT_FORMATTING)
94
- def formatting(ls: OpenCodeSqlLanguageServer, params: DocumentFormattingParams):
95
- doc = ls.workspace.get_text_document(params.text_document.uri)
96
- dialect = ls.dialect_for_document(params.text_document.uri)
97
- try:
98
- formatted = format_sql(doc.source, dialect=dialect)
99
- except Exception:
100
- return []
101
- # Replace entire document
102
- last_line = max(0, len(doc.lines) - 1)
103
- last_char = len(doc.lines[last_line]) if doc.lines else 0
104
- edit_range = Range(
105
- start=Position(line=0, character=0),
106
- end=Position(line=last_line, character=last_char),
107
- )
108
- return [TextEdit(range=edit_range, new_text=formatted)]