mirror of
https://github.com/chylex/IntelliJ-IdeaVim.git
synced 2025-08-17 07:31:47 +02:00
Compare commits
52 Commits
0.55.2-EAP
...
0.56
Author | SHA1 | Date | |
---|---|---|---|
![]() |
0a4683d908 | ||
![]() |
4c280b0193 | ||
![]() |
e88a3deafd | ||
![]() |
bd172b3300 | ||
![]() |
95c7a13cb5 | ||
![]() |
b1ddf03385 | ||
![]() |
a83c326736 | ||
![]() |
b1acb56247 | ||
![]() |
caa4731a13 | ||
![]() |
5b0ece7a91 | ||
![]() |
c8d64e0a06 | ||
![]() |
1a3dea0de6 | ||
![]() |
17b642280e | ||
![]() |
1c1717b78b | ||
![]() |
4bbbdf8108 | ||
![]() |
04a193309d | ||
![]() |
f106ffa176 | ||
![]() |
8d5d099542 | ||
![]() |
4849992ca9 | ||
![]() |
623105650e | ||
![]() |
5e2c01daa6 | ||
![]() |
58bf3a4d30 | ||
![]() |
2d434c38b9 | ||
![]() |
246f5cd8cf | ||
![]() |
5a174d21f1 | ||
![]() |
e632c653f6 | ||
![]() |
174d17b088 | ||
![]() |
3a35c931e4 | ||
![]() |
b768b26c85 | ||
![]() |
123ce6ebaf | ||
![]() |
276c8db512 | ||
![]() |
f898b8d181 | ||
![]() |
e9f9e531e4 | ||
![]() |
a7d813cb86 | ||
![]() |
75b6eedb12 | ||
![]() |
ec6860aa90 | ||
![]() |
5cf661c6ae | ||
![]() |
8c62caae7c | ||
![]() |
cc6fe21af6 | ||
![]() |
1902151efa | ||
![]() |
b7af1e6289 | ||
![]() |
0c77b320db | ||
![]() |
ee41adc4e9 | ||
![]() |
93462d7505 | ||
![]() |
6ec39314ee | ||
![]() |
14c8b6a248 | ||
![]() |
9b71215cde | ||
![]() |
8be572f976 | ||
![]() |
4f43bcffb9 | ||
![]() |
29e4dc5fb5 | ||
![]() |
0dc95cb13c | ||
![]() |
9fad4a74ed |
@@ -283,6 +283,10 @@ Contributors:
|
||||
[![icon][github]](https://github.com/angelbot)
|
||||
|
||||
John Weigel
|
||||
* [![icon][mail]](mailto:kevinz@weghst.com)
|
||||
[![icon][github]](https://github.com/kevin70)
|
||||
|
||||
kk
|
||||
|
||||
If you are a contributor and your name is not listed here, feel free to
|
||||
contact the maintainers.
|
||||
|
43
CHANGES.md
43
CHANGES.md
@@ -3,6 +3,12 @@ The Changelog
|
||||
|
||||
History of changes in IdeaVim for the IntelliJ platform.
|
||||
|
||||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
||||
and this project DOES NOT adhere to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||
|
||||
Stable versions use X.Y format.
|
||||
EAP versions use X.Y.Z format.
|
||||
|
||||
|
||||
Get an Early Access
|
||||
-------------------
|
||||
@@ -16,31 +22,34 @@ It is important to distinguish EAP from traditional pre-release software.
|
||||
Please note that the quality of EAP versions may at times be way below even
|
||||
usual beta standards.
|
||||
|
||||
To Be Released
|
||||
0.56, 2020-04-09
|
||||
--------------
|
||||
|
||||
_Available since 0.55.1 EAP:_
|
||||
|
||||
**Fixes:**
|
||||
* [VIM-1284](https://youtrack.jetbrains.com/issue/VIM-1284) Fix mapping of digits
|
||||
* Fix handling of counts on both operator and motion, e.g. `3d2w` deletes 6 words, instead of 32
|
||||
* Allow mapping of `<C-K>` and `<C-V>`/`<C-Q>`
|
||||
* [VIM-1899](https://youtrack.jetbrains.com/issue/VIM-1899) Add argument to `:registers` command
|
||||
* [VIM-1835](https://youtrack.jetbrains.com/issue/VIM-1835) Macros record input keystrokes instead of mapped keystrokes
|
||||
* [VIM-1900](https://youtrack.jetbrains.com/issue/VIM-1900) Ensure non-printable output for `:registers`, `:marks` and `:jumps` is encoded correctly
|
||||
|
||||
_Available since 0.55.2 EAP:_
|
||||
|
||||
**Features:**
|
||||
* `argtextobj.vim` plugin emulation ([argtextobj.vim](https://vim.sourceforge.io/scripts/script.php?script_id=2699))
|
||||
* `vim-textobj-entire` plugin emulation ([vim-textobj-entire](https://github.com/kana/vim-textobj-entire))
|
||||
* Support `ls/buffers/files` commands
|
||||
* `ReplaceWithRegister` plugin emulation ([ReplaceWithRegister](https://www.vim.org/scripts/script.php?script_id=2703)).
|
||||
* `argtextobj.vim` plugin emulation ([argtextobj.vim](https://vim.sourceforge.io/scripts/script.php?script_id=2699)).
|
||||
* `vim-textobj-entire` plugin emulation ([vim-textobj-entire](https://github.com/kana/vim-textobj-entire)).
|
||||
* [VIM-434](https://youtrack.jetbrains.com/issue/VIM-434) Add `'showcmd'` support, on by default.
|
||||
* Support `ls/buffers/files` commands.
|
||||
|
||||
**Changes:**
|
||||
* Replace `ideastatusbar` option with `ideastatusicon`. Now you can make the icon gray.
|
||||
|
||||
**Deprecations:**
|
||||
* `ideastatusbar` option is deprecated now. See `ideastatusicon`.
|
||||
|
||||
**Fixes:**
|
||||
* [VIM-1008](https://youtrack.jetbrains.com/issue/VIM-1008) Correct `ci{` behavior
|
||||
* [VIM-1284](https://youtrack.jetbrains.com/issue/VIM-1284) Fix mapping of digits.
|
||||
* Fix handling of counts on both operator and motion, e.g. `3d2w` deletes 6 words, instead of 32.
|
||||
* Allow mapping of `<C-K>` and `<C-V>`/`<C-Q>`.
|
||||
* [VIM-1899](https://youtrack.jetbrains.com/issue/VIM-1899) Add argument to `:registers` command.
|
||||
* [VIM-1835](https://youtrack.jetbrains.com/issue/VIM-1835) Macros record input keystrokes instead of mapped keystrokes.
|
||||
* [VIM-1900](https://youtrack.jetbrains.com/issue/VIM-1900) Ensure non-printable output for `:registers`, `:marks` and `:jumps` is encoded correctly.
|
||||
* [VIM-570](https://youtrack.jetbrains.com/issue/VIM-570) Print non-ascii characters in ex panel.
|
||||
* [VIM-926](https://youtrack.jetbrains.com/issue/VIM-926) Fix `<S-Space>` mapping.
|
||||
* [VIM-1958](https://youtrack.jetbrains.com/issue/VIM-1958) Fix `X` command for linewise selection.
|
||||
* [VIM-1911](https://youtrack.jetbrains.com/issue/VIM-1911) Lookup keys respect `IDE` handler.
|
||||
* [VIM-1008](https://youtrack.jetbrains.com/issue/VIM-1008) Correct `ci{` behavior.
|
||||
|
||||
0.55, 2020-01-20
|
||||
--------------
|
||||
|
23
README.md
23
README.md
@@ -97,8 +97,9 @@ Emulated Vim plugins:
|
||||
* vim-surround
|
||||
* vim-multiple-cursors
|
||||
* vim-commentary
|
||||
* argtextobj.vim [To Be Released]
|
||||
* vim-textobj-entire [To Be Released]
|
||||
* argtextobj.vim
|
||||
* vim-textobj-entire
|
||||
* ReplaceWithRegister
|
||||
|
||||
Not supported (yet):
|
||||
|
||||
@@ -167,12 +168,24 @@ Available extensions:
|
||||
* Emulates [commentary.vim](https://github.com/tpope/vim-commentary)
|
||||
* Commands: `gcc`, `gc + motion`, `v_gc`
|
||||
|
||||
* argtextobj [To Be Released]
|
||||
* Setup: `set argtextobj`
|
||||
* ReplaceWithRegister
|
||||
* Setup: `set ReplaceWithRegister`
|
||||
* Emulates [ReplaceWithRegister](https://github.com/vim-scripts/ReplaceWithRegister)
|
||||
* Commands: `gr`, `grr`
|
||||
|
||||
* argtextobj
|
||||
* Setup:
|
||||
* `set argtextobj`
|
||||
* By default, only the arguments inside parenthesis are considered. To extend the functionality
|
||||
to other types of brackets, set `g:argtextobj_pairs` variable to a comma-separated
|
||||
list of colon-separated pairs (same as VIM's `matchpairs` option), like
|
||||
`let g:argtextobj_pairs="(:),{:},<:>"`. The order of pairs matters when
|
||||
handling symbols that can also be operators: `func(x << 5, 20) >> 17`. To handle
|
||||
this syntax parenthesis, must come before angle brackets in the list.
|
||||
* Emulates [argtextobj.vim](https://www.vim.org/scripts/script.php?script_id=2699)
|
||||
* Additional text objects: `aa`, `ia`
|
||||
|
||||
* textobj-entire [To Be Released]
|
||||
* textobj-entire
|
||||
* Setup: `set textobj-entire`
|
||||
* Emulates [vim-textobj-entire](https://github.com/kana/vim-textobj-entire)
|
||||
* Additional text objects: `ae`, `ie`
|
||||
|
45
build.gradle
45
build.gradle
@@ -74,15 +74,56 @@ tasks.register("slackEapNotification") {
|
||||
doLast {
|
||||
if (!slackUrl) return
|
||||
def post = new URL(slackUrl).openConnection()
|
||||
def message = "{\"text\":\"New EAP released: $version\"}"
|
||||
def changeLog = extractChangelog()
|
||||
changeLog = changeLog.replace("* ", "• ") // Replace stars with bullets
|
||||
changeLog = changeLog.replace("**", "*") // Enable bold text
|
||||
changeLog = changeLog.replaceAll("\\[(.+)]\\(([^)]+)\\)", '<$2|$1>') // Enable links
|
||||
def message ="""
|
||||
{
|
||||
"text": "Danny Torrence left a 1 star review for your property.",
|
||||
"blocks": [
|
||||
{
|
||||
"type": "section",
|
||||
"text": {
|
||||
"type": "mrkdwn",
|
||||
"text": "New EAP released: $version\\n$changeLog"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
"""
|
||||
post.setRequestMethod("POST")
|
||||
post.setDoOutput(true)
|
||||
post.setRequestProperty("Content-Type", "application/json")
|
||||
post.getOutputStream().write(message.getBytes("UTF-8"))
|
||||
def postRC = post.getResponseCode()
|
||||
println(postRC)
|
||||
if(postRC.equals(200)) {
|
||||
if(postRC == 200) {
|
||||
println(post.getInputStream().getText())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Very primitive changelog extraction code
|
||||
def extractChangelog() {
|
||||
def startLine = "_Available since $version EAP:_"
|
||||
def endLine = "_To Be Released..._"
|
||||
def startSaving = false
|
||||
def res = new StringBuilder()
|
||||
new File("./CHANGES.md").eachLine { line ->
|
||||
if (startSaving) {
|
||||
if (line == endLine) {
|
||||
startSaving = false
|
||||
}
|
||||
else {
|
||||
res.append(line).append('\n')
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (line == startLine) {
|
||||
startSaving = true
|
||||
}
|
||||
}
|
||||
}
|
||||
return res.toString()
|
||||
}
|
@@ -65,6 +65,7 @@ The following `:set` commands can appear in `~/.ideavimrc` or be set manually in
|
||||
same as ideaselection - IdeaVim ONLY
|
||||
|
||||
'showmode' 'smd' message on the status line to show current mode
|
||||
'showcmd' 'sc' show (partial) command in the status bar
|
||||
'sidescroll' 'ss' minimum number of columns to scroll horizontally
|
||||
'sidescrolloff' 'siso' min. number of columns to left and right of cursor
|
||||
'smartcase' 'scs' no ignore case when pattern is uppercase
|
||||
@@ -109,7 +110,7 @@ The following `:set` commands can appear in `~/.ideavimrc` or be set manually in
|
||||
If false, IdeaVim icon won't be shown in the status bar.
|
||||
Works only from `~/.ideavimrc` after the IDE restart.
|
||||
|
||||
`ideastatusicon` `ideastatusicon` String(default "enabled") [To Be Released]
|
||||
`ideastatusicon` `ideastatusicon` String(default "enabled")
|
||||
|
||||
Define the behavior of IdeaVim icon in the status bar.
|
||||
|
||||
|
@@ -5,7 +5,7 @@ downloadIdeaSources=true
|
||||
instrumentPluginCode=true
|
||||
version=SNAPSHOT
|
||||
javaVersion=1.8
|
||||
kotlinVersion=1.3.61
|
||||
kotlinVersion=1.3.70
|
||||
publishUsername=username
|
||||
publishToken=token
|
||||
publishChannels=eap
|
||||
|
5
gradle/wrapper/gradle-wrapper.properties
vendored
5
gradle/wrapper/gradle-wrapper.properties
vendored
@@ -1,5 +1,6 @@
|
||||
#Fri Mar 20 11:41:45 MSK 2020
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-6.2.1-all.zip
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-6.2.1-bin.zip
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
|
@@ -197,8 +197,8 @@
|
||||
<vimAction implementation="com.maddyhome.idea.vim.action.change.insert.InsertSingleCommandAction" mappingModes="I" keys="«C-O»"/>
|
||||
<vimAction implementation="com.maddyhome.idea.vim.action.change.insert.VisualBlockInsertAction" mappingModes="X" keys="I"/>
|
||||
<vimAction implementation="com.maddyhome.idea.vim.action.change.insert.VisualBlockAppendAction" mappingModes="X" keys="A"/>
|
||||
<vimAction implementation="com.maddyhome.idea.vim.action.change.insert.StartInsertDigraphAction" mappingModes="IC" keys="«C-K»"/>
|
||||
<vimAction implementation="com.maddyhome.idea.vim.action.change.insert.StartInsertLiteralAction" mappingModes="IC" keys="«C-V»,«C-Q»"/>
|
||||
<vimAction implementation="com.maddyhome.idea.vim.action.change.insert.InsertCompletedDigraphAction" mappingModes="IC" keys="«C-K»"/>
|
||||
<vimAction implementation="com.maddyhome.idea.vim.action.change.insert.InsertCompletedLiteralAction" mappingModes="IC" keys="«C-V»,«C-Q»"/>
|
||||
|
||||
<!-- Delete -->
|
||||
<vimAction implementation="com.maddyhome.idea.vim.action.change.delete.DeleteCharacterAction" mappingModes="N" keys="«DEL»"/>
|
||||
@@ -260,14 +260,16 @@
|
||||
<vimAction implementation="com.maddyhome.idea.vim.action.copy.PutTextBeforeCursorNoIndentAction" mappingModes="N" keys="[P,]P,[p"/>
|
||||
<vimAction implementation="com.maddyhome.idea.vim.action.copy.PutTextAfterCursorActionMoveCursor" mappingModes="N" keys="gp"/>
|
||||
<vimAction implementation="com.maddyhome.idea.vim.action.copy.PutTextBeforeCursorActionMoveCursor" mappingModes="N" keys="gP"/>
|
||||
<vimAction implementation="com.maddyhome.idea.vim.action.copy.SelectRegisterAction" mappingModes="NXO" keys='"'/>
|
||||
<vimAction implementation="com.maddyhome.idea.vim.action.copy.YankLineAction" mappingModes="N" keys="Y"/>
|
||||
<vimAction implementation="com.maddyhome.idea.vim.action.copy.YankMotionAction" mappingModes="N" keys="y"/>
|
||||
<vimAction implementation="com.maddyhome.idea.vim.action.copy.YankVisualAction" mappingModes="X" keys="y"/>
|
||||
<vimAction implementation="com.maddyhome.idea.vim.action.copy.YankVisualLinesAction" mappingModes="X" keys="Y"/>
|
||||
<vimAction implementation="com.maddyhome.idea.vim.action.copy.PutVisualTextAction" mappingModes="X" keys="p,P"/>
|
||||
<vimAction implementation="com.maddyhome.idea.vim.action.copy.PutVisualTextNoIndentAction" mappingModes="X" keys="[p,]p,]P,[P"/>
|
||||
<vimAction implementation="com.maddyhome.idea.vim.action.copy.PutVisualTextMoveCursorAction" mappingModes="X" keys="gp,gP"/>
|
||||
<vimAction implementation="com.maddyhome.idea.vim.action.copy.PutVisualTextBeforeCursorAction" mappingModes="X" keys="P"/>
|
||||
<vimAction implementation="com.maddyhome.idea.vim.action.copy.PutVisualTextAfterCursorAction" mappingModes="X" keys="p"/>
|
||||
<vimAction implementation="com.maddyhome.idea.vim.action.copy.PutVisualTextBeforeCursorNoIndentAction" mappingModes="X" keys="]P,[P"/>
|
||||
<vimAction implementation="com.maddyhome.idea.vim.action.copy.PutVisualTextAfterCursorNoIndentAction" mappingModes="X" keys="[p,]p"/>
|
||||
<vimAction implementation="com.maddyhome.idea.vim.action.copy.PutVisualTextBeforeCursorMoveCursorAction" mappingModes="X" keys="gP"/>
|
||||
<vimAction implementation="com.maddyhome.idea.vim.action.copy.PutVisualTextAfterCursorMoveCursorAction" mappingModes="X" keys="gp"/>
|
||||
|
||||
<!-- File -->
|
||||
<vimAction implementation="com.maddyhome.idea.vim.action.file.FileSaveCloseAction" mappingModes="N" keys="ZQ,ZZ"/>
|
||||
|
@@ -5,5 +5,6 @@
|
||||
<vimExtension implementation="com.maddyhome.idea.vim.extension.commentary.CommentaryExtension"/>
|
||||
<vimExtension implementation="com.maddyhome.idea.vim.extension.textobjentire.VimTextObjEntireExtension"/>
|
||||
<vimExtension implementation="com.maddyhome.idea.vim.extension.argtextobj.VimArgTextObjExtension"/>
|
||||
<vimExtension implementation="com.maddyhome.idea.vim.extension.replacewithregister.ReplaceWithRegister"/>
|
||||
</extensions>
|
||||
</idea-plugin>
|
||||
|
@@ -3,9 +3,12 @@
|
||||
<id>IdeaVIM</id>
|
||||
<change-notes><![CDATA[
|
||||
<ul>
|
||||
<li>Support ReplaceWithRegister plugin emulation</li>
|
||||
<li>Support argtextobj.vim plugin emulation</li>
|
||||
<li>Support vim-textobj-entire plugin emulation</li>
|
||||
<li>Support showcmd command</li>
|
||||
<li>Support ls/buffers/files command</li>
|
||||
<li>Control the icon in the status bar using an `ideastatusicon` option</li>
|
||||
<li>Various bug fixes</li>
|
||||
</ul>
|
||||
<p>See also the complete <a href="https://github.com/JetBrains/ideavim/blob/master/CHANGES.md">changelog</a>.</p>
|
||||
@@ -53,6 +56,7 @@
|
||||
<applicationConfigurable groupId="editor" instance="com.maddyhome.idea.vim.ui.VimEmulationConfigurable"/>
|
||||
<projectService serviceImplementation="com.maddyhome.idea.vim.group.NotificationService"/>
|
||||
<statusBarWidgetProvider implementation="com.maddyhome.idea.vim.StatusBarIconProvider"/>
|
||||
<statusBarWidgetProvider implementation="com.maddyhome.idea.vim.ui.ShowCmdStatusBarWidget"/>
|
||||
|
||||
<applicationService serviceImplementation="com.maddyhome.idea.vim.VimLocalConfig"/>
|
||||
<applicationService serviceImplementation="com.maddyhome.idea.vim.VimPlugin"/>
|
||||
|
@@ -35,6 +35,8 @@ import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.ui.popup.JBPopupFactory;
|
||||
import com.intellij.openapi.ui.popup.ListPopup;
|
||||
import com.maddyhome.idea.vim.action.change.VimRepeater;
|
||||
import com.maddyhome.idea.vim.action.change.insert.InsertCompletedDigraphAction;
|
||||
import com.maddyhome.idea.vim.action.change.insert.InsertCompletedLiteralAction;
|
||||
import com.maddyhome.idea.vim.action.macro.ToggleRecordingAction;
|
||||
import com.maddyhome.idea.vim.command.*;
|
||||
import com.maddyhome.idea.vim.extension.VimExtensionHandler;
|
||||
@@ -48,6 +50,7 @@ import com.maddyhome.idea.vim.key.*;
|
||||
import com.maddyhome.idea.vim.listener.SelectionVimListenerSuppressor;
|
||||
import com.maddyhome.idea.vim.listener.VimListenerSuppressor;
|
||||
import com.maddyhome.idea.vim.option.OptionsManager;
|
||||
import com.maddyhome.idea.vim.ui.ShowCmd;
|
||||
import org.jetbrains.annotations.Contract;
|
||||
import org.jetbrains.annotations.NonNls;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
@@ -155,16 +158,6 @@ public class KeyHandler {
|
||||
return false;
|
||||
}
|
||||
|
||||
public void startDigraphSequence(@NotNull Editor editor) {
|
||||
final CommandState editorState = CommandState.getInstance(editor);
|
||||
editorState.startDigraphSequence();
|
||||
}
|
||||
|
||||
public void startLiteralSequence(@NotNull Editor editor) {
|
||||
final CommandState editorState = CommandState.getInstance(editor);
|
||||
editorState.startLiteralSequence();
|
||||
}
|
||||
|
||||
/**
|
||||
* This is the main key handler for the Vim plugin. Every keystroke not handled directly by Idea is sent here for
|
||||
* processing.
|
||||
@@ -221,7 +214,7 @@ public class KeyHandler {
|
||||
try {
|
||||
if (!allowKeyMappings || !handleKeyMapping(editor, key, context)) {
|
||||
if (isCommandCountKey(chKey, editorState)) {
|
||||
commandBuilder.addCountCharacter(chKey);
|
||||
commandBuilder.addCountCharacter(key);
|
||||
} else if (isDeleteCommandCountKey(key, editorState)) {
|
||||
commandBuilder.deleteCountCharacter();
|
||||
} else if (isEditorReset(key, editorState)) {
|
||||
@@ -232,21 +225,29 @@ public class KeyHandler {
|
||||
else if (isExpectingCharArgument(commandBuilder)) {
|
||||
handleCharArgument(key, chKey, editorState);
|
||||
}
|
||||
else if (editorState.getSubMode() == CommandState.SubMode.REGISTER_PENDING) {
|
||||
commandBuilder.addKey(key);
|
||||
handleSelectRegister(editorState, chKey);
|
||||
}
|
||||
// If we are this far, then the user must be entering a command or a non-single-character argument
|
||||
// to an entered command. Let's figure out which it is.
|
||||
else if (!handleDigraph(editor, key, context, editorState)) {
|
||||
|
||||
commandBuilder.addKey(key);
|
||||
|
||||
// Ask the key/action tree if this is an appropriate key at this point in the command and if so,
|
||||
// return the node matching this keystroke
|
||||
final Node node = mapOpCommand(key, commandBuilder.getChildNode(key), editorState);
|
||||
|
||||
if (node instanceof CommandNode) {
|
||||
handleCommandNode(editor, context, key, (CommandNode) node, editorState);
|
||||
commandBuilder.addKey(key);
|
||||
} else if (node instanceof CommandPartNode) {
|
||||
commandBuilder.setCurrentCommandPartNode((CommandPartNode) node);
|
||||
} else {
|
||||
commandBuilder.addKey(key);
|
||||
} else if (isSelectRegister(key, editorState)) {
|
||||
editorState.pushModes(CommandState.Mode.COMMAND, CommandState.SubMode.REGISTER_PENDING);
|
||||
commandBuilder.addKey(key);
|
||||
}
|
||||
else { // node == null
|
||||
|
||||
// If we are in insert/replace mode send this key in for processing
|
||||
if (editorState.getMode() == CommandState.Mode.INSERT || editorState.getMode() == CommandState.Mode.REPLACE) {
|
||||
shouldRecord &= VimPlugin.getChange().processKey(editor, context, key);
|
||||
@@ -271,10 +272,11 @@ public class KeyHandler {
|
||||
|
||||
// Do we have a fully entered command at this point? If so, let's execute it.
|
||||
if (commandBuilder.isReady()) {
|
||||
executeCommand(editor, key, context, editorState);
|
||||
executeCommand(editor, context, editorState);
|
||||
}
|
||||
else if (commandBuilder.isBad()) {
|
||||
editorState.resetOpPending();
|
||||
editorState.resetRegisterPending();
|
||||
VimPlugin.indicateError();
|
||||
reset(editor);
|
||||
}
|
||||
@@ -283,6 +285,9 @@ public class KeyHandler {
|
||||
if (shouldRecord && editorState.isRecording()) {
|
||||
VimPlugin.getRegister().recordKeyStroke(key);
|
||||
}
|
||||
|
||||
// This will update immediately, if we're on the EDT (which we are)
|
||||
ShowCmd.INSTANCE.update();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -570,7 +575,29 @@ public class KeyHandler {
|
||||
}
|
||||
|
||||
private boolean isEditorReset(@NotNull KeyStroke key, @NotNull CommandState editorState) {
|
||||
return (editorState.getMode() == CommandState.Mode.COMMAND) && StringHelper.isCloseKeyStroke(key);
|
||||
return editorState.getMode() == CommandState.Mode.COMMAND && StringHelper.isCloseKeyStroke(key);
|
||||
}
|
||||
|
||||
private boolean isSelectRegister(@NotNull KeyStroke key, @NotNull CommandState editorState) {
|
||||
if (editorState.getMode() != CommandState.Mode.COMMAND && editorState.getMode() != CommandState.Mode.VISUAL) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (editorState.getSubMode() == CommandState.SubMode.REGISTER_PENDING) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return key.getKeyChar() == '"' && !editorState.isOperatorPending() && editorState.getCommandBuilder().getExpectedArgumentType() == null;
|
||||
}
|
||||
|
||||
private void handleSelectRegister(@NotNull CommandState commandState, char chKey) {
|
||||
commandState.resetRegisterPending();
|
||||
if (VimPlugin.getRegister().isValid(chKey)) {
|
||||
commandState.getCommandBuilder().pushCommandPart(chKey);
|
||||
}
|
||||
else {
|
||||
commandState.getCommandBuilder().setCommandState(CurrentCommandState.BAD_COMMAND);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isExpectingCharArgument(@NotNull CommandBuilder commandBuilder) {
|
||||
@@ -615,10 +642,12 @@ public class KeyHandler {
|
||||
if (commandBuilder.getExpectedArgumentType() == Argument.Type.DIGRAPH) {
|
||||
if (DigraphSequence.isDigraphStart(key)) {
|
||||
editorState.startDigraphSequence();
|
||||
editorState.getCommandBuilder().addKey(key);
|
||||
return true;
|
||||
}
|
||||
if (DigraphSequence.isLiteralStart(key)) {
|
||||
editorState.startLiteralSequence();
|
||||
editorState.getCommandBuilder().addKey(key);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -626,7 +655,7 @@ public class KeyHandler {
|
||||
DigraphResult res = editorState.processDigraphKey(key, editor);
|
||||
switch (res.getResult()) {
|
||||
case DigraphResult.RES_HANDLED:
|
||||
case DigraphResult.RES_BAD:
|
||||
editorState.getCommandBuilder().addKey(key);
|
||||
return true;
|
||||
|
||||
case DigraphResult.RES_DONE:
|
||||
@@ -637,10 +666,20 @@ public class KeyHandler {
|
||||
if (stroke == null) {
|
||||
return false;
|
||||
}
|
||||
editorState.getCommandBuilder().addKey(key);
|
||||
handleKey(editor, stroke, context);
|
||||
return true;
|
||||
|
||||
case DigraphResult.RES_BAD:
|
||||
// BAD is an error. We were expecting a valid character, and we didn't get it.
|
||||
if (commandBuilder.getExpectedArgumentType() != null) {
|
||||
commandBuilder.setCommandState(CurrentCommandState.BAD_COMMAND);
|
||||
}
|
||||
return true;
|
||||
|
||||
case DigraphResult.RES_UNHANDLED:
|
||||
// UNHANDLED means the key stroke made no sense in the context of a digraph, but isn't an error in the current
|
||||
// state. E.g. waiting for {char} <BS> {char}. Let the key handler have a go at it.
|
||||
if (commandBuilder.getExpectedArgumentType() == Argument.Type.DIGRAPH) {
|
||||
commandBuilder.fallbackToCharacterArgument();
|
||||
handleKey(editor, key, context);
|
||||
@@ -653,7 +692,6 @@ public class KeyHandler {
|
||||
}
|
||||
|
||||
private void executeCommand(@NotNull Editor editor,
|
||||
@NotNull KeyStroke key,
|
||||
@NotNull DataContext context,
|
||||
@NotNull CommandState editorState) {
|
||||
final Command command = editorState.getCommandBuilder().buildCommand();
|
||||
@@ -676,7 +714,7 @@ public class KeyHandler {
|
||||
}
|
||||
|
||||
if (ApplicationManager.getApplication().isDispatchThread()) {
|
||||
Runnable action = new ActionRunner(editor, context, command, key);
|
||||
Runnable action = new ActionRunner(editor, context, command);
|
||||
EditorActionHandlerBase cmdAction = command.getAction();
|
||||
String name = cmdAction.getId();
|
||||
|
||||
@@ -714,7 +752,7 @@ public class KeyHandler {
|
||||
}
|
||||
else {
|
||||
final Argument.Type argumentType = action.getArgumentType();
|
||||
startWaitingForArgument(editor, context, key.getKeyChar(), argumentType, editorState);
|
||||
startWaitingForArgument(editor, context, key.getKeyChar(), action, argumentType, editorState);
|
||||
partialReset(editor);
|
||||
}
|
||||
|
||||
@@ -751,6 +789,7 @@ public class KeyHandler {
|
||||
private void startWaitingForArgument(Editor editor,
|
||||
DataContext context,
|
||||
char key,
|
||||
@NotNull EditorActionHandlerBase action,
|
||||
@NotNull Argument.Type argument,
|
||||
CommandState editorState) {
|
||||
final CommandBuilder commandBuilder = editorState.getCommandBuilder();
|
||||
@@ -761,6 +800,17 @@ public class KeyHandler {
|
||||
}
|
||||
editorState.pushModes(editorState.getMode(), CommandState.SubMode.OP_PENDING);
|
||||
break;
|
||||
case DIGRAPH:
|
||||
// Command actions represent the completion of a command. Showcmd relies on this - if the action represents a
|
||||
// part of a command, the showcmd output is reset part way through. This means we need to special case entering
|
||||
// digraph/literal input mode. We have an action that takes a digraph as an argument, and pushes it back through
|
||||
// the key handler when it's complete.
|
||||
if (action instanceof InsertCompletedDigraphAction) {
|
||||
editorState.startDigraphSequence();
|
||||
} else if (action instanceof InsertCompletedLiteralAction) {
|
||||
editorState.startLiteralSequence();
|
||||
}
|
||||
break;
|
||||
case EX_STRING:
|
||||
// The current Command expects an EX_STRING argument. E.g. SearchEntry(Fwd|Rev)Action. This won't execute until
|
||||
// state hits READY. Start the ex input field, push CMD_LINE mode and wait for the argument.
|
||||
@@ -884,11 +934,10 @@ public class KeyHandler {
|
||||
*/
|
||||
static class ActionRunner implements Runnable {
|
||||
@Contract(pure = true)
|
||||
ActionRunner(Editor editor, DataContext context, Command cmd, KeyStroke key) {
|
||||
ActionRunner(Editor editor, DataContext context, Command cmd) {
|
||||
this.editor = editor;
|
||||
this.context = context;
|
||||
this.cmd = cmd;
|
||||
this.key = key;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -897,6 +946,11 @@ public class KeyHandler {
|
||||
|
||||
editorState.getCommandBuilder().setCommandState(CurrentCommandState.NEW_COMMAND);
|
||||
|
||||
final Character register = cmd.getRegister();
|
||||
if (register != null) {
|
||||
VimPlugin.getRegister().selectRegister(register);
|
||||
}
|
||||
|
||||
executeVimAction(editor, cmd.getAction(), context);
|
||||
if (editorState.getMode() == CommandState.Mode.INSERT || editorState.getMode() == CommandState.Mode.REPLACE) {
|
||||
VimPlugin.getChange().processCommand(editor, cmd);
|
||||
@@ -905,10 +959,8 @@ public class KeyHandler {
|
||||
// Now the command has been executed let's clean up a few things.
|
||||
|
||||
// By default, the "empty" register is used by all commands, so we want to reset whatever the last register
|
||||
// selected by the user was to the empty register - unless we just executed the "select register" command.
|
||||
if (cmd.getType() != Command.Type.SELECT_REGISTER) {
|
||||
VimPlugin.getRegister().resetRegister();
|
||||
}
|
||||
// selected by the user was to the empty register
|
||||
VimPlugin.getRegister().resetRegister();
|
||||
|
||||
// If, at this point, we are not in insert, replace, or visual modes, we need to restore the previous
|
||||
// mode we were in. This handles commands in those modes that temporarily allow us to execute normal
|
||||
@@ -925,7 +977,6 @@ public class KeyHandler {
|
||||
private final Editor editor;
|
||||
private final DataContext context;
|
||||
private final Command cmd;
|
||||
private final KeyStroke key;
|
||||
}
|
||||
|
||||
private TypedActionHandler origHandler;
|
||||
|
@@ -61,6 +61,7 @@ import javax.swing.SwingConstants
|
||||
|
||||
private class StatusBarIconProvider : StatusBarWidgetProvider {
|
||||
override fun getWidget(project: Project): VimStatusBar? {
|
||||
@Suppress("DEPRECATION")
|
||||
if (!OptionsManager.ideastatusbar.isSet) return null
|
||||
if (OptionsManager.ideastatusicon.value == IdeaStatusIcon.disabled) return null
|
||||
return VimStatusBar
|
||||
@@ -69,6 +70,10 @@ private class StatusBarIconProvider : StatusBarWidgetProvider {
|
||||
|
||||
object VimStatusBar : StatusBarWidget, StatusBarWidget.IconPresentation {
|
||||
|
||||
init {
|
||||
OptionsManager.ideastatusicon.addOptionChangeListener { _, _ -> this.update() }
|
||||
}
|
||||
|
||||
private var statusBar: StatusBar? = null
|
||||
|
||||
override fun ID(): String = "IdeaVim-Icon"
|
||||
|
@@ -50,6 +50,7 @@ class VimLocalConfig : PersistentStateComponent<Element> {
|
||||
|
||||
companion object {
|
||||
fun initialize() {
|
||||
@Suppress("DEPRECATION")
|
||||
ServiceManager.getService(VimLocalConfig::class.java)
|
||||
}
|
||||
}
|
||||
|
@@ -114,7 +114,8 @@ public class VimPlugin implements PersistentStateComponent<Element>, Disposable
|
||||
Application application = ApplicationManager.getApplication();
|
||||
if (application.isUnitTestMode()) {
|
||||
application.invokeAndWait(this::turnOnPlugin);
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
application.invokeLater(this::turnOnPlugin);
|
||||
}
|
||||
}
|
||||
@@ -392,10 +393,6 @@ public class VimPlugin implements PersistentStateComponent<Element>, Disposable
|
||||
private void turnOnPlugin() {
|
||||
ApplicationManager.getApplication().invokeLater(this::updateState);
|
||||
|
||||
getEditor().turnOn();
|
||||
getSearch().turnOn();
|
||||
VimListenerManager.INSTANCE.turnOn();
|
||||
|
||||
// Register vim actions in command mode
|
||||
RegisterActions.registerActions();
|
||||
|
||||
@@ -407,17 +404,16 @@ public class VimPlugin implements PersistentStateComponent<Element>, Disposable
|
||||
|
||||
// Execute ~/.ideavimrc
|
||||
registerIdeavimrc();
|
||||
|
||||
// Turing on should be performed after all commands registration
|
||||
getEditor().turnOn();
|
||||
getSearch().turnOn();
|
||||
VimListenerManager.INSTANCE.turnOn();
|
||||
}
|
||||
|
||||
private void turnOffPlugin() {
|
||||
KeyHandler.getInstance().fullReset(null);
|
||||
|
||||
// Unregister vim actions in command mode
|
||||
RegisterActions.unregisterActions();
|
||||
|
||||
// Unregister ex handlers
|
||||
CommandParser.getInstance().unregisterHandlers();
|
||||
|
||||
EditorGroup editorGroup = getEditorIfCreated();
|
||||
if (editorGroup != null) {
|
||||
editorGroup.turnOff();
|
||||
@@ -428,6 +424,12 @@ public class VimPlugin implements PersistentStateComponent<Element>, Disposable
|
||||
}
|
||||
VimListenerManager.INSTANCE.turnOff();
|
||||
ExEntryPanel.fullReset();
|
||||
|
||||
// Unregister vim actions in command mode
|
||||
RegisterActions.unregisterActions();
|
||||
|
||||
// Unregister ex handlers
|
||||
CommandParser.getInstance().unregisterHandlers();
|
||||
}
|
||||
|
||||
private boolean stateUpdated = false;
|
||||
|
@@ -1,61 +0,0 @@
|
||||
/*
|
||||
* IdeaVim - Vim emulator for IDEs based on the IntelliJ platform
|
||||
* Copyright (C) 2003-2020 The IdeaVim authors
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.maddyhome.idea.vim;
|
||||
|
||||
import com.intellij.openapi.actionSystem.DataContext;
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.openapi.editor.Editor;
|
||||
import com.intellij.openapi.editor.actionSystem.ActionPlan;
|
||||
import com.intellij.openapi.editor.actionSystem.TypedActionHandler;
|
||||
import com.intellij.openapi.editor.actionSystem.TypedActionHandlerEx;
|
||||
import com.maddyhome.idea.vim.helper.EditorDataContext;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import javax.swing.*;
|
||||
|
||||
/**
|
||||
* Accepts all regular keystrokes and passes them on to the Vim key handler.
|
||||
*
|
||||
* IDE shortcut keys used by Vim commands are handled by {@link com.maddyhome.idea.vim.action.VimShortcutKeyAction}.
|
||||
*/
|
||||
public class VimTypedActionHandler implements TypedActionHandlerEx {
|
||||
private static final Logger logger = Logger.getInstance(VimTypedActionHandler.class.getName());
|
||||
|
||||
private final @NotNull KeyHandler handler;
|
||||
|
||||
public VimTypedActionHandler(TypedActionHandler origHandler) {
|
||||
handler = KeyHandler.getInstance();
|
||||
handler.setOriginalHandler(origHandler);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void beforeExecute(@NotNull Editor editor, char charTyped, @NotNull DataContext context, @NotNull ActionPlan plan) {
|
||||
handler.beforeHandleKey(editor, KeyStroke.getKeyStroke(charTyped), context, plan);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute(final @NotNull Editor editor, final char charTyped, final @NotNull DataContext context) {
|
||||
try {
|
||||
handler.handleKey(editor, KeyStroke.getKeyStroke(charTyped), new EditorDataContext(editor));
|
||||
}
|
||||
catch (Throwable e) {
|
||||
logger.error(e);
|
||||
}
|
||||
}
|
||||
}
|
74
src/com/maddyhome/idea/vim/VimTypedActionHandler.kt
Normal file
74
src/com/maddyhome/idea/vim/VimTypedActionHandler.kt
Normal file
@@ -0,0 +1,74 @@
|
||||
/*
|
||||
* IdeaVim - Vim emulator for IDEs based on the IntelliJ platform
|
||||
* Copyright (C) 2003-2020 The IdeaVim authors
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.maddyhome.idea.vim
|
||||
|
||||
import com.intellij.openapi.actionSystem.DataContext
|
||||
import com.intellij.openapi.diagnostic.logger
|
||||
import com.intellij.openapi.editor.Editor
|
||||
import com.intellij.openapi.editor.actionSystem.ActionPlan
|
||||
import com.intellij.openapi.editor.actionSystem.TypedActionHandler
|
||||
import com.intellij.openapi.editor.actionSystem.TypedActionHandlerEx
|
||||
import com.maddyhome.idea.vim.helper.EditorDataContext
|
||||
import java.awt.event.KeyAdapter
|
||||
import java.awt.event.KeyEvent
|
||||
import javax.swing.KeyStroke
|
||||
|
||||
/**
|
||||
* Accepts all regular keystrokes and passes them on to the Vim key handler.
|
||||
*
|
||||
* IDE shortcut keys used by Vim commands are handled by [com.maddyhome.idea.vim.action.VimShortcutKeyAction].
|
||||
*/
|
||||
class VimTypedActionHandler(origHandler: TypedActionHandler?) : TypedActionHandlerEx {
|
||||
private val handler = KeyHandler.getInstance()
|
||||
|
||||
init {
|
||||
handler.originalHandler = origHandler
|
||||
}
|
||||
|
||||
override fun beforeExecute(editor: Editor, charTyped: Char, context: DataContext, plan: ActionPlan) {
|
||||
val modifiers = if (charTyped == ' ' && VimKeyListener.isSpaceShift) KeyEvent.SHIFT_DOWN_MASK else 0
|
||||
val keyStroke = KeyStroke.getKeyStroke(charTyped, modifiers)
|
||||
handler.beforeHandleKey(editor, keyStroke, context, plan)
|
||||
}
|
||||
|
||||
override fun execute(editor: Editor, charTyped: Char, context: DataContext) {
|
||||
try {
|
||||
val modifiers = if (charTyped == ' ' && VimKeyListener.isSpaceShift) KeyEvent.SHIFT_DOWN_MASK else 0
|
||||
val keyStroke = KeyStroke.getKeyStroke(charTyped, modifiers)
|
||||
handler.handleKey(editor, keyStroke, EditorDataContext(editor))
|
||||
} catch (e: Throwable) {
|
||||
logger.error(e)
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val logger = logger<VimTypedActionHandler>()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A nasty workaround to handle `<S-Space>` events. Probably all the key events should go trough this listener.
|
||||
*/
|
||||
object VimKeyListener : KeyAdapter() {
|
||||
|
||||
var isSpaceShift = false
|
||||
|
||||
override fun keyPressed(e: KeyEvent) {
|
||||
isSpaceShift = e.modifiersEx and KeyEvent.SHIFT_DOWN_MASK != 0 && e.keyChar == ' '
|
||||
}
|
||||
}
|
@@ -86,27 +86,26 @@ class VimShortcutKeyAction : AnAction(), DumbAware {
|
||||
@Suppress("DEPRECATION")
|
||||
val SMART_STEP_INPLACE_DATA = Key.findKeyByName("SMART_STEP_INPLACE_DATA")
|
||||
if (SMART_STEP_INPLACE_DATA != null && editor.getUserData(SMART_STEP_INPLACE_DATA) != null) return false
|
||||
|
||||
if (aceJumpActive()) return false
|
||||
|
||||
val keyCode = keyStroke.keyCode
|
||||
if (LookupManager.getActiveLookup(editor) != null) {
|
||||
return LookupKeys.isEnabledForLookup(keyStroke)
|
||||
}
|
||||
if (keyCode == KeyEvent.VK_ESCAPE) {
|
||||
return isEnabledForEscape(editor)
|
||||
}
|
||||
|
||||
if (LookupManager.getActiveLookup(editor) != null && !LookupKeys.isEnabledForLookup(keyStroke)) return false
|
||||
|
||||
if (keyCode == KeyEvent.VK_ESCAPE) return isEnabledForEscape(editor)
|
||||
|
||||
if (editor.inInsertMode) { // XXX: <Tab> won't be recorded in macros
|
||||
if (keyCode == KeyEvent.VK_TAB) {
|
||||
VimPlugin.getChange().tabAction = true
|
||||
return false
|
||||
}
|
||||
// Debug watch, Python console, etc.
|
||||
if (NON_FILE_EDITOR_KEYS.contains(keyStroke) && !EditorHelper.isFileEditor(editor)) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
if (VIM_ONLY_EDITOR_KEYS.contains(keyStroke)) {
|
||||
return true
|
||||
if (keyStroke in NON_FILE_EDITOR_KEYS && !EditorHelper.isFileEditor(editor)) return false
|
||||
}
|
||||
|
||||
if (keyStroke in VIM_ONLY_EDITOR_KEYS) return true
|
||||
|
||||
val savedShortcutConflicts = VimPlugin.getKey().savedShortcutConflicts
|
||||
return when (savedShortcutConflicts[keyStroke]) {
|
||||
ShortcutOwner.VIM -> true
|
||||
@@ -191,15 +190,15 @@ class VimShortcutKeyAction : AnAction(), DumbAware {
|
||||
companion object {
|
||||
@JvmField
|
||||
val VIM_ONLY_EDITOR_KEYS: Set<KeyStroke> = ImmutableSet.builder<KeyStroke>().addAll(getKeyStrokes(KeyEvent.VK_ENTER, 0)).addAll(getKeyStrokes(KeyEvent.VK_ESCAPE, 0))
|
||||
.addAll(getKeyStrokes(KeyEvent.VK_TAB, 0)).addAll(getKeyStrokes(KeyEvent.VK_BACK_SPACE, 0, InputEvent.CTRL_MASK))
|
||||
.addAll(getKeyStrokes(KeyEvent.VK_INSERT, 0)).addAll(getKeyStrokes(KeyEvent.VK_DELETE, 0, InputEvent.CTRL_MASK))
|
||||
.addAll(getKeyStrokes(KeyEvent.VK_UP, 0, InputEvent.CTRL_MASK, InputEvent.SHIFT_MASK)).addAll(getKeyStrokes(KeyEvent.VK_DOWN, 0, InputEvent.CTRL_MASK, InputEvent.SHIFT_MASK))
|
||||
.addAll(getKeyStrokes(KeyEvent.VK_LEFT, 0, InputEvent.CTRL_MASK, InputEvent.SHIFT_MASK, InputEvent.CTRL_MASK or InputEvent.SHIFT_MASK))
|
||||
.addAll(getKeyStrokes(KeyEvent.VK_RIGHT, 0, InputEvent.CTRL_MASK, InputEvent.SHIFT_MASK, InputEvent.CTRL_MASK or InputEvent.SHIFT_MASK))
|
||||
.addAll(getKeyStrokes(KeyEvent.VK_HOME, 0, InputEvent.CTRL_MASK, InputEvent.SHIFT_MASK, InputEvent.CTRL_MASK or InputEvent.SHIFT_MASK))
|
||||
.addAll(getKeyStrokes(KeyEvent.VK_END, 0, InputEvent.CTRL_MASK, InputEvent.SHIFT_MASK, InputEvent.CTRL_MASK or InputEvent.SHIFT_MASK))
|
||||
.addAll(getKeyStrokes(KeyEvent.VK_PAGE_UP, 0, InputEvent.SHIFT_MASK, InputEvent.CTRL_MASK or InputEvent.SHIFT_MASK))
|
||||
.addAll(getKeyStrokes(KeyEvent.VK_PAGE_DOWN, 0, InputEvent.SHIFT_MASK, InputEvent.CTRL_MASK or InputEvent.SHIFT_MASK)).build()
|
||||
.addAll(getKeyStrokes(KeyEvent.VK_TAB, 0)).addAll(getKeyStrokes(KeyEvent.VK_BACK_SPACE, 0, InputEvent.CTRL_DOWN_MASK))
|
||||
.addAll(getKeyStrokes(KeyEvent.VK_INSERT, 0)).addAll(getKeyStrokes(KeyEvent.VK_DELETE, 0, InputEvent.CTRL_DOWN_MASK))
|
||||
.addAll(getKeyStrokes(KeyEvent.VK_UP, 0, InputEvent.CTRL_DOWN_MASK, InputEvent.SHIFT_DOWN_MASK)).addAll(getKeyStrokes(KeyEvent.VK_DOWN, 0, InputEvent.CTRL_DOWN_MASK, InputEvent.SHIFT_DOWN_MASK))
|
||||
.addAll(getKeyStrokes(KeyEvent.VK_LEFT, 0, InputEvent.CTRL_DOWN_MASK, InputEvent.SHIFT_DOWN_MASK, InputEvent.CTRL_DOWN_MASK or InputEvent.SHIFT_DOWN_MASK))
|
||||
.addAll(getKeyStrokes(KeyEvent.VK_RIGHT, 0, InputEvent.CTRL_DOWN_MASK, InputEvent.SHIFT_DOWN_MASK, InputEvent.CTRL_DOWN_MASK or InputEvent.SHIFT_DOWN_MASK))
|
||||
.addAll(getKeyStrokes(KeyEvent.VK_HOME, 0, InputEvent.CTRL_DOWN_MASK, InputEvent.SHIFT_DOWN_MASK, InputEvent.CTRL_DOWN_MASK or InputEvent.SHIFT_DOWN_MASK))
|
||||
.addAll(getKeyStrokes(KeyEvent.VK_END, 0, InputEvent.CTRL_DOWN_MASK, InputEvent.SHIFT_DOWN_MASK, InputEvent.CTRL_DOWN_MASK or InputEvent.SHIFT_DOWN_MASK))
|
||||
.addAll(getKeyStrokes(KeyEvent.VK_PAGE_UP, 0, InputEvent.SHIFT_DOWN_MASK, InputEvent.CTRL_DOWN_MASK or InputEvent.SHIFT_DOWN_MASK))
|
||||
.addAll(getKeyStrokes(KeyEvent.VK_PAGE_DOWN, 0, InputEvent.SHIFT_DOWN_MASK, InputEvent.CTRL_DOWN_MASK or InputEvent.SHIFT_DOWN_MASK)).build()
|
||||
|
||||
private const val ACTION_ID = "VimShortcutKeyAction"
|
||||
|
||||
|
@@ -64,7 +64,7 @@ class RepeatChangeAction : VimActionHandler.SingleExecution() {
|
||||
}
|
||||
state.setExecutingCommand(lastCommand)
|
||||
|
||||
KeyHandler.executeVimAction(editor, lastCommand.action, context)
|
||||
KeyHandler.executeVimAction(editor, lastCommand.action!!, context)
|
||||
|
||||
VimRepeater.saveLastChange(lastCommand)
|
||||
}
|
||||
|
@@ -23,14 +23,12 @@ import com.intellij.openapi.editor.Editor
|
||||
import com.maddyhome.idea.vim.VimPlugin
|
||||
import com.maddyhome.idea.vim.command.Command
|
||||
import com.maddyhome.idea.vim.command.CommandFlags
|
||||
import com.maddyhome.idea.vim.command.CommandState.SubMode
|
||||
import com.maddyhome.idea.vim.command.SelectionType
|
||||
import com.maddyhome.idea.vim.common.TextRange
|
||||
import com.maddyhome.idea.vim.group.visual.VimSelection
|
||||
import com.maddyhome.idea.vim.handler.VisualOperatorActionHandler
|
||||
import com.maddyhome.idea.vim.helper.EditorHelper
|
||||
import com.maddyhome.idea.vim.helper.enumSetOf
|
||||
import com.maddyhome.idea.vim.helper.subMode
|
||||
import java.util.*
|
||||
|
||||
/**
|
||||
@@ -46,15 +44,18 @@ class DeleteVisualLinesAction : VisualOperatorActionHandler.ForEachCaret() {
|
||||
context: DataContext,
|
||||
cmd: Command,
|
||||
range: VimSelection): Boolean {
|
||||
val mode = editor.subMode
|
||||
val textRange = range.toVimTextRange(false)
|
||||
return if (mode == SubMode.VISUAL_BLOCK) {
|
||||
VimPlugin.getChange()
|
||||
.deleteRange(editor, editor.caretModel.primaryCaret, textRange, SelectionType.fromSubMode(mode), false)
|
||||
} else {
|
||||
val lineRange = TextRange(EditorHelper.getLineStartForOffset(editor, textRange.startOffset),
|
||||
EditorHelper.getLineEndForOffset(editor, textRange.endOffset) + 1)
|
||||
VimPlugin.getChange().deleteRange(editor, caret, lineRange, SelectionType.LINE_WISE, false)
|
||||
val (usedCaret, usedRange, usedType) = when (range.type) {
|
||||
SelectionType.BLOCK_WISE -> Triple(editor.caretModel.primaryCaret, textRange, range.type)
|
||||
SelectionType.LINE_WISE -> Triple(caret, textRange, SelectionType.LINE_WISE)
|
||||
SelectionType.CHARACTER_WISE -> {
|
||||
val lineRange = TextRange(
|
||||
EditorHelper.getLineStartForOffset(editor, textRange.startOffset),
|
||||
EditorHelper.getLineEndForOffset(editor, textRange.endOffset) + 1
|
||||
)
|
||||
Triple(caret, lineRange, SelectionType.LINE_WISE)
|
||||
}
|
||||
}
|
||||
return VimPlugin.getChange().deleteRange(editor, usedCaret, usedRange, usedType, false)
|
||||
}
|
||||
}
|
||||
|
@@ -0,0 +1,21 @@
|
||||
package com.maddyhome.idea.vim.action.change.insert
|
||||
|
||||
import com.intellij.openapi.actionSystem.DataContext
|
||||
import com.intellij.openapi.editor.Editor
|
||||
import com.maddyhome.idea.vim.KeyHandler
|
||||
import com.maddyhome.idea.vim.command.Argument
|
||||
import com.maddyhome.idea.vim.command.Command
|
||||
import com.maddyhome.idea.vim.handler.VimActionHandler
|
||||
import javax.swing.KeyStroke
|
||||
|
||||
class InsertCompletedDigraphAction : VimActionHandler.SingleExecution() {
|
||||
override val type: Command.Type = Command.Type.INSERT
|
||||
override val argumentType: Argument.Type? = Argument.Type.DIGRAPH
|
||||
|
||||
override fun execute(editor: Editor, context: DataContext, cmd: Command): Boolean {
|
||||
// The converted digraph character has been captured as an argument, push it back through key handler
|
||||
val keyStroke = KeyStroke.getKeyStroke(cmd.argument!!.character)
|
||||
KeyHandler.getInstance().handleKey(editor, keyStroke, context)
|
||||
return true
|
||||
}
|
||||
}
|
@@ -0,0 +1,21 @@
|
||||
package com.maddyhome.idea.vim.action.change.insert
|
||||
|
||||
import com.intellij.openapi.actionSystem.DataContext
|
||||
import com.intellij.openapi.editor.Editor
|
||||
import com.maddyhome.idea.vim.KeyHandler
|
||||
import com.maddyhome.idea.vim.command.Argument
|
||||
import com.maddyhome.idea.vim.command.Command
|
||||
import com.maddyhome.idea.vim.handler.VimActionHandler
|
||||
import javax.swing.KeyStroke
|
||||
|
||||
class InsertCompletedLiteralAction : VimActionHandler.SingleExecution() {
|
||||
override val type: Command.Type = Command.Type.INSERT
|
||||
override val argumentType: Argument.Type? = Argument.Type.DIGRAPH
|
||||
|
||||
override fun execute(editor: Editor, context: DataContext, cmd: Command): Boolean {
|
||||
// The converted literal character has been captured as an argument, push it back through key handler
|
||||
val keyStroke = KeyStroke.getKeyStroke(cmd.argument!!.character)
|
||||
KeyHandler.getInstance().handleKey(editor, keyStroke, context)
|
||||
return true
|
||||
}
|
||||
}
|
@@ -29,9 +29,9 @@ import javax.swing.KeyStroke
|
||||
|
||||
class InsertPreviousInsertExitAction : ChangeEditorActionHandler.SingleExecution(), ComplicatedKeysAction {
|
||||
override val keyStrokesSet: Set<List<KeyStroke>> = setOf(
|
||||
listOf(KeyStroke.getKeyStroke(KeyEvent.VK_2, KeyEvent.CTRL_MASK or KeyEvent.SHIFT_MASK)),
|
||||
listOf(KeyStroke.getKeyStroke(KeyEvent.VK_2, KeyEvent.CTRL_MASK)),
|
||||
listOf(KeyStroke.getKeyStroke(KeyEvent.VK_AT, KeyEvent.CTRL_MASK))
|
||||
listOf(KeyStroke.getKeyStroke(KeyEvent.VK_2, KeyEvent.CTRL_DOWN_MASK or KeyEvent.SHIFT_DOWN_MASK)),
|
||||
listOf(KeyStroke.getKeyStroke(KeyEvent.VK_2, KeyEvent.CTRL_DOWN_MASK)),
|
||||
listOf(KeyStroke.getKeyStroke(KeyEvent.VK_AT, KeyEvent.CTRL_DOWN_MASK))
|
||||
)
|
||||
|
||||
override val type: Command.Type = Command.Type.INSERT
|
||||
|
@@ -1,16 +0,0 @@
|
||||
package com.maddyhome.idea.vim.action.change.insert
|
||||
|
||||
import com.intellij.openapi.actionSystem.DataContext
|
||||
import com.intellij.openapi.editor.Editor
|
||||
import com.maddyhome.idea.vim.KeyHandler
|
||||
import com.maddyhome.idea.vim.command.Command
|
||||
import com.maddyhome.idea.vim.handler.VimActionHandler
|
||||
|
||||
class StartInsertDigraphAction : VimActionHandler.SingleExecution() {
|
||||
override val type: Command.Type = Command.Type.INSERT
|
||||
|
||||
override fun execute(editor: Editor, context: DataContext, cmd: Command): Boolean {
|
||||
KeyHandler.getInstance().startDigraphSequence(editor)
|
||||
return true
|
||||
}
|
||||
}
|
@@ -1,16 +0,0 @@
|
||||
package com.maddyhome.idea.vim.action.change.insert
|
||||
|
||||
import com.intellij.openapi.actionSystem.DataContext
|
||||
import com.intellij.openapi.editor.Editor
|
||||
import com.maddyhome.idea.vim.KeyHandler
|
||||
import com.maddyhome.idea.vim.command.Command
|
||||
import com.maddyhome.idea.vim.handler.VimActionHandler
|
||||
|
||||
class StartInsertLiteralAction : VimActionHandler.SingleExecution() {
|
||||
override val type: Command.Type = Command.Type.INSERT
|
||||
|
||||
override fun execute(editor: Editor, context: DataContext, cmd: Command): Boolean {
|
||||
KeyHandler.getInstance().startLiteralSequence(editor)
|
||||
return true
|
||||
}
|
||||
}
|
@@ -26,17 +26,27 @@ import com.maddyhome.idea.vim.group.copy.PutData
|
||||
import com.maddyhome.idea.vim.group.copy.PutData.TextData
|
||||
import com.maddyhome.idea.vim.handler.ChangeEditorActionHandler
|
||||
|
||||
class PutTextBeforeCursorAction : ChangeEditorActionHandler.SingleExecution() {
|
||||
sealed class PutTextBaseAction(
|
||||
private val insertTextBeforeCaret: Boolean,
|
||||
private val indent: Boolean,
|
||||
private val caretAfterInsertedText: Boolean
|
||||
) : ChangeEditorActionHandler.SingleExecution() {
|
||||
override val type: Command.Type = Command.Type.OTHER_SELF_SYNCHRONIZED
|
||||
|
||||
override fun execute(editor: Editor,
|
||||
context: DataContext,
|
||||
count: Int,
|
||||
rawCount: Int,
|
||||
argument: Argument?): Boolean {
|
||||
override fun execute(editor: Editor, context: DataContext, count: Int, rawCount: Int, argument: Argument?): Boolean {
|
||||
val lastRegister = VimPlugin.getRegister().lastRegister
|
||||
val textData = if (lastRegister != null) TextData(lastRegister.text, lastRegister.type, lastRegister.transferableData) else null
|
||||
val putData = PutData(textData, null, count, insertTextBeforeCaret = true, _indent = true, caretAfterInsertedText = false, putToLine = -1)
|
||||
val putData = PutData(textData, null, count, insertTextBeforeCaret, indent, caretAfterInsertedText, -1)
|
||||
return VimPlugin.getPut().putText(editor, context, putData)
|
||||
}
|
||||
}
|
||||
|
||||
class PutTextAfterCursorAction : PutTextBaseAction(insertTextBeforeCaret = false, indent = true, caretAfterInsertedText = false)
|
||||
class PutTextAfterCursorActionMoveCursor : PutTextBaseAction(insertTextBeforeCaret = false, indent = true, caretAfterInsertedText = true)
|
||||
|
||||
class PutTextAfterCursorNoIndentAction : PutTextBaseAction(insertTextBeforeCaret = false, indent = false, caretAfterInsertedText = false)
|
||||
class PutTextBeforeCursorNoIndentAction : PutTextBaseAction(insertTextBeforeCaret = true, indent = false, caretAfterInsertedText = false)
|
||||
|
||||
class PutTextBeforeCursorAction : PutTextBaseAction(insertTextBeforeCaret = true, indent = true, caretAfterInsertedText = false)
|
||||
class PutTextBeforeCursorActionMoveCursor : PutTextBaseAction(insertTextBeforeCaret = true, indent = true, caretAfterInsertedText = true)
|
||||
|
@@ -1,42 +0,0 @@
|
||||
/*
|
||||
* IdeaVim - Vim emulator for IDEs based on the IntelliJ platform
|
||||
* Copyright (C) 2003-2020 The IdeaVim authors
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.maddyhome.idea.vim.action.copy
|
||||
|
||||
import com.intellij.openapi.actionSystem.DataContext
|
||||
import com.intellij.openapi.editor.Editor
|
||||
import com.maddyhome.idea.vim.VimPlugin
|
||||
import com.maddyhome.idea.vim.command.Argument
|
||||
import com.maddyhome.idea.vim.command.Command
|
||||
import com.maddyhome.idea.vim.group.copy.PutData
|
||||
import com.maddyhome.idea.vim.group.copy.PutData.TextData
|
||||
import com.maddyhome.idea.vim.handler.ChangeEditorActionHandler
|
||||
|
||||
class PutTextAfterCursorAction : ChangeEditorActionHandler.SingleExecution() {
|
||||
override val type: Command.Type = Command.Type.OTHER_SELF_SYNCHRONIZED
|
||||
|
||||
override fun execute(editor: Editor,
|
||||
context: DataContext,
|
||||
count: Int,
|
||||
rawCount: Int,
|
||||
argument: Argument?): Boolean {
|
||||
val lastRegister = VimPlugin.getRegister().lastRegister
|
||||
val textData = if (lastRegister != null) TextData(lastRegister.text, lastRegister.type, lastRegister.transferableData) else null
|
||||
val putData = PutData(textData, null, count, insertTextBeforeCaret = false, _indent = true, caretAfterInsertedText = false, putToLine = -1)
|
||||
return VimPlugin.getPut().putText(editor, context, putData)
|
||||
}
|
||||
}
|
@@ -1,42 +0,0 @@
|
||||
/*
|
||||
* IdeaVim - Vim emulator for IDEs based on the IntelliJ platform
|
||||
* Copyright (C) 2003-2020 The IdeaVim authors
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.maddyhome.idea.vim.action.copy
|
||||
|
||||
import com.intellij.openapi.actionSystem.DataContext
|
||||
import com.intellij.openapi.editor.Editor
|
||||
import com.maddyhome.idea.vim.VimPlugin
|
||||
import com.maddyhome.idea.vim.command.Argument
|
||||
import com.maddyhome.idea.vim.command.Command
|
||||
import com.maddyhome.idea.vim.group.copy.PutData
|
||||
import com.maddyhome.idea.vim.group.copy.PutData.TextData
|
||||
import com.maddyhome.idea.vim.handler.ChangeEditorActionHandler
|
||||
|
||||
class PutTextAfterCursorActionMoveCursor : ChangeEditorActionHandler.SingleExecution() {
|
||||
override val type: Command.Type = Command.Type.OTHER_SELF_SYNCHRONIZED
|
||||
|
||||
override fun execute(editor: Editor,
|
||||
context: DataContext,
|
||||
count: Int,
|
||||
rawCount: Int,
|
||||
argument: Argument?): Boolean {
|
||||
val lastRegister = VimPlugin.getRegister().lastRegister
|
||||
val textData = if (lastRegister != null) TextData(lastRegister.text, lastRegister.type, lastRegister.transferableData) else null
|
||||
val putData = PutData(textData, null, count, false, _indent = true, caretAfterInsertedText = true, putToLine = -1)
|
||||
return VimPlugin.getPut().putText(editor, context, putData)
|
||||
}
|
||||
}
|
@@ -1,42 +0,0 @@
|
||||
/*
|
||||
* IdeaVim - Vim emulator for IDEs based on the IntelliJ platform
|
||||
* Copyright (C) 2003-2020 The IdeaVim authors
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.maddyhome.idea.vim.action.copy
|
||||
|
||||
import com.intellij.openapi.actionSystem.DataContext
|
||||
import com.intellij.openapi.editor.Editor
|
||||
import com.maddyhome.idea.vim.VimPlugin
|
||||
import com.maddyhome.idea.vim.command.Argument
|
||||
import com.maddyhome.idea.vim.command.Command
|
||||
import com.maddyhome.idea.vim.group.copy.PutData
|
||||
import com.maddyhome.idea.vim.group.copy.PutData.TextData
|
||||
import com.maddyhome.idea.vim.handler.ChangeEditorActionHandler
|
||||
|
||||
class PutTextAfterCursorNoIndentAction : ChangeEditorActionHandler.SingleExecution() {
|
||||
override val type: Command.Type = Command.Type.OTHER_SELF_SYNCHRONIZED
|
||||
|
||||
override fun execute(editor: Editor,
|
||||
context: DataContext,
|
||||
count: Int,
|
||||
rawCount: Int,
|
||||
argument: Argument?): Boolean {
|
||||
val lastRegister = VimPlugin.getRegister().lastRegister
|
||||
val textData = if (lastRegister != null) TextData(lastRegister.text, lastRegister.type, lastRegister.transferableData) else null
|
||||
val putData = PutData(textData, null, count, insertTextBeforeCaret = false, _indent = false, caretAfterInsertedText = false, putToLine = -1)
|
||||
return VimPlugin.getPut().putText(editor, context, putData)
|
||||
}
|
||||
}
|
@@ -1,42 +0,0 @@
|
||||
/*
|
||||
* IdeaVim - Vim emulator for IDEs based on the IntelliJ platform
|
||||
* Copyright (C) 2003-2020 The IdeaVim authors
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.maddyhome.idea.vim.action.copy
|
||||
|
||||
import com.intellij.openapi.actionSystem.DataContext
|
||||
import com.intellij.openapi.editor.Editor
|
||||
import com.maddyhome.idea.vim.VimPlugin
|
||||
import com.maddyhome.idea.vim.command.Argument
|
||||
import com.maddyhome.idea.vim.command.Command
|
||||
import com.maddyhome.idea.vim.group.copy.PutData
|
||||
import com.maddyhome.idea.vim.group.copy.PutData.TextData
|
||||
import com.maddyhome.idea.vim.handler.ChangeEditorActionHandler
|
||||
|
||||
class PutTextBeforeCursorActionMoveCursor : ChangeEditorActionHandler.SingleExecution() {
|
||||
override val type: Command.Type = Command.Type.OTHER_SELF_SYNCHRONIZED
|
||||
|
||||
override fun execute(editor: Editor,
|
||||
context: DataContext,
|
||||
count: Int,
|
||||
rawCount: Int,
|
||||
argument: Argument?): Boolean {
|
||||
val lastRegister = VimPlugin.getRegister().lastRegister
|
||||
val textData = if (lastRegister != null) TextData(lastRegister.text, lastRegister.type, lastRegister.transferableData) else null
|
||||
val putData = PutData(textData, null, count, insertTextBeforeCaret = true, _indent = true, caretAfterInsertedText = true, putToLine = -1)
|
||||
return VimPlugin.getPut().putText(editor, context, putData)
|
||||
}
|
||||
}
|
@@ -1,42 +0,0 @@
|
||||
/*
|
||||
* IdeaVim - Vim emulator for IDEs based on the IntelliJ platform
|
||||
* Copyright (C) 2003-2020 The IdeaVim authors
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.maddyhome.idea.vim.action.copy
|
||||
|
||||
import com.intellij.openapi.actionSystem.DataContext
|
||||
import com.intellij.openapi.editor.Editor
|
||||
import com.maddyhome.idea.vim.VimPlugin
|
||||
import com.maddyhome.idea.vim.command.Argument
|
||||
import com.maddyhome.idea.vim.command.Command
|
||||
import com.maddyhome.idea.vim.group.copy.PutData
|
||||
import com.maddyhome.idea.vim.group.copy.PutData.TextData
|
||||
import com.maddyhome.idea.vim.handler.ChangeEditorActionHandler
|
||||
|
||||
class PutTextBeforeCursorNoIndentAction : ChangeEditorActionHandler.SingleExecution() {
|
||||
override val type: Command.Type = Command.Type.OTHER_SELF_SYNCHRONIZED
|
||||
|
||||
override fun execute(editor: Editor,
|
||||
context: DataContext,
|
||||
count: Int,
|
||||
rawCount: Int,
|
||||
argument: Argument?): Boolean {
|
||||
val lastRegister = VimPlugin.getRegister().lastRegister
|
||||
val textData = if (lastRegister != null) TextData(lastRegister.text, lastRegister.type, lastRegister.transferableData) else null
|
||||
val putData = PutData(textData, null, count, insertTextBeforeCaret = true, _indent = false, caretAfterInsertedText = false, putToLine = -1)
|
||||
return VimPlugin.getPut().putText(editor, context, putData)
|
||||
}
|
||||
}
|
@@ -33,23 +33,33 @@ import java.util.*
|
||||
/**
|
||||
* @author vlan
|
||||
*/
|
||||
class PutVisualTextAction : VisualOperatorActionHandler.SingleExecution() {
|
||||
sealed class PutVisualTextBaseAction(
|
||||
private val insertTextBeforeCaret: Boolean,
|
||||
private val indent: Boolean,
|
||||
private val caretAfterInsertedText: Boolean
|
||||
) : VisualOperatorActionHandler.SingleExecution() {
|
||||
|
||||
override val type: Command.Type = Command.Type.OTHER_SELF_SYNCHRONIZED
|
||||
|
||||
override val flags: EnumSet<CommandFlags> = enumSetOf(CommandFlags.FLAG_EXIT_VISUAL)
|
||||
override fun executeForAllCarets(editor: Editor,
|
||||
context: DataContext,
|
||||
cmd: Command,
|
||||
caretsAndSelections: Map<Caret, VimSelection>): Boolean {
|
||||
|
||||
override fun executeForAllCarets(editor: Editor, context: DataContext, cmd: Command, caretsAndSelections: Map<Caret, VimSelection>): Boolean {
|
||||
if (caretsAndSelections.isEmpty()) return false
|
||||
val textData = VimPlugin.getRegister().lastRegister?.let { PutData.TextData(it.text, it.type, it.transferableData) }
|
||||
VimPlugin.getRegister().resetRegister()
|
||||
|
||||
val insertTextBeforeCaret = cmd.keys[0].keyChar == 'P'
|
||||
val selection = PutData.VisualSelection(caretsAndSelections, caretsAndSelections.values.first().type)
|
||||
val putData = PutData(textData, selection, cmd.count, insertTextBeforeCaret, _indent = true, caretAfterInsertedText = false)
|
||||
val putData = PutData(textData, selection, cmd.count, insertTextBeforeCaret, indent, caretAfterInsertedText)
|
||||
|
||||
return VimPlugin.getPut().putText(editor, context, putData)
|
||||
}
|
||||
}
|
||||
|
||||
class PutVisualTextBeforeCursorAction: PutVisualTextBaseAction(insertTextBeforeCaret = true, indent = true, caretAfterInsertedText = false)
|
||||
class PutVisualTextAfterCursorAction: PutVisualTextBaseAction(insertTextBeforeCaret = false, indent = true, caretAfterInsertedText = false)
|
||||
|
||||
class PutVisualTextBeforeCursorNoIndentAction: PutVisualTextBaseAction(insertTextBeforeCaret = true, indent = false, caretAfterInsertedText = false)
|
||||
class PutVisualTextAfterCursorNoIndentAction: PutVisualTextBaseAction(insertTextBeforeCaret = false, indent = false, caretAfterInsertedText = false)
|
||||
|
||||
class PutVisualTextBeforeCursorMoveCursorAction: PutVisualTextBaseAction(insertTextBeforeCaret = true, indent = true, caretAfterInsertedText = true)
|
||||
class PutVisualTextAfterCursorMoveCursorAction: PutVisualTextBaseAction(insertTextBeforeCaret = false, indent = true, caretAfterInsertedText = true)
|
||||
|
@@ -1,53 +0,0 @@
|
||||
/*
|
||||
* IdeaVim - Vim emulator for IDEs based on the IntelliJ platform
|
||||
* Copyright (C) 2003-2020 The IdeaVim authors
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.maddyhome.idea.vim.action.copy
|
||||
|
||||
import com.intellij.openapi.actionSystem.DataContext
|
||||
import com.intellij.openapi.editor.Caret
|
||||
import com.intellij.openapi.editor.Editor
|
||||
import com.maddyhome.idea.vim.VimPlugin
|
||||
import com.maddyhome.idea.vim.command.Command
|
||||
import com.maddyhome.idea.vim.command.CommandFlags
|
||||
import com.maddyhome.idea.vim.group.copy.PutData
|
||||
import com.maddyhome.idea.vim.group.visual.VimSelection
|
||||
import com.maddyhome.idea.vim.handler.VisualOperatorActionHandler
|
||||
import com.maddyhome.idea.vim.helper.enumSetOf
|
||||
import java.util.*
|
||||
|
||||
/**
|
||||
* @author vlan
|
||||
*/
|
||||
class PutVisualTextMoveCursorAction : VisualOperatorActionHandler.SingleExecution() {
|
||||
|
||||
override val type: Command.Type = Command.Type.OTHER_SELF_SYNCHRONIZED
|
||||
|
||||
override val flags: EnumSet<CommandFlags> = enumSetOf(CommandFlags.FLAG_EXIT_VISUAL)
|
||||
|
||||
override fun executeForAllCarets(editor: Editor, context: DataContext, cmd: Command, caretsAndSelections: Map<Caret, VimSelection>): Boolean {
|
||||
if (caretsAndSelections.isEmpty()) return false
|
||||
val textData = VimPlugin.getRegister().lastRegister?.let { PutData.TextData(it.text, it.type, it.transferableData) }
|
||||
VimPlugin.getRegister().resetRegister()
|
||||
|
||||
val insertTextBeforeCaret = cmd.keys[1].keyChar == 'P'
|
||||
val selection = PutData.VisualSelection(caretsAndSelections, caretsAndSelections.values.first().type)
|
||||
val putData = PutData(textData, selection, cmd.count, insertTextBeforeCaret, _indent = true, caretAfterInsertedText = true)
|
||||
|
||||
return VimPlugin.getPut().putText(editor, context, putData)
|
||||
}
|
||||
}
|
@@ -1,53 +0,0 @@
|
||||
/*
|
||||
* IdeaVim - Vim emulator for IDEs based on the IntelliJ platform
|
||||
* Copyright (C) 2003-2020 The IdeaVim authors
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.maddyhome.idea.vim.action.copy
|
||||
|
||||
import com.intellij.openapi.actionSystem.DataContext
|
||||
import com.intellij.openapi.editor.Caret
|
||||
import com.intellij.openapi.editor.Editor
|
||||
import com.maddyhome.idea.vim.VimPlugin
|
||||
import com.maddyhome.idea.vim.command.Command
|
||||
import com.maddyhome.idea.vim.command.CommandFlags
|
||||
import com.maddyhome.idea.vim.group.copy.PutData
|
||||
import com.maddyhome.idea.vim.group.visual.VimSelection
|
||||
import com.maddyhome.idea.vim.handler.VisualOperatorActionHandler
|
||||
import com.maddyhome.idea.vim.helper.enumSetOf
|
||||
import java.util.*
|
||||
|
||||
/**
|
||||
* @author vlan
|
||||
*/
|
||||
class PutVisualTextNoIndentAction : VisualOperatorActionHandler.SingleExecution() {
|
||||
|
||||
override val type: Command.Type = Command.Type.OTHER_SELF_SYNCHRONIZED
|
||||
|
||||
override val flags: EnumSet<CommandFlags> = enumSetOf(CommandFlags.FLAG_EXIT_VISUAL)
|
||||
|
||||
override fun executeForAllCarets(editor: Editor, context: DataContext, cmd: Command, caretsAndSelections: Map<Caret, VimSelection>): Boolean {
|
||||
if (caretsAndSelections.isEmpty()) return false
|
||||
val textData = VimPlugin.getRegister().lastRegister?.let { PutData.TextData(it.text, it.type, it.transferableData) }
|
||||
VimPlugin.getRegister().resetRegister()
|
||||
|
||||
val insertBeforeCaret = cmd.keys[1].keyChar == 'P'
|
||||
val selection = PutData.VisualSelection(caretsAndSelections, caretsAndSelections.values.first().type)
|
||||
val putData = PutData(textData, selection, cmd.count, insertBeforeCaret, _indent = false, caretAfterInsertedText = false)
|
||||
|
||||
return VimPlugin.getPut().putText(editor, context, putData)
|
||||
}
|
||||
}
|
@@ -1,41 +0,0 @@
|
||||
/*
|
||||
* IdeaVim - Vim emulator for IDEs based on the IntelliJ platform
|
||||
* Copyright (C) 2003-2020 The IdeaVim authors
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.maddyhome.idea.vim.action.copy
|
||||
|
||||
import com.intellij.openapi.actionSystem.DataContext
|
||||
import com.intellij.openapi.editor.Editor
|
||||
import com.maddyhome.idea.vim.VimPlugin
|
||||
import com.maddyhome.idea.vim.command.Argument
|
||||
import com.maddyhome.idea.vim.command.Command
|
||||
import com.maddyhome.idea.vim.command.CommandFlags
|
||||
import com.maddyhome.idea.vim.handler.VimActionHandler
|
||||
import com.maddyhome.idea.vim.helper.enumSetOf
|
||||
import java.util.*
|
||||
|
||||
class SelectRegisterAction : VimActionHandler.SingleExecution() {
|
||||
override val type: Command.Type = Command.Type.SELECT_REGISTER
|
||||
|
||||
override val flags: EnumSet<CommandFlags> = enumSetOf(CommandFlags.FLAG_EXPECT_MORE)
|
||||
|
||||
override val argumentType: Argument.Type = Argument.Type.CHARACTER
|
||||
|
||||
override fun execute(editor: Editor, context: DataContext, cmd: Command): Boolean {
|
||||
val argument = cmd.argument
|
||||
return argument != null && VimPlugin.getRegister().selectRegister(argument.character)
|
||||
}
|
||||
}
|
@@ -34,7 +34,7 @@ class VimEditorBackSpace : VimActionHandler.SingleExecution(), ComplicatedKeysAc
|
||||
private val actionName: String = "EditorBackSpace"
|
||||
|
||||
override val keyStrokesSet: Set<List<KeyStroke>> = setOf(
|
||||
listOf(KeyStroke.getKeyStroke(KeyEvent.VK_H, KeyEvent.CTRL_MASK)),
|
||||
listOf(KeyStroke.getKeyStroke(KeyEvent.VK_H, KeyEvent.CTRL_DOWN_MASK)),
|
||||
listOf(KeyStroke.getKeyStroke(KeyEvent.VK_BACK_SPACE, 0))
|
||||
)
|
||||
|
||||
@@ -85,7 +85,7 @@ class VimEditorTab : VimActionHandler.SingleExecution(), ComplicatedKeysAction {
|
||||
private val actionName: String = "EditorTab"
|
||||
|
||||
override val keyStrokesSet: Set<List<KeyStroke>> = setOf(
|
||||
listOf(KeyStroke.getKeyStroke(KeyEvent.VK_I, KeyEvent.CTRL_MASK)),
|
||||
listOf(KeyStroke.getKeyStroke(KeyEvent.VK_I, KeyEvent.CTRL_DOWN_MASK)),
|
||||
listOf(KeyStroke.getKeyStroke(KeyEvent.VK_TAB, 0))
|
||||
)
|
||||
|
||||
|
@@ -28,8 +28,8 @@ import javax.swing.KeyStroke
|
||||
|
||||
class FilePreviousAction : VimActionHandler.SingleExecution(), ComplicatedKeysAction {
|
||||
override val keyStrokesSet: Set<List<KeyStroke>> = setOf(
|
||||
listOf(KeyStroke.getKeyStroke(KeyEvent.VK_6, KeyEvent.CTRL_MASK or KeyEvent.SHIFT_MASK)),
|
||||
listOf(KeyStroke.getKeyStroke(KeyEvent.VK_CIRCUMFLEX, KeyEvent.CTRL_MASK))
|
||||
listOf(KeyStroke.getKeyStroke(KeyEvent.VK_6, KeyEvent.CTRL_DOWN_MASK or KeyEvent.SHIFT_DOWN_MASK)),
|
||||
listOf(KeyStroke.getKeyStroke(KeyEvent.VK_CIRCUMFLEX, KeyEvent.CTRL_DOWN_MASK))
|
||||
)
|
||||
|
||||
override val type: Command.Type = Command.Type.OTHER_READONLY
|
||||
|
@@ -44,10 +44,10 @@ class MotionScrollPageDownInsertModeAction : VimActionHandler.SingleExecution(),
|
||||
|
||||
override val keyStrokesSet: Set<List<KeyStroke>> = setOf(
|
||||
listOf(KeyStroke.getKeyStroke(KeyEvent.VK_PAGE_DOWN, 0)),
|
||||
listOf(KeyStroke.getKeyStroke(KeyEvent.VK_DOWN, KeyEvent.CTRL_MASK)),
|
||||
listOf(KeyStroke.getKeyStroke(KeyEvent.VK_KP_DOWN, KeyEvent.CTRL_MASK)),
|
||||
listOf(KeyStroke.getKeyStroke(KeyEvent.VK_DOWN, KeyEvent.SHIFT_MASK)),
|
||||
listOf(KeyStroke.getKeyStroke(KeyEvent.VK_KP_DOWN, KeyEvent.SHIFT_MASK))
|
||||
listOf(KeyStroke.getKeyStroke(KeyEvent.VK_DOWN, KeyEvent.CTRL_DOWN_MASK)),
|
||||
listOf(KeyStroke.getKeyStroke(KeyEvent.VK_KP_DOWN, KeyEvent.CTRL_DOWN_MASK)),
|
||||
listOf(KeyStroke.getKeyStroke(KeyEvent.VK_DOWN, KeyEvent.SHIFT_DOWN_MASK)),
|
||||
listOf(KeyStroke.getKeyStroke(KeyEvent.VK_KP_DOWN, KeyEvent.SHIFT_DOWN_MASK))
|
||||
)
|
||||
|
||||
override val type: Command.Type = Command.Type.OTHER_READONLY
|
||||
|
@@ -44,10 +44,10 @@ class MotionScrollPageUpInsertModeAction : VimActionHandler.SingleExecution(), C
|
||||
|
||||
override val keyStrokesSet: Set<List<KeyStroke>> = setOf(
|
||||
listOf(KeyStroke.getKeyStroke(KeyEvent.VK_PAGE_UP, 0)),
|
||||
listOf(KeyStroke.getKeyStroke(KeyEvent.VK_UP, KeyEvent.CTRL_MASK)),
|
||||
listOf(KeyStroke.getKeyStroke(KeyEvent.VK_KP_UP, KeyEvent.CTRL_MASK)),
|
||||
listOf(KeyStroke.getKeyStroke(KeyEvent.VK_UP, KeyEvent.SHIFT_MASK)),
|
||||
listOf(KeyStroke.getKeyStroke(KeyEvent.VK_KP_UP, KeyEvent.SHIFT_MASK))
|
||||
listOf(KeyStroke.getKeyStroke(KeyEvent.VK_UP, KeyEvent.CTRL_DOWN_MASK)),
|
||||
listOf(KeyStroke.getKeyStroke(KeyEvent.VK_KP_UP, KeyEvent.CTRL_DOWN_MASK)),
|
||||
listOf(KeyStroke.getKeyStroke(KeyEvent.VK_UP, KeyEvent.SHIFT_DOWN_MASK)),
|
||||
listOf(KeyStroke.getKeyStroke(KeyEvent.VK_KP_UP, KeyEvent.SHIFT_DOWN_MASK))
|
||||
)
|
||||
|
||||
override val type: Command.Type = Command.Type.OTHER_READONLY
|
||||
|
@@ -50,10 +50,10 @@ class MotionWordLeftInsertAction : MotionActionHandler.ForEachCaret(), Complicat
|
||||
override val motionType: MotionType = MotionType.EXCLUSIVE
|
||||
|
||||
override val keyStrokesSet: Set<List<KeyStroke>> = setOf(
|
||||
listOf(KeyStroke.getKeyStroke(KeyEvent.VK_LEFT, KeyEvent.CTRL_MASK)),
|
||||
listOf(KeyStroke.getKeyStroke(KeyEvent.VK_KP_LEFT, KeyEvent.CTRL_MASK)),
|
||||
listOf(KeyStroke.getKeyStroke(KeyEvent.VK_LEFT, KeyEvent.SHIFT_MASK)),
|
||||
listOf(KeyStroke.getKeyStroke(KeyEvent.VK_KP_LEFT, KeyEvent.SHIFT_MASK))
|
||||
listOf(KeyStroke.getKeyStroke(KeyEvent.VK_LEFT, KeyEvent.CTRL_DOWN_MASK)),
|
||||
listOf(KeyStroke.getKeyStroke(KeyEvent.VK_KP_LEFT, KeyEvent.CTRL_DOWN_MASK)),
|
||||
listOf(KeyStroke.getKeyStroke(KeyEvent.VK_LEFT, KeyEvent.SHIFT_DOWN_MASK)),
|
||||
listOf(KeyStroke.getKeyStroke(KeyEvent.VK_KP_LEFT, KeyEvent.SHIFT_DOWN_MASK))
|
||||
)
|
||||
|
||||
override val flags: EnumSet<CommandFlags> = enumSetOf(CommandFlags.FLAG_SAVE_STROKE)
|
||||
|
@@ -50,10 +50,10 @@ class MotionWordRightInsertAction : MotionActionHandler.ForEachCaret(), Complica
|
||||
override val motionType: MotionType = MotionType.EXCLUSIVE
|
||||
|
||||
override val keyStrokesSet: Set<List<KeyStroke>> = setOf(
|
||||
listOf(KeyStroke.getKeyStroke(KeyEvent.VK_RIGHT, KeyEvent.CTRL_MASK)),
|
||||
listOf(KeyStroke.getKeyStroke(KeyEvent.VK_KP_RIGHT, KeyEvent.CTRL_MASK)),
|
||||
listOf(KeyStroke.getKeyStroke(KeyEvent.VK_RIGHT, KeyEvent.SHIFT_MASK)),
|
||||
listOf(KeyStroke.getKeyStroke(KeyEvent.VK_KP_RIGHT, KeyEvent.SHIFT_MASK))
|
||||
listOf(KeyStroke.getKeyStroke(KeyEvent.VK_RIGHT, KeyEvent.CTRL_DOWN_MASK)),
|
||||
listOf(KeyStroke.getKeyStroke(KeyEvent.VK_KP_RIGHT, KeyEvent.CTRL_DOWN_MASK)),
|
||||
listOf(KeyStroke.getKeyStroke(KeyEvent.VK_RIGHT, KeyEvent.SHIFT_DOWN_MASK)),
|
||||
listOf(KeyStroke.getKeyStroke(KeyEvent.VK_KP_RIGHT, KeyEvent.SHIFT_DOWN_MASK))
|
||||
)
|
||||
|
||||
override val flags: EnumSet<CommandFlags> = enumSetOf(CommandFlags.FLAG_SAVE_STROKE)
|
||||
|
@@ -50,6 +50,6 @@ class Argument private constructor(
|
||||
override fun execute(editor: Editor, context: DataContext, cmd: Command): Boolean = true
|
||||
|
||||
override val type: Command.Type = Command.Type.UNDEFINED
|
||||
}, Command.Type.UNDEFINED, EnumSet.noneOf(CommandFlags::class.java), emptyList())
|
||||
}, Command.Type.UNDEFINED, EnumSet.noneOf(CommandFlags::class.java))
|
||||
}
|
||||
}
|
||||
|
@@ -20,7 +20,6 @@ package com.maddyhome.idea.vim.command
|
||||
|
||||
import com.maddyhome.idea.vim.handler.EditorActionHandlerBase
|
||||
import java.util.*
|
||||
import javax.swing.KeyStroke
|
||||
|
||||
/**
|
||||
* This represents a single Vim command to be executed (operator, motion, text object, etc.). It may optionally include
|
||||
@@ -28,14 +27,17 @@ import javax.swing.KeyStroke
|
||||
*/
|
||||
data class Command(
|
||||
var rawCount: Int,
|
||||
var action: EditorActionHandlerBase,
|
||||
var action: EditorActionHandlerBase?,
|
||||
val type: Type,
|
||||
var flags: EnumSet<CommandFlags>,
|
||||
var keys: List<KeyStroke>
|
||||
var flags: EnumSet<CommandFlags>
|
||||
) {
|
||||
|
||||
constructor(rawCount: Int, register: Char): this(rawCount, null, Type.SELECT_REGISTER, EnumSet.of(CommandFlags.FLAG_EXPECT_MORE)) {
|
||||
this.register = register
|
||||
}
|
||||
|
||||
init {
|
||||
action.process(this)
|
||||
action?.process(this)
|
||||
}
|
||||
|
||||
var count: Int
|
||||
@@ -45,6 +47,7 @@ data class Command(
|
||||
}
|
||||
|
||||
var argument: Argument? = null
|
||||
var register: Char? = null
|
||||
|
||||
enum class Type {
|
||||
/**
|
||||
@@ -72,12 +75,12 @@ data class Command(
|
||||
*/
|
||||
COPY,
|
||||
PASTE,
|
||||
// TODO REMOVE?
|
||||
RESET,
|
||||
/**
|
||||
* Represents commands that select the register.
|
||||
*/
|
||||
SELECT_REGISTER,
|
||||
// TODO REMOVE?
|
||||
RESET,
|
||||
OTHER_READONLY,
|
||||
OTHER_WRITABLE,
|
||||
/**
|
||||
@@ -88,7 +91,7 @@ data class Command(
|
||||
|
||||
val isRead: Boolean
|
||||
get() = when (this) {
|
||||
MOTION, COPY, SELECT_REGISTER, OTHER_READONLY, COMPLETION -> true
|
||||
MOTION, COPY, OTHER_READONLY, COMPLETION -> true
|
||||
else -> false
|
||||
}
|
||||
|
||||
|
@@ -9,12 +9,13 @@ import java.util.*
|
||||
import javax.swing.KeyStroke
|
||||
|
||||
class CommandBuilder(private var currentCommandPartNode: CommandPartNode) {
|
||||
private val commandParts = Stack<Command>()
|
||||
private var keys = mutableListOf<KeyStroke>()
|
||||
private val commandParts = ArrayDeque<Command>()
|
||||
private var keyList = mutableListOf<KeyStroke>()
|
||||
|
||||
var commandState = CurrentCommandState.NEW_COMMAND
|
||||
var count = 0
|
||||
private set
|
||||
val keys: Iterable<KeyStroke> get() = keyList
|
||||
|
||||
// The argument type for the current command part's action. Kept separate to handle digraphs and characters. We first
|
||||
// try to accept a digraph. If we get it, set expected argument type to character and handle the converted key. If we
|
||||
@@ -24,7 +25,7 @@ class CommandBuilder(private var currentCommandPartNode: CommandPartNode) {
|
||||
|
||||
val isReady get() = commandState == CurrentCommandState.READY
|
||||
val isBad get() = commandState == CurrentCommandState.BAD_COMMAND
|
||||
val isEmpty get() = commandParts.empty()
|
||||
val isEmpty get() = commandParts.isEmpty()
|
||||
val isAtDefaultState get() = isEmpty && count == 0 && expectedArgumentType == null
|
||||
|
||||
val isExpectingCount: Boolean
|
||||
@@ -35,14 +36,23 @@ class CommandBuilder(private var currentCommandPartNode: CommandPartNode) {
|
||||
}
|
||||
|
||||
fun pushCommandPart(action: EditorActionHandlerBase) {
|
||||
commandParts.push(Command(count, action, action.type, action.flags, keys))
|
||||
commandParts.add(Command(count, action, action.type, action.flags))
|
||||
expectedArgumentType = action.argumentType
|
||||
keys = mutableListOf()
|
||||
count = 0
|
||||
}
|
||||
|
||||
fun popCommandPart() {
|
||||
commandParts.pop()
|
||||
expectedArgumentType = if (commandParts.size > 0) commandParts.peek().action.argumentType else null
|
||||
fun pushCommandPart(register: Char) {
|
||||
// We will never execute this command, but we need to push something to correctly handle counts on either side of a
|
||||
// select register command part. e.g. 2"a2d2w or even crazier 2"a2"a2"a2"a2"a2d2w
|
||||
commandParts.add(Command(count, register))
|
||||
expectedArgumentType = null
|
||||
count = 0
|
||||
}
|
||||
|
||||
fun popCommandPart(): Command {
|
||||
val command = commandParts.removeLast()
|
||||
expectedArgumentType = if (commandParts.size > 0) commandParts.peekLast().action?.argumentType else null
|
||||
return command
|
||||
}
|
||||
|
||||
fun fallbackToCharacterArgument() {
|
||||
@@ -52,16 +62,24 @@ class CommandBuilder(private var currentCommandPartNode: CommandPartNode) {
|
||||
expectedArgumentType = Argument.Type.CHARACTER
|
||||
}
|
||||
|
||||
fun addKey(keyStroke: KeyStroke) {
|
||||
keys.add(keyStroke)
|
||||
fun addKey(key: KeyStroke) {
|
||||
keyList.add(key)
|
||||
}
|
||||
|
||||
fun addCountCharacter(chKey: Char) {
|
||||
count = (count * 10) + (chKey - '0')
|
||||
fun addCountCharacter(key: KeyStroke) {
|
||||
count = (count * 10) + (key.keyChar - '0')
|
||||
// If count overflows and flips negative, reset to 999999999L. In Vim, count is a long, which is *usually* 32 bits,
|
||||
// so will flip at 2147483648. We store count as an Int, which is also 32 bit.
|
||||
// See https://github.com/vim/vim/blob/b376ace1aeaa7614debc725487d75c8f756dd773/src/normal.c#L631
|
||||
if (count < 0) {
|
||||
count = 999999999
|
||||
}
|
||||
addKey(key)
|
||||
}
|
||||
|
||||
fun deleteCountCharacter() {
|
||||
count /= 10
|
||||
keyList.removeAt(keyList.size - 1)
|
||||
}
|
||||
|
||||
fun setCurrentCommandPartNode(newNode: CommandPartNode) {
|
||||
@@ -74,7 +92,7 @@ class CommandBuilder(private var currentCommandPartNode: CommandPartNode) {
|
||||
|
||||
fun isAwaitingCharOrDigraphArgument(): Boolean {
|
||||
if (commandParts.size == 0) return false
|
||||
val argumentType = commandParts.peek().action.argumentType
|
||||
val argumentType = commandParts.peekLast().action?.argumentType
|
||||
return argumentType == Argument.Type.CHARACTER || argumentType == Argument.Type.DIGRAPH
|
||||
}
|
||||
|
||||
@@ -88,12 +106,12 @@ class CommandBuilder(private var currentCommandPartNode: CommandPartNode) {
|
||||
}
|
||||
|
||||
fun completeCommandPart(argument: Argument) {
|
||||
commandParts.peek().argument = argument
|
||||
commandParts.peekLast().argument = argument
|
||||
commandState = CurrentCommandState.READY
|
||||
}
|
||||
|
||||
fun isDuplicateOperatorKeyStroke(key: KeyStroke): Boolean {
|
||||
val action = commandParts.peek()?.action as? DuplicableOperatorAction
|
||||
val action = commandParts.peekLast()?.action as? DuplicableOperatorAction
|
||||
return action?.duplicateWith == key.keyChar
|
||||
}
|
||||
|
||||
@@ -102,27 +120,23 @@ class CommandBuilder(private var currentCommandPartNode: CommandPartNode) {
|
||||
}
|
||||
|
||||
fun buildCommand(): Command {
|
||||
/* Let's go through the command stack and merge it all into one command. At this time there should never
|
||||
be more than two commands on the stack - one is the actual command, and the other would be a motion
|
||||
command argument needed by the first command */
|
||||
var command: Command = commandParts.pop()
|
||||
while (commandParts.size > 0) {
|
||||
val top: Command = commandParts.pop()
|
||||
top.argument = Argument(command)
|
||||
command = top
|
||||
}
|
||||
return fixCommandCounts(command)
|
||||
}
|
||||
|
||||
private fun fixCommandCounts(command: Command): Command {
|
||||
// If we have a command with a motion command argument, both could have their own counts. We need to adjust the
|
||||
// counts, so the motion gets the product of both counts, and the count associated with the command gets reset.
|
||||
// E.g. 3c2w (change 2 words, three times) becomes c6w (change 6 words)
|
||||
if (command.argument?.type === Argument.Type.MOTION) {
|
||||
val motion = command.argument!!.motion
|
||||
motion.count = if (command.rawCount == 0 && motion.rawCount == 0) 0 else command.count * motion.count
|
||||
var command: Command = commandParts.removeFirst()
|
||||
while (commandParts.size > 0) {
|
||||
val next = commandParts.removeFirst()
|
||||
next.count = if (command.rawCount == 0 && next.rawCount == 0) 0 else command.count * next.count
|
||||
command.count = 0
|
||||
if (command.type == Command.Type.SELECT_REGISTER) {
|
||||
next.register = command.register
|
||||
command.register = null
|
||||
command = next
|
||||
}
|
||||
else {
|
||||
command.argument = Argument(next)
|
||||
assert(commandParts.size == 0)
|
||||
}
|
||||
}
|
||||
expectedArgumentType = null
|
||||
return command
|
||||
}
|
||||
|
||||
@@ -130,11 +144,11 @@ class CommandBuilder(private var currentCommandPartNode: CommandPartNode) {
|
||||
resetInProgressCommandPart(commandPartNode)
|
||||
commandState = CurrentCommandState.NEW_COMMAND
|
||||
commandParts.clear()
|
||||
keyList.clear()
|
||||
expectedArgumentType = null
|
||||
}
|
||||
|
||||
fun resetInProgressCommandPart(commandPartNode: CommandPartNode) {
|
||||
keys.clear()
|
||||
count = 0
|
||||
setCurrentCommandPartNode(commandPartNode)
|
||||
}
|
||||
|
@@ -164,6 +164,12 @@ public class CommandState {
|
||||
}
|
||||
}
|
||||
|
||||
public void resetRegisterPending() {
|
||||
if (getSubMode() == SubMode.REGISTER_PENDING) {
|
||||
popModes();
|
||||
}
|
||||
}
|
||||
|
||||
private void resetModes() {
|
||||
modeStates.clear();
|
||||
setMappingMode();
|
||||
@@ -343,7 +349,7 @@ public class CommandState {
|
||||
}
|
||||
|
||||
public enum SubMode {
|
||||
NONE, SINGLE_COMMAND, OP_PENDING, VISUAL_CHARACTER, VISUAL_LINE, VISUAL_BLOCK
|
||||
NONE, SINGLE_COMMAND, OP_PENDING, REGISTER_PENDING, VISUAL_CHARACTER, VISUAL_LINE, VISUAL_BLOCK
|
||||
}
|
||||
|
||||
private static class ModeState {
|
||||
|
@@ -60,4 +60,8 @@ enum class SelectionType(val value: Int) {
|
||||
else -> CHARACTER_WISE
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val SelectionType.isLine get() = this == SelectionType.LINE_WISE
|
||||
val SelectionType.isChar get() = this == SelectionType.CHARACTER_WISE
|
||||
val SelectionType.isBlock get() = this == SelectionType.BLOCK_WISE
|
||||
|
@@ -29,12 +29,14 @@ class Register {
|
||||
val type: SelectionType
|
||||
val keys: MutableList<KeyStroke>
|
||||
val transferableData: MutableList<out TextBlockTransferableData>
|
||||
val rawText: String?
|
||||
|
||||
constructor(name: Char, type: SelectionType, keys: MutableList<KeyStroke>) {
|
||||
this.name = name
|
||||
this.type = type
|
||||
this.keys = keys
|
||||
this.transferableData = mutableListOf()
|
||||
this.rawText = text
|
||||
}
|
||||
|
||||
constructor(name: Char, type: SelectionType, text: String, transferableData: MutableList<out TextBlockTransferableData>) {
|
||||
@@ -42,6 +44,15 @@ class Register {
|
||||
this.type = type
|
||||
this.keys = StringHelper.stringToKeys(text)
|
||||
this.transferableData = transferableData
|
||||
this.rawText = text
|
||||
}
|
||||
|
||||
constructor(name: Char, type: SelectionType, text: String, transferableData: MutableList<out TextBlockTransferableData>, rawText: String) {
|
||||
this.name = name
|
||||
this.type = type
|
||||
this.keys = StringHelper.stringToKeys(text)
|
||||
this.transferableData = transferableData
|
||||
this.rawText = rawText
|
||||
}
|
||||
|
||||
val text: String?
|
||||
|
@@ -17,7 +17,6 @@
|
||||
*/
|
||||
package com.maddyhome.idea.vim.ex
|
||||
|
||||
import com.intellij.openapi.actionSystem.DataContext
|
||||
import com.intellij.openapi.editor.Caret
|
||||
import com.intellij.openapi.editor.Editor
|
||||
import com.maddyhome.idea.vim.common.TextRange
|
||||
@@ -28,33 +27,33 @@ class ExCommand(val ranges: Ranges, val command: String, var argument: String) {
|
||||
|
||||
fun getLine(editor: Editor): Int = ranges.getLine(editor)
|
||||
|
||||
fun getLine(editor: Editor, caret: Caret, context: DataContext): Int = ranges.getLine(editor, caret, context)
|
||||
fun getLine(editor: Editor, caret: Caret): Int = ranges.getLine(editor, caret)
|
||||
|
||||
fun getCount(editor: Editor, context: DataContext?, defaultCount: Int, checkCount: Boolean): Int {
|
||||
fun getCount(editor: Editor, defaultCount: Int, checkCount: Boolean): Int {
|
||||
val count = if (checkCount) countArgument else -1
|
||||
|
||||
val res = ranges.getCount(editor, count)
|
||||
return if (res == -1) defaultCount else res
|
||||
}
|
||||
|
||||
fun getCount(editor: Editor, caret: Caret, context: DataContext, defaultCount: Int, checkCount: Boolean): Int {
|
||||
val count = ranges.getCount(editor, caret, context, if (checkCount) countArgument else -1)
|
||||
fun getCount(editor: Editor, caret: Caret, defaultCount: Int, checkCount: Boolean): Int {
|
||||
val count = ranges.getCount(editor, caret, if (checkCount) countArgument else -1)
|
||||
return if (count == -1) defaultCount else count
|
||||
}
|
||||
|
||||
fun getLineRange(editor: Editor): LineRange = ranges.getLineRange(editor, -1)
|
||||
|
||||
fun getLineRange(editor: Editor, caret: Caret, context: DataContext): LineRange {
|
||||
return ranges.getLineRange(editor, caret, context, -1)
|
||||
fun getLineRange(editor: Editor, caret: Caret): LineRange {
|
||||
return ranges.getLineRange(editor, caret, -1)
|
||||
}
|
||||
|
||||
fun getTextRange(editor: Editor, context: DataContext?, checkCount: Boolean): TextRange {
|
||||
fun getTextRange(editor: Editor, checkCount: Boolean): TextRange {
|
||||
val count = if (checkCount) countArgument else -1
|
||||
return ranges.getTextRange(editor, context, count)
|
||||
return ranges.getTextRange(editor, count)
|
||||
}
|
||||
|
||||
fun getTextRange(editor: Editor, caret: Caret, context: DataContext, checkCount: Boolean): TextRange {
|
||||
return ranges.getTextRange(editor, caret, context, if (checkCount) countArgument else -1)
|
||||
fun getTextRange(editor: Editor, caret: Caret, checkCount: Boolean): TextRange {
|
||||
return ranges.getTextRange(editor, caret, if (checkCount) countArgument else -1)
|
||||
}
|
||||
|
||||
private val countArgument: Int
|
||||
|
@@ -22,7 +22,11 @@ import com.intellij.openapi.actionSystem.DataContext
|
||||
import com.intellij.openapi.diagnostic.Logger
|
||||
import com.intellij.openapi.editor.Editor
|
||||
import com.maddyhome.idea.vim.VimPlugin
|
||||
import com.maddyhome.idea.vim.ex.*
|
||||
import com.maddyhome.idea.vim.ex.CommandHandler
|
||||
import com.maddyhome.idea.vim.ex.ExCommand
|
||||
import com.maddyhome.idea.vim.ex.ExException
|
||||
import com.maddyhome.idea.vim.ex.ExOutputModel
|
||||
import com.maddyhome.idea.vim.ex.flags
|
||||
import com.maddyhome.idea.vim.helper.MessageHelper
|
||||
import com.maddyhome.idea.vim.helper.Msg
|
||||
import java.io.IOException
|
||||
@@ -54,7 +58,7 @@ class CmdFilterHandler : CommandHandler.SingleExecution() {
|
||||
true
|
||||
} else {
|
||||
// Filter
|
||||
val range = cmd.getTextRange(editor, context, false)
|
||||
val range = cmd.getTextRange(editor, false)
|
||||
VimPlugin.getProcess().executeFilter(editor, range, command)
|
||||
}
|
||||
} catch (e: IOException) {
|
||||
|
@@ -22,7 +22,10 @@ import com.intellij.openapi.actionSystem.DataContext
|
||||
import com.intellij.openapi.editor.Editor
|
||||
import com.maddyhome.idea.vim.VimPlugin
|
||||
import com.maddyhome.idea.vim.command.SelectionType
|
||||
import com.maddyhome.idea.vim.ex.*
|
||||
import com.maddyhome.idea.vim.ex.CommandHandler
|
||||
import com.maddyhome.idea.vim.ex.CommandParser
|
||||
import com.maddyhome.idea.vim.ex.ExCommand
|
||||
import com.maddyhome.idea.vim.ex.flags
|
||||
import com.maddyhome.idea.vim.group.copy.PutData
|
||||
import com.maddyhome.idea.vim.helper.EditorHelper
|
||||
|
||||
@@ -31,11 +34,11 @@ class CopyTextHandler : CommandHandler.SingleExecution() {
|
||||
override fun execute(editor: Editor, context: DataContext, cmd: ExCommand): Boolean {
|
||||
val carets = EditorHelper.getOrderedCaretsList(editor)
|
||||
for (caret in carets) {
|
||||
val range = cmd.getTextRange(editor, caret, context, false)
|
||||
val range = cmd.getTextRange(editor, caret, false)
|
||||
val text = EditorHelper.getText(editor, range.startOffset, range.endOffset)
|
||||
|
||||
val arg = CommandParser.getInstance().parse(cmd.argument)
|
||||
val line = arg.ranges.getFirstLine(editor, caret, context)
|
||||
val line = arg.ranges.getFirstLine(editor, caret)
|
||||
|
||||
val transferableData = VimPlugin.getRegister().getTransferableData(editor, range, text)
|
||||
val textData = PutData.TextData(text, SelectionType.LINE_WISE, transferableData)
|
||||
|
@@ -40,7 +40,7 @@ class DeleteLinesHandler : CommandHandler.ForEachCaret() {
|
||||
|
||||
if (!VimPlugin.getRegister().selectRegister(register)) return false
|
||||
|
||||
val textRange = cmd.getTextRange(editor, caret, context, true)
|
||||
val textRange = cmd.getTextRange(editor, caret, true)
|
||||
return VimPlugin.getChange().deleteRange(editor, caret, textRange, SelectionType.LINE_WISE, false)
|
||||
}
|
||||
}
|
||||
|
@@ -29,7 +29,7 @@ import com.maddyhome.idea.vim.ex.flags
|
||||
class FileHandler : CommandHandler.SingleExecution() {
|
||||
override val argFlags = flags(RangeFlag.RANGE_IS_COUNT, ARGUMENT_FORBIDDEN, Access.READ_ONLY)
|
||||
override fun execute(editor: Editor, context: DataContext, cmd: ExCommand): Boolean {
|
||||
val count = cmd.getCount(editor, context, 0, false)
|
||||
val count = cmd.getCount(editor, 0, false)
|
||||
VimPlugin.getFile().displayFileInfo(editor, count > 0)
|
||||
return true
|
||||
}
|
||||
|
@@ -32,7 +32,7 @@ class GotoCharacterHandler : CommandHandler.ForEachCaret() {
|
||||
override val argFlags: CommandHandlerFlags = flags(RangeFlag.RANGE_IS_COUNT, ArgumentFlag.ARGUMENT_OPTIONAL, Access.READ_ONLY)
|
||||
|
||||
override fun execute(editor: Editor, caret: Caret, context: DataContext, cmd: ExCommand): Boolean {
|
||||
val count = cmd.getCount(editor, caret, context, 1, true)
|
||||
val count = cmd.getCount(editor, caret, 1, true)
|
||||
if (count <= 0) return false
|
||||
|
||||
val offset = VimPlugin.getMotion().moveCaretToNthCharacter(editor, count - 1)
|
||||
|
@@ -47,7 +47,7 @@ class GotoLineHandler : CommandHandler.ForEachCaret() {
|
||||
* @return True if able to perform the command, false if not
|
||||
*/
|
||||
override fun execute(editor: Editor, caret: Caret, context: DataContext, cmd: ExCommand): Boolean {
|
||||
val line = min(cmd.getLine(editor, caret, context), EditorHelper.getLineCount(editor) - 1)
|
||||
val line = min(cmd.getLine(editor, caret), EditorHelper.getLineCount(editor) - 1)
|
||||
|
||||
if (line >= 0) {
|
||||
MotionGroup.moveCaret(editor, caret, VimPlugin.getMotion().moveCaretToLineStartSkipLeading(editor, line))
|
||||
|
@@ -23,7 +23,10 @@ import com.intellij.openapi.editor.Caret
|
||||
import com.intellij.openapi.editor.Editor
|
||||
import com.maddyhome.idea.vim.VimPlugin
|
||||
import com.maddyhome.idea.vim.common.TextRange
|
||||
import com.maddyhome.idea.vim.ex.*
|
||||
import com.maddyhome.idea.vim.ex.CommandHandler
|
||||
import com.maddyhome.idea.vim.ex.CommandHandlerFlags
|
||||
import com.maddyhome.idea.vim.ex.ExCommand
|
||||
import com.maddyhome.idea.vim.ex.flags
|
||||
|
||||
class JoinLinesHandler : CommandHandler.ForEachCaret() {
|
||||
override val argFlags: CommandHandlerFlags = flags(RangeFlag.RANGE_OPTIONAL, ArgumentFlag.ARGUMENT_OPTIONAL, Access.WRITABLE)
|
||||
@@ -32,7 +35,7 @@ class JoinLinesHandler : CommandHandler.ForEachCaret() {
|
||||
val arg = cmd.argument
|
||||
val spaces = arg.isEmpty() || arg[0] != '!'
|
||||
|
||||
val textRange = cmd.getTextRange(editor, caret, context, true) ?: return false
|
||||
val textRange = cmd.getTextRange(editor, caret, true)
|
||||
|
||||
return VimPlugin.getChange().deleteJoinRange(editor, caret, TextRange(textRange.startOffset,
|
||||
textRange.endOffset - 1), spaces)
|
||||
|
@@ -24,7 +24,12 @@ import com.intellij.openapi.editor.Editor
|
||||
import com.maddyhome.idea.vim.VimPlugin
|
||||
import com.maddyhome.idea.vim.command.SelectionType
|
||||
import com.maddyhome.idea.vim.common.TextRange
|
||||
import com.maddyhome.idea.vim.ex.*
|
||||
import com.maddyhome.idea.vim.ex.CommandHandler
|
||||
import com.maddyhome.idea.vim.ex.CommandParser
|
||||
import com.maddyhome.idea.vim.ex.ExCommand
|
||||
import com.maddyhome.idea.vim.ex.ExException
|
||||
import com.maddyhome.idea.vim.ex.InvalidRangeException
|
||||
import com.maddyhome.idea.vim.ex.flags
|
||||
import com.maddyhome.idea.vim.ex.ranges.LineRange
|
||||
import com.maddyhome.idea.vim.group.copy.PutData
|
||||
import com.maddyhome.idea.vim.helper.EditorHelper
|
||||
@@ -48,10 +53,10 @@ class MoveTextHandler : CommandHandler.SingleExecution() {
|
||||
|
||||
var lastRange: TextRange? = null
|
||||
for (caret in carets) {
|
||||
val range = cmd.getTextRange(editor, caret, context, false)
|
||||
val lineRange = cmd.getLineRange(editor, caret, context)
|
||||
val range = cmd.getTextRange(editor, caret, false)
|
||||
val lineRange = cmd.getLineRange(editor, caret)
|
||||
|
||||
line = min(line, normalizeLine(editor, caret, context, command, lineRange))
|
||||
line = min(line, normalizeLine(editor, caret, command, lineRange))
|
||||
texts.add(EditorHelper.getText(editor, range.startOffset, range.endOffset))
|
||||
|
||||
if (lastRange == null || lastRange.startOffset != range.startOffset && lastRange.endOffset != range.endOffset) {
|
||||
@@ -75,9 +80,9 @@ class MoveTextHandler : CommandHandler.SingleExecution() {
|
||||
}
|
||||
|
||||
@Throws
|
||||
private fun normalizeLine(editor: Editor, caret: Caret, context: DataContext,
|
||||
command: ExCommand, lineRange: LineRange): Int {
|
||||
var line = command.ranges.getFirstLine(editor, caret, context)
|
||||
private fun normalizeLine(editor: Editor, caret: Caret, command: ExCommand,
|
||||
lineRange: LineRange): Int {
|
||||
var line = command.ranges.getFirstLine(editor, caret)
|
||||
val adj = lineRange.endLine - lineRange.startLine + 1
|
||||
if (line >= lineRange.endLine)
|
||||
line -= adj
|
||||
|
@@ -29,7 +29,7 @@ class NextFileHandler : CommandHandler.SingleExecution() {
|
||||
override val argFlags = flags(RangeFlag.RANGE_IS_COUNT, ArgumentFlag.ARGUMENT_OPTIONAL, Access.READ_ONLY)
|
||||
|
||||
override fun execute(editor: Editor, context: DataContext, cmd: ExCommand): Boolean {
|
||||
val count = cmd.getCount(editor, context, 1, true)
|
||||
val count = cmd.getCount(editor, 1, true)
|
||||
VimPlugin.getMark().saveJumpLocation(editor)
|
||||
VimPlugin.getFile().selectNextFile(count, context)
|
||||
return true
|
||||
|
@@ -28,7 +28,7 @@ import com.maddyhome.idea.vim.ex.flags
|
||||
class PreviousFileHandler : CommandHandler.SingleExecution() {
|
||||
override val argFlags = flags(RangeFlag.RANGE_IS_COUNT, ArgumentFlag.ARGUMENT_OPTIONAL, Access.READ_ONLY)
|
||||
override fun execute(editor: Editor, context: DataContext, cmd: ExCommand): Boolean {
|
||||
val count = cmd.getCount(editor, context, 1, true)
|
||||
val count = cmd.getCount(editor, 1, true)
|
||||
|
||||
VimPlugin.getMark().saveJumpLocation(editor)
|
||||
VimPlugin.getFile().selectNextFile(-count, context)
|
||||
|
@@ -37,7 +37,7 @@ class RepeatHandler : CommandHandler.ForEachCaret() {
|
||||
if (arg == '@') arg = lastArg
|
||||
lastArg = arg
|
||||
|
||||
val line = cmd.getLine(editor, caret, context)
|
||||
val line = cmd.getLine(editor, caret)
|
||||
MotionGroup.moveCaret(editor, caret, VimPlugin.getMotion().moveCaretToLine(editor, line, editor.caretModel.primaryCaret))
|
||||
|
||||
if (arg == ':') {
|
||||
|
@@ -29,7 +29,7 @@ class SelectFileHandler : CommandHandler.SingleExecution() {
|
||||
override val argFlags = flags(RangeFlag.RANGE_IS_COUNT, ArgumentFlag.ARGUMENT_OPTIONAL, Access.READ_ONLY)
|
||||
|
||||
override fun execute(editor: Editor, context: DataContext, cmd: ExCommand): Boolean {
|
||||
val count = cmd.getCount(editor, context, 0, true)
|
||||
val count = cmd.getCount(editor, 0, true)
|
||||
|
||||
if (count > 0) {
|
||||
val res = VimPlugin.getFile().selectFile(count - 1, context)
|
||||
|
@@ -36,7 +36,7 @@ class ShiftLeftHandler : CommandHandler.ForEachCaret(), ComplicatedNameExCommand
|
||||
override val argFlags: CommandHandlerFlags = flags(RangeFlag.RANGE_OPTIONAL, ArgumentFlag.ARGUMENT_OPTIONAL, Access.WRITABLE)
|
||||
|
||||
override fun execute(editor: Editor, caret: Caret, context: DataContext, cmd: ExCommand): Boolean {
|
||||
val range = cmd.getTextRange(editor, caret, context, true)
|
||||
val range = cmd.getTextRange(editor, caret, true)
|
||||
val endOffsets = range.endOffsets.map { it - 1 }.toIntArray()
|
||||
VimPlugin.getChange().indentRange(editor, caret, context,
|
||||
TextRange(range.startOffsets, endOffsets),
|
||||
|
@@ -36,7 +36,7 @@ class ShiftRightHandler : CommandHandler.ForEachCaret(), ComplicatedNameExComman
|
||||
override val argFlags: CommandHandlerFlags = flags(RangeFlag.RANGE_OPTIONAL, ArgumentFlag.ARGUMENT_OPTIONAL, Access.WRITABLE)
|
||||
|
||||
override fun execute(editor: Editor, caret: Caret, context: DataContext, cmd: ExCommand): Boolean {
|
||||
val range = cmd.getTextRange(editor, caret, context, true)
|
||||
val range = cmd.getTextRange(editor, caret, true)
|
||||
val endOffsets = range.endOffsets.map { it - 1 }.toIntArray()
|
||||
VimPlugin.getChange().indentRange(editor, caret, context,
|
||||
TextRange(range.startOffsets, endOffsets),
|
||||
|
@@ -23,7 +23,10 @@ import com.intellij.openapi.editor.Caret
|
||||
import com.intellij.openapi.editor.Editor
|
||||
import com.intellij.openapi.util.text.StringUtil
|
||||
import com.maddyhome.idea.vim.VimPlugin
|
||||
import com.maddyhome.idea.vim.ex.*
|
||||
import com.maddyhome.idea.vim.ex.CommandHandler
|
||||
import com.maddyhome.idea.vim.ex.ExCommand
|
||||
import com.maddyhome.idea.vim.ex.ExException
|
||||
import com.maddyhome.idea.vim.ex.flags
|
||||
import com.maddyhome.idea.vim.ex.ranges.LineRange
|
||||
import com.maddyhome.idea.vim.helper.inBlockSubMode
|
||||
import java.util.*
|
||||
@@ -46,7 +49,7 @@ class SortHandler : CommandHandler.SingleExecution() {
|
||||
val lineComparator = LineComparator(ignoreCase, number, reverse)
|
||||
if (editor.inBlockSubMode) {
|
||||
val primaryCaret = editor.caretModel.primaryCaret
|
||||
val range = getLineRange(editor, primaryCaret, context, cmd)
|
||||
val range = getLineRange(editor, primaryCaret, cmd)
|
||||
val worked = VimPlugin.getChange().sortRange(editor, range, lineComparator)
|
||||
primaryCaret.moveToOffset(VimPlugin.getMotion().moveCaretToLineStartSkipLeading(editor, range.startLine))
|
||||
return worked
|
||||
@@ -54,7 +57,7 @@ class SortHandler : CommandHandler.SingleExecution() {
|
||||
|
||||
var worked = true
|
||||
for (caret in editor.caretModel.allCarets) {
|
||||
val range = getLineRange(editor, caret, context, cmd)
|
||||
val range = getLineRange(editor, caret, cmd)
|
||||
if (!VimPlugin.getChange().sortRange(editor, range, lineComparator)) {
|
||||
worked = false
|
||||
}
|
||||
@@ -64,8 +67,8 @@ class SortHandler : CommandHandler.SingleExecution() {
|
||||
return worked
|
||||
}
|
||||
|
||||
private fun getLineRange(editor: Editor, caret: Caret, context: DataContext, cmd: ExCommand): LineRange {
|
||||
val range = cmd.getLineRange(editor, caret, context)
|
||||
private fun getLineRange(editor: Editor, caret: Caret, cmd: ExCommand): LineRange {
|
||||
val range = cmd.getLineRange(editor, caret)
|
||||
|
||||
// Something like "30,20sort" gets converted to "20,30sort"
|
||||
val normalizedRange = if (range.endLine < range.startLine) LineRange(range.endLine, range.startLine) else range
|
||||
|
@@ -30,7 +30,7 @@ class SubstituteHandler : CommandHandler.SingleExecution() {
|
||||
override fun execute(editor: Editor, context: DataContext, cmd: ExCommand): Boolean {
|
||||
var result = true
|
||||
for (caret in editor.caretModel.allCarets) {
|
||||
val lineRange = cmd.getLineRange(editor, caret, context)
|
||||
val lineRange = cmd.getLineRange(editor, caret)
|
||||
if (!VimPlugin.getSearch().searchAndReplace(editor, caret, lineRange, cmd.command, cmd.argument)) {
|
||||
result = false
|
||||
}
|
||||
|
@@ -28,7 +28,7 @@ import com.maddyhome.idea.vim.ex.flags
|
||||
class WriteNextFileHandler : CommandHandler.SingleExecution() {
|
||||
override val argFlags = flags(RangeFlag.RANGE_IS_COUNT, ArgumentFlag.ARGUMENT_OPTIONAL, Access.READ_ONLY)
|
||||
override fun execute(editor: Editor, context: DataContext, cmd: ExCommand): Boolean {
|
||||
val count = cmd.getCount(editor, context, 1, true)
|
||||
val count = cmd.getCount(editor, 1, true)
|
||||
|
||||
VimPlugin.getFile().saveFile(context)
|
||||
VimPlugin.getMark().saveJumpLocation(editor)
|
||||
|
@@ -28,7 +28,7 @@ import com.maddyhome.idea.vim.ex.flags
|
||||
class WritePreviousFileHandler : CommandHandler.SingleExecution() {
|
||||
override val argFlags = flags(RangeFlag.RANGE_OPTIONAL, ArgumentFlag.ARGUMENT_OPTIONAL, Access.READ_ONLY)
|
||||
override fun execute(editor: Editor, context: DataContext, cmd: ExCommand): Boolean {
|
||||
val count = cmd.getCount(editor, context, 1, true)
|
||||
val count = cmd.getCount(editor, 1, true)
|
||||
|
||||
VimPlugin.getFile().saveFile(context)
|
||||
VimPlugin.getMark().saveJumpLocation(editor)
|
||||
|
@@ -45,7 +45,7 @@ class YankLinesHandler : CommandHandler.SingleExecution() {
|
||||
val starts = ArrayList<Int>(caretModel.caretCount)
|
||||
val ends = ArrayList<Int>(caretModel.caretCount)
|
||||
for (caret in caretModel.allCarets) {
|
||||
val range = cmd.getTextRange(editor, caret, context, true)
|
||||
val range = cmd.getTextRange(editor, caret, true)
|
||||
starts.add(range.startOffset)
|
||||
ends.add(range.endOffset)
|
||||
}
|
||||
|
@@ -17,7 +17,6 @@
|
||||
*/
|
||||
package com.maddyhome.idea.vim.ex.ranges
|
||||
|
||||
import com.intellij.openapi.actionSystem.DataContext
|
||||
import com.intellij.openapi.editor.Caret
|
||||
import com.intellij.openapi.editor.Editor
|
||||
import com.maddyhome.idea.vim.VimPlugin
|
||||
@@ -59,13 +58,13 @@ class Ranges {
|
||||
return endLine
|
||||
}
|
||||
|
||||
fun getLine(editor: Editor, caret: Caret, context: DataContext): Int {
|
||||
processRange(editor, caret, context)
|
||||
fun getLine(editor: Editor, caret: Caret): Int {
|
||||
processRange(editor, caret)
|
||||
return endLine
|
||||
}
|
||||
|
||||
fun getFirstLine(editor: Editor, caret: Caret, context: DataContext): Int {
|
||||
processRange(editor, caret, context)
|
||||
fun getFirstLine(editor: Editor, caret: Caret): Int {
|
||||
processRange(editor, caret)
|
||||
return startLine
|
||||
}
|
||||
|
||||
@@ -79,8 +78,8 @@ class Ranges {
|
||||
*/
|
||||
fun getCount(editor: Editor, count: Int): Int = if (count == -1) getLine(editor) else count
|
||||
|
||||
fun getCount(editor: Editor, caret: Caret, context: DataContext, count: Int): Int {
|
||||
return if (count == -1) getLine(editor, caret, context) else count
|
||||
fun getCount(editor: Editor, caret: Caret, count: Int): Int {
|
||||
return if (count == -1) getLine(editor, caret) else count
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -105,8 +104,8 @@ class Ranges {
|
||||
return LineRange(start, end)
|
||||
}
|
||||
|
||||
fun getLineRange(editor: Editor, caret: Caret, context: DataContext, count: Int): LineRange {
|
||||
processRange(editor, caret, context)
|
||||
fun getLineRange(editor: Editor, caret: Caret, count: Int): LineRange {
|
||||
processRange(editor, caret)
|
||||
return if (count == -1) LineRange(startLine, endLine) else LineRange(endLine, endLine + count - 1)
|
||||
}
|
||||
|
||||
@@ -120,15 +119,15 @@ class Ranges {
|
||||
* @param count The count given at the end of the command or -1 if no such count
|
||||
* @return The text range
|
||||
*/
|
||||
fun getTextRange(editor: Editor, context: DataContext?, count: Int): TextRange {
|
||||
fun getTextRange(editor: Editor, count: Int): TextRange {
|
||||
val lr = getLineRange(editor, count)
|
||||
val start = EditorHelper.getLineStartOffset(editor, lr.startLine)
|
||||
val end = EditorHelper.getLineEndOffset(editor, lr.endLine, true) + 1
|
||||
return TextRange(start, min(end, EditorHelper.getFileSize(editor)))
|
||||
}
|
||||
|
||||
fun getTextRange(editor: Editor, caret: Caret, context: DataContext, count: Int): TextRange {
|
||||
val lineRange = getLineRange(editor, caret, context, count)
|
||||
fun getTextRange(editor: Editor, caret: Caret, count: Int): TextRange {
|
||||
val lineRange = getLineRange(editor, caret, count)
|
||||
val start = EditorHelper.getLineStartOffset(editor, lineRange.startLine)
|
||||
val end = EditorHelper.getLineEndOffset(editor, lineRange.endLine, true) + 1
|
||||
return TextRange(start, min(end, EditorHelper.getFileSize(editor)))
|
||||
@@ -165,7 +164,7 @@ class Ranges {
|
||||
done = true
|
||||
}
|
||||
|
||||
private fun processRange(editor: Editor, caret: Caret, context: DataContext) {
|
||||
private fun processRange(editor: Editor, caret: Caret) {
|
||||
startLine = if (defaultLine == -1) caret.logicalPosition.line else defaultLine
|
||||
endLine = startLine
|
||||
var lastZero = false
|
||||
|
@@ -7,6 +7,7 @@ import com.intellij.openapi.editor.Editor;
|
||||
import com.maddyhome.idea.vim.VimPlugin;
|
||||
import com.maddyhome.idea.vim.command.*;
|
||||
import com.maddyhome.idea.vim.common.TextRange;
|
||||
import com.maddyhome.idea.vim.ex.vimscript.VimScriptGlobalEnvironment;
|
||||
import com.maddyhome.idea.vim.extension.VimExtension;
|
||||
import com.maddyhome.idea.vim.extension.VimExtensionHandler;
|
||||
import com.maddyhome.idea.vim.handler.TextObjectActionHandler;
|
||||
@@ -46,6 +47,117 @@ public class VimArgTextObjExtension implements VimExtension {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* The pairs of brackets that delimit different types of argument lists.
|
||||
*/
|
||||
static private class BracketPairs {
|
||||
// NOTE: brackets must match by the position, and ordered by rank (highest to lowest).
|
||||
@NotNull private final String openBrackets;
|
||||
@NotNull private final String closeBrackets;
|
||||
|
||||
static class ParseError extends Exception {
|
||||
public ParseError(@NotNull String message) {
|
||||
super(message);
|
||||
}
|
||||
}
|
||||
|
||||
private enum ParseState {
|
||||
OPEN,
|
||||
COLON,
|
||||
CLOSE,
|
||||
COMMA,
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs @ref BracketPair from a string of bracket pairs with the same syntax
|
||||
* as VIM's @c matchpairs option: "(:),{:},[:]"
|
||||
*
|
||||
* @param bracketPairs comma-separated list of colon-separated bracket pairs.
|
||||
* @throws ParseError if a syntax error is detected.
|
||||
*/
|
||||
@NotNull
|
||||
static BracketPairs fromBracketPairList(@NotNull final String bracketPairs) throws ParseError {
|
||||
StringBuilder openBrackets = new StringBuilder();
|
||||
StringBuilder closeBrackets = new StringBuilder();
|
||||
ParseState state = ParseState.OPEN;
|
||||
for (char ch : bracketPairs.toCharArray()) {
|
||||
switch (state) {
|
||||
case OPEN:
|
||||
openBrackets.append(ch);
|
||||
state = ParseState.COLON;
|
||||
break;
|
||||
case COLON:
|
||||
if (ch == ':') {
|
||||
state = ParseState.CLOSE;
|
||||
} else {
|
||||
throw new ParseError("expecting ':', but got '" + ch + "' instead");
|
||||
}
|
||||
break;
|
||||
case CLOSE:
|
||||
final char lastOpenBracket = openBrackets.charAt(openBrackets.length() - 1);
|
||||
if (lastOpenBracket == ch) {
|
||||
throw new ParseError("open and close brackets must be different");
|
||||
}
|
||||
closeBrackets.append(ch);
|
||||
state = ParseState.COMMA;
|
||||
break;
|
||||
case COMMA:
|
||||
if (ch == ',') {
|
||||
state = ParseState.OPEN;
|
||||
} else {
|
||||
throw new ParseError("expecting ',', but got '" + ch + "' instead");
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (state != ParseState.COMMA) {
|
||||
throw new ParseError("list of pairs is incomplete");
|
||||
}
|
||||
return new BracketPairs(openBrackets.toString(), closeBrackets.toString());
|
||||
}
|
||||
|
||||
BracketPairs(@NotNull final String openBrackets, @NotNull final String closeBrackets) {
|
||||
assert openBrackets.length() == closeBrackets.length();
|
||||
this.openBrackets = openBrackets;
|
||||
this.closeBrackets = closeBrackets;
|
||||
}
|
||||
|
||||
int getBracketPrio(char ch) {
|
||||
return Math.max(openBrackets.toString().indexOf(ch), closeBrackets.indexOf(ch));
|
||||
}
|
||||
|
||||
char matchingBracket(char ch) {
|
||||
int idx = closeBrackets.indexOf(ch);
|
||||
if (idx != -1) {
|
||||
return openBrackets.charAt(idx);
|
||||
} else {
|
||||
assert isOpenBracket(ch);
|
||||
idx = openBrackets.toString().indexOf(ch);
|
||||
return closeBrackets.charAt(idx);
|
||||
}
|
||||
}
|
||||
|
||||
boolean isCloseBracket(final int ch) {
|
||||
return closeBrackets.indexOf(ch) != -1;
|
||||
}
|
||||
|
||||
boolean isOpenBracket(final int ch) {
|
||||
return openBrackets.toString().indexOf(ch) != -1;
|
||||
}
|
||||
}
|
||||
|
||||
public static final BracketPairs DEFAULT_BRACKET_PAIRS = new BracketPairs("(", ")");
|
||||
|
||||
@Nullable
|
||||
private static String bracketPairsVariable() {
|
||||
final VimScriptGlobalEnvironment env = VimScriptGlobalEnvironment.getInstance();
|
||||
final Object value = env.getVariables().get("g:argtextobj_pairs");
|
||||
if (value instanceof String) {
|
||||
return (String) value;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* A text object for an argument to a function definition or a call.
|
||||
*/
|
||||
@@ -66,7 +178,18 @@ public class VimArgTextObjExtension implements VimExtension {
|
||||
|
||||
@Override
|
||||
public @Nullable TextRange getRange(@NotNull Editor editor, @NotNull Caret caret, @NotNull DataContext context, int count, int rawCount, @Nullable Argument argument) {
|
||||
final ArgBoundsFinder finder = new ArgBoundsFinder(editor.getDocument());
|
||||
BracketPairs bracketPairs = DEFAULT_BRACKET_PAIRS;
|
||||
final String bracketPairsVar = bracketPairsVariable();
|
||||
if (bracketPairsVar != null) {
|
||||
try {
|
||||
bracketPairs = BracketPairs.fromBracketPairList(bracketPairsVar);
|
||||
} catch (BracketPairs.ParseError parseError) {
|
||||
VimPlugin.showMessage("argtextobj: Invalid value of g:argtextobj_pairs -- " + parseError.getMessage());
|
||||
VimPlugin.indicateError();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
final ArgBoundsFinder finder = new ArgBoundsFinder(editor.getDocument(), bracketPairs);
|
||||
int pos = caret.getOffset();
|
||||
|
||||
for (int i = 0; i < count; ++i) {
|
||||
@@ -114,9 +237,7 @@ public class VimArgTextObjExtension implements VimExtension {
|
||||
});
|
||||
} else {
|
||||
commandState.getCommandBuilder().completeCommandPart(new Argument(new Command(count,
|
||||
textObjectHandler, Command.Type.MOTION, EnumSet.of(CommandFlags.FLAG_MOT_CHARACTERWISE),
|
||||
emptyList()
|
||||
)));
|
||||
textObjectHandler, Command.Type.MOTION, EnumSet.of(CommandFlags.FLAG_MOT_CHARACTERWISE))));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -126,8 +247,9 @@ public class VimArgTextObjExtension implements VimExtension {
|
||||
* position
|
||||
*/
|
||||
private static class ArgBoundsFinder {
|
||||
private final CharSequence text;
|
||||
private final Document document;
|
||||
@NotNull private final CharSequence text;
|
||||
@NotNull private final Document document;
|
||||
@NotNull private final BracketPairs brackets;
|
||||
private int leftBound = Integer.MAX_VALUE;
|
||||
private int rightBound = Integer.MIN_VALUE;
|
||||
private int leftBracket;
|
||||
@@ -135,18 +257,13 @@ public class VimArgTextObjExtension implements VimExtension {
|
||||
private String error = null;
|
||||
private static final String QUOTES = "\"'";
|
||||
|
||||
// NOTE: brackets must match by the position, and ordered by rank.
|
||||
// NOTE2: The original implementation worked for ], } and > as well, but because of some broken cases this feature
|
||||
// was removed.
|
||||
private static final String OPEN_BRACKETS = "("; // "[{(<"
|
||||
private static final String CLOSE_BRACKETS = ")"; // "]})>"
|
||||
|
||||
private static final int MAX_SEARCH_LINES = 10;
|
||||
private static final int MAX_SEARCH_OFFSET = MAX_SEARCH_LINES * 80;
|
||||
|
||||
ArgBoundsFinder(@NotNull Document document) {
|
||||
ArgBoundsFinder(@NotNull Document document, @NotNull BracketPairs bracketPairs) {
|
||||
this.text = document.getImmutableCharSequence();
|
||||
this.document = document;
|
||||
this.brackets = bracketPairs;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -167,7 +284,7 @@ public class VimArgTextObjExtension implements VimExtension {
|
||||
rightBound = Math.max(position, rightBound);
|
||||
getOutOfQuotedText();
|
||||
if (rightBound == leftBound) {
|
||||
if (isCloseBracket(getCharAt(rightBound))) {
|
||||
if (brackets.isCloseBracket(getCharAt(rightBound))) {
|
||||
--leftBound;
|
||||
} else {
|
||||
++rightBound;
|
||||
@@ -196,14 +313,15 @@ public class VimArgTextObjExtension implements VimExtension {
|
||||
findRightBound();
|
||||
nextRight = rightBound + 1;
|
||||
//
|
||||
// If reached text boundaries or there is nothing between delimiters.
|
||||
// If reached text boundaries
|
||||
//
|
||||
if (nextLeft < leftLimit || nextRight > rightLimit || (rightBound - leftBound) == 1) {
|
||||
if (nextLeft < leftLimit || nextRight > rightLimit) {
|
||||
error = "not an argument";
|
||||
return false;
|
||||
}
|
||||
bothBrackets = getCharAt(leftBound) != ',' && getCharAt(rightBound) != ',';
|
||||
if (bothBrackets && isIdentPreceding()) {
|
||||
final boolean nonEmptyArg = (rightBound - leftBound) > 1;
|
||||
if (bothBrackets && nonEmptyArg && isIdentPreceding()) {
|
||||
// Looking at a pair of brackets preceded by an
|
||||
// identifier -- single argument function call.
|
||||
break;
|
||||
@@ -257,7 +375,7 @@ public class VimArgTextObjExtension implements VimExtension {
|
||||
private boolean isIdentPreceding() {
|
||||
int i = leftBound - 1;
|
||||
final int idEnd = i;
|
||||
while (i > 0 && Character.isJavaIdentifierPart(getCharAt(i))) {
|
||||
while (i >= 0 && Character.isJavaIdentifierPart(getCharAt(i))) {
|
||||
--i;
|
||||
}
|
||||
return (idEnd - i) > 0 && Character.isJavaIdentifierStart(getCharAt(i + 1));
|
||||
@@ -296,8 +414,8 @@ public class VimArgTextObjExtension implements VimExtension {
|
||||
if (ch == ',') {
|
||||
break;
|
||||
}
|
||||
if (isOpenBracket(ch)) {
|
||||
rightBound = skipSexp(rightBound, rightBracket, SexpDirection.FORWARD);
|
||||
if (brackets.isOpenBracket(ch)) {
|
||||
rightBound = skipSexp(rightBound, rightBracket, SexpDirection.forward(brackets));
|
||||
} else {
|
||||
if (isQuoteChar(ch)) {
|
||||
rightBound = skipQuotedTextForward(rightBound, rightBracket);
|
||||
@@ -307,33 +425,14 @@ public class VimArgTextObjExtension implements VimExtension {
|
||||
}
|
||||
}
|
||||
|
||||
private static char matchingBracket(char ch) {
|
||||
int idx = CLOSE_BRACKETS.indexOf(ch);
|
||||
if (idx != -1) {
|
||||
return OPEN_BRACKETS.charAt(idx);
|
||||
} else {
|
||||
assert isOpenBracket(ch);
|
||||
idx = OPEN_BRACKETS.indexOf(ch);
|
||||
return CLOSE_BRACKETS.charAt(idx);
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean isCloseBracket(final int ch) {
|
||||
return CLOSE_BRACKETS.indexOf(ch) != -1;
|
||||
}
|
||||
|
||||
private static boolean isOpenBracket(final int ch) {
|
||||
return OPEN_BRACKETS.indexOf(ch) != -1;
|
||||
}
|
||||
|
||||
private void findLeftBound() {
|
||||
while (leftBound > leftBracket) {
|
||||
final char ch = getCharAt(leftBound);
|
||||
if (ch == ',') {
|
||||
break;
|
||||
}
|
||||
if (isCloseBracket(ch)) {
|
||||
leftBound = skipSexp(leftBound, leftBracket, SexpDirection.BACKWARD);
|
||||
if (brackets.isCloseBracket(ch)) {
|
||||
leftBound = skipSexp(leftBound, leftBracket, SexpDirection.backward(brackets));
|
||||
} else {
|
||||
if (isQuoteChar(ch)) {
|
||||
leftBound = skipQuotedTextBackward(leftBound, leftBracket);
|
||||
@@ -365,7 +464,7 @@ public class VimArgTextObjExtension implements VimExtension {
|
||||
while (i <= end) {
|
||||
final char ch = getCharAt(i);
|
||||
if (ch == quoteChar && !backSlash) {
|
||||
// Found matching quote and it's not escaped.
|
||||
// Found a matching quote, and it's not escaped.
|
||||
break;
|
||||
} else {
|
||||
backSlash = ch == '\\' && !backSlash;
|
||||
@@ -386,7 +485,7 @@ public class VimArgTextObjExtension implements VimExtension {
|
||||
// NOTE: doesn't handle cases like \\"str", but they make no
|
||||
// sense anyway.
|
||||
if (ch == quoteChar && prevChar != '\\') {
|
||||
// Found matching quote and it's not escaped.
|
||||
// Found a matching quote, and it's not escaped.
|
||||
break;
|
||||
}
|
||||
--i;
|
||||
@@ -424,48 +523,53 @@ public class VimArgTextObjExtension implements VimExtension {
|
||||
|
||||
abstract int skipQuotedText(int pos, int end, ArgBoundsFinder self);
|
||||
|
||||
static final SexpDirection FORWARD = new SexpDirection() {
|
||||
@Override
|
||||
int delta() {
|
||||
return 1;
|
||||
}
|
||||
static SexpDirection forward(BracketPairs brackets) {
|
||||
return new SexpDirection() {
|
||||
@Override
|
||||
int delta() {
|
||||
return 1;
|
||||
}
|
||||
|
||||
@Override
|
||||
boolean isOpenBracket(char ch) {
|
||||
return ArgBoundsFinder.isOpenBracket(ch);
|
||||
}
|
||||
@Override
|
||||
boolean isOpenBracket(char ch) {
|
||||
return brackets.isOpenBracket(ch);
|
||||
}
|
||||
|
||||
@Override
|
||||
boolean isCloseBracket(char ch) {
|
||||
return ArgBoundsFinder.isCloseBracket(ch);
|
||||
}
|
||||
@Override
|
||||
boolean isCloseBracket(char ch) {
|
||||
return brackets.isCloseBracket(ch);
|
||||
}
|
||||
|
||||
@Override
|
||||
int skipQuotedText(int pos, int end, ArgBoundsFinder self) {
|
||||
return self.skipQuotedTextForward(pos, end);
|
||||
}
|
||||
};
|
||||
static final SexpDirection BACKWARD = new SexpDirection() {
|
||||
@Override
|
||||
int delta() {
|
||||
return -1;
|
||||
}
|
||||
@Override
|
||||
int skipQuotedText(int pos, int end, ArgBoundsFinder self) {
|
||||
return self.skipQuotedTextForward(pos, end);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
boolean isOpenBracket(char ch) {
|
||||
return ArgBoundsFinder.isCloseBracket(ch);
|
||||
}
|
||||
static SexpDirection backward(BracketPairs brackets) {
|
||||
return new SexpDirection() {
|
||||
@Override
|
||||
int delta() {
|
||||
return -1;
|
||||
}
|
||||
|
||||
@Override
|
||||
boolean isCloseBracket(char ch) {
|
||||
return ArgBoundsFinder.isOpenBracket(ch);
|
||||
}
|
||||
@Override
|
||||
boolean isOpenBracket(char ch) {
|
||||
return brackets.isCloseBracket(ch);
|
||||
}
|
||||
|
||||
@Override
|
||||
int skipQuotedText(int pos, int end, ArgBoundsFinder self) {
|
||||
return self.skipQuotedTextBackward(pos, end);
|
||||
}
|
||||
};
|
||||
@Override
|
||||
boolean isCloseBracket(char ch) {
|
||||
return brackets.isOpenBracket(ch);
|
||||
}
|
||||
|
||||
@Override
|
||||
int skipQuotedText(int pos, int end, ArgBoundsFinder self) {
|
||||
return self.skipQuotedTextBackward(pos, end);
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -489,11 +593,11 @@ public class VimArgTextObjExtension implements VimExtension {
|
||||
bracketStack.push(ch);
|
||||
} else {
|
||||
if (dir.isCloseBracket(ch)) {
|
||||
if (bracketStack.lastElement() == matchingBracket(ch)) {
|
||||
if (bracketStack.lastElement() == brackets.matchingBracket(ch)) {
|
||||
bracketStack.pop();
|
||||
} else {
|
||||
//noinspection StatementWithEmptyBody
|
||||
if (getBracketPrio(ch) < getBracketPrio(bracketStack.lastElement())) {
|
||||
if (brackets.getBracketPrio(ch) < brackets.getBracketPrio(bracketStack.lastElement())) {
|
||||
// (<...) -> (...)
|
||||
bracketStack.pop();
|
||||
// Retry the same character again for cases like (...<<...).
|
||||
@@ -518,13 +622,6 @@ public class VimArgTextObjExtension implements VimExtension {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return rank of a bracket.
|
||||
*/
|
||||
static int getBracketPrio(char ch) {
|
||||
return Math.max(OPEN_BRACKETS.indexOf(ch), CLOSE_BRACKETS.indexOf(ch));
|
||||
}
|
||||
|
||||
/**
|
||||
* Find a pair of brackets surrounding (leftBracket..rightBracket) block.
|
||||
*
|
||||
@@ -535,8 +632,8 @@ public class VimArgTextObjExtension implements VimExtension {
|
||||
boolean findOuterBrackets(final int start, final int end) {
|
||||
boolean hasNewBracket = findPrevOpenBracket(start) && findNextCloseBracket(end);
|
||||
while (hasNewBracket) {
|
||||
final int leftPrio = getBracketPrio(getCharAt(leftBracket));
|
||||
final int rightPrio = getBracketPrio(getCharAt(rightBracket));
|
||||
final int leftPrio = brackets.getBracketPrio(getCharAt(leftBracket));
|
||||
final int rightPrio = brackets.getBracketPrio(getCharAt(rightBracket));
|
||||
if (leftPrio == rightPrio) {
|
||||
// matching brackets
|
||||
return true;
|
||||
@@ -569,9 +666,9 @@ public class VimArgTextObjExtension implements VimExtension {
|
||||
*/
|
||||
private boolean findPrevOpenBracket(final int start) {
|
||||
char ch;
|
||||
while (!isOpenBracket(ch = getCharAt(leftBracket))) {
|
||||
if (isCloseBracket(ch)) {
|
||||
leftBracket = skipSexp(leftBracket, start, SexpDirection.BACKWARD);
|
||||
while (!brackets.isOpenBracket(ch = getCharAt(leftBracket))) {
|
||||
if (brackets.isCloseBracket(ch)) {
|
||||
leftBracket = skipSexp(leftBracket, start, SexpDirection.backward(brackets));
|
||||
} else {
|
||||
if (isQuoteChar(ch)) {
|
||||
leftBracket = skipQuotedTextBackward(leftBracket, start);
|
||||
@@ -594,9 +691,9 @@ public class VimArgTextObjExtension implements VimExtension {
|
||||
*/
|
||||
private boolean findNextCloseBracket(final int end) {
|
||||
char ch;
|
||||
while (!isCloseBracket(ch = getCharAt(rightBracket))) {
|
||||
if (isOpenBracket(ch)) {
|
||||
rightBracket = skipSexp(rightBracket, end, SexpDirection.FORWARD);
|
||||
while (!brackets.isCloseBracket(ch = getCharAt(rightBracket))) {
|
||||
if (brackets.isOpenBracket(ch)) {
|
||||
rightBracket = skipSexp(rightBracket, end, SexpDirection.forward(brackets));
|
||||
} else {
|
||||
if (isQuoteChar(ch)) {
|
||||
rightBracket = skipQuotedTextForward(rightBracket, end);
|
||||
|
@@ -0,0 +1,150 @@
|
||||
/*
|
||||
* IdeaVim - Vim emulator for IDEs based on the IntelliJ platform
|
||||
* Copyright (C) 2003-2020 The IdeaVim authors
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.maddyhome.idea.vim.extension.replacewithregister
|
||||
|
||||
import com.intellij.openapi.actionSystem.DataContext
|
||||
import com.intellij.openapi.editor.Caret
|
||||
import com.intellij.openapi.editor.Editor
|
||||
import com.maddyhome.idea.vim.VimPlugin
|
||||
import com.maddyhome.idea.vim.command.CommandState
|
||||
import com.maddyhome.idea.vim.command.MappingMode
|
||||
import com.maddyhome.idea.vim.command.SelectionType
|
||||
import com.maddyhome.idea.vim.command.isLine
|
||||
import com.maddyhome.idea.vim.common.TextRange
|
||||
import com.maddyhome.idea.vim.extension.VimExtension
|
||||
import com.maddyhome.idea.vim.extension.VimExtensionFacade
|
||||
import com.maddyhome.idea.vim.extension.VimExtensionFacade.executeNormal
|
||||
import com.maddyhome.idea.vim.extension.VimExtensionFacade.putKeyMapping
|
||||
import com.maddyhome.idea.vim.extension.VimExtensionFacade.setOperatorFunction
|
||||
import com.maddyhome.idea.vim.extension.VimExtensionHandler
|
||||
import com.maddyhome.idea.vim.group.MotionGroup
|
||||
import com.maddyhome.idea.vim.group.RegisterGroup
|
||||
import com.maddyhome.idea.vim.group.copy.PutData
|
||||
import com.maddyhome.idea.vim.group.visual.VimSelection
|
||||
import com.maddyhome.idea.vim.helper.EditorDataContext
|
||||
import com.maddyhome.idea.vim.helper.StringHelper.parseKeys
|
||||
import com.maddyhome.idea.vim.helper.exitVisualMode
|
||||
import com.maddyhome.idea.vim.helper.subMode
|
||||
import com.maddyhome.idea.vim.helper.vimForEachCaret
|
||||
import com.maddyhome.idea.vim.key.OperatorFunction
|
||||
|
||||
|
||||
class ReplaceWithRegister : VimExtension {
|
||||
override fun getName(): String = "ReplaceWithRegister"
|
||||
|
||||
override fun init() {
|
||||
VimExtensionFacade.putExtensionHandlerMapping(MappingMode.N, parseKeys(RWR_OPERATOR), owner, RwrMotion(), false)
|
||||
VimExtensionFacade.putExtensionHandlerMapping(MappingMode.N, parseKeys(RWR_LINE), owner, RwrLine(), false)
|
||||
VimExtensionFacade.putExtensionHandlerMapping(MappingMode.X, parseKeys(RWR_VISUAL), owner, RwrVisual(), false)
|
||||
|
||||
putKeyMapping(MappingMode.N, parseKeys("gr"), owner, parseKeys(RWR_OPERATOR), true)
|
||||
putKeyMapping(MappingMode.N, parseKeys("grr"), owner, parseKeys(RWR_LINE), true)
|
||||
putKeyMapping(MappingMode.X, parseKeys("gr"), owner, parseKeys(RWR_VISUAL), true)
|
||||
}
|
||||
|
||||
private class RwrVisual : VimExtensionHandler {
|
||||
override fun execute(editor: Editor, context: DataContext) {
|
||||
val caretsAndSelections = mutableMapOf<Caret, VimSelection>()
|
||||
val typeInEditor = SelectionType.fromSubMode(editor.subMode)
|
||||
editor.vimForEachCaret { caret ->
|
||||
val selectionStart = caret.selectionStart
|
||||
val selectionEnd = caret.selectionEnd
|
||||
|
||||
caretsAndSelections += caret to VimSelection.create(selectionStart, selectionEnd - 1, typeInEditor, editor)
|
||||
}
|
||||
doReplace(editor, PutData.VisualSelection(caretsAndSelections, typeInEditor))
|
||||
editor.exitVisualMode()
|
||||
}
|
||||
}
|
||||
|
||||
private class RwrMotion : VimExtensionHandler {
|
||||
override fun isRepeatable(): Boolean = true
|
||||
|
||||
override fun execute(editor: Editor, context: DataContext) {
|
||||
setOperatorFunction(Operator())
|
||||
executeNormal(parseKeys("g@"), editor)
|
||||
}
|
||||
}
|
||||
|
||||
private class RwrLine : VimExtensionHandler {
|
||||
override fun isRepeatable(): Boolean = true
|
||||
|
||||
override fun execute(editor: Editor, context: DataContext) {
|
||||
val caretsAndSelections = mutableMapOf<Caret, VimSelection>()
|
||||
editor.vimForEachCaret { caret ->
|
||||
val logicalLine = caret.logicalPosition.line
|
||||
val lineStart = editor.document.getLineStartOffset(logicalLine)
|
||||
val lineEnd = editor.document.getLineEndOffset(logicalLine)
|
||||
|
||||
caretsAndSelections += caret to VimSelection.create(lineStart, lineEnd, SelectionType.LINE_WISE, editor)
|
||||
}
|
||||
|
||||
val visualSelection = PutData.VisualSelection(caretsAndSelections, SelectionType.LINE_WISE)
|
||||
doReplace(editor, visualSelection)
|
||||
|
||||
editor.vimForEachCaret { caret ->
|
||||
val vimStart = caretsAndSelections[caret]?.vimStart
|
||||
if (vimStart != null) {
|
||||
MotionGroup.moveCaret(editor, caret, vimStart)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class Operator : OperatorFunction {
|
||||
override fun apply(editor: Editor, context: DataContext, selectionType: SelectionType): Boolean {
|
||||
val range = getRange(editor) ?: return false
|
||||
val visualSelection = PutData.VisualSelection(mapOf(editor.caretModel.primaryCaret to VimSelection.create(range.startOffset, range.endOffset - 1, selectionType, editor)), selectionType)
|
||||
doReplace(editor, visualSelection)
|
||||
return true
|
||||
}
|
||||
|
||||
private fun getRange(editor: Editor): TextRange? = when (CommandState.getInstance(editor).mode) {
|
||||
CommandState.Mode.COMMAND -> VimPlugin.getMark().getChangeMarks(editor)
|
||||
CommandState.Mode.VISUAL -> editor.caretModel.primaryCaret.run { TextRange(selectionStart, selectionEnd) }
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val RWR_OPERATOR = "<Plug>ReplaceWithRegisterOperator"
|
||||
private const val RWR_LINE = "<Plug>ReplaceWithRegisterLine"
|
||||
private const val RWR_VISUAL = "<Plug>ReplaceWithRegisterVisual"
|
||||
|
||||
private fun doReplace(editor: Editor, visualSelection: PutData.VisualSelection) {
|
||||
val savedRegister = VimPlugin.getRegister().lastRegister ?: return
|
||||
|
||||
var usedType = savedRegister.type
|
||||
var usedText = savedRegister.text
|
||||
if (usedType.isLine && usedText?.endsWith('\n') == true) {
|
||||
// Code from original plugin implementation. Correct text for linewise selected text
|
||||
usedText = usedText.dropLast(1)
|
||||
usedType = SelectionType.CHARACTER_WISE
|
||||
}
|
||||
|
||||
val textData = PutData.TextData(usedText, usedType, savedRegister.transferableData)
|
||||
|
||||
val putData = PutData(textData, visualSelection, 1, insertTextBeforeCaret = true, _indent = true, caretAfterInsertedText = false, putToLine = -1)
|
||||
VimPlugin.getPut().putText(editor, EditorDataContext(editor), putData)
|
||||
|
||||
VimPlugin.getRegister().saveRegister(savedRegister.name, savedRegister)
|
||||
VimPlugin.getRegister().saveRegister(RegisterGroup.DEFAULT_REGISTER, savedRegister)
|
||||
}
|
||||
}
|
||||
}
|
@@ -144,8 +144,7 @@ public class VimTextObjEntireExtension implements VimExtension {
|
||||
} else {
|
||||
commandState.getCommandBuilder().completeCommandPart(new Argument(new Command(count,
|
||||
textObjectHandler, Command.Type.MOTION,
|
||||
EnumSet.of(CommandFlags.FLAG_MOT_CHARACTERWISE),
|
||||
emptyList())));
|
||||
EnumSet.of(CommandFlags.FLAG_MOT_CHARACTERWISE))));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -84,6 +84,8 @@ public class ChangeGroup {
|
||||
private static final String VIM_MOTION_WORD_END_RIGHT = "VimMotionWordEndRightAction";
|
||||
private static final String VIM_MOTION_BIG_WORD_END_RIGHT = "VimMotionBigWordEndRightAction";
|
||||
private static final String VIM_MOTION_CAMEL_END_RIGHT = "VimMotionCamelEndRightAction";
|
||||
private static final ImmutableSet<String> wordMotions =
|
||||
ImmutableSet.of(VIM_MOTION_WORD_RIGHT, VIM_MOTION_BIG_WORD_RIGHT, VIM_MOTION_CAMEL_RIGHT);
|
||||
|
||||
private @Nullable Command lastInsert;
|
||||
|
||||
@@ -1223,13 +1225,12 @@ public class ChangeGroup {
|
||||
boolean bigWord = id.equals(VIM_MOTION_BIG_WORD_RIGHT);
|
||||
final CharSequence chars = editor.getDocument().getCharsSequence();
|
||||
final int offset = caret.getOffset();
|
||||
if (EditorHelper.getFileSize(editor) > 0) {
|
||||
int fileSize = EditorHelper.getFileSize(editor);
|
||||
if (fileSize > 0) {
|
||||
final CharacterHelper.CharacterType charType = CharacterHelper.charType(chars.charAt(offset), bigWord);
|
||||
if (charType != CharacterHelper.CharacterType.WHITESPACE) {
|
||||
final boolean lastWordChar = offset > EditorHelper.getFileSize(editor) ||
|
||||
final boolean lastWordChar = offset >= fileSize - 1 ||
|
||||
CharacterHelper.charType(chars.charAt(offset + 1), bigWord) != charType;
|
||||
final ImmutableSet<String> wordMotions =
|
||||
ImmutableSet.of(VIM_MOTION_WORD_RIGHT, VIM_MOTION_BIG_WORD_RIGHT, VIM_MOTION_CAMEL_RIGHT);
|
||||
if (wordMotions.contains(id) && lastWordChar && motion.getCount() == 1) {
|
||||
final boolean res = deleteCharacter(editor, caret, 1, true);
|
||||
if (res) {
|
||||
@@ -1258,7 +1259,7 @@ public class ChangeGroup {
|
||||
}
|
||||
|
||||
if (kludge) {
|
||||
int size = EditorHelper.getFileSize(editor);
|
||||
int size = fileSize;
|
||||
int cnt = count * motion.getCount();
|
||||
int pos1 = SearchHelper.findNextWordEnd(chars, offset, size, cnt, bigWord, false);
|
||||
int pos2 = SearchHelper.findNextWordEnd(chars, pos1, size, -cnt, bigWord, false);
|
||||
|
@@ -60,7 +60,6 @@ public class DigraphGroup {
|
||||
|
||||
final String digraph = keys.get(ch);
|
||||
final String digraphText = digraph == null ? "" : ", Digr " + digraph;
|
||||
final String hexText = (ch > 0xff) ? String.format("Hex %04x", (int) ch) : String.format("Hex %02x", (int) ch);
|
||||
|
||||
if (ch < 0x100) {
|
||||
VimPlugin.showMessage(String.format("<%s> %d, Hex %02x, Oct %03o%s",
|
||||
|
@@ -195,10 +195,11 @@ class NotificationService(private val project: Project?) {
|
||||
const val IDEAVIM_NOTIFICATION_ID = "ideavim"
|
||||
const val IDEAVIM_NOTIFICATION_TITLE = "IdeaVim"
|
||||
const val ideajoinExamplesUrl = "https://github.com/JetBrains/ideavim/wiki/%60ideajoin%60-examples"
|
||||
const val selectModeUrl = "https://vimhelp.org/visual.txt.html#Select-mode"
|
||||
|
||||
private fun createIdeaVimRcManually(message: String, project: Project?) {
|
||||
val notification = Notification(IDEAVIM_NOTIFICATION_ID, IDEAVIM_NOTIFICATION_TITLE, message, NotificationType.WARNING)
|
||||
@Suppress("UnstableApiUsage", "DEPRECATION")
|
||||
// [VERSION UPDATE] 193+ com.intellij.ide.actions.RevealFileAction.openDirectory
|
||||
var actionName = if (SystemInfo.isMac) "Reveal Home in Finder" else "Show Home in " + ShowFilePathAction.getFileManagerName()
|
||||
if (!File(System.getProperty("user.home")).exists()) {
|
||||
actionName = ""
|
||||
@@ -206,6 +207,8 @@ class NotificationService(private val project: Project?) {
|
||||
notification.addAction(object : AnAction(actionName) {
|
||||
override fun actionPerformed(e: AnActionEvent) {
|
||||
val homeDir = File(System.getProperty("user.home"))
|
||||
@Suppress("DEPRECATION", "UnstableApiUsage")
|
||||
// [VERSION UPDATE] 193+ com.intellij.ide.actions.RevealFileAction.openDirectory
|
||||
ShowFilePathAction.openDirectory(homeDir)
|
||||
notification.expire()
|
||||
}
|
||||
|
@@ -88,8 +88,8 @@ public class RegisterGroup implements PersistentStateComponent<Element> {
|
||||
private static final List<Character> CLIPBOARD_REGISTERS = ImmutableList.of('*', '+');
|
||||
private static final Logger logger = Logger.getInstance(RegisterGroup.class.getName());
|
||||
|
||||
private char defaultRegister = '"';
|
||||
private char lastRegister = defaultRegister;
|
||||
public static char DEFAULT_REGISTER = '"';
|
||||
private char lastRegister = DEFAULT_REGISTER;
|
||||
private final @NotNull HashMap<Character, Register> registers = new HashMap<>();
|
||||
private char recordRegister = 0;
|
||||
private @Nullable List<KeyStroke> recordList = null;
|
||||
@@ -98,15 +98,15 @@ public class RegisterGroup implements PersistentStateComponent<Element> {
|
||||
final ListOption clipboardOption = OptionsManager.INSTANCE.getClipboard();
|
||||
clipboardOption.addOptionChangeListenerAndExecute((oldValue, newValue) -> {
|
||||
if (clipboardOption.contains("unnamed")) {
|
||||
defaultRegister = '*';
|
||||
DEFAULT_REGISTER = '*';
|
||||
}
|
||||
else if (clipboardOption.contains("unnamedplus")) {
|
||||
defaultRegister = '+';
|
||||
DEFAULT_REGISTER = '+';
|
||||
}
|
||||
else {
|
||||
defaultRegister = '"';
|
||||
DEFAULT_REGISTER = '"';
|
||||
}
|
||||
lastRegister = defaultRegister;
|
||||
lastRegister = DEFAULT_REGISTER;
|
||||
});
|
||||
}
|
||||
|
||||
@@ -117,6 +117,10 @@ public class RegisterGroup implements PersistentStateComponent<Element> {
|
||||
return READONLY_REGISTERS.indexOf(lastRegister) < 0;
|
||||
}
|
||||
|
||||
public boolean isValid(char reg) {
|
||||
return VALID_REGISTERS.indexOf(reg) != -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Store which register the user wishes to work with.
|
||||
*
|
||||
@@ -124,7 +128,7 @@ public class RegisterGroup implements PersistentStateComponent<Element> {
|
||||
* @return true if a valid register name, false if not
|
||||
*/
|
||||
public boolean selectRegister(char reg) {
|
||||
if (VALID_REGISTERS.indexOf(reg) != -1) {
|
||||
if (isValid(reg)) {
|
||||
lastRegister = reg;
|
||||
if (logger.isDebugEnabled()) logger.debug("register selected: " + lastRegister);
|
||||
|
||||
@@ -139,7 +143,7 @@ public class RegisterGroup implements PersistentStateComponent<Element> {
|
||||
* Reset the selected register back to the default register.
|
||||
*/
|
||||
public void resetRegister() {
|
||||
lastRegister = defaultRegister;
|
||||
lastRegister = DEFAULT_REGISTER;
|
||||
logger.debug("Last register reset to default register");
|
||||
}
|
||||
|
||||
@@ -213,8 +217,8 @@ public class RegisterGroup implements PersistentStateComponent<Element> {
|
||||
}
|
||||
|
||||
// Also add it to the default register if the default wasn't specified
|
||||
if (register != defaultRegister && ".:/".indexOf(register) == -1) {
|
||||
registers.put(defaultRegister, new Register(defaultRegister, type, processedText, new ArrayList<>(transferableData)));
|
||||
if (register != DEFAULT_REGISTER && ".:/".indexOf(register) == -1) {
|
||||
registers.put(DEFAULT_REGISTER, new Register(DEFAULT_REGISTER, type, processedText, new ArrayList<>(transferableData)));
|
||||
if (logger.isDebugEnabled()) logger.debug("register '" + register + "' contains: \"" + processedText + "\"");
|
||||
}
|
||||
|
||||
@@ -223,7 +227,7 @@ public class RegisterGroup implements PersistentStateComponent<Element> {
|
||||
editor.offsetToLogicalPosition(start).line == editor.offsetToLogicalPosition(end).line;
|
||||
|
||||
// Deletes go into numbered registers only if text is smaller than a line, register is used or it's a special case
|
||||
if (!smallInlineDeletion || register != defaultRegister || isSmallDeletionSpecialCase(editor)) {
|
||||
if (!smallInlineDeletion || register != DEFAULT_REGISTER || isSmallDeletionSpecialCase(editor)) {
|
||||
// Old 1 goes to 2, etc. Old 8 to 9, old 9 is lost
|
||||
for (char d = '8'; d >= '1'; d--) {
|
||||
Register t = registers.get(d);
|
||||
@@ -236,12 +240,12 @@ public class RegisterGroup implements PersistentStateComponent<Element> {
|
||||
}
|
||||
|
||||
// Deletes smaller than one line and without specified register go the the "-" register
|
||||
if (smallInlineDeletion && register == defaultRegister) {
|
||||
if (smallInlineDeletion && register == DEFAULT_REGISTER) {
|
||||
registers.put('-', new Register('-', type, processedText, new ArrayList<>(transferableData)));
|
||||
}
|
||||
}
|
||||
// Yanks also go to register 0 if the default register was used
|
||||
else if (register == defaultRegister) {
|
||||
else if (register == DEFAULT_REGISTER) {
|
||||
registers.put('0', new Register('0', type, processedText, new ArrayList<>(transferableData)));
|
||||
if (logger.isDebugEnabled()) logger.debug("register '" + '0' + "' contains: \"" + processedText + "\"");
|
||||
}
|
||||
@@ -343,6 +347,17 @@ public class RegisterGroup implements PersistentStateComponent<Element> {
|
||||
return CLIPBOARD_REGISTERS.contains(r) ? refreshClipboardRegister(r) : registers.get(r);
|
||||
}
|
||||
|
||||
public void saveRegister(char r, Register register) {
|
||||
// Uppercase registers actually get the lowercase register
|
||||
if (Character.isUpperCase(r)) {
|
||||
r = Character.toLowerCase(r);
|
||||
}
|
||||
if (CLIPBOARD_REGISTERS.contains(r)) {
|
||||
ClipboardHandler.setClipboardText(register.getText(), new ArrayList<>(register.getTransferableData()), register.getRawText());
|
||||
}
|
||||
registers.put(r, register);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the last register name selected by the user
|
||||
*
|
||||
@@ -356,7 +371,7 @@ public class RegisterGroup implements PersistentStateComponent<Element> {
|
||||
* The register key for the default register.
|
||||
*/
|
||||
public char getDefaultRegister() {
|
||||
return defaultRegister;
|
||||
return DEFAULT_REGISTER;
|
||||
}
|
||||
|
||||
public @NotNull List<Register> getRegisters() {
|
||||
|
@@ -27,13 +27,21 @@ import com.intellij.openapi.actionSystem.PlatformDataKeys
|
||||
import com.intellij.openapi.application.ApplicationManager
|
||||
import com.intellij.openapi.diagnostic.Logger
|
||||
import com.intellij.openapi.diagnostic.debug
|
||||
import com.intellij.openapi.editor.*
|
||||
import com.intellij.openapi.editor.Caret
|
||||
import com.intellij.openapi.editor.CaretStateTransferableData
|
||||
import com.intellij.openapi.editor.Editor
|
||||
import com.intellij.openapi.editor.LogicalPosition
|
||||
import com.intellij.openapi.editor.RangeMarker
|
||||
import com.intellij.openapi.editor.RawText
|
||||
import com.intellij.openapi.ide.CopyPasteManager
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.openapi.util.text.StringUtil
|
||||
import com.maddyhome.idea.vim.VimPlugin
|
||||
import com.maddyhome.idea.vim.command.CommandState
|
||||
import com.maddyhome.idea.vim.command.SelectionType
|
||||
import com.maddyhome.idea.vim.command.isBlock
|
||||
import com.maddyhome.idea.vim.command.isChar
|
||||
import com.maddyhome.idea.vim.command.isLine
|
||||
import com.maddyhome.idea.vim.common.TextRange
|
||||
import com.maddyhome.idea.vim.group.MarkGroup
|
||||
import com.maddyhome.idea.vim.group.MotionGroup
|
||||
@@ -96,7 +104,7 @@ class PutGroup {
|
||||
}
|
||||
|
||||
private fun collectPreModificationData(editor: Editor, data: PutData): Map<String, Any> {
|
||||
return if (data.visualSelection != null && data.visualSelection.typeInEditor == SelectionType.BLOCK_WISE) {
|
||||
return if (data.visualSelection != null && data.visualSelection.typeInEditor.isBlock) {
|
||||
val vimSelection = data.visualSelection.caretsAndSelections.getValue(editor.caretModel.primaryCaret)
|
||||
val selStart = editor.offsetToLogicalPosition(vimSelection.vimStart)
|
||||
val selEnd = editor.offsetToLogicalPosition(vimSelection.vimEnd)
|
||||
@@ -132,9 +140,11 @@ class PutGroup {
|
||||
return null
|
||||
}
|
||||
|
||||
if (data.visualSelection?.typeInEditor == SelectionType.LINE_WISE && data.textData.typeInRegister == SelectionType.CHARACTER_WISE) text += "\n"
|
||||
if (data.visualSelection?.typeInEditor?.isLine == true && data.textData.typeInRegister.isChar) text += "\n"
|
||||
|
||||
if (data.textData.typeInRegister == SelectionType.LINE_WISE && text.isNotEmpty() && text.last() != '\n') text += '\n'
|
||||
if (data.textData.typeInRegister.isLine && text.isNotEmpty() && text.last() != '\n') text += '\n'
|
||||
|
||||
if (data.textData.typeInRegister.isChar && text.lastOrNull() == '\n' && data.visualSelection?.typeInEditor?.isLine != true) text = text.dropLast(1)
|
||||
|
||||
return ProcessedTextData(text, data.textData.typeInRegister, data.textData.transferableData)
|
||||
}
|
||||
@@ -163,7 +173,7 @@ class PutGroup {
|
||||
}
|
||||
|
||||
private fun putForCaret(editor: Editor, caret: Caret, data: PutData, additionalData: Map<String, Any>, context: DataContext, text: ProcessedTextData) {
|
||||
if (data.visualSelection?.typeInEditor == SelectionType.LINE_WISE && editor.isOneLineMode) return
|
||||
if (data.visualSelection?.typeInEditor?.isLine == true && editor.isOneLineMode) return
|
||||
val startOffsets = prepareDocumentAndGetStartOffsets(editor, caret, text.typeInRegister, data, additionalData)
|
||||
|
||||
startOffsets.forEach { startOffset ->
|
||||
@@ -179,11 +189,11 @@ class PutGroup {
|
||||
val application = ApplicationManager.getApplication()
|
||||
if (data.visualSelection != null) {
|
||||
return when {
|
||||
data.visualSelection.typeInEditor == SelectionType.CHARACTER_WISE && typeInRegister == SelectionType.LINE_WISE -> {
|
||||
data.visualSelection.typeInEditor.isChar && typeInRegister.isLine -> {
|
||||
application.runWriteAction { editor.document.insertString(caret.offset, "\n") }
|
||||
listOf(caret.offset + 1)
|
||||
}
|
||||
data.visualSelection.typeInEditor == SelectionType.BLOCK_WISE -> {
|
||||
data.visualSelection.typeInEditor.isBlock -> {
|
||||
val firstSelectedLine = additionalData["firstSelectedLine"] as Int
|
||||
val selectedLines = additionalData["selectedLines"] as Int
|
||||
val startColumnOfSelection = additionalData["startColumnOfSelection"] as Int
|
||||
@@ -235,8 +245,8 @@ class PutGroup {
|
||||
}
|
||||
|
||||
private fun getProviderForPasteViaIde(context: DataContext, typeInRegister: SelectionType, data: PutData): PasteProvider? {
|
||||
if (data.visualSelection != null && data.visualSelection.typeInEditor == SelectionType.BLOCK_WISE) return null
|
||||
if ((typeInRegister == SelectionType.LINE_WISE || typeInRegister == SelectionType.CHARACTER_WISE) && data.count == 1) {
|
||||
if (data.visualSelection != null && data.visualSelection.typeInEditor.isBlock) return null
|
||||
if ((typeInRegister.isLine || typeInRegister.isChar) && data.count == 1) {
|
||||
val provider = PlatformDataKeys.PASTE_PROVIDER.getData(context)
|
||||
if (provider != null && provider.isPasteEnabled(context)) return provider
|
||||
}
|
||||
|
@@ -18,6 +18,7 @@
|
||||
|
||||
package com.maddyhome.idea.vim.helper;
|
||||
|
||||
import com.intellij.openapi.application.ApplicationManager;
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.openapi.editor.Editor;
|
||||
import com.maddyhome.idea.vim.VimPlugin;
|
||||
@@ -32,11 +33,11 @@ public class DigraphSequence {
|
||||
}
|
||||
|
||||
public static boolean isDigraphStart(@NotNull KeyStroke key) {
|
||||
return key.getKeyCode() == KeyEvent.VK_K && (key.getModifiers() & KeyEvent.CTRL_MASK) != 0;
|
||||
return key.getKeyCode() == KeyEvent.VK_K && (key.getModifiers() & KeyEvent.CTRL_DOWN_MASK) != 0;
|
||||
}
|
||||
|
||||
public static boolean isLiteralStart(@NotNull KeyStroke key) {
|
||||
return (key.getKeyCode() == KeyEvent.VK_V || key.getKeyCode() == KeyEvent.VK_Q) && (key.getModifiers() & KeyEvent.CTRL_MASK) != 0;
|
||||
return (key.getKeyCode() == KeyEvent.VK_V || key.getKeyCode() == KeyEvent.VK_Q) && (key.getModifiers() & KeyEvent.CTRL_DOWN_MASK) != 0;
|
||||
}
|
||||
|
||||
public DigraphResult startDigraphSequence() {
|
||||
@@ -201,7 +202,11 @@ public class DigraphSequence {
|
||||
digraphState = DIG_STATE_PENDING;
|
||||
KeyStroke code = KeyStroke.getKeyStroke((char)val);
|
||||
|
||||
VimPlugin.getMacro().postKey(key, editor);
|
||||
if (!ApplicationManager.getApplication().isUnitTestMode()) {
|
||||
// The key we received isn't part of the literal, so post it to be handled after we've handled the literal.
|
||||
// This requires swing, so we can't run it in tests.
|
||||
VimPlugin.getMacro().postKey(key, editor);
|
||||
}
|
||||
|
||||
return DigraphResult.done(code);
|
||||
}
|
||||
|
@@ -107,7 +107,10 @@ public class SearchHelper {
|
||||
return findBlockLocation(chars, found, match, dir, pos, count, false);
|
||||
}
|
||||
|
||||
public static @Nullable TextRange findBlockRange(@NotNull Editor editor, @NotNull Caret caret, char type, int count,
|
||||
public static @Nullable TextRange findBlockRange(@NotNull Editor editor,
|
||||
@NotNull Caret caret,
|
||||
char type,
|
||||
int count,
|
||||
boolean isOuter) {
|
||||
CharSequence chars = editor.getDocument().getCharsSequence();
|
||||
int pos = caret.getOffset();
|
||||
@@ -119,15 +122,19 @@ public class SearchHelper {
|
||||
|
||||
boolean rangeSelection = end - start > 1;
|
||||
if (rangeSelection && start == 0) // early return not only for optimization
|
||||
{
|
||||
return null; // but also not to break the interval semantic on this edge case (see below)
|
||||
}
|
||||
|
||||
/* In case of successive inner selection. We want to break out of
|
||||
* the block delimiter of the current inner selection.
|
||||
* In other terms, for the rest of the algorithm, a previous inner selection of a block
|
||||
* if equivalent to an outer one. */
|
||||
if (!isOuter
|
||||
&& (start - 1) >= 0 && type == chars.charAt(start - 1)
|
||||
&& end < chars.length() && close == chars.charAt(end)) {
|
||||
if (!isOuter &&
|
||||
(start - 1) >= 0 &&
|
||||
type == chars.charAt(start - 1) &&
|
||||
end < chars.length() &&
|
||||
close == chars.charAt(end)) {
|
||||
start = start - 1;
|
||||
pos = start;
|
||||
rangeSelection = true;
|
||||
@@ -136,8 +143,7 @@ public class SearchHelper {
|
||||
/* when one char is selected, we want to find the enclosing block of (start,end]
|
||||
* although when a range of characters is selected, we want the enclosing block of [start, end]
|
||||
* shifting the position allow to express which kind of interval we work on */
|
||||
if (rangeSelection)
|
||||
pos = Math.max(0, start - 1);
|
||||
if (rangeSelection) pos = Math.max(0, start - 1);
|
||||
|
||||
boolean initialPosIsInString = checkInString(chars, pos, true);
|
||||
|
||||
@@ -203,7 +209,9 @@ public class SearchHelper {
|
||||
return new TextRange(bstart, bend + 1);
|
||||
}
|
||||
|
||||
private static int findMatchingBlockCommentPair(@NotNull PsiComment comment, int pos, @Nullable String prefix,
|
||||
private static int findMatchingBlockCommentPair(@NotNull PsiComment comment,
|
||||
int pos,
|
||||
@Nullable String prefix,
|
||||
@Nullable String suffix) {
|
||||
if (prefix != null && suffix != null) {
|
||||
// TODO: Try to get rid of `getText()` because it takes a lot of time to calculate the string
|
||||
@@ -262,7 +270,7 @@ public class SearchHelper {
|
||||
|
||||
// To handle the case where visual mode allows the user to go past the end of the line,
|
||||
// which will prevent loc from finding a pairable character below
|
||||
if(pos > 0 && pos == end) {
|
||||
if (pos > 0 && pos == end) {
|
||||
pos = end - 1;
|
||||
}
|
||||
|
||||
@@ -324,7 +332,7 @@ public class SearchHelper {
|
||||
// Search to start or end of file, as appropriate
|
||||
Set<Character> charsToSearch = new HashSet<>(Arrays.asList('\'', '"', '\n', match, found));
|
||||
while (pos >= 0 && pos < chars.length() && cnt > 0) {
|
||||
Pair<Character, Integer> ci = findPositionOfFirstCharacter(chars, pos, charsToSearch, false, dir);
|
||||
Pair<Character, Integer> ci = findPositionOfFirstCharacter(chars, pos, charsToSearch, false, dir);
|
||||
if (ci == null) {
|
||||
return -1;
|
||||
}
|
||||
@@ -421,13 +429,11 @@ public class SearchHelper {
|
||||
return cnt;
|
||||
}
|
||||
|
||||
public static @Nullable Pair<Character, Integer> findPositionOfFirstCharacter(
|
||||
@NotNull CharSequence chars,
|
||||
int pos,
|
||||
final Set<Character> needles,
|
||||
boolean searchEscaped,
|
||||
@NotNull Direction direction
|
||||
) {
|
||||
public static @Nullable Pair<Character, Integer> findPositionOfFirstCharacter(@NotNull CharSequence chars,
|
||||
int pos,
|
||||
final Set<Character> needles,
|
||||
boolean searchEscaped,
|
||||
@NotNull Direction direction) {
|
||||
int dir = direction.toInt();
|
||||
while (pos >= 0 && pos < chars.length()) {
|
||||
final char c = chars.charAt(pos);
|
||||
@@ -439,8 +445,12 @@ public class SearchHelper {
|
||||
return null;
|
||||
}
|
||||
|
||||
private static int findCharacterPosition(@NotNull CharSequence chars, int pos, final char c, boolean currentLineOnly,
|
||||
boolean searchEscaped, @NotNull Direction direction) {
|
||||
private static int findCharacterPosition(@NotNull CharSequence chars,
|
||||
int pos,
|
||||
final char c,
|
||||
boolean currentLineOnly,
|
||||
boolean searchEscaped,
|
||||
@NotNull Direction direction) {
|
||||
while (pos >= 0 && pos < chars.length() && (!currentLineOnly || chars.charAt(pos) != '\n')) {
|
||||
if (chars.charAt(pos) == c && (pos == 0 || searchEscaped || isQuoteWithoutEscape(chars, pos, c))) {
|
||||
return pos;
|
||||
@@ -450,7 +460,9 @@ public class SearchHelper {
|
||||
return -1;
|
||||
}
|
||||
|
||||
/** returns new position which ignore whitespaces at beginning of the line*/
|
||||
/**
|
||||
* returns new position which ignore whitespaces at beginning of the line
|
||||
*/
|
||||
private static int ignoreWhitespaceAtLineStart(CharSequence seq, int lineStart, int pos) {
|
||||
if (seq.subSequence(lineStart, pos).chars().allMatch(Character::isWhitespace)) {
|
||||
while (pos < seq.length() && seq.charAt(pos) != '\n' && Character.isWhitespace(seq.charAt(pos))) {
|
||||
@@ -461,7 +473,10 @@ public class SearchHelper {
|
||||
}
|
||||
|
||||
|
||||
public static @Nullable TextRange findBlockTagRange(@NotNull Editor editor, @NotNull Caret caret, int count, boolean isOuter) {
|
||||
public static @Nullable TextRange findBlockTagRange(@NotNull Editor editor,
|
||||
@NotNull Caret caret,
|
||||
int count,
|
||||
boolean isOuter) {
|
||||
final int position = caret.getOffset();
|
||||
final CharSequence sequence = editor.getDocument().getCharsSequence();
|
||||
|
||||
@@ -475,20 +490,21 @@ public class SearchHelper {
|
||||
final int line = caret.getLogicalPosition().line;
|
||||
final int lineBegin = editor.getDocument().getLineStartOffset(line);
|
||||
searchStartPosition = ignoreWhitespaceAtLineStart(sequence, lineBegin, position);
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
searchStartPosition = selectionEnd;
|
||||
}
|
||||
|
||||
if (isInHTMLTag(sequence, searchStartPosition, false)) {
|
||||
// caret is inside opening tag. Move to closing '>'.
|
||||
while (searchStartPosition < sequence.length() && sequence.charAt(searchStartPosition) != '>') {
|
||||
searchStartPosition ++;
|
||||
searchStartPosition++;
|
||||
}
|
||||
}
|
||||
else if (isInHTMLTag(sequence, searchStartPosition, true)) {
|
||||
// caret is inside closing tag. Move to starting '<'.
|
||||
while (searchStartPosition > 0 && sequence.charAt(searchStartPosition) != '<') {
|
||||
searchStartPosition --;
|
||||
searchStartPosition--;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -514,22 +530,25 @@ public class SearchHelper {
|
||||
|
||||
int selectionEndWithoutNewline = selectionEnd;
|
||||
while (selectionEndWithoutNewline < sequence.length() && sequence.charAt(selectionEndWithoutNewline) == '\n') {
|
||||
selectionEndWithoutNewline ++;
|
||||
selectionEndWithoutNewline++;
|
||||
}
|
||||
|
||||
if (closingTagTextRange.getStartOffset() == selectionEndWithoutNewline && openingTag.getEndOffset() == selectionStart) {
|
||||
if (closingTagTextRange.getStartOffset() == selectionEndWithoutNewline &&
|
||||
openingTag.getEndOffset() == selectionStart) {
|
||||
// Special case: if the inner tag is already selected we should like isOuter is active
|
||||
// Note that we need to ignore newlines, because their selection is lost between multiple "it" invocations
|
||||
isOuter = true;
|
||||
} else
|
||||
if (openingTag.getEndOffset() == closingTagTextRange.getStartOffset() && selectionStart == openingTag.getEndOffset()) {
|
||||
}
|
||||
else if (openingTag.getEndOffset() == closingTagTextRange.getStartOffset() &&
|
||||
selectionStart == openingTag.getEndOffset()) {
|
||||
// Special case: for an empty tag pair (e.g. <a></a>) the whole tag is selected if the caret is in the middle.
|
||||
isOuter = true;
|
||||
}
|
||||
|
||||
if (isOuter) {
|
||||
return new TextRange(openingTag.getStartOffset(), closingTagTextRange.getEndOffset());
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
return new TextRange(openingTag.getEndOffset(), closingTagTextRange.getStartOffset());
|
||||
}
|
||||
}
|
||||
@@ -570,7 +589,9 @@ public class SearchHelper {
|
||||
return closingBracket != -1 && sequence.charAt(closingBracket - 1) != '/';
|
||||
}
|
||||
|
||||
private static @Nullable Pair<TextRange,String> findUnmatchedClosingTag(final @NotNull CharSequence sequence, final int position, int count) {
|
||||
private static @Nullable Pair<TextRange, String> findUnmatchedClosingTag(final @NotNull CharSequence sequence,
|
||||
final int position,
|
||||
int count) {
|
||||
// The tag name may contain any characters except slashes, whitespace and '>'
|
||||
final String tagNamePattern = "([^/\\s>]+)";
|
||||
// An opening tag consists of '<' followed by a tag name, optionally some additional text after whitespace and a '>'
|
||||
@@ -592,13 +613,16 @@ public class SearchHelper {
|
||||
if (openTags.isEmpty()) {
|
||||
if (count <= 1) {
|
||||
return Pair.create(new TextRange(position + matcher.start(), position + matcher.end()), tagName);
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
count--;
|
||||
}
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
openTags.pop();
|
||||
}
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
final String tagName = matcher.group(1);
|
||||
openTags.push(tagName);
|
||||
}
|
||||
@@ -606,14 +630,23 @@ public class SearchHelper {
|
||||
return null;
|
||||
}
|
||||
|
||||
private static @Nullable TextRange findUnmatchedOpeningTag(@NotNull CharSequence sequence, int position, @NotNull String tagName) {
|
||||
private static @Nullable TextRange findUnmatchedOpeningTag(@NotNull CharSequence sequence,
|
||||
int position,
|
||||
@NotNull String tagName) {
|
||||
final String quotedTagName = Pattern.quote(tagName);
|
||||
final String patternString = "(</%s>)" // match closing tags
|
||||
+ "|(<%s" // or opening tags starting with tagName
|
||||
+ "(\\s([^>]*" // After at least one whitespace there might be additional text in the tag. E.g. <html lang="en">
|
||||
+ "[^/])?)?>)"; // Slash is not allowed as last character (this would be a self closing tag).
|
||||
final Pattern tagPattern = Pattern.compile(String.format(patternString, quotedTagName, quotedTagName), Pattern.CASE_INSENSITIVE);
|
||||
final Matcher matcher = tagPattern.matcher(sequence.subSequence(0, position+1));
|
||||
final String patternString = "(</%s>)"
|
||||
// match closing tags
|
||||
+
|
||||
"|(<%s"
|
||||
// or opening tags starting with tagName
|
||||
+
|
||||
"(\\s([^>]*"
|
||||
// After at least one whitespace there might be additional text in the tag. E.g. <html lang="en">
|
||||
+
|
||||
"[^/])?)?>)"; // Slash is not allowed as last character (this would be a self closing tag).
|
||||
final Pattern tagPattern =
|
||||
Pattern.compile(String.format(patternString, quotedTagName, quotedTagName), Pattern.CASE_INSENSITIVE);
|
||||
final Matcher matcher = tagPattern.matcher(sequence.subSequence(0, position + 1));
|
||||
final Stack<TextRange> openTags = new Stack<>();
|
||||
|
||||
while (matcher.find()) {
|
||||
@@ -630,13 +663,16 @@ public class SearchHelper {
|
||||
|
||||
if (openTags.isEmpty()) {
|
||||
return null;
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
return openTags.pop();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static @Nullable TextRange findBlockQuoteInLineRange(@NotNull Editor editor, @NotNull Caret caret, char quote,
|
||||
public static @Nullable TextRange findBlockQuoteInLineRange(@NotNull Editor editor,
|
||||
@NotNull Caret caret,
|
||||
char quote,
|
||||
boolean isOuter) {
|
||||
final CharSequence chars = editor.getDocument().getCharsSequence();
|
||||
final int pos = caret.getOffset();
|
||||
@@ -832,7 +868,11 @@ public class SearchHelper {
|
||||
return findNextWord(chars, searchFrom, size, count, bigWord, false);
|
||||
}
|
||||
|
||||
public static int findNextWord(@NotNull CharSequence chars, int pos, int size, int count, boolean bigWord,
|
||||
public static int findNextWord(@NotNull CharSequence chars,
|
||||
int pos,
|
||||
int size,
|
||||
int count,
|
||||
boolean bigWord,
|
||||
boolean spaceWords) {
|
||||
int step = count >= 0 ? 1 : -1;
|
||||
count = Math.abs(count);
|
||||
@@ -848,7 +888,11 @@ public class SearchHelper {
|
||||
return res;
|
||||
}
|
||||
|
||||
private static int findNextWordOne(@NotNull CharSequence chars, int pos, int size, int step, boolean bigWord,
|
||||
private static int findNextWordOne(@NotNull CharSequence chars,
|
||||
int pos,
|
||||
int size,
|
||||
int step,
|
||||
boolean bigWord,
|
||||
boolean spaceWords) {
|
||||
boolean found = false;
|
||||
pos = pos < size ? pos : Math.min(size, chars.length() - 1);
|
||||
@@ -916,8 +960,11 @@ public class SearchHelper {
|
||||
return res;
|
||||
}
|
||||
|
||||
public static @NotNull List<TextRange> findNumbersInRange(final @NotNull Editor editor, @NotNull TextRange textRange,
|
||||
final boolean alpha, final boolean hex, final boolean octal) {
|
||||
public static @NotNull List<TextRange> findNumbersInRange(final @NotNull Editor editor,
|
||||
@NotNull TextRange textRange,
|
||||
final boolean alpha,
|
||||
final boolean hex,
|
||||
final boolean octal) {
|
||||
List<TextRange> result = new ArrayList<>();
|
||||
int firstLine = editor.offsetToLogicalPosition(textRange.getStartOffset()).line;
|
||||
int lastLine = editor.offsetToLogicalPosition(textRange.getEndOffset()).line;
|
||||
@@ -941,8 +988,11 @@ public class SearchHelper {
|
||||
return result;
|
||||
}
|
||||
|
||||
public static @Nullable TextRange findNumberUnderCursor(final @NotNull Editor editor, @NotNull Caret caret, final boolean alpha,
|
||||
final boolean hex, final boolean octal) {
|
||||
public static @Nullable TextRange findNumberUnderCursor(final @NotNull Editor editor,
|
||||
@NotNull Caret caret,
|
||||
final boolean alpha,
|
||||
final boolean hex,
|
||||
final boolean octal) {
|
||||
int lline = caret.getLogicalPosition().line;
|
||||
String text = EditorHelper.getLineText(editor, lline).toLowerCase();
|
||||
int startLineOffset = EditorHelper.getLineStartOffset(editor, lline);
|
||||
@@ -954,18 +1004,21 @@ public class SearchHelper {
|
||||
return null;
|
||||
}
|
||||
return new TextRange(numberTextRange.getStartOffset() + startLineOffset,
|
||||
numberTextRange.getEndOffset() + startLineOffset);
|
||||
numberTextRange.getEndOffset() + startLineOffset);
|
||||
}
|
||||
|
||||
/**
|
||||
* Search for number in given text from start position
|
||||
*
|
||||
* @param textInRange - text to search in
|
||||
* @param textInRange - text to search in
|
||||
* @param startPosOnLine - start offset to search
|
||||
* @return - text range with number
|
||||
*/
|
||||
public static @Nullable TextRange findNumberInText(final @NotNull String textInRange, int startPosOnLine, final boolean alpha,
|
||||
final boolean hex, final boolean octal) {
|
||||
public static @Nullable TextRange findNumberInText(final @NotNull String textInRange,
|
||||
int startPosOnLine,
|
||||
final boolean alpha,
|
||||
final boolean hex,
|
||||
final boolean octal) {
|
||||
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("text=" + textInRange);
|
||||
@@ -990,7 +1043,9 @@ public class SearchHelper {
|
||||
|
||||
if (hex) {
|
||||
// Ox and OX handling
|
||||
if (textInRange.charAt(pos) == '0' && pos < lineEndOffset - 1 && "xX".indexOf(textInRange.charAt(pos + 1)) >= 0) {
|
||||
if (textInRange.charAt(pos) == '0' &&
|
||||
pos < lineEndOffset - 1 &&
|
||||
"xX".indexOf(textInRange.charAt(pos + 1)) >= 0) {
|
||||
pos += 2;
|
||||
}
|
||||
else if ("xX".indexOf(textInRange.charAt(pos)) >= 0 && pos > 0 && textInRange.charAt(pos - 1) == '0') {
|
||||
@@ -1055,8 +1110,12 @@ public class SearchHelper {
|
||||
/**
|
||||
* Searches for digits block that matches parameters
|
||||
*/
|
||||
private static @NotNull Pair<Integer, Integer> findRange(final @NotNull String text, final int pos, final boolean alpha,
|
||||
final boolean hex, final boolean octal, final boolean decimal) {
|
||||
private static @NotNull Pair<Integer, Integer> findRange(final @NotNull String text,
|
||||
final int pos,
|
||||
final boolean alpha,
|
||||
final boolean hex,
|
||||
final boolean octal,
|
||||
final boolean decimal) {
|
||||
int end = pos;
|
||||
while (end < text.length() && isNumberChar(text.charAt(end), alpha, hex, octal, decimal)) {
|
||||
end++;
|
||||
@@ -1093,7 +1152,7 @@ public class SearchHelper {
|
||||
* Find the word under the cursor or the next word to the right of the cursor on the current line.
|
||||
*
|
||||
* @param editor The editor to find the word in
|
||||
* @param caret The caret to find word under
|
||||
* @param caret The caret to find word under
|
||||
* @return The text range of the found word or null if there is no word under/after the cursor on the line
|
||||
*/
|
||||
public static @Nullable TextRange findWordUnderCursor(@NotNull Editor editor, @NotNull Caret caret) {
|
||||
@@ -1146,8 +1205,13 @@ public class SearchHelper {
|
||||
}
|
||||
|
||||
@Contract("_, _, _, _, _, _, _ -> new")
|
||||
public static @NotNull TextRange findWordUnderCursor(@NotNull Editor editor, @NotNull Caret caret, int count, int dir,
|
||||
boolean isOuter, boolean isBig, boolean hasSelection) {
|
||||
public static @NotNull TextRange findWordUnderCursor(@NotNull Editor editor,
|
||||
@NotNull Caret caret,
|
||||
int count,
|
||||
int dir,
|
||||
boolean isOuter,
|
||||
boolean isBig,
|
||||
boolean hasSelection) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("count=" + count);
|
||||
logger.debug("dir=" + dir);
|
||||
@@ -1161,6 +1225,7 @@ public class SearchHelper {
|
||||
//int max = EditorHelper.getLineEndOffset(editor, EditorHelper.getCurrentLogicalLine(editor), true);
|
||||
int min = 0;
|
||||
int max = EditorHelper.getFileSize(editor);
|
||||
if (max == 0) return new TextRange(0, 0);
|
||||
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("min=" + min);
|
||||
@@ -1304,7 +1369,11 @@ public class SearchHelper {
|
||||
return findNextWordEnd(chars, pos, size, count, bigWord, false);
|
||||
}
|
||||
|
||||
public static int findNextWordEnd(@NotNull CharSequence chars, int pos, int size, int count, boolean bigWord,
|
||||
public static int findNextWordEnd(@NotNull CharSequence chars,
|
||||
int pos,
|
||||
int size,
|
||||
int count,
|
||||
boolean bigWord,
|
||||
boolean spaceWords) {
|
||||
int step = count >= 0 ? 1 : -1;
|
||||
count = Math.abs(count);
|
||||
@@ -1320,7 +1389,11 @@ public class SearchHelper {
|
||||
return res;
|
||||
}
|
||||
|
||||
private static int findNextWordEndOne(@NotNull CharSequence chars, int pos, int size, int step, boolean bigWord,
|
||||
private static int findNextWordEndOne(@NotNull CharSequence chars,
|
||||
int pos,
|
||||
int size,
|
||||
int step,
|
||||
boolean bigWord,
|
||||
boolean spaceWords) {
|
||||
boolean found = false;
|
||||
// For forward searches, skip any current whitespace so we start at the start of a word
|
||||
@@ -1366,12 +1439,10 @@ public class SearchHelper {
|
||||
}
|
||||
|
||||
if (found) {
|
||||
if (res < 0)
|
||||
{
|
||||
if (res < 0) {
|
||||
res = 0;
|
||||
}
|
||||
else if (res >= size)
|
||||
{
|
||||
else if (res >= size) {
|
||||
res = size - 1;
|
||||
}
|
||||
}
|
||||
@@ -1445,7 +1516,10 @@ public class SearchHelper {
|
||||
}
|
||||
}
|
||||
|
||||
public static int findNextSentenceStart(@NotNull Editor editor, @NotNull Caret caret, int count, boolean countCurrent,
|
||||
public static int findNextSentenceStart(@NotNull Editor editor,
|
||||
@NotNull Caret caret,
|
||||
int count,
|
||||
boolean countCurrent,
|
||||
boolean requireAll) {
|
||||
int dir = count > 0 ? 1 : -1;
|
||||
count = Math.abs(count);
|
||||
@@ -1483,7 +1557,10 @@ public class SearchHelper {
|
||||
return findNextSentenceStart(editor, editor.getCaretModel().getPrimaryCaret(), count, countCurrent, requireAll);
|
||||
}
|
||||
|
||||
public static int findNextSentenceEnd(@NotNull Editor editor, @NotNull Caret caret, int count, boolean countCurrent,
|
||||
public static int findNextSentenceEnd(@NotNull Editor editor,
|
||||
@NotNull Caret caret,
|
||||
int count,
|
||||
boolean countCurrent,
|
||||
boolean requireAll) {
|
||||
int dir = count > 0 ? 1 : -1;
|
||||
count = Math.abs(count);
|
||||
@@ -1514,8 +1591,13 @@ public class SearchHelper {
|
||||
return res;
|
||||
}
|
||||
|
||||
private static int findSentenceStart(@NotNull Editor editor, @NotNull CharSequence chars, int start, int max, int dir,
|
||||
boolean countCurrent, boolean multiple) {
|
||||
private static int findSentenceStart(@NotNull Editor editor,
|
||||
@NotNull CharSequence chars,
|
||||
int start,
|
||||
int max,
|
||||
int dir,
|
||||
boolean countCurrent,
|
||||
boolean multiple) {
|
||||
// Save off the next paragraph since a paragraph is a valid sentence.
|
||||
int lline = editor.offsetToLogicalPosition(start).line;
|
||||
int np = findNextParagraph(editor, lline, dir, false, multiple);
|
||||
@@ -1600,8 +1682,13 @@ public class SearchHelper {
|
||||
return res;
|
||||
}
|
||||
|
||||
private static int findSentenceEnd(@NotNull Editor editor, @NotNull CharSequence chars, int start, int max, int dir,
|
||||
boolean countCurrent, boolean multiple) {
|
||||
private static int findSentenceEnd(@NotNull Editor editor,
|
||||
@NotNull CharSequence chars,
|
||||
int start,
|
||||
int max,
|
||||
int dir,
|
||||
boolean countCurrent,
|
||||
boolean multiple) {
|
||||
if (dir > 0 && start >= EditorHelper.getFileSize(editor) - 1) {
|
||||
return -1;
|
||||
}
|
||||
@@ -1752,8 +1839,13 @@ public class SearchHelper {
|
||||
return res;
|
||||
}
|
||||
|
||||
private static int findSentenceRangeEnd(@NotNull Editor editor, @NotNull CharSequence chars, int start, int max,
|
||||
int count, boolean isOuter, boolean oneway) {
|
||||
private static int findSentenceRangeEnd(@NotNull Editor editor,
|
||||
@NotNull CharSequence chars,
|
||||
int start,
|
||||
int max,
|
||||
int count,
|
||||
boolean isOuter,
|
||||
boolean oneway) {
|
||||
int dir = count > 0 ? 1 : -1;
|
||||
count = Math.abs(count);
|
||||
int total = count;
|
||||
@@ -1878,7 +1970,10 @@ public class SearchHelper {
|
||||
}
|
||||
|
||||
@Contract("_, _, _, _ -> new")
|
||||
public static @NotNull TextRange findSentenceRange(@NotNull Editor editor, @NotNull Caret caret, int count, boolean isOuter) {
|
||||
public static @NotNull TextRange findSentenceRange(@NotNull Editor editor,
|
||||
@NotNull Caret caret,
|
||||
int count,
|
||||
boolean isOuter) {
|
||||
CharSequence chars = editor.getDocument().getCharsSequence();
|
||||
if (chars.length() == 0) return new TextRange(0, 0);
|
||||
int max = EditorHelper.getFileSize(editor);
|
||||
@@ -1939,7 +2034,10 @@ public class SearchHelper {
|
||||
return findNextParagraph(editor, editor.getCaretModel().getPrimaryCaret(), count, allowBlanks);
|
||||
}
|
||||
|
||||
private static int findNextParagraph(@NotNull Editor editor, int lline, int dir, boolean allowBlanks,
|
||||
private static int findNextParagraph(@NotNull Editor editor,
|
||||
int lline,
|
||||
int dir,
|
||||
boolean allowBlanks,
|
||||
boolean skipLines) {
|
||||
int line = findNextParagraphLine(editor, lline, dir, allowBlanks, skipLines);
|
||||
|
||||
@@ -1951,7 +2049,9 @@ public class SearchHelper {
|
||||
}
|
||||
}
|
||||
|
||||
private static int findNextParagraphLine(@NotNull Editor editor, @NotNull Caret caret, int count,
|
||||
private static int findNextParagraphLine(@NotNull Editor editor,
|
||||
@NotNull Caret caret,
|
||||
int count,
|
||||
boolean allowBlanks) {
|
||||
int line = caret.getLogicalPosition().line;
|
||||
|
||||
@@ -1982,7 +2082,10 @@ public class SearchHelper {
|
||||
return findNextParagraphLine(editor, editor.getCaretModel().getPrimaryCaret(), count, allowBlanks);
|
||||
}
|
||||
|
||||
private static int findNextParagraphLine(@NotNull Editor editor, int line, int dir, boolean allowBlanks,
|
||||
private static int findNextParagraphLine(@NotNull Editor editor,
|
||||
int line,
|
||||
int dir,
|
||||
boolean allowBlanks,
|
||||
boolean skipLines) {
|
||||
int maxline = EditorHelper.getLineCount(editor);
|
||||
int res = -1;
|
||||
@@ -2015,7 +2118,10 @@ public class SearchHelper {
|
||||
return line;
|
||||
}
|
||||
|
||||
public static @Nullable TextRange findParagraphRange(@NotNull Editor editor, @NotNull Caret caret, int count, boolean isOuter) {
|
||||
public static @Nullable TextRange findParagraphRange(@NotNull Editor editor,
|
||||
@NotNull Caret caret,
|
||||
int count,
|
||||
boolean isOuter) {
|
||||
int line = caret.getLogicalPosition().line;
|
||||
int maxline = EditorHelper.getLineCount(editor);
|
||||
if (logger.isDebugEnabled()) logger.debug("starting on line " + line);
|
||||
|
@@ -101,7 +101,7 @@ public class StringHelper {
|
||||
stroke = getKeyStroke(c, 0);
|
||||
}
|
||||
else if (isControlCharacter(c)) {
|
||||
stroke = getKeyStroke(c + 'A' - 1, CTRL_MASK);
|
||||
stroke = getKeyStroke(c + 'A' - 1, CTRL_DOWN_MASK);
|
||||
}
|
||||
else {
|
||||
stroke = getKeyStroke(c);
|
||||
@@ -205,16 +205,16 @@ public class StringHelper {
|
||||
}
|
||||
|
||||
String prefix = "";
|
||||
if ((modifiers & META_MASK) != 0) {
|
||||
if ((modifiers & META_DOWN_MASK) != 0) {
|
||||
prefix += "M-";
|
||||
}
|
||||
if ((modifiers & ALT_MASK) != 0) {
|
||||
if ((modifiers & ALT_DOWN_MASK) != 0) {
|
||||
prefix += "A-";
|
||||
}
|
||||
if ((modifiers & CTRL_MASK) != 0) {
|
||||
if ((modifiers & CTRL_DOWN_MASK) != 0) {
|
||||
prefix += "C-";
|
||||
}
|
||||
if ((modifiers & SHIFT_MASK) != 0) {
|
||||
if ((modifiers & SHIFT_DOWN_MASK) != 0) {
|
||||
prefix += "S-";
|
||||
}
|
||||
|
||||
@@ -268,7 +268,7 @@ public class StringHelper {
|
||||
if (c == CHAR_UNDEFINED && key.getModifiers() == 0) {
|
||||
c = (char)key.getKeyCode();
|
||||
}
|
||||
else if (c == CHAR_UNDEFINED && (key.getModifiers() & CTRL_MASK) != 0) {
|
||||
else if (c == CHAR_UNDEFINED && (key.getModifiers() & CTRL_DOWN_MASK) != 0) {
|
||||
c = (char)(key.getKeyCode() - 'A' + 1);
|
||||
}
|
||||
|
||||
@@ -304,8 +304,8 @@ public class StringHelper {
|
||||
public static boolean isCloseKeyStroke(@NotNull KeyStroke key) {
|
||||
return key.getKeyCode() == VK_ESCAPE ||
|
||||
key.getKeyChar() == VK_ESCAPE ||
|
||||
key.getKeyCode() == VK_C && (key.getModifiers() & CTRL_MASK) != 0 ||
|
||||
key.getKeyCode() == '[' && (key.getModifiers() & CTRL_MASK) != 0;
|
||||
key.getKeyCode() == VK_C && (key.getModifiers() & CTRL_DOWN_MASK) != 0 ||
|
||||
key.getKeyCode() == '[' && (key.getModifiers() & CTRL_DOWN_MASK) != 0;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -367,16 +367,16 @@ public class StringHelper {
|
||||
return getTypedOrPressedKeyStroke(typedChar, modifiers);
|
||||
}
|
||||
else if (lower.startsWith(META_PREFIX)) {
|
||||
return parseSpecialKey(s.substring(META_PREFIX.length()), modifiers | META_MASK);
|
||||
return parseSpecialKey(s.substring(META_PREFIX.length()), modifiers | META_DOWN_MASK);
|
||||
}
|
||||
else if (lower.startsWith(ALT_PREFIX)) {
|
||||
return parseSpecialKey(s.substring(ALT_PREFIX.length()), modifiers | ALT_MASK);
|
||||
return parseSpecialKey(s.substring(ALT_PREFIX.length()), modifiers | ALT_DOWN_MASK);
|
||||
}
|
||||
else if (lower.startsWith(CTRL_PREFIX)) {
|
||||
return parseSpecialKey(s.substring(CTRL_PREFIX.length()), modifiers | CTRL_MASK);
|
||||
return parseSpecialKey(s.substring(CTRL_PREFIX.length()), modifiers | CTRL_DOWN_MASK);
|
||||
}
|
||||
else if (lower.startsWith(SHIFT_PREFIX)) {
|
||||
return parseSpecialKey(s.substring(SHIFT_PREFIX.length()), modifiers | SHIFT_MASK);
|
||||
return parseSpecialKey(s.substring(SHIFT_PREFIX.length()), modifiers | SHIFT_DOWN_MASK);
|
||||
}
|
||||
else if (s.length() == 1) {
|
||||
return getTypedOrPressedKeyStroke(s.charAt(0), modifiers);
|
||||
@@ -535,8 +535,8 @@ public class StringHelper {
|
||||
if (modifiers == 0) {
|
||||
return getKeyStroke(c);
|
||||
}
|
||||
else if (modifiers == SHIFT_MASK) {
|
||||
return getKeyStroke(Character.toUpperCase(c));
|
||||
else if (modifiers == SHIFT_DOWN_MASK) {
|
||||
return getKeyStroke(Character.toUpperCase(c), modifiers);
|
||||
}
|
||||
else {
|
||||
return getKeyStroke(Character.toUpperCase(c), modifiers);
|
||||
|
@@ -42,8 +42,11 @@ fun runAfterGotFocus(runnable: Runnable) {
|
||||
IdeFocusManager.findInstance().doWhenFocusSettlesDown(runnable, ModalityState.defaultModalityState())
|
||||
}
|
||||
|
||||
val editorFont: Font
|
||||
get() {
|
||||
val scheme = EditorColorsManager.getInstance().globalScheme
|
||||
return Font(scheme.editorFontName, Font.PLAIN, scheme.editorFontSize)
|
||||
}
|
||||
fun selectFont(forStr: String): Font {
|
||||
val scheme = EditorColorsManager.getInstance().globalScheme
|
||||
|
||||
val fontName = scheme.fontPreferences.realFontFamilies.firstOrNull {
|
||||
Font(it, Font.PLAIN, scheme.editorFontSize).canDisplayUpTo(forStr) == -1
|
||||
} ?: return Font(scheme.editorFontName, Font.PLAIN, scheme.editorFontSize)
|
||||
return Font(fontName, Font.PLAIN, scheme.editorFontSize)
|
||||
}
|
||||
|
@@ -40,6 +40,7 @@ import com.intellij.openapi.project.Project
|
||||
import com.intellij.openapi.project.ProjectManager
|
||||
import com.maddyhome.idea.vim.EventFacade
|
||||
import com.maddyhome.idea.vim.KeyHandler
|
||||
import com.maddyhome.idea.vim.VimKeyListener
|
||||
import com.maddyhome.idea.vim.VimPlugin
|
||||
import com.maddyhome.idea.vim.VimTypedActionHandler
|
||||
import com.maddyhome.idea.vim.command.CommandState
|
||||
@@ -65,6 +66,7 @@ import com.maddyhome.idea.vim.helper.vimLastColumn
|
||||
import com.maddyhome.idea.vim.helper.vimMotionGroup
|
||||
import com.maddyhome.idea.vim.option.OptionsManager
|
||||
import com.maddyhome.idea.vim.ui.ExEntryPanel
|
||||
import com.maddyhome.idea.vim.ui.ShowCmdOptionChangeListener
|
||||
import java.awt.event.MouseAdapter
|
||||
import java.awt.event.MouseEvent
|
||||
import java.io.Closeable
|
||||
@@ -159,6 +161,8 @@ object VimListenerManager {
|
||||
|
||||
object GlobalListeners {
|
||||
fun enable() {
|
||||
@Suppress("DEPRECATION")
|
||||
// [VERSION UPDATE] 193+ com.intellij.openapi.editor.actionSystem.TypedAction.getInstance
|
||||
val typedAction = EditorActionManager.getInstance().typedAction
|
||||
if (typedAction.rawHandler !is VimTypedActionHandler) {
|
||||
// Actually this if should always be true, but just as protection
|
||||
@@ -167,6 +171,7 @@ object VimListenerManager {
|
||||
|
||||
OptionsManager.number.addOptionChangeListener(EditorGroup.NumberChangeListener.INSTANCE)
|
||||
OptionsManager.relativenumber.addOptionChangeListener(EditorGroup.NumberChangeListener.INSTANCE)
|
||||
OptionsManager.showcmd.addOptionChangeListener(ShowCmdOptionChangeListener)
|
||||
|
||||
EventFacade.getInstance().addEditorFactoryListener(VimEditorFactoryListener, ApplicationManager.getApplication())
|
||||
}
|
||||
@@ -176,6 +181,7 @@ object VimListenerManager {
|
||||
|
||||
OptionsManager.number.removeOptionChangeListener(EditorGroup.NumberChangeListener.INSTANCE)
|
||||
OptionsManager.relativenumber.removeOptionChangeListener(EditorGroup.NumberChangeListener.INSTANCE)
|
||||
OptionsManager.showcmd.addOptionChangeListener(ShowCmdOptionChangeListener)
|
||||
|
||||
EventFacade.getInstance().removeEditorFactoryListener(VimEditorFactoryListener)
|
||||
}
|
||||
@@ -223,6 +229,7 @@ object VimListenerManager {
|
||||
|
||||
@JvmStatic
|
||||
fun add(editor: Editor) {
|
||||
editor.contentComponent.addKeyListener(VimKeyListener)
|
||||
val eventFacade = EventFacade.getInstance()
|
||||
eventFacade.addEditorMouseListener(editor, EditorMouseHandler)
|
||||
eventFacade.addEditorMouseMotionListener(editor, EditorMouseHandler)
|
||||
@@ -232,6 +239,7 @@ object VimListenerManager {
|
||||
|
||||
@JvmStatic
|
||||
fun remove(editor: Editor) {
|
||||
editor.contentComponent.removeKeyListener(VimKeyListener)
|
||||
val eventFacade = EventFacade.getInstance()
|
||||
eventFacade.removeEditorMouseListener(editor, EditorMouseHandler)
|
||||
eventFacade.removeEditorMouseMotionListener(editor, EditorMouseHandler)
|
||||
|
@@ -70,6 +70,7 @@ object OptionsManager {
|
||||
val scrolloff = addOption(NumberOption("scrolloff", "so", 0))
|
||||
val selection = addOption(BoundStringOption("selection", "sel", "inclusive", arrayOf("old", "inclusive", "exclusive")))
|
||||
val selectmode = addOption(SelectModeOptionData.option)
|
||||
val showcmd = addOption(ToggleOption("showcmd", "sc", true)) // Vim: Off by default on platforms with possibly slow tty. On by default elsewhere.
|
||||
val showmode = addOption(ToggleOption("showmode", "smd", false))
|
||||
val sidescroll = addOption(NumberOption("sidescroll", "ss", 0))
|
||||
val sidescrolloff = addOption(NumberOption("sidescrolloff", "siso", 0))
|
||||
|
@@ -40,7 +40,7 @@
|
||||
* |i_CTRL-I| IntelliJ editor tab
|
||||
* |i_<NL>| {@link com.maddyhome.idea.vim.action.change.insert.InsertEnterAction}
|
||||
* |i_CTRL-J| TO BE IMPLEMENTED
|
||||
* |i_CTRL-K| {@link com.maddyhome.idea.vim.action.change.insert.StartInsertDigraphAction}
|
||||
* |i_CTRL-K| {@link com.maddyhome.idea.vim.action.change.insert.InsertCompletedDigraphAction}
|
||||
* |i_CTRL-L| TO BE IMPLEMENTED
|
||||
* |i_<CR>| {@link com.maddyhome.idea.vim.action.change.insert.InsertEnterAction}
|
||||
* |i_CTRL-M| {@link com.maddyhome.idea.vim.action.change.insert.InsertEnterAction}
|
||||
@@ -54,8 +54,8 @@
|
||||
* |i_CTRL-R_CTRL-P| TO BE IMPLEMENTED
|
||||
* |i_CTRL-T| {@link com.maddyhome.idea.vim.action.change.shift.ShiftRightLinesAction}
|
||||
* |i_CTRL-U| {@link com.maddyhome.idea.vim.action.change.insert.InsertDeleteInsertedTextAction}
|
||||
* |i_CTRL-V| {@link com.maddyhome.idea.vim.action.change.insert.StartInsertLiteralAction}
|
||||
* |i_CTRL-V_digit| {@link com.maddyhome.idea.vim.action.change.insert.StartInsertLiteralAction}
|
||||
* |i_CTRL-V| {@link com.maddyhome.idea.vim.action.change.insert.InsertCompletedLiteralAction}
|
||||
* |i_CTRL-V_digit| {@link com.maddyhome.idea.vim.action.change.insert.InsertCompletedLiteralAction}
|
||||
* |i_CTRL-W| {@link com.maddyhome.idea.vim.action.change.insert.InsertDeletePreviousWordAction}
|
||||
* |i_CTRL-X| TO BE IMPLEMENTED
|
||||
* |i_CTRL-Y| {@link com.maddyhome.idea.vim.action.change.insert.InsertCharacterAboveCursorAction}
|
||||
@@ -130,7 +130,7 @@
|
||||
* |<Space>| {@link com.maddyhome.idea.vim.action.motion.leftright.MotionRightWrapAction}
|
||||
* |!| {@link com.maddyhome.idea.vim.action.change.change.FilterMotionAction}
|
||||
* |!!| translated to !_
|
||||
* |quote| {@link com.maddyhome.idea.vim.action.copy.SelectRegisterAction}
|
||||
* |quote| handled by command key parser
|
||||
* |#| {@link com.maddyhome.idea.vim.action.motion.search.SearchWholeWordBackwardAction}
|
||||
* |$| {@link com.maddyhome.idea.vim.action.motion.leftright.MotionLastColumnAction}
|
||||
* |%| {@link com.maddyhome.idea.vim.action.motion.updown.MotionPercentOrMatchAction}
|
||||
@@ -330,7 +330,7 @@
|
||||
* |[D| TO BE IMPLEMENTED
|
||||
* |[I| TO BE IMPLEMENTED
|
||||
* |[M| {@link com.maddyhome.idea.vim.action.motion.text.MotionMethodPreviousEndAction}
|
||||
* |[P| {@link com.maddyhome.idea.vim.action.copy.PutVisualTextNoIndentAction}
|
||||
* |[P| {@link com.maddyhome.idea.vim.action.copy.PutVisualTextBeforeCursorNoIndentAction}
|
||||
* |[P| {@link com.maddyhome.idea.vim.action.copy.PutTextBeforeCursorNoIndentAction}
|
||||
* |[[| {@link com.maddyhome.idea.vim.action.motion.text.MotionSectionBackwardStartAction}
|
||||
* |[]| {@link com.maddyhome.idea.vim.action.motion.text.MotionSectionBackwardEndAction}
|
||||
@@ -339,7 +339,7 @@
|
||||
* |[f| TO BE IMPLEMENTED
|
||||
* |[i| TO BE IMPLEMENTED
|
||||
* |[m| {@link com.maddyhome.idea.vim.action.motion.text.MotionMethodPreviousStartAction}
|
||||
* |[p| {@link com.maddyhome.idea.vim.action.copy.PutVisualTextNoIndentAction}
|
||||
* |[p| {@link com.maddyhome.idea.vim.action.copy.PutVisualTextAfterCursorNoIndentAction}
|
||||
* |[p| {@link com.maddyhome.idea.vim.action.copy.PutTextAfterCursorNoIndentAction}
|
||||
* |[s| TO BE IMPLEMENTED
|
||||
* |[z| TO BE IMPLEMENTED
|
||||
@@ -355,7 +355,7 @@
|
||||
* |]D| TO BE IMPLEMENTED
|
||||
* |]I| TO BE IMPLEMENTED
|
||||
* |]M| {@link com.maddyhome.idea.vim.action.motion.text.MotionMethodNextEndAction}
|
||||
* |]P| {@link com.maddyhome.idea.vim.action.copy.PutVisualTextNoIndentAction}
|
||||
* |]P| {@link com.maddyhome.idea.vim.action.copy.PutVisualTextBeforeCursorNoIndentAction}
|
||||
* |]P| {@link com.maddyhome.idea.vim.action.copy.PutTextBeforeCursorNoIndentAction}
|
||||
* |][| {@link com.maddyhome.idea.vim.action.motion.text.MotionSectionForwardStartAction}
|
||||
* |]]| {@link com.maddyhome.idea.vim.action.motion.text.MotionSectionForwardEndAction}
|
||||
@@ -364,7 +364,7 @@
|
||||
* |]f| TO BE IMPLEMENTED
|
||||
* |]i| TO BE IMPLEMENTED
|
||||
* |]m| {@link com.maddyhome.idea.vim.action.motion.text.MotionMethodNextStartAction}
|
||||
* |]p| {@link com.maddyhome.idea.vim.action.copy.PutVisualTextNoIndentAction}
|
||||
* |]p| {@link com.maddyhome.idea.vim.action.copy.PutVisualTextAfterCursorNoIndentAction}
|
||||
* |]p| {@link com.maddyhome.idea.vim.action.copy.PutTextAfterCursorNoIndentAction}
|
||||
* |]s| TO BE IMPLEMENTED
|
||||
* |]z| TO BE IMPLEMENTED
|
||||
@@ -404,6 +404,7 @@
|
||||
* |gJ| {@link com.maddyhome.idea.vim.action.change.delete.DeleteJoinLinesAction}
|
||||
* |gN| {@link com.maddyhome.idea.vim.action.motion.gn.VisualSelectPreviousSearch}
|
||||
* |gN| {@link com.maddyhome.idea.vim.action.motion.gn.GnPreviousTextObject}
|
||||
* |gP| {@link com.maddyhome.idea.vim.action.copy.PutVisualTextBeforeCursorMoveCursorAction}
|
||||
* |gP| {@link com.maddyhome.idea.vim.action.copy.PutTextBeforeCursorActionMoveCursor}
|
||||
* |gQ| TO BE IMPLEMENTED
|
||||
* |gR| TO BE IMPLEMENTED
|
||||
@@ -426,7 +427,7 @@
|
||||
* |gn| {@link com.maddyhome.idea.vim.action.motion.gn.GnNextTextObject}
|
||||
* |gm| {@link com.maddyhome.idea.vim.action.motion.leftright.MotionMiddleColumnAction}
|
||||
* |go| {@link com.maddyhome.idea.vim.action.motion.text.MotionNthCharacterAction}
|
||||
* |gp| {@link com.maddyhome.idea.vim.action.copy.PutVisualTextMoveCursorAction}
|
||||
* |gp| {@link com.maddyhome.idea.vim.action.copy.PutVisualTextAfterCursorMoveCursorAction}
|
||||
* |gp| {@link com.maddyhome.idea.vim.action.copy.PutTextAfterCursorActionMoveCursor}
|
||||
* |gq| {@link com.maddyhome.idea.vim.action.change.change.ReformatCodeMotionAction}
|
||||
* |gr| TO BE IMPLEMENTED
|
||||
@@ -522,7 +523,7 @@
|
||||
* |v_J| {@link com.maddyhome.idea.vim.action.change.delete.DeleteJoinVisualLinesSpacesAction}
|
||||
* |v_K| TO BE IMPLEMENTED
|
||||
* |v_O| {@link com.maddyhome.idea.vim.action.motion.visual.VisualSwapEndsBlockAction}
|
||||
* |v_P| {@link com.maddyhome.idea.vim.action.copy.PutVisualTextAction}
|
||||
* |v_P| {@link com.maddyhome.idea.vim.action.copy.PutVisualTextBeforeCursorAction}
|
||||
* |v_R| {@link com.maddyhome.idea.vim.action.change.change.ChangeVisualLinesAction}
|
||||
* |v_S| {@link com.maddyhome.idea.vim.action.change.change.ChangeVisualLinesAction}
|
||||
* |v_U| {@link com.maddyhome.idea.vim.action.change.change.ChangeCaseUpperVisualAction}
|
||||
@@ -574,7 +575,7 @@
|
||||
* |v_i{| {@link com.maddyhome.idea.vim.action.motion.object.MotionInnerBlockBraceAction}
|
||||
* |v_i}| {@link com.maddyhome.idea.vim.action.motion.object.MotionInnerBlockBraceAction}
|
||||
* |v_o| {@link com.maddyhome.idea.vim.action.motion.visual.VisualSwapEndsAction}
|
||||
* |v_p| {@link com.maddyhome.idea.vim.action.copy.PutVisualTextAction}
|
||||
* |v_p| {@link com.maddyhome.idea.vim.action.copy.PutVisualTextAfterCursorAction}
|
||||
* |v_r| {@link com.maddyhome.idea.vim.action.change.change.ChangeVisualCharacterAction}
|
||||
* |v_s| {@link com.maddyhome.idea.vim.action.change.change.ChangeVisualAction}
|
||||
* |v_u| {@link com.maddyhome.idea.vim.action.change.change.ChangeCaseLowerVisualAction}
|
||||
@@ -621,8 +622,8 @@
|
||||
* |c_CTRL-K| {@link com.maddyhome.idea.vim.ui.ExEditorKit.StartDigraphAction}
|
||||
* |c_CTRL-L| TO BE IMPLEMENTED
|
||||
* |c_CTRL-M| {@link com.maddyhome.idea.vim.action.ex.ProcessExEntryAction}
|
||||
* |c_CTRL-N| {@link com.maddyhome.idea.vim.ui.ExEditorKit.HistoryDownAction} [To Be Released]
|
||||
* |c_CTRL-P| {@link com.maddyhome.idea.vim.ui.ExEditorKit.HistoryUpAction} [To Be Released]
|
||||
* |c_CTRL-N| {@link com.maddyhome.idea.vim.ui.ExEditorKit.HistoryDownAction}
|
||||
* |c_CTRL-P| {@link com.maddyhome.idea.vim.ui.ExEditorKit.HistoryUpAction}
|
||||
* |c_CTRL-Q| {@link com.maddyhome.idea.vim.ui.ExEditorKit.StartDigraphAction}
|
||||
* |c_CTRL-R| {@link com.maddyhome.idea.vim.ui.ExEditorKit.InsertRegisterAction}
|
||||
* |c_CTRL-R_CTRL-A| TO BE IMPLEMENTED
|
||||
|
@@ -86,7 +86,7 @@ public class ExEditorKit extends DefaultEditorKit {
|
||||
if (cmd != null && cmd.length() > 0) {
|
||||
char ch = cmd.charAt(0);
|
||||
if (ch < ' ') {
|
||||
if ((mods & KeyEvent.CTRL_MASK) != 0) {
|
||||
if ((mods & ActionEvent.CTRL_MASK) != 0) {
|
||||
return KeyStroke.getKeyStroke(KeyEvent.VK_A + ch - 1, mods);
|
||||
}
|
||||
}
|
||||
@@ -246,7 +246,7 @@ public class ExEditorKit extends DefaultEditorKit {
|
||||
target.setCaretPosition(offset + text.length());
|
||||
}
|
||||
}
|
||||
} else if ((key.getModifiers() & KeyEvent.CTRL_MASK) != 0 && key.getKeyCode() == KeyEvent.VK_C) {
|
||||
} else if ((key.getModifiers() & KeyEvent.CTRL_DOWN_MASK) != 0 && key.getKeyCode() == KeyEvent.VK_C) {
|
||||
// Eat any unused keys, unless it's <C-C>, in which case forward on and cancel entry
|
||||
target.handleKey(key);
|
||||
}
|
||||
@@ -507,7 +507,7 @@ public class ExEditorKit extends DefaultEditorKit {
|
||||
target.clearCurrentAction();
|
||||
// Eat the character, unless it's <C-C>, in which case, forward on and cancel entry. Note that at some point
|
||||
// we should support input of control characters
|
||||
if ((key.getModifiers() & KeyEvent.CTRL_MASK) != 0 && key.getKeyCode() == KeyEvent.VK_C) {
|
||||
if ((key.getModifiers() & KeyEvent.CTRL_DOWN_MASK) != 0 && key.getKeyCode() == KeyEvent.VK_C) {
|
||||
target.handleKey(key);
|
||||
}
|
||||
break;
|
||||
|
@@ -122,15 +122,18 @@ public class ExEntryPanel extends JPanel implements LafManagerListener {
|
||||
* @param count A holder for the ex entry count
|
||||
*/
|
||||
public void activate(@NotNull Editor editor, DataContext context, @NotNull String label, String initText, int count) {
|
||||
logger.info("Activate ex entry panel");
|
||||
this.label.setText(label);
|
||||
this.label.setFont(UiHelper.selectFont(label));
|
||||
this.count = count;
|
||||
setFontForElements();
|
||||
entry.reset();
|
||||
entry.setEditor(editor, context);
|
||||
entry.setText(initText);
|
||||
entry.setFont(UiHelper.selectFont(initText));
|
||||
entry.setType(label);
|
||||
parent = editor.getContentComponent();
|
||||
|
||||
entry.getDocument().addDocumentListener(fontListener);
|
||||
if (isIncSearchEnabled()) {
|
||||
entry.getDocument().addDocumentListener(incSearchDocumentListener);
|
||||
caretOffset = editor.getCaretModel().getOffset();
|
||||
@@ -177,6 +180,7 @@ public class ExEntryPanel extends JPanel implements LafManagerListener {
|
||||
active = false;
|
||||
|
||||
try {
|
||||
entry.getDocument().removeDocumentListener(fontListener);
|
||||
// incsearch won't change in the lifetime of this activation
|
||||
if (isIncSearchEnabled()) {
|
||||
entry.getDocument().removeDocumentListener(incSearchDocumentListener);
|
||||
@@ -236,6 +240,17 @@ public class ExEntryPanel extends JPanel implements LafManagerListener {
|
||||
}
|
||||
}
|
||||
|
||||
private final @NotNull DocumentListener fontListener = new DocumentAdapter() {
|
||||
@Override
|
||||
protected void textChanged(@NotNull DocumentEvent e) {
|
||||
String text = entry.getActualText();
|
||||
Font newFont = UiHelper.selectFont(text);
|
||||
if (newFont != entry.getFont()) {
|
||||
entry.setFont(newFont);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
private final @NotNull DocumentListener incSearchDocumentListener = new DocumentAdapter() {
|
||||
@Override
|
||||
protected void textChanged(@NotNull DocumentEvent e) {
|
||||
@@ -246,6 +261,7 @@ public class ExEntryPanel extends JPanel implements LafManagerListener {
|
||||
char separator = label.getText().charAt(0);
|
||||
String searchText = entry.getActualText();
|
||||
if (label.getText().equals(":")) {
|
||||
if (searchText.isEmpty()) return;
|
||||
final ExCommand command = getIncsearchCommand(searchText);
|
||||
if (command == null) {
|
||||
return;
|
||||
@@ -389,9 +405,8 @@ public class ExEntryPanel extends JPanel implements LafManagerListener {
|
||||
}
|
||||
|
||||
private void setFontForElements() {
|
||||
final Font font = UiHelper.getEditorFont();
|
||||
label.setFont(font);
|
||||
entry.setFont(font);
|
||||
label.setFont(UiHelper.selectFont(label.getText()));
|
||||
entry.setFont(UiHelper.selectFont(entry.getActualText()));
|
||||
}
|
||||
|
||||
private void positionPanel() {
|
||||
|
@@ -31,60 +31,60 @@ object ExKeyBindings {
|
||||
arrayOf(
|
||||
// Escape will cancel a pending insert digraph/register before cancelling
|
||||
KeyBinding(KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0), ExEditorKit.EscapeChar),
|
||||
KeyBinding(KeyStroke.getKeyStroke(KeyEvent.VK_OPEN_BRACKET, KeyEvent.CTRL_MASK), ExEditorKit.EscapeChar),
|
||||
KeyBinding(KeyStroke.getKeyStroke(KeyEvent.VK_OPEN_BRACKET, KeyEvent.CTRL_DOWN_MASK), ExEditorKit.EscapeChar),
|
||||
|
||||
// Cancel entry, ignoring any pending actions such as digraph/registry entry
|
||||
KeyBinding(KeyStroke.getKeyStroke(KeyEvent.VK_C, KeyEvent.CTRL_MASK), ExEditorKit.CancelEntry),
|
||||
KeyBinding(KeyStroke.getKeyStroke(KeyEvent.VK_C, KeyEvent.CTRL_DOWN_MASK), ExEditorKit.CancelEntry),
|
||||
|
||||
KeyBinding(KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0), ExEditorKit.CompleteEntry),
|
||||
KeyBinding(KeyStroke.getKeyStroke(KeyEvent.VK_J, KeyEvent.CTRL_MASK), ExEditorKit.CompleteEntry),
|
||||
KeyBinding(KeyStroke.getKeyStroke(KeyEvent.VK_M, KeyEvent.CTRL_MASK), ExEditorKit.CompleteEntry),
|
||||
KeyBinding(KeyStroke.getKeyStroke(KeyEvent.VK_J, KeyEvent.CTRL_DOWN_MASK), ExEditorKit.CompleteEntry),
|
||||
KeyBinding(KeyStroke.getKeyStroke(KeyEvent.VK_M, KeyEvent.CTRL_DOWN_MASK), ExEditorKit.CompleteEntry),
|
||||
|
||||
KeyBinding(KeyStroke.getKeyStroke(KeyEvent.VK_B, KeyEvent.CTRL_MASK), ExEditorKit.beginLineAction),
|
||||
KeyBinding(KeyStroke.getKeyStroke(0x02.toChar().toInt(), KeyEvent.CTRL_MASK), ExEditorKit.beginLineAction),
|
||||
KeyBinding(KeyStroke.getKeyStroke(KeyEvent.VK_B, KeyEvent.CTRL_DOWN_MASK), ExEditorKit.beginLineAction),
|
||||
KeyBinding(KeyStroke.getKeyStroke(0x02.toChar().toInt(), KeyEvent.CTRL_DOWN_MASK), ExEditorKit.beginLineAction),
|
||||
KeyBinding(KeyStroke.getKeyStroke(KeyEvent.VK_HOME, 0), ExEditorKit.beginLineAction),
|
||||
|
||||
KeyBinding(KeyStroke.getKeyStroke(KeyEvent.VK_E, KeyEvent.CTRL_MASK), ExEditorKit.endLineAction),
|
||||
KeyBinding(KeyStroke.getKeyStroke(0x05.toChar().toInt(), KeyEvent.CTRL_MASK), ExEditorKit.endLineAction),
|
||||
KeyBinding(KeyStroke.getKeyStroke(KeyEvent.VK_E, KeyEvent.CTRL_DOWN_MASK), ExEditorKit.endLineAction),
|
||||
KeyBinding(KeyStroke.getKeyStroke(0x05.toChar().toInt(), KeyEvent.CTRL_DOWN_MASK), ExEditorKit.endLineAction),
|
||||
KeyBinding(KeyStroke.getKeyStroke(KeyEvent.VK_END, 0), ExEditorKit.endLineAction),
|
||||
|
||||
KeyBinding(KeyStroke.getKeyStroke(KeyEvent.VK_BACK_SPACE, 0), ExEditorKit.deletePrevCharAction),
|
||||
KeyBinding(KeyStroke.getKeyStroke(KeyEvent.VK_H, KeyEvent.CTRL_MASK), ExEditorKit.deletePrevCharAction),
|
||||
KeyBinding(KeyStroke.getKeyStroke(0x08.toChar().toInt(), KeyEvent.CTRL_MASK), ExEditorKit.deletePrevCharAction),
|
||||
KeyBinding(KeyStroke.getKeyStroke(KeyEvent.VK_H, KeyEvent.CTRL_DOWN_MASK), ExEditorKit.deletePrevCharAction),
|
||||
KeyBinding(KeyStroke.getKeyStroke(0x08.toChar().toInt(), KeyEvent.CTRL_DOWN_MASK), ExEditorKit.deletePrevCharAction),
|
||||
|
||||
KeyBinding(KeyStroke.getKeyStroke(KeyEvent.VK_DELETE, 0), ExEditorKit.deleteNextCharAction),
|
||||
|
||||
KeyBinding(KeyStroke.getKeyStroke(KeyEvent.VK_W, KeyEvent.CTRL_MASK), ExEditorKit.deletePrevWordAction),
|
||||
KeyBinding(KeyStroke.getKeyStroke(KeyEvent.VK_W, KeyEvent.CTRL_DOWN_MASK), ExEditorKit.deletePrevWordAction),
|
||||
|
||||
KeyBinding(KeyStroke.getKeyStroke(KeyEvent.VK_U, KeyEvent.CTRL_MASK), ExEditorKit.DeleteToCursor),
|
||||
KeyBinding(KeyStroke.getKeyStroke(KeyEvent.VK_U, KeyEvent.CTRL_DOWN_MASK), ExEditorKit.DeleteToCursor),
|
||||
|
||||
KeyBinding(KeyStroke.getKeyStroke(KeyEvent.VK_UP, 0), ExEditorKit.HistoryUpFilter),
|
||||
KeyBinding(KeyStroke.getKeyStroke(KeyEvent.VK_UP, KeyEvent.SHIFT_MASK), ExEditorKit.HistoryUp),
|
||||
KeyBinding(KeyStroke.getKeyStroke(KeyEvent.VK_UP, KeyEvent.SHIFT_DOWN_MASK), ExEditorKit.HistoryUp),
|
||||
KeyBinding(KeyStroke.getKeyStroke(KeyEvent.VK_PAGE_UP, 0), ExEditorKit.HistoryUp),
|
||||
KeyBinding(KeyStroke.getKeyStroke(KeyEvent.VK_P, KeyEvent.CTRL_MASK), ExEditorKit.HistoryUp),
|
||||
KeyBinding(KeyStroke.getKeyStroke(KeyEvent.VK_P, KeyEvent.CTRL_DOWN_MASK), ExEditorKit.HistoryUp),
|
||||
KeyBinding(KeyStroke.getKeyStroke(KeyEvent.VK_DOWN, 0), ExEditorKit.HistoryDownFilter),
|
||||
KeyBinding(KeyStroke.getKeyStroke(KeyEvent.VK_DOWN, KeyEvent.SHIFT_MASK), ExEditorKit.HistoryDown),
|
||||
KeyBinding(KeyStroke.getKeyStroke(KeyEvent.VK_DOWN, KeyEvent.SHIFT_DOWN_MASK), ExEditorKit.HistoryDown),
|
||||
KeyBinding(KeyStroke.getKeyStroke(KeyEvent.VK_PAGE_DOWN, 0), ExEditorKit.HistoryDown),
|
||||
KeyBinding(KeyStroke.getKeyStroke(KeyEvent.VK_N, KeyEvent.CTRL_MASK), ExEditorKit.HistoryDown),
|
||||
KeyBinding(KeyStroke.getKeyStroke(KeyEvent.VK_N, KeyEvent.CTRL_DOWN_MASK), ExEditorKit.HistoryDown),
|
||||
|
||||
KeyBinding(KeyStroke.getKeyStroke(KeyEvent.VK_INSERT, 0), ExEditorKit.ToggleInsertReplace),
|
||||
|
||||
KeyBinding(KeyStroke.getKeyStroke(KeyEvent.VK_LEFT, 0), ExEditorKit.backwardAction),
|
||||
KeyBinding(KeyStroke.getKeyStroke(KeyEvent.VK_LEFT, KeyEvent.SHIFT_MASK), ExEditorKit.previousWordAction),
|
||||
KeyBinding(KeyStroke.getKeyStroke(KeyEvent.VK_LEFT, KeyEvent.CTRL_MASK), ExEditorKit.previousWordAction),
|
||||
KeyBinding(KeyStroke.getKeyStroke(KeyEvent.VK_LEFT, KeyEvent.SHIFT_DOWN_MASK), ExEditorKit.previousWordAction),
|
||||
KeyBinding(KeyStroke.getKeyStroke(KeyEvent.VK_LEFT, KeyEvent.CTRL_DOWN_MASK), ExEditorKit.previousWordAction),
|
||||
KeyBinding(KeyStroke.getKeyStroke(KeyEvent.VK_RIGHT, 0), ExEditorKit.forwardAction),
|
||||
KeyBinding(KeyStroke.getKeyStroke(KeyEvent.VK_RIGHT, KeyEvent.SHIFT_MASK), ExEditorKit.nextWordAction),
|
||||
KeyBinding(KeyStroke.getKeyStroke(KeyEvent.VK_RIGHT, KeyEvent.CTRL_MASK), ExEditorKit.nextWordAction),
|
||||
KeyBinding(KeyStroke.getKeyStroke(KeyEvent.VK_RIGHT, KeyEvent.SHIFT_DOWN_MASK), ExEditorKit.nextWordAction),
|
||||
KeyBinding(KeyStroke.getKeyStroke(KeyEvent.VK_RIGHT, KeyEvent.CTRL_DOWN_MASK), ExEditorKit.nextWordAction),
|
||||
|
||||
KeyBinding(KeyStroke.getKeyStroke(KeyEvent.VK_K, KeyEvent.CTRL_MASK), ExEditorKit.StartDigraph),
|
||||
KeyBinding(KeyStroke.getKeyStroke(KeyEvent.VK_V, KeyEvent.CTRL_MASK), ExEditorKit.StartLiteral),
|
||||
KeyBinding(KeyStroke.getKeyStroke(KeyEvent.VK_Q, KeyEvent.CTRL_MASK), ExEditorKit.StartLiteral),
|
||||
KeyBinding(KeyStroke.getKeyStroke(KeyEvent.VK_K, KeyEvent.CTRL_DOWN_MASK), ExEditorKit.StartDigraph),
|
||||
KeyBinding(KeyStroke.getKeyStroke(KeyEvent.VK_V, KeyEvent.CTRL_DOWN_MASK), ExEditorKit.StartLiteral),
|
||||
KeyBinding(KeyStroke.getKeyStroke(KeyEvent.VK_Q, KeyEvent.CTRL_DOWN_MASK), ExEditorKit.StartLiteral),
|
||||
|
||||
KeyBinding(KeyStroke.getKeyStroke(KeyEvent.VK_R, KeyEvent.CTRL_MASK), ExEditorKit.InsertRegister),
|
||||
KeyBinding(KeyStroke.getKeyStroke(KeyEvent.VK_R, KeyEvent.CTRL_DOWN_MASK), ExEditorKit.InsertRegister),
|
||||
|
||||
// These appear to be non-Vim shortcuts
|
||||
KeyBinding(KeyStroke.getKeyStroke(KeyEvent.VK_V, KeyEvent.META_MASK), ExEditorKit.pasteAction),
|
||||
KeyBinding(KeyStroke.getKeyStroke(KeyEvent.VK_INSERT, KeyEvent.SHIFT_MASK), ExEditorKit.pasteAction)
|
||||
KeyBinding(KeyStroke.getKeyStroke(KeyEvent.VK_V, KeyEvent.META_DOWN_MASK), ExEditorKit.pasteAction),
|
||||
KeyBinding(KeyStroke.getKeyStroke(KeyEvent.VK_INSERT, KeyEvent.SHIFT_DOWN_MASK), ExEditorKit.pasteAction)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@@ -18,6 +18,7 @@
|
||||
|
||||
package com.maddyhome.idea.vim.ui;
|
||||
|
||||
import com.intellij.ide.IdeTooltip;
|
||||
import com.intellij.ide.ui.LafManager;
|
||||
import com.intellij.ide.ui.LafManagerListener;
|
||||
import com.intellij.openapi.application.ApplicationManager;
|
||||
@@ -133,6 +134,7 @@ public class ExOutputPanel extends JPanel implements LafManagerListener {
|
||||
}
|
||||
|
||||
myText.setText(data);
|
||||
myText.setFont(UiHelper.selectFont(data));
|
||||
myText.setCaretPosition(0);
|
||||
if (data.length() > 0) {
|
||||
activate();
|
||||
@@ -197,9 +199,8 @@ public class ExOutputPanel extends JPanel implements LafManagerListener {
|
||||
}
|
||||
|
||||
private void setFontForElements() {
|
||||
final Font font = UiHelper.getEditorFont();
|
||||
myText.setFont(font);
|
||||
myLabel.setFont(font);
|
||||
myText.setFont(UiHelper.selectFont(myText.getText()));
|
||||
myLabel.setFont(UiHelper.selectFont(myLabel.getText()));
|
||||
}
|
||||
|
||||
private static int countLines(@NotNull String text) {
|
||||
@@ -245,6 +246,7 @@ public class ExOutputPanel extends JPanel implements LafManagerListener {
|
||||
|
||||
private void badKey() {
|
||||
myLabel.setText("-- MORE -- (RET: line, SPACE: page, d: half page, q: quit)");
|
||||
myLabel.setFont(UiHelper.selectFont(myLabel.getText()));
|
||||
}
|
||||
|
||||
private void scrollOffset(int more) {
|
||||
@@ -256,9 +258,11 @@ public class ExOutputPanel extends JPanel implements LafManagerListener {
|
||||
myScrollPane.getVerticalScrollBar().getMaximum() - myScrollPane.getVerticalScrollBar().getVisibleAmount()) {
|
||||
myAtEnd = true;
|
||||
myLabel.setText("Hit ENTER or type command to continue");
|
||||
myLabel.setFont(UiHelper.selectFont(myLabel.getText()));
|
||||
}
|
||||
else {
|
||||
myLabel.setText("-- MORE --");
|
||||
myLabel.setFont(UiHelper.selectFont(myLabel.getText()));
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -26,6 +26,7 @@ import com.intellij.openapi.util.Disposer;
|
||||
import com.intellij.util.ui.JBUI;
|
||||
import com.maddyhome.idea.vim.VimPlugin;
|
||||
import com.maddyhome.idea.vim.group.HistoryGroup;
|
||||
import com.maddyhome.idea.vim.helper.UiHelper;
|
||||
import kotlin.text.StringsKt;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
@@ -188,8 +189,15 @@ public class ExTextField extends JTextField {
|
||||
}
|
||||
}
|
||||
|
||||
// fix https://youtrack.jetbrains.com/issue/VIM-570
|
||||
private void resetFont(String string) {
|
||||
super.setFont(UiHelper.selectFont(string));
|
||||
}
|
||||
|
||||
private void updateText(String string) {
|
||||
super.setText(string);
|
||||
|
||||
resetFont(string);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -197,6 +205,7 @@ public class ExTextField extends JTextField {
|
||||
super.setText(string);
|
||||
|
||||
saveLastEntry();
|
||||
resetFont(string);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -245,7 +254,7 @@ public class ExTextField extends JTextField {
|
||||
char c = keyChar;
|
||||
final int modifiers = stroke.getModifiers();
|
||||
final int keyCode = stroke.getKeyCode();
|
||||
if ((modifiers & KeyEvent.CTRL_MASK) != 0) {
|
||||
if ((modifiers & KeyEvent.CTRL_DOWN_MASK) != 0) {
|
||||
final int codePoint = keyCode - KeyEvent.VK_A + 1;
|
||||
if (codePoint > 0) {
|
||||
c = Character.toChars(codePoint)[0];
|
||||
|
@@ -1,95 +0,0 @@
|
||||
/*
|
||||
* IdeaVim - Vim emulator for IDEs based on the IntelliJ platform
|
||||
* Copyright (C) 2003-2020 The IdeaVim authors
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.maddyhome.idea.vim.ui;
|
||||
|
||||
import com.intellij.openapi.editor.Editor;
|
||||
import com.maddyhome.idea.vim.helper.UiHelper;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.awt.*;
|
||||
import java.awt.event.KeyListener;
|
||||
|
||||
/**
|
||||
* @author vlan
|
||||
*/
|
||||
public class ModalEntryDialog extends JDialog {
|
||||
private final @NotNull JTextField myEntry;
|
||||
private final @NotNull JLabel myLabel;
|
||||
private final @NotNull JComponent myParent;
|
||||
|
||||
public ModalEntryDialog(@NotNull Editor editor, @NotNull String prompt) {
|
||||
super((Frame)null, true);
|
||||
myParent = editor.getContentComponent();
|
||||
myLabel = new JLabel(prompt);
|
||||
myEntry = new JTextField();
|
||||
|
||||
myEntry.setBorder(null);
|
||||
|
||||
final Font font = UiHelper.getEditorFont();
|
||||
myLabel.setFont(font);
|
||||
myEntry.setFont(font);
|
||||
|
||||
setForeground(myEntry.getForeground());
|
||||
setBackground(myEntry.getBackground());
|
||||
|
||||
myLabel.setForeground(myEntry.getForeground());
|
||||
myLabel.setBackground(myEntry.getBackground());
|
||||
|
||||
GridBagLayout layout = new GridBagLayout();
|
||||
GridBagConstraints gbc = new GridBagConstraints();
|
||||
|
||||
setLayout(layout);
|
||||
gbc.gridx = 0;
|
||||
layout.setConstraints(myLabel, gbc);
|
||||
add(myLabel);
|
||||
gbc.gridx = 1;
|
||||
gbc.weightx = 1;
|
||||
gbc.fill = GridBagConstraints.HORIZONTAL;
|
||||
layout.setConstraints(myEntry, gbc);
|
||||
add(myEntry);
|
||||
|
||||
setUndecorated(true);
|
||||
|
||||
final Container scroll = SwingUtilities.getAncestorOfClass(JScrollPane.class, myParent);
|
||||
final int height = (int)getPreferredSize().getHeight();
|
||||
if (scroll != null) {
|
||||
final Rectangle bounds = scroll.getBounds();
|
||||
bounds.translate(0, scroll.getHeight() - height);
|
||||
bounds.height = height;
|
||||
final JRootPane rootPane = SwingUtilities.getRootPane(myParent);
|
||||
final Point pos = SwingUtilities.convertPoint(scroll.getParent(), bounds.getLocation(), rootPane);
|
||||
final Window window = SwingUtilities.getWindowAncestor(myParent);
|
||||
final Point windowPos = window.getLocation();
|
||||
pos.translate(windowPos.x, windowPos.y);
|
||||
final Insets windowInsets = window.getInsets();
|
||||
pos.translate(windowInsets.left, windowInsets.top);
|
||||
bounds.setLocation(pos);
|
||||
setBounds(bounds);
|
||||
}
|
||||
}
|
||||
|
||||
public void setEntryKeyListener(@NotNull KeyListener listener) {
|
||||
myEntry.addKeyListener(listener);
|
||||
}
|
||||
|
||||
public @NotNull String getText() {
|
||||
return myEntry.getText();
|
||||
}
|
||||
}
|
99
src/com/maddyhome/idea/vim/ui/ShowCmd.kt
Normal file
99
src/com/maddyhome/idea/vim/ui/ShowCmd.kt
Normal file
@@ -0,0 +1,99 @@
|
||||
package com.maddyhome.idea.vim.ui
|
||||
|
||||
import com.intellij.openapi.editor.Editor
|
||||
import com.intellij.openapi.fileEditor.FileEditorManagerEvent
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.openapi.project.ProjectManager
|
||||
import com.intellij.openapi.wm.StatusBar
|
||||
import com.intellij.openapi.wm.StatusBarWidget
|
||||
import com.intellij.openapi.wm.StatusBarWidgetProvider
|
||||
import com.intellij.openapi.wm.WindowManager
|
||||
import com.intellij.openapi.wm.impl.status.EditorBasedWidget
|
||||
import com.intellij.util.Consumer
|
||||
import com.maddyhome.idea.vim.helper.StringHelper
|
||||
import com.maddyhome.idea.vim.helper.vimCommandState
|
||||
import com.maddyhome.idea.vim.option.OptionChangeListener
|
||||
import com.maddyhome.idea.vim.option.OptionsManager
|
||||
import java.awt.Component
|
||||
import java.awt.event.MouseEvent
|
||||
|
||||
object ShowCmd {
|
||||
// https://github.com/vim/vim/blob/b376ace1aeaa7614debc725487d75c8f756dd773/src/vim.h#L1721
|
||||
private const val SHOWCMD_COLS = 10
|
||||
|
||||
fun update() {
|
||||
val windowManager = WindowManager.getInstance()
|
||||
ProjectManager.getInstance().openProjects.forEach {
|
||||
val statusBar = windowManager.getStatusBar(it)
|
||||
statusBar.updateWidget(ShowCmdStatusBarWidget.ID)
|
||||
}
|
||||
}
|
||||
|
||||
fun getWidgetText(editor: Editor?): String {
|
||||
// Vim only shows the last 10 characters. See normal.c:add_to_showcmd
|
||||
// https://github.com/vim/vim/blob/b376ace1aeaa7614debc725487d75c8f756dd773/src/normal.c#L1885-L1890
|
||||
return getFullText(editor).takeLast(SHOWCMD_COLS)
|
||||
}
|
||||
|
||||
fun getFullText(editor: Editor?): String {
|
||||
if (!OptionsManager.showcmd.isSet || editor == null || editor.isDisposed) return ""
|
||||
|
||||
val editorState = editor.vimCommandState ?: return ""
|
||||
return StringHelper.toPrintableCharacters(editorState.commandBuilder.keys + editorState.mappingState.keys)
|
||||
}
|
||||
}
|
||||
|
||||
object ShowCmdOptionChangeListener: OptionChangeListener<Boolean> {
|
||||
override fun valueChange(oldValue: Boolean?, newValue: Boolean?) {
|
||||
ShowCmd.update()
|
||||
}
|
||||
}
|
||||
|
||||
class ShowCmdStatusBarWidget: StatusBarWidgetProvider {
|
||||
companion object {
|
||||
const val ID = "IdeaVim::ShowCmd"
|
||||
}
|
||||
|
||||
override fun getWidget(project: Project): StatusBarWidget = Widget(project)
|
||||
override fun getAnchor(): String = StatusBar.Anchors.before(StatusBar.StandardWidgets.POSITION_PANEL)
|
||||
|
||||
// `:help 'showcmd'`
|
||||
// Widget shows:
|
||||
// * Partial command, as it's being typed
|
||||
// * When selecting characters within a line, the number of characters
|
||||
// * Tabs are shown as one char
|
||||
// * If the number of bytes is different, this is also shown: "2-6"
|
||||
// * When selecting more than one line, the number of lines
|
||||
// * When selecting a block, the size in screen characters: {lines}x{columns}
|
||||
//
|
||||
// We only need to show partial commands, since the standard PositionPanel shows the other information already, with
|
||||
// the exception of "{lines}x{columns}" (it shows "x carets" instead)
|
||||
class Widget(project: Project)
|
||||
: EditorBasedWidget(project), StatusBarWidget.Multiframe, StatusBarWidget.TextPresentation {
|
||||
|
||||
override fun ID() = ID
|
||||
|
||||
// [VERSION UPDATE] After 193 use `getPresentation()`
|
||||
@Suppress("UnstableApiUsage", "DEPRECATION")
|
||||
override fun getPresentation(type: StatusBarWidget.PlatformType): StatusBarWidget.WidgetPresentation? = this
|
||||
|
||||
override fun getClickConsumer(): Consumer<MouseEvent>? = null
|
||||
|
||||
override fun getTooltipText(): String {
|
||||
var count = ShowCmd.getFullText(this.editor)
|
||||
if (!count.isBlank()) count = ": $count"
|
||||
return "IdeaVim showcmd$count"
|
||||
}
|
||||
|
||||
override fun getText(): String = ShowCmd.getWidgetText(editor)
|
||||
override fun getAlignment() = Component.CENTER_ALIGNMENT
|
||||
|
||||
// Multiframe#copy to show the widget on popped out editors
|
||||
override fun copy(): StatusBarWidget = Widget(myProject)
|
||||
|
||||
override fun selectionChanged(event: FileEditorManagerEvent) {
|
||||
// Update when changing selected editor
|
||||
myStatusBar?.updateWidget(ID)
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,6 +1,6 @@
|
||||
# Manual Tests
|
||||
|
||||
## #1 [Last run: 20-11-2019]
|
||||
## #1 [Last run: 2020-04-09]
|
||||
|
||||
_Initial mode:_ NORMAL
|
||||
|
||||
@@ -14,7 +14,7 @@ Word is selected, block-caret is placed on the word end (offset = `word end - 1`
|
||||

|
||||
|
||||
|
||||
## #2 [Last run: 04-02-2020]
|
||||
## #2 [Last run: 2020-04-09]
|
||||
|
||||
_Initial mode:_ NORMAL
|
||||
|
||||
@@ -26,7 +26,7 @@ Last word is selected, block caret is placed on the word end without bouncing
|
||||
|
||||

|
||||
|
||||
## #3 [Last run: 04-02-2020]
|
||||
## #3 [Last run: 2020-04-09]
|
||||
|
||||
_Initial mode:_ NORMAL
|
||||
|
||||
@@ -38,7 +38,7 @@ Line is selected. Caret is placed on the line end
|
||||
|
||||

|
||||
|
||||
## #4 [Last run: 04-02-2020]
|
||||
## #4 [Last run: 2020-04-09]
|
||||
|
||||
_Initial mode:_ NORMAL
|
||||
|
||||
@@ -51,7 +51,7 @@ After mouse release, caret moves one character back and becomes block shape
|
||||
|
||||

|
||||
|
||||
## #5 [Last run: 04-02-2020]
|
||||
## #5 [Last run: 2020-04-09]
|
||||
|
||||
_Initial mode:_ NORMAL
|
||||
|
||||
@@ -65,7 +65,7 @@ After mouse release, caret moves one character back and becomes block shape
|
||||
|
||||

|
||||
|
||||
## #6 [Last run: 04-02-2020]
|
||||
## #6 [Last run: 2020-04-09]
|
||||
|
||||
_Initial mode:_ NORMAL
|
||||
|
||||
@@ -77,7 +77,7 @@ Line is selected, caret is on the first position
|
||||
|
||||

|
||||
|
||||
## #6 [Last run: 04-02-2020]
|
||||
## #6 [Last run: 2020-04-09]
|
||||
|
||||
_Initial mode:_ NORMAL
|
||||
|
||||
@@ -94,7 +94,7 @@ Caret stays in _block_ shape with a normal mode
|
||||

|
||||
|
||||
|
||||
## #7 [Last run: 04-02-2020]
|
||||
## #7 [Last run: 2020-04-09]
|
||||
|
||||
_Action:_
|
||||
Turn emulation off and on
|
||||
@@ -102,7 +102,7 @@ Turn emulation off and on
|
||||
_Result:_
|
||||
Vim emulator works as expected
|
||||
|
||||
## #8 [Last run: 04-02-2020]
|
||||
## #8 [Last run: 2020-04-09
|
||||
|
||||
_Action:_
|
||||
Start up IJ with disabled emulator, turn it on
|
||||
@@ -110,7 +110,7 @@ Start up IJ with disabled emulator, turn it on
|
||||
_Result:_
|
||||
Vim emulator works as expected
|
||||
|
||||
## #9 [Last run: 04-02-2020]
|
||||
## #9 [Last run: 2020-04-09]
|
||||
|
||||
_Action:_
|
||||
Wrap with if
|
||||
|
@@ -249,6 +249,10 @@ public abstract class VimTestCase extends UsefulTestCase {
|
||||
assertEquals(isError, VimPlugin.isError());
|
||||
}
|
||||
|
||||
public void assertPluginErrorMessageContains(@NotNull String message) {
|
||||
assertTrue(VimPlugin.getMessage().contains(message));
|
||||
}
|
||||
|
||||
protected void assertCaretsColour() {
|
||||
Color selectionColour = myFixture.getEditor().getColorsScheme().getColor(EditorColors.SELECTION_BACKGROUND_COLOR);
|
||||
Color caretColour = myFixture.getEditor().getColorsScheme().getColor(EditorColors.CARET_COLOR);
|
||||
|
@@ -21,4 +21,23 @@ class CommandCountTest : VimTestCase() {
|
||||
typeText(parseKeys("2d3l"))
|
||||
myFixture.checkResult("7890")
|
||||
}
|
||||
|
||||
// See https://github.com/vim/vim/blob/b376ace1aeaa7614debc725487d75c8f756dd773/src/normal.c#L631
|
||||
fun `test count resets to 999999999L if gets too large`() {
|
||||
configureByText("1")
|
||||
typeText(parseKeys("12345678901234567890<C-A>"))
|
||||
myFixture.checkResult("1000000000")
|
||||
}
|
||||
|
||||
fun `test count select register count operator count motion`() {
|
||||
configureByText("${c}123456789012345678901234567890")
|
||||
typeText(parseKeys("2\"a3d4l")) // Delete 24 characters
|
||||
myFixture.checkResult("567890")
|
||||
}
|
||||
|
||||
fun `test multiple select register counts`() {
|
||||
configureByText("${c}12345678901234567890123456789012345678901234567890")
|
||||
typeText(parseKeys("2\"a2\"b2\"b2d2l")) // Delete 32 characters
|
||||
myFixture.checkResult("345678901234567890")
|
||||
}
|
||||
}
|
||||
|
@@ -79,6 +79,25 @@ public class CopyActionTest extends VimTestCase {
|
||||
myFixture.checkResult("hellolo world\n");
|
||||
}
|
||||
|
||||
// |register| |y| |quote|
|
||||
public void testYankRegisterUsesLastEnteredRegister() {
|
||||
typeTextInFile(parseKeys("\"a\"byl", "\"ap"),
|
||||
"hel<caret>lo world\n");
|
||||
myFixture.checkResult("helllo world\n");
|
||||
}
|
||||
|
||||
public void testYankAppendRegister() {
|
||||
typeTextInFile(parseKeys("\"Ayl", "l", "\"Ayl", "\"Ap"),
|
||||
"hel<caret>lo world\n");
|
||||
myFixture.checkResult("hellolo world\n");
|
||||
}
|
||||
|
||||
public void testYankWithInvalidRegister() {
|
||||
typeTextInFile(parseKeys("\"&"),
|
||||
"hel<caret>lo world\n");
|
||||
assertPluginError(true);
|
||||
}
|
||||
|
||||
// |P|
|
||||
public void testYankPutBefore() {
|
||||
typeTextInFile(parseKeys("y2l", "P"),
|
||||
|
@@ -125,4 +125,8 @@ class ChangeMotionActionTest : VimTestCase() {
|
||||
fun testChangeLastCharInLine() {
|
||||
doTest(parseKeys("cw"), "fo${c}o\n", "fo${c}\n", CommandState.Mode.INSERT, CommandState.SubMode.NONE)
|
||||
}
|
||||
|
||||
fun testLastSymbolInWord() {
|
||||
doTest(parseKeys("cw"), "fo${c}o", "fo${c}", CommandState.Mode.INSERT, CommandState.SubMode.NONE)
|
||||
}
|
||||
}
|
||||
|
@@ -22,11 +22,16 @@ package org.jetbrains.plugins.ideavim.action.change.delete
|
||||
|
||||
import com.maddyhome.idea.vim.VimPlugin
|
||||
import com.maddyhome.idea.vim.helper.StringHelper.parseKeys
|
||||
import com.maddyhome.idea.vim.helper.VimBehaviorDiffers
|
||||
import org.jetbrains.plugins.ideavim.VimTestCase
|
||||
|
||||
class DeleteMotionActionTest : VimTestCase() {
|
||||
|
||||
fun `ignoreTest delete last line`() {
|
||||
@VimBehaviorDiffers(originalVimAfter = """
|
||||
def xxx():
|
||||
${c}expression one
|
||||
""")
|
||||
fun `test delete last line`() {
|
||||
typeTextInFile(parseKeys("dd"),
|
||||
"""
|
||||
def xxx():
|
||||
@@ -35,11 +40,13 @@ class DeleteMotionActionTest : VimTestCase() {
|
||||
""".trimIndent())
|
||||
myFixture.checkResult("""
|
||||
def xxx():
|
||||
${c}expression one
|
||||
expression on${c}e
|
||||
|
||||
""".trimIndent())
|
||||
}
|
||||
|
||||
fun `ignoreTest delete last line stored with new line`() {
|
||||
@VimBehaviorDiffers(originalVimAfter = " expression two\n")
|
||||
fun `test delete last line stored with new line`() {
|
||||
typeTextInFile(parseKeys("dd"),
|
||||
"""
|
||||
def xxx():
|
||||
@@ -47,10 +54,10 @@ class DeleteMotionActionTest : VimTestCase() {
|
||||
expression${c} two
|
||||
""".trimIndent())
|
||||
val savedText = VimPlugin.getRegister().lastRegister?.text ?: ""
|
||||
assertEquals(" expression two\n", savedText)
|
||||
assertEquals(" expression two", savedText)
|
||||
}
|
||||
|
||||
fun `ignoreTest delete line action multicaret`() {
|
||||
fun `test delete line action multicaret`() {
|
||||
typeTextInFile(parseKeys("d3d"),
|
||||
"""
|
||||
abc${c}de
|
||||
@@ -62,7 +69,7 @@ class DeleteMotionActionTest : VimTestCase() {
|
||||
abcde
|
||||
|
||||
""".trimIndent())
|
||||
myFixture.checkResult("${c}abcde\n")
|
||||
myFixture.checkResult("${c}abcd${c}e\n")
|
||||
}
|
||||
|
||||
fun `test delete motion action multicaret`() {
|
||||
|
@@ -0,0 +1,102 @@
|
||||
/*
|
||||
* IdeaVim - Vim emulator for IDEs based on the IntelliJ platform
|
||||
* Copyright (C) 2003-2020 The IdeaVim authors
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.jetbrains.plugins.ideavim.action.change.delete
|
||||
|
||||
import com.maddyhome.idea.vim.command.CommandState
|
||||
import com.maddyhome.idea.vim.helper.StringHelper
|
||||
import com.maddyhome.idea.vim.helper.VimBehaviorDiffers
|
||||
import org.jetbrains.plugins.ideavim.VimTestCase
|
||||
|
||||
class DeleteVisualLinesActionTest : VimTestCase() {
|
||||
fun `test remove line in char visual mode`() {
|
||||
doTest(StringHelper.parseKeys("vlllX"),
|
||||
"""
|
||||
I found ${c}it in a legendary land
|
||||
all rocks and lavender and tufted grass,
|
||||
where it was settled on some sodden sand
|
||||
hard by the torrent of a mountain pass.
|
||||
""".trimIndent(),
|
||||
"""
|
||||
${c}all rocks and lavender and tufted grass,
|
||||
where it was settled on some sodden sand
|
||||
hard by the torrent of a mountain pass.
|
||||
""".trimIndent(),
|
||||
CommandState.Mode.COMMAND,
|
||||
CommandState.SubMode.NONE)
|
||||
}
|
||||
|
||||
@VimBehaviorDiffers(originalVimAfter = """
|
||||
I found it in a legendary land
|
||||
all rocks and lavender and tufted grass,
|
||||
${c}where it was settled on some sodden sand""")
|
||||
fun `test remove line in char visual mode last line`() {
|
||||
doTest(StringHelper.parseKeys("vlllX"),
|
||||
"""
|
||||
I found it in a legendary land
|
||||
all rocks and lavender and tufted grass,
|
||||
where it was settled on some sodden sand
|
||||
hard by ${c}the torrent of a mountain pass.""".trimIndent(),
|
||||
"""
|
||||
I found it in a legendary land
|
||||
all rocks and lavender and tufted grass,
|
||||
where it was settled on some sodden san${c}d
|
||||
|
||||
""".trimIndent(),
|
||||
CommandState.Mode.COMMAND,
|
||||
CommandState.SubMode.NONE)
|
||||
}
|
||||
|
||||
fun `test remove line in line visual mode`() {
|
||||
doTest(StringHelper.parseKeys("VX"),
|
||||
"""
|
||||
I found ${c}it in a legendary land
|
||||
all rocks and lavender and tufted grass,
|
||||
where it was settled on some sodden sand
|
||||
hard by the torrent of a mountain pass.
|
||||
""".trimIndent(),
|
||||
"""
|
||||
${c}all rocks and lavender and tufted grass,
|
||||
where it was settled on some sodden sand
|
||||
hard by the torrent of a mountain pass.
|
||||
""".trimIndent(),
|
||||
CommandState.Mode.COMMAND,
|
||||
CommandState.SubMode.NONE)
|
||||
}
|
||||
|
||||
@VimBehaviorDiffers(originalVimAfter = """
|
||||
I found it in a legendary land
|
||||
all rocks and lavender and tufted grass,
|
||||
${c}where it was settled on some sodden sand""")
|
||||
fun `test remove line in line visual mode line end`() {
|
||||
doTest(StringHelper.parseKeys("VX"),
|
||||
"""
|
||||
I found it in a legendary land
|
||||
all rocks and lavender and tufted grass,
|
||||
where it was settled on some sodden sand
|
||||
hard by ${c}the torrent of a mountain pass.""".trimIndent(),
|
||||
"""
|
||||
I found it in a legendary land
|
||||
all rocks and lavender and tufted grass,
|
||||
where it was settled on some sodden san${c}d
|
||||
|
||||
""".trimIndent(),
|
||||
CommandState.Mode.COMMAND,
|
||||
CommandState.SubMode.NONE)
|
||||
}
|
||||
}
|
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
* IdeaVim - Vim emulator for IDEs based on the IntelliJ platform
|
||||
* Copyright (C) 2003-2020 The IdeaVim authors
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.jetbrains.plugins.ideavim.action.motion.`object`
|
||||
|
||||
import com.maddyhome.idea.vim.command.CommandState
|
||||
import com.maddyhome.idea.vim.helper.StringHelper.parseKeys
|
||||
import org.jetbrains.plugins.ideavim.VimTestCase
|
||||
|
||||
class MotionInnerWordActionTest : VimTestCase() {
|
||||
fun `test empty text`() {
|
||||
doTest(parseKeys("viw"), "", "",
|
||||
CommandState.Mode.VISUAL, CommandState.SubMode.VISUAL_CHARACTER)
|
||||
}
|
||||
}
|
@@ -1,6 +1,25 @@
|
||||
package org.jetbrains.plugins.ideavim.extesion.argtextobj;
|
||||
/*
|
||||
* IdeaVim - Vim emulator for IDEs based on the IntelliJ platform
|
||||
* Copyright (C) 2003-2020 The IdeaVim authors
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.jetbrains.plugins.ideavim.extension.argtextobj;
|
||||
|
||||
import com.maddyhome.idea.vim.command.CommandState;
|
||||
import com.maddyhome.idea.vim.ex.vimscript.VimScriptGlobalEnvironment;
|
||||
import com.maddyhome.idea.vim.helper.VimBehaviorDiffers;
|
||||
import org.jetbrains.plugins.ideavim.VimTestCase;
|
||||
|
||||
@@ -16,6 +35,10 @@ public class VimArgTextObjExtensionTest extends VimTestCase {
|
||||
enableExtensions("argtextobj");
|
||||
}
|
||||
|
||||
private void setArgTextObjPairsVariable(String value) {
|
||||
VimScriptGlobalEnvironment.getInstance().getVariables().put("g:argtextobj_pairs", value);
|
||||
}
|
||||
|
||||
public void testDeleteAnArgument() {
|
||||
doTest(parseKeys("daa"),
|
||||
"function(int arg1, char<caret>* arg2=\"a,b,c(d,e)\")",
|
||||
@@ -102,15 +125,25 @@ public class VimArgTextObjExtensionTest extends VimTestCase {
|
||||
CommandState.Mode.VISUAL, CommandState.SubMode.VISUAL_CHARACTER);
|
||||
}
|
||||
|
||||
// The original author of this extension wanted this case to work
|
||||
/*
|
||||
public void testArgumentsInsideAngleBrackets() {
|
||||
setArgTextObjPairsVariable("(:),<:>");
|
||||
doTest(parseKeys("dia"),
|
||||
"std::vector<int, std::unique_p<caret>tr<bool>> v{};",
|
||||
"std::vector<int, <caret>> v{};",
|
||||
CommandState.Mode.COMMAND, CommandState.SubMode.NONE);
|
||||
}
|
||||
*/
|
||||
|
||||
public void testWhenUnbalancedHigherPriorityPairIsUsed() {
|
||||
setArgTextObjPairsVariable("{:},(:)");
|
||||
doTest(parseKeys("dia"),
|
||||
"namespace foo { void foo(int arg1, bool arg2<caret> { body }\n}",
|
||||
"namespace foo { void foo(int arg1, <caret>}",
|
||||
CommandState.Mode.COMMAND, CommandState.SubMode.NONE);
|
||||
doTest(parseKeys("dia"),
|
||||
"namespace foo { void foo(int <caret>arg1, bool arg2 { body }\n}",
|
||||
"namespace foo { <caret>, bool arg2 { body }\n}",
|
||||
CommandState.Mode.COMMAND, CommandState.SubMode.NONE);
|
||||
}
|
||||
|
||||
public void testBracketPriorityToHangleShiftOperators() {
|
||||
doTest(parseKeys("dia"),
|
||||
@@ -186,6 +219,13 @@ public class VimArgTextObjExtensionTest extends VimTestCase {
|
||||
CommandState.Mode.COMMAND, CommandState.SubMode.NONE);
|
||||
}
|
||||
|
||||
public void testHandleNestedParenthesisForASingleArgument() {
|
||||
doTest(parseKeys("dia"),
|
||||
"foo((20*<caret>30))",
|
||||
"foo(<caret>)",
|
||||
CommandState.Mode.COMMAND, CommandState.SubMode.NONE);
|
||||
}
|
||||
|
||||
public void testHandleImbalancedPairs() {
|
||||
doTest(parseKeys("dia"),
|
||||
"foo(arg1, ba<caret>r(not-an-arg{body",
|
||||
@@ -238,6 +278,7 @@ public class VimArgTextObjExtensionTest extends VimTestCase {
|
||||
}
|
||||
|
||||
public void testDeleteArrayArgument() {
|
||||
setArgTextObjPairsVariable("[:],(:)");
|
||||
doTest(parseKeys("dia"),
|
||||
"function(int a, String[<caret>] b)",
|
||||
"function(int a, <caret>)",
|
||||
@@ -259,7 +300,6 @@ public class VimArgTextObjExtensionTest extends VimTestCase {
|
||||
CommandState.Mode.COMMAND, CommandState.SubMode.NONE);
|
||||
}
|
||||
|
||||
// Original plugin doesn't remove the argument in case of space after function name
|
||||
public void testFunctionWithSpaceAfterName() {
|
||||
doTest(parseKeys("dia"),
|
||||
"function (int <caret>a)",
|
||||
@@ -296,4 +336,58 @@ public class VimArgTextObjExtensionTest extends VimTestCase {
|
||||
"class MyClass{ public int myFun() { if (tr<caret>ue) { somFunction(); } } }",
|
||||
CommandState.Mode.COMMAND, CommandState.SubMode.NONE);
|
||||
}
|
||||
|
||||
public void testParseVariablePairs() {
|
||||
assertPluginError(false);
|
||||
setArgTextObjPairsVariable("[:], (:)");
|
||||
doTest(parseKeys("daa"), "f(a<caret>)", "f(a<caret>)", CommandState.Mode.COMMAND, CommandState.SubMode.NONE);
|
||||
assertPluginError(true);
|
||||
assertPluginErrorMessageContains("expecting ':', but got '(' instead");
|
||||
|
||||
setArgTextObjPairsVariable("[:](:)");
|
||||
doTest(parseKeys("daa"), "f(a<caret>)", "f(a<caret>)", CommandState.Mode.COMMAND, CommandState.SubMode.NONE);
|
||||
assertPluginError(true);
|
||||
assertPluginErrorMessageContains("expecting ',', but got '(' instead");
|
||||
|
||||
setArgTextObjPairsVariable("=:=");
|
||||
doTest(parseKeys("daa"), "f(a<caret>)", "f(a<caret>)", CommandState.Mode.COMMAND, CommandState.SubMode.NONE);
|
||||
assertPluginError(true);
|
||||
assertPluginErrorMessageContains("open and close brackets must be different");
|
||||
|
||||
setArgTextObjPairsVariable("[:],(:");
|
||||
doTest(parseKeys("daa"), "f(a<caret>)", "f(a<caret>)", CommandState.Mode.COMMAND, CommandState.SubMode.NONE);
|
||||
assertPluginError(true);
|
||||
assertPluginErrorMessageContains("list of pairs is incomplete");
|
||||
|
||||
setArgTextObjPairsVariable("");
|
||||
doTest(parseKeys("daa"), "f(a<caret>)", "f(a<caret>)", CommandState.Mode.COMMAND, CommandState.SubMode.NONE);
|
||||
assertPluginError(true);
|
||||
assertPluginErrorMessageContains("list of pairs is incomplete");
|
||||
|
||||
setArgTextObjPairsVariable("[:],(:)");
|
||||
doTest(parseKeys("daa"), "f[a<caret>]", "f[<caret>]", CommandState.Mode.COMMAND, CommandState.SubMode.NONE);
|
||||
assertPluginError(false);
|
||||
|
||||
setArgTextObjPairsVariable("::;");
|
||||
doTest(parseKeys("daa"), "f: a<caret> ;", "f:<caret>;", CommandState.Mode.COMMAND, CommandState.SubMode.NONE);
|
||||
assertPluginError(false);
|
||||
|
||||
}
|
||||
|
||||
public void testCppLambaArguments() {
|
||||
setArgTextObjPairsVariable("[:],(:),{:},<:>");
|
||||
doTest(parseKeys("daa"),
|
||||
"[capture1, c = <caret>capture2] { return Clazz<int, bool>{ctorParam1, ctorParam2}; }",
|
||||
"[capture1] { return Clazz<int, bool>{ctorParam1, ctorParam2}; }",
|
||||
CommandState.Mode.COMMAND, CommandState.SubMode.NONE);
|
||||
doTest(parseKeys("daa"),
|
||||
"[capture1, c = capture2] { return Clazz<int,<caret> bool>{ctorParam1, ctorParam2}; }",
|
||||
"[capture1, c = capture2] { return Clazz<int>{ctorParam1, ctorParam2}; }",
|
||||
CommandState.Mode.COMMAND, CommandState.SubMode.NONE);
|
||||
doTest(parseKeys("daa"),
|
||||
"[capture1, c = capture2] { return Clazz<int, bool>{ctorPar<caret>am1, ctorParam2}; }",
|
||||
"[capture1, c = capture2] { return Clazz<int, bool>{ctorParam2}; }",
|
||||
CommandState.Mode.COMMAND, CommandState.SubMode.NONE);
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,430 @@
|
||||
/*
|
||||
* IdeaVim - Vim emulator for IDEs based on the IntelliJ platform
|
||||
* Copyright (C) 2003-2020 The IdeaVim authors
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.jetbrains.plugins.ideavim.extension.replacewithregister
|
||||
|
||||
import com.maddyhome.idea.vim.VimPlugin
|
||||
import com.maddyhome.idea.vim.command.CommandState
|
||||
import com.maddyhome.idea.vim.command.SelectionType
|
||||
import com.maddyhome.idea.vim.common.TextRange
|
||||
import com.maddyhome.idea.vim.helper.StringHelper.parseKeys
|
||||
import com.maddyhome.idea.vim.helper.VimBehaviorDiffers
|
||||
import junit.framework.Assert
|
||||
import org.jetbrains.plugins.ideavim.VimTestCase
|
||||
import org.jetbrains.plugins.ideavim.rangeOf
|
||||
|
||||
class ReplaceWithRegisterTest : VimTestCase() {
|
||||
|
||||
override fun setUp() {
|
||||
super.setUp()
|
||||
enableExtensions("ReplaceWithRegister")
|
||||
}
|
||||
|
||||
fun `test replace with empty register`() {
|
||||
val text = "one ${c}two three"
|
||||
|
||||
configureByText(text)
|
||||
typeText(parseKeys("griw"))
|
||||
myFixture.checkResult(text)
|
||||
}
|
||||
|
||||
fun `test simple replace`() {
|
||||
val text = "one ${c}two three"
|
||||
|
||||
configureByText(text)
|
||||
VimPlugin.getRegister().storeText(myFixture.editor, text rangeOf "one", SelectionType.CHARACTER_WISE, false)
|
||||
typeText(parseKeys("griw"))
|
||||
myFixture.checkResult("one on${c}e three")
|
||||
Assert.assertEquals("one", VimPlugin.getRegister().lastRegister?.text)
|
||||
}
|
||||
|
||||
fun `test empty text`() {
|
||||
val text = ""
|
||||
|
||||
configureByText(text)
|
||||
VimPlugin.getRegister().storeTextInternal(myFixture.editor, TextRange(0, 0), "one", SelectionType.CHARACTER_WISE, '"', false)
|
||||
typeText(parseKeys("griw"))
|
||||
myFixture.checkResult("on${c}e")
|
||||
}
|
||||
|
||||
fun `test replace with empty text`() {
|
||||
val text = "${c}one"
|
||||
|
||||
configureByText(text)
|
||||
VimPlugin.getRegister().storeText(myFixture.editor, "" rangeOf "", SelectionType.CHARACTER_WISE, false)
|
||||
typeText(parseKeys("griw"))
|
||||
myFixture.checkResult(c)
|
||||
}
|
||||
|
||||
fun `test replace use different register`() {
|
||||
val text = "one ${c}two three four"
|
||||
|
||||
configureByText(text)
|
||||
typeText(parseKeys("\"ayiw", "w", "\"agriw"))
|
||||
myFixture.checkResult("one two tw${c}o four")
|
||||
Assert.assertEquals("two", VimPlugin.getRegister().lastRegister?.text)
|
||||
typeText(parseKeys("w", "griw"))
|
||||
myFixture.checkResult("one two two tw${c}o")
|
||||
Assert.assertEquals("two", VimPlugin.getRegister().lastRegister?.text)
|
||||
}
|
||||
|
||||
fun `test replace use clipboard register`() {
|
||||
val text = "one ${c}two three four"
|
||||
|
||||
configureByText(text)
|
||||
typeText(parseKeys("\"+yiw", "w", "\"+griw", "w", "\"+griw"))
|
||||
myFixture.checkResult("one two two tw${c}o")
|
||||
Assert.assertEquals("two", VimPlugin.getRegister().lastRegister?.text)
|
||||
}
|
||||
|
||||
fun `test replace use wrong register`() {
|
||||
val text = "one ${c}two three"
|
||||
|
||||
configureByText(text)
|
||||
typeText(parseKeys("\"ayiw", "\"bgriw"))
|
||||
myFixture.checkResult(text)
|
||||
}
|
||||
|
||||
fun `test replace with line`() {
|
||||
val text = """
|
||||
|I fou${c}nd it in a legendary land|
|
||||
all rocks and lavender and tufted grass,
|
||||
""".trimIndent()
|
||||
|
||||
configureByText(text)
|
||||
typeText(parseKeys("yy", "j", "griw"))
|
||||
myFixture.checkResult("""
|
||||
|I found it in a legendary land|
|
||||
all |I found it in a legendary land${c}| and lavender and tufted grass,
|
||||
""".trimIndent())
|
||||
}
|
||||
|
||||
fun `test replace with line with clipboard register`() {
|
||||
val text = """
|
||||
|I fou${c}nd it in a legendary land|
|
||||
all rocks and lavender and tufted grass,
|
||||
""".trimIndent()
|
||||
|
||||
configureByText(text)
|
||||
typeText(parseKeys("\"+yy", "j", "\"+griw"))
|
||||
myFixture.checkResult("""
|
||||
|I found it in a legendary land|
|
||||
all |I found it in a legendary land${c}| and lavender and tufted grass,
|
||||
""".trimIndent())
|
||||
}
|
||||
|
||||
fun `test replace block selection`() {
|
||||
val text = """
|
||||
${c}one two three
|
||||
one two three
|
||||
one two three
|
||||
one two three
|
||||
""".trimIndent()
|
||||
|
||||
configureByText(text)
|
||||
typeText(parseKeys("<C-v>jjlly", "gg^w", "griw"))
|
||||
myFixture.checkResult("""
|
||||
one ${c}one three
|
||||
one onetwo three
|
||||
one onetwo three
|
||||
one two three
|
||||
""".trimIndent())
|
||||
}
|
||||
|
||||
fun `test replace with number`() {
|
||||
val text = "one ${c}two three four"
|
||||
|
||||
configureByText(text)
|
||||
VimPlugin.getRegister().storeText(myFixture.editor, text rangeOf "one", SelectionType.CHARACTER_WISE, false)
|
||||
typeText(parseKeys("3griw"))
|
||||
myFixture.checkResult("one on${c}e four")
|
||||
Assert.assertEquals("one", VimPlugin.getRegister().lastRegister?.text)
|
||||
}
|
||||
|
||||
@VimBehaviorDiffers("one on${c}e on${c}e four")
|
||||
fun `test replace with multiple carets`() {
|
||||
val text = "one ${c}two ${c}three four"
|
||||
|
||||
configureByText(text)
|
||||
VimPlugin.getRegister().storeText(myFixture.editor, text rangeOf "one", SelectionType.CHARACTER_WISE, false)
|
||||
typeText(parseKeys("griw"))
|
||||
myFixture.checkResult("one two one four")
|
||||
Assert.assertEquals("one", VimPlugin.getRegister().lastRegister?.text)
|
||||
}
|
||||
|
||||
fun `test dot repeat`() {
|
||||
val text = "one ${c}two three four"
|
||||
|
||||
configureByText(text)
|
||||
VimPlugin.getRegister().storeText(myFixture.editor, text rangeOf "one", SelectionType.CHARACTER_WISE, false)
|
||||
typeText(parseKeys("griw", "w", "."))
|
||||
myFixture.checkResult("one one on${c}e four")
|
||||
Assert.assertEquals("one", VimPlugin.getRegister().lastRegister?.text)
|
||||
}
|
||||
|
||||
// --------------------------------------- grr --------------------------
|
||||
|
||||
fun `test line replace`() {
|
||||
val text = """
|
||||
I found it in ${c}a legendary land
|
||||
all rocks and lavender and tufted grass,
|
||||
where it was settled on some sodden sand
|
||||
hard by the torrent of a mountain pass.
|
||||
""".trimIndent()
|
||||
|
||||
configureByText(text)
|
||||
VimPlugin.getRegister().storeText(myFixture.editor, text rangeOf "legendary", SelectionType.CHARACTER_WISE, false)
|
||||
typeText(parseKeys("grr"))
|
||||
myFixture.checkResult("""
|
||||
${c}legendary
|
||||
all rocks and lavender and tufted grass,
|
||||
where it was settled on some sodden sand
|
||||
hard by the torrent of a mountain pass.
|
||||
""".trimIndent())
|
||||
Assert.assertEquals("legendary", VimPlugin.getRegister().lastRegister?.text)
|
||||
}
|
||||
|
||||
fun `test line replace with line`() {
|
||||
val text = """
|
||||
I found it in ${c}a legendary land
|
||||
all rocks and lavender and tufted grass,
|
||||
where it was settled on some sodden sand
|
||||
hard by the torrent of a mountain pass.
|
||||
""".trimIndent()
|
||||
|
||||
configureByText(text)
|
||||
typeText(parseKeys("yyj", "grr"))
|
||||
myFixture.checkResult("""
|
||||
I found it in a legendary land
|
||||
${c}I found it in a legendary land
|
||||
where it was settled on some sodden sand
|
||||
hard by the torrent of a mountain pass.
|
||||
""".trimIndent())
|
||||
}
|
||||
|
||||
fun `test line replace with line empty line`() {
|
||||
val text = """
|
||||
I found it in ${c}a legendary land
|
||||
|
||||
all rocks and lavender and tufted grass,
|
||||
where it was settled on some sodden sand
|
||||
hard by the torrent of a mountain pass.
|
||||
""".trimIndent()
|
||||
|
||||
configureByText(text)
|
||||
typeText(parseKeys("yyj", "grr"))
|
||||
myFixture.checkResult("""
|
||||
I found it in a legendary land
|
||||
${c}I found it in a legendary land
|
||||
all rocks and lavender and tufted grass,
|
||||
where it was settled on some sodden sand
|
||||
hard by the torrent of a mountain pass.
|
||||
""".trimIndent())
|
||||
}
|
||||
|
||||
@VimBehaviorDiffers(description = "Where is the new line comes from?...")
|
||||
fun `test line replace with block`() {
|
||||
val text = """
|
||||
${c}one two three
|
||||
one two three
|
||||
one two three
|
||||
one two three
|
||||
""".trimIndent()
|
||||
|
||||
configureByText(text)
|
||||
typeText(parseKeys("<C-V>lljjyj", "grr"))
|
||||
myFixture.checkResult("""
|
||||
one two three
|
||||
${c}one
|
||||
one
|
||||
one
|
||||
one two three
|
||||
one two three
|
||||
|
||||
""".trimIndent())
|
||||
}
|
||||
|
||||
@VimBehaviorDiffers("""
|
||||
I found it in a legendary land
|
||||
${c}I found it in a legendary land
|
||||
hard by the torrent of a mountain pass.
|
||||
""")
|
||||
fun `test line with number`() {
|
||||
val text = """
|
||||
I found it in ${c}a legendary land
|
||||
all rocks and lavender and tufted grass,
|
||||
where it was settled on some sodden sand
|
||||
hard by the torrent of a mountain pass.
|
||||
""".trimIndent()
|
||||
|
||||
configureByText(text)
|
||||
typeText(parseKeys("yyj", "2grr"))
|
||||
myFixture.checkResult("""
|
||||
I found it in a legendary land
|
||||
${c}I found it in a legendary land
|
||||
where it was settled on some sodden sand
|
||||
hard by the torrent of a mountain pass.
|
||||
""".trimIndent())
|
||||
}
|
||||
|
||||
fun `test line dot repeat`() {
|
||||
val text = """
|
||||
I found it in ${c}a legendary land
|
||||
all rocks and lavender and tufted grass,
|
||||
where it was settled on some sodden sand
|
||||
hard by the torrent of a mountain pass.
|
||||
""".trimIndent()
|
||||
|
||||
configureByText(text)
|
||||
typeText(parseKeys("yyj", "grr", "j", "."))
|
||||
myFixture.checkResult("""
|
||||
I found it in a legendary land
|
||||
I found it in a legendary land
|
||||
${c}I found it in a legendary land
|
||||
hard by the torrent of a mountain pass.
|
||||
""".trimIndent())
|
||||
}
|
||||
|
||||
@VimBehaviorDiffers("""
|
||||
I found it in a legendary land
|
||||
${c}I found it in a legendary land
|
||||
where it was settled on some sodden sand
|
||||
${c}where it was settled on some sodden sand
|
||||
""")
|
||||
fun `test line multicaret`() {
|
||||
val text = """
|
||||
I found it in ${c}a legendary land
|
||||
all rocks and lavender and tufted grass,
|
||||
where it was s${c}ettled on some sodden sand
|
||||
hard by the torrent of a mountain pass.
|
||||
""".trimIndent()
|
||||
|
||||
configureByText(text)
|
||||
typeText(parseKeys("yyj", "grr"))
|
||||
myFixture.checkResult("""
|
||||
I found it in a legendary land
|
||||
I found it in a legendary land
|
||||
where it was settled on some sodden sand
|
||||
where it was settled on some sodden sand
|
||||
I found it in a legendary land
|
||||
where it was settled on some sodden sand
|
||||
|
||||
""".trimIndent())
|
||||
}
|
||||
|
||||
// ------------------------------------- gr + visual ----------------------
|
||||
|
||||
fun `test visual replace`() {
|
||||
val text = """
|
||||
I ${c}found it in a legendary land
|
||||
all rocks and lavender and tufted grass,
|
||||
where it was settled on some sodden sand
|
||||
hard by the torrent of a mountain pass.
|
||||
""".trimIndent()
|
||||
|
||||
configureByText(text)
|
||||
VimPlugin.getRegister().storeText(myFixture.editor, text rangeOf "legendary", SelectionType.CHARACTER_WISE, false)
|
||||
typeText(parseKeys("viw", "gr"))
|
||||
myFixture.checkResult("""
|
||||
I legendar${c}y it in a legendary land
|
||||
all rocks and lavender and tufted grass,
|
||||
where it was settled on some sodden sand
|
||||
hard by the torrent of a mountain pass.
|
||||
""".trimIndent())
|
||||
Assert.assertEquals("legendary", VimPlugin.getRegister().lastRegister?.text)
|
||||
assertMode(CommandState.Mode.COMMAND)
|
||||
}
|
||||
|
||||
fun `test visual replace with line`() {
|
||||
val text = """
|
||||
|I fo${c}und it in a legendary land|
|
||||
all rocks and lavender and tufted grass,
|
||||
where it was settled on some sodden sand
|
||||
hard by the torrent of a mountain pass.
|
||||
""".trimIndent()
|
||||
|
||||
configureByText(text)
|
||||
typeText(parseKeys("yyj", "viw", "gr"))
|
||||
myFixture.checkResult("""
|
||||
|I found it in a legendary land|
|
||||
all |I found it in a legendary land${c}| and lavender and tufted grass,
|
||||
where it was settled on some sodden sand
|
||||
hard by the torrent of a mountain pass.
|
||||
""".trimIndent())
|
||||
assertMode(CommandState.Mode.COMMAND)
|
||||
}
|
||||
|
||||
fun `test visual replace with two lines`() {
|
||||
val text = """
|
||||
|I found it in ${c}a legendary land|
|
||||
|all rocks and lavender and tufted grass,|
|
||||
where it was settled on some sodden sand
|
||||
hard by the torrent of a mountain pass.
|
||||
""".trimIndent()
|
||||
|
||||
configureByText(text)
|
||||
typeText(parseKeys("Vjyjj3w", "viw", "gr"))
|
||||
myFixture.checkResult("""
|
||||
|I found it in a legendary land|
|
||||
|all rocks and lavender and tufted grass,|
|
||||
where it was |I found it in a legendary land|
|
||||
|all rocks and lavender and tufted grass,${c}| on some sodden sand
|
||||
hard by the torrent of a mountain pass.
|
||||
""".trimIndent())
|
||||
assertMode(CommandState.Mode.COMMAND)
|
||||
}
|
||||
|
||||
fun `test visual line replace`() {
|
||||
val text = """
|
||||
I fo${c}und it in a legendary land
|
||||
all rocks and lavender and tufted grass,
|
||||
where it was settled on some sodden sand
|
||||
hard by the torrent of a mountain pass.
|
||||
""".trimIndent()
|
||||
|
||||
configureByText(text)
|
||||
VimPlugin.getRegister().storeText(myFixture.editor, text rangeOf "legendary", SelectionType.CHARACTER_WISE, false)
|
||||
typeText(parseKeys("V", "gr"))
|
||||
myFixture.checkResult("""
|
||||
${c}legendary
|
||||
all rocks and lavender and tufted grass,
|
||||
where it was settled on some sodden sand
|
||||
hard by the torrent of a mountain pass.
|
||||
""".trimIndent())
|
||||
assertMode(CommandState.Mode.COMMAND)
|
||||
}
|
||||
|
||||
fun `test visual line replace with line`() {
|
||||
val text = """
|
||||
I fo${c}und it in a legendary land
|
||||
all rocks and lavender and tufted grass,
|
||||
where it was settled on some sodden sand
|
||||
hard by the torrent of a mountain pass.
|
||||
""".trimIndent()
|
||||
|
||||
configureByText(text)
|
||||
typeText(parseKeys("yyj", "V", "gr"))
|
||||
myFixture.checkResult("""
|
||||
I found it in a legendary land
|
||||
${c}I found it in a legendary land
|
||||
where it was settled on some sodden sand
|
||||
hard by the torrent of a mountain pass.
|
||||
""".trimIndent())
|
||||
assertMode(CommandState.Mode.COMMAND)
|
||||
}
|
||||
}
|
@@ -24,6 +24,7 @@ import org.jetbrains.plugins.ideavim.VimTestCase;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.awt.event.InputEvent;
|
||||
import java.awt.event.KeyEvent;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
@@ -31,7 +32,7 @@ import java.util.List;
|
||||
*/
|
||||
public class StringHelperTest extends VimTestCase {
|
||||
public void testParseKeyModifiers() {
|
||||
assertTypedKeyStroke('C', "C");
|
||||
assertTypedKeyStroke('C', "<S-C>");
|
||||
assertTypedKeyStroke('c', "c");
|
||||
|
||||
assertPressedKeyStroke("control C", "<C-C>");
|
||||
@@ -94,8 +95,8 @@ public class StringHelperTest extends VimTestCase {
|
||||
}
|
||||
|
||||
public void testControlBoundCharacters() {
|
||||
assertKeyStroke(KeyStroke.getKeyStroke('@', InputEvent.CTRL_MASK), "\u0000");
|
||||
assertKeyStroke(KeyStroke.getKeyStroke('_', InputEvent.CTRL_MASK), "\u001F");
|
||||
assertKeyStroke(KeyStroke.getKeyStroke('@', InputEvent.CTRL_DOWN_MASK), "\u0000");
|
||||
assertKeyStroke(KeyStroke.getKeyStroke('_', InputEvent.CTRL_DOWN_MASK), "\u001F");
|
||||
}
|
||||
|
||||
public void testControlExceptionCharacters() {
|
||||
@@ -112,7 +113,12 @@ public class StringHelperTest extends VimTestCase {
|
||||
}
|
||||
|
||||
private void assertTypedKeyStroke(char expected, @NotNull String actual) {
|
||||
assertEquals(KeyStroke.getKeyStroke(expected), parseKeyStroke(actual));
|
||||
if (Character.isUpperCase(expected)) {
|
||||
assertEquals(KeyStroke.getKeyStroke(expected, KeyEvent.SHIFT_DOWN_MASK), parseKeyStroke(actual));
|
||||
}
|
||||
else {
|
||||
assertEquals(KeyStroke.getKeyStroke(expected), parseKeyStroke(actual));
|
||||
}
|
||||
}
|
||||
|
||||
@NotNull
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user