nx_lsp.nx
buildroot/runtime/nx_lsp.nx
about
============================================================================================
nx_lsp.nx -- LN11: THE NISHILANG LANGUAGE SERVER. LSP 3.17 over stdio, JSON-RPC 2.0,
Content-Length framed.
THE ARCHITECTURE BAR (lang.plan, rust-analyzer 2020 architecture note): the front end is ONE
query surface serving the IDE, the CLI and agents -- a language server that ships a SECOND parser
is a second source of truth that drifts from the compiler, and every drift is a diagnostic the
editor shows and the build does not (or worse, the reverse). So this organ contains NO lexer, NO
parser and NO diagnostic text of its own. It calls, verbatim:
nx_import.nx expand_imports / expand_ctx_enable_linemap (the import closure + LineMap)
nx_tokenizer.nx lex_source (the tokens)
nx_parse.nx parse_module / nx_diag_set_source / nx_diag_set_linemap
(the caret + did-you-mean
diagnostics, live since 08-05)
Every message a user sees in their editor is a byte the compiler wrote.
WHY A FORK PER ANALYSIS. The compiler's diagnostic sites sys_exit(2) by design -- recovery stops
at NX_DIAG_MAX_ERRS and every parser DESYNC site is fatal on purpose (past a desync each further
diagnostic points at innocent code). Calling parse_module in-process would therefore let one bad
keystroke KILL THE LANGUAGE SERVER. The analysis runs in a forked child with fd 2 redirected to a
per-document artifact; the parent survives sys_exit, SIGSEGV and SIGALRM alike and reports WHICH.
This is the same isolation rust-analyzer gives flycheck, reached for the same reason.
WHY A SHADOW FILE. expand_imports resolves an import by walking UP from the IMPORTING file's own
directory. An unsaved buffer analysed from anywhere else would resolve a DIFFERENT import set --
the measured-a-different-subject defect, and it would show up as a flood of phantom "I do not know
the name" errors for every symbol the real build resolves fine. So the dirty buffer is written to
<document-directory>/<shadow-dir-name>/<basename>: exactly one hop below the document, so the
resolver walks the identical chain. When the directory cannot be created the server analyses the
SAVED file and SAYS SO over window/logMessage rather than silently reporting stale diagnostics.
TRUTHFUL CAPABILITIES. initialize advertises textDocumentSync=Full, definitionProvider and
hoverProvider and NOTHING ELSE. A server that advertises a capability it does not serve is the
stub-carrying-the-contract-name defect wearing an editor's clothes: the client greys out its own
working fallback and the user gets silence instead of a feature.
POSITIONS ARE UTF-16 CODE UNITS, as LSP requires by default. The compiler counts BYTES, so every
column crosses lsp_u16_of_bytes / lsp_bytes_of_u16. Getting this wrong is invisible in ASCII and
silently off-by-N in every file with an accent in a comment.
dependencies 10 imports · 0 importers
imports: nx_syscalls.nxnx_types.nxnx_lex_kinds.nxnx_ir.nxnx_linemap.nxnx_tokenizer.nxnx_parse.nxnx_import.nxnx_json_lib.nxnx_lineconf_lib.nx
imported by: nobody (leaf or entry point)
call flow from main pre-order; caps 40 nodes / depth 6 declared; ↻ = already shown
structs
| none |
consts
| 71 | const LSP_BS: i64 = 8 |
| 72 | const LSP_TAB: i64 = 9 |
| 73 | const LSP_LF: i64 = 10 |
| 74 | const LSP_FF: i64 = 12 |
| 75 | const LSP_CR: i64 = 13 |
| 76 | const LSP_SPACE: i64 = 32 |
| 77 | const LSP_QUOTE: i64 = 34 |
| 78 | const LSP_PCT: i64 = 37 |
| 79 | const LSP_COMMA: i64 = 44 |
| 80 | const LSP_MINUS: i64 = 45 |
| 81 | const LSP_DOT: i64 = 46 |
| 82 | const LSP_SLASH: i64 = 47 |
| 83 | const LSP_D0: i64 = 48 |
| 84 | const LSP_D9: i64 = 57 |
| 85 | const LSP_COLON: i64 = 58 |
| 86 | const LSP_UA: i64 = 65 |
| 87 | const LSP_UF: i64 = 70 |
| 88 | const LSP_UZ: i64 = 90 |
| 89 | const LSP_BSLASH: i64 = 92 |
| 90 | const LSP_CARET: i64 = 94 |
| 91 | const LSP_USCORE: i64 = 95 |
| 92 | const LSP_LA: i64 = 97 |
| 93 | const LSP_LF_HEX: i64 = 102 |
| 94 | const LSP_LZ: i64 = 122 |
| 95 | const LSP_RBRACE: i64 = 125 |
| 96 | const LSP_TILDE: i64 = 126 |
| 98 | const LSP_B10: i64 = 10 |
| 99 | const LSP_B16: i64 = 16 |
| 100 | const LSP_WORD: i64 = 8 |
| 101 | const LSP_NUMBUF: i64 = 32 |
| 104 | const LSP_U8_2: i64 = 192 |
| 105 | const LSP_U8_3: i64 = 224 |
| 106 | const LSP_U8_4: i64 = 240 |
| 109 | const LSP_SYNC_FULL: i64 = 1 |
| 110 | const LSP_SEV_ERROR: i64 = 1 |
| 111 | const LSP_SEV_WARN: i64 = 2 |
| 112 | const LSP_MSG_ERROR: i64 = 1 |
| 113 | const LSP_MSG_WARN: i64 = 2 |
| 114 | const LSP_MSG_INFO: i64 = 3 |
| 115 | const LSP_RPC_METHOD_NOT_FOUND: i64 = 0 - 32601 |
| 117 | const LSP_EXIT_OK: i64 = 0 |
| 118 | const LSP_EXIT_NO_SHUTDOWN: i64 = 1 |
| 119 | const LSP_EXIT_USAGE: i64 = 3 |
| 120 | const LSP_EXIT_CONF: i64 = 4 |
| 127 | const LSP_JSON_ESC_MAX: i64 = 6 |
| 128 | const LSP_SCAFFOLD_BYTES: i64 = 2048 |
| 129 | const LSP_PER_DIAG_BYTES: i64 = 256 |
| 130 | const LSP_URI_ESC_MAX: i64 = 3 |
| 132 | const LSP_OUTHDR_BYTES: i64 = 64 |
| 133 | const LSP_ID_BYTES: i64 = 256 |
| 134 | const LSP_SYMROW_BYTES: i64 = 192 |
| 135 | const LSP_KINDBUF: i64 = 32 |
| 136 | const LSP_NAMEBUF: i64 = 128 |
functions
| 164 | func lsp_slen(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } return n } |
| 169 | func lsp_dec_at(b: *u8, s: i64, e: i64) -> i64; called by 1: lsp_sym_find |
| 170 | func lsp_slen_bounded(b: *u8, cap: i64) -> i64; |
| 172 | func lsp_streq(a: *u8, b: *u8) -> i64 |
| 183 | func lsp_write_all(fd: i64, buf: *u8, n: i64) -> i64 called by 7: lsp_loglsp_lognumlsp_write_filelsp_child_analyzelsp_sendlsp_mode_diag+1 calls 1: sys_write |
| 195 | func lsp_log(s: *u8) -> i64 { lsp_write_all(2, s, lsp_slen(s)); return 0 } called by 10: lsp_conf_intlsp_conf_strlsp_analyzelsp_sendlsp_build_diagslsp_read_header+4 calls 2: lsp_write_alllsp_slen |
| 197 | func lsp_lognum(v: i64) -> i64 |
| 215 | func lsp_put(d: *u8, o: i64, cap: i64, c: i64) -> i64 |
| 221 | func lsp_str_into(d: *u8, o: i64, cap: i64, s: *u8) -> i64 called by 13: lsp_jstr_intolsp_scratch_pathlsp_shadow_makelsp_child_analyzelsp_sendlsp_window_log+7 calls 1: lsp_put |
| 228 | func lsp_bytes_into(d: *u8, o: i64, cap: i64, s: *u8, n: i64) -> i64 calls 1: lsp_put |
| 235 | func lsp_num_into(d: *u8, o: i64, cap: i64, v: i64) -> i64 |
| 248 | func lsp_hexdig(v: i64) -> i64 |
| 255 | func lsp_jstr_into(d: *u8, o: i64, cap: i64, s: *u8, n: i64) -> i64 |
| 277 | func lsp_jstrz_into(d: *u8, o: i64, cap: i64, s: *u8) -> i64 |
| 285 | func lsp_conf_int(key: *u8) -> i64 |
| 296 | func lsp_conf_str(key: *u8) -> *u8 |
| 308 | func lsp_cfg_load() -> i64 |
| 334 | func lsp_line_start(t: *u8, n: i64, line0: i64) -> i64 |
| 352 | func lsp_line_end(t: *u8, n: i64, start: i64) -> i64 |
| 364 | func lsp_u16_of_bytes(lp: *u8, n: i64, byte_off: i64) -> i64 |
| 385 | func lsp_bytes_of_u16(lp: *u8, n: i64, u16col: i64) -> i64 called by 1: lsp_resolve |
| 406 | func lsp_is_word(c: i64) -> i64 called by 1: lsp_word_at |
| 415 | func lsp_word_at(t: *u8, n: i64, off: i64, s_out: *i64, e_out: *i64) -> i64 |
| 450 | func lsp_hexval(c: i64) -> i64 |
| 459 | func lsp_uri_to_path(uri: *u8, out: *u8, cap: i64) -> i64 |
| 488 | func lsp_uri_unreserved(c: i64) -> i64 |
| 499 | func lsp_path_to_uri(path: *u8, out: *u8, cap: i64) -> i64 |
| 526 | func lsp_basename(path: *u8) -> *u8 |
| 541 | func lsp_uri_slot(i: i64) -> *u8 { return ((g_doc_uri as i64) + i * g_uri_bytes) as *u8 } |
| 542 | func lsp_path_slot(i: i64) -> *u8 { return ((g_doc_path as i64) + i * SYS_PATH_MAX) as *u8 } |
| 543 | func lsp_shadow_slot(i: i64) -> *u8 { return ((g_doc_shadow as i64) + i * SYS_PATH_MAX) as *u8 } |
| 545 | func lsp_doc_find(uri: *u8) -> i64 |
| 559 | func lsp_doc_put(uri: *u8, txt: *u8, n: i64) -> i64 |
| 589 | func lsp_scratch_path(slot: i64, ext: *u8, out: *u8, cap: i64) -> i64 called by 1: lsp_analyze calls 17: sys_mmapexpand_ctx_newexpand_ctx_enable_linemapexpand_importslsp_str_intolsp_num_into+11 |
| 598 | func lsp_write_file(path: *u8, buf: *u8, n: i64) -> i64 |
| 607 | func lsp_truncate_file(path: *u8) -> i64 |
| 615 | func lsp_shadow_make(path: *u8, txt: *u8, n: i64, out: *u8, cap: i64) -> i64 |
| 638 | func lsp_child_analyze(path: *u8, symp: *u8) -> i64 |
| 757 | func lsp_analyze(slot: i64, sig_out: *i64) -> i64 |
| 799 | func lsp_starts(b: *u8, n: i64, at: i64, s: *u8) -> i64 |
| 813 | func lsp_caret_col(b: *u8, ls: i64, le: i64) -> i64 |
| 840 | func lsp_next_marker(b: *u8, n: i64, from: i64, kind_out: *i64) -> i64 |
| 860 | func lsp_parse_diags(b: *u8, n: i64, base: *u8, cap: i64, |
| 972 | func lsp_sym_find(slot: i64, name: *u8, kind_out: *u8, kcap: i64, |
| 1060 | func lsp_dec_at(b: *u8, s: i64, e: i64) -> i64 |
| 1075 | func lsp_send(body: *u8, n: i64) -> i64 |
| 1094 | func lsp_window_log(kind: i64, msg: *u8) -> i64 called by 2: lsp_publishlsp_mode_diag calls 20: sys_mmaplsp_analyzelsp_scratch_pathsys_read_filelsp_parse_diagslsp_basename+14 |
| 1107 | func lsp_reply_initialize(id: *u8) -> i64 |
| 1119 | func lsp_reply_null(id: *u8) -> i64 |
| 1128 | func lsp_reply_error(id: *u8, code: i64, msg: *u8) -> i64 |
| 1143 | func lsp_build_diags(slot: i64, out_cap_out: *i64, nd_out: *i64) -> *u8 |
| 1248 | func lsp_publish(slot: i64) -> i64 |
| 1257 | func lsp_slen_bounded(b: *u8, cap: i64) -> i64 |
| 1268 | func lsp_resolve(slot: i64, line0: i64, char0: i64, |
| 1307 | func lsp_decl_line(slot: i64, path: *u8, line: i64, out: *u8, cap: i64, trim: i64) -> i64 |
| 1341 | func lsp_handle_definition(id: *u8, slot: i64, line0: i64, char0: i64) -> i64 called by 1: lsp_dispatch calls 11: sys_mmaplsp_resolvelsp_reply_nulllsp_decl_linelsp_slenlsp_str_into+5 |
| 1390 | func lsp_handle_hover(id: *u8, slot: i64, line0: i64, char0: i64) -> i64 |
| 1444 | func lsp_get_id_raw(b: *u8, n: i64, out: *u8, cap: i64) -> i64 |
| 1487 | func lsp_read_header(clen_out: *i64) -> i64 |
| 1539 | func lsp_read_body(n: i64) -> *u8 |
| 1554 | func lsp_params_pos(b: *u8, n: i64, l_out: *i64, c_out: *i64) -> i64 |
| 1568 | func lsp_dispatch(b: *u8, n: i64) -> i64 |
| 1656 | func lsp_serve() -> i64 |
| 1683 | func lsp_oneshot_load(path: *u8) -> i64 |
| 1696 | func lsp_mode_diag(path: *u8) -> i64 |
| 1710 | func lsp_mode_symbols(path: *u8) -> i64 |
| 1731 | func lsp_usage() -> i64 |
| 1742 | func main(argc: i64, argv: *i64) -> i64 |