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,498 @@
|
|
|
1
|
+
// completions.js — Completion item provider for the RapydScript language service.
|
|
2
|
+
//
|
|
3
|
+
// Usage:
|
|
4
|
+
// import { CompletionEngine } from './completions.js';
|
|
5
|
+
// const engine = new CompletionEngine(analyzer, { virtualFiles, builtinNames });
|
|
6
|
+
// const items = engine.getCompletions(scopeMap, position, linePrefix, monacoKind);
|
|
7
|
+
|
|
8
|
+
import { SourceAnalyzer } from './analyzer.js';
|
|
9
|
+
import { resolve_first_type } from './dts.js';
|
|
10
|
+
|
|
11
|
+
// ---------------------------------------------------------------------------
|
|
12
|
+
// Context detection
|
|
13
|
+
// ---------------------------------------------------------------------------
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Inspect the text before the cursor and classify what kind of completion
|
|
17
|
+
* is appropriate.
|
|
18
|
+
*
|
|
19
|
+
* Returns one of:
|
|
20
|
+
* { type: 'dot', objectName: string, prefix: string }
|
|
21
|
+
* { type: 'from_import', moduleName: string, prefix: string }
|
|
22
|
+
* { type: 'import', prefix: string }
|
|
23
|
+
* { type: 'identifier', prefix: string }
|
|
24
|
+
*
|
|
25
|
+
* @param {string} linePrefix text from column 1 up to (but not including) the cursor
|
|
26
|
+
* @returns {{ type: string, objectName?: string, moduleName?: string, prefix: string }}
|
|
27
|
+
*/
|
|
28
|
+
export function detect_context(linePrefix) {
|
|
29
|
+
// `obj.attr` or `obj.sub.attr` — dot access (captures multi-level path)
|
|
30
|
+
const dot_match = linePrefix.match(/([\w.]+)\.([\w]*)$/);
|
|
31
|
+
if (dot_match) {
|
|
32
|
+
return { type: 'dot', objectName: dot_match[1], prefix: dot_match[2] };
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// `from modname import partial`
|
|
36
|
+
const from_match = linePrefix.match(/^\s*from\s+([\w.]+)\s+import\s+([\w]*)$/);
|
|
37
|
+
if (from_match) {
|
|
38
|
+
return { type: 'from_import', moduleName: from_match[1], prefix: from_match[2] };
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// `import partial` (bare import at start of line)
|
|
42
|
+
const import_match = linePrefix.match(/^\s*import\s+([\w.]*)$/);
|
|
43
|
+
if (import_match) {
|
|
44
|
+
return { type: 'import', prefix: import_match[1] };
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// Default: identifier completion
|
|
48
|
+
const ident_match = linePrefix.match(/(\w*)$/);
|
|
49
|
+
return { type: 'identifier', prefix: ident_match ? ident_match[1] : '' };
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// ---------------------------------------------------------------------------
|
|
53
|
+
// Monaco item helpers
|
|
54
|
+
// ---------------------------------------------------------------------------
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Build a Monaco IRange covering the word currently being typed.
|
|
58
|
+
* @param {{lineNumber:number,column:number}} position Monaco 1-indexed position
|
|
59
|
+
* @param {string} prefix the portion of the word already typed
|
|
60
|
+
* @returns {{startLineNumber,startColumn,endLineNumber,endColumn}}
|
|
61
|
+
*/
|
|
62
|
+
function word_range(position, prefix) {
|
|
63
|
+
return {
|
|
64
|
+
startLineNumber: position.lineNumber,
|
|
65
|
+
startColumn: position.column - prefix.length,
|
|
66
|
+
endLineNumber: position.lineNumber,
|
|
67
|
+
endColumn: position.column,
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Map a SymbolInfo kind to a Monaco CompletionItemKind value.
|
|
73
|
+
* monacoKind is the Monaco.languages.CompletionItemKind enum object.
|
|
74
|
+
* @param {string} kind
|
|
75
|
+
* @param {object} monacoKind
|
|
76
|
+
* @returns {number}
|
|
77
|
+
*/
|
|
78
|
+
function kind_to_monaco(kind, monacoKind) {
|
|
79
|
+
switch (kind) {
|
|
80
|
+
case 'function': return monacoKind.Function;
|
|
81
|
+
case 'method': return monacoKind.Method;
|
|
82
|
+
case 'class': return monacoKind.Class;
|
|
83
|
+
case 'parameter': return monacoKind.Variable;
|
|
84
|
+
case 'import': return monacoKind.Module;
|
|
85
|
+
default: return monacoKind.Variable;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Build a Monaco CompletionItem from a SymbolInfo.
|
|
91
|
+
* @param {import('./scope.js').SymbolInfo} sym
|
|
92
|
+
* @param {object} range Monaco IRange
|
|
93
|
+
* @param {object} monacoKind Monaco CompletionItemKind enum
|
|
94
|
+
* @param {string} sortPrefix e.g. '0', '1', '2' for ranking
|
|
95
|
+
* @returns {object} Monaco CompletionItem
|
|
96
|
+
*/
|
|
97
|
+
function symbol_to_item(sym, range, monacoKind, sortPrefix) {
|
|
98
|
+
let detail = sym.kind;
|
|
99
|
+
if (sym.params) {
|
|
100
|
+
const param_str = sym.params.map(p => {
|
|
101
|
+
if (p.is_separator) return p.name; // '/' or '*'
|
|
102
|
+
if (p.is_kwargs) return '**' + p.name;
|
|
103
|
+
if (p.is_rest) return '*' + p.name;
|
|
104
|
+
return p.name;
|
|
105
|
+
}).join(', ');
|
|
106
|
+
detail = '(' + param_str + ')';
|
|
107
|
+
}
|
|
108
|
+
return {
|
|
109
|
+
label: sym.name,
|
|
110
|
+
kind: kind_to_monaco(sym.kind, monacoKind),
|
|
111
|
+
detail,
|
|
112
|
+
documentation: sym.doc || undefined,
|
|
113
|
+
sortText: sortPrefix + '_' + sym.name,
|
|
114
|
+
insertText: sym.name,
|
|
115
|
+
range,
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* Build a simple completion item from a plain name string (e.g. a builtin with no stub).
|
|
121
|
+
* @param {string} name
|
|
122
|
+
* @param {object} range
|
|
123
|
+
* @param {object} monacoKind
|
|
124
|
+
* @returns {object}
|
|
125
|
+
*/
|
|
126
|
+
function name_to_item(name, range, monacoKind) {
|
|
127
|
+
return {
|
|
128
|
+
label: name,
|
|
129
|
+
kind: monacoKind.Variable,
|
|
130
|
+
sortText: '2_' + name,
|
|
131
|
+
insertText: name,
|
|
132
|
+
range,
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* Build a Monaco CompletionItem from a BuiltinInfo stub (richer than name_to_item).
|
|
138
|
+
* @param {import('./builtins.js').BuiltinInfo} stub
|
|
139
|
+
* @param {object} range
|
|
140
|
+
* @param {object} monacoKind
|
|
141
|
+
* @returns {object}
|
|
142
|
+
*/
|
|
143
|
+
function _builtin_to_item(stub, range, monacoKind) {
|
|
144
|
+
const kind = stub.kind === 'function' ? monacoKind.Function :
|
|
145
|
+
stub.kind === 'class' ? monacoKind.Class : monacoKind.Variable;
|
|
146
|
+
let detail = stub.kind;
|
|
147
|
+
if (stub.params) {
|
|
148
|
+
const ps = stub.params.map(function (p) {
|
|
149
|
+
let s = p.label;
|
|
150
|
+
if (p.type && p.type !== 'any') s += ': ' + p.type;
|
|
151
|
+
return s;
|
|
152
|
+
}).join(', ');
|
|
153
|
+
detail = '(' + ps + ')';
|
|
154
|
+
if (stub.return_type && stub.return_type !== 'None') {
|
|
155
|
+
detail += ' → ' + stub.return_type;
|
|
156
|
+
}
|
|
157
|
+
} else if (stub.return_type) {
|
|
158
|
+
detail = stub.return_type;
|
|
159
|
+
}
|
|
160
|
+
return {
|
|
161
|
+
label: stub.name,
|
|
162
|
+
kind,
|
|
163
|
+
detail,
|
|
164
|
+
documentation: stub.doc || undefined,
|
|
165
|
+
sortText: '2_' + stub.name,
|
|
166
|
+
insertText: stub.name,
|
|
167
|
+
range,
|
|
168
|
+
};
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
/**
|
|
172
|
+
* Build a Monaco CompletionItem from a DTS TypeInfo member (method or property).
|
|
173
|
+
* @param {import('./dts.js').TypeInfo} member
|
|
174
|
+
* @param {object} range
|
|
175
|
+
* @param {object} monacoKind
|
|
176
|
+
* @returns {object}
|
|
177
|
+
*/
|
|
178
|
+
function _dts_member_to_item(member, range, monacoKind) {
|
|
179
|
+
const kind = member.kind === 'method' ? monacoKind.Method : monacoKind.Property;
|
|
180
|
+
let detail = member.kind;
|
|
181
|
+
if (member.kind === 'method' && member.params) {
|
|
182
|
+
const ps = member.params.map(function (p) {
|
|
183
|
+
let s = p.rest ? '...' : '';
|
|
184
|
+
s += p.name;
|
|
185
|
+
if (p.optional) s += '?';
|
|
186
|
+
return s;
|
|
187
|
+
}).join(', ');
|
|
188
|
+
detail = '(' + ps + ')';
|
|
189
|
+
if (member.return_type && member.return_type !== 'void') {
|
|
190
|
+
detail += ': ' + member.return_type;
|
|
191
|
+
}
|
|
192
|
+
} else if (member.return_type) {
|
|
193
|
+
detail = member.return_type;
|
|
194
|
+
}
|
|
195
|
+
return {
|
|
196
|
+
label: member.name,
|
|
197
|
+
kind,
|
|
198
|
+
detail,
|
|
199
|
+
documentation: member.doc || undefined,
|
|
200
|
+
sortText: '0_' + member.name,
|
|
201
|
+
insertText: member.name,
|
|
202
|
+
range,
|
|
203
|
+
};
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
// ---------------------------------------------------------------------------
|
|
207
|
+
// CompletionEngine
|
|
208
|
+
// ---------------------------------------------------------------------------
|
|
209
|
+
|
|
210
|
+
export class CompletionEngine {
|
|
211
|
+
/**
|
|
212
|
+
* @param {import('./analyzer.js').SourceAnalyzer} analyzer
|
|
213
|
+
* @param {object} opts
|
|
214
|
+
* @param {object} [opts.virtualFiles] module-name → source
|
|
215
|
+
* @param {object} [opts.stdlibFiles] stdlib module-name → source (fallback for from X import)
|
|
216
|
+
* @param {string[]} [opts.builtinNames] names always available (BASE_BUILTINS + extras)
|
|
217
|
+
* @param {import('./dts.js').DtsRegistry|null} [opts.dtsRegistry] DTS globals for dot completion
|
|
218
|
+
* @param {import('./builtins.js').BuiltinsRegistry|null} [opts.builtinsRegistry] stubs for rich builtin items
|
|
219
|
+
*/
|
|
220
|
+
constructor(analyzer, opts) {
|
|
221
|
+
this._analyzer = analyzer;
|
|
222
|
+
this._virtualFiles = opts.virtualFiles || {};
|
|
223
|
+
this._stdlibFiles = opts.stdlibFiles || {};
|
|
224
|
+
this._builtinNames = opts.builtinNames || [];
|
|
225
|
+
this._dts = opts.dtsRegistry || null;
|
|
226
|
+
this._builtins = opts.builtinsRegistry || null;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
/**
|
|
230
|
+
* Update the virtual files available to import analysis.
|
|
231
|
+
* @param {object} virtualFiles
|
|
232
|
+
*/
|
|
233
|
+
setVirtualFiles(virtualFiles) {
|
|
234
|
+
this._virtualFiles = virtualFiles;
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
/**
|
|
238
|
+
* Produce Monaco completion items for the given position.
|
|
239
|
+
*
|
|
240
|
+
* @param {import('./scope.js').ScopeMap|null} scopeMap
|
|
241
|
+
* @param {{lineNumber:number,column:number}} position 1-indexed Monaco position
|
|
242
|
+
* @param {string} linePrefix text on the current line up to the cursor
|
|
243
|
+
* @param {object} monacoKind Monaco.languages.CompletionItemKind enum
|
|
244
|
+
* @returns {{ suggestions: object[] }} Monaco CompletionList
|
|
245
|
+
*/
|
|
246
|
+
getCompletions(scopeMap, position, linePrefix, monacoKind) {
|
|
247
|
+
const ctx = detect_context(linePrefix);
|
|
248
|
+
|
|
249
|
+
if (ctx.type === 'dot') {
|
|
250
|
+
return { suggestions: this._dot_completions(scopeMap, position, ctx, monacoKind) };
|
|
251
|
+
}
|
|
252
|
+
if (ctx.type === 'from_import') {
|
|
253
|
+
return { suggestions: this._from_import_completions(position, ctx, monacoKind) };
|
|
254
|
+
}
|
|
255
|
+
if (ctx.type === 'import') {
|
|
256
|
+
return { suggestions: this._module_name_completions(position, ctx, monacoKind) };
|
|
257
|
+
}
|
|
258
|
+
// Default: identifier completions from scope + builtins
|
|
259
|
+
return { suggestions: this._scope_completions(scopeMap, position, ctx, monacoKind) };
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
// ---- Identifier completions (scope + builtins) -------------------------
|
|
263
|
+
|
|
264
|
+
_scope_completions(scopeMap, position, ctx, monacoKind) {
|
|
265
|
+
const range = word_range(position, ctx.prefix);
|
|
266
|
+
const items = [];
|
|
267
|
+
const seen = new Set();
|
|
268
|
+
|
|
269
|
+
if (scopeMap) {
|
|
270
|
+
const symbols = scopeMap.getSymbolsAtPosition(position.lineNumber, position.column);
|
|
271
|
+
for (const sym of symbols) {
|
|
272
|
+
if (!ctx.prefix || sym.name.startsWith(ctx.prefix)) {
|
|
273
|
+
if (!seen.has(sym.name)) {
|
|
274
|
+
seen.add(sym.name);
|
|
275
|
+
const sort = sym.scope_depth > 0 ? '0' : '1';
|
|
276
|
+
items.push(symbol_to_item(sym, range, monacoKind, sort));
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
// Builtins (lowest priority) — use rich stub item when available
|
|
283
|
+
for (const name of this._builtinNames) {
|
|
284
|
+
if (!seen.has(name) && (!ctx.prefix || name.startsWith(ctx.prefix))) {
|
|
285
|
+
seen.add(name);
|
|
286
|
+
const stub = this._builtins ? this._builtins.get(name) : null;
|
|
287
|
+
items.push(stub
|
|
288
|
+
? _builtin_to_item(stub, range, monacoKind)
|
|
289
|
+
: name_to_item(name, range, monacoKind)
|
|
290
|
+
);
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
return items;
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
// ---- Dot completions ---------------------------------------------------
|
|
298
|
+
|
|
299
|
+
_dot_completions(scopeMap, position, ctx, monacoKind) {
|
|
300
|
+
const range = word_range(position, ctx.prefix);
|
|
301
|
+
const items = [];
|
|
302
|
+
const seen = new Set();
|
|
303
|
+
let scope_matched = false;
|
|
304
|
+
let obj_sym = null;
|
|
305
|
+
|
|
306
|
+
// 0. Multi-level path (e.g. 'ns.hacknet') — resolve via DTS type chain only.
|
|
307
|
+
if (ctx.objectName.includes('.') && this._dts) {
|
|
308
|
+
const parts = ctx.objectName.split('.');
|
|
309
|
+
// Resolve root symbol
|
|
310
|
+
let ti = this._dts.getGlobal(parts[0]);
|
|
311
|
+
// Follow first var → type reference (e.g. var ns: NS → NS interface)
|
|
312
|
+
if (ti && !ti.members && ti.return_type) {
|
|
313
|
+
ti = this._dts.getGlobal(resolve_first_type(ti.return_type));
|
|
314
|
+
}
|
|
315
|
+
// Walk remaining path segments through member types
|
|
316
|
+
for (let i = 1; i < parts.length && ti; i++) {
|
|
317
|
+
const member = ti.members ? ti.members.get(parts[i]) : null;
|
|
318
|
+
if (!member) { ti = null; break; }
|
|
319
|
+
if (member.members) {
|
|
320
|
+
ti = member;
|
|
321
|
+
} else if (member.return_type) {
|
|
322
|
+
ti = this._dts.getGlobal(resolve_first_type(member.return_type));
|
|
323
|
+
} else {
|
|
324
|
+
ti = null;
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
if (ti && ti.members) {
|
|
328
|
+
for (const [name, member] of ti.members) {
|
|
329
|
+
if (!ctx.prefix || name.startsWith(ctx.prefix)) {
|
|
330
|
+
items.push(_dts_member_to_item(member, range, monacoKind));
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
return items;
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
// 1. ScopeMap lookup — user-defined classes and inferred instances.
|
|
338
|
+
if (scopeMap) {
|
|
339
|
+
obj_sym = scopeMap.getSymbol(
|
|
340
|
+
ctx.objectName,
|
|
341
|
+
position.lineNumber,
|
|
342
|
+
position.column
|
|
343
|
+
);
|
|
344
|
+
// Fallback: the cursor may be past the end of all parsed scope ranges
|
|
345
|
+
// (e.g. the user is typing on a new line that isn't in the last debounced
|
|
346
|
+
// parse yet). Search all frames innermost-first so we still find the symbol.
|
|
347
|
+
if (!obj_sym) {
|
|
348
|
+
const all = scopeMap.frames.slice().sort((a, b) => b.depth - a.depth);
|
|
349
|
+
for (const frame of all) {
|
|
350
|
+
const sym = frame.getSymbol(ctx.objectName);
|
|
351
|
+
if (sym) { obj_sym = sym; break; }
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
let class_name = null;
|
|
356
|
+
if (obj_sym) {
|
|
357
|
+
if (obj_sym.kind === 'class') {
|
|
358
|
+
class_name = obj_sym.name;
|
|
359
|
+
} else if (obj_sym.inferred_class) {
|
|
360
|
+
class_name = obj_sym.inferred_class;
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
if (class_name) {
|
|
365
|
+
for (const frame of scopeMap.frames) {
|
|
366
|
+
if (frame.kind === 'class' && frame.name === class_name) {
|
|
367
|
+
scope_matched = true;
|
|
368
|
+
for (const [name, sym] of frame.symbols) {
|
|
369
|
+
if (!ctx.prefix || name.startsWith(ctx.prefix)) {
|
|
370
|
+
if (!seen.has(name)) {
|
|
371
|
+
seen.add(name);
|
|
372
|
+
items.push(symbol_to_item(sym, range, monacoKind, '0'));
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
// 1.5. Built-in type members — list, str, dict, number.
|
|
382
|
+
// Used when inferred_class names a built-in type, not a user class.
|
|
383
|
+
if (!scope_matched && this._builtins && obj_sym && obj_sym.inferred_class) {
|
|
384
|
+
const members = this._builtins.getTypeMembers(obj_sym.inferred_class);
|
|
385
|
+
if (members) {
|
|
386
|
+
scope_matched = true;
|
|
387
|
+
for (const [name, member] of members) {
|
|
388
|
+
if (!ctx.prefix || name.startsWith(ctx.prefix)) {
|
|
389
|
+
if (!seen.has(name)) {
|
|
390
|
+
seen.add(name);
|
|
391
|
+
items.push(_dts_member_to_item(member, range, monacoKind));
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
// 1.75. DTS return-type resolution — variables assigned from DTS method calls.
|
|
399
|
+
// e.g. `server = ns.getServer(...)` → resolve return type of ns.getServer from DTS.
|
|
400
|
+
if (!scope_matched && this._dts && obj_sym && obj_sym.dts_call_path) {
|
|
401
|
+
const call_path = obj_sym.dts_call_path;
|
|
402
|
+
const last_dot = call_path.lastIndexOf('.');
|
|
403
|
+
if (last_dot > 0) {
|
|
404
|
+
const object_path = call_path.slice(0, last_dot);
|
|
405
|
+
const method_name = call_path.slice(last_dot + 1);
|
|
406
|
+
const member_ti = this._dts.getMemberInfo(object_path, method_name);
|
|
407
|
+
if (member_ti && member_ti.return_type) {
|
|
408
|
+
const return_ti = this._dts.getGlobal(resolve_first_type(member_ti.return_type));
|
|
409
|
+
if (return_ti && return_ti.members) {
|
|
410
|
+
scope_matched = true;
|
|
411
|
+
for (const [name, member] of return_ti.members) {
|
|
412
|
+
if (!ctx.prefix || name.startsWith(ctx.prefix)) {
|
|
413
|
+
if (!seen.has(name)) {
|
|
414
|
+
seen.add(name);
|
|
415
|
+
items.push(_dts_member_to_item(member, range, monacoKind));
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
// 2. DTS registry fallback — namespaces / interfaces / classes from .d.ts.
|
|
425
|
+
// Skipped when ScopeMap already matched a class (ScopeMap wins).
|
|
426
|
+
if (!scope_matched && this._dts) {
|
|
427
|
+
let ti = this._dts.getGlobal(ctx.objectName);
|
|
428
|
+
// Follow type reference: `var console: Console` → look up `Console`
|
|
429
|
+
if (ti && !ti.members && ti.return_type) {
|
|
430
|
+
ti = this._dts.getGlobal(resolve_first_type(ti.return_type));
|
|
431
|
+
}
|
|
432
|
+
if (ti && ti.members) {
|
|
433
|
+
for (const [name, member] of ti.members) {
|
|
434
|
+
if (!ctx.prefix || name.startsWith(ctx.prefix)) {
|
|
435
|
+
if (!seen.has(name)) {
|
|
436
|
+
seen.add(name);
|
|
437
|
+
items.push(_dts_member_to_item(member, range, monacoKind));
|
|
438
|
+
}
|
|
439
|
+
}
|
|
440
|
+
}
|
|
441
|
+
}
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
return items;
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
// ---- from X import completions -----------------------------------------
|
|
448
|
+
|
|
449
|
+
_from_import_completions(position, ctx, monacoKind) {
|
|
450
|
+
const range = word_range(position, ctx.prefix);
|
|
451
|
+
const items = [];
|
|
452
|
+
|
|
453
|
+
const src = this._virtualFiles[ctx.moduleName] || this._stdlibFiles[ctx.moduleName];
|
|
454
|
+
if (!src) return items;
|
|
455
|
+
|
|
456
|
+
let scopeMap;
|
|
457
|
+
try {
|
|
458
|
+
scopeMap = this._analyzer.analyze(src, {});
|
|
459
|
+
} catch (_e) {
|
|
460
|
+
return items;
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
// Return all module-level symbols
|
|
464
|
+
const module_frame = scopeMap.frames.find(f => f.kind === 'module');
|
|
465
|
+
if (!module_frame) return items;
|
|
466
|
+
|
|
467
|
+
for (const [name, sym] of module_frame.symbols) {
|
|
468
|
+
if (!ctx.prefix || name.startsWith(ctx.prefix)) {
|
|
469
|
+
items.push(symbol_to_item(sym, range, monacoKind, '0'));
|
|
470
|
+
}
|
|
471
|
+
}
|
|
472
|
+
return items;
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
// ---- import module name completions ------------------------------------
|
|
476
|
+
|
|
477
|
+
_module_name_completions(position, ctx, monacoKind) {
|
|
478
|
+
const range = word_range(position, ctx.prefix);
|
|
479
|
+
const items = [];
|
|
480
|
+
const seen = new Set();
|
|
481
|
+
|
|
482
|
+
for (const modname of [...Object.keys(this._virtualFiles), ...Object.keys(this._stdlibFiles)]) {
|
|
483
|
+
if (!ctx.prefix || modname.startsWith(ctx.prefix)) {
|
|
484
|
+
if (!seen.has(modname)) {
|
|
485
|
+
seen.add(modname);
|
|
486
|
+
items.push({
|
|
487
|
+
label: modname,
|
|
488
|
+
kind: monacoKind.Module,
|
|
489
|
+
sortText: '0_' + modname,
|
|
490
|
+
insertText: modname,
|
|
491
|
+
range,
|
|
492
|
+
});
|
|
493
|
+
}
|
|
494
|
+
}
|
|
495
|
+
}
|
|
496
|
+
return items;
|
|
497
|
+
}
|
|
498
|
+
}
|