@tishlang/tish 1.6.0 → 1.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 (113) hide show
  1. package/Cargo.toml +2 -0
  2. package/README.md +2 -0
  3. package/bin/tish +0 -0
  4. package/crates/js_to_tish/src/error.rs +2 -8
  5. package/crates/js_to_tish/src/transform/expr.rs +128 -137
  6. package/crates/js_to_tish/src/transform/stmt.rs +62 -32
  7. package/crates/tish/Cargo.toml +15 -5
  8. package/crates/tish/src/cargo_native_registry.rs +29 -0
  9. package/crates/tish/src/cli_help.rs +92 -39
  10. package/crates/tish/src/main.rs +172 -86
  11. package/crates/tish/src/repl_completion.rs +3 -3
  12. package/crates/tish/tests/cargo_example_compile.rs +4 -2
  13. package/crates/tish/tests/integration_test.rs +216 -54
  14. package/crates/tish/tests/run_optimize_stdout_parity.rs +3 -7
  15. package/crates/tish/tests/shortcircuit.rs +20 -5
  16. package/crates/tish_ast/src/ast.rs +92 -23
  17. package/crates/tish_build_utils/Cargo.toml +4 -0
  18. package/crates/tish_build_utils/src/lib.rs +136 -8
  19. package/crates/tish_builtins/Cargo.toml +5 -1
  20. package/crates/tish_builtins/src/array.rs +65 -33
  21. package/crates/tish_builtins/src/construct.rs +34 -39
  22. package/crates/tish_builtins/src/globals.rs +42 -26
  23. package/crates/tish_builtins/src/helpers.rs +2 -1
  24. package/crates/tish_builtins/src/lib.rs +5 -5
  25. package/crates/tish_builtins/src/math.rs +5 -3
  26. package/crates/tish_builtins/src/object.rs +3 -2
  27. package/crates/tish_builtins/src/string.rs +144 -22
  28. package/crates/tish_bytecode/src/chunk.rs +0 -1
  29. package/crates/tish_bytecode/src/compiler.rs +173 -71
  30. package/crates/tish_bytecode/src/opcode.rs +24 -6
  31. package/crates/tish_bytecode/src/peephole.rs +2 -2
  32. package/crates/tish_compile/Cargo.toml +1 -0
  33. package/crates/tish_compile/src/codegen.rs +1621 -453
  34. package/crates/tish_compile/src/infer.rs +75 -19
  35. package/crates/tish_compile/src/lib.rs +19 -8
  36. package/crates/tish_compile/src/resolve.rs +278 -137
  37. package/crates/tish_compile/src/types.rs +184 -24
  38. package/crates/tish_compile_js/Cargo.toml +1 -0
  39. package/crates/tish_compile_js/src/codegen.rs +181 -37
  40. package/crates/tish_compile_js/src/lib.rs +3 -1
  41. package/crates/tish_compile_js/src/tests_jsx.rs +30 -6
  42. package/crates/tish_compiler_wasm/src/lib.rs +16 -13
  43. package/crates/tish_compiler_wasm/src/resolve_virtual.rs +69 -59
  44. package/crates/tish_core/Cargo.toml +8 -0
  45. package/crates/tish_core/src/json.rs +107 -56
  46. package/crates/tish_core/src/lib.rs +4 -2
  47. package/crates/tish_core/src/macros.rs +5 -5
  48. package/crates/tish_core/src/uri.rs +9 -6
  49. package/crates/tish_core/src/value.rs +145 -43
  50. package/crates/tish_core/src/vmref.rs +178 -0
  51. package/crates/tish_cranelift/src/link.rs +6 -9
  52. package/crates/tish_cranelift/src/lower.rs +14 -8
  53. package/crates/tish_eval/Cargo.toml +17 -2
  54. package/crates/tish_eval/src/eval.rs +474 -165
  55. package/crates/tish_eval/src/http.rs +61 -0
  56. package/crates/tish_eval/src/lib.rs +12 -8
  57. package/crates/tish_eval/src/natives.rs +136 -38
  58. package/crates/tish_eval/src/promise.rs +14 -8
  59. package/crates/tish_eval/src/timers.rs +28 -19
  60. package/crates/tish_eval/src/value.rs +17 -6
  61. package/crates/tish_eval/src/value_convert.rs +13 -5
  62. package/crates/tish_fmt/src/lib.rs +149 -43
  63. package/crates/tish_lexer/src/lib.rs +232 -63
  64. package/crates/tish_lexer/src/token.rs +10 -6
  65. package/crates/tish_llvm/src/lib.rs +17 -8
  66. package/crates/tish_lsp/Cargo.toml +4 -1
  67. package/crates/tish_lsp/README.md +1 -1
  68. package/crates/tish_lsp/src/builtin_goto.rs +261 -0
  69. package/crates/tish_lsp/src/import_goto.rs +549 -0
  70. package/crates/tish_lsp/src/main.rs +504 -106
  71. package/crates/tish_native/src/build.rs +4 -8
  72. package/crates/tish_native/src/lib.rs +54 -21
  73. package/crates/tish_opt/src/lib.rs +84 -52
  74. package/crates/tish_parser/src/lib.rs +45 -13
  75. package/crates/tish_parser/src/parser.rs +505 -130
  76. package/crates/tish_resolve/Cargo.toml +13 -0
  77. package/crates/tish_resolve/src/lib.rs +3436 -0
  78. package/crates/tish_resolve/src/pos.rs +133 -0
  79. package/crates/tish_runtime/Cargo.toml +68 -3
  80. package/crates/tish_runtime/src/http.rs +1136 -145
  81. package/crates/tish_runtime/src/http_fetch.rs +38 -27
  82. package/crates/tish_runtime/src/http_hyper.rs +418 -0
  83. package/crates/tish_runtime/src/http_prefork.rs +189 -0
  84. package/crates/tish_runtime/src/lib.rs +375 -189
  85. package/crates/tish_runtime/src/promise.rs +199 -40
  86. package/crates/tish_runtime/src/promise_io.rs +2 -1
  87. package/crates/tish_runtime/src/timers.rs +37 -1
  88. package/crates/tish_runtime/src/ws.rs +65 -42
  89. package/crates/tish_runtime/tests/fetch_readable_stream.rs +5 -4
  90. package/crates/tish_ui/src/jsx.rs +317 -27
  91. package/crates/tish_ui/src/lib.rs +5 -2
  92. package/crates/tish_ui/src/runtime/hooks.rs +406 -45
  93. package/crates/tish_ui/src/runtime/mod.rs +36 -9
  94. package/crates/tish_vm/Cargo.toml +15 -5
  95. package/crates/tish_vm/src/vm.rs +725 -281
  96. package/crates/tish_vm/tests/peephole_jump_chain_logical_or.rs +11 -4
  97. package/crates/tish_wasm/src/lib.rs +55 -42
  98. package/crates/tish_wasm_runtime/Cargo.toml +2 -1
  99. package/crates/tish_wasm_runtime/src/lib.rs +1 -1
  100. package/crates/tishlang_cargo_bindgen/Cargo.toml +26 -0
  101. package/crates/tishlang_cargo_bindgen/src/classify.rs +265 -0
  102. package/crates/tishlang_cargo_bindgen/src/discover.rs +120 -0
  103. package/crates/tishlang_cargo_bindgen/src/infer.rs +372 -0
  104. package/crates/tishlang_cargo_bindgen/src/lib.rs +350 -0
  105. package/crates/tishlang_cargo_bindgen/src/main.rs +164 -0
  106. package/crates/tishlang_cargo_bindgen/src/metadata.rs +114 -0
  107. package/justfile +8 -0
  108. package/package.json +1 -1
  109. package/platform/darwin-arm64/tish +0 -0
  110. package/platform/darwin-x64/tish +0 -0
  111. package/platform/linux-arm64/tish +0 -0
  112. package/platform/linux-x64/tish +0 -0
  113. package/platform/win32-x64/tish.exe +0 -0
