Repository navigation
Conversation
pgls_statement_splitter::split_edited re-splits the statements around the changed part of the text and keeps the others, with the same result as split. It falls back to a full split when the change touches a quote, a comment, a dollar quote or a psql command, or when the statements at the edges of the region don't match the previous split. The document keeps its SplitResult for the next change. Suppressions skip the lines of a document without suppression comments, and LineIndex looks lines up with a binary search.
This was referenced Oct 26, 2026
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 #1006. Part of the rebuild described in #997. Replaces #964 (the fix from #963 it carried is on
mainnow).Every
change_filesplit the whole document again (lexer, splitter, and the blank-line recovery pass) and scanned every line for suppression comments. That is linear in the file and runs on every keystroke:change_file, one keystroke in the middle of the fileMeasured with a throwaway example (not committed) that types and deletes a character in a table name in the middle of a migration-style file, 200 times, through
WorkspaceServer::change_file.Incremental split
pgls_statement_splitter::split_edited(old, previous, new)returns the same result assplit(new):Find the change. The common prefix and suffix of the old and new text give the changed part, compared in 64-byte blocks. Several edits at once become one changed part.
Re-split a region. The region runs from
EDIT_CONTEXT_STATEMENTS = 2statements before the change to as many after it, split on its own withsplit. The statements before the region are kept, and the ones after it are shifted by the change in length. Errors are kept and shifted the same way, lexer errors first and then splitter errors, assplitorders them.Check the edges. The two statements at each edge of the region have to come out exactly as in the previous split. That shows the region lines up with the statements around it:
look_back,look_ahead);The first and the last statement of the region, the ones that are split without their real neighbours (no
look_backat the start, the end of the region instead of the next statement), are taken from the previous split as well. Without that, an error that a statement reports at the end of the text (Expected FROM_KWondeletefollowed by EOF) leaked into the result.Fall back to a full split when the change, or one byte on either side of it, touches
$ ' " / * - \. Those can open or close a string, a dollar quote, a comment or a psql command, and so change how everything after them is lexed. A full split is also used when the edge statements don't match.The
Documentkeeps theSplitResultof its content and passes it tosplit_editedinupdate_content. TheContentChangeof #1005 is computed from the previous and the new ranges as before, so the walk's caches keep what comes before the change.SplitResultderivesClone/PartialEqand records how many of its errors come from the lexer.Suppressions
Suppressions::fromnow returns early when the text doesn't contain-ignore. Every suppression comment contains it, and the check is a single substring search. Without line suppressions the line index isn't needed, so it isn't built either. This took 2.5 ms of the remaining 3 ms at 20,000 statements. With suppressions in the file, the parser still runs over all lines as before.One small fix in the same code:
LineIndex::line_for_offsetwas a linear scan over all lines. It is a binary search now, which matters when a file with suppressions has many diagnostics.Tests
split_edited_is_split: 3,000 random edits over 10 documents built from statements the splitter treats differently:$$andBEGIN ATOMICfunctions, comments, quoted;, blank lines inside statements, statements without;, invalid SQL. The edits insert, delete or replace text, including fragments that cut or merge statements (;,\n\n,select,begin atomic,',$$,/*,--,\d), sometimes two at once. After each edit the result must equalsplit(new), ranges and errors included. Each edit starts from the previous incremental result, so errors would accumulate.SPLIT_SEEDandSPLIT_EDITSchange the run; locally, 12 seeds × 50,000 edits passed in release. About 43% of these edits are split incrementally; the rest touch a quote or comment and fall back. Normal typing (letters, spaces,;, parentheses) is incremental.updates_split_like_the_whole_document(workspace): 1,000 edits throughDocument::update_content. After each, the document's split and syntax diagnostics equal those of a full split.documents_without_suppressions_skip_the_lines: the fast path gives the same suppressions as parsing the lines.Not covered
update_contentstill hashes the text of every statement, about 0.3 ms at 20,000 statements in release. It could use the changed region too.Update: This is #964 rebuilt on the new stack. With the whole stack, the benchmark of #1005 (release, 20 keystrokes) measures
change_fileat 2.7 to 2.9 ms per keystroke at 5,000 statements and 12 to 16 ms at 20,000, against 7 to 9 ms and 41 to 59 ms onmain. What is left at 20,000 statements is mostly the pruning of the statement caches (about 10 ms), which hashes the text of every statement; it could use the changed region instead.The rest of this update: The splitter and suppression changes are the same;
mainalready skips the lines of a diagnostic when there are no line suppressions, so that fix is gone.