rapydscript-ng 0.7.23 → 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.
Files changed (114) hide show
  1. package/CHANGELOG.md +24 -0
  2. package/README.md +72 -26
  3. package/bin/package.json +1 -0
  4. package/bin/rapydscript +65 -62
  5. package/editor-plugins/nvim/rapydscript/README.md +184 -0
  6. package/editor-plugins/nvim/rapydscript/bin/rapydscript.so +0 -0
  7. package/editor-plugins/nvim/rapydscript/ftdetect/rapydscript.lua +10 -0
  8. package/editor-plugins/nvim/rapydscript/lua/rapydscript/health.lua +88 -0
  9. package/editor-plugins/nvim/rapydscript/lua/rapydscript/init.lua +279 -0
  10. package/local-agent.md +28 -0
  11. package/package.json +4 -5
  12. package/release/baselib-plain-pretty.js +448 -326
  13. package/release/baselib-plain-ugly.js +3 -3
  14. package/release/compiler.js +2528 -1672
  15. package/release/signatures.json +17 -17
  16. package/src/ast.pyj +73 -25
  17. package/src/baselib-builtins.pyj +2 -2
  18. package/src/baselib-containers.pyj +153 -151
  19. package/src/baselib-internal.pyj +3 -3
  20. package/src/baselib-str.pyj +5 -5
  21. package/src/lib/aes.pyj +1 -1
  22. package/src/lib/encodings.pyj +1 -1
  23. package/src/lib/pythonize.pyj +1 -1
  24. package/src/lib/re.pyj +2 -2
  25. package/src/lib/traceback.pyj +165 -28
  26. package/src/lib/uuid.pyj +1 -1
  27. package/src/output/codegen.pyj +10 -3
  28. package/src/output/functions.pyj +31 -40
  29. package/src/output/literals.pyj +11 -14
  30. package/src/output/loops.pyj +25 -87
  31. package/src/output/modules.pyj +5 -47
  32. package/src/output/statements.pyj +1 -1
  33. package/src/output/stream.pyj +58 -31
  34. package/src/parse.pyj +777 -427
  35. package/src/tokenizer.pyj +16 -4
  36. package/src/utils.pyj +1 -1
  37. package/test/_treeshake_mod.pyj +30 -0
  38. package/test/_treeshake_side_effect.pyj +9 -0
  39. package/test/ast_serialization.pyj +392 -0
  40. package/test/async.pyj +95 -0
  41. package/test/baselib.pyj +1 -2
  42. package/test/embedded_compiler.pyj +57 -0
  43. package/test/fmt.pyj +201 -0
  44. package/test/generic.pyj +7 -3
  45. package/test/imports.pyj +8 -6
  46. package/test/internationalization.pyj +38 -37
  47. package/test/lint.pyj +120 -115
  48. package/test/lsp.pyj +222 -0
  49. package/test/repl.pyj +70 -65
  50. package/test/sourcemaps.pyj +315 -0
  51. package/test/starargs.pyj +46 -39
  52. package/test/traceback.pyj +263 -0
  53. package/test/treeshaking.pyj +181 -0
  54. package/test/web_repl_export.pyj +88 -0
  55. package/tools/ast_serialize.mjs +557 -0
  56. package/tools/cli.mjs +510 -0
  57. package/tools/compile.mjs +201 -0
  58. package/tools/compiler.mjs +127 -0
  59. package/tools/{completer.js → completer.mjs} +6 -6
  60. package/tools/{embedded_compiler.js → embedded_compiler.mjs} +17 -13
  61. package/tools/export.js +109 -56
  62. package/tools/fmt.mjs +735 -0
  63. package/tools/{gettext.js → gettext.mjs} +46 -53
  64. package/tools/{ini.js → ini.mjs} +9 -10
  65. package/tools/{lint.js → lint.mjs} +107 -56
  66. package/tools/lsp.mjs +914 -0
  67. package/tools/lsp_protocol.mjs +259 -0
  68. package/tools/lsp_symbols.mjs +418 -0
  69. package/tools/{msgfmt.js → msgfmt.mjs} +18 -18
  70. package/tools/{repl.js → repl.mjs} +52 -41
  71. package/tools/{self.js → self.mjs} +56 -46
  72. package/tools/sourcemap.mjs +123 -0
  73. package/tools/test.mjs +152 -0
  74. package/tools/treeshake.mjs +400 -0
  75. package/tools/{utils.js → utils.mjs} +26 -23
  76. package/tools/{web_repl.js → web_repl.mjs} +15 -13
  77. package/tools/web_repl_export.mjs +93 -0
  78. package/tree-sitter/README.md +101 -0
  79. package/tree-sitter/grammar.js +992 -0
  80. package/tree-sitter/package.json +43 -0
  81. package/tree-sitter/queries/rapydscript/highlights.scm +232 -0
  82. package/tree-sitter/queries/rapydscript/indents.scm +64 -0
  83. package/tree-sitter/queries/rapydscript/injections.scm +14 -0
  84. package/tree-sitter/queries/rapydscript/locals.scm +108 -0
  85. package/tree-sitter/src/grammar.json +4976 -0
  86. package/tree-sitter/src/node-types.json +2901 -0
  87. package/tree-sitter/src/parser.c +196465 -0
  88. package/tree-sitter/src/scanner.c +294 -0
  89. package/tree-sitter/src/tree_sitter/alloc.h +54 -0
  90. package/tree-sitter/src/tree_sitter/array.h +330 -0
  91. package/tree-sitter/src/tree_sitter/parser.h +286 -0
  92. package/tree-sitter/test/corpus/chaining.txt +99 -0
  93. package/tree-sitter/test/corpus/classes.txt +147 -0
  94. package/tree-sitter/test/corpus/comprehensions.txt +155 -0
  95. package/tree-sitter/test/corpus/containers.txt +242 -0
  96. package/tree-sitter/test/corpus/control_flow.txt +361 -0
  97. package/tree-sitter/test/corpus/decorators.txt +64 -0
  98. package/tree-sitter/test/corpus/existential.txt +102 -0
  99. package/tree-sitter/test/corpus/functions.txt +293 -0
  100. package/tree-sitter/test/corpus/imports.txt +117 -0
  101. package/tree-sitter/test/corpus/literals.txt +135 -0
  102. package/tree-sitter/test/corpus/operators.txt +296 -0
  103. package/tree-sitter/test/corpus/statements.txt +189 -0
  104. package/tree-sitter/test/corpus/strings.txt +90 -0
  105. package/tree-sitter/test/corpus/subscripts.txt +227 -0
  106. package/tree-sitter/tree-sitter.json +36 -0
  107. package/web-repl/env.js +35 -23
  108. package/web-repl/main.js +8 -8
  109. package/bin/export +0 -75
  110. package/bin/web-repl-export +0 -102
  111. package/tools/cli.js +0 -523
  112. package/tools/compile.js +0 -184
  113. package/tools/compiler.js +0 -84
  114. package/tools/test.js +0 -110
