rapydscript-ns 0.8.0 → 0.8.2

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.
@@ -0,0 +1,83 @@
1
+ /*
2
+ * test/unit/language-service-bundle.js
3
+ *
4
+ * Verifies that the built web-repl/language-service.js can be loaded as an
5
+ * ES module without errors (e.g. duplicate top-level declarations).
6
+ *
7
+ * Usage:
8
+ * node test/unit/language-service-bundle.js
9
+ */
10
+ "use strict";
11
+
12
+ var path = require("path");
13
+ var url = require("url");
14
+ var fs = require("fs");
15
+ var utils = require("../../tools/utils");
16
+ var colored = utils.safe_colored;
17
+
18
+ var bundle_path = path.join(__dirname, "../../web-repl/language-service.js");
19
+
20
+ if (!fs.existsSync(bundle_path)) {
21
+ console.error(colored("SKIP language_service_bundle_loads", "yellow") +
22
+ " – web-repl/language-service.js not found; run: npm run build:ls");
23
+ process.exit(0);
24
+ }
25
+
26
+ var bundle_url = url.pathToFileURL(bundle_path).href;
27
+
28
+ import(bundle_url).then(function (mod) {
29
+ var passed = 0;
30
+ var failed = 0;
31
+
32
+ function pass(name, desc) {
33
+ passed++;
34
+ console.log(colored("PASS " + name, "green") + " \u2013 " + desc);
35
+ }
36
+ function fail(name, desc, err) {
37
+ failed++;
38
+ console.log(colored("FAIL " + name, "red") +
39
+ " \u2013 " + desc + "\n " + (err.message || String(err)));
40
+ }
41
+
42
+ // Test 1: module loads without parse/runtime errors (implicit — we got here)
43
+ pass("language_service_bundle_loads",
44
+ "web-repl/language-service.js loads as an ES module without errors");
45
+
46
+ // Test 2: registerRapydScript is exported
47
+ try {
48
+ if (typeof mod.registerRapydScript !== "function")
49
+ throw new Error("registerRapydScript is not a function, got: " + typeof mod.registerRapydScript);
50
+ pass("language_service_bundle_exports_register",
51
+ "registerRapydScript is exported as a function");
52
+ } catch (e) {
53
+ fail("language_service_bundle_exports_register",
54
+ "registerRapydScript is exported as a function", e);
55
+ }
56
+
57
+ // Test 3: web_repl is exported
58
+ try {
59
+ if (typeof mod.web_repl !== "function")
60
+ throw new Error("web_repl is not a function, got: " + typeof mod.web_repl);
61
+ pass("language_service_bundle_exports_web_repl",
62
+ "web_repl is exported as a function");
63
+ } catch (e) {
64
+ fail("language_service_bundle_exports_web_repl",
65
+ "web_repl is exported as a function", e);
66
+ }
67
+
68
+ console.log("");
69
+ if (failed) {
70
+ console.log(colored(failed + " test(s) failed.", "red"));
71
+ process.exit(1);
72
+ } else {
73
+ console.log(colored("All " + passed + " language-service-bundle tests passed!", "green"));
74
+ }
75
+
76
+ }).catch(function (e) {
77
+ console.log(colored("FAIL language_service_bundle_loads", "red") +
78
+ " \u2013 web-repl/language-service.js failed to load as an ES module");
79
+ console.log(" " + (e.message || String(e)));
80
+ console.log("");
81
+ console.log(colored("1 test(s) failed.", "red"));
82
+ process.exit(1);
83
+ });
@@ -26,6 +26,7 @@ var MockKind = {
26
26
  Class: 5,
27
27
  Variable: 6,
28
28
  Module: 8,
29
+ Keyword: 17,
29
30
  };
30
31
 
31
32
  // Mock Monaco position object
@@ -988,6 +989,114 @@ function make_tests(CompletionEngine, detect_context, SourceAnalyzer, DtsRegistr
988
989
  },
989
990
  },
990
991
 
