stan-language-server 0.4.4 → 0.4.6
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.
- package/README.md +73 -14
- package/dist/handlers/completion/constraints.d.ts +2 -0
- package/dist/handlers/completion/datatypes.d.ts +2 -0
- package/dist/handlers/completion/distributions.d.ts +2 -0
- package/dist/handlers/completion/functions.d.ts +2 -0
- package/dist/handlers/completion/keywords.d.ts +2 -0
- package/dist/handlers/completion/snippets.d.ts +2 -0
- package/dist/handlers/completion.d.ts +3 -2
- package/dist/language/hover/util.d.ts +1 -1
- package/dist/server/index.js +268 -208
- package/dist/types/common.d.ts +0 -4
- package/dist/types/index.d.ts +1 -2
- package/package.json +5 -2
- package/dist/__tests__/language/completion/providers/datatypes.test.d.ts +0 -1
- package/dist/__tests__/language/completion/providers/distributions.test.d.ts +0 -1
- package/dist/__tests__/language/completion/providers/functions.test.d.ts +0 -1
- package/dist/__tests__/language/completion/providers/keywords.test.d.ts +0 -1
- package/dist/__tests__/language/completion/util.test.d.ts +0 -1
- package/dist/language/completion/providers/constraints.d.ts +0 -3
- package/dist/language/completion/providers/datatypes.d.ts +0 -3
- package/dist/language/completion/providers/distributions.d.ts +0 -2
- package/dist/language/completion/providers/functions.d.ts +0 -2
- package/dist/language/completion/providers/keywords.d.ts +0 -2
- package/dist/language/completion/util.d.ts +0 -4
- package/dist/types/completion.d.ts +0 -15
- /package/dist/__tests__/{language/completion/providers/constraints.test.d.ts → handlers/completion.test.d.ts} +0 -0
package/README.md
CHANGED
|
@@ -10,7 +10,6 @@ A language server for the Stan probabilistic programming language written in Typ
|
|
|
10
10
|
- **Code formatting**: Using the official Stan compiler
|
|
11
11
|
- **Include file support**: Full `#include` resolution and compilation
|
|
12
12
|
|
|
13
|
-
|
|
14
13
|
## Editor-specific configuration
|
|
15
14
|
|
|
16
15
|
### VSCode:
|
|
@@ -21,7 +20,19 @@ or [open-vsx](https://open-vsx.org/extension/wardbrian/vscode-stan-extension).
|
|
|
21
20
|
|
|
22
21
|
### Neovim
|
|
23
22
|
|
|
24
|
-
|
|
23
|
+
#### Mason
|
|
24
|
+
|
|
25
|
+
The Stan language server is installable with
|
|
26
|
+
[Mason](https://github.com/mason-org/mason.nvim) by running `:MasonInstall
|
|
27
|
+
stan-language-server`.
|
|
28
|
+
|
|
29
|
+
#### Manual install
|
|
30
|
+
|
|
31
|
+
The language server can also be configured manually by first installing
|
|
32
|
+
the language server executable. This can be done directly via `npm install -g
|
|
33
|
+
stan-language-server-bin` or by downloading the latest executable from
|
|
34
|
+
[GitHub](https://github.com/tomatitito/stan-language-server/releases) and putting
|
|
35
|
+
it somewhere in your `PATH`.
|
|
25
36
|
|
|
26
37
|
#### Neovim 0.11+ (built-in LSP)
|
|
27
38
|
|
|
@@ -95,27 +106,74 @@ and add the following to the settings file:
|
|
|
95
106
|
### Emacs (eglot)
|
|
96
107
|
|
|
97
108
|
Assuming you are using [stan-ts-mode](https://github.com/WardBrian/stan-ts-mode),
|
|
98
|
-
|
|
99
|
-
|
|
109
|
+
add the following to your `init.el`.
|
|
110
|
+
This will download the latest release the first time you load a Stan file.
|
|
100
111
|
|
|
101
112
|
```elisp
|
|
102
|
-
; elgot is built in to emacs 29+, but some features work better if you use the
|
|
103
|
-
;; latest version from GNU ELPA
|
|
104
113
|
(require 'package)
|
|
105
|
-
|
|
114
|
+
;; elgot is built in to emacs 29+, but some features work better if you use the
|
|
115
|
+
;; latest version from GNU ELPA
|
|
116
|
+
(add-to-list 'package-archives '("gnu-devel" . "https://elpa.gnu.org/devel/"))
|
|
106
117
|
(package-initialize)
|
|
107
118
|
|
|
119
|
+
;; work around https://debbugs.gnu.org/cgi/bugreport.cgi?bug=69423
|
|
120
|
+
(assq-delete-all 'eglot package--builtins)
|
|
121
|
+
(assq-delete-all 'eglot package--builtin-versions)
|
|
122
|
+
|
|
123
|
+
(defcustom bmw/stan-language-server-location
|
|
124
|
+
(expand-file-name (concat "bin/stan-language-server" (car exec-suffixes)) user-emacs-directory)
|
|
125
|
+
"Location to download the stan-language-server binary to."
|
|
126
|
+
:type 'file
|
|
127
|
+
:group 'stan)
|
|
128
|
+
|
|
129
|
+
(use-package url)
|
|
130
|
+
|
|
131
|
+
(defun bmw/download-stan-language-server (&optional force)
|
|
132
|
+
"Download the latest copy of the stan-language-server.
|
|
133
|
+
The location is determined by stan-ts-mode-language-server-location.
|
|
134
|
+
Argument FORCE will make the download proceed even if the file exists."
|
|
135
|
+
(interactive "P")
|
|
136
|
+
(when (or force (not (file-exists-p bmw/stan-language-server-location)))
|
|
137
|
+
(let*
|
|
138
|
+
((version
|
|
139
|
+
(with-temp-buffer
|
|
140
|
+
(url-insert-file-contents
|
|
141
|
+
"https://api.github.com/repos/tomatitito/stan-language-server/releases/latest")
|
|
142
|
+
(let ((json (json-parse-buffer)))
|
|
143
|
+
(gethash "tag_name" json))))
|
|
144
|
+
(os-tag
|
|
145
|
+
(pcase system-type
|
|
146
|
+
((or 'windows-nt 'cygwin 'ms-dos) "windows-x86_64")
|
|
147
|
+
('darwin (concat "macos-"
|
|
148
|
+
(if (string-match-p "aarch64\\|arm" system-configuration ) "aarch64" "x86_64") ))
|
|
149
|
+
(_ (concat "linux-"
|
|
150
|
+
(if (string-match-p "aarch64\\|arm" system-configuration ) "arm64" "x86_64")))))
|
|
151
|
+
(url
|
|
152
|
+
(concat
|
|
153
|
+
"https://github.com/tomatitito/stan-language-server/releases/download/"
|
|
154
|
+
version
|
|
155
|
+
"/stan-ls-"
|
|
156
|
+
version
|
|
157
|
+
"-"
|
|
158
|
+
os-tag
|
|
159
|
+
(car exec-suffixes)))
|
|
160
|
+
(file bmw/stan-language-server-location))
|
|
161
|
+
(make-empty-file file t)
|
|
162
|
+
(delete-file file)
|
|
163
|
+
(url-copy-file url file)
|
|
164
|
+
(chmod file 500))))
|
|
165
|
+
|
|
108
166
|
(use-package eglot
|
|
109
167
|
:ensure t
|
|
110
|
-
:
|
|
111
|
-
:
|
|
112
|
-
|
|
168
|
+
:pin gnu-devel
|
|
169
|
+
:hook ((stan-ts-base-mode . bmw/download-stan-language-server)
|
|
170
|
+
(stan-ts-base-mode . eglot-ensure))
|
|
113
171
|
:config
|
|
114
|
-
(add-to-list
|
|
115
|
-
|
|
172
|
+
(add-to-list
|
|
173
|
+
'eglot-server-programs
|
|
174
|
+
`(stan-ts-base-mode . (,bmw/stan-language-server-location "--stdio"))))
|
|
116
175
|
```
|
|
117
176
|
|
|
118
|
-
|
|
119
177
|
## For developers
|
|
120
178
|
|
|
121
179
|
Development uses [bun](https://bun.sh/)
|
|
@@ -133,12 +191,13 @@ bun build:binary
|
|
|
133
191
|
```
|
|
134
192
|
|
|
135
193
|
To run unit tests:
|
|
194
|
+
|
|
136
195
|
```bash
|
|
137
196
|
bun test
|
|
138
197
|
```
|
|
139
198
|
|
|
140
199
|
To run end-to-end tests:
|
|
200
|
+
|
|
141
201
|
```bash
|
|
142
202
|
bun test:e2e
|
|
143
203
|
```
|
|
144
|
-
|
|
@@ -1,3 +1,4 @@
|
|
|
1
1
|
import { type CompletionParams, TextDocuments, CompletionItem } from "vscode-languageserver";
|
|
2
|
-
import { TextDocument } from "vscode-languageserver-textdocument";
|
|
3
|
-
export declare
|
|
2
|
+
import { TextDocument, type Position } from "vscode-languageserver-textdocument";
|
|
3
|
+
export declare const getTextUpToCursor: (text: string, position: Position) => string;
|
|
4
|
+
export declare function handleCompletion(params: CompletionParams, documents: TextDocuments<TextDocument>, supportsSnippets: boolean): CompletionItem[];
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
export declare const isWordChar: (char: string) => boolean;
|
|
2
|
-
export declare const isWhitespace: (char: string) => char is "
|
|
2
|
+
export declare const isWhitespace: (char: string) => char is "\n" | " " | "\t" | "\r";
|
|
3
3
|
export declare const previousWordBoundary: (text: string, pos: number) => number;
|
|
4
4
|
export declare const wordUntilNextParenthesis: (text: string, pos: number) => number;
|
package/dist/server/index.js
CHANGED
|
@@ -11,25 +11,61 @@ import {
|
|
|
11
11
|
|
|
12
12
|
// src/handlers/completion.ts
|
|
13
13
|
import {
|
|
14
|
-
CompletionItemKind
|
|
14
|
+
CompletionItemKind as CompletionItemKind7
|
|
15
15
|
} from "vscode-languageserver";
|
|
16
|
-
|
|
17
|
-
// src/language/completion/util.ts
|
|
18
16
|
import TrieSearch from "trie-search";
|
|
19
|
-
var getSearchableItems = (xs, options = {}) => {
|
|
20
|
-
const searchableItem = new TrieSearch("name", options);
|
|
21
|
-
searchableItem.addAll(xs);
|
|
22
|
-
return searchableItem;
|
|
23
|
-
};
|
|
24
|
-
var getTextUpToCursor = (text, position) => {
|
|
25
|
-
const lines = text.split(`
|
|
26
|
-
`);
|
|
27
|
-
const currentLine = lines[position.line] || "";
|
|
28
|
-
return currentLine.substring(0, position.character);
|
|
29
|
-
};
|
|
30
17
|
|
|
31
|
-
// src/
|
|
32
|
-
|
|
18
|
+
// src/handlers/completion/constraints.ts
|
|
19
|
+
import { CompletionItemKind } from "vscode-languageserver";
|
|
20
|
+
function constraintToCompletionItem(constraint) {
|
|
21
|
+
return {
|
|
22
|
+
label: constraint,
|
|
23
|
+
kind: CompletionItemKind.Property
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
var CONSTRAINTS = ["lower", "upper", "offset", "multiplier"].map(constraintToCompletionItem);
|
|
27
|
+
|
|
28
|
+
// src/handlers/completion/datatypes.ts
|
|
29
|
+
import { CompletionItemKind as CompletionItemKind2 } from "vscode-languageserver";
|
|
30
|
+
function datatypeToCompletionItem(datatype) {
|
|
31
|
+
return {
|
|
32
|
+
label: datatype,
|
|
33
|
+
kind: CompletionItemKind2.Class
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
var DATATYPES = [
|
|
37
|
+
"void",
|
|
38
|
+
"int",
|
|
39
|
+
"real",
|
|
40
|
+
"complex",
|
|
41
|
+
"vector",
|
|
42
|
+
"row_vector",
|
|
43
|
+
"matrix",
|
|
44
|
+
"complex_vector",
|
|
45
|
+
"complex_row_vector",
|
|
46
|
+
"complex_matrix",
|
|
47
|
+
"ordered",
|
|
48
|
+
"positive_ordered",
|
|
49
|
+
"simplex",
|
|
50
|
+
"unit_vector",
|
|
51
|
+
"sum_to_zero_vector",
|
|
52
|
+
"cholesky_factor_corr",
|
|
53
|
+
"cholesky_factor_cov",
|
|
54
|
+
"corr_matrix",
|
|
55
|
+
"cov_matrix",
|
|
56
|
+
"stochastic_column_matrix",
|
|
57
|
+
"stochastic_row_matrix"
|
|
58
|
+
].map(datatypeToCompletionItem);
|
|
59
|
+
|
|
60
|
+
// src/handlers/completion/keywords.ts
|
|
61
|
+
import { CompletionItemKind as CompletionItemKind3 } from "vscode-languageserver";
|
|
62
|
+
function keywordToCompletionItem(keyword) {
|
|
63
|
+
return {
|
|
64
|
+
label: keyword,
|
|
65
|
+
kind: CompletionItemKind3.Keyword
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
var KEYWORDS = [
|
|
33
69
|
"for",
|
|
34
70
|
"in",
|
|
35
71
|
"while",
|
|
@@ -71,94 +107,158 @@ var ALL_KEYWORDS = [
|
|
|
71
107
|
"tuple",
|
|
72
108
|
"truncate",
|
|
73
109
|
"jacobian"
|
|
74
|
-
];
|
|
75
|
-
var getKeywords = () => {
|
|
76
|
-
return ALL_KEYWORDS.map((keyword) => ({
|
|
77
|
-
name: keyword
|
|
78
|
-
}));
|
|
79
|
-
};
|
|
80
|
-
var provideKeywordCompletions = (text, position) => {
|
|
81
|
-
const textUpToCursor = getTextUpToCursor(text, position);
|
|
82
|
-
const keywords = getKeywords();
|
|
83
|
-
const searchableKeywords = getSearchableItems(keywords, {
|
|
84
|
-
splitOnRegEx: /[\s_]/g,
|
|
85
|
-
min: 0
|
|
86
|
-
});
|
|
87
|
-
const match = textUpToCursor.match(/(?:^|\s)([\w_]+)$/);
|
|
88
|
-
if (match) {
|
|
89
|
-
const keywordName = match[1] || "";
|
|
90
|
-
const completionProposals = searchableKeywords.search(keywordName);
|
|
91
|
-
return completionProposals;
|
|
92
|
-
}
|
|
93
|
-
return [];
|
|
94
|
-
};
|
|
110
|
+
].map(keywordToCompletionItem);
|
|
95
111
|
|
|
96
|
-
// src/
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
112
|
+
// src/handlers/completion/snippets.ts
|
|
113
|
+
import {
|
|
114
|
+
CompletionItemKind as CompletionItemKind4,
|
|
115
|
+
InsertTextFormat,
|
|
116
|
+
InsertTextMode
|
|
117
|
+
} from "vscode-languageserver";
|
|
118
|
+
function snippetToCompletionItem(snippet) {
|
|
119
|
+
return {
|
|
120
|
+
label: snippet.name,
|
|
121
|
+
kind: CompletionItemKind4.Snippet,
|
|
122
|
+
insertText: snippet.body.join(`
|
|
123
|
+
`),
|
|
124
|
+
insertTextFormat: InsertTextFormat.Snippet,
|
|
125
|
+
insertTextMode: InsertTextMode.adjustIndentation,
|
|
126
|
+
detail: snippet.description
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
var SNIPPETS = [
|
|
130
|
+
{
|
|
131
|
+
name: "else",
|
|
132
|
+
body: ["else {", " ${0:/* code */}", "}"],
|
|
133
|
+
description: "Code snippet for 'else' conditional"
|
|
134
|
+
},
|
|
135
|
+
{
|
|
136
|
+
name: "elseif",
|
|
137
|
+
body: ["else if (${1:/* condition */}) {", " ${0:/* code */}", "}"],
|
|
138
|
+
description: "Code snippet for 'else if' conditional"
|
|
139
|
+
},
|
|
140
|
+
{
|
|
141
|
+
name: "for",
|
|
142
|
+
body: [
|
|
143
|
+
"for (${1:identifier} in ${2:collection}) {",
|
|
144
|
+
" ${0:/* code */}",
|
|
145
|
+
"}"
|
|
146
|
+
],
|
|
147
|
+
description: "Code snippet for 'for' loop"
|
|
148
|
+
},
|
|
149
|
+
{
|
|
150
|
+
name: "if",
|
|
151
|
+
body: ["if (${1:/* condition */}) {", " ${0:/* code */}", "}"],
|
|
152
|
+
description: "Code snippet for 'if' conditional"
|
|
153
|
+
},
|
|
154
|
+
{
|
|
155
|
+
name: "ifelse",
|
|
156
|
+
body: [
|
|
157
|
+
"if (${1:/* condition */}) {",
|
|
158
|
+
" ${2:/* code */}",
|
|
159
|
+
"} else {",
|
|
160
|
+
" ${0:/* code */}",
|
|
161
|
+
"}"
|
|
162
|
+
],
|
|
163
|
+
description: "Code snippet for 'if-else' conditional block"
|
|
164
|
+
},
|
|
165
|
+
{
|
|
166
|
+
name: "while",
|
|
167
|
+
body: ["while (${1:/* condition */}) {", " ${0:/* code */}", "}"],
|
|
168
|
+
description: "Code snippet for 'while' loop"
|
|
169
|
+
},
|
|
170
|
+
{
|
|
171
|
+
name: "profile",
|
|
172
|
+
body: ['profile("${1:name}") {', " ${0:/* code to be profiled */}", "}"],
|
|
173
|
+
description: "Code snippet for 'profile' block"
|
|
174
|
+
},
|
|
175
|
+
{
|
|
176
|
+
name: "data",
|
|
177
|
+
body: ["data {", " ${0:/* ... declarations ... */}", "}"],
|
|
178
|
+
description: "Code snippet for 'data' block"
|
|
179
|
+
},
|
|
180
|
+
{
|
|
181
|
+
name: "transformed data",
|
|
182
|
+
body: [
|
|
183
|
+
"transformed data {",
|
|
184
|
+
" ${0:/* ... declarations ... statements ... */}",
|
|
185
|
+
"}"
|
|
186
|
+
],
|
|
187
|
+
description: "Code snippet for 'transformed data' block"
|
|
188
|
+
},
|
|
189
|
+
{
|
|
190
|
+
name: "parameters",
|
|
191
|
+
body: ["parameters {", " ${0:/* ... declarations ... */}", "}"],
|
|
192
|
+
description: "Code snippet for 'parameters' block"
|
|
193
|
+
},
|
|
194
|
+
{
|
|
195
|
+
name: "transformed parameters",
|
|
196
|
+
body: [
|
|
197
|
+
"transformed parameters {",
|
|
198
|
+
" ${0:/* ... declarations ... statements ... */}",
|
|
199
|
+
"}"
|
|
200
|
+
],
|
|
201
|
+
description: "Code snippet for 'transformed parameters' block"
|
|
202
|
+
},
|
|
203
|
+
{
|
|
204
|
+
name: "model",
|
|
205
|
+
body: ["model {", " ${0:/* ... declarations ... statements ... */}", "}"],
|
|
206
|
+
description: "Code snippet for 'model' block"
|
|
207
|
+
},
|
|
208
|
+
{
|
|
209
|
+
name: "generated quantities",
|
|
210
|
+
body: [
|
|
211
|
+
"generated quantities {",
|
|
212
|
+
" ${0:/* ... declarations ... statements ... */}",
|
|
213
|
+
"}"
|
|
214
|
+
],
|
|
215
|
+
description: "Code snippet for 'generated quantities' block"
|
|
216
|
+
},
|
|
217
|
+
{
|
|
218
|
+
name: "functions",
|
|
219
|
+
body: [
|
|
220
|
+
"functions {",
|
|
221
|
+
" ${0:/* ... function declarations and definitions ... */}",
|
|
222
|
+
"}"
|
|
223
|
+
],
|
|
224
|
+
description: "Code snippet for 'functions' block"
|
|
225
|
+
},
|
|
226
|
+
{
|
|
227
|
+
name: "include",
|
|
228
|
+
body: ['#include "${1:filename}"'],
|
|
229
|
+
description: "Code snippet for 'include' preprocessor"
|
|
230
|
+
},
|
|
231
|
+
{
|
|
232
|
+
name: "target",
|
|
233
|
+
body: ["target += ${1};"],
|
|
234
|
+
description: "Code snippet for 'target +=' statement"
|
|
235
|
+
},
|
|
236
|
+
{
|
|
237
|
+
name: "jacobian",
|
|
238
|
+
body: ["jacobian += ${1};"],
|
|
239
|
+
description: "Code snippet for 'jacobian +=' statement"
|
|
114
240
|
}
|
|
115
|
-
|
|
116
|
-
};
|
|
241
|
+
].map(snippetToCompletionItem);
|
|
117
242
|
|
|
118
|
-
// src/
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
"positive_ordered",
|
|
132
|
-
"simplex",
|
|
133
|
-
"unit_vector",
|
|
134
|
-
"sum_to_zero_vector",
|
|
135
|
-
"cholesky_factor_corr",
|
|
136
|
-
"cholesky_factor_cov",
|
|
137
|
-
"corr_matrix",
|
|
138
|
-
"cov_matrix",
|
|
139
|
-
"stochastic_column_matrix",
|
|
140
|
-
"stochastic_row_matrix"
|
|
141
|
-
];
|
|
142
|
-
var getDatatypes = () => {
|
|
143
|
-
return DATATYPES.map((datatype) => ({
|
|
144
|
-
name: datatype
|
|
145
|
-
}));
|
|
146
|
-
};
|
|
147
|
-
var provideDatatypeCompletions = (text, position) => {
|
|
148
|
-
const textUpToCursor = getTextUpToCursor(text, position);
|
|
149
|
-
const datatypes = getDatatypes();
|
|
150
|
-
const searchableDatatypes = getSearchableItems(datatypes, {
|
|
151
|
-
splitOnRegEx: /[\s_]/g,
|
|
152
|
-
min: 0
|
|
153
|
-
});
|
|
154
|
-
const match = textUpToCursor.match(/(?:^|\s)([\w_]+)$/);
|
|
155
|
-
if (match) {
|
|
156
|
-
const typeName = match[1] || "";
|
|
157
|
-
const completionProposals = searchableDatatypes.search(typeName);
|
|
158
|
-
return completionProposals;
|
|
159
|
-
}
|
|
160
|
-
return [];
|
|
243
|
+
// src/handlers/completion/distributions.ts
|
|
244
|
+
import { CompletionItemKind as CompletionItemKind5 } from "vscode-languageserver";
|
|
245
|
+
import { dump_stan_math_distributions } from "stanc3";
|
|
246
|
+
function distributionToCompletionItem(distribution) {
|
|
247
|
+
return {
|
|
248
|
+
label: distribution,
|
|
249
|
+
kind: CompletionItemKind5.Function
|
|
250
|
+
};
|
|
251
|
+
}
|
|
252
|
+
var getDistributions = () => {
|
|
253
|
+
const distributions = dump_stan_math_distributions().split(`
|
|
254
|
+
`).map((line) => line.split(":")[0]?.trim() ?? "").filter((name) => name !== "");
|
|
255
|
+
return distributions.map(distributionToCompletionItem);
|
|
161
256
|
};
|
|
257
|
+
var DISTRIBUTIONS = getDistributions();
|
|
258
|
+
|
|
259
|
+
// src/handlers/completion/functions.ts
|
|
260
|
+
import { CompletionItemKind as CompletionItemKind6 } from "vscode-languageserver";
|
|
261
|
+
import { dump_stan_math_signatures as dump_stan_math_signatures2 } from "stanc3";
|
|
162
262
|
|
|
163
263
|
// src/handlers/hover.ts
|
|
164
264
|
import { dump_stan_math_signatures } from "stanc3";
|
|
@@ -202,10 +302,10 @@ var wordUntilNextParenthesis = (text, pos) => {
|
|
|
202
302
|
};
|
|
203
303
|
|
|
204
304
|
// src/language/hover/distributions.ts
|
|
205
|
-
import { dump_stan_math_distributions } from "stanc3";
|
|
305
|
+
import { dump_stan_math_distributions as dump_stan_math_distributions2 } from "stanc3";
|
|
206
306
|
var setupDistributionMap = () => {
|
|
207
307
|
const distributionToFunctionMap = new Map;
|
|
208
|
-
const mathDistributions =
|
|
308
|
+
const mathDistributions = dump_stan_math_distributions2();
|
|
209
309
|
const distLines = mathDistributions.split(`
|
|
210
310
|
`);
|
|
211
311
|
for (const line of distLines) {
|
|
@@ -358,123 +458,81 @@ async function handleHover(document, params) {
|
|
|
358
458
|
}
|
|
359
459
|
var hover_default = handleHover;
|
|
360
460
|
|
|
361
|
-
// src/
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
461
|
+
// src/handlers/completion/functions.ts
|
|
462
|
+
function functionToCompletionItem(func) {
|
|
463
|
+
return {
|
|
464
|
+
label: func,
|
|
465
|
+
kind: CompletionItemKind6.Function
|
|
466
|
+
};
|
|
467
|
+
}
|
|
468
|
+
var getFunctions = () => {
|
|
469
|
+
const functions = dump_stan_math_signatures2().split(`
|
|
470
|
+
`);
|
|
471
|
+
const functionNames = functions.map((line) => line.split("(", 1)[0]?.trim() ?? "").filter((name) => name !== "");
|
|
472
|
+
const uniqueFunctions = [...new Set([...functionNames, ...manual_functions])];
|
|
473
|
+
return uniqueFunctions.map(functionToCompletionItem);
|
|
474
|
+
};
|
|
475
|
+
var FUNCTIONS = getFunctions();
|
|
476
|
+
|
|
477
|
+
// src/handlers/completion.ts
|
|
478
|
+
var COMPLETION_TRIE = new TrieSearch("label", {
|
|
479
|
+
splitOnRegEx: /[\s_]/g,
|
|
480
|
+
min: 0
|
|
481
|
+
});
|
|
482
|
+
COMPLETION_TRIE.addAll([
|
|
483
|
+
...CONSTRAINTS,
|
|
484
|
+
...DATATYPES,
|
|
485
|
+
...KEYWORDS,
|
|
486
|
+
...FUNCTIONS,
|
|
487
|
+
...SNIPPETS
|
|
488
|
+
]);
|
|
489
|
+
var searchWords = (textUpToCursor, supportsSnippets) => {
|
|
371
490
|
const match = textUpToCursor.match(/(?:^|\s)([\w_]+)$/);
|
|
372
491
|
if (match) {
|
|
373
|
-
const
|
|
374
|
-
const completionProposals =
|
|
492
|
+
const word = match[1] || "";
|
|
493
|
+
const completionProposals = COMPLETION_TRIE.search(word);
|
|
494
|
+
if (!supportsSnippets) {
|
|
495
|
+
return completionProposals.filter((item) => item.kind !== CompletionItemKind7.Snippet);
|
|
496
|
+
}
|
|
375
497
|
return completionProposals;
|
|
376
498
|
}
|
|
377
499
|
return [];
|
|
378
500
|
};
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
"ordered",
|
|
387
|
-
"positive_ordered",
|
|
388
|
-
"simplex",
|
|
389
|
-
"unit_vector",
|
|
390
|
-
"sum_to_zero_vector",
|
|
391
|
-
"cholesky_factor_corr",
|
|
392
|
-
"cholesky_factor_cov",
|
|
393
|
-
"corr_matrix",
|
|
394
|
-
"cov_matrix",
|
|
395
|
-
"stochastic_column_matrix",
|
|
396
|
-
"stochastic_row_matrix"
|
|
397
|
-
];
|
|
398
|
-
var getConstraints = () => {
|
|
399
|
-
return CONSTRAINTS.map((constraint) => ({
|
|
400
|
-
name: constraint
|
|
401
|
-
}));
|
|
402
|
-
};
|
|
403
|
-
var provideConstraintCompletions = (text, position) => {
|
|
404
|
-
const textUpToCursor = getTextUpToCursor(text, position);
|
|
405
|
-
const constraints = getConstraints();
|
|
406
|
-
const searchableConstraints = getSearchableItems(constraints, {
|
|
407
|
-
splitOnRegEx: /[\s_]/g,
|
|
408
|
-
min: 0
|
|
409
|
-
});
|
|
410
|
-
const match = textUpToCursor.match(/(?:^|\s)([\w_]+)$/);
|
|
501
|
+
var DISTRIBUTION_TRIE = new TrieSearch("label", {
|
|
502
|
+
splitOnRegEx: /[\s_]/g,
|
|
503
|
+
min: 0
|
|
504
|
+
});
|
|
505
|
+
DISTRIBUTION_TRIE.addAll(DISTRIBUTIONS);
|
|
506
|
+
var searchDistributions = (textUpToCursor) => {
|
|
507
|
+
const match = textUpToCursor.match(/.*~\s*([\w_]*)$/);
|
|
411
508
|
if (match) {
|
|
412
|
-
const
|
|
413
|
-
|
|
509
|
+
const distName = match[1] || "";
|
|
510
|
+
let completionProposals;
|
|
511
|
+
if (distName === "") {
|
|
512
|
+
completionProposals = DISTRIBUTIONS;
|
|
513
|
+
} else {
|
|
514
|
+
completionProposals = DISTRIBUTION_TRIE.search(distName);
|
|
515
|
+
}
|
|
414
516
|
return completionProposals;
|
|
415
517
|
}
|
|
416
518
|
return [];
|
|
417
519
|
};
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
import {
|
|
421
|
-
dump_stan_math_distributions as dump_stan_math_distributions2,
|
|
422
|
-
dump_stan_math_signatures as dump_stan_math_signatures2
|
|
423
|
-
} from "stanc3";
|
|
424
|
-
function keywordToCompletionItem(keyword) {
|
|
425
|
-
return {
|
|
426
|
-
label: keyword.name,
|
|
427
|
-
kind: CompletionItemKind.Keyword
|
|
428
|
-
};
|
|
429
|
-
}
|
|
430
|
-
function distributionToCompletionItem(distribution) {
|
|
431
|
-
return {
|
|
432
|
-
label: distribution.name,
|
|
433
|
-
kind: CompletionItemKind.Function
|
|
434
|
-
};
|
|
435
|
-
}
|
|
436
|
-
function datatypeToCompletionItem(datatype) {
|
|
437
|
-
return {
|
|
438
|
-
label: datatype.name,
|
|
439
|
-
kind: CompletionItemKind.Class
|
|
440
|
-
};
|
|
441
|
-
}
|
|
442
|
-
function functionToCompletionItem(func) {
|
|
443
|
-
return {
|
|
444
|
-
label: func.name,
|
|
445
|
-
kind: CompletionItemKind.Function
|
|
446
|
-
};
|
|
447
|
-
}
|
|
448
|
-
function constraintToCompletionItem(constraint) {
|
|
449
|
-
return {
|
|
450
|
-
label: constraint.name,
|
|
451
|
-
kind: CompletionItemKind.Property
|
|
452
|
-
};
|
|
453
|
-
}
|
|
454
|
-
var DISTRIBUTION_DATA = dump_stan_math_distributions2().split(`
|
|
455
|
-
`).map((line) => line.split(":")[0]?.trim() ?? "").filter((name) => name !== "");
|
|
456
|
-
var FUNCTION_DATA = dump_stan_math_signatures2().split(`
|
|
520
|
+
var getTextUpToCursor = (text, position) => {
|
|
521
|
+
const lines = text.split(`
|
|
457
522
|
`);
|
|
458
|
-
|
|
523
|
+
const currentLine = lines[position.line] || "";
|
|
524
|
+
return currentLine.substring(0, position.character);
|
|
525
|
+
};
|
|
526
|
+
function handleCompletion(params, documents, supportsSnippets) {
|
|
459
527
|
const document = documents.get(params.textDocument.uri);
|
|
460
528
|
if (!document) {
|
|
461
529
|
return [];
|
|
462
530
|
}
|
|
463
|
-
const
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
const datatypes = provideDatatypeCompletions(text, position);
|
|
468
|
-
const functions = provideFunctionCompletions(text, position, FUNCTION_DATA);
|
|
469
|
-
const constraints = provideConstraintCompletions(text, position);
|
|
470
|
-
const allItems = [
|
|
471
|
-
...keywords.map(keywordToCompletionItem),
|
|
472
|
-
...distributions.map(distributionToCompletionItem),
|
|
473
|
-
...datatypes.map(datatypeToCompletionItem),
|
|
474
|
-
...functions.map(functionToCompletionItem),
|
|
475
|
-
...constraints.map(constraintToCompletionItem)
|
|
531
|
+
const textUpToCursor = getTextUpToCursor(document.getText(), params.position);
|
|
532
|
+
return [
|
|
533
|
+
...searchDistributions(textUpToCursor),
|
|
534
|
+
...searchWords(textUpToCursor, supportsSnippets)
|
|
476
535
|
];
|
|
477
|
-
return allItems;
|
|
478
536
|
}
|
|
479
537
|
// src/handlers/diagnostics.ts
|
|
480
538
|
import {
|
|
@@ -640,7 +698,7 @@ var readIncludedFileFromFileSystem = async (filename, dirs, fileSystemReader) =>
|
|
|
640
698
|
const localPath = join(currentDir, filename);
|
|
641
699
|
const content = await fileSystemReader(localPath);
|
|
642
700
|
return TextDocument.create(URI.file(localPath).toString(), "stan", 0, content);
|
|
643
|
-
} catch (
|
|
701
|
+
} catch (_error) {}
|
|
644
702
|
}
|
|
645
703
|
return Promise.resolve({ msg: `File not found: ${filename}` });
|
|
646
704
|
};
|
|
@@ -726,12 +784,14 @@ var startLanguageServer = (connection, reader) => {
|
|
|
726
784
|
let hasConfigurationCapability = false;
|
|
727
785
|
let hasWorkspaceFolderCapability = false;
|
|
728
786
|
let hasDynamicConfigurationRequestCapability = false;
|
|
787
|
+
let hasSnippetSupport = false;
|
|
729
788
|
connection.onInitialize((params) => {
|
|
730
789
|
connection.console.info("Initializing Stan language server...");
|
|
731
|
-
|
|
790
|
+
const capabilities = params.capabilities;
|
|
732
791
|
hasConfigurationCapability = Boolean(capabilities.workspace?.configuration);
|
|
733
792
|
hasDynamicConfigurationRequestCapability = Boolean(capabilities.workspace?.configuration && capabilities.workspace?.didChangeConfiguration?.dynamicRegistration);
|
|
734
793
|
hasWorkspaceFolderCapability = Boolean(capabilities.workspace?.workspaceFolders);
|
|
794
|
+
hasSnippetSupport = Boolean(capabilities.textDocument?.completion?.completionItem?.snippetSupport);
|
|
735
795
|
return {
|
|
736
796
|
capabilities: {
|
|
737
797
|
textDocumentSync: TextDocumentSyncKind.Incremental,
|
|
@@ -765,7 +825,7 @@ var startLanguageServer = (connection, reader) => {
|
|
|
765
825
|
connection.console.info("Stan language server is exiting...");
|
|
766
826
|
});
|
|
767
827
|
let globalSettings = defaultSettings;
|
|
768
|
-
|
|
828
|
+
const documentSettings = new Map;
|
|
769
829
|
connection.onDidChangeConfiguration((change) => {
|
|
770
830
|
if (hasDynamicConfigurationRequestCapability) {
|
|
771
831
|
documentSettings.clear();
|
|
@@ -779,11 +839,11 @@ var startLanguageServer = (connection, reader) => {
|
|
|
779
839
|
if (!hasConfigurationCapability) {
|
|
780
840
|
return Promise.resolve(globalSettings);
|
|
781
841
|
}
|
|
782
|
-
|
|
842
|
+
const result = documentSettings.get(resource);
|
|
783
843
|
if (result !== undefined) {
|
|
784
844
|
return result;
|
|
785
845
|
}
|
|
786
|
-
|
|
846
|
+
const clientSettings = await connection.workspace.getConfiguration({
|
|
787
847
|
scopeUri: resource,
|
|
788
848
|
section: SERVER_ID
|
|
789
849
|
}) || {};
|
|
@@ -793,7 +853,7 @@ var startLanguageServer = (connection, reader) => {
|
|
|
793
853
|
};
|
|
794
854
|
const documents = new TextDocuments3(TextDocument2);
|
|
795
855
|
connection.onCompletion((params) => {
|
|
796
|
-
return handleCompletion(params, documents);
|
|
856
|
+
return handleCompletion(params, documents, hasSnippetSupport);
|
|
797
857
|
});
|
|
798
858
|
const getWorkspaceFolders = async () => {
|
|
799
859
|
if (hasWorkspaceFolderCapability) {
|
package/dist/types/common.d.ts
CHANGED
package/dist/types/index.d.ts
CHANGED
|
@@ -1,2 +1 @@
|
|
|
1
|
-
export * from
|
|
2
|
-
export * from './completion';
|
|
1
|
+
export * from "./common";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "stan-language-server",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.6",
|
|
4
4
|
"description": "Language Server Protocol implementation for the Stan probabilistic programming language",
|
|
5
5
|
"main": "dist/server/index.js",
|
|
6
6
|
"module": "src/server/index.ts",
|
|
@@ -47,7 +47,7 @@
|
|
|
47
47
|
},
|
|
48
48
|
"scripts": {
|
|
49
49
|
"build": "bun build src/server/index.ts --outdir dist/server --packages external --target browser --format esm && tsc --emitDeclarationOnly --outDir dist",
|
|
50
|
-
"build:binary": "bun build src/server/cli.ts --
|
|
50
|
+
"build:binary": "bun build src/server/cli.ts --compile --outfile bin/stan-language-server",
|
|
51
51
|
"build:bin-package": "bun run scripts/build-bin-package.ts",
|
|
52
52
|
"prepublishOnly": "bun run build",
|
|
53
53
|
"version:set": "bun run scripts/set-version.ts",
|
|
@@ -72,6 +72,9 @@
|
|
|
72
72
|
"@typescript-eslint/eslint-plugin": "8.57.1",
|
|
73
73
|
"@typescript-eslint/parser": "8.57.1",
|
|
74
74
|
"eslint": "10.0.3",
|
|
75
|
+
"globals": "^17.3.0",
|
|
76
|
+
"@eslint/js": "^10.0.1",
|
|
77
|
+
"typescript-eslint": "^8.57.0",
|
|
75
78
|
"typescript": "5.9.3"
|
|
76
79
|
},
|
|
77
80
|
"dependencies": {
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
|
@@ -1,4 +0,0 @@
|
|
|
1
|
-
import TrieSearch, { type TrieSearchOptions } from "trie-search";
|
|
2
|
-
import type { Searchable, Position } from "../../types/completion";
|
|
3
|
-
export declare const getSearchableItems: <T extends Searchable>(xs: T[], options?: TrieSearchOptions<T>) => TrieSearch<T>;
|
|
4
|
-
export declare const getTextUpToCursor: (text: string, position: Position) => string;
|
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
import type { Position } from "./common";
|
|
2
|
-
export type { Position };
|
|
3
|
-
export interface Searchable {
|
|
4
|
-
name: string;
|
|
5
|
-
}
|
|
6
|
-
export interface Distribution extends Searchable {
|
|
7
|
-
}
|
|
8
|
-
export interface StanFunction extends Searchable {
|
|
9
|
-
}
|
|
10
|
-
export interface Keyword extends Searchable {
|
|
11
|
-
}
|
|
12
|
-
export interface Datatype extends Searchable {
|
|
13
|
-
}
|
|
14
|
-
export interface Constraint extends Searchable {
|
|
15
|
-
}
|
|
File without changes
|