package/CHANGELOG.md CHANGED
@@ -1,3 +1,27 @@
1
+ version 0.8.0
2
+ =======================
3
+
4
+ * Implement generation of source maps via the --source-map option
5
+ * Implement dead code removal via the --tree-shaking option
6
+ * Add support for async and await
7
+ * Implement a ``fmt`` command for code formatting and an ``lsp`` command for a Language Server
8
+ * Implement a tree sitter grammar so that robust syntax highlighting is potentially available in all editors
9
+ * Implement a neovim plugin that enables treesitter based syntax highlighting and LSP server for ``.pyj`` files
10
+ * Implement support for importing user modules in the embedded compiler
11
+ * Double the compilation speed, especially useful for large projects
12
+ * Drop support for ES5
13
+ * List objects are now unmodified JS Arrays for greater performance. Python
14
+ list methods continue to work.
15
+ * Change the cache format to cache only the AST and not final output so that
16
+ the cache can be re-used regardless of output and tree shaking options
17
+ * Make the compiler internally use async IO. This means the embedded compiler
18
+ functions are also async.
19
+
20
+ version 0.7.24
21
+ =======================
22
+
23
+ * Fix linter warning about end of line semi colons inside multiline string literals
24
+
1
25
  version 0.7.23
2
26
  =======================
3
27
 