@@ -5,7 +5,7 @@
5
5
 
6
6
  mod token;
7
7
 
8
- pub use token::{Token, TokenKind, Span};
8
+ pub use token::{Span, Token, TokenKind};
9
9
 
10
10
  use std::collections::VecDeque;
11
11
  use std::iter::Peekable;
@@ -74,14 +74,21 @@ impl<'a> Lexer<'a> {
74
74
  loop {
75
75
  match self.peek() {
76
76
  None | Some('{') | Some('<') => break,
77
- Some(c) => { self.advance(); s.push(c); }
77
+ Some(c) => {
78
+ self.advance();
79
+ s.push(c);
80
+ }
78
81
  }
79
82
  }
80
83
  if s.is_empty() {
81
84
  Ok(None)
82
85
  } else {
83
86
  let end = self.span_start();
84
- Ok(Some(Token { kind: TokenKind::JsxText, span: Span { start, end }, literal: Some(s.into()) }))
87
+ Ok(Some(Token {
88
+ kind: TokenKind::JsxText,
89
+ span: Span { start, end },
90
+ literal: Some(s.into()),
91
+ }))
85
92
  }
86
93
  }
87
94
 
@@ -139,7 +146,9 @@ impl<'a> Lexer<'a> {
139
146
 
140
147
  fn skip_line_comment(&mut self) {
141
148
  while let Some(c) = self.advance() {
142
- if c == '\n' { break; }
149
+ if c == '\n' {
150
+ break;
151
+ }
143
152
  }
144
153
  }
145
154
 
@@ -147,8 +156,14 @@ impl<'a> Lexer<'a> {
147
156
  let mut depth = 1;
148
157
  while depth > 0 {
149
158
  match self.advance() {
150
- Some('*') if self.peek() == Some('/') => { self.advance(); depth -= 1; }
151
- Some('/') if self.peek() == Some('*') => { self.advance(); depth += 1; }
159
+ Some('*') if self.peek() == Some('/') => {
160
+ self.advance();
161
+ depth -= 1;
162
+ }
163
+ Some('/') if self.peek() == Some('*') => {
164
+ self.advance();
165
+ depth += 1;
166
+ }
152
167
  None => return Err("Unterminated block comment".to_string()),
153
168
  _ => {}
154
169
  }
@@ -186,7 +201,11 @@ impl<'a> Lexer<'a> {
186
201
 
187
202
  fn read_string(&mut self, quote: char) -> Result<String, String> {
188
203
  let mut s = String::with_capacity(32);
189
- let extra = if quote == '"' { &['"', '\''][..] } else { &['\'', '"'][..] };
204
+ let extra = if quote == '"' {
205
+ &['"', '\''][..]
206
+ } else {
207
+ &['\'', '"'][..]
208
+ };
190
209
  loop {
191
210
  match self.advance() {
192
211
  None => return Err("Unterminated string".to_string()),
@@ -213,24 +232,44 @@ impl<'a> Lexer<'a> {
213
232
  }
214
233
 
215
234
  /// Read a template literal. If `is_continuation` is true, we're continuing after a `}`.
216
- fn read_template(&mut self, start: (usize, usize), is_continuation: bool) -> Result<Option<Token>, String> {
235
+ fn read_template(
236
+ &mut self,
237
+ start: (usize, usize),
238
+ is_continuation: bool,
239
+ ) -> Result<Option<Token>, String> {
217
240
  let mut s = String::with_capacity(if is_continuation { 32 } else { 64 });
218
241
  let extra = &['`', '$', '{'][..];
219
-
242
+
220
243
  loop {
221
244
  match self.advance() {
222
245
  None => return Err("Unterminated template literal".to_string()),
223
246
  Some('`') => {
224
247
  let end = self.span_start();
225
- let kind = if is_continuation { TokenKind::TemplateTail } else { TokenKind::TemplateNoSub };
226
- return Ok(Some(Token { kind, span: Span { start, end }, literal: Some(s.into()) }));
248
+ let kind = if is_continuation {
249
+ TokenKind::TemplateTail
250
+ } else {
251
+ TokenKind::TemplateNoSub
252
+ };
253
+ return Ok(Some(Token {
254
+ kind,
255
+ span: Span { start, end },
256
+ literal: Some(s.into()),
257
+ }));
227
258
  }
228
259
  Some('$') if self.peek() == Some('{') => {
229
260
  self.advance();
230
261
  self.template_brace_stack.push(1);
231
262
  let end = self.span_start();
232
- let kind = if is_continuation { TokenKind::TemplateMiddle } else { TokenKind::TemplateHead };
233
- return Ok(Some(Token { kind, span: Span { start, end }, literal: Some(s.into()) }));
263
+ let kind = if is_continuation {
264
+ TokenKind::TemplateMiddle
265
+ } else {
266
+ TokenKind::TemplateHead
267
+ };
268
+ return Ok(Some(Token {
269
+ kind,
270
+ span: Span { start, end },
271
+ literal: Some(s.into()),
272
+ }));
234
273
  }
235
274
  Some('\\') => s.push(self.handle_escape(extra)?),
236
275
  Some(c) => s.push(c),
@@ -308,7 +347,10 @@ impl<'a> Lexer<'a> {
308
347
  self.indent_stack.pop();
309
348
  return Ok(Some(Token {
310
349
  kind: TokenKind::Dedent,
311
- span: Span { start: (self.line, self.col), end: (self.line, self.col) },
350
+ span: Span {
351
+ start: (self.line, self.col),
352
+ end: (self.line, self.col),
353
+ },
312
354
  literal: None,
313
355
  }));
314
356
  }
@@ -362,31 +404,66 @@ impl<'a> Lexer<'a> {
362
404
  ';' => TokenKind::Semicolon,
363
405
  ',' => TokenKind::Comma,
364
406
  '.' => {
365
- if self.peek() == Some('?') { self.advance(); TokenKind::OptionalChain }
366
- else if self.peek() == Some('.') {
407
+ if self.peek() == Some('?') {
408
+ self.advance();
409
+ TokenKind::OptionalChain
410
+ } else if self.peek() == Some('.') {
367
411
  self.advance();
368
- if self.peek() == Some('.') { self.advance(); TokenKind::Spread }
369
- else { return Err("Unexpected .. (use ... for rest params)".to_string()); }
370
- } else { TokenKind::Dot }
412
+ if self.peek() == Some('.') {
413
+ self.advance();
414
+ TokenKind::Spread
415
+ } else {
416
+ return Err("Unexpected .. (use ... for rest params)".to_string());
417
+ }
418
+ } else {
419
+ TokenKind::Dot
420
+ }
371
421
  }
372
422
  '=' => {
373
423
  if self.peek() == Some('=') {
374
424
  self.advance();
375
- if self.peek() == Some('=') { self.advance(); TokenKind::StrictEq } else { TokenKind::Eq }
376
- } else if self.peek() == Some('>') { self.advance(); TokenKind::Arrow }
377
- else { TokenKind::Assign }
425
+ if self.peek() == Some('=') {
426
+ self.advance();
427
+ TokenKind::StrictEq
428
+ } else {
429
+ TokenKind::Eq
430
+ }
431
+ } else if self.peek() == Some('>') {
432
+ self.advance();
433
+ TokenKind::Arrow
434
+ } else {
435
+ TokenKind::Assign
436
+ }
378
437
  }
379
438
  '!' => {
380
439
  if self.peek() == Some('=') {
381
440
  self.advance();
382
- if self.peek() == Some('=') { self.advance(); TokenKind::StrictNe } else { TokenKind::Ne }
383
- } else { TokenKind::Not }
441
+ if self.peek() == Some('=') {
442
+ self.advance();
443
+ TokenKind::StrictNe
444
+ } else {
445
+ TokenKind::Ne
446
+ }
447
+ } else {
448
+ TokenKind::Not
449
+ }
384
450
  }
385
451
  '<' => {
386
- if self.peek() == Some('=') { self.advance(); TokenKind::Le }
387
- else if self.peek() == Some('<') { self.advance(); TokenKind::Shl }
388
- else if self.peek() == Some('/') { self.jsx_in_closing_tag = true; TokenKind::Lt }
389
- else if self.peek() == Some('>') || self.peek().map(|c| c.is_ascii_alphabetic() || c == '_').unwrap_or(false) {
452
+ if self.peek() == Some('=') {
453
+ self.advance();
454
+ TokenKind::Le
455
+ } else if self.peek() == Some('<') {
456
+ self.advance();
457
+ TokenKind::Shl
458
+ } else if self.peek() == Some('/') {
459
+ self.jsx_in_closing_tag = true;
460
+ TokenKind::Lt
461
+ } else if self.peek() == Some('>')
462
+ || self
463
+ .peek()
464
+ .map(|c| c.is_ascii_alphabetic() || c == '_')
465
+ .unwrap_or(false)
466
+ {
390
467
  self.jsx_depth += 1;
391
468
  self.jsx_stack.push(JsxEl {
392
469
  in_opener: true,
@@ -394,12 +471,18 @@ impl<'a> Lexer<'a> {
394
471
  });
395
472
  self.jsx_in_opening_tag = true;
396
473
  TokenKind::Lt
397
- } else { TokenKind::Lt }
474
+ } else {
475
+ TokenKind::Lt
476
+ }
398
477
  }
399
478
  '>' => {
400
- if self.peek() == Some('=') { self.advance(); TokenKind::Ge }
401
- else if self.peek() == Some('>') { self.advance(); TokenKind::Shr }
402
- else {
479
+ if self.peek() == Some('=') {
480
+ self.advance();
481
+ TokenKind::Ge
482
+ } else if self.peek() == Some('>') {
483
+ self.advance();
484
+ TokenKind::Shr
485
+ } else {
403
486
  if self.jsx_in_closing_tag {
404
487
  self.jsx_depth = (self.jsx_depth - 1).max(0);
405
488
  self.jsx_stack.pop();
@@ -425,66 +508,133 @@ impl<'a> Lexer<'a> {
425
508
  '^' => TokenKind::BitXor,
426
509
  '~' => TokenKind::BitNot,
427
510
  '+' => {
428
- if self.peek() == Some('+') { self.advance(); TokenKind::PlusPlus }
429
- else if self.peek() == Some('=') { self.advance(); TokenKind::PlusAssign }
430
- else { TokenKind::Plus }
511
+ if self.peek() == Some('+') {
512
+ self.advance();
513
+ TokenKind::PlusPlus
514
+ } else if self.peek() == Some('=') {
515
+ self.advance();
516
+ TokenKind::PlusAssign
517
+ } else {
518
+ TokenKind::Plus
519
+ }
431
520
  }
432
521
  '-' => {
433
- if self.peek() == Some('-') { self.advance(); TokenKind::MinusMinus }
434
- else if self.peek() == Some('=') { self.advance(); TokenKind::MinusAssign }
435
- else { TokenKind::Minus }
522
+ if self.peek() == Some('-') {
523
+ self.advance();
524
+ TokenKind::MinusMinus
525
+ } else if self.peek() == Some('=') {
526
+ self.advance();
527
+ TokenKind::MinusAssign
528
+ } else {
529
+ TokenKind::Minus
530
+ }
436
531
  }
437
532
  '*' => {
438
- if self.peek() == Some('*') { self.advance(); TokenKind::StarStar }
439
- else if self.peek() == Some('=') { self.advance(); TokenKind::StarAssign }
440
- else { TokenKind::Star }
533
+ if self.peek() == Some('*') {
534
+ self.advance();
535
+ TokenKind::StarStar
536
+ } else if self.peek() == Some('=') {
537
+ self.advance();
538
+ TokenKind::StarAssign
539
+ } else {
540
+ TokenKind::Star
541
+ }
441
542
  }
442
543
  '/' => {
443
- if self.peek() == Some('/') { self.advance(); self.skip_line_comment(); return self.next_token(); }
444
- else if self.peek() == Some('*') { self.advance(); self.skip_block_comment()?; return self.next_token(); }
445
- else if self.peek() == Some('=') { self.advance(); TokenKind::SlashAssign }
446
- else {
447
- if self.jsx_in_opening_tag { self.jsx_saw_slash_before_gt = true; }
544
+ if self.peek() == Some('/') {
545
+ self.advance();
546
+ self.skip_line_comment();
547
+ // `skip_line_comment` consumes the newline via `advance()`, which sets
548
+ // `at_line_start` before we would normally run `skip_whitespace()`. Without
549
+ // stripping the next line's leading spaces here, `read_indent_level` would see
550
+ // physical indentation and emit a spurious `Indent` (breaks e.g. object
551
+ // literals with trailing `//` comments). Newlines handled in `skip_whitespace`
552
+ // eat those spaces before the indent pass; match that behavior.
553
+ self.skip_whitespace();
554
+ return self.next_token();
555
+ } else if self.peek() == Some('*') {
556
+ self.advance();
557
+ self.skip_block_comment()?;
558
+ return self.next_token();
559
+ } else if self.peek() == Some('=') {
560
+ self.advance();
561
+ TokenKind::SlashAssign
562
+ } else {
563
+ if self.jsx_in_opening_tag {
564
+ self.jsx_saw_slash_before_gt = true;
565
+ }
448
566
  TokenKind::Slash
449
567
  }
450
568
  }
451
569
  '%' => {
452
- if self.peek() == Some('=') { self.advance(); TokenKind::PercentAssign }
453
- else { TokenKind::Percent }
570
+ if self.peek() == Some('=') {
571
+ self.advance();
572
+ TokenKind::PercentAssign
573
+ } else {
574
+ TokenKind::Percent
575
+ }
454
576
  }
455
577
  '&' => {
456
578
  if self.peek() == Some('&') {
457
579
  self.advance();
458
- if self.peek() == Some('=') { self.advance(); TokenKind::AndAndAssign }
459
- else { TokenKind::And }
460
- } else { TokenKind::BitAnd }
580
+ if self.peek() == Some('=') {
581
+ self.advance();
582
+ TokenKind::AndAndAssign
583
+ } else {
584
+ TokenKind::And
585
+ }
586
+ } else {
587
+ TokenKind::BitAnd
588
+ }
461
589
  }
462
590
  '|' => {
463
591
  if self.peek() == Some('|') {
464
592
  self.advance();
465
- if self.peek() == Some('=') { self.advance(); TokenKind::OrOrAssign }
466
- else { TokenKind::Or }
467
- } else { TokenKind::BitOr }
593
+ if self.peek() == Some('=') {
594
+ self.advance();
595
+ TokenKind::OrOrAssign
596
+ } else {
597
+ TokenKind::Or
598
+ }
599
+ } else {
600
+ TokenKind::BitOr
601
+ }
468
602
  }
469
603
  '?' => {
470
604
  if self.peek() == Some('?') {
471
605
  self.advance();
472
- if self.peek() == Some('=') { self.advance(); TokenKind::NullishAssign }
473
- else { TokenKind::NullishCoalesce }
474
- } else if self.peek() == Some('.') { self.advance(); TokenKind::OptionalChain }
475
- else { TokenKind::Question }
606
+ if self.peek() == Some('=') {
607
+ self.advance();
608
+ TokenKind::NullishAssign
609
+ } else {
610
+ TokenKind::NullishCoalesce
611
+ }
612
+ } else if self.peek() == Some('.') {
613
+ self.advance();
614
+ TokenKind::OptionalChain
615
+ } else {
616
+ TokenKind::Question
617
+ }
476
618
  }
477
619
  ':' => TokenKind::Colon,
478
620
  '"' | '\'' => {
479
621
  let s = self.read_string(c)?;
480
622
  let end = self.span_start();
481
- return Ok(Some(Token { kind: TokenKind::String, span: Span { start, end }, literal: Some(s.into()) }));
623
+ return Ok(Some(Token {
624
+ kind: TokenKind::String,
625
+ span: Span { start, end },
626
+ literal: Some(s.into()),
627
+ }));
482
628
  }
483
629
  '`' => return self.read_template(start, false),
484
630
  '0'..='9' => {
485
631
  let num = self.read_number(c);
486
632
  let end = self.span_start();
487
- return Ok(Some(Token { kind: TokenKind::Number, span: Span { start, end }, literal: Some(num.into()) }));
633
+ return Ok(Some(Token {
634
+ kind: TokenKind::Number,
635
+ span: Span { start, end },
636
+ literal: Some(num.into()),
637
+ }));
488
638
  }
489
639
  'a'..='z' | 'A'..='Z' | '_' => {
490
640
  let ident = self.read_ident_or_keyword(c);
@@ -493,15 +643,23 @@ impl<'a> Lexer<'a> {
493
643
  return Ok(Some(Token {
494
644
  kind,
495
645
  span: Span { start, end },
496
- literal: if matches!(kind, TokenKind::Ident) { Some(ident.into()) } else { None },
646
+ // Spelling is useful for keywords too (e.g. object keys, type names like `type`).
647
+ literal: Some(ident.into()),
497
648
  }));
498
649
  }
499
- '\n' => { self.at_line_start = true; return self.next_token(); }
650
+ '\n' => {
651
+ self.at_line_start = true;
652
+ return self.next_token();
653
+ }
500
654
  _ => return Err(format!("Unexpected character: {:?}", c)),
501
655
  };
502
656
 
503
657
  let end = self.span_start();
504
- Ok(Some(Token { kind, span: Span { start, end }, literal: None }))
658
+ Ok(Some(Token {
659
+ kind,
660
+ span: Span { start, end },
661
+ literal: None,
662
+ }))
505
663
  }
506
664
  }
507
665
 
@@ -539,4 +697,15 @@ mod tests {
539
697
  let string_tok = tokens.iter().find(|t| t.kind == TokenKind::String).unwrap();
540
698
  assert_eq!(string_tok.literal.as_deref(), Some("H"));
541
699
  }
700
+
701
+ #[test]
702
+ fn line_comment_does_not_emit_spurious_indent_before_next_line() {
703
+ let with_comment = "fn f() {\n return {\n a: 1, // c\n b: 2\n }\n}\n";
704
+ let tokens: Vec<_> = Lexer::new(with_comment).collect::<Result<Vec<_>, _>>().unwrap();
705
+ assert!(
706
+ !tokens.iter().any(|t| t.kind == TokenKind::Indent),
707
+ "unexpected Indent after line comment: {:?}",
708
+ tokens.iter().map(|t| format!("{:?}", t.kind)).collect::<Vec<_>>()
709
+ );
710
+ }
542
711
  }
@@ -57,6 +57,8 @@ pub enum TokenKind {
57
57
  New,
58
58
  Import,
59
59
  Export,
60
+ Type,
61
+ Declare,
60
62
 
61
63
  // Punctuation
62
64
  LParen,
@@ -110,14 +112,14 @@ pub enum TokenKind {
110
112
  NullishCoalesce,
111
113
  Question,
112
114
  Arrow,
113
-
115
+
114
116
  // Template literal tokens
115
- TemplateNoSub, // `text` (no interpolation)
116
- TemplateHead, // `text${ (start with interpolation)
117
- TemplateMiddle, // }text${ (middle part)
118
- TemplateTail, // }text` (end part)
117
+ TemplateNoSub, // `text` (no interpolation)
118
+ TemplateHead, // `text${ (start with interpolation)
119
+ TemplateMiddle, // }text${ (middle part)
120
+ TemplateTail, // }text` (end part)
119
121
 
120
- JsxText, // Raw text in JSX children (emojis, etc.); only {}<> are special
122
+ JsxText, // Raw text in JSX children (emojis, etc.); only {}<> are special
121
123
  }
122
124
 
123
125
  impl TokenKind {
@@ -153,6 +155,8 @@ impl TokenKind {
153
155
  "new" => TokenKind::New,
154
156
  "import" => TokenKind::Import,
155
157
  "export" => TokenKind::Export,
158
+ "type" => TokenKind::Type,
159
+ "declare" => TokenKind::Declare,
156
160
  _ => TokenKind::Ident,
157
161
  }
158
162
  }
@@ -17,10 +17,17 @@ pub fn compile_to_native(
17
17
  project_root: Option<&Path>,
18
18
  output_path: &Path,
19
19
  ) -> Result<(), LlvmError> {
20
- let modules = resolve_project(entry_path, project_root)
21
- .map_err(|e| LlvmError { message: e.to_string() })?;
22
- detect_cycles(&modules).map_err(|e| LlvmError { message: e.to_string() })?;
23
- let program = merge_modules(modules).map_err(|e| LlvmError { message: e.to_string() })?;
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
+ })?;
24
31
  let chunk = tishlang_bytecode::compile(&program).map_err(|e| LlvmError {
25
32
  message: e.to_string(),
26
33
  })?;
@@ -71,7 +78,10 @@ const uint64_t tish_chunk_len = sizeof(tish_chunk_data);
71
78
  .arg(&chunk_c_path)
72
79
  .status()
73
80
  .map_err(|e| LlvmError {
74
- message: format!("Failed to run clang: {}. Install clang (LLVM) or set CLANG env var.", e),
81
+ message: format!(
82
+ "Failed to run clang: {}. Install clang (LLVM) or set CLANG env var.",
83
+ e
84
+ ),
75
85
  })?;
76
86
 
77
87
  if !status.success() {
@@ -84,9 +94,8 @@ const uint64_t tish_chunk_len = sizeof(tish_chunk_data);
84
94
  let object_path_canonical = object_path.canonicalize().map_err(|e| LlvmError {
85
95
  message: format!("Cannot canonicalize object path: {}", e),
86
96
  })?;
87
- tishlang_cranelift::link_to_binary(&object_path_canonical, output_path, features).map_err(|e| LlvmError {
88
- message: e.message,
89
- })?;
97
+ tishlang_cranelift::link_to_binary(&object_path_canonical, output_path, features)
98
+ .map_err(|e| LlvmError { message: e.message })?;
90
99
 
91
100
  Ok(())
92
101
  }
@@ -1,6 +1,6 @@
1
1
  [package]
2
2
  name = "tishlang_lsp"
3
- version = "0.1.0"
3
+ version = "0.1.1"
4
4
  edition = "2021"
5
5
  description = "Language Server Protocol implementation for Tish"
6
6
  license-file = { workspace = true }
@@ -12,9 +12,12 @@ path = "src/main.rs"
12
12
 
13
13
  [dependencies]
14
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" }
15
17
  tishlang_parser = { path = "../tish_parser", version = ">=0.1" }
16
18
  tishlang_fmt = { path = "../tish_fmt", version = ">=0.1" }
17
19
  tishlang_lint = { path = "../tish_lint", version = ">=0.1" }
20
+ tishlang_resolve = { path = "../tish_resolve", version = ">=0.1" }
18
21
  tower-lsp = "0.20"
19
22
  tokio = { version = "1", features = ["rt-multi-thread", "macros", "io-std"] }
20
23
  serde_json = "1"
@@ -14,7 +14,7 @@ Binary: `target/release/tish-lsp` (stdio LSP).
14
14
 
15
15
  - Parse diagnostics + lint warnings (via `tish_lint` **library** — use **`tish-lint`** CLI separately in CI)
16
16
  - Document symbols, completion, formatting (via `tish_fmt` **library** — use **`tish-fmt`** CLI separately in CI)
17
- - Go to definition (same file + relative imports)
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
18
  - Workspace symbol search (`**/*.tish`)
19
19
 
20
20
  ## Client configuration