rapydscript-ns 0.8.0
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/.agignore +1 -0
- package/.gitattributes +4 -0
- package/.github/workflows/ci.yml +38 -0
- package/.github/workflows/web-repl-page-deploy.yml +42 -0
- package/=template.pyj +5 -0
- package/CHANGELOG.md +456 -0
- package/CONTRIBUTORS +13 -0
- package/HACKING.md +103 -0
- package/LICENSE +24 -0
- package/README.md +2512 -0
- package/TODO.md +327 -0
- package/add-toc-to-readme +2 -0
- package/bin/export +75 -0
- package/bin/rapydscript +70 -0
- package/bin/web-repl-export +102 -0
- package/build +3 -0
- package/package.json +46 -0
- package/publish.py +37 -0
- package/release/baselib-plain-pretty.js +4370 -0
- package/release/baselib-plain-ugly.js +3 -0
- package/release/compiler.js +18394 -0
- package/release/signatures.json +31 -0
- package/session.vim +4 -0
- package/setup.cfg +2 -0
- package/src/ast.pyj +1356 -0
- package/src/baselib-builtins.pyj +279 -0
- package/src/baselib-containers.pyj +723 -0
- package/src/baselib-errors.pyj +37 -0
- package/src/baselib-internal.pyj +421 -0
- package/src/baselib-itertools.pyj +97 -0
- package/src/baselib-str.pyj +798 -0
- package/src/compiler.pyj +36 -0
- package/src/errors.pyj +30 -0
- package/src/lib/aes.pyj +646 -0
- package/src/lib/collections.pyj +695 -0
- package/src/lib/elementmaker.pyj +83 -0
- package/src/lib/encodings.pyj +126 -0
- package/src/lib/functools.pyj +148 -0
- package/src/lib/gettext.pyj +569 -0
- package/src/lib/itertools.pyj +580 -0
- package/src/lib/math.pyj +193 -0
- package/src/lib/numpy.pyj +2101 -0
- package/src/lib/operator.pyj +11 -0
- package/src/lib/pythonize.pyj +20 -0
- package/src/lib/random.pyj +118 -0
- package/src/lib/re.pyj +470 -0
- package/src/lib/traceback.pyj +63 -0
- package/src/lib/uuid.pyj +77 -0
- package/src/monaco-language-service/analyzer.js +526 -0
- package/src/monaco-language-service/builtins.js +543 -0
- package/src/monaco-language-service/completions.js +498 -0
- package/src/monaco-language-service/diagnostics.js +643 -0
- package/src/monaco-language-service/dts.js +550 -0
- package/src/monaco-language-service/hover.js +121 -0
- package/src/monaco-language-service/index.js +386 -0
- package/src/monaco-language-service/scope.js +162 -0
- package/src/monaco-language-service/signature.js +144 -0
- package/src/output/__init__.pyj +0 -0
- package/src/output/classes.pyj +296 -0
- package/src/output/codegen.pyj +492 -0
- package/src/output/comments.pyj +45 -0
- package/src/output/exceptions.pyj +105 -0
- package/src/output/functions.pyj +491 -0
- package/src/output/literals.pyj +109 -0
- package/src/output/loops.pyj +444 -0
- package/src/output/modules.pyj +329 -0
- package/src/output/operators.pyj +429 -0
- package/src/output/statements.pyj +463 -0
- package/src/output/stream.pyj +309 -0
- package/src/output/treeshake.pyj +182 -0
- package/src/output/utils.pyj +72 -0
- package/src/parse.pyj +3106 -0
- package/src/string_interpolation.pyj +72 -0
- package/src/tokenizer.pyj +702 -0
- package/src/unicode_aliases.pyj +576 -0
- package/src/utils.pyj +192 -0
- package/test/_import_one.pyj +37 -0
- package/test/_import_two/__init__.pyj +11 -0
- package/test/_import_two/level2/__init__.pyj +0 -0
- package/test/_import_two/level2/deep.pyj +4 -0
- package/test/_import_two/other.pyj +6 -0
- package/test/_import_two/sub.pyj +13 -0
- package/test/aes_vectors.pyj +421 -0
- package/test/annotations.pyj +80 -0
- package/test/baselib.pyj +319 -0
- package/test/classes.pyj +452 -0
- package/test/collections.pyj +152 -0
- package/test/decorators.pyj +77 -0
- package/test/dict_spread.pyj +76 -0
- package/test/docstrings.pyj +39 -0
- package/test/elementmaker_test.pyj +45 -0
- package/test/ellipsis.pyj +49 -0
- package/test/functions.pyj +151 -0
- package/test/generators.pyj +41 -0
- package/test/generic.pyj +370 -0
- package/test/imports.pyj +72 -0
- package/test/internationalization.pyj +73 -0
- package/test/lint.pyj +164 -0
- package/test/loops.pyj +85 -0
- package/test/numpy.pyj +734 -0
- package/test/omit_function_metadata.pyj +20 -0
- package/test/regexp.pyj +55 -0
- package/test/repl.pyj +121 -0
- package/test/scoped_flags.pyj +76 -0
- package/test/starargs.pyj +506 -0
- package/test/starred_assign.pyj +104 -0
- package/test/str.pyj +198 -0
- package/test/subscript_tuple.pyj +53 -0
- package/test/unit/fixtures/fibonacci_expected.js +46 -0
- package/test/unit/index.js +2989 -0
- package/test/unit/language-service-builtins.js +815 -0
- package/test/unit/language-service-completions.js +1067 -0
- package/test/unit/language-service-dts.js +543 -0
- package/test/unit/language-service-hover.js +455 -0
- package/test/unit/language-service-scope.js +833 -0
- package/test/unit/language-service-signature.js +458 -0
- package/test/unit/language-service.js +705 -0
- package/test/unit/run-language-service.js +41 -0
- package/test/unit/web-repl.js +484 -0
- package/tools/build-language-service.js +190 -0
- package/tools/cli.js +547 -0
- package/tools/compile.js +219 -0
- package/tools/compiler.js +108 -0
- package/tools/completer.js +131 -0
- package/tools/embedded_compiler.js +251 -0
- package/tools/export.js +316 -0
- package/tools/gettext.js +185 -0
- package/tools/ini.js +65 -0
- package/tools/lint.js +705 -0
- package/tools/msgfmt.js +187 -0
- package/tools/repl.js +223 -0
- package/tools/self.js +162 -0
- package/tools/test.js +118 -0
- package/tools/utils.js +128 -0
- package/tools/web_repl.js +95 -0
- package/try +41 -0
- package/web-repl/env.js +74 -0
- package/web-repl/index.html +163 -0
- package/web-repl/language-service.js +4084 -0
- package/web-repl/main.js +254 -0
- package/web-repl/prism.css +139 -0
- package/web-repl/prism.js +113 -0
- package/web-repl/rapydscript.js +435 -0
- package/web-repl/sha1.js +25 -0
|
@@ -0,0 +1,550 @@
|
|
|
1
|
+
// dts.js — Minimal .d.ts parser and type registry for the RapydScript language service.
|
|
2
|
+
//
|
|
3
|
+
// Parses a useful subset of TypeScript declaration syntax:
|
|
4
|
+
// declare var / let / const name: Type;
|
|
5
|
+
// declare function name(params): ReturnType;
|
|
6
|
+
// declare class Name { ... }
|
|
7
|
+
// interface Name { ... }
|
|
8
|
+
// declare namespace Name { ... }
|
|
9
|
+
// type Alias = Type;
|
|
10
|
+
//
|
|
11
|
+
// Usage:
|
|
12
|
+
// import { DtsRegistry } from './dts.js';
|
|
13
|
+
// const reg = new DtsRegistry();
|
|
14
|
+
// reg.addDts('lib.dom', dtsText);
|
|
15
|
+
// reg.getGlobalNames(); // string[]
|
|
16
|
+
// reg.getGlobal('alert'); // TypeInfo | null
|
|
17
|
+
// reg.getHoverMarkdown('alert'); // string | null
|
|
18
|
+
|
|
19
|
+
// ---------------------------------------------------------------------------
|
|
20
|
+
// TypeInfo
|
|
21
|
+
// ---------------------------------------------------------------------------
|
|
22
|
+
|
|
23
|
+
export class TypeInfo {
|
|
24
|
+
/**
|
|
25
|
+
* @param {object} opts
|
|
26
|
+
* @param {string} opts.name
|
|
27
|
+
* @param {string} opts.kind 'var'|'function'|'class'|'interface'|'namespace'|'method'|'property'
|
|
28
|
+
* @param {Array|null} [opts.params] [{name,type,optional,rest}]
|
|
29
|
+
* @param {string|null} [opts.return_type]
|
|
30
|
+
* @param {Map|null} [opts.members] Map<string, TypeInfo>
|
|
31
|
+
* @param {string|null} [opts.doc]
|
|
32
|
+
* @param {string} [opts.source] 'dts'
|
|
33
|
+
*/
|
|
34
|
+
constructor(opts) {
|
|
35
|
+
this.name = opts.name;
|
|
36
|
+
this.kind = opts.kind;
|
|
37
|
+
this.params = opts.params || null;
|
|
38
|
+
this.return_type = opts.return_type || null;
|
|
39
|
+
this.members = opts.members || null;
|
|
40
|
+
this.doc = opts.doc || null;
|
|
41
|
+
this.source = opts.source || 'dts';
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// ---------------------------------------------------------------------------
|
|
46
|
+
// Low-level helpers
|
|
47
|
+
// ---------------------------------------------------------------------------
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Find the index of the closing parenthesis that matches the opening one at
|
|
51
|
+
* `start` in `str`. Returns -1 if not found.
|
|
52
|
+
*/
|
|
53
|
+
function find_close_paren(str, start) {
|
|
54
|
+
let depth = 0;
|
|
55
|
+
for (let i = start; i < str.length; i++) {
|
|
56
|
+
if (str[i] === '(') depth++;
|
|
57
|
+
else if (str[i] === ')') { depth--; if (depth === 0) return i; }
|
|
58
|
+
}
|
|
59
|
+
return -1;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Split a parameter list string on commas at depth 0, respecting
|
|
64
|
+
* generic brackets `<>`, parens `()`, and square brackets `[]`.
|
|
65
|
+
*/
|
|
66
|
+
function split_params(s) {
|
|
67
|
+
const parts = [];
|
|
68
|
+
let depth = 0;
|
|
69
|
+
let cur = '';
|
|
70
|
+
for (let i = 0; i < s.length; i++) {
|
|
71
|
+
const ch = s[i];
|
|
72
|
+
if (ch === '<' || ch === '(' || ch === '[' || ch === '{') depth++;
|
|
73
|
+
else if (ch === '>' || ch === ')' || ch === ']' || ch === '}') depth--;
|
|
74
|
+
else if (ch === ',' && depth === 0) {
|
|
75
|
+
parts.push(cur.trim());
|
|
76
|
+
cur = '';
|
|
77
|
+
continue;
|
|
78
|
+
}
|
|
79
|
+
cur += ch;
|
|
80
|
+
}
|
|
81
|
+
if (cur.trim()) parts.push(cur.trim());
|
|
82
|
+
return parts;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Parse a single parameter token like `name: Type`, `name?: Type`,
|
|
87
|
+
* `...name: Type[]` into `{name, type, optional, rest}`.
|
|
88
|
+
*/
|
|
89
|
+
function parse_one_param(s) {
|
|
90
|
+
const rest = s.startsWith('...');
|
|
91
|
+
if (rest) s = s.slice(3);
|
|
92
|
+
|
|
93
|
+
const colon = s.indexOf(':');
|
|
94
|
+
let name, type;
|
|
95
|
+
if (colon !== -1) {
|
|
96
|
+
name = s.slice(0, colon).replace(/\?$/, '').trim();
|
|
97
|
+
type = s.slice(colon + 1).trim();
|
|
98
|
+
} else {
|
|
99
|
+
name = s.replace(/\?$/, '').trim();
|
|
100
|
+
type = 'any';
|
|
101
|
+
}
|
|
102
|
+
const optional = colon !== -1
|
|
103
|
+
? s.slice(0, colon).trimEnd().endsWith('?')
|
|
104
|
+
: s.trimEnd().endsWith('?');
|
|
105
|
+
|
|
106
|
+
return { name, type, optional, rest };
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Parse a `(...)` parameter section from a declaration string.
|
|
111
|
+
* `decl` is the portion of text starting with `(`.
|
|
112
|
+
* Returns `{ params, rest }` where `rest` is the text after the closing `)`.
|
|
113
|
+
*/
|
|
114
|
+
function parse_params_from(decl) {
|
|
115
|
+
if (!decl.startsWith('(')) return { params: [], rest: decl };
|
|
116
|
+
const close = find_close_paren(decl, 0);
|
|
117
|
+
if (close < 0) return { params: [], rest: '' };
|
|
118
|
+
const inner = decl.slice(1, close);
|
|
119
|
+
const rest = decl.slice(close + 1);
|
|
120
|
+
const params = inner.trim()
|
|
121
|
+
? split_params(inner).map(parse_one_param)
|
|
122
|
+
: [];
|
|
123
|
+
return { params, rest };
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* Extract the return type from text like `: ReturnType;` or `): ReturnType {`.
|
|
128
|
+
* Returns null if no type annotation is found.
|
|
129
|
+
*/
|
|
130
|
+
function parse_return_type(s) {
|
|
131
|
+
const m = s.match(/^\s*:\s*(.+?)(?:\s*[;{]|$)/);
|
|
132
|
+
return m ? m[1].trim() : null;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* Collect lines from a `/**...* /` block and return the trimmed doc text.
|
|
137
|
+
*/
|
|
138
|
+
function extract_jsdoc(lines) {
|
|
139
|
+
const text = lines
|
|
140
|
+
.map(l => l
|
|
141
|
+
.replace(/^\s*\/\*\*?\s?/, '')
|
|
142
|
+
.replace(/\*\/$/, '')
|
|
143
|
+
.replace(/^\s*\*\s?/, '')
|
|
144
|
+
.trim()
|
|
145
|
+
)
|
|
146
|
+
.filter(l => l.length > 0)
|
|
147
|
+
.join(' ');
|
|
148
|
+
return text || null;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
// ---------------------------------------------------------------------------
|
|
152
|
+
// Block body collector
|
|
153
|
+
// ---------------------------------------------------------------------------
|
|
154
|
+
|
|
155
|
+
/**
|
|
156
|
+
* Starting at line index `i`, collect all text up to and including the
|
|
157
|
+
* matching closing `}`, tracking brace depth.
|
|
158
|
+
* Returns `{ body, next_i }`.
|
|
159
|
+
*/
|
|
160
|
+
function collect_block(lines, i) {
|
|
161
|
+
const body_lines = [];
|
|
162
|
+
let depth = 0;
|
|
163
|
+
while (i < lines.length) {
|
|
164
|
+
const l = lines[i];
|
|
165
|
+
for (const ch of l) {
|
|
166
|
+
if (ch === '{') depth++;
|
|
167
|
+
else if (ch === '}') depth--;
|
|
168
|
+
}
|
|
169
|
+
body_lines.push(l);
|
|
170
|
+
i++;
|
|
171
|
+
if (depth === 0 && body_lines.length > 0) break;
|
|
172
|
+
}
|
|
173
|
+
const joined = body_lines.join('\n');
|
|
174
|
+
const open = joined.indexOf('{');
|
|
175
|
+
const close = joined.lastIndexOf('}');
|
|
176
|
+
const inner = (open >= 0 && close > open) ? joined.slice(open + 1, close) : '';
|
|
177
|
+
return { inner, next_i: i };
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
// ---------------------------------------------------------------------------
|
|
181
|
+
// Member parser (inside class / interface / namespace bodies)
|
|
182
|
+
// ---------------------------------------------------------------------------
|
|
183
|
+
|
|
184
|
+
function parse_members(text) {
|
|
185
|
+
const members = new Map();
|
|
186
|
+
const lines = text.split('\n');
|
|
187
|
+
let jsdoc_lines = [];
|
|
188
|
+
let i = 0;
|
|
189
|
+
|
|
190
|
+
while (i < lines.length) {
|
|
191
|
+
const raw = lines[i];
|
|
192
|
+
const line = raw.trim();
|
|
193
|
+
|
|
194
|
+
// JSDoc block
|
|
195
|
+
if (line.startsWith('/**') || (line.startsWith('/*') && !line.startsWith('*/')) ) {
|
|
196
|
+
jsdoc_lines = [line];
|
|
197
|
+
// If the block isn't closed on this line, keep reading
|
|
198
|
+
if (!line.includes('*/')) {
|
|
199
|
+
i++;
|
|
200
|
+
while (i < lines.length && !lines[i].includes('*/')) {
|
|
201
|
+
jsdoc_lines.push(lines[i].trim());
|
|
202
|
+
i++;
|
|
203
|
+
}
|
|
204
|
+
if (i < lines.length) { jsdoc_lines.push(lines[i].trim()); i++; }
|
|
205
|
+
} else {
|
|
206
|
+
i++;
|
|
207
|
+
}
|
|
208
|
+
continue;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
if (line.startsWith('//') || !line) {
|
|
212
|
+
if (!line) jsdoc_lines = [];
|
|
213
|
+
i++;
|
|
214
|
+
continue;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
const doc = extract_jsdoc(jsdoc_lines);
|
|
218
|
+
jsdoc_lines = [];
|
|
219
|
+
|
|
220
|
+
// Strip access modifiers and declaration keywords
|
|
221
|
+
let l = line.replace(
|
|
222
|
+
/^(?:static\s+|readonly\s+|abstract\s+|override\s+|protected\s+|private\s+|public\s+)*/,
|
|
223
|
+
''
|
|
224
|
+
).trim();
|
|
225
|
+
// Inside namespaces, members can be prefixed with function/const/let/var
|
|
226
|
+
const fn_kw = l.match(/^function\s+/);
|
|
227
|
+
if (fn_kw) l = l.slice(fn_kw[0].length).trim();
|
|
228
|
+
const var_kw = l.match(/^(?:const|let|var)\s+/);
|
|
229
|
+
if (var_kw) l = l.slice(var_kw[0].length).trim();
|
|
230
|
+
|
|
231
|
+
// Skip index signatures [key: Type]: Type
|
|
232
|
+
if (l.startsWith('[')) { i++; continue; }
|
|
233
|
+
|
|
234
|
+
// Skip nested blocks (e.g. nested namespace)
|
|
235
|
+
if (/^(?:class|interface|namespace|module)\s/.test(l)) { i++; continue; }
|
|
236
|
+
|
|
237
|
+
// method(...): ReturnType; or method(...) {
|
|
238
|
+
const paren_idx = l.indexOf('(');
|
|
239
|
+
if (paren_idx > 0) {
|
|
240
|
+
const name = l.slice(0, paren_idx).replace(/[?<].*$/, '').trim();
|
|
241
|
+
if (name && /^\w+$/.test(name) && name !== 'constructor') {
|
|
242
|
+
const after_name = l.slice(paren_idx);
|
|
243
|
+
const { params, rest } = parse_params_from(after_name);
|
|
244
|
+
const return_type = parse_return_type(rest);
|
|
245
|
+
members.set(name, new TypeInfo({
|
|
246
|
+
name,
|
|
247
|
+
kind: 'method',
|
|
248
|
+
params,
|
|
249
|
+
return_type,
|
|
250
|
+
doc,
|
|
251
|
+
}));
|
|
252
|
+
}
|
|
253
|
+
i++;
|
|
254
|
+
continue;
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
// property: Type;
|
|
258
|
+
const prop_m = l.match(/^(\w+)\??\s*:\s*(.+?)\s*[;,]?$/);
|
|
259
|
+
if (prop_m) {
|
|
260
|
+
members.set(prop_m[1], new TypeInfo({
|
|
261
|
+
name: prop_m[1],
|
|
262
|
+
kind: 'property',
|
|
263
|
+
return_type: prop_m[2],
|
|
264
|
+
doc,
|
|
265
|
+
}));
|
|
266
|
+
i++;
|
|
267
|
+
continue;
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
i++;
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
return members;
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
// ---------------------------------------------------------------------------
|
|
277
|
+
// Top-level parser
|
|
278
|
+
// ---------------------------------------------------------------------------
|
|
279
|
+
|
|
280
|
+
/**
|
|
281
|
+
* Parse a .d.ts file and return an array of TypeInfo objects (top-level only).
|
|
282
|
+
* @param {string} text
|
|
283
|
+
* @returns {TypeInfo[]}
|
|
284
|
+
*/
|
|
285
|
+
export function parse_dts(text) {
|
|
286
|
+
const lines = text.split('\n');
|
|
287
|
+
const results = [];
|
|
288
|
+
let jsdoc_lines = [];
|
|
289
|
+
let i = 0;
|
|
290
|
+
|
|
291
|
+
while (i < lines.length) {
|
|
292
|
+
const raw = lines[i];
|
|
293
|
+
const line = raw.trim();
|
|
294
|
+
|
|
295
|
+
// Accumulate JSDoc
|
|
296
|
+
if (line.startsWith('/**') || (line.startsWith('/*') && !line.startsWith('*/'))) {
|
|
297
|
+
jsdoc_lines = [line];
|
|
298
|
+
if (!line.includes('*/')) {
|
|
299
|
+
i++;
|
|
300
|
+
while (i < lines.length && !lines[i].includes('*/')) {
|
|
301
|
+
jsdoc_lines.push(lines[i].trim());
|
|
302
|
+
i++;
|
|
303
|
+
}
|
|
304
|
+
if (i < lines.length) { jsdoc_lines.push(lines[i].trim()); i++; }
|
|
305
|
+
} else {
|
|
306
|
+
i++;
|
|
307
|
+
}
|
|
308
|
+
continue;
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
if (line.startsWith('//') || !line) {
|
|
312
|
+
if (!line) jsdoc_lines = [];
|
|
313
|
+
i++;
|
|
314
|
+
continue;
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
const doc = extract_jsdoc(jsdoc_lines);
|
|
318
|
+
jsdoc_lines = [];
|
|
319
|
+
|
|
320
|
+
// Strip `export` and `declare` prefixes
|
|
321
|
+
let l = line
|
|
322
|
+
.replace(/^export\s+default\s+/, '')
|
|
323
|
+
.replace(/^export\s+/, '')
|
|
324
|
+
.replace(/^declare\s+/, '')
|
|
325
|
+
.trim();
|
|
326
|
+
|
|
327
|
+
// ── var / let / const ──────────────────────────────────────────────
|
|
328
|
+
const var_m = l.match(/^(?:var|let|const)\s+(\w+)\s*(?::\s*(.+?))?\s*[;=]/);
|
|
329
|
+
if (var_m) {
|
|
330
|
+
results.push(new TypeInfo({
|
|
331
|
+
name: var_m[1],
|
|
332
|
+
kind: 'var',
|
|
333
|
+
return_type: var_m[2] ? var_m[2].trim() : null,
|
|
334
|
+
doc,
|
|
335
|
+
}));
|
|
336
|
+
i++;
|
|
337
|
+
continue;
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
// ── function ───────────────────────────────────────────────────────
|
|
341
|
+
const fn_paren = l.indexOf('(');
|
|
342
|
+
const fn_m = fn_paren > 0 && l.match(/^function\s+(\w+)/);
|
|
343
|
+
if (fn_m) {
|
|
344
|
+
const name = fn_m[1];
|
|
345
|
+
const after_name = l.slice(l.indexOf('('));
|
|
346
|
+
const { params, rest } = parse_params_from(after_name);
|
|
347
|
+
const return_type = parse_return_type(rest);
|
|
348
|
+
results.push(new TypeInfo({ name, kind: 'function', params, return_type, doc }));
|
|
349
|
+
i++;
|
|
350
|
+
continue;
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
// ── class / interface / abstract class / namespace / module ────────
|
|
354
|
+
const block_m = l.match(/^(abstract\s+class|class|interface|namespace|module)\s+(\w+)/);
|
|
355
|
+
if (block_m) {
|
|
356
|
+
const kind_raw = block_m[1].replace('abstract ', '').trim();
|
|
357
|
+
const kind = (kind_raw === 'module') ? 'namespace' : kind_raw;
|
|
358
|
+
const name = block_m[2];
|
|
359
|
+
|
|
360
|
+
if (l.includes('{')) {
|
|
361
|
+
// Block opens on this line — collect to matching '}'
|
|
362
|
+
const { inner, next_i } = collect_block(lines, i);
|
|
363
|
+
const members = parse_members(inner);
|
|
364
|
+
results.push(new TypeInfo({ name, kind, members, doc }));
|
|
365
|
+
i = next_i;
|
|
366
|
+
} else {
|
|
367
|
+
// Declaration without body (e.g. `declare class Foo;`)
|
|
368
|
+
results.push(new TypeInfo({ name, kind, doc }));
|
|
369
|
+
i++;
|
|
370
|
+
}
|
|
371
|
+
continue;
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
// ── type alias (no body extracted — just register the name) ────────
|
|
375
|
+
const type_m = l.match(/^type\s+(\w+)\s*(?:<[^>]*>)?\s*=/);
|
|
376
|
+
if (type_m) {
|
|
377
|
+
results.push(new TypeInfo({ name: type_m[1], kind: 'var', doc }));
|
|
378
|
+
i++;
|
|
379
|
+
continue;
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
i++;
|
|
383
|
+
jsdoc_lines = [];
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
return results;
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
// ---------------------------------------------------------------------------
|
|
390
|
+
// Type-string helpers
|
|
391
|
+
// ---------------------------------------------------------------------------
|
|
392
|
+
|
|
393
|
+
/**
|
|
394
|
+
* Extract the first concrete type name from a TypeScript type string.
|
|
395
|
+
* Handles union types (A | B), generic types (A<B>), and simple names.
|
|
396
|
+
* Returns null if no plain identifier can be extracted.
|
|
397
|
+
*
|
|
398
|
+
* Examples:
|
|
399
|
+
* 'Server' → 'Server'
|
|
400
|
+
* 'Server | (DarkServer & { isOnline: boolean })' → 'Server'
|
|
401
|
+
* 'Promise<number>' → 'Promise'
|
|
402
|
+
*/
|
|
403
|
+
export function resolve_first_type(type_str) {
|
|
404
|
+
if (!type_str) return null;
|
|
405
|
+
const first = type_str.split('|')[0].trim();
|
|
406
|
+
const base = first.replace(/<.*$/, '').trim();
|
|
407
|
+
return /^\w+$/.test(base) ? base : null;
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
// ---------------------------------------------------------------------------
|
|
411
|
+
// DtsRegistry
|
|
412
|
+
// ---------------------------------------------------------------------------
|
|
413
|
+
|
|
414
|
+
export class DtsRegistry {
|
|
415
|
+
constructor() {
|
|
416
|
+
this._globals = new Map(); // name → TypeInfo
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
/**
|
|
420
|
+
* Parse and register a .d.ts file. Calling this multiple times merges
|
|
421
|
+
* all declarations into the same global namespace.
|
|
422
|
+
* @param {string} _name a label for this file (reserved for future dedup)
|
|
423
|
+
* @param {string} text the .d.ts source text
|
|
424
|
+
*/
|
|
425
|
+
addDts(_name, text) {
|
|
426
|
+
const types = parse_dts(text);
|
|
427
|
+
for (const ti of types) {
|
|
428
|
+
this._globals.set(ti.name, ti);
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
/**
|
|
433
|
+
* Return all registered global symbol names.
|
|
434
|
+
* @returns {string[]}
|
|
435
|
+
*/
|
|
436
|
+
getGlobalNames() {
|
|
437
|
+
return Array.from(this._globals.keys());
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
/**
|
|
441
|
+
* Return the TypeInfo for a name, or null.
|
|
442
|
+
* @param {string} name
|
|
443
|
+
* @returns {TypeInfo|null}
|
|
444
|
+
*/
|
|
445
|
+
getGlobal(name) {
|
|
446
|
+
return this._globals.get(name) || null;
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
/**
|
|
450
|
+
* Return a Monaco hover markdown string for a name, or null.
|
|
451
|
+
* @param {string} name
|
|
452
|
+
* @returns {string|null}
|
|
453
|
+
*/
|
|
454
|
+
getHoverMarkdown(name) {
|
|
455
|
+
const ti = this.getGlobal(name);
|
|
456
|
+
return ti ? build_dts_hover(ti) : null;
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
/**
|
|
460
|
+
* Return completion-style member names for a type (used for dot-completion
|
|
461
|
+
* on .d.ts-typed variables when Phase 6 type inference is wired up).
|
|
462
|
+
* @param {string} name
|
|
463
|
+
* @returns {string[]}
|
|
464
|
+
*/
|
|
465
|
+
getMemberNames(name) {
|
|
466
|
+
const ti = this.getGlobal(name);
|
|
467
|
+
if (!ti || !ti.members) return [];
|
|
468
|
+
return Array.from(ti.members.keys());
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
/**
|
|
472
|
+
* Follow a dot-path through the type registry and return the TypeInfo
|
|
473
|
+
* for the named member at the end of the chain.
|
|
474
|
+
*
|
|
475
|
+
* Examples:
|
|
476
|
+
* getMemberInfo('ns', 'hack') → TypeInfo for NS.hack
|
|
477
|
+
* getMemberInfo('ns.hacknet', 'purchaseNode') → TypeInfo for Hacknet.purchaseNode
|
|
478
|
+
*
|
|
479
|
+
* @param {string} objectPath dot-separated path (e.g. 'ns' or 'ns.hacknet')
|
|
480
|
+
* @param {string} memberName the attribute being accessed
|
|
481
|
+
* @returns {TypeInfo|null}
|
|
482
|
+
*/
|
|
483
|
+
getMemberInfo(objectPath, memberName) {
|
|
484
|
+
const parts = objectPath.split('.');
|
|
485
|
+
let ti = this._globals.get(parts[0]);
|
|
486
|
+
// Dereference var → type (e.g. var ns: NS → NS interface)
|
|
487
|
+
if (ti && !ti.members && ti.return_type) {
|
|
488
|
+
ti = this._globals.get(resolve_first_type(ti.return_type));
|
|
489
|
+
}
|
|
490
|
+
// Walk remaining path segments
|
|
491
|
+
for (let i = 1; i < parts.length && ti; i++) {
|
|
492
|
+
const member = ti.members ? ti.members.get(parts[i]) : null;
|
|
493
|
+
if (!member) { ti = null; break; }
|
|
494
|
+
if (member.members) {
|
|
495
|
+
ti = member;
|
|
496
|
+
} else if (member.return_type) {
|
|
497
|
+
ti = this._globals.get(resolve_first_type(member.return_type));
|
|
498
|
+
} else {
|
|
499
|
+
ti = null;
|
|
500
|
+
}
|
|
501
|
+
}
|
|
502
|
+
if (!ti || !ti.members) return null;
|
|
503
|
+
return ti.members.get(memberName) || null;
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
/**
|
|
507
|
+
* Return hover markdown for a member accessed via a dot-path, or null.
|
|
508
|
+
* @param {string} objectPath
|
|
509
|
+
* @param {string} memberName
|
|
510
|
+
* @returns {string|null}
|
|
511
|
+
*/
|
|
512
|
+
getMemberHoverMarkdown(objectPath, memberName) {
|
|
513
|
+
const ti = this.getMemberInfo(objectPath, memberName);
|
|
514
|
+
return ti ? build_dts_hover(ti) : null;
|
|
515
|
+
}
|
|
516
|
+
}
|
|
517
|
+
|
|
518
|
+
// ---------------------------------------------------------------------------
|
|
519
|
+
// DTS hover rendering
|
|
520
|
+
// ---------------------------------------------------------------------------
|
|
521
|
+
|
|
522
|
+
function build_dts_sig(ti) {
|
|
523
|
+
if (ti.kind === 'function' || ti.kind === 'method') {
|
|
524
|
+
const param_str = ti.params
|
|
525
|
+
? ti.params.map(function (p) {
|
|
526
|
+
let s = p.rest ? '...' : '';
|
|
527
|
+
s += p.name;
|
|
528
|
+
if (p.optional) s += '?';
|
|
529
|
+
if (p.type && p.type !== 'any') s += ': ' + p.type;
|
|
530
|
+
return s;
|
|
531
|
+
}).join(', ')
|
|
532
|
+
: '';
|
|
533
|
+
let sig = '(' + ti.kind + ') ' + ti.name + '(' + param_str + ')';
|
|
534
|
+
if (ti.return_type && ti.return_type !== 'void') sig += ': ' + ti.return_type;
|
|
535
|
+
return sig;
|
|
536
|
+
}
|
|
537
|
+
if (ti.kind === 'property' || ti.kind === 'var') {
|
|
538
|
+
let sig = '(' + ti.kind + ') ' + ti.name;
|
|
539
|
+
if (ti.return_type) sig += ': ' + ti.return_type;
|
|
540
|
+
return sig;
|
|
541
|
+
}
|
|
542
|
+
return '(' + ti.kind + ') ' + ti.name;
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
function build_dts_hover(ti) {
|
|
546
|
+
const sig = build_dts_sig(ti);
|
|
547
|
+
let md = '```typescript\n' + sig + '\n```';
|
|
548
|
+
if (ti.doc) md += '\n\n' + ti.doc;
|
|
549
|
+
return md;
|
|
550
|
+
}
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
// hover.js — Hover provider for the RapydScript language service.
|
|
2
|
+
//
|
|
3
|
+
// Usage:
|
|
4
|
+
// import { HoverEngine } from './hover.js';
|
|
5
|
+
// const engine = new HoverEngine();
|
|
6
|
+
// const hover = engine.getHover(scopeMap, position, word);
|
|
7
|
+
|
|
8
|
+
// ---------------------------------------------------------------------------
|
|
9
|
+
// Rendering helpers
|
|
10
|
+
// ---------------------------------------------------------------------------
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Build a one-line signature string for a symbol.
|
|
14
|
+
* Examples:
|
|
15
|
+
* (function) add(x, y)
|
|
16
|
+
* (class) Dog
|
|
17
|
+
* (variable) my_var
|
|
18
|
+
* (import) math
|
|
19
|
+
*
|
|
20
|
+
* @param {import('./scope.js').SymbolInfo} sym
|
|
21
|
+
* @returns {string}
|
|
22
|
+
*/
|
|
23
|
+
function build_signature(sym) {
|
|
24
|
+
const kind_label = '(' + sym.kind + ') ';
|
|
25
|
+
|
|
26
|
+
if ((sym.kind === 'function' || sym.kind === 'method') && sym.params) {
|
|
27
|
+
const param_str = sym.params.map(function (p) {
|
|
28
|
+
if (p.is_separator) return p.name; // '/' or '*'
|
|
29
|
+
if (p.is_kwargs) return '**' + p.name;
|
|
30
|
+
if (p.is_rest) return '*' + p.name;
|
|
31
|
+
return p.name;
|
|
32
|
+
}).join(', ');
|
|
33
|
+
return kind_label + sym.name + '(' + param_str + ')';
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
return kind_label + sym.name;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Build the full markdown hover string for a symbol.
|
|
41
|
+
* Returns a fenced code block for the signature, plus the docstring if present.
|
|
42
|
+
*
|
|
43
|
+
* @param {import('./scope.js').SymbolInfo} sym
|
|
44
|
+
* @returns {string}
|
|
45
|
+
*/
|
|
46
|
+
function build_hover_markdown(sym) {
|
|
47
|
+
const sig = build_signature(sym);
|
|
48
|
+
|
|
49
|
+
// Use a code block so Monaco renders it with syntax highlighting
|
|
50
|
+
let md = '```\n' + sig + '\n```';
|
|
51
|
+
|
|
52
|
+
if (sym.doc) {
|
|
53
|
+
md += '\n\n' + sym.doc;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
return md;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// ---------------------------------------------------------------------------
|
|
60
|
+
// HoverEngine
|
|
61
|
+
// ---------------------------------------------------------------------------
|
|
62
|
+
|
|
63
|
+
export class HoverEngine {
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* @param {import('./dts.js').DtsRegistry|null} [dtsRegistry]
|
|
67
|
+
* @param {import('./builtins.js').BuiltinsRegistry|null} [builtinsRegistry]
|
|
68
|
+
*/
|
|
69
|
+
constructor(dtsRegistry, builtinsRegistry) {
|
|
70
|
+
this._dts = dtsRegistry || null;
|
|
71
|
+
this._builtins = builtinsRegistry || null;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Return a Monaco IHover for the word under the cursor.
|
|
76
|
+
* Priority: ScopeMap (user-defined) → DTS member (dot-chain) → DTS global → builtins.
|
|
77
|
+
* Returns null if the word is not found in any source.
|
|
78
|
+
*
|
|
79
|
+
* @param {import('./scope.js').ScopeMap|null} scopeMap
|
|
80
|
+
* @param {{lineNumber:number,column:number}} position 1-indexed Monaco position
|
|
81
|
+
* @param {string|null} word the identifier under the cursor (from model.getWordAtPosition)
|
|
82
|
+
* @param {string} [line_before_word] text on the current line before the word's start column
|
|
83
|
+
* @returns {{ contents: {value:string}[], range?: object }|null}
|
|
84
|
+
*/
|
|
85
|
+
getHover(scopeMap, position, word, line_before_word) {
|
|
86
|
+
if (!word) return null;
|
|
87
|
+
|
|
88
|
+
// 1. ScopeMap lookup (user-defined symbols)
|
|
89
|
+
if (scopeMap) {
|
|
90
|
+
const sym = scopeMap.getSymbol(word, position.lineNumber, position.column);
|
|
91
|
+
if (sym) {
|
|
92
|
+
return { contents: [{ value: build_hover_markdown(sym) }] };
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// 2. DTS member lookup — check for dot-chain access
|
|
97
|
+
// e.g. hovering 'hack' in 'ns.hack(' → line_before_word ends with 'ns.'
|
|
98
|
+
// e.g. hovering 'purchaseNode' in 'ns.hacknet.purchaseNode(' → ends with 'ns.hacknet.'
|
|
99
|
+
if (this._dts && line_before_word) {
|
|
100
|
+
const dot_match = line_before_word.match(/([\w.]+)\.$/);
|
|
101
|
+
if (dot_match) {
|
|
102
|
+
const md = this._dts.getMemberHoverMarkdown(dot_match[1], word);
|
|
103
|
+
if (md) return { contents: [{ value: md }] };
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// 3. DTS global lookup (top-level declarations like `var ns: NS`)
|
|
108
|
+
if (this._dts) {
|
|
109
|
+
const md = this._dts.getHoverMarkdown(word);
|
|
110
|
+
if (md) return { contents: [{ value: md }] };
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// 4. Built-in stubs
|
|
114
|
+
if (this._builtins) {
|
|
115
|
+
const md = this._builtins.getHoverMarkdown(word);
|
|
116
|
+
if (md) return { contents: [{ value: md }] };
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
return null;
|
|
120
|
+
}
|
|
121
|
+
}
|