@tishlang/tish-format 1.0.12 → 2.0.1

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 (189) hide show
  1. package/Cargo.toml +51 -0
  2. package/LICENSE +13 -0
  3. package/bin/tish-format +0 -0
  4. package/crates/js_to_tish/Cargo.toml +11 -0
  5. package/crates/js_to_tish/README.md +18 -0
  6. package/crates/js_to_tish/src/error.rs +55 -0
  7. package/crates/js_to_tish/src/lib.rs +11 -0
  8. package/crates/js_to_tish/src/span_util.rs +35 -0
  9. package/crates/js_to_tish/src/transform/expr.rs +611 -0
  10. package/crates/js_to_tish/src/transform/stmt.rs +503 -0
  11. package/crates/js_to_tish/src/transform.rs +60 -0
  12. package/crates/tish/Cargo.toml +62 -0
  13. package/crates/tish/build.rs +21 -0
  14. package/crates/tish/src/cargo_native_registry.rs +32 -0
  15. package/crates/tish/src/cli_help.rs +576 -0
  16. package/crates/tish/src/main.rs +853 -0
  17. package/crates/tish/src/repl_completion.rs +199 -0
  18. package/crates/tish/tests/cargo_example_compile.rs +67 -0
  19. package/crates/tish/tests/error_source_location.rs +36 -0
  20. package/crates/tish/tests/fixtures/cargo_example_project/Cargo.toml +3 -0
  21. package/crates/tish/tests/fixtures/cargo_example_project/crates/demo-shim/Cargo.toml +11 -0
  22. package/crates/tish/tests/fixtures/cargo_example_project/crates/demo-shim/src/lib.rs +12 -0
  23. package/crates/tish/tests/fixtures/cargo_example_project/package.json +10 -0
  24. package/crates/tish/tests/fixtures/cargo_example_project/src/main.tish +3 -0
  25. package/crates/tish/tests/fixtures/runtime_error_location.tish +5 -0
  26. package/crates/tish/tests/fixtures/trycatch_runtime_errors.tish +15 -0
  27. package/crates/tish/tests/fixtures/tty_capability.tish +9 -0
  28. package/crates/tish/tests/integration_test.rs +1406 -0
  29. package/crates/tish/tests/run_optimize_stdout_parity.rs +50 -0
  30. package/crates/tish/tests/shortcircuit.rs +65 -0
  31. package/crates/tish/tests/trycatch_runtime_errors.rs +45 -0
  32. package/crates/tish/tests/tty_capability.rs +43 -0
  33. package/crates/tish_ast/Cargo.toml +9 -0
  34. package/crates/tish_ast/src/ast.rs +649 -0
  35. package/crates/tish_ast/src/lib.rs +5 -0
  36. package/crates/tish_build_utils/Cargo.toml +11 -0
  37. package/crates/tish_build_utils/src/lib.rs +577 -0
  38. package/crates/tish_builtins/Cargo.toml +22 -0
  39. package/crates/tish_builtins/src/array.rs +803 -0
  40. package/crates/tish_builtins/src/collections.rs +481 -0
  41. package/crates/tish_builtins/src/construct.rs +199 -0
  42. package/crates/tish_builtins/src/date.rs +538 -0
  43. package/crates/tish_builtins/src/globals.rs +293 -0
  44. package/crates/tish_builtins/src/helpers.rs +35 -0
  45. package/crates/tish_builtins/src/iterator.rs +129 -0
  46. package/crates/tish_builtins/src/lib.rs +21 -0
  47. package/crates/tish_builtins/src/math.rs +89 -0
  48. package/crates/tish_builtins/src/number.rs +96 -0
  49. package/crates/tish_builtins/src/object.rs +36 -0
  50. package/crates/tish_builtins/src/string.rs +646 -0
  51. package/crates/tish_builtins/src/symbol.rs +83 -0
  52. package/crates/tish_builtins/src/typedarrays.rs +298 -0
  53. package/crates/tish_bytecode/Cargo.toml +17 -0
  54. package/crates/tish_bytecode/src/chunk.rs +164 -0
  55. package/crates/tish_bytecode/src/compiler.rs +2604 -0
  56. package/crates/tish_bytecode/src/encoding.rs +102 -0
  57. package/crates/tish_bytecode/src/lib.rs +20 -0
  58. package/crates/tish_bytecode/src/opcode.rs +185 -0
  59. package/crates/tish_bytecode/src/peephole.rs +189 -0
  60. package/crates/tish_bytecode/src/serialize.rs +193 -0
  61. package/crates/tish_bytecode/tests/break_continue_bytecode.rs +44 -0
  62. package/crates/tish_bytecode/tests/constant_folding.rs +84 -0
  63. package/crates/tish_bytecode/tests/sort_optimization.rs +31 -0
  64. package/crates/tish_compile/Cargo.toml +27 -0
  65. package/crates/tish_compile/src/check.rs +774 -0
  66. package/crates/tish_compile/src/codegen.rs +7317 -0
  67. package/crates/tish_compile/src/infer.rs +1681 -0
  68. package/crates/tish_compile/src/lib.rs +206 -0
  69. package/crates/tish_compile/src/resolve.rs +1951 -0
  70. package/crates/tish_compile/src/types.rs +605 -0
  71. package/crates/tish_compile_js/Cargo.toml +18 -0
  72. package/crates/tish_compile_js/examples/jsx_vdom_smoke.tish +8 -0
  73. package/crates/tish_compile_js/src/codegen.rs +938 -0
  74. package/crates/tish_compile_js/src/error.rs +20 -0
  75. package/crates/tish_compile_js/src/lib.rs +26 -0
  76. package/crates/tish_compile_js/src/tests_jsx.rs +414 -0
  77. package/crates/tish_compiler_wasm/Cargo.toml +21 -0
  78. package/crates/tish_compiler_wasm/src/lib.rs +57 -0
  79. package/crates/tish_compiler_wasm/src/resolve_virtual.rs +473 -0
  80. package/crates/tish_core/Cargo.toml +32 -0
  81. package/crates/tish_core/src/console_style.rs +170 -0
  82. package/crates/tish_core/src/json.rs +430 -0
  83. package/crates/tish_core/src/lib.rs +20 -0
  84. package/crates/tish_core/src/macros.rs +36 -0
  85. package/crates/tish_core/src/shape.rs +85 -0
  86. package/crates/tish_core/src/uri.rs +118 -0
  87. package/crates/tish_core/src/value.rs +1350 -0
  88. package/crates/tish_core/src/vmref.rs +183 -0
  89. package/crates/tish_cranelift/Cargo.toml +19 -0
  90. package/crates/tish_cranelift/src/lib.rs +43 -0
  91. package/crates/tish_cranelift/src/link.rs +130 -0
  92. package/crates/tish_cranelift/src/lower.rs +85 -0
  93. package/crates/tish_cranelift_runtime/Cargo.toml +26 -0
  94. package/crates/tish_cranelift_runtime/src/lib.rs +45 -0
  95. package/crates/tish_eval/Cargo.toml +51 -0
  96. package/crates/tish_eval/src/eval.rs +4265 -0
  97. package/crates/tish_eval/src/http.rs +191 -0
  98. package/crates/tish_eval/src/lib.rs +99 -0
  99. package/crates/tish_eval/src/natives.rs +551 -0
  100. package/crates/tish_eval/src/promise.rs +179 -0
  101. package/crates/tish_eval/src/regex.rs +299 -0
  102. package/crates/tish_eval/src/timers.rs +120 -0
  103. package/crates/tish_eval/src/value.rs +336 -0
  104. package/crates/tish_eval/src/value_convert.rs +117 -0
  105. package/crates/tish_ffi/Cargo.toml +26 -0
  106. package/crates/tish_ffi/src/lib.rs +518 -0
  107. package/crates/tish_ffi/tests/fixtures/testmod/Cargo.toml +18 -0
  108. package/crates/tish_ffi/tests/fixtures/testmod/src/lib.rs +46 -0
  109. package/crates/tish_ffi/tests/loader.rs +65 -0
  110. package/crates/tish_fmt/Cargo.toml +16 -0
  111. package/crates/tish_fmt/src/bin/tish-fmt.rs +41 -0
  112. package/crates/tish_fmt/src/lib.rs +2157 -0
  113. package/crates/tish_jsx_web/Cargo.toml +9 -0
  114. package/crates/tish_jsx_web/README.md +5 -0
  115. package/crates/tish_jsx_web/src/lib.rs +2 -0
  116. package/crates/tish_lexer/Cargo.toml +9 -0
  117. package/crates/tish_lexer/src/lib.rs +1104 -0
  118. package/crates/tish_lexer/src/token.rs +170 -0
  119. package/crates/tish_lint/Cargo.toml +18 -0
  120. package/crates/tish_lint/src/bin/tish-lint.rs +195 -0
  121. package/crates/tish_lint/src/lib.rs +281 -0
  122. package/crates/tish_llvm/Cargo.toml +13 -0
  123. package/crates/tish_llvm/src/lib.rs +115 -0
  124. package/crates/tish_lsp/Cargo.toml +25 -0
  125. package/crates/tish_lsp/README.md +26 -0
  126. package/crates/tish_lsp/src/builtin_goto.rs +362 -0
  127. package/crates/tish_lsp/src/import_goto.rs +564 -0
  128. package/crates/tish_lsp/src/main.rs +1459 -0
  129. package/crates/tish_native/Cargo.toml +16 -0
  130. package/crates/tish_native/src/build.rs +481 -0
  131. package/crates/tish_native/src/config.rs +48 -0
  132. package/crates/tish_native/src/lib.rs +416 -0
  133. package/crates/tish_opt/Cargo.toml +13 -0
  134. package/crates/tish_opt/src/lib.rs +1046 -0
  135. package/crates/tish_parser/Cargo.toml +11 -0
  136. package/crates/tish_parser/src/lib.rs +386 -0
  137. package/crates/tish_parser/src/parser.rs +2726 -0
  138. package/crates/tish_pg/Cargo.toml +34 -0
  139. package/crates/tish_pg/README.md +38 -0
  140. package/crates/tish_pg/src/error.rs +52 -0
  141. package/crates/tish_pg/src/lib.rs +955 -0
  142. package/crates/tish_resolve/Cargo.toml +13 -0
  143. package/crates/tish_resolve/src/lib.rs +3601 -0
  144. package/crates/tish_resolve/src/pos.rs +141 -0
  145. package/crates/tish_runtime/Cargo.toml +100 -0
  146. package/crates/tish_runtime/src/http.rs +1347 -0
  147. package/crates/tish_runtime/src/http_fetch.rs +492 -0
  148. package/crates/tish_runtime/src/http_hyper.rs +441 -0
  149. package/crates/tish_runtime/src/http_prefork.rs +189 -0
  150. package/crates/tish_runtime/src/lib.rs +1447 -0
  151. package/crates/tish_runtime/src/native_promise.rs +15 -0
  152. package/crates/tish_runtime/src/promise.rs +558 -0
  153. package/crates/tish_runtime/src/promise_io.rs +38 -0
  154. package/crates/tish_runtime/src/timers.rs +172 -0
  155. package/crates/tish_runtime/src/tty.rs +226 -0
  156. package/crates/tish_runtime/src/ws.rs +778 -0
  157. package/crates/tish_runtime/tests/fetch_readable_stream.rs +102 -0
  158. package/crates/tish_ui/Cargo.toml +17 -0
  159. package/crates/tish_ui/src/jsx.rs +692 -0
  160. package/crates/tish_ui/src/lib.rs +20 -0
  161. package/crates/tish_ui/src/runtime/hooks.rs +573 -0
  162. package/crates/tish_ui/src/runtime/mod.rs +183 -0
  163. package/crates/tish_vm/Cargo.toml +60 -0
  164. package/crates/tish_vm/src/jit.rs +1050 -0
  165. package/crates/tish_vm/src/lib.rs +41 -0
  166. package/crates/tish_vm/src/vm.rs +3536 -0
  167. package/crates/tish_vm/tests/concurrent_shared_state.rs +140 -0
  168. package/crates/tish_vm/tests/fixtures/or_string_cmd.tish +2 -0
  169. package/crates/tish_vm/tests/lexical_scope_declare.rs +34 -0
  170. package/crates/tish_vm/tests/peephole_jump_chain_logical_or.rs +150 -0
  171. package/crates/tish_wasm/Cargo.toml +15 -0
  172. package/crates/tish_wasm/src/lib.rs +428 -0
  173. package/crates/tish_wasm_runtime/Cargo.toml +37 -0
  174. package/crates/tish_wasm_runtime/src/gpu.rs +429 -0
  175. package/crates/tish_wasm_runtime/src/lib.rs +42 -0
  176. package/crates/tishlang_cargo_bindgen/Cargo.toml +26 -0
  177. package/crates/tishlang_cargo_bindgen/src/classify.rs +261 -0
  178. package/crates/tishlang_cargo_bindgen/src/discover.rs +125 -0
  179. package/crates/tishlang_cargo_bindgen/src/infer.rs +382 -0
  180. package/crates/tishlang_cargo_bindgen/src/lib.rs +349 -0
  181. package/crates/tishlang_cargo_bindgen/src/main.rs +167 -0
  182. package/crates/tishlang_cargo_bindgen/src/metadata.rs +117 -0
  183. package/justfile +276 -0
  184. package/package.json +2 -2
  185. package/platform/darwin-arm64/tish-fmt +0 -0
  186. package/platform/darwin-x64/tish-fmt +0 -0
  187. package/platform/linux-arm64/tish-fmt +0 -0
  188. package/platform/linux-x64/tish-fmt +0 -0
  189. package/platform/win32-x64/tish-fmt.exe +0 -0
