@tishlang/tish 1.0.7 → 1.0.10

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 (127) hide show
  1. package/Cargo.toml +43 -0
  2. package/LICENSE +13 -0
  3. package/README.md +66 -0
  4. package/crates/js_to_tish/Cargo.toml +9 -0
  5. package/crates/js_to_tish/README.md +18 -0
  6. package/crates/js_to_tish/src/error.rs +61 -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 +608 -0
  10. package/crates/js_to_tish/src/transform/stmt.rs +474 -0
  11. package/crates/js_to_tish/src/transform.rs +60 -0
  12. package/crates/tish/Cargo.toml +44 -0
  13. package/crates/tish/src/main.rs +585 -0
  14. package/crates/tish/src/repl_completion.rs +200 -0
  15. package/crates/tish/tests/integration_test.rs +726 -0
  16. package/crates/tish_ast/Cargo.toml +7 -0
  17. package/crates/tish_ast/src/ast.rs +494 -0
  18. package/crates/tish_ast/src/lib.rs +5 -0
  19. package/crates/tish_build_utils/Cargo.toml +5 -0
  20. package/crates/tish_build_utils/src/lib.rs +175 -0
  21. package/crates/tish_builtins/Cargo.toml +12 -0
  22. package/crates/tish_builtins/src/array.rs +410 -0
  23. package/crates/tish_builtins/src/globals.rs +197 -0
  24. package/crates/tish_builtins/src/helpers.rs +38 -0
  25. package/crates/tish_builtins/src/lib.rs +14 -0
  26. package/crates/tish_builtins/src/math.rs +80 -0
  27. package/crates/tish_builtins/src/object.rs +36 -0
  28. package/crates/tish_builtins/src/string.rs +253 -0
  29. package/crates/tish_bytecode/Cargo.toml +15 -0
  30. package/crates/tish_bytecode/src/chunk.rs +97 -0
  31. package/crates/tish_bytecode/src/compiler.rs +1361 -0
  32. package/crates/tish_bytecode/src/encoding.rs +100 -0
  33. package/crates/tish_bytecode/src/lib.rs +19 -0
  34. package/crates/tish_bytecode/src/opcode.rs +110 -0
  35. package/crates/tish_bytecode/src/peephole.rs +159 -0
  36. package/crates/tish_bytecode/src/serialize.rs +163 -0
  37. package/crates/tish_bytecode/tests/constant_folding.rs +84 -0
  38. package/crates/tish_bytecode/tests/shortcircuit.rs +49 -0
  39. package/crates/tish_bytecode/tests/sort_optimization.rs +31 -0
  40. package/crates/tish_compile/Cargo.toml +21 -0
  41. package/crates/tish_compile/src/codegen.rs +3316 -0
  42. package/crates/tish_compile/src/lib.rs +71 -0
  43. package/crates/tish_compile/src/resolve.rs +631 -0
  44. package/crates/tish_compile/src/types.rs +304 -0
  45. package/crates/tish_compile_js/Cargo.toml +16 -0
  46. package/crates/tish_compile_js/examples/jsx_vdom_smoke.tish +8 -0
  47. package/crates/tish_compile_js/src/codegen.rs +794 -0
  48. package/crates/tish_compile_js/src/error.rs +20 -0
  49. package/crates/tish_compile_js/src/js_intrinsics.rs +82 -0
  50. package/crates/tish_compile_js/src/lib.rs +27 -0
  51. package/crates/tish_compile_js/src/tests_jsx.rs +32 -0
  52. package/crates/tish_compiler_wasm/Cargo.toml +19 -0
  53. package/crates/tish_compiler_wasm/src/lib.rs +55 -0
  54. package/crates/tish_compiler_wasm/src/resolve_virtual.rs +462 -0
  55. package/crates/tish_core/Cargo.toml +11 -0
  56. package/crates/tish_core/src/console_style.rs +128 -0
  57. package/crates/tish_core/src/json.rs +327 -0
  58. package/crates/tish_core/src/lib.rs +15 -0
  59. package/crates/tish_core/src/macros.rs +37 -0
  60. package/crates/tish_core/src/uri.rs +115 -0
  61. package/crates/tish_core/src/value.rs +376 -0
  62. package/crates/tish_cranelift/Cargo.toml +17 -0
  63. package/crates/tish_cranelift/src/lib.rs +41 -0
  64. package/crates/tish_cranelift/src/link.rs +120 -0
  65. package/crates/tish_cranelift/src/lower.rs +77 -0
  66. package/crates/tish_cranelift_runtime/Cargo.toml +19 -0
  67. package/crates/tish_cranelift_runtime/src/lib.rs +43 -0
  68. package/crates/tish_eval/Cargo.toml +26 -0
  69. package/crates/tish_eval/src/eval.rs +3205 -0
  70. package/crates/tish_eval/src/http.rs +122 -0
  71. package/crates/tish_eval/src/lib.rs +59 -0
  72. package/crates/tish_eval/src/natives.rs +301 -0
  73. package/crates/tish_eval/src/promise.rs +173 -0
  74. package/crates/tish_eval/src/regex.rs +298 -0
  75. package/crates/tish_eval/src/timers.rs +111 -0
  76. package/crates/tish_eval/src/value.rs +224 -0
  77. package/crates/tish_eval/src/value_convert.rs +85 -0
  78. package/crates/tish_fmt/Cargo.toml +16 -0
  79. package/crates/tish_fmt/src/bin/tish-fmt.rs +41 -0
  80. package/crates/tish_fmt/src/lib.rs +884 -0
  81. package/crates/tish_jsx_web/Cargo.toml +7 -0
  82. package/crates/tish_jsx_web/README.md +18 -0
  83. package/crates/tish_jsx_web/src/lib.rs +157 -0
  84. package/crates/tish_jsx_web/vendor/Lattish.tish +347 -0
  85. package/crates/tish_lexer/Cargo.toml +7 -0
  86. package/crates/tish_lexer/src/lib.rs +430 -0
  87. package/crates/tish_lexer/src/token.rs +155 -0
  88. package/crates/tish_lint/Cargo.toml +17 -0
  89. package/crates/tish_lint/src/bin/tish-lint.rs +77 -0
  90. package/crates/tish_lint/src/lib.rs +278 -0
  91. package/crates/tish_llvm/Cargo.toml +11 -0
  92. package/crates/tish_llvm/src/lib.rs +106 -0
  93. package/crates/tish_lsp/Cargo.toml +22 -0
  94. package/crates/tish_lsp/README.md +26 -0
  95. package/crates/tish_lsp/src/main.rs +615 -0
  96. package/crates/tish_native/Cargo.toml +14 -0
  97. package/crates/tish_native/src/build.rs +102 -0
  98. package/crates/tish_native/src/lib.rs +237 -0
  99. package/crates/tish_opt/Cargo.toml +11 -0
  100. package/crates/tish_opt/src/lib.rs +896 -0
  101. package/crates/tish_parser/Cargo.toml +9 -0
  102. package/crates/tish_parser/src/lib.rs +123 -0
  103. package/crates/tish_parser/src/parser.rs +1714 -0
  104. package/crates/tish_runtime/Cargo.toml +26 -0
  105. package/crates/tish_runtime/src/http.rs +308 -0
  106. package/crates/tish_runtime/src/http_fetch.rs +453 -0
  107. package/crates/tish_runtime/src/lib.rs +1004 -0
  108. package/crates/tish_runtime/src/native_promise.rs +26 -0
  109. package/crates/tish_runtime/src/promise.rs +77 -0
  110. package/crates/tish_runtime/src/promise_io.rs +41 -0
  111. package/crates/tish_runtime/src/timers.rs +125 -0
  112. package/crates/tish_runtime/src/ws.rs +725 -0
  113. package/crates/tish_runtime/tests/fetch_readable_stream.rs +99 -0
  114. package/crates/tish_vm/Cargo.toml +31 -0
  115. package/crates/tish_vm/src/lib.rs +39 -0
  116. package/crates/tish_vm/src/vm.rs +1399 -0
  117. package/crates/tish_wasm/Cargo.toml +13 -0
  118. package/crates/tish_wasm/src/lib.rs +358 -0
  119. package/crates/tish_wasm_runtime/Cargo.toml +25 -0
  120. package/crates/tish_wasm_runtime/src/lib.rs +36 -0
  121. package/justfile +260 -0
  122. package/package.json +8 -3
  123. package/platform/darwin-arm64/tish +0 -0
  124. package/platform/darwin-x64/tish +0 -0
  125. package/platform/linux-arm64/tish +0 -0
  126. package/platform/linux-x64/tish +0 -0
  127. package/platform/win32-x64/tish.exe +0 -0
