Skip to content

fix(ios): app paths and Alpine exec; feat(lsp): project-wide TypeScript worker with progress - #11

Merged
deadlyjack merged 5 commits into
Acode-Foundation:mainfrom
bajrangCoder:fix/ios-paths-and-alpine-exec
Oct 3, 2026
Merged

deadlyjack merged 5 commits into
Acode-Foundation:mainfrom
bajrangCoder:fix/ios-paths-and-alpine-exec

Conversation

@bajrangCoder

@bajrangCoder bajrangCoder commented Oct 1, 2026 •

Copy link
Copy Markdown
Member

Summary

  • iOS paths — normalized app directory URLs, readable path names in the UI, and "Open in Terminal" on iOS.
  • Alpine exec — fixes an emulator bug that crashed Node-based language servers (TypeScript 7) on start.
  • Project-wide TypeScript worker — the built-in TypeScript/JavaScript language server now understands whole projects, adds go to implementation and type definition, and shows loading progress.

iOS paths

  • FileService.paths() appended / to FileManager directory URLs that already end in /, so JS received .../Library/NoCloud//, .../Caches//, .../Documents// and runner.app//. File entries resolve to single-slash URLs, so prefix checks against these roots never matched. Directory URLs now get exactly one trailing slash.
  • helpers.getVirtualPath() also aliases the built-in roots ("Terminal Public" and the iOS "Acode" Documents folder) and picks the longest matching root. File Info, tab subtitles, recents, find-file and the LSP panel now show e.g. Terminal Public/Acode/src/main.js instead of the container path.
  • "Open in Terminal" was gated on Android's /data/user/0/<pkg>/files/public. On iOS every app root is bind-mounted into Alpine at its host path, so it is offered for any local or Alpine folder, with Terminal Public mapped to /public.
  • Folder URLs from the internal filesystem are already decoded, so the iOS terminal path conversion (src/lib/iosGuestPath.js) uses them as-is: names containing #, % or a literal %20 reach the right folder. Alpine URLs are decoded exactly once per segment.

Alpine exec (vendored ios-linuxkit)

  • The TypeScript 7 server crashed with Assertion failed: errno == EINTR (uv__io_poll). Its Node launcher execves from the main thread while V8 worker threads wait in epoll_wait; the emulator ran close-on-exec on the fd table still shared with those threads and closed their epoll fd.
  • __do_execve now gives the exec'ing task a private copy of the fd table before close-on-exec, like Linux unshare_files(). The swap happens under pids_lock; copying and releasing happen outside it. Upstream ios-linuxkit has the same bug.
  • Linux also stops sibling threads on exec (de_thread); the emulator still does not, so the old threads linger until the new program exits. Left as a follow-up.

Project-wide TypeScript worker

Previously the worker only saw open tabs plus the bundled lib files.

  • Projects. Each listable workspace folder becomes a TypeScript project. tsconfig.json / jsconfig.json are honoured (include/exclude, extends, paths); without one, the folder's source files form an inferred project.
  • File access. The compiler reads from a virtual file system backed by the app's file system. Unknown files and folders load in the background (new readDirectory and size-capped readFile host requests), and the program is rebuilt until imports, including node_modules types, resolve.
  • Paths. file: folders keep their real path, so configs and node_modules above the folder resolve through readable parent folders, for example extends: "../tsconfig.base.json" or hoisted dependencies. Other providers, such as Android content:// storage, get isolated roots, and their paths always come from the provider's own listings.
  • New and changed files. Files created after loading join their project when opened: the nearest known folder is listed again. Closed tabs are re-read from disk, and cached text can never be mistaken for newer content.
  • Cross-file results. Definition, references, rename, code actions, and the new go to implementation and go to type definition return locations in unopened files.
  • Fallback. Files outside any folder, unsaved files, FTP/SFTP folders, and JavaScript in a project without allowJs keep single-file analysis.
  • Limits. Up to 1,500 folders and 3,000 source files are scanned per project, including tsconfig include enumeration; files over 2 MB are skipped. Configuration errors that persist after loading, such as an unreadable extends, are written to the LSP log.
  • Progress. Workers can create standard work-done progress tokens after initialized, and the worker transport acknowledges window/workDoneProgress/create like the WebSocket transport does. Project loading shows "Loading " with the number of scanned folders and files.
  • Structure. The single 993-line worker is now a set of focused modules under src/cm/lsp/workers/typescript/.
  • Size. The worker's own code grows from about 17 KB to 26 KB minified (6 KB to 9 KB gzipped); the bundled TypeScript compiler dominates the file and is unchanged.

