lispython 0.3.3__tar.gz → 0.4.0__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.
- lispython-0.4.0/.vscode/settings.json +3 -0
- {lispython-0.3.3 → lispython-0.4.0}/PKG-INFO +2 -1
- {lispython-0.3.3 → lispython-0.4.0}/pyproject.toml +3 -2
- lispython-0.4.0/src/lispy/lsp/__init__.py +6 -0
- lispython-0.4.0/src/lispy/lsp/__main__.py +3 -0
- lispython-0.4.0/src/lispy/lsp/server.lpy +721 -0
- {lispython-0.3.3 → lispython-0.4.0}/uv.lock +62 -0
- {lispython-0.3.3 → lispython-0.4.0}/.claude/settings.local.json +0 -0
- {lispython-0.3.3 → lispython-0.4.0}/.gitignore +0 -0
- {lispython-0.3.3 → lispython-0.4.0}/.pre-commit-config.yaml +0 -0
- {lispython-0.3.3 → lispython-0.4.0}/LICENSE.md +0 -0
- {lispython-0.3.3 → lispython-0.4.0}/README.md +0 -0
- {lispython-0.3.3 → lispython-0.4.0}/docs/index.md +0 -0
- {lispython-0.3.3 → lispython-0.4.0}/docs/macros.md +0 -0
- {lispython-0.3.3 → lispython-0.4.0}/docs/syntax/expressions.md +0 -0
- {lispython-0.3.3 → lispython-0.4.0}/docs/syntax/overview.md +0 -0
- {lispython-0.3.3 → lispython-0.4.0}/docs/syntax/statements.md +0 -0
- {lispython-0.3.3 → lispython-0.4.0}/docs/usage/cli.md +0 -0
- {lispython-0.3.3 → lispython-0.4.0}/docs/usage/getting-started.md +0 -0
- {lispython-0.3.3 → lispython-0.4.0}/docs/version_macro.py +0 -0
- {lispython-0.3.3 → lispython-0.4.0}/docs/why-lispy.md +0 -0
- {lispython-0.3.3 → lispython-0.4.0}/mkdocs.yml +0 -0
- {lispython-0.3.3 → lispython-0.4.0}/src/lispy/__init__.py +0 -0
- {lispython-0.3.3 → lispython-0.4.0}/src/lispy/core/compiler/__init__.py +0 -0
- {lispython-0.3.3 → lispython-0.4.0}/src/lispy/core/compiler/expr.py +0 -0
- {lispython-0.3.3 → lispython-0.4.0}/src/lispy/core/compiler/literal.py +0 -0
- {lispython-0.3.3 → lispython-0.4.0}/src/lispy/core/compiler/stmt.py +0 -0
- {lispython-0.3.3 → lispython-0.4.0}/src/lispy/core/compiler/utils.py +0 -0
- {lispython-0.3.3 → lispython-0.4.0}/src/lispy/core/importer.py +0 -0
- {lispython-0.3.3 → lispython-0.4.0}/src/lispy/core/macro.py +0 -0
- {lispython-0.3.3 → lispython-0.4.0}/src/lispy/core/meta_functions.py +0 -0
- {lispython-0.3.3 → lispython-0.4.0}/src/lispy/core/nodes.py +0 -0
- {lispython-0.3.3 → lispython-0.4.0}/src/lispy/core/parser.py +0 -0
- {lispython-0.3.3 → lispython-0.4.0}/src/lispy/core/utils.py +0 -0
- {lispython-0.3.3 → lispython-0.4.0}/src/lispy/core_meta_functions.lpy +0 -0
- {lispython-0.3.3 → lispython-0.4.0}/src/lispy/macros/__init__.py +0 -0
- {lispython-0.3.3 → lispython-0.4.0}/src/lispy/macros/init.lpy +0 -0
- {lispython-0.3.3 → lispython-0.4.0}/src/lispy/macros/sugar.lpy +0 -0
- {lispython-0.3.3 → lispython-0.4.0}/src/lispy/tools.lpy +0 -0
- {lispython-0.3.3 → lispython-0.4.0}/tests/__init__.py +0 -0
- {lispython-0.3.3 → lispython-0.4.0}/tests/test_expr.py +0 -0
- {lispython-0.3.3 → lispython-0.4.0}/tests/test_include_meta.py +0 -0
- {lispython-0.3.3 → lispython-0.4.0}/tests/test_literal.py +0 -0
- {lispython-0.3.3 → lispython-0.4.0}/tests/test_meta_functions.py +0 -0
- {lispython-0.3.3 → lispython-0.4.0}/tests/test_parser.py +0 -0
- {lispython-0.3.3 → lispython-0.4.0}/tests/test_stmt.py +0 -0
- {lispython-0.3.3 → lispython-0.4.0}/tests/utils.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: lispython
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.4.0
|
|
4
4
|
Summary: Lisp-like Syntax for Python with Lisp-like Macros
|
|
5
5
|
Project-URL: Homepage, https://jetack.github.io/lispython
|
|
6
6
|
Project-URL: Repository, https://github.com/jetack/lispython
|
|
@@ -8,6 +8,7 @@ Author-email: Jetack <jetack23@gmail.com>
|
|
|
8
8
|
License: MIT
|
|
9
9
|
License-File: LICENSE.md
|
|
10
10
|
Requires-Python: >=3.11
|
|
11
|
+
Requires-Dist: pygls>=1.0.0
|
|
11
12
|
Provides-Extra: dev
|
|
12
13
|
Requires-Dist: pre-commit>=3.6.0; extra == 'dev'
|
|
13
14
|
Requires-Dist: pytest>=8.0.0; extra == 'dev'
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "lispython"
|
|
3
|
-
version = "0.
|
|
3
|
+
version = "0.4.0"
|
|
4
4
|
description = "Lisp-like Syntax for Python with Lisp-like Macros"
|
|
5
5
|
authors = [{ name = "Jetack", email = "jetack23@gmail.com" }]
|
|
6
6
|
license = { text = "MIT" }
|
|
7
7
|
readme = "README.md"
|
|
8
8
|
requires-python = ">=3.11"
|
|
9
|
-
dependencies = []
|
|
9
|
+
dependencies = ["pygls>=1.0.0"]
|
|
10
10
|
|
|
11
11
|
[project.urls]
|
|
12
12
|
Homepage = "https://jetack.github.io/lispython"
|
|
@@ -15,6 +15,7 @@ Repository = "https://github.com/jetack/lispython"
|
|
|
15
15
|
[project.scripts]
|
|
16
16
|
lpy = "lispy:run"
|
|
17
17
|
l2py = "lispy:l2py"
|
|
18
|
+
lpy-lsp = "lispy.lsp:main"
|
|
18
19
|
test = "pytest:main"
|
|
19
20
|
|
|
20
21
|
[project.optional-dependencies]
|
|
@@ -0,0 +1,721 @@
|
|
|
1
|
+
(require lispy.macros *)
|
|
2
|
+
|
|
3
|
+
(import builtins)
|
|
4
|
+
(import glob)
|
|
5
|
+
(import inspect)
|
|
6
|
+
(import logging)
|
|
7
|
+
(import os)
|
|
8
|
+
(import os.path as osp)
|
|
9
|
+
(import re)
|
|
10
|
+
(import pathlib)
|
|
11
|
+
|
|
12
|
+
(from lsprotocol [types as lsp])
|
|
13
|
+
(from pygls.lsp.server [LanguageServer])
|
|
14
|
+
|
|
15
|
+
(from lispy.core.nodes *)
|
|
16
|
+
(from lispy.core.parser [parse])
|
|
17
|
+
|
|
18
|
+
(= logger (logging.getLogger __name__))
|
|
19
|
+
|
|
20
|
+
;; ---------------------------------------------------------------------------
|
|
21
|
+
;; Server instance
|
|
22
|
+
;; ---------------------------------------------------------------------------
|
|
23
|
+
|
|
24
|
+
(= server (LanguageServer "lpy-lsp" "v0.1.0"))
|
|
25
|
+
|
|
26
|
+
;; ---------------------------------------------------------------------------
|
|
27
|
+
;; Special-form documentation (for hover)
|
|
28
|
+
;; ---------------------------------------------------------------------------
|
|
29
|
+
|
|
30
|
+
(= SPECIAL-FORM-DOCS
|
|
31
|
+
{;; Statement forms
|
|
32
|
+
"def" "```\n(def name [params] body...)\n```\n\nDefine a function. Parameters are given in a bracket list. An optional docstring can follow the parameter list."
|
|
33
|
+
"async-def" "```\n(async-def name [params] body...)\n```\n\nDefine an async function (coroutine)."
|
|
34
|
+
"class" "```\n(class Name [bases] body...)\n```\n\nDefine a class. Bases are given in a bracket list."
|
|
35
|
+
"defmacro" "```\n(defmacro name [params] body...)\n```\n\nDefine a compile-time macro. The macro receives unevaluated S-expressions and must return an S-expression."
|
|
36
|
+
"if" "```\n(if condition then-body)\n(if condition then-body else-body)\n```\n\nConditional statement. Compiles to Python `if/else`."
|
|
37
|
+
"while" "```\n(while condition body...)\n```\n\nWhile loop."
|
|
38
|
+
"for" "```\n(for target iterable body...)\n```\n\nFor loop. `target` is bound to each element of `iterable`."
|
|
39
|
+
"async-for" "```\n(async-for target iterable body...)\n```\n\nAsync for loop — use inside `async-def`."
|
|
40
|
+
"do" "```\n(do body...)\n```\n\nExecute a sequence of statements (block). Used where a single expression is expected but multiple statements are needed."
|
|
41
|
+
"match" "```\n(match subject case...)\n```\n\nStructural pattern matching (Python 3.10+)."
|
|
42
|
+
"try" "```\n(try body except-clauses... [finally-clause])\n```\n\nTry/except/finally statement."
|
|
43
|
+
"with" "```\n(with [ctx-expr as-name] body...)\n```\n\nContext manager statement."
|
|
44
|
+
"async-with" "```\n(async-with [ctx-expr as-name] body...)\n```\n\nAsync context manager — use inside `async-def`."
|
|
45
|
+
"import" "```\n(import module)\n(import module :as alias)\n```\n\nImport a module."
|
|
46
|
+
"from" "```\n(from module [names...])\n(from module [name :as alias ...])\n```\n\nImport specific names from a module."
|
|
47
|
+
"require" "```\n(require module *)\n(require module [names...])\n```\n\nImport macros from a LisPython macro module at compile time."
|
|
48
|
+
"=" "```\n(= target value)\n```\n\nAssignment statement."
|
|
49
|
+
":=" "```\n(:= target value)\n```\n\nWalrus operator (assignment expression)."
|
|
50
|
+
"return" "```\n(return value)\n```\n\nReturn a value from a function."
|
|
51
|
+
"raise" "```\n(raise exception)\n```\n\nRaise an exception."
|
|
52
|
+
"assert" "```\n(assert condition)\n(assert condition message)\n```\n\nAssert that a condition is true."
|
|
53
|
+
"del" "```\n(del target)\n```\n\nDelete a name or item."
|
|
54
|
+
"pass" "```\n(pass)\n```\n\nNo-op placeholder statement."
|
|
55
|
+
"break" "```\n(break)\n```\n\nBreak out of a loop."
|
|
56
|
+
"continue" "```\n(continue)\n```\n\nContinue to the next loop iteration."
|
|
57
|
+
"global" "```\n(global name...)\n```\n\nDeclare names as global variables."
|
|
58
|
+
"nonlocal" "```\n(nonlocal name...)\n```\n\nDeclare names as nonlocal variables."
|
|
59
|
+
"yield" "```\n(yield value)\n```\n\nYield a value from a generator."
|
|
60
|
+
"yield-from" "```\n(yield-from iterable)\n```\n\nYield all values from a sub-generator."
|
|
61
|
+
"await" "```\n(await expr)\n```\n\nAwait a coroutine."
|
|
62
|
+
"deco" "```\n(deco decorator (def ...))\n```\n\nApply a decorator to the following function/class definition."
|
|
63
|
+
"lambda" "```\n(lambda [params] body)\n```\n\nAnonymous function (lambda expression)."
|
|
64
|
+
;; Expression forms
|
|
65
|
+
"ife" "```\n(ife condition then-expr else-expr)\n```\n\nTernary conditional expression (`then-expr if condition else else-expr`)."
|
|
66
|
+
"." "```\n(. obj attr)\n```\n\nAttribute access (`obj.attr`)."
|
|
67
|
+
"sub" "```\n(sub obj index)\n```\n\nSubscript access (`obj[index]`)."
|
|
68
|
+
"," "```\n(, a b c)\n```\n\nTuple literal."
|
|
69
|
+
"fn" "```\n(fn [params] body)\n```\n\nLambda alias — same as `lambda`."
|
|
70
|
+
;; Operators
|
|
71
|
+
"+" "Arithmetic addition / unary positive."
|
|
72
|
+
"-" "Arithmetic subtraction / unary negative."
|
|
73
|
+
"*" "Arithmetic multiplication / iterable unpacking."
|
|
74
|
+
"/" "True division."
|
|
75
|
+
"//" "Floor (integer) division."
|
|
76
|
+
"%" "Modulo (remainder)."
|
|
77
|
+
"**" "Exponentiation."
|
|
78
|
+
"@" "Matrix multiplication."
|
|
79
|
+
"<<" "Bitwise left shift."
|
|
80
|
+
">>" "Bitwise right shift."
|
|
81
|
+
"|" "Bitwise OR."
|
|
82
|
+
"^" "Bitwise XOR."
|
|
83
|
+
"&" "Bitwise AND."
|
|
84
|
+
"~" "Bitwise NOT / unquote (inside quasiquote)."
|
|
85
|
+
"and" "Logical AND (short-circuit)."
|
|
86
|
+
"or" "Logical OR (short-circuit)."
|
|
87
|
+
"not" "Logical NOT."
|
|
88
|
+
"==" "Equality test."
|
|
89
|
+
"!=" "Inequality test."
|
|
90
|
+
"<" "Less than."
|
|
91
|
+
"<=" "Less than or equal."
|
|
92
|
+
">" "Greater than."
|
|
93
|
+
">=" "Greater than or equal."
|
|
94
|
+
"is" "Identity test (`x is y`)."
|
|
95
|
+
"is-not" "Negated identity test (`x is not y`)."
|
|
96
|
+
"in" "Membership test (`x in y`)."
|
|
97
|
+
"not-in" "Negated membership test (`x not in y`)."
|
|
98
|
+
;; Macro-related
|
|
99
|
+
"'" "Quote — prevent evaluation; return the form as data."
|
|
100
|
+
"`" "Quasiquote — like quote but allows unquote (`~`) splicing."
|
|
101
|
+
"~@" "Unquote-splice — splice a list into a quasiquote."
|
|
102
|
+
"f-string" "```\nf\"text {expr} more\"\n```\n\nFormatted string literal (f-string)."})
|
|
103
|
+
|
|
104
|
+
;; ---------------------------------------------------------------------------
|
|
105
|
+
;; Python builtins introspection
|
|
106
|
+
;; ---------------------------------------------------------------------------
|
|
107
|
+
|
|
108
|
+
(def build-builtin-info []
|
|
109
|
+
"Build a dict of Python builtin names -> {signature, doc, source_file, lineno}."
|
|
110
|
+
(= info {})
|
|
111
|
+
(for name in (dir builtins)
|
|
112
|
+
(if (.startswith name "_")
|
|
113
|
+
(continue))
|
|
114
|
+
(= obj (getattr builtins name))
|
|
115
|
+
(if (and (not (callable obj)) (not (isinstance obj type)))
|
|
116
|
+
(continue))
|
|
117
|
+
(= entry {"name" name})
|
|
118
|
+
;; Signature
|
|
119
|
+
(try
|
|
120
|
+
(do (= sig (inspect.signature obj))
|
|
121
|
+
(= (sub entry "signature") f"{name}{sig}"))
|
|
122
|
+
(except [(, ValueError TypeError)]
|
|
123
|
+
(= (sub entry "signature") name)))
|
|
124
|
+
;; Docstring
|
|
125
|
+
(= doc (inspect.getdoc obj))
|
|
126
|
+
(if doc
|
|
127
|
+
(= (sub entry "doc") (sub (.split doc "\n\n") 0))
|
|
128
|
+
(= (sub entry "doc") ""))
|
|
129
|
+
;; Source file
|
|
130
|
+
(try
|
|
131
|
+
(do (= source-file (inspect.getfile obj))
|
|
132
|
+
(try
|
|
133
|
+
(do (= (, _ lineno) (inspect.getsourcelines obj))
|
|
134
|
+
(= (sub entry "lineno") lineno))
|
|
135
|
+
(except [(, OSError TypeError)]
|
|
136
|
+
(= (sub entry "lineno") 1)))
|
|
137
|
+
(= (sub entry "source_file") source-file))
|
|
138
|
+
(except [(, TypeError OSError)]
|
|
139
|
+
(= (sub entry "source_file") None)
|
|
140
|
+
(= (sub entry "lineno") None)))
|
|
141
|
+
(= (sub info name) entry))
|
|
142
|
+
(return info))
|
|
143
|
+
|
|
144
|
+
(= BUILTIN-INFO (build-builtin-info))
|
|
145
|
+
|
|
146
|
+
;; ---------------------------------------------------------------------------
|
|
147
|
+
;; Workspace index
|
|
148
|
+
;; ---------------------------------------------------------------------------
|
|
149
|
+
|
|
150
|
+
;; Global index: {symbol_name: [Location, ...]} across all .lpy files
|
|
151
|
+
(= WORKSPACE-INDEX {})
|
|
152
|
+
;; Cache of parsed trees per URI: {uri: tree}
|
|
153
|
+
(= FILE-TREES {})
|
|
154
|
+
|
|
155
|
+
(def uri-to-path [uri]
|
|
156
|
+
"Convert a file:// URI to a filesystem path."
|
|
157
|
+
(if (.startswith uri "file://")
|
|
158
|
+
(return (sub uri [: 7 None]))
|
|
159
|
+
(return uri)))
|
|
160
|
+
|
|
161
|
+
(def path-to-uri [path]
|
|
162
|
+
"Convert a filesystem path to a file:// URI."
|
|
163
|
+
(return (.as-uri (pathlib.Path path))))
|
|
164
|
+
|
|
165
|
+
(def index-file [uri]
|
|
166
|
+
"Parse a .lpy file and add its definitions to the workspace index.
|
|
167
|
+
Removes old entries for this URI before re-indexing."
|
|
168
|
+
;; Remove old entries for this URI
|
|
169
|
+
(= to-remove [])
|
|
170
|
+
(for [name locs] in (.items WORKSPACE-INDEX)
|
|
171
|
+
(= (sub WORKSPACE-INDEX name)
|
|
172
|
+
[loc for loc in locs if (!= loc.uri uri)])
|
|
173
|
+
(if (== (len (sub WORKSPACE-INDEX name)) 0)
|
|
174
|
+
(.append to-remove name)))
|
|
175
|
+
(for name in to-remove
|
|
176
|
+
(del (sub WORKSPACE-INDEX name)))
|
|
177
|
+
|
|
178
|
+
;; Parse and collect definitions
|
|
179
|
+
(= fpath (uri-to-path uri))
|
|
180
|
+
(if (not (osp.isfile fpath))
|
|
181
|
+
(return))
|
|
182
|
+
(try
|
|
183
|
+
(with [(open fpath "rb") as f]
|
|
184
|
+
(= src (.decode (f.read) "utf-8")))
|
|
185
|
+
(= tree (parse src))
|
|
186
|
+
(= (sub FILE-TREES uri) tree)
|
|
187
|
+
(collect-definitions tree uri :defs WORKSPACE-INDEX)
|
|
188
|
+
(except [Exception as exc]
|
|
189
|
+
(logger.debug f"Failed to index {fpath}: {exc}"))))
|
|
190
|
+
|
|
191
|
+
(def index-workspace [workspace-folders]
|
|
192
|
+
"Scan all .lpy files in workspace folders and index them."
|
|
193
|
+
(for folder in workspace-folders
|
|
194
|
+
(= root (uri-to-path folder.uri))
|
|
195
|
+
(for fpath in (glob.glob (osp.join root "**" "*.lpy") :recursive True)
|
|
196
|
+
(index-file (path-to-uri fpath))))
|
|
197
|
+
(logger.info f"Indexed {(len WORKSPACE-INDEX)} symbols from workspace"))
|
|
198
|
+
|
|
199
|
+
(def resolve-module-to-file [module-name workspace-folders]
|
|
200
|
+
"Resolve a LisPython module name to a .lpy file path.
|
|
201
|
+
e.g., 'lispy.tools' -> '/path/to/src/lispy/tools.lpy'"
|
|
202
|
+
(= parts (.split (.replace module-name "-" "_") "."))
|
|
203
|
+
(= rel-path (+ (osp.join *parts) ".lpy"))
|
|
204
|
+
;; Search in workspace folders and sys.path-like locations
|
|
205
|
+
(for folder in workspace-folders
|
|
206
|
+
(= root (uri-to-path folder.uri))
|
|
207
|
+
;; Direct match
|
|
208
|
+
(= candidate (osp.join root rel-path))
|
|
209
|
+
(if (osp.isfile candidate)
|
|
210
|
+
(return candidate))
|
|
211
|
+
;; Check in src/ subdirectory
|
|
212
|
+
(= candidate (osp.join root "src" rel-path))
|
|
213
|
+
(if (osp.isfile candidate)
|
|
214
|
+
(return candidate)))
|
|
215
|
+
(return None))
|
|
216
|
+
|
|
217
|
+
(def find-import-target [node workspace-folders]
|
|
218
|
+
"Given an import/from/require node, resolve where the imported name is defined."
|
|
219
|
+
(= form (. node op value))
|
|
220
|
+
(cond
|
|
221
|
+
(== form "from")
|
|
222
|
+
(do ;; (from module [names...])
|
|
223
|
+
(if (< (len node) 3)
|
|
224
|
+
(return None))
|
|
225
|
+
(= module-name (. (sub node 1) value))
|
|
226
|
+
(= fpath (resolve-module-to-file module-name workspace-folders))
|
|
227
|
+
(if fpath
|
|
228
|
+
(return fpath))
|
|
229
|
+
(return None))
|
|
230
|
+
|
|
231
|
+
(== form "import")
|
|
232
|
+
(do ;; (import module)
|
|
233
|
+
(if (< (len node) 2)
|
|
234
|
+
(return None))
|
|
235
|
+
(= module-name (. (sub node 1) value))
|
|
236
|
+
(= fpath (resolve-module-to-file module-name workspace-folders))
|
|
237
|
+
(if fpath
|
|
238
|
+
(return fpath))
|
|
239
|
+
(return None))
|
|
240
|
+
|
|
241
|
+
(== form "require")
|
|
242
|
+
(do ;; (require module *)
|
|
243
|
+
(if (< (len node) 2)
|
|
244
|
+
(return None))
|
|
245
|
+
(= module-name (. (sub node 1) value))
|
|
246
|
+
(= fpath (resolve-module-to-file module-name workspace-folders))
|
|
247
|
+
(if fpath
|
|
248
|
+
(return fpath))
|
|
249
|
+
(return None))
|
|
250
|
+
|
|
251
|
+
(return None)))
|
|
252
|
+
|
|
253
|
+
(def find-import-for-name [tree name]
|
|
254
|
+
"Find the import/from/require node that imports a given name."
|
|
255
|
+
(for node in tree
|
|
256
|
+
(if (not (isinstance node Paren))
|
|
257
|
+
(continue))
|
|
258
|
+
(if (== (len node) 0)
|
|
259
|
+
(continue))
|
|
260
|
+
(= form (ife (isinstance node.op Symbol) node.op.value ""))
|
|
261
|
+
(cond
|
|
262
|
+
(== form "from")
|
|
263
|
+
(do ;; (from module [names...])
|
|
264
|
+
(for child in (sub node.list [: 1 None])
|
|
265
|
+
(if (isinstance child Bracket)
|
|
266
|
+
(for item in child.list
|
|
267
|
+
(if (and (isinstance item Symbol) (== item.value name))
|
|
268
|
+
(return node))))))
|
|
269
|
+
(== form "import")
|
|
270
|
+
(do ;; (import module) or (import module as alias)
|
|
271
|
+
(for child in (sub node.list [: 1 None])
|
|
272
|
+
(if (and (isinstance child Symbol) (== child.value name))
|
|
273
|
+
(return node))))
|
|
274
|
+
(== form "require")
|
|
275
|
+
(do ;; (require module *) - imports all macros
|
|
276
|
+
(return node))))
|
|
277
|
+
(return None))
|
|
278
|
+
|
|
279
|
+
;; ---------------------------------------------------------------------------
|
|
280
|
+
;; Helpers
|
|
281
|
+
;; ---------------------------------------------------------------------------
|
|
282
|
+
|
|
283
|
+
(def make-position [line char]
|
|
284
|
+
(return (lsp.Position :line line :character char)))
|
|
285
|
+
|
|
286
|
+
(def make-range [start-line start-char end-line end-char]
|
|
287
|
+
(return (lsp.Range
|
|
288
|
+
:start (make-position start-line start-char)
|
|
289
|
+
:end (make-position end-line end-char))))
|
|
290
|
+
|
|
291
|
+
(def node-range [node]
|
|
292
|
+
"Convert a LisPython AST node's position info to an LSP Range.
|
|
293
|
+
The parser uses 1-based line numbers; LSP uses 0-based."
|
|
294
|
+
(return (make-range
|
|
295
|
+
(- node.lineno 1)
|
|
296
|
+
node.col_offset
|
|
297
|
+
(- node.end_lineno 1)
|
|
298
|
+
node.end_col_offset)))
|
|
299
|
+
|
|
300
|
+
(def extract-error-position [exc src]
|
|
301
|
+
"Best-effort extraction of line/col from a parse or compile error."
|
|
302
|
+
;; Some Python exceptions carry lineno/offset
|
|
303
|
+
(= lineno (getattr exc "lineno" None))
|
|
304
|
+
(if (is-not lineno None)
|
|
305
|
+
(do (= col (- (or (getattr exc "offset" None) 1) 1))
|
|
306
|
+
(return (, (- lineno 1) col (- lineno 1) col))))
|
|
307
|
+
;; Try to extract from the message
|
|
308
|
+
(= msg (str exc))
|
|
309
|
+
(= m (re.search "line\\s+(\\d+)" msg re.IGNORECASE))
|
|
310
|
+
(if m
|
|
311
|
+
(do (= line (- (int (.group m 1)) 1))
|
|
312
|
+
(= col 0)
|
|
313
|
+
(= mc (re.search "col(?:umn)?\\s+(\\d+)" msg re.IGNORECASE))
|
|
314
|
+
(if mc
|
|
315
|
+
(= col (int (.group mc 1))))
|
|
316
|
+
(return (, line col line col))))
|
|
317
|
+
(return None))
|
|
318
|
+
|
|
319
|
+
;; ---------------------------------------------------------------------------
|
|
320
|
+
;; Diagnostics
|
|
321
|
+
;; ---------------------------------------------------------------------------
|
|
322
|
+
|
|
323
|
+
(def compute-diagnostics [source uri]
|
|
324
|
+
(= diagnostics [])
|
|
325
|
+
;; 1. Parse
|
|
326
|
+
(try
|
|
327
|
+
(= tree (parse source))
|
|
328
|
+
(except [Exception as exc]
|
|
329
|
+
(= pos (extract-error-position exc source))
|
|
330
|
+
(if (is pos None)
|
|
331
|
+
(= pos (, 0 0 0 0)))
|
|
332
|
+
(.append diagnostics
|
|
333
|
+
(lsp.Diagnostic
|
|
334
|
+
:range (make-range *pos)
|
|
335
|
+
:severity lsp.DiagnosticSeverity.Error
|
|
336
|
+
:source "lpy-lsp"
|
|
337
|
+
:message f"Parse error: {exc}"))
|
|
338
|
+
(return diagnostics)))
|
|
339
|
+
;; 2. Compile (best-effort)
|
|
340
|
+
(try
|
|
341
|
+
(do (from lispy.core.macro [macroexpand-then-compile])
|
|
342
|
+
(macroexpand-then-compile tree))
|
|
343
|
+
(except [Exception as exc]
|
|
344
|
+
(= pos (extract-error-position exc source))
|
|
345
|
+
(if (is pos None)
|
|
346
|
+
(= pos (, 0 0 0 0)))
|
|
347
|
+
(.append diagnostics
|
|
348
|
+
(lsp.Diagnostic
|
|
349
|
+
:range (make-range *pos)
|
|
350
|
+
:severity lsp.DiagnosticSeverity.Error
|
|
351
|
+
:source "lpy-lsp"
|
|
352
|
+
:message f"Compile error: {exc}"))))
|
|
353
|
+
(return diagnostics))
|
|
354
|
+
|
|
355
|
+
(def publish-diagnostics [ls uri]
|
|
356
|
+
(= doc (.get-text-document ls.workspace uri))
|
|
357
|
+
(= diagnostics (compute-diagnostics doc.source uri))
|
|
358
|
+
(.text-document-publish-diagnostics ls
|
|
359
|
+
(lsp.PublishDiagnosticsParams :uri uri :diagnostics diagnostics)))
|
|
360
|
+
|
|
361
|
+
;; ---------------------------------------------------------------------------
|
|
362
|
+
;; Document symbols
|
|
363
|
+
;; ---------------------------------------------------------------------------
|
|
364
|
+
|
|
365
|
+
(def symbol-name [node]
|
|
366
|
+
"Extract a string name from a node."
|
|
367
|
+
(cond (isinstance node Symbol)
|
|
368
|
+
(return node.value)
|
|
369
|
+
(isinstance node Annotation)
|
|
370
|
+
(return (symbol-name node.value))
|
|
371
|
+
(isinstance node String)
|
|
372
|
+
(return (.strip node.value "\"'"))
|
|
373
|
+
(return (str node))))
|
|
374
|
+
|
|
375
|
+
(def walk-symbols [nodes]
|
|
376
|
+
"Walk top-level parsed forms and produce DocumentSymbol entries."
|
|
377
|
+
(= symbols [])
|
|
378
|
+
(for node in nodes
|
|
379
|
+
(if (or (not (isinstance node Paren)) (== (len node) 0))
|
|
380
|
+
(continue))
|
|
381
|
+
(= op node.op)
|
|
382
|
+
(if (not (isinstance op Symbol))
|
|
383
|
+
(continue))
|
|
384
|
+
(= form op.value)
|
|
385
|
+
|
|
386
|
+
(cond
|
|
387
|
+
(in form (, "def" "async-def"))
|
|
388
|
+
(do (if (< (len node) 2) (continue))
|
|
389
|
+
(= name (symbol-name (sub node 1)))
|
|
390
|
+
(= rng (node-range node))
|
|
391
|
+
(= sel (node-range (sub node 1)))
|
|
392
|
+
(= children (ife (> (len node) 3)
|
|
393
|
+
(walk-symbols (sub node.list [: 3 None]))
|
|
394
|
+
[]))
|
|
395
|
+
(.append symbols
|
|
396
|
+
(lsp.DocumentSymbol
|
|
397
|
+
:name name
|
|
398
|
+
:kind lsp.SymbolKind.Function
|
|
399
|
+
:range rng
|
|
400
|
+
:selection-range sel
|
|
401
|
+
:children (or children None))))
|
|
402
|
+
|
|
403
|
+
(== form "class")
|
|
404
|
+
(do (if (< (len node) 2) (continue))
|
|
405
|
+
(= name (symbol-name (sub node 1)))
|
|
406
|
+
(= rng (node-range node))
|
|
407
|
+
(= sel (node-range (sub node 1)))
|
|
408
|
+
(= body-nodes (ife (> (len node) 3)
|
|
409
|
+
(sub node.list [: 3 None])
|
|
410
|
+
(sub node.list [: 2 None])))
|
|
411
|
+
(= children (walk-symbols body-nodes))
|
|
412
|
+
(.append symbols
|
|
413
|
+
(lsp.DocumentSymbol
|
|
414
|
+
:name name
|
|
415
|
+
:kind lsp.SymbolKind.Class
|
|
416
|
+
:range rng
|
|
417
|
+
:selection-range sel
|
|
418
|
+
:children (or children None))))
|
|
419
|
+
|
|
420
|
+
(== form "defmacro")
|
|
421
|
+
(do (if (< (len node) 2) (continue))
|
|
422
|
+
(.append symbols
|
|
423
|
+
(lsp.DocumentSymbol
|
|
424
|
+
:name (symbol-name (sub node 1))
|
|
425
|
+
:kind lsp.SymbolKind.Function
|
|
426
|
+
:range (node-range node)
|
|
427
|
+
:selection-range (node-range (sub node 1))
|
|
428
|
+
:detail "macro")))
|
|
429
|
+
|
|
430
|
+
(== form "=")
|
|
431
|
+
(do (if (< (len node) 2) (continue))
|
|
432
|
+
(.append symbols
|
|
433
|
+
(lsp.DocumentSymbol
|
|
434
|
+
:name (symbol-name (sub node 1))
|
|
435
|
+
:kind lsp.SymbolKind.Variable
|
|
436
|
+
:range (node-range node)
|
|
437
|
+
:selection-range (node-range (sub node 1)))))
|
|
438
|
+
|
|
439
|
+
(in form (, "import" "from"))
|
|
440
|
+
(do (if (< (len node) 2) (continue))
|
|
441
|
+
(.append symbols
|
|
442
|
+
(lsp.DocumentSymbol
|
|
443
|
+
:name (symbol-name (sub node 1))
|
|
444
|
+
:kind lsp.SymbolKind.Module
|
|
445
|
+
:range (node-range node)
|
|
446
|
+
:selection-range (node-range (sub node 1)))))
|
|
447
|
+
|
|
448
|
+
(== form "require")
|
|
449
|
+
(do (if (< (len node) 2) (continue))
|
|
450
|
+
(.append symbols
|
|
451
|
+
(lsp.DocumentSymbol
|
|
452
|
+
:name (symbol-name (sub node 1))
|
|
453
|
+
:kind lsp.SymbolKind.Module
|
|
454
|
+
:range (node-range node)
|
|
455
|
+
:selection-range (node-range (sub node 1))
|
|
456
|
+
:detail "macro require")))))
|
|
457
|
+
|
|
458
|
+
(return symbols))
|
|
459
|
+
|
|
460
|
+
;; ---------------------------------------------------------------------------
|
|
461
|
+
;; Hover
|
|
462
|
+
;; ---------------------------------------------------------------------------
|
|
463
|
+
|
|
464
|
+
(def node-at-position [nodes line-0 col]
|
|
465
|
+
"Find the most specific node at the given 0-based position."
|
|
466
|
+
(for node in nodes
|
|
467
|
+
(= n-start-line (- node.lineno 1))
|
|
468
|
+
(= n-start-col node.col_offset)
|
|
469
|
+
(= n-end-line (- node.end_lineno 1))
|
|
470
|
+
(= n-end-col node.end_col_offset)
|
|
471
|
+
;; Check if position is inside this node's range
|
|
472
|
+
(if (< (, line-0 col) (, n-start-line n-start-col))
|
|
473
|
+
(continue))
|
|
474
|
+
(if (> (, line-0 col) (, n-end-line n-end-col))
|
|
475
|
+
(continue))
|
|
476
|
+
;; Position is within this node — try to go deeper
|
|
477
|
+
(if (isinstance node Expression)
|
|
478
|
+
(do (= deeper (node-at-position node.list line-0 col))
|
|
479
|
+
(if (is-not deeper None)
|
|
480
|
+
(return deeper))))
|
|
481
|
+
(if (isinstance node (, Wrapper MetaIndicator))
|
|
482
|
+
(do (= deeper (node-at-position [node.value] line-0 col))
|
|
483
|
+
(if (is-not deeper None)
|
|
484
|
+
(return deeper))))
|
|
485
|
+
(return node))
|
|
486
|
+
(return None))
|
|
487
|
+
|
|
488
|
+
;; ---------------------------------------------------------------------------
|
|
489
|
+
;; Definition index
|
|
490
|
+
;; ---------------------------------------------------------------------------
|
|
491
|
+
|
|
492
|
+
(def collect-definitions [nodes uri :defs None]
|
|
493
|
+
"Walk AST and collect all definition locations keyed by symbol name."
|
|
494
|
+
(if (is defs None)
|
|
495
|
+
(= defs {}))
|
|
496
|
+
|
|
497
|
+
(for node in nodes
|
|
498
|
+
(if (or (not (isinstance node Paren)) (== (len node) 0))
|
|
499
|
+
(continue))
|
|
500
|
+
(= op node.op)
|
|
501
|
+
(if (not (isinstance op Symbol))
|
|
502
|
+
(continue))
|
|
503
|
+
(= form op.value)
|
|
504
|
+
|
|
505
|
+
(cond
|
|
506
|
+
(in form (, "def" "async-def" "defmacro"))
|
|
507
|
+
(do (if (and (>= (len node) 2) (isinstance (sub node 1) Symbol))
|
|
508
|
+
(do (= name (. (sub node 1) value))
|
|
509
|
+
(= loc (lsp.Location :uri uri :range (node-range (sub node 1))))
|
|
510
|
+
(.append (.setdefault defs name []) loc)
|
|
511
|
+
(if (> (len node) 3)
|
|
512
|
+
(collect-definitions (sub node.list [: 3 None]) uri :defs defs)))))
|
|
513
|
+
|
|
514
|
+
(== form "class")
|
|
515
|
+
(do (if (and (>= (len node) 2) (isinstance (sub node 1) Symbol))
|
|
516
|
+
(do (= name (. (sub node 1) value))
|
|
517
|
+
(= loc (lsp.Location :uri uri :range (node-range (sub node 1))))
|
|
518
|
+
(.append (.setdefault defs name []) loc)
|
|
519
|
+
(= body (ife (> (len node) 3)
|
|
520
|
+
(sub node.list [: 3 None])
|
|
521
|
+
(sub node.list [: 2 None])))
|
|
522
|
+
(collect-definitions body uri :defs defs))))
|
|
523
|
+
|
|
524
|
+
(== form "=")
|
|
525
|
+
(do (if (and (>= (len node) 2) (isinstance (sub node 1) Symbol))
|
|
526
|
+
(do (= name (. (sub node 1) value))
|
|
527
|
+
(= loc (lsp.Location :uri uri :range (node-range (sub node 1))))
|
|
528
|
+
(.append (.setdefault defs name []) loc))))
|
|
529
|
+
|
|
530
|
+
(in form (, "for" "async-for"))
|
|
531
|
+
(do (if (and (>= (len node) 2) (isinstance (sub node 1) Symbol))
|
|
532
|
+
(do (= name (. (sub node 1) value))
|
|
533
|
+
(= loc (lsp.Location :uri uri :range (node-range (sub node 1))))
|
|
534
|
+
(.append (.setdefault defs name []) loc)))
|
|
535
|
+
(collect-definitions (sub node.list [: 1 None]) uri :defs defs))
|
|
536
|
+
|
|
537
|
+
(in form (, "import" "from"))
|
|
538
|
+
(do (for child in (sub node.list [: 1 None])
|
|
539
|
+
(cond
|
|
540
|
+
(and (isinstance child Symbol) (not-in child.value (, "as" "*")))
|
|
541
|
+
(do (= loc (lsp.Location :uri uri :range (node-range child)))
|
|
542
|
+
(.append (.setdefault defs child.value []) loc))
|
|
543
|
+
(isinstance child Bracket)
|
|
544
|
+
(for item in child.list
|
|
545
|
+
(if (and (isinstance item Symbol) (!= item.value "as"))
|
|
546
|
+
(do (= loc (lsp.Location :uri uri :range (node-range item)))
|
|
547
|
+
(.append (.setdefault defs item.value []) loc)))))))
|
|
548
|
+
|
|
549
|
+
;; Default: recurse into nested paren forms
|
|
550
|
+
(do (collect-definitions
|
|
551
|
+
[c for c in node.list if (isinstance c Paren)]
|
|
552
|
+
uri :defs defs))))
|
|
553
|
+
|
|
554
|
+
(return defs))
|
|
555
|
+
|
|
556
|
+
;; ---------------------------------------------------------------------------
|
|
557
|
+
;; References
|
|
558
|
+
;; ---------------------------------------------------------------------------
|
|
559
|
+
|
|
560
|
+
(def collect-references [nodes name uri]
|
|
561
|
+
"Find all occurrences of a symbol name in the AST."
|
|
562
|
+
(= refs [])
|
|
563
|
+
(for node in nodes
|
|
564
|
+
(if (and (isinstance node Symbol) (== node.value name))
|
|
565
|
+
(.append refs (lsp.Location :uri uri :range (node-range node))))
|
|
566
|
+
(if (isinstance node Expression)
|
|
567
|
+
(.extend refs (collect-references node.list name uri)))
|
|
568
|
+
(if (and (isinstance node (, Wrapper MetaIndicator))
|
|
569
|
+
(is-not node.value None))
|
|
570
|
+
(.extend refs (collect-references [node.value] name uri))))
|
|
571
|
+
(return refs))
|
|
572
|
+
|
|
573
|
+
;; ---------------------------------------------------------------------------
|
|
574
|
+
;; LSP event handlers
|
|
575
|
+
;; ---------------------------------------------------------------------------
|
|
576
|
+
|
|
577
|
+
(deco (server.feature lsp.INITIALIZED)
|
|
578
|
+
(def on-initialized [ls params]
|
|
579
|
+
(= folders (ife ls.workspace.folders
|
|
580
|
+
(list (.values ls.workspace.folders))
|
|
581
|
+
[]))
|
|
582
|
+
(if folders
|
|
583
|
+
(index-workspace folders))))
|
|
584
|
+
|
|
585
|
+
(deco (server.feature lsp.TEXT_DOCUMENT_DID_OPEN)
|
|
586
|
+
(def did-open [ls params]
|
|
587
|
+
(= uri params.text_document.uri)
|
|
588
|
+
(index-file uri)
|
|
589
|
+
(publish-diagnostics ls uri)))
|
|
590
|
+
|
|
591
|
+
(deco (server.feature lsp.TEXT_DOCUMENT_DID_CHANGE)
|
|
592
|
+
(def did-change [ls params]
|
|
593
|
+
(publish-diagnostics ls params.text_document.uri)))
|
|
594
|
+
|
|
595
|
+
(deco (server.feature lsp.TEXT_DOCUMENT_DID_SAVE)
|
|
596
|
+
(def did-save [ls params]
|
|
597
|
+
(= uri params.text_document.uri)
|
|
598
|
+
(index-file uri)
|
|
599
|
+
(publish-diagnostics ls uri)))
|
|
600
|
+
|
|
601
|
+
(deco (server.feature lsp.TEXT_DOCUMENT_DOCUMENT_SYMBOL)
|
|
602
|
+
(def document-symbol [ls params]
|
|
603
|
+
(= doc (.get-text-document ls.workspace params.text_document.uri))
|
|
604
|
+
(try
|
|
605
|
+
(= tree (parse doc.source))
|
|
606
|
+
(except [Exception]
|
|
607
|
+
(return [])))
|
|
608
|
+
(return (walk-symbols tree))))
|
|
609
|
+
|
|
610
|
+
(deco (server.feature lsp.TEXT_DOCUMENT_HOVER)
|
|
611
|
+
(def hover [ls params]
|
|
612
|
+
(= doc (.get-text-document ls.workspace params.text_document.uri))
|
|
613
|
+
(try
|
|
614
|
+
(= tree (parse doc.source))
|
|
615
|
+
(except [Exception]
|
|
616
|
+
(return None)))
|
|
617
|
+
(= pos params.position)
|
|
618
|
+
(= target (node-at-position tree pos.line pos.character))
|
|
619
|
+
(if (is target None)
|
|
620
|
+
(return None))
|
|
621
|
+
;; Look up documentation for the symbol
|
|
622
|
+
(if (isinstance target Symbol)
|
|
623
|
+
(do
|
|
624
|
+
(= name target.value)
|
|
625
|
+
;; 1. LisPython special forms
|
|
626
|
+
(= doc-text (.get SPECIAL-FORM-DOCS name))
|
|
627
|
+
(if doc-text
|
|
628
|
+
(return (lsp.Hover
|
|
629
|
+
:contents (lsp.MarkupContent
|
|
630
|
+
:kind lsp.MarkupKind.Markdown
|
|
631
|
+
:value doc-text)
|
|
632
|
+
:range (node-range target))))
|
|
633
|
+
;; 2. Python builtins
|
|
634
|
+
(= builtin-name (.replace name "-" "_"))
|
|
635
|
+
(= bi (.get BUILTIN-INFO builtin-name))
|
|
636
|
+
(if bi
|
|
637
|
+
(do (= sig (sub bi "signature"))
|
|
638
|
+
(= doc-body (sub bi "doc"))
|
|
639
|
+
(= hover-md f"```python\n{sig}\n```\n\n*(Python builtin)*")
|
|
640
|
+
(if doc-body
|
|
641
|
+
(+= hover-md f"\n\n{doc-body}"))
|
|
642
|
+
(return (lsp.Hover
|
|
643
|
+
:contents (lsp.MarkupContent
|
|
644
|
+
:kind lsp.MarkupKind.Markdown
|
|
645
|
+
:value hover-md)
|
|
646
|
+
:range (node-range target)))))))
|
|
647
|
+
(return None)))
|
|
648
|
+
|
|
649
|
+
(deco (server.feature lsp.TEXT_DOCUMENT_DEFINITION)
|
|
650
|
+
(def definition [ls params]
|
|
651
|
+
(= doc (.get-text-document ls.workspace params.text_document.uri))
|
|
652
|
+
(try
|
|
653
|
+
(= tree (parse doc.source))
|
|
654
|
+
(except [Exception]
|
|
655
|
+
(return None)))
|
|
656
|
+
(= pos params.position)
|
|
657
|
+
(= target (node-at-position tree pos.line pos.character))
|
|
658
|
+
(if (or (is target None) (not (isinstance target Symbol)))
|
|
659
|
+
(return None))
|
|
660
|
+
(= name target.value)
|
|
661
|
+
(if (in name SPECIAL-FORM-DOCS)
|
|
662
|
+
(return None))
|
|
663
|
+
|
|
664
|
+
;; 1. Local definitions in the file
|
|
665
|
+
(= defs (collect-definitions tree doc.uri))
|
|
666
|
+
(= local (.get defs name))
|
|
667
|
+
(if local
|
|
668
|
+
(return local))
|
|
669
|
+
|
|
670
|
+
;; 2. Resolve via import statements — find which module imports this name
|
|
671
|
+
(= folders (ife ls.workspace.folders
|
|
672
|
+
(list (.values ls.workspace.folders))
|
|
673
|
+
[]))
|
|
674
|
+
(= import-node (find-import-for-name tree name))
|
|
675
|
+
(if import-node
|
|
676
|
+
(do (= fpath (find-import-target import-node folders))
|
|
677
|
+
(if fpath
|
|
678
|
+
(do (= target-uri (path-to-uri fpath))
|
|
679
|
+
;; Make sure the target file is indexed
|
|
680
|
+
(if (not-in target-uri FILE-TREES)
|
|
681
|
+
(index-file target-uri))
|
|
682
|
+
;; Look up the name in the target file's definitions
|
|
683
|
+
(= target-tree (.get FILE-TREES target-uri))
|
|
684
|
+
(if target-tree
|
|
685
|
+
(do (= target-defs (collect-definitions target-tree target-uri))
|
|
686
|
+
(= result (.get target-defs name))
|
|
687
|
+
(if result
|
|
688
|
+
(return result))))))))
|
|
689
|
+
|
|
690
|
+
;; 3. Workspace-wide index lookup
|
|
691
|
+
(= ws-result (.get WORKSPACE-INDEX name))
|
|
692
|
+
(if ws-result
|
|
693
|
+
;; Filter out definitions from the current file (already checked)
|
|
694
|
+
(do (= external [loc for loc in ws-result if (!= loc.uri doc.uri)])
|
|
695
|
+
(if external
|
|
696
|
+
(return external))))
|
|
697
|
+
|
|
698
|
+
;; 4. Python builtins with source files
|
|
699
|
+
(= builtin-name (.replace name "-" "_"))
|
|
700
|
+
(= bi (.get BUILTIN-INFO builtin-name))
|
|
701
|
+
(if (and bi (sub bi "source_file"))
|
|
702
|
+
(do (= source-path (pathlib.Path (sub bi "source_file")))
|
|
703
|
+
(if (.exists source-path)
|
|
704
|
+
(do (= lineno (- (or (sub bi "lineno") 1) 1))
|
|
705
|
+
(return [(lsp.Location
|
|
706
|
+
:uri (.as-uri source-path)
|
|
707
|
+
:range (make-range lineno 0 lineno 0))])))))
|
|
708
|
+
(return None)))
|
|
709
|
+
|
|
710
|
+
(deco (server.feature lsp.TEXT_DOCUMENT_REFERENCES)
|
|
711
|
+
(def references [ls params]
|
|
712
|
+
(= doc (.get-text-document ls.workspace params.text_document.uri))
|
|
713
|
+
(try
|
|
714
|
+
(= tree (parse doc.source))
|
|
715
|
+
(except [Exception]
|
|
716
|
+
(return None)))
|
|
717
|
+
(= pos params.position)
|
|
718
|
+
(= target (node-at-position tree pos.line pos.character))
|
|
719
|
+
(if (or (is target None) (not (isinstance target Symbol)))
|
|
720
|
+
(return None))
|
|
721
|
+
(return (collect-references tree target.value doc.uri))))
|
|
@@ -2,6 +2,15 @@ version = 1
|
|
|
2
2
|
revision = 3
|
|
3
3
|
requires-python = ">=3.11"
|
|
4
4
|
|
|
5
|
+
[[package]]
|
|
6
|
+
name = "attrs"
|
|
7
|
+
version = "26.1.0"
|
|
8
|
+
source = { registry = "https://pypi.org/simple" }
|
|
9
|
+
sdist = { url = "https://files.pythonhosted.org/packages/9a/8e/82a0fe20a541c03148528be8cac2408564a6c9a0cc7e9171802bc1d26985/attrs-26.1.0.tar.gz", hash = "sha256:d03ceb89cb322a8fd706d4fb91940737b6642aa36998fe130a9bc96c985eff32", size = 952055, upload-time = "2026-03-19T14:22:25.026Z" }
|
|
10
|
+
wheels = [
|
|
11
|
+
{ url = "https://files.pythonhosted.org/packages/64/b4/17d4b0b2a2dc85a6df63d1157e028ed19f90d4cd97c36717afef2bc2f395/attrs-26.1.0-py3-none-any.whl", hash = "sha256:c647aa4a12dfbad9333ca4e71fe62ddc36f4e63b2d260a37a8b83d2f043ac309", size = 67548, upload-time = "2026-03-19T14:22:23.645Z" },
|
|
12
|
+
]
|
|
13
|
+
|
|
5
14
|
[[package]]
|
|
6
15
|
name = "babel"
|
|
7
16
|
version = "2.17.0"
|
|
@@ -25,6 +34,19 @@ wheels = [
|
|
|
25
34
|
{ url = "https://files.pythonhosted.org/packages/02/e3/a4fa1946722c4c7b063cc25043a12d9ce9b4323777f89643be74cef2993c/backrefs-6.1-py39-none-any.whl", hash = "sha256:a9e99b8a4867852cad177a6430e31b0f6e495d65f8c6c134b68c14c3c95bf4b0", size = 381058, upload-time = "2025-11-15T14:52:06.698Z" },
|
|
26
35
|
]
|
|
27
36
|
|
|
37
|
+
[[package]]
|
|
38
|
+
name = "cattrs"
|
|
39
|
+
version = "26.1.0"
|
|
40
|
+
source = { registry = "https://pypi.org/simple" }
|
|
41
|
+
dependencies = [
|
|
42
|
+
{ name = "attrs" },
|
|
43
|
+
{ name = "typing-extensions" },
|
|
44
|
+
]
|
|
45
|
+
sdist = { url = "https://files.pythonhosted.org/packages/a0/ec/ba18945e7d6e55a58364d9fb2e46049c1c2998b3d805f19b703f14e81057/cattrs-26.1.0.tar.gz", hash = "sha256:fa239e0f0ec0715ba34852ce813986dfed1e12117e209b816ab87401271cdd40", size = 495672, upload-time = "2026-02-18T22:15:19.406Z" }
|
|
46
|
+
wheels = [
|
|
47
|
+
{ url = "https://files.pythonhosted.org/packages/80/56/60547f7801b97c67e97491dc3d9ade9fbccbd0325058fd3dfcb2f5d98d90/cattrs-26.1.0-py3-none-any.whl", hash = "sha256:d1e0804c42639494d469d08d4f26d6b9de9b8ab26b446db7b5f8c2e97f7c3096", size = 73054, upload-time = "2026-02-18T22:15:17.958Z" },
|
|
48
|
+
]
|
|
49
|
+
|
|
28
50
|
[[package]]
|
|
29
51
|
name = "certifi"
|
|
30
52
|
version = "2025.11.12"
|
|
@@ -240,6 +262,9 @@ wheels = [
|
|
|
240
262
|
name = "lispython"
|
|
241
263
|
version = "0.3.3"
|
|
242
264
|
source = { editable = "." }
|
|
265
|
+
dependencies = [
|
|
266
|
+
{ name = "pygls" },
|
|
267
|
+
]
|
|
243
268
|
|
|
244
269
|
[package.optional-dependencies]
|
|
245
270
|
dev = [
|
|
@@ -260,12 +285,26 @@ requires-dist = [
|
|
|
260
285
|
{ name = "mkdocs-macros-plugin", marker = "extra == 'docs'", specifier = ">=1.3.7" },
|
|
261
286
|
{ name = "mkdocs-material", marker = "extra == 'docs'", specifier = ">=9.6.14" },
|
|
262
287
|
{ name = "pre-commit", marker = "extra == 'dev'", specifier = ">=3.6.0" },
|
|
288
|
+
{ name = "pygls", specifier = ">=1.0.0" },
|
|
263
289
|
{ name = "pytest", marker = "extra == 'dev'", specifier = ">=8.0.0" },
|
|
264
290
|
{ name = "ruff", marker = "extra == 'dev'", specifier = ">=0.8.0" },
|
|
265
291
|
{ name = "toml", marker = "extra == 'dev'", specifier = ">=0.10.2" },
|
|
266
292
|
]
|
|
267
293
|
provides-extras = ["dev", "docs"]
|
|
268
294
|
|
|
295
|
+
[[package]]
|
|
296
|
+
name = "lsprotocol"
|
|
297
|
+
version = "2025.0.0"
|
|
298
|
+
source = { registry = "https://pypi.org/simple" }
|
|
299
|
+
dependencies = [
|
|
300
|
+
{ name = "attrs" },
|
|
301
|
+
{ name = "cattrs" },
|
|
302
|
+
]
|
|
303
|
+
sdist = { url = "https://files.pythonhosted.org/packages/e9/26/67b84e6ec1402f0e6764ef3d2a0aaf9a79522cc1d37738f4e5bb0b21521a/lsprotocol-2025.0.0.tar.gz", hash = "sha256:e879da2b9301e82cfc3e60d805630487ac2f7ab17492f4f5ba5aaba94fe56c29", size = 74896, upload-time = "2025-06-17T21:30:18.156Z" }
|
|
304
|
+
wheels = [
|
|
305
|
+
{ url = "https://files.pythonhosted.org/packages/7b/f0/92f2d609d6642b5f30cb50a885d2bf1483301c69d5786286500d15651ef2/lsprotocol-2025.0.0-py3-none-any.whl", hash = "sha256:f9d78f25221f2a60eaa4a96d3b4ffae011b107537facee61d3da3313880995c7", size = 76250, upload-time = "2025-06-17T21:30:19.455Z" },
|
|
306
|
+
]
|
|
307
|
+
|
|
269
308
|
[[package]]
|
|
270
309
|
name = "markdown"
|
|
271
310
|
version = "3.10"
|
|
@@ -537,6 +576,20 @@ wheels = [
|
|
|
537
576
|
{ url = "https://files.pythonhosted.org/packages/5d/19/fd3ef348460c80af7bb4669ea7926651d1f95c23ff2df18b9d24bab4f3fa/pre_commit-4.5.1-py2.py3-none-any.whl", hash = "sha256:3b3afd891e97337708c1674210f8eba659b52a38ea5f822ff142d10786221f77", size = 226437, upload-time = "2025-12-16T21:14:32.409Z" },
|
|
538
577
|
]
|
|
539
578
|
|
|
579
|
+
[[package]]
|
|
580
|
+
name = "pygls"
|
|
581
|
+
version = "2.1.1"
|
|
582
|
+
source = { registry = "https://pypi.org/simple" }
|
|
583
|
+
dependencies = [
|
|
584
|
+
{ name = "attrs" },
|
|
585
|
+
{ name = "cattrs" },
|
|
586
|
+
{ name = "lsprotocol" },
|
|
587
|
+
]
|
|
588
|
+
sdist = { url = "https://files.pythonhosted.org/packages/da/2e/7bbe061d175c0baddde8fc9edb908a4c31ba5d9165b8c68e3439c3a9f138/pygls-2.1.1.tar.gz", hash = "sha256:1da03ba9053201bb337dcdd8d121df70feb2a91e1a0dcc74de5da79755b1a201", size = 55091, upload-time = "2026-03-25T11:19:10.541Z" }
|
|
589
|
+
wheels = [
|
|
590
|
+
{ url = "https://files.pythonhosted.org/packages/fd/1a/208293b6c350f5abea6941d5606080d4a492644052504f5312e5de30a902/pygls-2.1.1-py3-none-any.whl", hash = "sha256:510a6dea2476177230c7d851125e5948efdf3fdb9ebfd8543fc434972f8faed4", size = 68975, upload-time = "2026-03-25T11:19:11.374Z" },
|
|
591
|
+
]
|
|
592
|
+
|
|
540
593
|
[[package]]
|
|
541
594
|
name = "pygments"
|
|
542
595
|
version = "2.19.2"
|
|
@@ -743,6 +796,15 @@ wheels = [
|
|
|
743
796
|
{ url = "https://files.pythonhosted.org/packages/44/6f/7120676b6d73228c96e17f1f794d8ab046fc910d781c8d151120c3f1569e/toml-0.10.2-py2.py3-none-any.whl", hash = "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b", size = 16588, upload-time = "2020-11-01T01:40:20.672Z" },
|
|
744
797
|
]
|
|
745
798
|
|
|
799
|
+
[[package]]
|
|
800
|
+
name = "typing-extensions"
|
|
801
|
+
version = "4.15.0"
|
|
802
|
+
source = { registry = "https://pypi.org/simple" }
|
|
803
|
+
sdist = { url = "https://files.pythonhosted.org/packages/72/94/1a15dd82efb362ac84269196e94cf00f187f7ed21c242792a923cdb1c61f/typing_extensions-4.15.0.tar.gz", hash = "sha256:0cea48d173cc12fa28ecabc3b837ea3cf6f38c6d1136f85cbaaf598984861466", size = 109391, upload-time = "2025-08-25T13:49:26.313Z" }
|
|
804
|
+
wheels = [
|
|
805
|
+
{ url = "https://files.pythonhosted.org/packages/18/67/36e9267722cc04a6b9f15c7f3441c2363321a3ea07da7ae0c0707beb2a9c/typing_extensions-4.15.0-py3-none-any.whl", hash = "sha256:f0fa19c6845758ab08074a0cfa8b7aecb71c999ca73d62883bc25cc018c4e548", size = 44614, upload-time = "2025-08-25T13:49:24.86Z" },
|
|
806
|
+
]
|
|
807
|
+
|
|
746
808
|
[[package]]
|
|
747
809
|
name = "urllib3"
|
|
748
810
|
version = "2.6.2"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|