Repository navigation
Conversation
Hover, completions and inlay hints walked every statement before the cursor after each change, mapping and applying all of them to the catalog. The walk now records its state at the top-level statement of the request and at the statement where the last change starts, and the next walk resumes from the last checkpoint before its statement. A checkpoint stays valid while the text before it and the statements that start before it don't change, and while the settings and the database snapshot are the same. A section marker inserted right before it moves it into another section, so it is checked against the current sections too. A document keeps at most four checkpoints, and drops them with its other caches.
This was referenced Oct 25, 2026
Collaborator
Author
|
Superseded by #1005 (rebuild of the document caches and walk). |
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 #977.
Hover, completions and inlay hints walk the statements before the cursor so that the catalog follows the file (#974). They did that from the start of the file after every change: they mapped each statement (parse cache lookup, a lexer pass for identifier parameters, copying the text) and applied it to the catalog. In a 5,000-statement file that took about 45 ms per keystroke for inlay hints, and 245 ms at 20,000 statements.
The state of the walk before a top-level statement only depends on the text before it, plus the database snapshot and the settings. An edit leaves that state valid for every statement before the edit, so the walk can resume from there.
What changed
pgls_analyser::walk_statementscan resumefromaWalkState, and records the states at thecheckpointsyou pass it. AWalkStateholds the unit's catalog and session, the catalog of the up sections when it is in a down section, and the start of its section.WorkspaceServer::walk_statementstakes the start of the statements that the request visits. It records two checkpoints: one at the request's top-level statement, and one at the statement where the last change of the document starts (Document::edited_at). The next keystroke in that statement keeps the second one, also when the request is further down. The next walk resumes from the last valid checkpoint before its statement, and only maps the statements from there on (Document::iter_from).Document::update_contentdoesn't drop it. It keeps a checkpoint while the text before it is unchanged and the statements that start before it have the same ranges. The splitter looks ahead, so an edit after a statement can change where that statement ends. That is why the ranges are compared too.pgls_statement_splitter::common_prefixis now public for this.CatalogBasehaven't changed;WalkState::fits). A marker typed right before the checkpoint's statement starts a new section there, even though the text before the checkpoint is the same.clear_cachesdrops them, so theCacheBudgeteviction from perf(workspace): bound the memory of the editor walk cache #953 covers them too.DocumentCacheStatsreports them.When the walk resumes, the
AnalysedFileContextit hands to the visitor only knows the statements from the checkpoint on (previous_stmts,stmt_count). The workspace visitors don't use those. The analyser always walks from the start, so lint is unchanged.Numbers
Release build, a dbmate file of tables, indexes, SQL functions and queries. Each "keystroke" changes a name in one statement, followed by hover, completions and inlay hints in the requested group. Averages over 20 keystrokes. #977 → this PR:
CREATE TABLE3 statements aboveThe machine is shared, so expect about ±15% noise. The last case is the same walk as before, plus recording two checkpoints.
What the checkpoints don't fix:
Catalog::snapshot(), which builds theSnapshotthatpgls_hoverreads. At 20,000 statements that took 630 ms of the 744 ms.materialize.rsdoes atables.iter().findand acolumns.retainover all columns for each relation the file defines, which is quadratic in the number of tables. When you type inside the hovered statement, the snapshot comes from thesnapshot_beforecache, which is why that case is fast. A linear materialization would be a separate change inpgls_catalog.get_statement_for_completions:Document::count()maps every statement, and theGetCompletionsFilterpass visits every statement.Catalogwould make both cheap.Memory, release, test defaults:
memory_boundsone document retained 25.3 → 26.0 MiB, all open retained 220.7 → 228.1 MiB, peak 242.4 → 249.8 MiB.edit_churn32.9/36.1 → 33.5/36.6 MiB with 3 checkpoints at the end. Both pass unchanged.Tests
walks_resume_from_checkpoints_before_the_editcounts the statements each request walks. A cfg(test) counter onWorkspaceServerdoes the counting. It checks these cases:CREATE TABLEdrops the checkpoints, and completions and hover then see the new column;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. It compares hover and inlay hints against the same text opened fresh, with requests from the end of the file backwards. If I disable dropping stale checkpoints, it fails. If I disablefits, the walker'sdebug_assertfails.pgls_workspace(including the memory and edit-churn tests),pgls_analyser,pgls_statement_splitterand clippy pass.