@@ -0,0 +1,115 @@
1
+ //! LLVM/clang link path for the **embedded-bytecode + VM** native binary.
2
+ //!
3
+ //! Emits a C file that only holds the serialized chunk as bytes; clang produces `chunk.o`,
4
+ //! then links with **`tishlang_cranelift_runtime`** (same `tish_run_chunk` + `tishlang_vm`
5
+ //! entry as `--native-backend cranelift`). This is **not** LLVM IR lowering of Tish opcodes.
6
+
7
+ use std::fs;
8
+ use std::path::Path;
9
+ use std::process::Command;
10
+
11
+ use tishlang_bytecode::{serialize, Chunk};
12
+ use tishlang_compile::{detect_cycles, merge_modules, resolve_project};
13
+
14
+ /// Compile a Tish program to a native binary via clang/LLVM.
15
+ pub fn compile_to_native(
16
+ entry_path: &Path,
17
+ project_root: Option<&Path>,
18
+ output_path: &Path,
19
+ ) -> Result<(), LlvmError> {
20
+ let modules = resolve_project(entry_path, project_root).map_err(|e| LlvmError {
21
+ message: e.to_string(),
22
+ })?;
23
+ detect_cycles(&modules).map_err(|e| LlvmError {
24
+ message: e.to_string(),
25
+ })?;
26
+ let program = merge_modules(modules)
27
+ .map(|m| m.program)
28
+ .map_err(|e| LlvmError {
29
+ message: e.to_string(),
30
+ })?;
31
+ let chunk = tishlang_bytecode::compile(&program).map_err(|e| LlvmError {
32
+ message: e.to_string(),
33
+ })?;
34
+
35
+ let features = tishlang_compile::extract_native_import_features(&program);
36
+ compile_chunk_to_native(&chunk, output_path, &features)
37
+ }
38
+
39
+ /// Compile a bytecode chunk to a native binary.
40
+ /// `features` are passed to tishlang_cranelift_runtime (e.g. fs, process, http for built-in modules).
41
+ pub fn compile_chunk_to_native(
42
+ chunk: &Chunk,
43
+ output_path: &Path,
44
+ features: &[String],
45
+ ) -> Result<(), LlvmError> {
46
+ let chunk_bytes = serialize(chunk);
47
+ let out_name = output_path
48
+ .file_stem()
49
+ .and_then(|s| s.to_str())
50
+ .unwrap_or("tish_out");
51
+ let build_dir = tishlang_build_utils::create_build_dir("tishlang_llvm_build", out_name)
52
+ .map_err(|e| LlvmError { message: e })?;
53
+
54
+ // Generate C source with embedded bytecode (data only; main comes from Rust link step)
55
+ let bytes_str: Vec<String> = chunk_bytes.iter().map(|b| b.to_string()).collect();
56
+ let chunk_c = format!(
57
+ r#"/* Generated by tishlang_llvm - do not edit */
58
+ #include <stddef.h>
59
+ #include <stdint.h>
60
+
61
+ const uint8_t tish_chunk_data[] = {{ {} }};
62
+ const uint64_t tish_chunk_len = sizeof(tish_chunk_data);
63
+ "#,
64
+ bytes_str.join(",")
65
+ );
66
+
67
+ let chunk_c_path = build_dir.join("chunk.c");
68
+ fs::write(&chunk_c_path, chunk_c).map_err(|e| LlvmError {
69
+ message: format!("Cannot write chunk.c: {}", e),
70
+ })?;
71
+
72
+ // Compile C to object file using clang (LLVM)
73
+ let object_path = build_dir.join("chunk.o");
74
+ let clang = std::env::var("CLANG").unwrap_or_else(|_| "clang".to_string());
75
+ let status = Command::new(&clang)
76
+ .args(["-c", "-o"])
77
+ .arg(&object_path)
78
+ .arg(&chunk_c_path)
79
+ .status()
80
+ .map_err(|e| LlvmError {
81
+ message: format!(
82
+ "Failed to run clang: {}. Install clang (LLVM) or set CLANG env var.",
83
+ e
84
+ ),
85
+ })?;
86
+
87
+ if !status.success() {
88
+ return Err(LlvmError {
89
+ message: "clang compilation failed. Ensure clang (LLVM) is installed.".to_string(),
90
+ });
91
+ }
92
+
93
+ // Link using the same infrastructure as Cranelift (links .o with tishlang_cranelift_runtime)
94
+ let object_path_canonical = object_path.canonicalize().map_err(|e| LlvmError {
95
+ message: format!("Cannot canonicalize object path: {}", e),
96
+ })?;
97
+ tishlang_cranelift::link_to_binary(&object_path_canonical, output_path, features)
98
+ .map_err(|e| LlvmError { message: e.message })?;
99
+
100
+ Ok(())
101
+ }
102
+
103
+ /// Error from LLVM compilation.
104
+ #[derive(Debug)]
105
+ pub struct LlvmError {
106
+ pub message: String,
107
+ }
108
+
109
+ impl std::fmt::Display for LlvmError {
110
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
111
+ write!(f, "{}", self.message)
112
+ }
113
+ }
114
+
115
+ impl std::error::Error for LlvmError {}
@@ -0,0 +1,25 @@
1
+ [package]
2
+ name = "tishlang_lsp"
3
+ version = "0.1.1"
4
+ edition = "2021"
5
+ description = "Language Server Protocol implementation for Tish"
6
+ license-file = { workspace = true }
7
+ repository = { workspace = true }
8
+
9
+ [[bin]]
10
+ name = "tish-lsp"
11
+ path = "src/main.rs"
12
+
13
+ [dependencies]
14
+ tishlang_ast = { path = "../tish_ast", version = ">=0.1" }
15
+ tishlang_cargo_bindgen = { path = "../tishlang_cargo_bindgen", version = ">=0.1" }
16
+ tishlang_compile = { path = "../tish_compile", version = ">=0.1" }
17
+ tishlang_parser = { path = "../tish_parser", version = ">=0.1" }
18
+ tishlang_fmt = { path = "../tish_fmt", version = ">=0.1" }
19
+ tishlang_lint = { path = "../tish_lint", version = ">=0.1" }
20
+ tishlang_resolve = { path = "../tish_resolve", version = ">=0.1" }
21
+ tower-lsp = "0.20"
22
+ tokio = { version = "1", features = ["rt-multi-thread", "macros", "io-std"] }
23
+ serde_json = "1"
24
+ regex = "1"
25
+ walkdir = "2"
@@ -0,0 +1,26 @@
1
+ # tish-lsp
2
+
3
+ Language Server Protocol implementation for [Tish](https://github.com/tishlang/tish).
4
+
5
+ ## Build
6
+
7
+ ```bash
8
+ cargo build --release -p tishlang_lsp
9
+ ```
10
+
11
+ Binary: `target/release/tish-lsp` (stdio LSP).
12
+
13
+ ## Features
14
+
15
+ - Parse diagnostics + lint warnings (via `tish_lint` **library** — use **`tish-lint`** CLI separately in CI)
16
+ - Document symbols, completion, formatting (via `tish_fmt` **library** — use **`tish-fmt`** CLI separately in CI)
17
+ - Go to definition (same file, relative `./` / `../`, bare `node_modules` packages like Node, and native `tish:` / `@scope/pkg` / `cargo:` → Rust `pub fn` via `syn` + `cargo metadata` where configured)
18
+ - Workspace symbol search (`**/*.tish`)
19
+
20
+ ## Client configuration
21
+
22
+ See the [Tish docs — Language server](https://tishlang.com/docs/reference/language-server/) and [Editor setup](https://tishlang.com/docs/getting-started/editor/).
23
+
24
+ ## Developing
25
+
26
+ See the repo [`docs/tooling.md`](../../docs/tooling.md).
@@ -0,0 +1,362 @@
1
+ //! Built-in and JSX-intrinsic "go to definition" for the Tish LSP.
2
+ //!
3
+ //! Global / namespace member anchors are loaded from `stdlib/builtins.d.tish` in the `tish`
4
+ //! repository via `// @tish-source <symbol> <rel-path> <1-based-line>` pragmas. The type
5
+ //! surface in that file is the canonical declaration for ambient builtins.
6
+ //!
7
+ //! HTML / SVG intrinsic tag names are still listed here (sorted) for fast lookup; each maps
8
+ //! to the same vnode factory as in the pragma table for `div`.
9
+ //!
10
+ //! Definitions resolve to `file://` URIs only when `TISHLANG_SOURCE_ROOT` is set or the client
11
+ //! passes `tishlangSourceRoot` in LSP `initializationOptions` (VS Code: `tish.tishlangSourceRoot`).
12
+
13
+ use std::collections::HashMap;
14
+ use std::path::Path;
15
+ use std::sync::OnceLock;
16
+
17
+ use regex::Regex;
18
+ use tower_lsp::lsp_types::{Location, Position, Range, Url};
19
+
20
+ /// Stable path relative to the `tish` repository root (where the workspace `Cargo.toml` lives).
21
+ #[derive(Clone, Debug, PartialEq, Eq)]
22
+ pub struct BuiltinDef {
23
+ pub rel_path: String,
24
+ /// 0-based LSP line in the target file.
25
+ pub line: u32,
26
+ /// 0-based UTF-16 code unit offset on that line (ASCII-only targets use byte column).
27
+ pub character: u32,
28
+ }
29
+
30
+ fn is_ident_char(c: char) -> bool {
31
+ c.is_alphanumeric() || c == '_'
32
+ }
33
+
34
+ /// HTML / SVG intrinsic tag names (lowercase) that JSX lowers to `h("tag", …)`; must stay sorted for `binary_search`.
35
+ const HTML_INTRINSIC_TAGS: &[&str] = &[
36
+ "a",
37
+ "abbr",
38
+ "address",
39
+ "area",
40
+ "article",
41
+ "aside",
42
+ "audio",
43
+ "b",
44
+ "base",
45
+ "bdi",
46
+ "bdo",
47
+ "blockquote",
48
+ "body",
49
+ "br",
50
+ "button",
51
+ "canvas",
52
+ "caption",
53
+ "cite",
54
+ "code",
55
+ "col",
56
+ "colgroup",
57
+ "data",
58
+ "datalist",
59
+ "dd",
60
+ "del",
61
+ "details",
62
+ "dfn",
63
+ "dialog",
64
+ "div",
65
+ "dl",
66
+ "dt",
67
+ "em",
68
+ "embed",
69
+ "fieldset",
70
+ "figcaption",
71
+ "figure",
72
+ "footer",
73
+ "form",
74
+ "h1",
75
+ "h2",
76
+ "h3",
77
+ "h4",
78
+ "h5",
79
+ "h6",
80
+ "head",
81
+ "header",
82
+ "hr",
83
+ "html",
84
+ "i",
85
+ "iframe",
86
+ "img",
87
+ "input",
88
+ "ins",
89
+ "kbd",
90
+ "label",
91
+ "legend",
92
+ "li",
93
+ "link",
94
+ "main",
95
+ "map",
96
+ "mark",
97
+ "meta",
98
+ "meter",
99
+ "nav",
100
+ "noscript",
101
+ "object",
102
+ "ol",
103
+ "optgroup",
104
+ "option",
105
+ "output",
106
+ "p",
107
+ "param",
108
+ "picture",
109
+ "pre",
110
+ "progress",
111
+ "q",
112
+ "rp",
113
+ "rt",
114
+ "ruby",
115
+ "s",
116
+ "samp",
117
+ "script",
118
+ "section",
119
+ "select",
120
+ "slot",
121
+ "small",
122
+ "source",
123
+ "span",
124
+ "strong",
125
+ "style",
126
+ "sub",
127
+ "summary",
128
+ "sup",
129
+ "svg",
130
+ "table",
131
+ "tbody",
132
+ "td",
133
+ "template",
134
+ "textarea",
135
+ "tfoot",
136
+ "th",
137
+ "thead",
138
+ "time",
139
+ "title",
140
+ "tr",
141
+ "track",
142
+ "u",
143
+ "ul",
144
+ "var",
145
+ "video",
146
+ "wbr",
147
+ ];
148
+
149
+ static SOURCE_MAP: OnceLock<HashMap<String, BuiltinDef>> = OnceLock::new();
150
+
151
+ /// Parse `// @tish-source <symbol> <rel-path> <1-based-line>` lines (same format as `stdlib/builtins.d.tish`).
152
+ pub fn parse_tish_source_pragmas(src: &str) -> HashMap<String, BuiltinDef> {
153
+ let re = Regex::new(r"(?m)^\s*//\s*@tish-source\s+(\S+)\s+(\S+)\s+(\d+)\s*$")
154
+ .expect("builtin pragma regex");
155
+ let mut m = HashMap::new();
156
+ for cap in re.captures_iter(src) {
157
+ let sym = cap[1].to_string();
158
+ let rel = cap[2].to_string();
159
+ let line_1: u32 = cap[3].parse().unwrap_or(1);
160
+ m.insert(
161
+ sym,
162
+ BuiltinDef {
163
+ rel_path: rel,
164
+ line: line_1.saturating_sub(1),
165
+ character: 0,
166
+ },
167
+ );
168
+ }
169
+ m
170
+ }
171
+
172
+ fn source_map() -> &'static HashMap<String, BuiltinDef> {
173
+ SOURCE_MAP.get_or_init(|| {
174
+ let src = include_str!(concat!(
175
+ env!("CARGO_MANIFEST_DIR"),
176
+ "/../../stdlib/builtins.d.tish"
177
+ ));
178
+ parse_tish_source_pragmas(src)
179
+ })
180
+ }
181
+
182
+ /// If `col` lies on the name of a JSX opening (or closing) tag on this line, returns the span of that name in **character indices** (same convention as [`tower_lsp::lsp_types::Position::character`] for ASCII lines).
183
+ fn jsx_tag_name_char_span(line: &str, col: usize) -> Option<(usize, usize)> {
184
+ let chars: Vec<char> = line.chars().collect();
185
+ if chars.is_empty() {
186
+ return None;
187
+ }
188
+ let col = col.min(chars.len().saturating_sub(1));
189
+ let mut j = col;
190
+ while j > 0 {
191
+ j -= 1;
192
+ if chars[j] == '>' {
193
+ return None;
194
+ }
195
+ if chars[j] == '<' {
196
+ let mut k = j + 1;
197
+ while k < chars.len() && chars[k].is_whitespace() {
198
+ k += 1;
199
+ }
200
+ if k < chars.len() && chars[k] == '/' {
201
+ k += 1;
202
+ while k < chars.len() && chars[k].is_whitespace() {
203
+ k += 1;
204
+ }
205
+ }
206
+ let name_start = k;
207
+ while k < chars.len() {
208
+ let c = chars[k];
209
+ if c.is_whitespace() || c == '>' || c == '/' || c == '{' {
210
+ break;
211
+ }
212
+ if !(c.is_alphanumeric() || c == '_' || c == '-') {
213
+ break;
214
+ }
215
+ k += 1;
216
+ }
217
+ let name_end = k;
218
+ if name_start < name_end && col >= name_start && col < name_end {
219
+ return Some((name_start, name_end));
220
+ }
221
+ return None;
222
+ }
223
+ }
224
+ None
225
+ }
226
+
227
+ fn tag_name_at_span(line: &str, start: usize, end: usize) -> String {
228
+ line.chars()
229
+ .enumerate()
230
+ .filter(|(i, _)| *i >= start && *i < end)
231
+ .map(|(_, c)| c)
232
+ .collect()
233
+ }
234
+
235
+ /// `base.member` when the cursor is on `member` (same line, character-index column as `word_at_position`).
236
+ fn split_property_access(line: &str, col: usize) -> Option<(String, String)> {
237
+ let chars: Vec<char> = line.chars().collect();
238
+ if chars.is_empty() {
239
+ return None;
240
+ }
241
+ let col = col.min(chars.len().saturating_sub(1));
242
+ if !is_ident_char(chars[col]) {
243
+ return None;
244
+ }
245
+ let mut end = col;
246
+ while end + 1 < chars.len() && is_ident_char(chars[end + 1]) {
247
+ end += 1;
248
+ }
249
+ let mut start = col;
250
+ while start > 0 && is_ident_char(chars[start - 1]) {
251
+ start -= 1;
252
+ }
253
+ if start == 0 {
254
+ return None;
255
+ }
256
+ if chars[start - 1] != '.' {
257
+ return None;
258
+ }
259
+ let member: String = chars[start..=end].iter().collect();
260
+ let mut k = start - 2;
261
+ while k > 0 && is_ident_char(chars[k - 1]) {
262
+ k -= 1;
263
+ }
264
+ let base: String = chars[k..start - 1].iter().collect();
265
+ if base.is_empty() {
266
+ return None;
267
+ }
268
+ Some((base, member))
269
+ }
270
+
271
+ fn lookup_dotted(base: &str, member: &str) -> Option<BuiltinDef> {
272
+ let key = format!("{base}.{member}");
273
+ source_map().get(&key).cloned()
274
+ }
275
+
276
+ fn lookup_global(word: &str) -> Option<BuiltinDef> {
277
+ source_map().get(word).cloned()
278
+ }
279
+
280
+ /// Built-in or JSX-intrinsic definition for the identifier at `position`, if any.
281
+ pub fn definition_for_builtin(
282
+ text: &str,
283
+ line: u32,
284
+ character: u32,
285
+ word: &str,
286
+ ) -> Option<BuiltinDef> {
287
+ let line_str = text.lines().nth(line as usize)?;
288
+ let col = character as usize;
289
+
290
+ if let Some((ns, ne)) = jsx_tag_name_char_span(line_str, col) {
291
+ let tag = tag_name_at_span(line_str, ns, ne);
292
+ if tag == word {
293
+ if word == "Fragment" {
294
+ return lookup_global("Fragment");
295
+ }
296
+ if HTML_INTRINSIC_TAGS.binary_search(&word).is_ok() {
297
+ // Intrinsic tags share the same `ui_h` entry point as `div`.
298
+ return lookup_global("div");
299
+ }
300
+ }
301
+ }
302
+
303
+ if let Some((base, member)) = split_property_access(line_str, col) {
304
+ if let Some(d) = lookup_dotted(base.as_str(), member.as_str()) {
305
+ return Some(d);
306
+ }
307
+ }
308
+
309
+ lookup_global(word)
310
+ }
311
+
312
+ pub fn to_file_location(root: &Path, def: &BuiltinDef) -> Option<Location> {
313
+ let path = root.join(&def.rel_path);
314
+ if !path.is_file() {
315
+ return None;
316
+ }
317
+ let uri = Url::from_file_path(&path).ok()?;
318
+ let p = Position {
319
+ line: def.line,
320
+ character: def.character,
321
+ };
322
+ Some(Location {
323
+ uri,
324
+ range: Range { start: p, end: p },
325
+ })
326
+ }
327
+
328
+ #[cfg(test)]
329
+ mod tests {
330
+ use super::*;
331
+
332
+ #[test]
333
+ fn pragma_map_loads_console_log() {
334
+ let d = lookup_global("console").expect("console");
335
+ assert!(d.rel_path.contains("eval.rs"));
336
+ let m = lookup_dotted("console", "log").expect("console.log");
337
+ assert!(m.rel_path.contains("natives.rs"));
338
+ }
339
+
340
+ #[test]
341
+ fn jsx_div_maps_to_ui_h() {
342
+ let text = "let x = <div />";
343
+ let line = 0u32;
344
+ let defn = definition_for_builtin(text, line, 9, "div").expect("div builtin");
345
+ assert!(defn.rel_path.contains("runtime"));
346
+ assert_eq!(defn.line, 40);
347
+ }
348
+
349
+ #[test]
350
+ fn set_timeout_global() {
351
+ let defn =
352
+ definition_for_builtin("setTimeout(0, fn() {})\n", 0, 0, "setTimeout").expect("timer");
353
+ assert_eq!(defn.rel_path, "crates/tish_eval/src/timers.rs");
354
+ }
355
+
356
+ #[test]
357
+ fn console_log_qualified() {
358
+ let text = "console.log(1)";
359
+ let defn = definition_for_builtin(text, 0, 10, "log").expect("console.log");
360
+ assert_eq!(defn.rel_path, "crates/tish_eval/src/natives.rs");
361
+ }
362
+ }