992
+ // ── Keyword completions ───────────────────────────────────────────
993
+
994
+ {
995
+ name: "keywords_present_in_scope_completions",
996
+ description: "Control-flow keywords appear in identifier completions",
997
+ run: function () {
998
+ var engine = make_engine();
999
+ var list = engine.getCompletions(null, pos(1, 1), "", MockKind);
1000
+ assert_has(list, 'continue', 'continue keyword');
1001
+ assert_has(list, 'break', 'break keyword');
1002
+ assert_has(list, 'return', 'return keyword');
1003
+ assert_has(list, 'for', 'for keyword');
1004
+ assert_has(list, 'while', 'while keyword');
1005
+ assert_has(list, 'if', 'if keyword');
1006
+ assert_has(list, 'def', 'def keyword');
1007
+ assert_has(list, 'class', 'class keyword');
1008
+ assert_has(list, 'try', 'try keyword');
1009
+ assert_has(list, 'pass', 'pass keyword');
1010
+ assert_has(list, 'yield', 'yield keyword');
1011
+ assert_has(list, 'True', 'True keyword');
1012
+ assert_has(list, 'False', 'False keyword');
1013
+ assert_has(list, 'None', 'None keyword');
1014
+ },
1015
+ },
1016
+
1017
+ {
1018
+ name: "keywords_prefix_filter",
1019
+ description: "Prefix filter narrows keyword completions",
1020
+ run: function () {
1021
+ var engine = make_engine();
1022
+ var list = engine.getCompletions(null, pos(1, 4), "con", MockKind);
1023
+ assert_has(list, 'continue', 'continue matches con');
1024
+ assert_missing(list,'break', 'break does not match con');
1025
+ assert_missing(list,'return', 'return does not match con');
1026
+ },
1027
+ },
1028
+
1029
+ {
1030
+ name: "keywords_have_keyword_kind",
1031
+ description: "Keyword completion items use the Keyword kind",
1032
+ run: function () {
1033
+ var engine = make_engine();
1034
+ var list = engine.getCompletions(null, pos(1, 4), "con", MockKind);
1035
+ var item = find_item(list, 'continue');
1036
+ assert.ok(item, 'continue should be in suggestions');
1037
+ assert.strictEqual(item.kind, MockKind.Keyword, 'continue should have Keyword kind');
1038
+ },
1039
+ },
1040
+
1041
+ {
1042
+ name: "keywords_sort_after_builtins",
1043
+ description: "Keywords sort after builtins (sortText '3_' vs '2_')",
1044
+ run: function () {
1045
+ var engine = make_engine({}, ['for_builtin_test']);
1046
+ var list = engine.getCompletions(null, pos(1, 1), "", MockKind);
1047
+ var builtin_item = find_item(list, 'for_builtin_test');
1048
+ var kw_item = find_item(list, 'for');
1049
+ assert.ok(builtin_item, 'builtin should be present');
1050
+ assert.ok(kw_item, 'keyword should be present');
1051
+ assert.ok(builtin_item.sortText < kw_item.sortText,
1052
+ 'builtin sorts before keyword: ' + builtin_item.sortText + ' vs ' + kw_item.sortText);
1053
+ },
1054
+ },
1055
+
1056
+ {
1057
+ name: "keywords_not_in_dot_completions",
1058
+ description: "Keywords do not appear in dot (attribute) completions",
1059
+ run: function () {
1060
+ var engine = make_engine();
1061
+ var analyzer = new SourceAnalyzer(RS);
1062
+ var scopeMap = analyzer.analyze([
1063
+ "class Dog:",
1064
+ " def bark(self): return 'woof'",
1065
+ "d = Dog()",
1066
+ "pass",
1067
+ ].join("\n"), {});
1068
+
1069
+ var list = engine.getCompletions(scopeMap, pos(4, 1), "d.", MockKind);
1070
+ assert_missing(list, 'continue', 'continue must not appear in dot completions');
1071
+ assert_missing(list, 'return', 'return must not appear in dot completions');
1072
+ },
1073
+ },
1074
+
1075
+ {
1076
+ name: "keywords_not_in_from_import_completions",
1077
+ description: "Keywords do not appear in from X import completions",
1078
+ run: function () {
1079
+ var vf = { mymod: "def foo(): pass" };
1080
+ var engine = make_engine(vf);
1081
+ var list = engine.getCompletions(null, pos(1, 22), "from mymod import ", MockKind);
1082
+ assert_missing(list, 'continue', 'continue must not appear in from-import completions');
1083
+ assert_missing(list, 'return', 'return must not appear in from-import completions');
1084
+ },
1085
+ },
1086
+
1087
+ {
1088
+ name: "keywords_deduplicated_from_builtins",
1089
+ description: "Keywords that overlap with builtins are not duplicated",
1090
+ run: function () {
1091
+ // 'not', 'in', 'is' etc. are keywords; if any also appear as a builtin name
1092
+ // they must not appear twice.
1093
+ var engine = make_engine({}, ['True']); // True is also a keyword
1094
+ var list = engine.getCompletions(null, pos(1, 1), "", MockKind);
1095
+ var true_items = list.suggestions.filter(function (s) { return s.label === 'True'; });
1096
+ assert.strictEqual(true_items.length, 1, 'True should appear exactly once');
1097
+ },
1098
+ },
1099
+
991
1100
  ];