@@ -0,0 +1,615 @@
1
+ //! Tish Language Server — diagnostics, symbols, completion, format, go-to-definition, workspace symbols.
2
+
3
+ use std::collections::HashMap;
4
+ use std::path::{Path, PathBuf};
5
+ use std::sync::{Arc, RwLock};
6
+
7
+ use regex::Regex;
8
+ use tower_lsp::jsonrpc::Result;
9
+ use tower_lsp::lsp_types::{
10
+ CompletionItem, CompletionItemKind, NumberOrString,
11
+ CompletionParams, CompletionResponse, CompletionTriggerKind, Diagnostic, DiagnosticSeverity,
12
+ DidChangeTextDocumentParams, DidCloseTextDocumentParams, DidOpenTextDocumentParams,
13
+ DocumentFormattingParams, DocumentSymbolParams, DocumentSymbolResponse, GotoDefinitionParams,
14
+ GotoDefinitionResponse, InitializeParams, InitializeResult, Location, MessageType, OneOf,
15
+ Position, Range, ServerCapabilities, ServerInfo, SymbolInformation, SymbolKind,
16
+ TextDocumentPositionParams, TextDocumentSyncCapability, TextDocumentSyncKind, Url,
17
+ WorkspaceSymbolParams,
18
+ };
19
+ use tower_lsp::{Client, LanguageServer, LspService, Server};
20
+ use walkdir::WalkDir;
21
+
22
+ #[derive(Debug)]
23
+ struct Backend {
24
+ client: Client,
25
+ docs: Arc<RwLock<HashMap<Url, String>>>,
26
+ roots: Arc<RwLock<Vec<PathBuf>>>,
27
+ }
28
+
29
+ #[tokio::main]
30
+ async fn main() {
31
+ let stdin = tokio::io::stdin();
32
+ let stdout = tokio::io::stdout();
33
+
34
+ let (service, socket) = LspService::new(|client| Backend {
35
+ client,
36
+ docs: Arc::new(RwLock::new(HashMap::new())),
37
+ roots: Arc::new(RwLock::new(Vec::new())),
38
+ });
39
+ Server::new(stdin, stdout, socket).serve(service).await;
40
+ }
41
+
42
+ fn parse_error_pos(err: &str) -> (u32, u32) {
43
+ static RE: std::sync::OnceLock<Regex> = std::sync::OnceLock::new();
44
+ let re = RE.get_or_init(|| Regex::new(r"start: \((\d+), (\d+)\)").unwrap());
45
+ if let Some(c) = re.captures(err) {
46
+ let line: u32 = c.get(1).and_then(|m| m.as_str().parse().ok()).unwrap_or(1);
47
+ let col: u32 = c.get(2).and_then(|m| m.as_str().parse().ok()).unwrap_or(1);
48
+ return (line.saturating_sub(1), col.saturating_sub(1));
49
+ }
50
+ (0, 0)
51
+ }
52
+
53
+ fn pos(line: u32, col: u32) -> Position {
54
+ Position {
55
+ line,
56
+ character: col,
57
+ }
58
+ }
59
+
60
+ fn diag_range(line: u32, col: u32, text: &str) -> Range {
61
+ let line_str = text.lines().nth(line as usize).unwrap_or("");
62
+ let end_char = line_str.len().max(col as usize + 1) as u32;
63
+ Range {
64
+ start: pos(line, col),
65
+ end: pos(line, end_char.min(col + 80)),
66
+ }
67
+ }
68
+
69
+ fn publish_parse_and_lint(client: &Client, uri: Url, text: &str) {
70
+ let mut diags = Vec::new();
71
+ match tish_parser::parse(text) {
72
+ Ok(program) => {
73
+ for d in tish_lint::lint_program(&program) {
74
+ let sev = match d.severity {
75
+ tish_lint::Severity::Error => DiagnosticSeverity::ERROR,
76
+ tish_lint::Severity::Warning => DiagnosticSeverity::WARNING,
77
+ };
78
+ diags.push(Diagnostic {
79
+ range: diag_range(d.line.saturating_sub(1), d.col.saturating_sub(1), text),
80
+ severity: Some(sev),
81
+ code: Some(NumberOrString::String(d.code.to_string())),
82
+ message: d.message,
83
+ ..Default::default()
84
+ });
85
+ }
86
+ }
87
+ Err(e) => {
88
+ let (l, c) = parse_error_pos(&e);
89
+ diags.push(Diagnostic {
90
+ range: diag_range(l, c, text),
91
+ severity: Some(DiagnosticSeverity::ERROR),
92
+ message: e,
93
+ ..Default::default()
94
+ });
95
+ }
96
+ }
97
+ let _ = client.publish_diagnostics(uri, diags, None);
98
+ }
99
+
100
+ #[tower_lsp::async_trait]
101
+ impl LanguageServer for Backend {
102
+ async fn initialize(&self, params: InitializeParams) -> Result<InitializeResult> {
103
+ let mut roots = self.roots.write().unwrap();
104
+ roots.clear();
105
+ if let Some(folders) = params.workspace_folders {
106
+ for f in folders {
107
+ if let Ok(p) = f.uri.to_file_path() {
108
+ roots.push(p);
109
+ }
110
+ }
111
+ } else if let Some(uri) = params.root_uri {
112
+ if let Ok(p) = uri.to_file_path() {
113
+ roots.push(p);
114
+ }
115
+ }
116
+ Ok(InitializeResult {
117
+ capabilities: ServerCapabilities {
118
+ text_document_sync: Some(TextDocumentSyncCapability::Kind(
119
+ TextDocumentSyncKind::FULL,
120
+ )),
121
+ completion_provider: Some(tower_lsp::lsp_types::CompletionOptions {
122
+ trigger_characters: Some(vec![".".to_string()]),
123
+ ..Default::default()
124
+ }),
125
+ definition_provider: Some(OneOf::Left(true)),
126
+ document_formatting_provider: Some(OneOf::Left(true)),
127
+ document_symbol_provider: Some(OneOf::Left(true)),
128
+ workspace_symbol_provider: Some(OneOf::Left(true)),
129
+ ..Default::default()
130
+ },
131
+ server_info: Some(ServerInfo {
132
+ name: "tish-lsp".into(),
133
+ version: Some(env!("CARGO_PKG_VERSION").into()),
134
+ }),
135
+ })
136
+ }
137
+
138
+ async fn initialized(&self, _: tower_lsp::lsp_types::InitializedParams) {
139
+ self.client
140
+ .log_message(MessageType::INFO, "tish-lsp ready")
141
+ .await;
142
+ }
143
+
144
+ async fn shutdown(&self) -> Result<()> {
145
+ Ok(())
146
+ }
147
+
148
+ async fn did_open(&self, p: DidOpenTextDocumentParams) {
149
+ let uri = p.text_document.uri;
150
+ let text = p.text_document.text;
151
+ self.docs.write().unwrap().insert(uri.clone(), text.clone());
152
+ publish_parse_and_lint(&self.client, uri, &text);
153
+ }
154
+
155
+ async fn did_change(&self, p: DidChangeTextDocumentParams) {
156
+ let uri = p.text_document.uri;
157
+ if let Some(chg) = p.content_changes.into_iter().last() {
158
+ self.docs
159
+ .write()
160
+ .unwrap()
161
+ .insert(uri.clone(), chg.text.clone());
162
+ publish_parse_and_lint(&self.client, uri, &chg.text);
163
+ }
164
+ }
165
+
166
+ async fn did_close(&self, p: DidCloseTextDocumentParams) {
167
+ self.docs.write().unwrap().remove(&p.text_document.uri);
168
+ let _ = self
169
+ .client
170
+ .publish_diagnostics(p.text_document.uri, vec![], None);
171
+ }
172
+
173
+ async fn completion(&self, params: CompletionParams) -> Result<Option<CompletionResponse>> {
174
+ let uri = params
175
+ .text_document_position
176
+ .text_document
177
+ .uri
178
+ .clone();
179
+ let _pos = params.text_document_position.position;
180
+ let text = {
181
+ let g = self.docs.read().unwrap();
182
+ g.get(&uri).cloned()
183
+ };
184
+ let Some(text) = text else {
185
+ return Ok(None);
186
+ };
187
+
188
+ let keywords = [
189
+ "fn", "async", "let", "const", "if", "else", "while", "for", "return", "break",
190
+ "continue", "switch", "case", "default", "try", "catch", "finally", "throw", "import",
191
+ "export", "from", "typeof", "void", "await", "of", "in", "true", "false", "null",
192
+ "function", "do",
193
+ ];
194
+ let mut items: Vec<CompletionItem> = keywords
195
+ .iter()
196
+ .map(|k| CompletionItem {
197
+ label: (*k).to_string(),
198
+ kind: Some(CompletionItemKind::KEYWORD),
199
+ ..Default::default()
200
+ })
201
+ .collect();
202
+
203
+ if let Ok(program) = tish_parser::parse(&text) {
204
+ for s in &program.statements {
205
+ match s {
206
+ tish_ast::Statement::FunDecl { name, .. } => {
207
+ items.push(CompletionItem {
208
+ label: name.to_string(),
209
+ kind: Some(CompletionItemKind::FUNCTION),
210
+ ..Default::default()
211
+ });
212
+ }
213
+ tish_ast::Statement::VarDecl { name, .. } => {
214
+ items.push(CompletionItem {
215
+ label: name.to_string(),
216
+ kind: Some(CompletionItemKind::VARIABLE),
217
+ ..Default::default()
218
+ });
219
+ }
220
+ _ => {}
221
+ }
222
+ }
223
+ }
224
+
225
+ if let Some(ctx) = params.context {
226
+ if matches!(
227
+ ctx.trigger_kind,
228
+ CompletionTriggerKind::TRIGGER_CHARACTER
229
+ ) && ctx.trigger_character.as_deref() == Some(".")
230
+ {
231
+ // After dot: could add member completion later
232
+ }
233
+ }
234
+
235
+ Ok(Some(CompletionResponse::Array(items)))
236
+ }
237
+
238
+ async fn document_symbol(
239
+ &self,
240
+ params: DocumentSymbolParams,
241
+ ) -> Result<Option<DocumentSymbolResponse>> {
242
+ let uri = params.text_document.uri;
243
+ let text = {
244
+ let g = self.docs.read().unwrap();
245
+ g.get(&uri).cloned()
246
+ };
247
+ let Some(text) = text else {
248
+ return Ok(None);
249
+ };
250
+ let Ok(program) = tish_parser::parse(&text) else {
251
+ return Ok(None);
252
+ };
253
+
254
+ let mut syms: Vec<tower_lsp::lsp_types::DocumentSymbol> = Vec::new();
255
+ for s in &program.statements {
256
+ doc_symbol_stmt(s, &text, &mut syms);
257
+ }
258
+ Ok(Some(DocumentSymbolResponse::Nested(syms)))
259
+ }
260
+
261
+ async fn goto_definition(
262
+ &self,
263
+ params: GotoDefinitionParams,
264
+ ) -> Result<Option<GotoDefinitionResponse>> {
265
+ let TextDocumentPositionParams {
266
+ text_document,
267
+ position,
268
+ } = params.text_document_position_params;
269
+ let uri = text_document.uri;
270
+ let text = {
271
+ let g = self.docs.read().unwrap();
272
+ g.get(&uri).cloned()
273
+ };
274
+ let Some(text) = text else {
275
+ return Ok(None);
276
+ };
277
+ let Ok(program) = tish_parser::parse(&text) else {
278
+ return Ok(None);
279
+ };
280
+
281
+ let word = word_at_position(&text, position);
282
+ if word.is_empty() {
283
+ return Ok(None);
284
+ }
285
+
286
+ let path = uri.to_file_path().ok();
287
+
288
+ for s in &program.statements {
289
+ if let Some(loc) = find_decl_in_stmt(s, &word, &uri, &text) {
290
+ return Ok(Some(GotoDefinitionResponse::Scalar(loc)));
291
+ }
292
+ }
293
+
294
+ if let Some(ref base) = path {
295
+ for s in &program.statements {
296
+ if let tish_ast::Statement::Import {
297
+ specifiers,
298
+ from,
299
+ ..
300
+ } = s
301
+ {
302
+ for sp in specifiers {
303
+ let (imported, local) = match sp {
304
+ tish_ast::ImportSpecifier::Named { name, alias } => {
305
+ (name.as_ref(), alias.as_ref().map(|a| a.as_ref()).unwrap_or(name.as_ref()))
306
+ }
307
+ tish_ast::ImportSpecifier::Default(n) => (n.as_ref(), n.as_ref()),
308
+ _ => continue,
309
+ };
310
+ if local != word.as_str() {
311
+ continue;
312
+ }
313
+ let from_s = from.as_ref();
314
+ if !from_s.starts_with("./") && !from_s.starts_with("../") {
315
+ continue;
316
+ }
317
+ let dir = base.parent().unwrap_or(Path::new(""));
318
+ let target = dir.join(from_s.trim_start_matches("./"));
319
+ let target = if target.extension().is_none() {
320
+ target.with_extension("tish")
321
+ } else {
322
+ target
323
+ };
324
+ if let Ok(can) = target.canonicalize() {
325
+ if let Ok(u) = Url::from_file_path(&can) {
326
+ if let Ok(src) = std::fs::read_to_string(&can) {
327
+ if let Ok(prog) = tish_parser::parse(&src) {
328
+ if let Some(loc) =
329
+ find_export(&prog, imported, &u, &src)
330
+ {
331
+ return Ok(Some(GotoDefinitionResponse::Scalar(loc)));
332
+ }
333
+ }
334
+ }
335
+ }
336
+ }
337
+ }
338
+ }
339
+ }
340
+ }
341
+
342
+ Ok(None)
343
+ }
344
+
345
+ async fn formatting(&self, params: DocumentFormattingParams) -> Result<Option<Vec<tower_lsp::lsp_types::TextEdit>>> {
346
+ let uri = params.text_document.uri;
347
+ let text = {
348
+ let g = self.docs.read().unwrap();
349
+ g.get(&uri).cloned()
350
+ };
351
+ let Some(text) = text else {
352
+ return Ok(None);
353
+ };
354
+ match tish_fmt::format_source(&text) {
355
+ Ok(formatted) => {
356
+ let lines = text.lines().count() as u32;
357
+ let last_line = text.lines().last().map(|l| l.len() as u32).unwrap_or(0);
358
+ Ok(Some(vec![tower_lsp::lsp_types::TextEdit {
359
+ range: Range {
360
+ start: pos(0, 0),
361
+ end: pos(lines.saturating_sub(1), last_line),
362
+ },
363
+ new_text: formatted,
364
+ }]))
365
+ }
366
+ Err(e) => {
367
+ self.client
368
+ .show_message(
369
+ MessageType::ERROR,
370
+ format!("tish-fmt (formatter): {}", e),
371
+ )
372
+ .await;
373
+ Ok(None)
374
+ }
375
+ }
376
+ }
377
+
378
+ async fn symbol(
379
+ &self,
380
+ params: WorkspaceSymbolParams,
381
+ ) -> Result<Option<Vec<SymbolInformation>>> {
382
+ let query = params.query.to_lowercase();
383
+ if query.is_empty() {
384
+ return Ok(Some(vec![]));
385
+ }
386
+ let roots = self.roots.read().unwrap().clone();
387
+ let mut out = Vec::new();
388
+
389
+ for root in roots {
390
+ for e in WalkDir::new(&root)
391
+ .into_iter()
392
+ .filter_map(|e| e.ok())
393
+ .filter(|e| e.path().extension().map(|x| x == "tish").unwrap_or(false))
394
+ {
395
+ let path = e.path();
396
+ let Ok(src) = std::fs::read_to_string(path) else {
397
+ continue;
398
+ };
399
+ let Ok(program) = tish_parser::parse(&src) else {
400
+ continue;
401
+ };
402
+ let Ok(uri) = Url::from_file_path(path) else {
403
+ continue;
404
+ };
405
+ for s in &program.statements {
406
+ collect_workspace_syms(s, &src, &uri, &query, &mut out);
407
+ }
408
+ }
409
+ }
410
+ Ok(Some(out))
411
+ }
412
+ }
413
+
414
+ fn collect_workspace_syms(
415
+ s: &tish_ast::Statement,
416
+ text: &str,
417
+ uri: &Url,
418
+ query: &str,
419
+ out: &mut Vec<SymbolInformation>,
420
+ ) {
421
+ match s {
422
+ tish_ast::Statement::FunDecl { name, span, .. } => {
423
+ if name.to_lowercase().contains(query) {
424
+ out.push(SymbolInformation {
425
+ name: name.to_string(),
426
+ kind: SymbolKind::FUNCTION,
427
+ tags: None,
428
+ deprecated: None,
429
+ location: Location {
430
+ uri: uri.clone(),
431
+ range: span_to_range(span, text),
432
+ },
433
+ container_name: None,
434
+ });
435
+ }
436
+ }
437
+ tish_ast::Statement::VarDecl { name, span, .. } => {
438
+ if name.to_lowercase().contains(query) {
439
+ out.push(SymbolInformation {
440
+ name: name.to_string(),
441
+ kind: SymbolKind::VARIABLE,
442
+ tags: None,
443
+ deprecated: None,
444
+ location: Location {
445
+ uri: uri.clone(),
446
+ range: span_to_range(span, text),
447
+ },
448
+ container_name: None,
449
+ });
450
+ }
451
+ }
452
+ tish_ast::Statement::Block { statements, .. } => {
453
+ for x in statements {
454
+ collect_workspace_syms(x, text, uri, query, out);
455
+ }
456
+ }
457
+ _ => {}
458
+ }
459
+ }
460
+
461
+ fn find_export(
462
+ program: &tish_ast::Program,
463
+ name: &str,
464
+ uri: &Url,
465
+ text: &str,
466
+ ) -> Option<Location> {
467
+ for s in &program.statements {
468
+ match s {
469
+ tish_ast::Statement::FunDecl { name: n, span, .. } if n.as_ref() == name => {
470
+ return Some(Location {
471
+ uri: uri.clone(),
472
+ range: span_to_range(span, text),
473
+ });
474
+ }
475
+ tish_ast::Statement::VarDecl { name: n, span, .. } if n.as_ref() == name => {
476
+ return Some(Location {
477
+ uri: uri.clone(),
478
+ range: span_to_range(span, text),
479
+ });
480
+ }
481
+ tish_ast::Statement::Export { declaration, .. } => match declaration.as_ref() {
482
+ tish_ast::ExportDeclaration::Named(inner) => {
483
+ if let Some(loc) = find_decl_in_stmt(inner, name, uri, text) {
484
+ return Some(loc);
485
+ }
486
+ }
487
+ _ => {}
488
+ },
489
+ _ => {}
490
+ }
491
+ }
492
+ None
493
+ }
494
+
495
+ fn find_decl_in_stmt(
496
+ s: &tish_ast::Statement,
497
+ word: &str,
498
+ uri: &Url,
499
+ text: &str,
500
+ ) -> Option<Location> {
501
+ match s {
502
+ tish_ast::Statement::FunDecl { name, span, .. } if name.as_ref() == word => Some(Location {
503
+ uri: uri.clone(),
504
+ range: span_to_range(span, text),
505
+ }),
506
+ tish_ast::Statement::VarDecl { name, span, .. } if name.as_ref() == word => Some(Location {
507
+ uri: uri.clone(),
508
+ range: span_to_range(span, text),
509
+ }),
510
+ tish_ast::Statement::Block { statements, .. } => {
511
+ for x in statements {
512
+ if let Some(l) = find_decl_in_stmt(x, word, uri, text) {
513
+ return Some(l);
514
+ }
515
+ }
516
+ None
517
+ }
518
+ _ => None,
519
+ }
520
+ }
521
+
522
+ fn span_to_range(span: &tish_ast::Span, _text: &str) -> Range {
523
+ Range {
524
+ start: pos(span.start.0.saturating_sub(1) as u32, span.start.1.saturating_sub(1) as u32),
525
+ end: pos(span.end.0.saturating_sub(1) as u32, span.end.1.saturating_sub(1) as u32),
526
+ }
527
+ }
528
+
529
+ fn word_at_position(text: &str, position: Position) -> String {
530
+ let line = text.lines().nth(position.line as usize).unwrap_or("");
531
+ let col = position.character as usize;
532
+ let bytes: Vec<(usize, char)> = line.char_indices().collect();
533
+ let mut start = col.min(bytes.len().saturating_sub(1));
534
+ while start > 0 && !is_ident_char(bytes.get(start).map(|(_, c)| *c).unwrap_or(' ')) {
535
+ start = start.saturating_sub(1);
536
+ }
537
+ let mut i = start;
538
+ while i < bytes.len() && is_ident_char(bytes[i].1) {
539
+ i += 1;
540
+ }
541
+ if start < bytes.len() {
542
+ line[bytes[start].0..bytes.get(i).map(|(p, _)| *p).unwrap_or(line.len())].to_string()
543
+ } else {
544
+ String::new()
545
+ }
546
+ }
547
+
548
+ fn is_ident_char(c: char) -> bool {
549
+ c.is_alphanumeric() || c == '_'
550
+ }
551
+
552
+ fn doc_symbol_stmt(
553
+ s: &tish_ast::Statement,
554
+ text: &str,
555
+ out: &mut Vec<tower_lsp::lsp_types::DocumentSymbol>,
556
+ ) {
557
+ match s {
558
+ tish_ast::Statement::FunDecl {
559
+ name,
560
+ span,
561
+ body,
562
+ ..
563
+ } => {
564
+ let mut children = Vec::new();
565
+ collect_child_syms(body, text, &mut children);
566
+ out.push(tower_lsp::lsp_types::DocumentSymbol {
567
+ name: name.to_string(),
568
+ detail: None,
569
+ kind: tower_lsp::lsp_types::SymbolKind::FUNCTION,
570
+ tags: None,
571
+ deprecated: None,
572
+ range: span_to_range(span, text),
573
+ selection_range: span_to_range(span, text),
574
+ children: if children.is_empty() {
575
+ None
576
+ } else {
577
+ Some(children)
578
+ },
579
+ });
580
+ }
581
+ tish_ast::Statement::VarDecl { name, span, .. } => {
582
+ out.push(tower_lsp::lsp_types::DocumentSymbol {
583
+ name: name.to_string(),
584
+ detail: None,
585
+ kind: tower_lsp::lsp_types::SymbolKind::VARIABLE,
586
+ tags: None,
587
+ deprecated: None,
588
+ range: span_to_range(span, text),
589
+ selection_range: span_to_range(span, text),
590
+ children: None,
591
+ });
592
+ }
593
+ tish_ast::Statement::Block { statements, .. } => {
594
+ for x in statements {
595
+ doc_symbol_stmt(x, text, out);
596
+ }
597
+ }
598
+ _ => {}
599
+ }
600
+ }
601
+
602
+ fn collect_child_syms(
603
+ s: &tish_ast::Statement,
604
+ text: &str,
605
+ out: &mut Vec<tower_lsp::lsp_types::DocumentSymbol>,
606
+ ) {
607
+ match s {
608
+ tish_ast::Statement::Block { statements, .. } => {
609
+ for x in statements {
610
+ doc_symbol_stmt(x, text, out);
611
+ }
612
+ }
613
+ _ => doc_symbol_stmt(s, text, out),
614
+ }
615
+ }
@@ -0,0 +1,14 @@
1
+ [package]
2
+ name = "tish_native"
3
+ version = "0.1.0"
4
+ edition = "2021"
5
+ description = "Native code generation backend for Tish (Cranelift / direct compile)"
6
+
7
+ [dependencies]
8
+ tish_build_utils = { path = "../tish_build_utils" }
9
+ tish_ast = { path = "../tish_ast" }
10
+ tish_bytecode = { path = "../tish_bytecode" }
11
+ tish_compile = { path = "../tish_compile" }
12
+ tish_cranelift = { path = "../tish_cranelift" }
13
+ tish_llvm = { path = "../tish_llvm" }
14
+ tish_opt = { path = "../tish_opt" }