package/README.md CHANGED
@@ -42,11 +42,13 @@ backwards compatible) features. For more on the forking, [see the bottom of this
42
42
  - [Method Binding](#method-binding)
43
43
  - [Iterators](#iterators)
44
44
  - [Generators](#generators)
45
+ - [Asnyc and Await](#async-and-await)
45
46
  - [Modules](#modules)
46
47
  - [Exception Handling](#exception-handling)
47
48
  - [Scope Control](#scope-control)
48
49
  - [Available Libraries](#available-libraries)
49
50
  - [Linter](#linter)
51
+ - [Language Server (LSP)](#language-server-lsp)
50
52
  - [Making RapydScript even more pythonic](#making-rapydscript-even-more-pythonic)
51
53
  - [Advanced Usage Topics](#advanced-usage-topics)
52
54
  - [Browser Compatibility](#browser-compatibility)
@@ -1254,9 +1256,12 @@ syntax.
1254
1256
  Generators create JavaScript iterator objects. For differences between python
1255
1257
  and JavaScript iterators, see the section on iterators above.
1256
1258
 
1257
- Currently, generators are down-converted to ES 5 switch statements. In the
1258
- future, when ES 6 support is widespread, they will be converted to native
1259
- JavaScript ES 6 generators.
1259
+ Async and Await
1260
+ ------------------
1261
+
1262
+ Python's ``async`` and ``await`` keywords are supported, but note that async
1263
+ functions return JavaScript Promise objects not co-routines. This is so that
1264
+ they interoperate with the JS ecosystem and browser APIs in particular.
1260
1265
 
1261
1266
  Modules
1262
1267
  -------
@@ -1427,6 +1432,41 @@ that the linter will not raise undefined errors for. You can turn off
1427
1432
  individual checks that you do not find useful. See ``rapydscript lint -h`` for
1428
1433
  details.
1429
1434
 
1435
+ Language Server (LSP)
1436
+ -----------------------
1437
+
1438
+ RapydScript ships with a Language Server Protocol server so that editors can
1439
+ offer rich language support for ``.pyj`` files. Start it with:
1440
+
1441
+ rapydscript lsp
1442
+
1443
+ The server talks the standard LSP JSON-RPC protocol over stdin/stdout, so it is
1444
+ meant to be launched by an editor/LSP client rather than run interactively. It
1445
+ provides:
1446
+
1447
+ - **Code completion** — in-scope symbols, builtins and keywords, plus member
1448
+ completion for imported modules (``mod.<complete>``).
1449
+ - **Diagnostics** — the same checks as the ``lint`` sub-command, plus warnings
1450
+ for imports that cannot be resolved in the import path. The parser recovers
1451
+ from syntax errors, so a file that is being edited is still analysed (and
1452
+ multiple errors can be reported at once).
1453
+ - **Hover** — the kind, origin and docstring of the symbol under the cursor.
1454
+ - **Code actions** — quick fixes such as removing an unused import/local,
1455
+ silencing a check with a ``# noqa`` comment, and formatting the document.
1456
+ - **Document formatting** — the same formatter as the ``fmt`` sub-command,
1457
+ controlled by ``--line-length`` and ``--preferred-quote``.
1458
+ - **Go to definition** — including jumping into imported modules.
1459
+ - **Find all references** — resolved across files through the import graph.
1460
+ - **Rename** — renames a symbol everywhere it is used across the workspace.
1461
+
1462
+ Imports are resolved with the same ``--import-path`` (``-p``) option as the
1463
+ ``compile`` sub-command, so point it at your project's import directories. For
1464
+ example:
1465
+
1466
+ rapydscript lsp --import-path src:vendor --line-length 100
1467
+
1468
+ See ``rapydscript lsp -h`` for the full list of options.
1469
+
1430
1470
  Making RapydScript even more pythonic
1431
1471
  ---------------------------------------
1432
1472
 
@@ -1454,11 +1494,9 @@ Advanced Usage Topics
1454
1494
 
1455
1495
  #### Browser Compatibility
1456
1496
 
1457
- RapydScript compiles your code such that it will work on browsers that are
1458
- compatible with the ES 5 JavaScript standard. The compiler has a
1459
- ``--js-version`` option that can also be used to output ES 6 only code. This
1460
- code is smaller and faster than the ES 5 version, but is not as widely
1461
- compatible.
1497
+ RapydScript compiles your code to ES 6 compatible JavaScript. Any modern
1498
+ browser or Node.js version that supports ES 6 will run the output. The
1499
+ ``--js-version`` option only accepts ``6``.
1462
1500
 
1463
1501
  #### Tabs vs Spaces
1464
1502
 
@@ -1491,9 +1529,9 @@ To do so, simply include the [embeddable rapydscript compiler](https://sw.kovidg
1491
1529
  in your page, and use it to compile arbitrary RapydScript code.
1492
1530
 
1493
1531
  You create the compiler by calling: `RapydScript.create_embedded_compiler()` and compile
1494
- code with `compiler.compile(code)`. You can execute the resulting JavaScript
1495
- using the standard `eval()` function. See the sample
1496
- HTML below for an example.
1532
+ code with `await compiler.compile(code)`. Both calls return Promises, so you must use
1533
+ `async`/`await` (or `.then()`) to get the results. You can execute the resulting JavaScript
1534
+ using the standard `eval()` function. See the sample HTML below for an example.
1497
1535
 
1498
1536
  ```html
1499
1537
  <!DOCTYPE html>
@@ -1503,9 +1541,9 @@ HTML below for an example.
1503
1541
  <title>Test embedded RapydScript</title>
1504
1542
  <script charset="UTF-8" src="https://sw.kovidgoyal.net/rapydscript/repl/rapydscript.js"></script>
1505
1543
  <script>
1506
- var compiler = RapydScript.create_embedded_compiler();
1507
- var js = compiler.compile("def hello_world():\n a='RapydScript is cool!'\n print(a)\n alert(a)");
1508
- window.onload = function() {
1544
+ window.onload = async function() {
1545
+ var compiler = await RapydScript.create_embedded_compiler();
1546
+ var js = await compiler.compile("def hello_world():\n a='RapydScript is cool!'\n print(a)\n alert(a)");
1509
1547
  document.body.textContent = js;
1510
1548
  eval(js);
1511
1549
  eval('hello_world()');
@@ -1519,20 +1557,28 @@ window.onload = function() {
1519
1557
  There are a couple of caveats when using the embedded compiler:
1520
1558
 
1521
1559
  * It only works when run in a modern browser (one that supports ES6) so no
1522
- Internet Explorer. You can have it work in an ES 5 runtime by passing
1523
- an option to the compile() method, like this:
1524
- ```
1525
- compiler.compile(code, {js_version:5})
1526
- ```
1527
- Note that doing this means that you cannot use generators and the
1528
- yield keyword in your RapydScript code.
1560
+ Internet Explorer.
1529
1561
 
1530
- * Importing of modules only works with the standard library modules. There is
1531
- currently no way to make your own modules importable.
1562
+ * To implement importing of modules you need to create a virtual file system
1563
+ like this, before calling ``create_embedded_compiler()``:
1532
1564
 
1533
- * To generate the embedded compiler yourself (rapydscript.js) from a source
1534
- checkout of rapydscript, follow the instructions above for installing from
1535
- source, then run `bin/web-repl-export /path/to/export/directory`
1565
+ ```
1566
+ RapydScript.virtual_file_system = {
1567
+ read_file: my_read_file,
1568
+ write_file: my_write_file,
1569
+ stat_file: my_stat_file
1570
+ }
1571
+ ```
1572
+ These functions should have the same signature and semantics as the
1573
+ ``fs.promises.readFile, fs.promises.writeFile and fs.promises.statFile``
1574
+ functions from Node. Currently only the ``mtimeMs`` field from the stat
1575
+ result is needed. They will be passed file paths starting with ``__vfs__/``
1576
+ and should read/write/stat them as needed.
1577
+
1578
+ * To generate the embedded compiler yourself (rapydscript.js)
1579
+ run `rapydscript web-repl-export /path/to/export/directory`.
1580
+ Then you can simply open the ``index.html`` file in that directory in a browser
1581
+ to see the web REPL in action.
1536
1582
 
1537
1583
  Internationalization
1538
1584
  -------------------------
@@ -0,0 +1 @@
1
+ {"type": "module"}
package/bin/rapydscript CHANGED
@@ -1,70 +1,73 @@
1
- #!/usr/bin/env node
1
+ #!/usr/bin/env node
2
2
  // vim:ft=javascript:ts=4:et
3
3
 
4
- "use strict";
4
+ import { fileURLToPath } from 'url';
5
+ import path from 'path';
6
+ import { spawn } from 'child_process';
7
+ import * as utils from '../tools/utils.mjs';
8
+ import { create_compiler } from '../tools/compiler.mjs';
5
9
 
6
- function load(mod) {
7
- return require('../tools/' + mod);
8
- }
10
+ const __filename = fileURLToPath(import.meta.url);
11
+
12
+ var start_time = new Date().getTime();
13
+
14
+ var { argv } = await import('../tools/cli.mjs');
15
+
16
+ var base_path = path.normalize(path.join(path.dirname(__filename), ".."));
17
+ var src_path = path.join(base_path, 'src');
18
+ var lib_path = path.join(base_path, 'dev');
19
+ if (!(await utils.path_exists(path.join(lib_path, 'compiler.js')))) lib_path = path.join(base_path, 'release');
9
20
 
10
- var utils = load('utils');
21
+ // Create compiler instance once, then expose it synchronously via the global
22
+ // so bundled modules (repl, gettext, etc.) can call create_rapydscript_compiler() synchronously.
23
+ const _compiler_instance = await create_compiler();
24
+ globalThis.create_rapydscript_compiler = () => _compiler_instance;
11
25
 
12
- // We need ES 6 generators so relaunch with the --harmony flag
13
- if (!utils.generators_available()) {
14
- if (process.execArgv.indexOf('--harmony') != -1) {
15
- console.error('RapydScript needs ES 6 generators, update your version of nodejs');
16
- process.exit(1);
26
+ if (argv.mode === 'self') {
27
+ if (argv.files.length > 0) {
28
+ console.error("WARN: Ignoring input files since --self was passed");
17
29
  }
18
- var args = ['--harmony', module.filename].concat(process.argv.slice(2));
19
- require('child_process').spawn(process.execPath, args, {stdio:'inherit'}).on('exit', function(code, signal) {
20
- process.exit(code);
21
- });
22
- } else {
23
-
24
- var start_time = new Date().getTime();
25
- var path = require('path');
26
-
27
- var argv = load('cli').argv;
28
-
29
- var base_path = path.normalize(path.join(path.dirname(module.filename), ".."));
30
- var src_path = path.join(base_path, 'src');
31
- var lib_path = path.join(base_path, 'dev');
32
- if (!utils.path_exists(path.join(lib_path, 'compiler.js'))) lib_path = path.join(base_path, 'release');
33
-
34
- if (argv.mode === 'self') {
35
- if (argv.files.length > 0) {
36
- console.error("WARN: Ignoring input files since --self was passed");
37
- }
38
- load('self')(base_path, src_path, lib_path, argv.complete, argv.profile);
39
- if (argv.test) {
40
- console.log('\nRunning test suite...\n');
41
- argv.files = []; // Ensure all tests are run
42
- load('test')(argv, base_path, src_path, path.join(base_path, 'dev'));
43
- }
44
- process.exit(0);
45
- } else
46
-
47
- if (argv.mode === 'test') {
48
- load('test')(argv, base_path, src_path, lib_path);
49
- } else
50
-
51
- if (argv.mode === 'lint') {
52
- load('lint').cli(argv, base_path, src_path, lib_path);
53
- } else
54
-
55
- if (argv.mode === 'repl') {
56
- load('repl')({'lib_path':lib_path, 'imp_path':path.join(src_path, 'lib'), show_js:!argv.no_js});
57
- } else
58
-
59
- if (argv.mode === 'gettext') {
60
- load('gettext').cli(argv, base_path, src_path, lib_path);
61
- } else
62
-
63
- if (argv.mode === 'msgfmt') {
64
- load('msgfmt').cli(argv, base_path, src_path, lib_path);
65
- } else
66
-
67
- {
68
- load('compile')(start_time, argv, base_path, src_path, lib_path);
30
+ await (await import('../tools/self.mjs')).default(base_path, src_path, lib_path, argv.complete, argv.profile);
31
+ if (argv.test) {
32
+ console.log('\nRunning test suite...\n');
33
+ argv.files = []; // Ensure all tests are run
34
+ await (await import('../tools/test.mjs')).default(argv, base_path, src_path, path.join(base_path, 'dev'));
69
35
  }
36
+ process.exit(0);
37
+ } else
38
+
39
+ if (argv.mode === 'test') {
40
+ await (await import('../tools/test.mjs')).default(argv, base_path, src_path, lib_path);
41
+ } else
42
+
43
+ if (argv.mode === 'lint') {
44
+ await (await import('../tools/lint.mjs')).cli(argv, base_path, src_path, lib_path);
45
+ } else
46
+
47
+ if (argv.mode === 'fmt') {
48
+ await (await import('../tools/fmt.mjs')).cli(argv, base_path, src_path, lib_path);
49
+ } else
50
+
51
+ if (argv.mode === 'lsp') {
52
+ await (await import('../tools/lsp.mjs')).cli(argv, base_path, src_path, lib_path);
53
+ } else
54
+
55
+ if (argv.mode === 'repl') {
56
+ await (await import('../tools/repl.mjs')).default({'lib_path':lib_path, 'imp_path':path.join(src_path, 'lib'), show_js:!argv.no_js});
57
+ } else
58
+
59
+ if (argv.mode === 'gettext') {
60
+ await (await import('../tools/gettext.mjs')).cli(argv, base_path, src_path, lib_path);
61
+ } else
62
+
63
+ if (argv.mode === 'msgfmt') {
64
+ (await import('../tools/msgfmt.mjs')).cli(argv, base_path, src_path, lib_path);
65
+ } else
66
+
67
+ if (argv.mode === 'web-repl-export') {
68
+ await (await import('../tools/web_repl_export.mjs')).default(base_path, lib_path, argv);
69
+ } else
70
+
71
+ {
72
+ await (await import('../tools/compile.mjs')).default(start_time, argv, base_path, src_path, lib_path);
70
73
  }
@@ -0,0 +1,184 @@
1
+ # rapydscript.nvim
2
+
3
+ Neovim plugin that wires up the RapydScript LSP server (`rapydscript lsp`) as a
4
+ native Neovim LSP client for `.pyj` files.
5
+
6
+ ## Features
7
+
8
+ Provided by the LSP server — no extra plugins required:
9
+
10
+ - **Completions** — in-scope symbols, builtins, keywords, and member completion (`mod.<TAB>`)
11
+ - **Diagnostics** — lint checks and unresolved import warnings, updated as you type
12
+ - **Hover** (`K`) — kind, origin, and docstring of the symbol under the cursor
13
+ - **Go to definition** (`gd`) — including jumping into imported modules
14
+ - **Find references** (`gr`) — resolved across files through the import graph
15
+ - **Rename** (`<leader>rn`) — renames everywhere across the workspace
16
+ - **Code actions** (`<leader>ca`) — remove unused import/local, add `# noqa`, format document
17
+ - **Document formatting** (`<leader>f`) — same as `rapydscript fmt`
18
+ - **Document symbols** — outline view via any symbols picker
19
+
20
+ ## Requirements
21
+
22
+ - Neovim ≥ 0.10
23
+ - `rapydscript` on your `$PATH` (install with `npm install -g rapydscript-ng`), or the
24
+ plugin directory must be inside a RapydScript repository checkout (the repo's own
25
+ `bin/rapydscript` is used automatically as a fallback)
26
+ - **Syntax highlighting only**: a C compiler on your `$PATH` is required to compile the
27
+ tree-sitter parser the first time the plugin loads. On Linux/macOS any `cc`-compatible
28
+ compiler works (gcc, clang, etc.). On Windows, `cl` (MSVC) or `clang-cl` must be
29
+ available — run Neovim from a Visual Studio Developer Command Prompt, or add the
30
+ compiler to your `PATH` manually. LSP features work regardless of whether compilation
31
+ succeeds.
32
+
33
+ ## Installation
34
+
35
+ ### lazy.nvim
36
+
37
+ ```lua
38
+ {
39
+ dir = '/path/to/editor-plugins/nvim/rapydscript',
40
+ ft = 'rapydscript',
41
+ opts = {},
42
+ }
43
+ ```
44
+
45
+ **Example with options:**
46
+
47
+ ```lua
48
+ {
49
+ dir = "/path/to/editor-plugins/nvim/rapydscript",
50
+ ft = "rapydscript",
51
+ opts = {
52
+ line_length = 100,
53
+ preferred_quote = "double",
54
+ },
55
+ }
56
+ ```
57
+
58
+ ### Manual (no plugin manager)
59
+
60
+ Add the plugin directory to your runtime path and call `setup()`:
61
+
62
+ ```lua
63
+ -- in ~/.config/nvim/init.lua
64
+ vim.opt.rtp:prepend("/path/to/rapydscript/nvim-lsp-plugin")
65
+ require("rapydscript").setup()
66
+ ```
67
+
68
+ ## Configuration reference
69
+
70
+ | Key | Type | Default | Description |
71
+ |-----|------|---------|-------------|
72
+ | `cmd` | `string[]` | `{"rapydscript","lsp"}` | Command used to start the server |
73
+ | `import_path_patterns` | `string[]` | `{".", "src", "src/pyj"}` | Glob patterns relative to the project root; matching directories that contain `.pyj` files are passed automatically as `--import-path` |
74
+ | `line_length` | `number\|nil` | `nil` | Max line length for the formatter, passed as `--line-length` |
75
+ | `preferred_quote` | `string\|nil` | `nil` | `"single"` or `"double"`, passed as `--preferred-quote` |
76
+ | `filetypes` | `string[]` | `{"rapydscript"}` | Filetypes that trigger server attachment |
77
+ | `root_markers` | `string[]` | `{".git","package.json","rapydscript.json"}` | Files/dirs used to detect the project root |
78
+
79
+ ### Automatic import path detection
80
+
81
+ When a project root is found (via `root_markers`), the plugin expands each pattern in
82
+ `import_path_patterns` as a glob relative to that root. Any matching directory that
83
+ contains at least one `.pyj` file is added to the server's import search path
84
+ automatically — no manual configuration needed for standard project layouts.
85
+
86
+ If no project root is detected the setting has no effect.
87
+
88
+ To add custom search locations alongside the defaults:
89
+
90
+ ```lua
91
+ opts = {
92
+ import_path_patterns = { ".", "src", "src/pyj", "vendor/pyj" },
93
+ }
94
+ ```
95
+
96
+ To disable auto-detection entirely, pass an empty list:
97
+
98
+ ```lua
99
+ opts = {
100
+ import_path_patterns = {},
101
+ }
102
+ ```
103
+
104
+ ## Changing settings at runtime
105
+
106
+ The server accepts live setting updates via the LSP
107
+ `workspace/didChangeConfiguration` notification — no restart needed. Call
108
+ `require("rapydscript").update_settings(opts)` with any subset of the two
109
+ settings keys:
110
+
111
+ ```lua
112
+ require("rapydscript").update_settings({
113
+ line_length = 100, -- new max line length for the formatter
114
+ preferred_quote = "double", -- "single" or "double"
115
+ })
116
+ ```
117
+
118
+ Only the keys you pass are updated; the rest keep their current values.
119
+
120
+ ### Neovim user commands
121
+
122
+ A convenient way to expose this as editor commands — add to your config after
123
+ `setup()`:
124
+
125
+ ```lua
126
+ -- :RapydLineLength 100
127
+ vim.api.nvim_create_user_command("RapydLineLength", function(args)
128
+ require("rapydscript").update_settings({ line_length = tonumber(args.args) })
129
+ end, { nargs = 1, desc = "Set RapydScript LSP line length" })
130
+
131
+ -- :RapydQuote double
132
+ vim.api.nvim_create_user_command("RapydQuote", function(args)
133
+ require("rapydscript").update_settings({ preferred_quote = args.args })
134
+ end, { nargs = 1, desc = "Set RapydScript LSP preferred quote style" })
135
+ ```
136
+
137
+ ### In lazy.nvim config
138
+
139
+ If you want the commands available automatically, put them in the `config`
140
+ function:
141
+
142
+ ```lua
143
+ {
144
+ dir = "/path/to/rapydscript/nvim-lsp-plugin",
145
+ ft = "rapydscript",
146
+ opts = { line_length = 100 },
147
+ config = function(_, opts)
148
+ require("rapydscript").setup(opts)
149
+
150
+ vim.api.nvim_create_user_command("RapydLineLength", function(args)
151
+ require("rapydscript").update_settings({ line_length = tonumber(args.args) })
152
+ end, { nargs = 1 })
153
+
154
+ vim.api.nvim_create_user_command("RapydQuote", function(args)
155
+ require("rapydscript").update_settings({ preferred_quote = args.args })
156
+ end, { nargs = 1 })
157
+ end,
158
+ }
159
+ ```
160
+
161
+ Changes to `line_length` and `preferred_quote` take effect on the next format operation.
162
+
163
+ ## Keymaps
164
+
165
+ The plugin does not define any keymaps — it relies on the standard Neovim LSP
166
+ keymaps that are set up by `vim.lsp.buf.*`. A minimal set to add to your LSP
167
+ `on_attach` or a `LspAttach` autocmd:
168
+
169
+ ```lua
170
+ vim.api.nvim_create_autocmd("LspAttach", {
171
+ callback = function(ev)
172
+ local buf = ev.buf
173
+ local map = function(mode, lhs, rhs)
174
+ vim.keymap.set(mode, lhs, rhs, { buffer = buf })
175
+ end
176
+ map("n", "K", vim.lsp.buf.hover)
177
+ map("n", "gd", vim.lsp.buf.definition)
178
+ map("n", "gr", vim.lsp.buf.references)
179
+ map("n", "<leader>rn", vim.lsp.buf.rename)
180
+ map("n", "<leader>ca", vim.lsp.buf.code_action)
181
+ map("n", "<leader>f", function() vim.lsp.buf.format({ async = true }) end)
182
+ end,
183
+ })
184
+ ```
@@ -0,0 +1,10 @@
1
+ #! /usr/bin/env lua
2
+ --
3
+ -- rapydscript.lua
4
+ -- Copyright (C) 2026 Kovid Goyal <kovid at kovidgoyal.net>
5
+ --
6
+ -- Distributed under terms of the MIT license.
7
+ --
8
+
9
+
10
+ vim.filetype.add({ extension = { pyj = 'rapydscript' } })
@@ -0,0 +1,88 @@
1
+ local M = {}
2
+
3
+ local function ver_str(v)
4
+ return v and table.concat(v, ".") or "unknown"
5
+ end
6
+
7
+ function M.check()
8
+ local h = vim.health
9
+ local rs_ok, rs = pcall(require, "rapydscript")
10
+ if not rs_ok then
11
+ h.start("rapydscript")
12
+ h.error("Failed to load plugin: " .. tostring(rs))
13
+ return
14
+ end
15
+
16
+ -- Binary -------------------------------------------------------------------
17
+ h.start("rapydscript binary")
18
+ local info = rs._binary_info
19
+ local sys = info.system
20
+ local repo = info.repo
21
+
22
+ if sys then
23
+ if sys.rejected then
24
+ h.error(string.format(
25
+ "System binary rejected: %s (v%s < minimum 0.8.0)",
26
+ sys.path, ver_str(sys.version)
27
+ ))
28
+ elseif sys.version then
29
+ h.ok(string.format("System binary: %s (v%s)", sys.path, ver_str(sys.version)))
30
+ else
31
+ h.warn(string.format("System binary: %s (version unknown)", sys.path))
32
+ end
33
+ else
34
+ h.warn("rapydscript not found on PATH")
35
+ end
36
+
37
+ if repo then
38
+ h.ok(string.format("Repo binary: %s (v%s)", repo.path, ver_str(repo.version)))
39
+ else
40
+ h.info("No repo binary present")
41
+ end
42
+
43
+ if not sys and not repo then
44
+ h.error("No rapydscript binary found — LSP will not start")
45
+ end
46
+
47
+ -- Tree-sitter --------------------------------------------------------------
48
+ h.start("rapydscript tree-sitter")
49
+ if rs._ts_so then
50
+ h.ok("Parser compiled: " .. rs._ts_so)
51
+ else
52
+ h.warn("Tree-sitter parser not compiled (syntax highlighting unavailable)")
53
+ end
54
+
55
+ -- LSP clients --------------------------------------------------------------
56
+ h.start("rapydscript LSP")
57
+ local clients = vim.lsp.get_clients({ name = "rapydscript" })
58
+ if #clients == 0 then
59
+ h.warn("No active rapydscript LSP client")
60
+ else
61
+ for _, client in ipairs(clients) do
62
+ local cmd_str = type(client.config.cmd) == "table"
63
+ and table.concat(client.config.cmd, " ")
64
+ or tostring(client.config.cmd)
65
+ h.ok(string.format("Client #%d root: %s", client.id, client.config.root_dir or "?"))
66
+ h.info("cmd: " .. cmd_str)
67
+ end
68
+ end
69
+
70
+ -- Settings -----------------------------------------------------------------
71
+ h.start("rapydscript settings")
72
+ local opts = rs._active_opts
73
+ if not opts then
74
+ h.warn("setup() has not been called — showing defaults")
75
+ opts = rs.defaults
76
+ else
77
+ h.ok("setup() called")
78
+ end
79
+
80
+ h.info("cmd: " .. table.concat(opts.cmd, " "))
81
+ h.info("line_length: " .. tostring(opts.line_length))
82
+ h.info("preferred_quote: " .. tostring(opts.preferred_quote))
83
+ h.info("filetypes: " .. table.concat(opts.filetypes, ", "))
84
+ h.info("root_markers: " .. table.concat(opts.root_markers, ", "))
85
+ h.info("import_path_patterns: " .. table.concat(opts.import_path_patterns, ", "))
86
+ end
87
+
88
+ return M