bajrangCoder and others added 3 commits October 1, 2026 23:58
…ng fds

- FileService: stop appending a second "/" to FileManager directory URLs, so
  dataDirectory/cacheDirectory/documentsDirectory/applicationDirectory match
  the single-slash URLs that file entries resolve to.
- getVirtualPath: alias the built-in Terminal Public and iOS Documents roots
  (longest match wins), so File Info, tab titles, recents and the LSP panel
  show short paths instead of the raw sandbox container path.
- openFolder: offer "Open in Terminal" on iOS and map Terminal Public to
  /public inside Alpine.
- ios-linuxkit exec: give the exec'ing task a private fd table before
  close-on-exec, like Linux unshare_files(). Node's tsc launcher execs while
  V8 worker threads wait in epoll_wait on a close-on-exec fd; closing it under
  them made libuv abort with "Assertion failed: errno == EINTR".

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The built-in TypeScript worker only saw open tabs and bundled lib files.
It now builds a project for every listable workspace folder:

- Reads tsconfig.json/jsconfig.json (include/exclude, extends, paths) or
  infers a project from the folder's source files.
- Serves the compiler from a virtual file system backed by the host;
  unknown files and directories load in the background and the program
  is rebuilt until imports, including node_modules types, resolve.
- Definitions, references, rename, code actions, implementations and
  type definitions return locations in unopened files.
- Files outside any folder, unsaved files and FTP/SFTP folders keep the
  previous single-file analysis.

Adds go to implementation/type definition, and standard LSP work-done
progress: workers can create tokens after `initialized`, and the worker
transport acknowledges window/workDoneProgress/create like the WebSocket
transport. Project loading reports scanned folder and file counts.
@bajrangCoder bajrangCoder changed the title fix(ios): normalize app paths and stop Alpine exec from crashing Node LSPs fix(ios): app paths and Alpine exec; feat(lsp): project-wide TypeScript worker with progress Oct 1, 2026
…dings

- Keep per-file versions increasing across invalidation, so result
  positions are never computed from text that predates a reload. Closing
  an edited file no longer makes definitions and rename point at stale
  offsets.
- Locate newly opened files through fresh listings: re-list the nearest
  known folder and walk down. Paths still come from provider listings,
  so opaque content:// URIs map correctly.
- Give file: folders their real path, so `extends` and node_modules above
  the folder resolve through readable parents; other providers keep
  isolated roots. Persisting config errors are logged.
- Enumerate tsconfig includes only over folders the budgeted crawl
  listed, and reuse in-flight listings instead of loading twice.
- Claim documents only when the project's options accept them, so
  JavaScript in a project without allowJs uses single-file analysis.
- Convert iOS terminal folders without decoding already-decoded file
  URLs; decode Alpine URLs exactly once per segment.
- Rebuild inferred projects' file lists from current folder listings on
  every refresh, so files created after loading stay in references and
  rename after their tabs close.
- Accept JavaScript with TypeScript's effective allowJs, so checkJs alone
  enables it while an explicit allowJs: false still excludes it.
- Assign files to the deepest owning workspace folder regardless of the
  order folders were added.
- Keep decoded file: folder names as-is in progress labels and decode
  other providers' names safely, so names with "%" no longer abort loading.
@deadlyjack
deadlyjack merged commit 44e71a4 into Acode-Foundation:main Oct 3, 2026
6 of 11 checks passed
@bajrangCoder
bajrangCoder deleted the fix/ios-paths-and-alpine-exec branch October 4, 2026 02:56
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants