@tishlang/tish-lsp 3.2.2 → 3.3.3

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.
@@ -1,6 +1,6 @@
1
1
  [package]
2
2
  name = "tishlang_lsp"
3
- version = "3.2.2"
3
+ version = "3.3.3"
4
4
  edition = "2021"
5
5
  description = "Language Server Protocol implementation for Tish"
6
6
  license-file = { workspace = true }
@@ -1,7 +1,7 @@
1
1
  //! Build native binary via cargo (interim path until Cranelift backend is ready).
2
2
 
3
3
  use std::fs;
4
- use std::path::Path;
4
+ use std::path::{Path, PathBuf};
5
5
 
6
6
  use tishlang_compile::ResolvedNativeModule;
7
7
 
@@ -627,15 +627,34 @@ runner = ["mgba-qt", "-C", "logToStdout=1", "-C", "logLevel.gba.debug=127"]
627
627
  // with `-Tgba.ld` — cargo *joins* rustflags arrays across config files, so a
628
628
  // config-file approach would pass `-Tgba.ld` twice ("region already defined").
629
629
  // build-std still comes from the generated `[unstable]` config (not rustflags).
630
- let status = Command::new("cargo")
630
+ //
631
+ // TISH_GBA_TARGET_DIR (opt-in, explicit — deliberately NOT the ambient
632
+ // CARGO_TARGET_DIR, which the desktop path scrubs for good reason): share ONE
633
+ // cargo target dir across every example's ROM build. Each example otherwise
634
+ // carries a full private dependency tree — measured at ~45 GB across the
635
+ // tish-gba fleet, and it filled the disk mid-build once. An inherited
636
+ // CARGO_TARGET_DIR meanwhile made cargo write where the ELF lookup below
637
+ // never looked ("GBA ELF not found" on a SUCCESSFUL compile), so that env is
638
+ // scrubbed here and only the explicit opt-in steers both paths together.
639
+ let shared_target = std::env::var_os("TISH_GBA_TARGET_DIR").map(PathBuf::from);
640
+ let target_root = shared_target
641
+ .clone()
642
+ .unwrap_or_else(|| build_dir.join("target"));
643
+ let mut cargo = Command::new("cargo");
644
+ cargo
631
645
  .arg("build")
632
646
  .arg("--release")
633
647
  .current_dir(&build_dir)
648
+ .env_remove("CARGO_TARGET_DIR")
634
649
  .env(
635
650
  "RUSTFLAGS",
636
651
  "-Clink-arg=-Tgba.ld -Ctarget-cpu=arm7tdmi -Cforce-frame-pointers=yes",
637
652
  )
638
- .env("CARGO_TERM_COLOR", "always")
653
+ .env("CARGO_TERM_COLOR", "always");
654
+ if let Some(dir) = &shared_target {
655
+ cargo.env("CARGO_TARGET_DIR", dir);
656
+ }
657
+ let status = cargo
639
658
  .status()
640
659
  .map_err(|e| format!("Failed to run cargo for GBA build: {}", e))?;
641
660
  if !status.success() {
@@ -645,9 +664,8 @@ runner = ["mgba-qt", "-C", "logToStdout=1", "-C", "logLevel.gba.debug=127"]
645
664
  ));
646
665
  }
647
666
 
648
- // ELF → .gba via agb-gbafix.
649
- let elf = build_dir
650
- .join("target")
667
+ // ELF → .gba via agb-gbafix. Resolved from the SAME root the build wrote to.
668
+ let elf = target_root
651
669
  .join("thumbv4t-none-eabi")
652
670
  .join("release")
653
671
  .join(&cargo_name);
@@ -101,21 +101,41 @@ type BgAsset = (
101
101
  &'static agb::display::tile_data::TileData,
102
102
  );
103
103
 