992
1101
 
993
1102
  return TESTS;
@@ -242,7 +242,7 @@ function make_tests(Diagnostics, RS) {
242
242
 
243
243
  {
244
244
  name: "import_error_caught",
245
- description: "An unresolvable import is caught and returned as a single Error marker",
245
+ description: "An unresolvable import produces a single Error marker (unused-import) when no module registry is configured",
246
246
  run: function () {
247
247
  var markers = d().check("from nonexistent_lib_xyz import something");
248
248
  assert.strictEqual(markers.length, 1);
@@ -250,6 +250,78 @@ function make_tests(Diagnostics, RS) {
250
250
  },
251
251
  },
252
252
 
253
+ // ── Bad-import squiggles ───────────────────────────────────────────
254
+
255
+ {
256
+ name: "bad_import_unknown_module",
257
+ description: "Importing from an unknown module produces a bad-import Error when a module registry is active",
258
+ run: function () {
259
+ var markers = d().check(
260
+ "from typo_mod import foo\nprint(foo())",
261
+ { virtualFiles: { mymod: "def foo(): return 1" } }
262
+ );
263
+ var bad = markers.filter(function (m) { return m.message.indexOf('Unknown module') !== -1; });
264
+ assert.ok(bad.length >= 1, "Expected at least one 'Unknown module' marker, got: " + JSON.stringify(markers));
265
+ assert.strictEqual(bad[0].severity, SEV_ERROR);
266
+ // Squiggle should point at the module name on line 1
267
+ assert.strictEqual(bad[0].startLineNumber, 1);
268
+ },
269
+ },
270
+
271
+ {
272
+ name: "bad_import_known_module_no_error",
273
+ description: "Importing from a registered module produces no bad-import error",
274
+ run: function () {
275
+ var markers = d().check(
276
+ "from mymod import foo\nprint(foo())",
277
+ { virtualFiles: { mymod: "def foo(): return 1" } }
278
+ );
279
+ var bad = markers.filter(function (m) { return m.message.indexOf('Unknown module') !== -1; });
280
+ assert.strictEqual(bad.length, 0, "Expected no 'Unknown module' marker for a known module");
281
+ },
282
+ },
283
+
284
+ {
285
+ name: "bad_import_no_registry_no_error",
286
+ description: "Unknown imports produce no bad-import error when no module registry is configured",
287
+ run: function () {
288
+ var markers = d().check("from anything import foo\nprint(foo())");
289
+ var bad = markers.filter(function (m) { return m.message.indexOf('Unknown module') !== -1; });
290
+ assert.strictEqual(bad.length, 0, "Expected no 'Unknown module' marker when no registry is configured");
291
+ },
292
+ },
293
+
294
+ {
295
+ name: "bad_import_squiggle_span",
296
+ description: "The bad-import squiggle covers the module name",
297
+ run: function () {
298
+ var markers = d().check(
299
+ "from typo_mod import foo",
300
+ { virtualFiles: { mymod: "def foo(): return 1" } }
301
+ );
302
+ var bad = markers.filter(function (m) { return m.message.indexOf('Unknown module') !== -1; });
303
+ assert.ok(bad.length >= 1, "Expected a bad-import marker");
304
+ var m = bad[0];
305
+ // 'typo_mod' appears after 'from ' (col 5, 1-indexed col 6)
306
+ assert.strictEqual(m.startColumn, 6, "startColumn should be 6 (start of 'typo_mod')");
307
+ // endColumn should cover all of 'typo_mod' (8 chars)
308
+ assert.ok(m.endColumn > m.startColumn, "endColumn should be past startColumn");
309
+ },
310
+ },
311
+
312
+ {
313
+ name: "bad_import_stdlib_registry",
314
+ description: "Importing from a registered stdlib module produces no bad-import error",
315
+ run: function () {
316
+ var markers = d().check(
317
+ "from mathlib import sqrt",
318
+ { stdlibFiles: { mathlib: "def sqrt(x): return x ** 0.5" } }
319
+ );
320
+ var bad = markers.filter(function (m) { return m.message.indexOf('Unknown module') !== -1; });
321
+ assert.strictEqual(bad.length, 0, "Expected no 'Unknown module' marker for a known stdlib module");
322
+ },
323
+ },
324
+
253
325
  // ── Semicolon warnings ────────────────────────────────────────────
254
326
 
255
327
  {
@@ -641,6 +713,56 @@ function make_tests(Diagnostics, RS) {
641
713
  },
642
714
  },
643
715
 
716
+ // ── Type annotations ──────────────────────────────────────────────
717
+
718
+ {
719
+ name: "annotated_assign_no_undef",
720
+ description: "Variable declared with type annotation (a: int = 5) is not flagged as undefined",
721
+ run: function () {
722
+ var markers = d().check([
723
+ "async def main():",
724
+ " a: int = 5",
725
+ " a += 4",
726
+ ].join("\n"));
727
+ var undef = markers.filter(function (m) { return m.message.indexOf("Undefined symbol") !== -1; });
728
+ assert.deepStrictEqual(undef, [],
729
+ "Expected no 'Undefined symbol' errors but got: " + JSON.stringify(undef));
730
+ },
731
+ },
732
+
733
+ {
734
+ name: "annotated_assign_used",
735
+ description: "Variable declared with type annotation is recognized as used when referenced",
736
+ run: function () {
737
+ var markers = d().check([
738
+ "def foo():",
739
+ " x: str = 'hello'",
740
+ " return x",
741
+ ].join("\n"));
742
+ var errors = markers.filter(function (m) { return m.severity === SEV_ERROR; });
743
+ assert.deepStrictEqual(errors, [],
744
+ "Expected no errors for annotated var used in same scope, got: " + JSON.stringify(errors));
745
+ },
746
+ },
747
+
748
+ {
749
+ name: "annotated_assign_compound_ops",
750
+ description: "All compound assignment operators work on annotated variables without undef errors",
751
+ run: function () {
752
+ var markers = d().check([
753
+ "def bar():",
754
+ " n: int = 10",
755
+ " n += 1",
756
+ " n -= 1",
757
+ " n *= 2",
758
+ " return n",
759
+ ].join("\n"));
760
+ var undef = markers.filter(function (m) { return m.message.indexOf("Undefined symbol") !== -1; });
761
+ assert.deepStrictEqual(undef, [],
762
+ "Expected no 'Undefined symbol' errors but got: " + JSON.stringify(undef));
763
+ },
764
+ },
765
+
644
766
  ];
645
767
 
646
768
  return TESTS;
@@ -24,6 +24,7 @@ var FILES = [
24
24
  "language-service-hover.js",
25
25
  "language-service-dts.js",
26
26
  "language-service-builtins.js",
27
+ "language-service-bundle.js",
27
28
  "web-repl.js",
28
29
  ];
29
30
 
package/tools/lint.js CHANGED
@@ -33,7 +33,7 @@ var BUILTINS = Object.create(null);
33
33
  ' eval undefined arguments abs max min enumerate pow callable reversed sum' +
34
34
  ' getattr isFinite setattr hasattr parseInt parseFloat options_object' +
35
35
  ' isNaN JSON Math list set list_wrap ρσ_modules require bool int bin' +
36
- ' float iter Error EvalError set_wrap RangeError ReferenceError SyntaxError' +
36
+ ' float iter Error EvalError set_wrap frozenset RangeError ReferenceError SyntaxError' +
37
37
  ' str TypeError URIError Exception AssertionError IndexError AttributeError KeyError' +
38
38
  ' ValueError ZeroDivisionError map hex filter zip dict dict_wrap UnicodeDecodeError HTMLCollection' +
39
39
  ' NodeList alert console Node Symbol NamedNodeMap ρσ_eslice ρσ_delslice Number' +
package/tools/self.js CHANGED
@@ -91,7 +91,7 @@ function check_for_changes(base_path, src_path, signatures) {
91
91
  }
92
92
 
93
93
 
94
- function compile(src_path, lib_path, sources, source_hash, profile) {
94
+ function compile(src_path, lib_path, sources, source_hash) {
95
95
  var file = path.join(src_path, 'compiler.pyj');
96
96
  var t1 = new Date().getTime();
97
97
  var RapydScript = require('./compiler').create_compiler();
@@ -116,15 +116,7 @@ function compile(src_path, lib_path, sources, source_hash, profile) {
116
116
  }
117
117
 
118
118
  try {
119
- if (profile) {
120
- profiler = require('v8-profiler');
121
- profiler.startProfiling();
122
- }
123
119
  toplevel = parse_file(raw, file);
124
- if (profile) {
125
- cpu_profile = profiler.stopProfiling();
126
- fs.writeFileSync('self.cpuprofile', JSON.stringify(cpu_profile), 'utf-8');
127
- }
128
120
  } catch (e) {
129
121
  if (!(e instanceof RapydScript.SyntaxError)) throw e;
130
122
  console.error(e.toString());