Accessing alias[0] without an isEmpty() guard crashed with
IndexOutOfBoundsException when the user typed a :command with only
-nargs specified but no command name (e.g. ":command -nargs=0").
After -nargs processing strips the flag, the remaining argument is
empty, so alias becomes "" and alias[0] throws. Adding alias.isEmpty()
guard treats the missing name as an invalid command name (E183).
Adds a regression test to ensure this case no longer crashes.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The `isRegisterPending` field was not considered in several methods of
`CommandBuilder`, causing subtle bugs:
- `isEmpty` returned `true` while waiting for a register character
(after typing `"`), which caused `EditorResetConsumer` to treat the
partially-built command as if no command was in progress. This could
trigger an incorrect error indicator (beep) when pressing `<Esc>` to
cancel register selection in Normal mode, instead of silently resetting.
- `clone()` did not copy `isRegisterPending`, meaning a cloned builder
would lose pending register state. This is a latent bug affecting the
unused `AsyncKeyProcessBuilder`.
- `equals()` and `hashCode()` did not include `isRegisterPending`, so
two builders differing only in pending-register state were considered
equal, which is incorrect.
Add a regression test that verifies `isEmpty` returns `false` while a
register selection is pending, and `true` after cancelling with Escape.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Delayed extension init runs after .ideavimrc, so the plugin's default
`nmap gr`/`nmap grr`/`vmap gr` used to clobber user mappings. Switch to
`nmapPluginAction`/`vmapPluginAction` which guard on `hasmapto`, matching
the original Vim plugin's behavior.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Guard against VIM-4193 regressing again: both plugins are back on the
pre-migration TextObjectActionHandler API, so multi-caret daa/dia/dii
works today. If either plugin is re-migrated to the new TextObjectScope
API before it grows a per-caret read primitive (withCurrentCaret or
equivalent), these tests will fail loudly instead of the bug sneaking
through silently.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Same per-caret gap as the argtextobj revert (VIM-4193): the new
TextObjectScope rangeProvider lambda has no way to read state for the
caret currently being iterated, so the post-migration version falls
back to withPrimaryCaret and breaks multi-caret ai/aI/ii.
Restore VimIndentObject.kt to its state just before a6db9acd7
("Refactor: Migrate VimIndentObject extension to new VimApi"), keeping
it on the old TextObjectActionHandler-based API until the new API
exposes a per-caret read primitive.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Pending a fix for the current-caret gap in the new TextObjectScope API
(VIM-4193): the rangeProvider lambda has no way to know which caret the
engine is currently iterating over, so the post-migration extension
falls back to withPrimaryCaret and breaks multi-caret daa/dia.
Restore VimArgTextObjExtension.kt to its state just before 86bf54d84
("Migrate argtextobj extension to new textObjects API"), keeping it on
the old TextObjectActionHandler-based API until the new API exposes a
per-caret read primitive.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The $APP_CONFIG$ path macro in @Storage annotations is rejected by
SecurityHelper.validateSettingsFile in remote dev / split mode. The
platform resolves app-level storage to the config directory automatically,
so the bare filename is sufficient.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
In split mode, VimShortcutKeyAction and ToolWindowNavDispatcher claimed Enter/arrow key shortcuts on the Python console editor component, preventing the thin client from creating backend delegating actions for Console.Execute and history navigation.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Split showing error message into two separate methods. Once that appends the error to the current output panel and the second that clears the output panel before showing the error. So when no search results are found we don;t show hit enter message,
When using \/, \?, or \& in an Ex command range (e.g., :\/ d) without a
previous search or substitute pattern, the code stored null in the
patterns list and then threw NullPointerException via the !! assertion
in calculateLine1.
Instead, throw the appropriate Vim error eagerly when building the
SearchAddress: E35 for \/ and \? (no previous search), E33 for \& (no
previous substitute). The patterns list is now non-nullable, eliminating
the !! assertion.
Add regression tests that would have caught this NPE.
- Remove `test history cmd lists empty command history` which was an
exact duplicate of `test history cmd lists current cmd in history`
- Fix typo "saerch" -> "search" in test name
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Octopus is disabled for Rider (VIM-3815), and Rider's LookupSummaryInfo popup causes the popup manager to consume Escape before IdeaVim's action handlers can process it, so we now listen for explicit lookup cancellation via LookupListener to exit insert mode.
The mock services (VimMarkService, VimJumpService) were replaced via
MockTestCase.mockService() using @TestDisposable, which is disposed
AFTER @AfterEach. During super.tearDown(), editor disposal triggers
injector.markService.editorReleased(vimEditor), which still hits the
mock (service replacement not yet undone). This records a new Mockito
invocation on the EDT's MockingProgressImpl thread-local, holding
IjVimEditor → EditorImpl → ProjectImpl — causing the leaked project.
Fix: use a separate Disposable for service replacements and dispose it
before super.tearDown(), so editorReleased goes to the real service.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
After the K3 coroutine audit made VimApi methods suspend,
these tests were not updated to wrap calls in runBlocking.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Part of the VimApi freeze decision (VIM-4161). Reverting the
partial migration to keep Exchange fully on the old API.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Replace RangeHighlighter field in Exchange with HighlightId. Use
injector.highlightingService for adding/removing highlights instead
of direct markupModel access. Update Util.clearExchange to take
VimEditor. Update test assertHighlighter to check markup model
directly (area validation lost — tracked with TODO).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The old condition `!isVisualLine && (hlArea == EXACT_RANGE || isVisual)`
was equivalent to `ex.type != LINE_WISE` because when !isVisualLine is
true, hlArea is always EXACT_RANGE, making the isVisual branch
unreachable. Pure logic simplification, no behavior change.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Replace separate nnoremap+nmap/putExtensionHandlerMapping+putKeyMappingIfMissing
with the combined nmapPluginAction/xmapPluginAction helpers for all four
mappings (cx, cxx, cxc, X). Remove ExchangeClearHandler and VExchangeHandler
classes; their logic is now in top-level bridge functions that still delegate
to the old Operator/Util code.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Preparation for new API migration: Exchange now stores startLine/startCol/
startOffset/endLine/endCol/endOffset directly, removing dependency on the
internal Mark type. All comparison and cursor logic updated accordingly.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Replace ExchangeHandler class with suspend fun VimApi.exchangeAction()
and register via initApi.mappings { nnoremap/nmap }.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>