The command() handler now receives startLine and endLine (0-based)
from the resolved ex-command range. Previously the range was received
by the internal CommandAliasHandler but discarded before reaching the
plugin lambda.
Also fix using editor.projectId instead of the VimApiImpl's own
projectId, so the handler resolves the correct editor context.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Per VIM-4144 coroutine audit: methods inside non-locking scopes
(OptionScope, DigraphScope, OutputPanelScope) should be suspend
for RemDev future-proofing. Updated interfaces, implementations,
and extension functions.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Per VIM-4144 coroutine audit: read/change functions stay non-suspend
(Kotlin contracts don't support suspend, and they're already callable
from the suspend editor {} block). Block parameters stay non-suspend
(inside locks). Updated stale KDoc that referenced Deferred/Job.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Per VIM-4144 coroutine audit: handler lambdas should be suspend
for RemDev future-proofing. Implementation uses runBlocking bridge.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Per VIM-4144 coroutine audit: handler lambdas should be suspend
for RemDev future-proofing. Implementation uses runBlocking bridge.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Per VIM-4144 coroutine audit: handler lambdas should be suspend
for RemDev future-proofing. Also exposes command() at init time.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Per VIM-4144 coroutine audit: handler lambdas should be suspend
for RemDev future-proofing. Implementation uses runBlocking bridge.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Remove enterInsertMode(), enterNormalMode(), enterVisualMode() from VimApi.
Mode changes should use normal() — e.g., normal("<Esc>"), normal("i"),
normal("v") — matching how real Vim plugins handle mode transitions.
Neither Vim nor Neovim has a direct "set mode" API. All mode changes in
real plugins (surround, exchange, commentary, ReplaceWithRegister) use
normal!, feedkeys(), or :stopinsert. The removed methods used incomplete
internal delegation (changeMode Level 2) that skipped proper entry/exit
setup (marks, strokes, dot-repeat, document listeners).
Also removes the now-unused changeMode() function from Modes.kt.
See VIM-4143 for future proper mode-changing API design.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
After setAsCurrentWindow(), getSelectedTextEditor() returns stale data
because the platform propagates the change asynchronously via
flatMapLatest + stateIn, and there is no way to observe when
propagation completes.
Comment out window APIs in VimApi, VimApiImpl, CaretRead, CaretReadImpl.
Add limitation comment to VimWindowGroup. Update EditorContextTest to
call injector.window directly. Track re-enablement in VIM-4138.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Introduce VimInitApi as a restricted wrapper around VimApi that exposes
only init-safe methods (getVariable, mappings, textObjects,
exportOperatorFunction). During plugin init() there is no editor context,
so editor operations should not be callable. VimInitApi enforces this at
the type level via delegation rather than inheritance.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Replace the FLAG_TEXT_BLOCK command flag with a more descriptive
`preserveSelectionAnchor` property in TextObjectActionHandler.
This property controls what happens when the selection anchor is outside
the target text object range in visual mode:
- true (default): extends selection from current anchor
- false: jumps to select only the text object
Commands like iw/aw preserve anchor (extend), while structural text
objects like i(/a(, i"/a", is/as reset anchor (jump).
Also adds comprehensive documentation with examples to both the
internal handler and the public API.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Introduce TextObjectScope for registering custom text objects via the
plugin API. Text objects can be used with operators (d, c, y) or in
visual mode.
- Add TextObjectScope interface with register() method
- Add TextObjectRange sealed interface (CharacterWise/LineWise)
- Implement TextObjectScopeImpl using IdeaVim's internal mechanisms
- Migrate VimTextObjEntireExtension from Java to Kotlin using new API
- Add textObjects() function to VimApi
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Remove selection parameter from updateCaret - selection now extends
automatically if present, matching Vim's native cursor movement behavior.
- Remove selection: Range.Simple? parameter from updateCaret
- Add mode-aware range validation ([0, fileSize) vs [0, fileSize])
- Remove selection-related tests that are no longer applicable
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Range.Simple and Range.Block now always have start <= end
- Document that ranges are normalized with exclusive end semantics
- Normalize Range.Block by using min/max on vimSelectionStart and offset
- Add tests verifying ranges are normalized regardless of selection direction
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Change Range.Block from storing an array of per-line ranges to using
simple start/end offsets, matching how block selection is stored
internally in VimBlockSelection. The conversion to per-line ranges
now happens internally in CaretTransactionImpl.
Changes:
- Range.Block now uses (start: Int, end: Int) like Range.Simple
- Add replaceTextBlockwise(Range.Block, String) overload for single text
- Update CaretReadImpl to return block start/end from primary caret
- Add blockToLineRanges() helper in CaretTransactionImpl
- Update ReplaceWithRegisterNewApi to use simplified API
- Add 17 new tests for block selection and replacement
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Add extension functions for creating plugin mappings in specific modes:
- nmapPluginAction: normal mode
- vmapPluginAction: visual and select modes
- xmapPluginAction: visual mode
- smapPluginAction: select mode
- omapPluginAction: operator-pending mode
- imapPluginAction: insert mode
- cmapPluginAction: command-line mode
Each function checks if a mapping already exists for that specific mode
before adding, allowing users to override default mappings per-mode.
Also update mapPluginAction to check each mode individually instead of
using the generic hasmapto check.
- Update MappingScope documentation: v* functions now correctly
documented as "visual and select modes", x* functions as "visual mode"
- Fix MappingScopeImpl: vmap, vnoremap, vunmap, vhasmapto now operate
on both MappingMode.VISUAL and MappingMode.SELECT
Implement mode-specific hasmapto functions following the MappingScope style:
- hasmapto() - checks nvo modes
- nhasmapto() - checks normal mode
- vhasmapto() - checks visual mode
- xhasmapto() - checks visual exclusive mode
- shasmapto() - checks select mode
- ohasmapto() - checks operator pending mode
- ihasmapto() - checks insert mode
- chasmapto() - checks command line mode
These functions check if any mapping exists that maps TO the given string,
which is essential for plugins to detect if users have already mapped to
their <Plug> mappings. This enables the common pattern: "only create default
key mapping if user hasn't already mapped to the <Plug> mapping".
Implementation delegates to VimKeyGroup.hasmapto().
The map/noremap/unmap functions should only affect Normal, Visual, Select,
and Operator-pending modes (nvo), not all modes including Insert and
Command-line. This matches Vim's behavior where :map affects nvo modes only.
Changed from MappingMode.ALL to MappingMode.NVO.
Group mapping functions by mode instead of by operation type for better
organization and readability. Each mode group now contains:
- Recursive mapping (map variants)
- Non-recursive mapping (noremap variants)
- Unmap function
Order: map, nmap, vmap, xmap, smap, omap, imap, cmap
This makes the API more discoverable and easier to navigate.
The 3-argument mapping functions (e.g., nmap(keys, actionName, action)) had
incorrect implementation that used recursive mappings for both sub-mappings.
Also, this approach makes it impossible to implement other mapping features
such as skipping mapping registration if the mapping is already defined in
.ideavimrc.
A different solution for convenient mapping patterns will be implemented later.
Changes:
- Remove 16 three-argument function declarations from MappingScope interface
- Remove 16 three-argument function implementations from MappingScopeImpl
- Update ReplaceWithRegister plugins to use correct 2-step pattern:
1. nnoremap("<Plug>...") for non-recursive <Plug> → action
2. nmap("key", "<Plug>...") for recursive key → <Plug>
Total: 374 lines deleted, 18 lines added
The append, prepend, and remove functions now correctly handle empty
option values. When an option is empty (""), we use an empty list
instead of splitting the empty string which would result in [""].
This fixes test failures in OptionScopeTest for operations on empty options.
This allows users to easily retrieve values from option scope:
val x = option { get<Int>("history") }
- Changed option() signature from Unit to generic T return type
- Updated VimApiImpl implementation to return the lambda result
- Added test demonstrating the new return value capability
The implementation always returns a value or throws an exception,
so the return type should be non-nullable to reflect this behavior.
Updated extension functions to remove unnecessary null checks.
- Convert OptionScope from abstract class to interface
- Extract inline functions with reified types as extension functions
- Make getOptionValue() and setOption() public interface methods
- Remove internal modifier layer functions
- Update OptionScopeImpl to implement new interface
- Add documentation recommending extension function usage
- Update test imports to use new extension functions
- append(): adds values to end of comma-separated list (like Vim's +=)
- prepend(): adds values to beginning of list (like Vim's ^=)
- remove(): removes values from list (like Vim's -=)
- All functions prevent duplicate values from being added
- Comprehensive test coverage for all scenarios
Adds a simple toggle() function that flips boolean option values.
Works with both full option names and abbreviations.
Example usage:
toggle("ignorecase") // true → false, false → true
toggle("ic") // works with abbreviations
Adds a concise String.split() extension function within OptionScope that splits
comma-separated option values into lists. This simplifies working with list-type
options like 'virtualedit', 'whichwrap', etc.
Example usage:
val values = get<String>("virtualedit")?.split() ?: emptyList()
// "block,all" → ["block", "all"]
1) ExException is wrapped with the IllegalArgumentException. We cannot use the raw ExException as it's not defined in the API module. So, if any client will have an access only to the API module, they won't be able to handle this kind of the exception.
2) getOption throws IllegalArgumentException if the type classifier is null. I don't know when this might happen, but this looks more like a plugin developer error. Also, this allows to distinguish the wrong option name vs this type of problem
3) In setOption and getOption throw an exception if there is no such option. This clearly explains what is wrong with this call.
Note: there was a question of using exceptions vs some return values, even like `Result`. The decision falls into the using of exceptions as using the wrong arguments in the plugin is a programming error from the user point of view.
This means, if there is a plugin that checks some option that in reality doesn't exist, it's not a fail of the end user and the end user cannot do anything about that.
Also, using the `Result` will force the plugin developer to handle all possible failure branches, what is quite unnecessary when accessing a well-defined option.
This function is not provided by vim and by using it from the plugins we may drop the digraphs created by other plugins.
It'd be better to provide a just remove function. However, let's skip it for now. Also, maybe we should use an approach with the owners