fix(ios): app paths and Alpine exec; feat(lsp): project-wide TypeScript worker with progress - #11
Merged
deadlyjack merged 5 commits intoOct 3, 2026
Conversation
…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.
…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.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
iOS paths
FileService.paths()appended/toFileManagerdirectory URLs that already end in/, so JS received.../Library/NoCloud//,.../Caches//,.../Documents//andrunner.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.jsinstead of the container path./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.src/lib/iosGuestPath.js) uses them as-is: names containing#,%or a literal%20reach the right folder. Alpine URLs are decoded exactly once per segment.Alpine exec (vendored ios-linuxkit)
Assertion failed: errno == EINTR (uv__io_poll). Its Node launcherexecves from the main thread while V8 worker threads wait inepoll_wait; the emulator ran close-on-exec on the fd table still shared with those threads and closed their epoll fd.__do_execvenow gives the exec'ing task a private copy of the fd table before close-on-exec, like Linuxunshare_files(). The swap happens underpids_lock; copying and releasing happen outside it. Upstream ios-linuxkit has the same bug.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.
tsconfig.json/jsconfig.jsonare honoured (include/exclude,extends,paths); without one, the folder's source files form an inferred project.readDirectoryand size-cappedreadFilehost requests), and the program is rebuilt until imports, includingnode_modulestypes, resolve.file:folders keep their real path, so configs andnode_modulesabove the folder resolve through readable parent folders, for exampleextends: "../tsconfig.base.json"or hoisted dependencies. Other providers, such as Androidcontent://storage, get isolated roots, and their paths always come from the provider's own listings.allowJskeep single-file analysis.tsconfiginclude enumeration; files over 2 MB are skipped. Configuration errors that persist after loading, such as an unreadableextends, are written to the LSP log.initialized, and the worker transport acknowledgeswindow/workDoneProgress/createlike the WebSocket transport does. Project loading shows "Loading " with the number of scanned folders and files.src/cm/lsp/workers/typescript/.