104
- static ASSET_BGS: SingleCore<RefCell<Vec<BgAsset>>> = SingleCore::new(RefCell::new(Vec::new()));
104
+ /// ⚠️ A FIXED TABLE, not a `Vec`, because every entry is `&'static` cartridge data and the registry
105
+ /// never needed to own one.
106
+ ///
107
+ /// As a `Vec` it did: registering N backgrounds ran N pushes with log2(N) reallocs, all during
108
+ /// module init before the program body, and the freed buffers left the heap too fragmented for the
109
+ /// one big contiguous block a GBA UI canvas wants. A game with a handful of backgrounds booted; a
110
+ /// game with 162 died in the allocator with plenty of total heap free.
111
+ ///
112
+ /// This costs `MAX_BGS * size_of::<Option<BgAsset>>()` of STATIC memory and allocates nothing.
113
+ /// ⚠️ Not a `Vec` with `reserve` — that charges every small game for the largest one.
114
+ const MAX_BGS: usize = 256;
115
+ static ASSET_BGS: SingleCore<RefCell<[Option<BgAsset>; MAX_BGS]>> =
116
+ SingleCore::new(RefCell::new([None; MAX_BGS]));
117
+ static ASSET_BGS_N: SingleCore<RefCell<usize>> = SingleCore::new(RefCell::new(0));
105
118
 
106
119
  /// Register a background (palettes + tile data), returning its i32 handle.
107
120
  pub fn __asset_register_bg(bg: BgAsset) -> i32 {
108
- ASSET_BGS.with(|c| {
109
- let mut v = c.borrow_mut();
110
- let idx = v.len() as i32;
111
- v.push(bg);
121
+ ASSET_BGS_N.with(|n| {
122
+ let mut n = n.borrow_mut();
123
+ if *n >= MAX_BGS {
124
+ return -1;
125
+ }
126
+ let idx = *n as i32;
127
+ ASSET_BGS.with(|c| c.borrow_mut()[*n] = Some(bg));
128
+ *n += 1;
112
129
  idx
113
130
  })
114
131
  }
115
132
 
116
133
  /// Look up a registered background by handle. `None` if out of range.
117
134
  pub fn asset_bg(handle: i32) -> Option<BgAsset> {
118
- ASSET_BGS.with(|c| c.borrow().get(handle as usize).copied())
135
+ if handle < 0 || handle as usize >= MAX_BGS {
136
+ return None;
137
+ }
138
+ ASSET_BGS.with(|c| c.borrow()[handle as usize])
119
139
  }
120
140
 
121
141
  /// Registered fonts (`font<N>:` imports) — a compile-time-baked `Font`. All `font<N>:` schemes share
@@ -283,6 +283,35 @@ pub fn str_index(s: &str, idx: &Value) -> Value {
283
283
  }
284
284
  }
285
285
 
286
+ /// `&str` charCodeAt — the code unit at `idx`, NaN when out of range. Mirrors
287
+ /// `tish_runtime::str_char_code_at`, and missing here for the same reason `str_index` was: codegen
288
+ /// emits it whenever the receiver is statically a `&str`, so `"abc".charCodeAt(i)` was a hard E0425
289
+ /// on the GBA target only while compiling everywhere else. Found by a ROM that hashes its own trace.
290
+ #[inline]
291
+ pub fn str_char_code_at(s: &str, idx: &Value) -> Value {
292
+ let i = match idx {
293
+ Value::Number(n) if *n >= 0.0 && n.fract() == 0.0 => *n as usize,
294
+ _ => return Value::Number(f64::NAN),
295
+ };
296
+ match s.chars().nth(i) {
297
+ Some(c) => Value::Number(c as u32 as f64),
298
+ None => Value::Number(f64::NAN),
299
+ }
300
+ }
301
+
302
+ /// `&str` charAt — the 1-character string at `idx`, `""` out of range. Same reason as above.
303
+ #[inline]
304
+ pub fn str_char_at(s: &str, idx: &Value) -> Value {
305
+ let i = match idx {
306
+ Value::Number(n) if *n >= 0.0 && n.fract() == 0.0 => *n as usize,
307
+ _ => return Value::String("".into()),
308
+ };
309
+ match s.chars().nth(i) {
310
+ Some(c) => Value::String(c.to_string().into()),
311
+ None => Value::String("".into()),
312
+ }
313
+ }
314
+
286
315
  pub fn delete_property(obj: &Value, key: &Value) -> Value {
287
316
  match obj {
288
317
  Value::Object(m) => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tishlang/tish-lsp",
3
- "version": "3.2.2",
3
+ "version": "3.3.3",
4
4
  "description": "Tish language server — diagnostics, completion, formatting, hover, go-to-definition (LSP over stdio)",
5
5
  "license": "PIF",
6
6
  "repository": {
Binary file
Binary file
Binary file
Binary file
Binary file