Repository navigation
Conversation
The walk of the editor features resumes from checkpoints that the document keeps (pgls_analyser::WalkState), at the statement of the last request and of the last edit, and the references it resolves are kept per top-level statement, with the snapshot that hover and completions last built. All of it lives in the document's cache under its CacheKey, and an edit keeps what comes before it. Inlay hints and semantic tokens are computed from the kept references, and a request whose statements are all resolved doesn't walk. Completions find their statement without going over the whole file, and the catalog shares the objects of its clones.
This was referenced Oct 26, 2026
The analysis, the catalog copies of the walk checkpoints and the snapshot of a long file hold every object it defines, and dropping them took 60 to 90 ms of change_file on a 20,000-statement file. update_content returns them, and change_file drops them on a thread of their own after releasing the documents lock. The editor features docs describe the cached walk.
WebAssembly has no threads, and spawning one doesn't link there.
This was referenced Oct 26, 2026
Closed
This branch has not been deployed
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.
Stacked on #1004. Part of the rebuild described in #997 (points 3 and 4). Replaces #930, #982 and #984, and takes the shared catalog objects from #953.
Hover, completions, inlay hints and semantic tokens walked the file from its first statement on every request, and resolved every statement in their range again. On a 20,000-statement file that was seconds per keystroke for each of them, and the editor sends several per keystroke.
The design
The state of the walk before a top-level statement only depends on the text before it, its section, and what the
CacheKeyof #1004 covers (settings, project, database snapshot). The references of a statement also depend on the statement itself. So everything the walk leaves behind is kept in the document's cache (document_cache/walk.rs,WalkCache) under that key, and an edit keeps what comes before it:pgls_analyser::WalkState): the catalog and the session before a top-level statement, with the section it is in.walk_statementscan resumefromone and records new ones at thecheckpointsit is given. A request records two: at its first statement, and at the statement where the last edit starts, so the next keystroke there resumes right before it, also for requests further down. A document keeps at most 4, least recently used out first.inlay_hints_of_references,semantic_tokens_of_references), hover and the expand-*action look them up. A request whose statements are all resolved doesn't walk at all.Catalog::snapshot()is the most expensive part of hover after an edit above the hovered statement.Document::update_contentcomputes aContentChange: the length of the text that is the same, and the start of the first top-level statement that isn't the same after the edit or doesn't end before it (the splitter looks ahead, so a statement before the edit can end elsewhere). One rule then applies to all three: a checkpoint, and the snapshot of a top-level statement, stay if their statement starts in the unchanged text and no statement before it changed; resolved statements, and the snapshot of a function body, also need their statement unchanged. A checkpoint is also only used if its statement is still in the same migration section (WalkState::fits): a marker typed right before a statement moves it into a new section although the text before its old start is the same.The workspace's side is
server/walk.rs:resolved_statements(range),snapshot_before(statement),hover_referencesandreferences_at, on top of onewalkthat picks the checkpoint, records the new ones and counts the statements it walks (in tests). The server-wide one-entrysnapshot_beforecache and its hashing of the text before the statement on every request are gone.Also in this PR:
Document::iter_for_completionsonly filters the top-level statements around the cursor (two binary searches).Entry::Definedholds anArc, so recording a checkpoint copies the catalog's maps but not the tables, types and functions in them, and a change copies only the changed object.Timings
Release build, a dbmate file of tables, indexes, SQL functions and queries, on a shared machine (expect ±20% noise; the "before" run was on
mainat the base of the stack). Each "keystroke" changes a name in one statement, followed by change_file, hover, completions, inlay hints and semantic tokens over the 2 KB before the requested statement. Averages over 20 keystrokes, in ms; diagnostics and code actions over the first 5.CREATE TABLE3 statements above: hover / completions / inlay hints / tokensCREATE TABLEabove: hover / completions / inlay hintsUnchanged file, 20,000 statements: full semantic tokens 5,615 ms the first time and 5,829 ms again on
main; 2,366 ms and 167 ms here. Hover 3,909 ms and 1,675 ms onmain; 1,801 ms and 2.5 ms here.What remains:
Catalog::snapshot(), whose materialization is quadratic in the tables the file defines (materialize.rsdoes afindand aretainover all columns per relation). That is a change inpgls_catalogof its own.pgls_completions::completeitself, which goes over every object the file adds to the snapshot.change_filestill re-splits the whole file; that follows later in the stack.Tests
walks_resume_from_checkpoints_before_the_editcounts the statements each request walks: the first hover walks the file and a repeated one nothing; after an edit in the hovered statement it walks that statement; completions after a new statement resume from the previous checkpoint; an edit to the firstCREATE TABLEdrops everything, and completions and hover then see the new column; the second keystroke in a statement above the request walks two statements; a section marker inserted right before a checkpoint makes the walk skip it; and a document keeps at most 4 checkpoints.walks_from_checkpoints_match_full_walksmakes edits early, in the middle and late in a file with up/down sections, unparsable statements, functions and their calls, and compares hover, inlay hints and semantic tokens with the same text opened fresh, with requests from the end of the file backwards. It fails if the resolved statements or the checkpoints are not dropped on an edit.finds_the_statement_like_a_filter_over_all_statementscompares the completions lookup with filtering all statements, at every position of a document with every case the filter cares about.pgls_workspace,pgls_analyser(rules specs, Postgres regression),pgls_catalog,pgls_inlay_hints,pgls_semantic_tokens,pgls_hoverandpgls_lsppass, and clippy on the whole workspace.Not covered
Update:
update_contentnow returns what an edit invalidated (StaleEntries), andchange_filedrops it on a thread of its own after releasing the documents lock. On a 20,000-statement file, dropping the analysis, the catalog copies of the checkpoints and the snapshot took 60 to 90 ms of every keystroke near the start of the file (measured with a traced build: the split took 0.2 ms, pruning the statement caches 10 ms). The editor features docs now describe the cached walk.