A look behind matches when its pattern ends exactly where the look behind
started. The simulation of the assertion returned as soon as it reached the
accept state, and the caller then compared that one result against the
current index, so the alternatives that were still on the stack were thrown
away.
With `\%(a\|ab\)\@<=c` on "abc" the shorter alternative was found first,
ended at the wrong index, and the whole start position was rejected, so the
positive assertion did not match and the negative one wrongly did.
The simulation now takes an optional target index. Reaching the accept state
at another index no longer stops it, so every way the pattern can match is
tried. Only the look behind passes a target index, so nothing else changes.
The backtracking simulation applied the captures of every state it visited
directly to the shared capture group collection and never undid them, so
what a failed branch captured leaked into the branch that did match.
For \zs, that moved the start of the match: `a\zsc\|ab` matched "b" instead
of "ab", because the first branch set the start of the match before failing.
Fix(VIM-4297): the same leak through \ze force ended the match. `a\zec\|ab`
matched "a" instead of "ab", because the force ended flag of the failed
branch made the end capture of the matching branch be ignored.
The capture group collection can now take and restore a snapshot of itself,
and counts its own changes. Each frame of the simulation stack remembers
that count, and every capture pushes a snapshot, so popping a frame can undo
everything that the branches explored since have captured. The stack is
shared with the nested simulations of assertions, and an assertion that does
not contribute to the match undoes its captures too.
Vim applies 'ignorecase' and \c to the individual characters and to the
ranges of a collection, but never to a character class, so \c[[:upper:]]
does not match a lowercase character.
The matcher tested the class against both the lowercase and the uppercase
variant of the character, which made \c[[:upper:]] match every letter.
:help aquote - any trailing white space is included, unless there is none,
then leading white space is included. Vim's in_quote uses vim_iswhite, which
matches a space or a tab, but IdeaVim only looked for a space.
With a tab after the closing quote, `da"` found no trailing white space and
fell back to including the leading space, deleting the wrong text.
Char.isWhitespace() is deliberately not used, because it also matches a new
line, which would let the scan leave the line.
For a file that ends with a new line, the IDE shows an extra empty last
line. Vim has no such line - the new line is just the last line's EOL.
findSentenceStart() treated that line as a paragraph boundary and returned
its offset, which is the size of the file, so `)` on the last sentence put
the caret on a position that does not exist in Vim, and `d)` deleted the
trailing new line along with the sentence.
Clamp the paragraph offset to the last character of the file. The clamp is
applied in findSentenceStart() rather than in findNextParagraph(), because
findSentenceEnd() relies on comparing its result against the file size when
the last sentence has no terminating punctuation.
'iskeyword' only applies to Latin-1 characters. Vim classifies everything
above with a fixed table of character classes and treats a character as a
word character when its class is greater than punctuation (vim_iswordc_buf
and utf_class_buf in mbyte.c).
IdeaVim treated every character above U+00FF as a keyword character, so word
motions never stopped at non-ASCII punctuation: `w` on "foo—bar" skipped the
whole run, and `2e` on "です。next" jumped past the ideographic full stop.
Port Vim's table of blank and punctuation intervals. 'isfname' keeps
treating every multibyte character as a filename character, which is what
vim_isfilec does.
Vim's forward word motions fail at the end of the file, but `nv_wordcmd`
only reports the failure when no operator is pending. With an operator, the
operator is applied from the caret to the end of the file, so `dw` on the
last character of the file deletes that character.
IdeaVim returned Motion.Error in both cases, and getMotionRange() aborted
the operator, so `dw`, `dW` and `de` on the last character of the file did
nothing at all.
Forward word motions now opt in to clamping the failed motion to the end of
the file, which getMotionRange() (only ever called with a pending operator)
applies. Plain `w` in normal mode still beeps, and the backward motions `b`,
`ge` and `gE` are unchanged - Vim beeps for those even with an operator.
:help exclusive - when an exclusive motion ends in column 1, its end moves
to the end of the previous line and the motion becomes inclusive. Only the
second half of the rule (the motion becomes linewise when the start is at or
before the first non-blank) was implemented, in the delete and yank
operators.
Without the first half, d} from the middle of a paragraph deleted the new
line of the last line of the paragraph too, merging away the blank line that
separates the paragraphs, and y} yanked a trailing new line that Vim does
not yank.
The operators recognise the linewise half of the rule by the end of the
range being in column 1, so the end is only adjusted when the start is not
in the indent, which is exactly when Vim makes the motion inclusive instead
of linewise.
Backward motions are left alone. Vim opts the affected ones out explicitly,
e.g. <BS> and h wrapping to the previous line set CA_NO_ADJ_OP_END.
anyNonWhitespace() coerced the end of its backward scan to 0, so at offset 0
the range 0..0 inspected the character *under* the caret instead of being
empty. The caller then believed there was non-blank text before the range
start and skipped Vim's charwise-to-linewise promotion for exclusive
motions, but only at the very first character of the file.
Dropping the coercion leaves an empty 0..-1 range, matching what already
happens at every other line-start offset.
A WORD is a sequence of non-blank characters, separated with white space
(:help WORD). Vim's cls() returns the same class for every non-blank
character when cls_bigword is set, so a change of Unicode script must not
end a WORD.
charType() consulted the Hiragana/Katakana/CJK Unicode blocks before the
punctuationAsLetters (bigWord) flag, so W, E, B and dW stopped at the
boundary between scripts: E on "abcあいう def" landed on 'c' instead of 'う'.
Short-circuit on punctuationAsLetters before the script checks. The script
classes still apply to small-word motions, which is where they belong.
Document VIM-4287 and VIM-4301 under 2.45.0 (both commits are contained
in the 2.45.0 tag) and list PRs #1946 and #1949 under Merged PRs.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Document the VIM-330 spelling dictionary commands in the changelog and
bring whatsnew-tbr.html up to date with the changelog entries that were
not yet represented on the page.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
When in normal mode it was wierd that enter has moved between lines instead of exeecuting it. To execute command in python console cursor must be at the end of line so we move it in normal mode