1
0
mirror of https://github.com/chylex/IntelliJ-IdeaVim.git synced 2025-08-18 10:31:44 +02:00

Compare commits

...

67 Commits

Author SHA1 Message Date
Alex Plate
2f5946640e Update changelog 2020-03-04 10:44:28 +03:00
Alex Plate
7cdb7dc308 Fix some tests for older versions of IDE 2020-03-04 10:32:05 +03:00
Alex Plate
c0038d0373 Add John Weigel to contributors list 2020-03-03 11:11:09 +03:00
Alex Plate
2820decb5e Rename variable 2020-03-03 11:07:07 +03:00
Alex Pláte
c64f368e6a Merge pull request #217 from angelbot/master
Add support for buffer list (buffers, files, ls)
2020-03-03 11:05:43 +03:00
Alex Plate
b7c8e84f5e Minor cleanup 2020-03-03 11:03:36 +03:00
Alex Plate
5acf6c9158 Convert VimPlugin to service 2020-02-28 21:11:12 +03:00
Alex Plate
a8197b0c84 Convert runnableHelper to kt 2020-02-28 18:15:40 +03:00
Alex Plate
2e03062c24 Rename .java to .kt 2020-02-28 18:15:39 +03:00
Alex Plate
7fb60a185b Update gradle version 2020-02-28 17:35:46 +03:00
Alex Plate
0327ea972b Make Open ideavimrc dumb aware 2020-02-28 10:29:38 +03:00
Alex Plate
561cc77ecc Move related methods closer to each other 2020-02-28 09:42:22 +03:00
Alex Plate
a1ab4acd14 Add comment for EPs 2020-02-28 09:39:33 +03:00
Alex Plate
d4939803da Update changelist 2020-02-27 14:27:13 +03:00
Alex Pláte
730ce3aca9 Merge pull request #226 from agrison/master
Implement the vim-textobj-entire plugin emulation.
2020-02-27 14:23:31 +03:00
Alexandre Grison
1893dc6afd Fixes from feedback.
Renamed `entiretextobj` to `textobj-entire` including packages and class name.
Renamed `<Plug>IncludingLeadingTrailing` to `<Plug>textobj-entire-a`.
Renamed `<Plug>IgnoringLeadingTrailing` to `<Plug>textobj-entire-i`.
Avoid iterating too much the buffer content.
2020-02-27 11:46:23 +01:00
John Weigel
c87528939b Fix buffer numbering bug with filters.
Update test to cover fix.
2020-02-23 21:11:36 -06:00
Alex Plate
e56646105d Add test bages 2020-02-21 12:05:05 +03:00
Alex Plate
b8a40d93f7 Now every service handles it's state separately. VimLocalConfig is a service 2020-02-21 12:03:02 +03:00
Alexandre Grison
4bfc025248 Fixes typo 2020-02-20 12:24:38 +01:00
Alexandre Grison
36f6027b0e Implement the vim-textobj-entire plugin emulation. 2020-02-20 12:13:55 +01:00
Alex Plate
e032377e68 Update annotations 2020-02-20 10:35:09 +03:00
Alex Plate
929eee4a12 Add comments for NotificationService.kt 2020-02-20 10:13:47 +03:00
Alex Plate
61ce50264a Add Alexey Gerasimov to contributors list 2020-02-19 12:02:10 +03:00
Alex Plate
48927b1207 Small corrections after merge 2020-02-19 11:58:37 +03:00
Alex Plate
0820893dc6 Update annotations to java 8 style 2020-02-19 11:58:27 +03:00
Alex Pláte
dd6079cfa6 Merge pull request #219 from fan-tom/bugifx/1008
Fix block actions (i.e ci{) in presence of quotes (VIM-1008)
2020-02-19 11:53:19 +03:00
John Weigel
3d7d75bae4 Merge remote-tracking branch 'upstream/master' 2020-02-16 21:11:02 -06:00
John Weigel
6da4d0ce5e Rework buffer list to more closely mimic vim. 2020-02-16 20:40:17 -06:00
Alex Plate
4994d70b1a Update changelog 2020-02-14 12:42:22 +03:00
Alex Plate
c873081dc3 Merge pull request #133 from igrekster/master
Add argtextobj.vim plugin emulation
2020-02-14 12:30:13 +03:00
Alex Plate
070237f77f Add [To Be Released] label 2020-02-14 12:24:34 +03:00
Alex Plate
eb01b25f35 Fix some cases by disabling [, { and < support (what is not supported in the original plugin) 2020-02-14 12:23:32 +03:00
Alex Plate
c0c9cfaf86 Get rid of several getText methods 2020-02-14 10:54:22 +03:00
Alex Plate
304f860eb2 Use java 8 JetBrains annotations 2020-02-14 10:32:18 +03:00
Alex Plate
2f18b25593 Update gradle dependencies 2020-02-14 10:17:00 +03:00
Alex Plate
adaa683e58 Use 2020.1 build for README label 2020-02-11 10:11:52 +03:00
igrekster
5ee0a93675 Add argtextobj.vim plugin emulation 2020-02-09 11:57:54 +11:00
Alex Plate
767b3c4a39 Add some scheduled for removal annotations 2020-02-08 20:57:44 +03:00
Alex Plate
bb948a463c Add option to make status bar icon gray 2020-02-08 20:56:13 +03:00
Alex Plate
e4e9a03d0a Add information about why EPs are used to register actions and ex handlers. 2020-02-08 18:14:04 +03:00
Alex Plate
50ba386f59 Write tests for dynamic extensions 2020-02-08 18:07:20 +03:00
Alex Plate
79d0565c2d Update some tests 2020-02-08 16:09:39 +03:00
Alex Plate
bcc9b0a7b1 Remove plugin owner after extension removal 2020-02-08 15:38:54 +03:00
Alex Plate
2c8f4940b9 Support EasyMotion extension 2020-02-08 15:25:24 +03:00
Alex Plate
41876cf8fd Make vimExtension dynamic 2020-02-08 14:56:39 +03:00
Alex Plate
f6fd0b52f0 Rename RequiredShortcutOwner to MappingOwner 2020-02-08 14:36:35 +03:00
Alex Plate
843faa7cc6 Make plugins disposable 2020-02-08 14:36:01 +03:00
Alexey Gerasimov
a8af2c3242 Fix Set creation 2020-02-07 22:24:46 +05:00
Alexey Gerasimov
e5bfad974e Copyright and comment 2020-02-07 21:50:06 +05:00
Alexey Gerasimov
59d87e0c94 More tests 2020-02-07 19:48:40 +05:00
Alexey Gerasimov
50c2d04503 Migrate to new checkInString 2020-02-07 19:48:40 +05:00
Alexey Gerasimov
480de62686 Improve existing checkInString 2020-02-07 19:48:40 +05:00
Alexey Gerasimov
955b501058 Make Direction enum public 2020-02-07 19:48:40 +05:00
Alexey Gerasimov
d985527624 Rewrite checkInString in Kotlin 2020-02-07 19:48:40 +05:00
Alexey Gerasimov
afbe7f0e69 Add findPositionOfFirstCharacter function 2020-02-07 19:48:40 +05:00
Alexey Gerasimov
94e65ddce6 Use isQuoteWithoutEscape when findCharacterPosition to detect escaped char 2020-02-07 19:48:40 +05:00
Alexey Gerasimov
cb9f144255 isQuoteWithoutEscape small improvement 2020-02-07 19:48:40 +05:00
Alexey Gerasimov
ac84624faa Use Direction enum instead of int 2020-02-07 19:48:40 +05:00
Alexey Gerasimov
c2196785e7 Add tests 2020-02-07 19:48:40 +05:00
Alexey Gerasimov
30097fbae6 Assume that caret is in string/char only if there is closing char 2020-02-07 19:48:40 +05:00
Alex Plate
c295dd5c62 Use special class for storing requiredShortcuts 2020-02-07 16:07:14 +03:00
Alex Plate
373fef2824 Refactor MappingInfo 2020-02-07 12:42:36 +03:00
Alex Plate
cfc255bf2b Rename .java to .kt 2020-02-07 12:41:57 +03:00
Alex Plate
ea7e58535b Fix tests 2020-02-07 12:41:47 +03:00
John Weigel
ff209d0120 Merge remote-tracking branch 'origin/master' 2020-02-01 22:38:18 -06:00
John Weigel
ea2fe618b5 Add support for buffer list (buffers, files, ls). 2020-02-01 22:33:12 -06:00
98 changed files with 3358 additions and 1240 deletions

View File

@@ -271,6 +271,18 @@ Contributors:
[![icon][github]](https://github.com/igrekster)
&nbsp;
igrekster
* [![icon][mail]](mailto:lokomot476@gmail.com)
[![icon][github]](https://github.com/fan-tom)
&nbsp;
Alexey Gerasimov
* [![icon][mail]](mailto:a.grison+github@gmail.com)
[![icon][github]](https://github.com/agrison)
&nbsp;
Alexandre Grison
* [![icon][mail]](mailto:angel@knight-industries.com)
[![icon][github]](https://github.com/angelbot)
&nbsp;
John Weigel
If you are a contributor and your name is not listed here, feel free to
contact the maintainers.

View File

@@ -29,6 +29,19 @@ _Available since 0.55.1 EAP:_
* [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
**Changes:**
* Replace `ideastatusbar` option with `ideastatusicon`. Now you can make the icon gray.
**Fixes:**
* [VIM-1008](https://youtrack.jetbrains.com/issue/VIM-1008) Correct `ci{` behavior
0.55, 2020-01-20
--------------

View File

@@ -16,6 +16,18 @@
</a>
<span>2019.2 Tests</span>
</div>
<div>
<a href="https://teamcity.jetbrains.com/viewType.html?buildTypeId=IdeaVim_TestsForIntelliJ20193&guest=1">
<img src="https://teamcity.jetbrains.com/app/rest/builds/buildType:(id:IdeaVim_TestsForIntelliJ20193)/statusIcon.svg?guest=1"/>
</a>
<span>2019.3 Tests</span>
</div>
<div>
<a href="https://teamcity.jetbrains.com/viewType.html?buildTypeId=IdeaVim_TestsForIntelliJ20201&guest=1">
<img src="https://teamcity.jetbrains.com/app/rest/builds/buildType:(id:IdeaVim_TestsForIntelliJ20201)/statusIcon.svg?guest=1"/>
</a>
<span>2020.1 Tests</span>
</div>
### Where to Start

View File

@@ -1,4 +1,4 @@
<img src="resources/META-INF/pluginIcon.svg" width="80" height="80" alt="icon" align="left"/>
<img src="resources/META-INF/pluginIcon.svg" width="80" height="80" alt="icon" align="left"/>
IdeaVim
===
@@ -7,8 +7,8 @@ IdeaVim
<a href="https://confluence.jetbrains.com/display/ALL/JetBrains+on+GitHub">
<img src="https://jb.gg/badges/official.svg" alt="official JetBrains project"/>
</a>
<a href="https://teamcity.jetbrains.com/viewType.html?buildTypeId=IdeaVim_TestsForIntelliJ20191&guest=1">
<img src="https://teamcity.jetbrains.com/app/rest/builds/buildType:(id:IdeaVim_TestsForIntelliJ20191)/statusIcon.svg?guest=1"/>
<a href="https://teamcity.jetbrains.com/viewType.html?buildTypeId=IdeaVim_TestsForIntelliJ20201&guest=1">
<img src="https://teamcity.jetbrains.com/app/rest/builds/buildType:(id:IdeaVim_TestsForIntelliJ20201)/statusIcon.svg?guest=1" alt="TeamCity Build"/>
</a>
</div>
@@ -97,6 +97,8 @@ Emulated Vim plugins:
* vim-surround
* vim-multiple-cursors
* vim-commentary
* argtextobj.vim [To Be Released]
* vim-textobj-entire [To Be Released]
Not supported (yet):
@@ -165,6 +167,16 @@ Available extensions:
* Emulates [commentary.vim](https://github.com/tpope/vim-commentary)
* Commands: `gcc`, `gc + motion`, `v_gc`
* argtextobj [To Be Released]
* Setup: `set argtextobj`
* Emulates [argtextobj.vim](https://www.vim.org/scripts/script.php?script_id=2699)
* Additional text objects: `aa`, `ia`
* textobj-entire [To Be Released]
* Setup: `set textobj-entire`
* Emulates [vim-textobj-entire](https://github.com/kana/vim-textobj-entire)
* Additional text objects: `ae`, `ie`
Changes to the IDE
------------------

View File

@@ -9,7 +9,7 @@ buildscript {
}
plugins {
id 'org.jetbrains.intellij' version '0.4.9'
id 'org.jetbrains.intellij' version '0.4.16'
}
apply plugin: 'java'
@@ -56,7 +56,7 @@ repositories {
dependencies {
compileOnly "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlinVersion"
compileOnly "org.jetbrains:annotations:17.0.0"
compileOnly "org.jetbrains:annotations:19.0.0"
}
compileKotlin {

View File

@@ -104,10 +104,22 @@ The following `:set` commands can appear in `~/.ideavimrc` or be set manually in
See wiki/`ideajoin` examples
`ideastatusbar` `ideastatusbar` Boolean (default true)
DEPRECATED. Please use `ideastatusicon`
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]
Define the behavior of IdeaVim icon in the status bar.
Use one of the following values:
- enabled - icon is shown in the status bar
- gray - use the gray version of the icon
- disabled - hide the icon
Works only from `~/.ideavimrc` after the IDE restart.
`lookupkeys` `lookupkeys` List of strings
List of keys that should be processed by the IDE during the active lookup (autocompletion).

Binary file not shown.

View File

@@ -1,6 +1,5 @@
#Fri Nov 22 14:42:01 MSK 2019
distributionUrl=https\://services.gradle.org/distributions/gradle-6.0.1-all.zip
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-6.2.1-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists

22
gradlew vendored
View File

@@ -1,5 +1,21 @@
#!/usr/bin/env sh
#
# Copyright 2015 the original author or authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
##############################################################################
##
## Gradle start up script for UN*X
@@ -28,7 +44,7 @@ APP_NAME="Gradle"
APP_BASE_NAME=`basename "$0"`
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS='"-Xmx64m"'
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD="maximum"
@@ -109,8 +125,8 @@ if $darwin; then
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
fi
# For Cygwin, switch paths to Windows format before running java
if $cygwin ; then
# For Cygwin or MSYS, switch paths to Windows format before running java
if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
JAVACMD=`cygpath --unix "$JAVACMD"`

18
gradlew.bat vendored
View File

@@ -1,3 +1,19 @@
@rem
@rem Copyright 2015 the original author or authors.
@rem
@rem Licensed under the Apache License, Version 2.0 (the "License");
@rem you may not use this file except in compliance with the License.
@rem You may obtain a copy of the License at
@rem
@rem https://www.apache.org/licenses/LICENSE-2.0
@rem
@rem Unless required by applicable law or agreed to in writing, software
@rem distributed under the License is distributed on an "AS IS" BASIS,
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@rem See the License for the specific language governing permissions and
@rem limitations under the License.
@rem
@if "%DEBUG%" == "" @echo off
@rem ##########################################################################
@rem
@@ -14,7 +30,7 @@ set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS="-Xmx64m"
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
@rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome

View File

@@ -60,5 +60,6 @@
<vimExCommand implementation="com.maddyhome.idea.vim.ex.handler.NextTabHandler" names="tabn[ext]"/>
<vimExCommand implementation="com.maddyhome.idea.vim.ex.handler.PreviousTabHandler" names="tabp[revious],tabN[ext]"/>
<vimExCommand implementation="com.maddyhome.idea.vim.ex.handler.TabOnlyHandler" names="tabo[nly]"/>
<vimExCommand implementation="com.maddyhome.idea.vim.ex.handler.BufferListHandler" names="buffers,ls,files"/>
</extensions>
</idea-plugin>

View File

@@ -3,5 +3,7 @@
<vimExtension implementation="com.maddyhome.idea.vim.extension.surround.VimSurroundExtension"/>
<vimExtension implementation="com.maddyhome.idea.vim.extension.multiplecursors.VimMultipleCursorsExtension"/>
<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"/>
</extensions>
</idea-plugin>

View File

@@ -3,9 +3,9 @@
<id>IdeaVIM</id>
<change-notes><![CDATA[
<ul>
<li>Support dot command for Surround and Commentary extensions</li>
<li>Support XDG settings standard</li>
<li>Add option to remove the status bar icon</li>
<li>Support argtextobj.vim plugin emulation</li>
<li>Support vim-textobj-entire plugin emulation</li>
<li>Support ls/buffers/files command</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>
@@ -34,25 +34,16 @@
<component>
<implementation-class>com.maddyhome.idea.vim.DynamicLoaderStopper</implementation-class>
</component>
<component>
<implementation-class>com.maddyhome.idea.vim.VimPlugin</implementation-class>
</component>
<component>
<implementation-class>com.maddyhome.idea.vim.VimLocalConfig</implementation-class>
</component>
</application-components>
<project-components>
<component>
<implementation-class>com.maddyhome.idea.vim.VimProjectComponent</implementation-class>
</component>
</project-components>
<extensionPoints>
<extensionPoint name="vimExtension" interface="com.maddyhome.idea.vim.extension.VimExtension"/>
<extensionPoint name="vimExtension" interface="com.maddyhome.idea.vim.extension.VimExtension" dynamic="true"/>
<!-- For internal use only -->
<extensionPoint name="vimExCommand" beanClass="com.maddyhome.idea.vim.ex.ExBeanClass" dynamic="true">
<with attribute="implementation" implements="com.maddyhome.idea.vim.ex.CommandHandler"/>
</extensionPoint>
<!-- For internal use only -->
<extensionPoint name="vimAction" beanClass="com.maddyhome.idea.vim.handler.ActionBeanClass" dynamic="true">
<with attribute="implementation" implements="com.maddyhome.idea.vim.handler.EditorActionHandlerBase"/>
</extensionPoint>
@@ -62,6 +53,11 @@
<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"/>
<applicationService serviceImplementation="com.maddyhome.idea.vim.VimLocalConfig"/>
<applicationService serviceImplementation="com.maddyhome.idea.vim.VimPlugin"/>
<postStartupActivity implementation="com.maddyhome.idea.vim.PluginStartup"/>
</extensions>
<xi:include href="/META-INF/includes/ApplicationServices.xml" xpointer="xpointer(/idea-plugin/*)"/>

View File

@@ -53,16 +53,15 @@ import java.util.Map;
* @author vlan
*/
public class EventFacade {
@NotNull private static final EventFacade ourInstance = new EventFacade();
private static final @NotNull EventFacade ourInstance = new EventFacade();
@Nullable private TypedActionHandler myOriginalTypedActionHandler;
private @Nullable TypedActionHandler myOriginalTypedActionHandler;
private Map<Project, MessageBusConnection> connections = new HashMap<>();
private EventFacade() {
}
@NotNull
public static EventFacade getInstance() {
public static @NotNull EventFacade getInstance() {
return ourInstance;
}
@@ -193,8 +192,7 @@ public class EventFacade {
return connections.get(project);
}
@NotNull
private TypedAction getTypedAction() {
private @NotNull TypedAction getTypedAction() {
return EditorActionManager.getInstance().getTypedAction();
}
}

View File

@@ -56,8 +56,10 @@ import org.jetbrains.annotations.Nullable;
import javax.swing.*;
import java.awt.*;
import java.awt.event.KeyEvent;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.*;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;
@@ -74,8 +76,7 @@ public class KeyHandler {
*
* @return A reference to the singleton
*/
@NotNull
public static KeyHandler getInstance() {
public static @NotNull KeyHandler getInstance() {
if (instance == null) {
instance = new KeyHandler();
}
@@ -306,7 +307,7 @@ public class KeyHandler {
return true;
}
private void handleEditorReset(@NotNull Editor editor, @NotNull KeyStroke key, @NotNull final DataContext context, @NotNull CommandState editorState) {
private void handleEditorReset(@NotNull Editor editor, @NotNull KeyStroke key, final @NotNull DataContext context, @NotNull CommandState editorState) {
if (editorState.getCommandBuilder().isAtDefaultState()) {
RegisterGroup register = VimPlugin.getRegister();
if (register.getCurrentRegister() == register.getDefaultRegister()) {
@@ -321,9 +322,9 @@ public class KeyHandler {
ChangeGroup.resetCaret(editor, false);
}
private boolean handleKeyMapping(@NotNull final Editor editor,
@NotNull final KeyStroke key,
@NotNull final DataContext context) {
private boolean handleKeyMapping(final @NotNull Editor editor,
final @NotNull KeyStroke key,
final @NotNull DataContext context) {
final CommandState commandState = CommandState.getInstance(editor);
final MappingState mappingState = commandState.getMappingState();
@@ -430,10 +431,8 @@ public class KeyHandler {
final EditorDataContext currentContext = new EditorDataContext(editor);
final List<KeyStroke> toKeys = mappingInfo.getToKeys();
final VimExtensionHandler extensionHandler = mappingInfo.getExtensionHandler();
if (toKeys != null) {
if (mappingInfo instanceof ToKeysMappingInfo) {
final List<KeyStroke> toKeys = ((ToKeysMappingInfo)mappingInfo).getToKeys();
final boolean fromIsPrefix = isPrefix(mappingInfo.getFromKeys(), toKeys);
boolean first = true;
for (KeyStroke keyStroke : toKeys) {
@@ -442,7 +441,8 @@ public class KeyHandler {
first = false;
}
}
else if (extensionHandler != null) {
else if (mappingInfo instanceof ToHandlerMappingInfo) {
final VimExtensionHandler extensionHandler = ((ToHandlerMappingInfo)mappingInfo).getExtensionHandler();
final CommandProcessor processor = CommandProcessor.getInstance();
// Cache isOperatorPending in case the extension changes the mode while moving the caret
@@ -810,8 +810,7 @@ public class KeyHandler {
editorState.getCommandBuilder().resetAll(getKeyRoot(editorState.getMappingState().getMappingMode()));
}
@NotNull
private CommandPartNode getKeyRoot(MappingMode mappingMode) {
private @NotNull CommandPartNode getKeyRoot(MappingMode mappingMode) {
return VimPlugin.getKey().getKeyRoot(mappingMode);
}
@@ -825,7 +824,10 @@ public class KeyHandler {
VimPlugin.clearError();
CommandState.getInstance(editor).reset();
reset(editor);
VimPlugin.getRegister().resetRegister();
RegisterGroup registerGroup = VimPlugin.getRegisterIfCreated();
if (registerGroup != null) {
registerGroup.resetRegister();
}
if (editor != null) {
VisualGroupKt.updateCaretState(editor);
editor.getSelectionModel().removeSelection();
@@ -833,9 +835,8 @@ public class KeyHandler {
}
// This method is copied from com.intellij.openapi.editor.actionSystem.EditorAction.getProjectAwareDataContext
@NotNull
private static DataContext getProjectAwareDataContext(@NotNull final Editor editor,
@NotNull final DataContext original) {
private static @NotNull DataContext getProjectAwareDataContext(final @NotNull Editor editor,
final @NotNull DataContext original) {
if (PROJECT.getData(original) == editor.getProject()) {
return new DialogAwareDataContext(original);
}
@@ -853,7 +854,7 @@ public class KeyHandler {
}
// This class is copied from com.intellij.openapi.editor.actionSystem.DialogAwareDataContext.DialogAwareDataContext
private final static class DialogAwareDataContext implements DataContext {
private static final class DialogAwareDataContext implements DataContext {
@SuppressWarnings("rawtypes")
private static final DataKey[] keys = {PROJECT, PROJECT_FILE_DIRECTORY, EDITOR, VIRTUAL_FILE, PSI_FILE};
private final Map<String, Object> values = new HashMap<>();
@@ -865,9 +866,8 @@ public class KeyHandler {
}
}
@Nullable
@Override
public Object getData(@NotNull @NonNls String dataId) {
public @Nullable Object getData(@NotNull @NonNls String dataId) {
if (values.containsKey(dataId)) {
return values.get(dataId);
}

View File

@@ -0,0 +1,48 @@
/*
* 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.project.DumbAware
import com.intellij.openapi.project.Project
import com.intellij.openapi.startup.StartupActivity
import com.maddyhome.idea.vim.listener.VimListenerManager
/**
* @author Alex Plate
*
* [VERSION UPDATE] 193+ Use StartupActivity.DumbAware
*/
class PluginStartup : StartupActivity, DumbAware {
private var firstInitializationOccurred = false
override fun runActivity(project: Project) {
if (firstInitializationOccurred && VimPlugin.isEnabled()) {
// This code should be executed on every project open
// Project listeners are self-disposable, so there is no need to unregister them on project close
VimListenerManager.ProjectListeners.add(project)
}
if (firstInitializationOccurred) return
firstInitializationOccurred = true
// This code should be executed once
VimPlugin.getInstance().initialize()
}
}

View File

@@ -23,6 +23,7 @@ import com.intellij.openapi.extensions.PluginDescriptor;
import com.maddyhome.idea.vim.group.KeyGroup;
import com.maddyhome.idea.vim.handler.ActionBeanClass;
import com.maddyhome.idea.vim.handler.EditorActionHandlerBase;
import com.maddyhome.idea.vim.key.MappingOwner;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -44,7 +45,7 @@ public class RegisterActions {
// ExtensionPoint.addExtensionPointListener(ExtensionPointChangeListener, boolean, Disposable)
VIM_ACTIONS_EP.getPoint(null).addExtensionPointListener(new ExtensionPointListener<ActionBeanClass>() {
@Override
public void extensionAdded(@NotNull ActionBeanClass extension, @NotNull PluginDescriptor pluginDescriptor) {
public void extensionAdded(@NotNull ActionBeanClass extension, PluginDescriptor pluginDescriptor) {
// Suppress listener before the `VimPlugin.turnOn()` function execution. This logic should be rewritten after
// version update (or earlier).
if (!initialRegistration) return;
@@ -53,7 +54,7 @@ public class RegisterActions {
}
@Override
public void extensionRemoved(@NotNull ActionBeanClass extension, @NotNull PluginDescriptor pluginDescriptor) {
public void extensionRemoved(@NotNull ActionBeanClass extension, PluginDescriptor pluginDescriptor) {
if (!initialRegistration) return;
unregisterActions();
registerActions();
@@ -64,27 +65,28 @@ public class RegisterActions {
/**
* Register all the key/action mappings for the plugin.
*/
static void registerActions() {
public static void registerActions() {
registerVimCommandActions();
registerEmptyShortcuts();
initialRegistration = true;
}
@Nullable
public static EditorActionHandlerBase findAction(@NotNull String id) {
public static @Nullable EditorActionHandlerBase findAction(@NotNull String id) {
return VIM_ACTIONS_EP.extensions().filter(vimActionBean -> vimActionBean.getActionId().equals(id)).findFirst()
.map(ActionBeanClass::getAction).orElse(null);
}
@NotNull
public static EditorActionHandlerBase findActionOrDie(@NotNull String id) {
public static @NotNull EditorActionHandlerBase findActionOrDie(@NotNull String id) {
EditorActionHandlerBase action = findAction(id);
if (action == null) throw new RuntimeException("Action " + id + " is not registered");
return action;
}
public static void unregisterActions() {
VimPlugin.getKey().unregisterCommandActions();
KeyGroup keyGroup = VimPlugin.getKeyIfCreated();
if (keyGroup != null) {
keyGroup.unregisterCommandActions();
}
}
private static void registerVimCommandActions() {
@@ -97,6 +99,6 @@ public class RegisterActions {
// The {char1} <BS> {char2} shortcut is handled directly by KeyHandler#handleKey, so doesn't have an action. But we
// still need to register the shortcut, to make sure the editor doesn't swallow it.
parser.registerShortcutWithoutAction(KeyStroke.getKeyStroke(KeyEvent.VK_BACK_SPACE, 0));
parser.registerShortcutWithoutAction(KeyStroke.getKeyStroke(KeyEvent.VK_BACK_SPACE, 0), MappingOwner.IdeaVim.INSTANCE);
}
}

View File

@@ -50,6 +50,7 @@ import com.intellij.ui.awt.RelativePoint
import com.intellij.util.Consumer
import com.intellij.util.text.VersionComparatorUtil
import com.maddyhome.idea.vim.group.NotificationService
import com.maddyhome.idea.vim.option.IdeaStatusIcon
import com.maddyhome.idea.vim.option.OptionsManager
import com.maddyhome.idea.vim.ui.VimEmulationConfigurable
import icons.VimIcons
@@ -59,7 +60,11 @@ import javax.swing.Icon
import javax.swing.SwingConstants
private class StatusBarIconProvider : StatusBarWidgetProvider {
override fun getWidget(project: Project): VimStatusBar? = if (OptionsManager.ideastatusbar.isSet) VimStatusBar else null
override fun getWidget(project: Project): VimStatusBar? {
if (!OptionsManager.ideastatusbar.isSet) return null
if (OptionsManager.ideastatusicon.value == IdeaStatusIcon.disabled) return null
return VimStatusBar
}
}
object VimStatusBar : StatusBarWidget, StatusBarWidget.IconPresentation {
@@ -76,7 +81,10 @@ object VimStatusBar : StatusBarWidget, StatusBarWidget.IconPresentation {
override fun getTooltipText() = "IdeaVim"
override fun getIcon(): Icon = if (VimPlugin.isEnabled()) VimIcons.IDEAVIM else VimIcons.IDEAVIM_DISABLED
override fun getIcon(): Icon {
if (OptionsManager.ideastatusicon.value == IdeaStatusIcon.gray) return VimIcons.IDEAVIM_DISABLED
return if (VimPlugin.isEnabled()) VimIcons.IDEAVIM else VimIcons.IDEAVIM_DISABLED
}
override fun getClickConsumer() = Consumer<MouseEvent> { event ->
val component = event.component

View File

@@ -20,9 +20,9 @@ package com.maddyhome.idea.vim
import com.intellij.openapi.components.PersistentStateComponent
import com.intellij.openapi.components.RoamingType
import com.intellij.openapi.components.ServiceManager
import com.intellij.openapi.components.State
import com.intellij.openapi.components.Storage
import com.maddyhome.idea.vim.VimPlugin.STATE_VERSION
import org.jdom.Element
/**
@@ -30,29 +30,27 @@ import org.jdom.Element
*/
@State(name = "VimLocalSettings", storages = [
Storage("\$APP_CONFIG$$/vim_local_settings.xml", roamingType = RoamingType.DISABLED, deprecated = true),
Storage("\$APP_CONFIG$/vim_local_settings.xml", roamingType = RoamingType.DISABLED)
])
Storage("\$APP_CONFIG$$/vim_local_settings.xml", roamingType = RoamingType.DISABLED, deprecated = true),
Storage("\$APP_CONFIG$/vim_local_settings.xml", roamingType = RoamingType.DISABLED, deprecated = true)
])
// TODO: 27.01.2020 [VERSION UPDATE] 2019.3 https://www.jetbrains.org/intellij/sdk/docs/basics/plugin_structure/plugin_services.html#light-services
@Deprecated("The data from this class will be stored in vim_settings")
class VimLocalConfig : PersistentStateComponent<Element> {
override fun getState(): Element {
val element = Element("ideavim-local")
val state = Element("state")
state.setAttribute("version", STATE_VERSION.toString())
element.addContent(state)
VimPlugin.getMark().saveData(element)
VimPlugin.getRegister().saveData(element)
VimPlugin.getSearch().saveData(element)
VimPlugin.getHistory().saveData(element)
return element
}
override fun getState(): Element? = null
override fun loadState(state: Element) {
// This is initialization of state from the legacy configuration structure.
// This code should be performed only once on settings migration.
// After the migration is done, the file with settings gets removed and this method won't be called again.
VimPlugin.getMark().readData(state)
VimPlugin.getRegister().readData(state)
VimPlugin.getSearch().readData(state)
VimPlugin.getHistory().readData(state)
}
companion object {
fun initialize() {
ServiceManager.getService(VimLocalConfig::class.java)
}
}
}

View File

@@ -23,10 +23,14 @@ import com.intellij.ide.util.PropertiesComponent;
import com.intellij.notification.Notification;
import com.intellij.notification.NotificationListener;
import com.intellij.openapi.Disposable;
import com.intellij.openapi.application.Application;
import com.intellij.openapi.application.ApplicationInfo;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.application.PermanentInstallationID;
import com.intellij.openapi.components.*;
import com.intellij.openapi.components.PersistentStateComponent;
import com.intellij.openapi.components.ServiceManager;
import com.intellij.openapi.components.State;
import com.intellij.openapi.components.Storage;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.extensions.PluginId;
import com.intellij.openapi.keymap.Keymap;
@@ -66,6 +70,9 @@ import java.io.IOException;
import java.net.URLEncoder;
import java.util.concurrent.TimeUnit;
import static com.maddyhome.idea.vim.group.EditorGroup.EDITOR_STORE_ELEMENT;
import static com.maddyhome.idea.vim.group.KeyGroup.SHORTCUT_CONFLICTS_ELEMENT;
/**
* This plugin attempts to emulate the key binding and general functionality of Vim and gVim. See the supplied
* documentation for a complete list of supported and unsupported Vim emulation. The code base contains some debugging
@@ -75,11 +82,10 @@ import java.util.concurrent.TimeUnit;
* Registers and marks are shared across open projects so you can copy and paste between files of different projects.
*/
@State(name = "VimSettings", storages = {@Storage("$APP_CONFIG$/vim_settings.xml")})
public class VimPlugin implements BaseComponent, PersistentStateComponent<Element>, Disposable {
private static final String IDEAVIM_COMPONENT_NAME = "VimPlugin";
public class VimPlugin implements PersistentStateComponent<Element>, Disposable {
private static final String IDEAVIM_PLUGIN_ID = "IdeaVIM";
private static final String IDEAVIM_STATISTICS_TIMESTAMP_KEY = "ideavim.statistics.timestamp";
public static final int STATE_VERSION = 5;
private static final int STATE_VERSION = 6;
private static long lastBeepTimeMillis;
@@ -94,21 +100,24 @@ public class VimPlugin implements BaseComponent, PersistentStateComponent<Elemen
private static final Logger LOG = Logger.getInstance(VimPlugin.class);
@NotNull
@Override
public String getComponentName() {
return IDEAVIM_COMPONENT_NAME;
}
private final @NotNull VimState state = new VimState();
@NotNull private final VimState state = new VimState();
// [VERSION UPDATE] 193+ replace with com.intellij.openapi.components.PersistentStateComponent.initializeComponent
@Override
public void initComponent() {
public void initialize() {
LOG.debug("initComponent");
if (enabled) turnOnPlugin();
// Initialize a legacy local config.
if (previousStateVersion == 5) {
//noinspection deprecation
VimLocalConfig.Companion.initialize();
}
if (enabled) {
Application application = ApplicationManager.getApplication();
if (application.isUnitTestMode()) {
application.invokeAndWait(this::turnOnPlugin);
} else {
application.invokeLater(this::turnOnPlugin);
}
}
LOG.debug("done");
}
@@ -123,23 +132,21 @@ public class VimPlugin implements BaseComponent, PersistentStateComponent<Elemen
/**
* @return NotificationService as applicationService if project is null and projectService otherwise
*/
@NotNull
public static NotificationService getNotifications(@Nullable Project project) {
public static @NotNull NotificationService getNotifications(@Nullable Project project) {
if (project == null) {
return ServiceManager.getService(NotificationService.class);
} else {
}
else {
return ServiceManager.getService(project, NotificationService.class);
}
}
@NotNull
public static VimState getVimState() {
public static @NotNull VimState getVimState() {
return getInstance().state;
}
@NotNull
public static MotionGroup getMotion() {
public static @NotNull MotionGroup getMotion() {
return ServiceManager.getService(MotionGroup.class);
}
@@ -151,18 +158,17 @@ public class VimPlugin implements BaseComponent, PersistentStateComponent<Elemen
public static void statisticReport() {
final PropertiesComponent propertiesComponent = PropertiesComponent.getInstance();
final long lastUpdate = propertiesComponent.getOrInitLong(IDEAVIM_STATISTICS_TIMESTAMP_KEY, 0);
final boolean outOfDate =
lastUpdate == 0 || System.currentTimeMillis() - lastUpdate > TimeUnit.DAYS.toMillis(1);
final boolean outOfDate = lastUpdate == 0 || System.currentTimeMillis() - lastUpdate > TimeUnit.DAYS.toMillis(1);
if (outOfDate && isEnabled()) {
ApplicationManager.getApplication().executeOnPooledThread(() -> {
try {
final String buildNumber = ApplicationInfo.getInstance().getBuild().asString();
final String version = URLEncoder.encode(getVersion(), CharsetToolkit.UTF8);
final String os =
URLEncoder.encode(SystemInfo.OS_NAME + " " + SystemInfo.OS_VERSION, CharsetToolkit.UTF8);
final String os = URLEncoder.encode(SystemInfo.OS_NAME + " " + SystemInfo.OS_VERSION, CharsetToolkit.UTF8);
final String uid = PermanentInstallationID.get();
final String url = "https://plugins.jetbrains.com/plugins/list" +
"?pluginId=" + IDEAVIM_PLUGIN_ID +
"?pluginId=" +
IDEAVIM_PLUGIN_ID +
"&build=" +
buildNumber +
"&pluginVersion=" +
@@ -191,106 +197,87 @@ public class VimPlugin implements BaseComponent, PersistentStateComponent<Elemen
}
}
@NotNull
public static ChangeGroup getChange() {
public static @NotNull ChangeGroup getChange() {
return ServiceManager.getService(ChangeGroup.class);
}
@NotNull
public static CommandGroup getCommand() {
public static @NotNull CommandGroup getCommand() {
return ServiceManager.getService(CommandGroup.class);
}
@NotNull
public static MarkGroup getMark() {
public static @NotNull MarkGroup getMark() {
return ServiceManager.getService(MarkGroup.class);
}
@NotNull
public static RegisterGroup getRegister() {
public static @NotNull RegisterGroup getRegister() {
return ServiceManager.getService(RegisterGroup.class);
}
@NotNull
public static FileGroup getFile() {
public static @Nullable RegisterGroup getRegisterIfCreated() {
return ServiceManager.getServiceIfCreated(RegisterGroup.class);
}
public static @NotNull FileGroup getFile() {
return ServiceManager.getService(FileGroup.class);
}
@NotNull
public static SearchGroup getSearch() {
public static @NotNull SearchGroup getSearch() {
return ServiceManager.getService(SearchGroup.class);
}
@NotNull
public static ProcessGroup getProcess() {
public static @Nullable SearchGroup getSearchIfCreated() {
return ServiceManager.getServiceIfCreated(SearchGroup.class);
}
public static @NotNull ProcessGroup getProcess() {
return ServiceManager.getService(ProcessGroup.class);
}
@NotNull
public static MacroGroup getMacro() {
public static @NotNull MacroGroup getMacro() {
return ServiceManager.getService(MacroGroup.class);
}
@NotNull
public static DigraphGroup getDigraph() {
public static @NotNull DigraphGroup getDigraph() {
return ServiceManager.getService(DigraphGroup.class);
}
@NotNull
public static HistoryGroup getHistory() {
public static @NotNull HistoryGroup getHistory() {
return ServiceManager.getService(HistoryGroup.class);
}
@NotNull
public static KeyGroup getKey() {
public static @NotNull KeyGroup getKey() {
return ServiceManager.getService(KeyGroup.class);
}
@NotNull
public static WindowGroup getWindow() {
public static @Nullable KeyGroup getKeyIfCreated() {
return ServiceManager.getServiceIfCreated(KeyGroup.class);
}
public static @NotNull WindowGroup getWindow() {
return ServiceManager.getService(WindowGroup.class);
}
@NotNull
public static EditorGroup getEditor() {
public static @NotNull EditorGroup getEditor() {
return ServiceManager.getService(EditorGroup.class);
}
@NotNull
public static VisualMotionGroup getVisualMotion() {
public static @Nullable EditorGroup getEditorIfCreated() {
return ServiceManager.getServiceIfCreated(EditorGroup.class);
}
public static @NotNull VisualMotionGroup getVisualMotion() {
return ServiceManager.getService(VisualMotionGroup.class);
}
@NotNull
public static YankGroup getYank() {
public static @NotNull YankGroup getYank() {
return ServiceManager.getService(YankGroup.class);
}
@NotNull
public static PutGroup getPut() {
public static @NotNull PutGroup getPut() {
return ServiceManager.getService(PutGroup.class);
}
@Override
public Element getState() {
LOG.debug("Saving state");
final Element element = new Element("ideavim");
// Save whether the plugin is enabled or not
final Element state = new Element("state");
state.setAttribute("version", Integer.toString(STATE_VERSION));
state.setAttribute("enabled", Boolean.toString(enabled));
element.addContent(state);
getKey().saveData(element);
getEditor().saveData(element);
this.state.saveData(element);
return element;
}
@NotNull
private static NotificationService getNotifications() {
private static @NotNull NotificationService getNotifications() {
return getNotifications(null);
}
@@ -308,15 +295,13 @@ public class VimPlugin implements BaseComponent, PersistentStateComponent<Elemen
}
}
@NotNull
public static PluginId getPluginId() {
public static @NotNull PluginId getPluginId() {
return PluginId.getId(IDEAVIM_PLUGIN_ID);
}
// [VERSION UPDATE] 193+ remove suppress
@SuppressWarnings({"MissingRecentApi", "UnstableApiUsage"})
@NotNull
public static String getVersion() {
public static @NotNull String getVersion() {
final IdeaPluginDescriptor plugin = PluginManager.getPlugin(getPluginId());
if (!ApplicationManager.getApplication().isInternal()) {
return plugin != null ? plugin.getVersion() : "SNAPSHOT";
@@ -400,9 +385,8 @@ public class VimPlugin implements BaseComponent, PersistentStateComponent<Elemen
}
}
@NotNull
private static VimPlugin getInstance() {
return ApplicationManager.getApplication().getComponent(VimPlugin.class);
public static @NotNull VimPlugin getInstance() {
return ServiceManager.getService(VimPlugin.class);
}
private void turnOnPlugin() {
@@ -434,8 +418,14 @@ public class VimPlugin implements BaseComponent, PersistentStateComponent<Elemen
// Unregister ex handlers
CommandParser.getInstance().unregisterHandlers();
getEditor().turnOff();
getSearch().turnOff();
EditorGroup editorGroup = getEditorIfCreated();
if (editorGroup != null) {
editorGroup.turnOff();
}
SearchGroup searchGroup = getSearchIfCreated();
if (searchGroup != null) {
searchGroup.turnOff();
}
VimListenerManager.INSTANCE.turnOff();
ExEntryPanel.fullReset();
}
@@ -485,7 +475,7 @@ public class VimPlugin implements BaseComponent, PersistentStateComponent<Elemen
}
@Override
public void loadState(@NotNull final Element element) {
public void loadState(final @NotNull Element element) {
LOG.debug("Loading state");
// Restore whether the plugin is enabled or not
@@ -500,6 +490,27 @@ public class VimPlugin implements BaseComponent, PersistentStateComponent<Elemen
previousKeyMap = state.getAttributeValue("keymap");
}
legacyStateLoading(element);
this.state.readData(element);
}
@Override
public Element getState() {
LOG.debug("Saving state");
final Element element = new Element("ideavim");
// Save whether the plugin is enabled or not
final Element state = new Element("state");
state.setAttribute("version", Integer.toString(STATE_VERSION));
state.setAttribute("enabled", Boolean.toString(enabled));
element.addContent(state);
this.state.saveData(element);
return element;
}
private void legacyStateLoading(@NotNull Element element) {
if (previousStateVersion > 0 && previousStateVersion < 5) {
// Migrate settings from 4 to 5 version
getMark().readData(element);
@@ -507,8 +518,11 @@ public class VimPlugin implements BaseComponent, PersistentStateComponent<Elemen
getSearch().readData(element);
getHistory().readData(element);
}
getKey().readData(element);
getEditor().readData(element);
this.state.readData(element);
if (element.getChild(SHORTCUT_CONFLICTS_ELEMENT) != null) {
getKey().readData(element);
}
if (element.getChild(EDITOR_STORE_ELEMENT) != null) {
getEditor().readData(element);
}
}
}

View File

@@ -37,7 +37,7 @@ import javax.swing.*;
public class VimTypedActionHandler implements TypedActionHandlerEx {
private static final Logger logger = Logger.getInstance(VimTypedActionHandler.class.getName());
@NotNull private final KeyHandler handler;
private final @NotNull KeyHandler handler;
public VimTypedActionHandler(TypedActionHandler origHandler) {
handler = KeyHandler.getInstance();
@@ -50,7 +50,7 @@ public class VimTypedActionHandler implements TypedActionHandlerEx {
}
@Override
public void execute(@NotNull final Editor editor, final char charTyped, @NotNull final DataContext context) {
public void execute(final @NotNull Editor editor, final char charTyped, final @NotNull DataContext context) {
try {
handler.handleKey(editor, KeyStroke.getKeyStroke(charTyped), new EditorDataContext(editor));
}

View File

@@ -60,15 +60,14 @@ public class CommandState {
*
* This field is reset after the command has been executed.
*/
@Nullable private Command executingCommand;
private @Nullable Command executingCommand;
private CommandState() {
pushModes(defaultModeState.getMode(), defaultModeState.getSubMode());
}
@Contract("null -> new")
@NotNull
public static CommandState getInstance(@Nullable Editor editor) {
public static @NotNull CommandState getInstance(@Nullable Editor editor) {
if (editor == null) {
return new CommandState();
}
@@ -82,8 +81,7 @@ public class CommandState {
return res;
}
@NotNull
private static CommandPartNode getKeyRootNode(MappingMode mappingMode) {
private static @NotNull CommandPartNode getKeyRootNode(MappingMode mappingMode) {
return VimPlugin.getKey().getKeyRoot(mappingMode);
}
@@ -106,13 +104,11 @@ public class CommandState {
return commandBuilder;
}
@NotNull
public MappingState getMappingState() {
public @NotNull MappingState getMappingState() {
return mappingState;
}
@Nullable
public Command getExecutingCommand() {
public @Nullable Command getExecutingCommand() {
return executingCommand;
}
@@ -202,13 +198,11 @@ public class CommandState {
throw new IllegalArgumentException("Unexpected mode: " + mode);
}
@NotNull
public Mode getMode() {
public @NotNull Mode getMode() {
return currentModeState().getMode();
}
@NotNull
public SubMode getSubMode() {
public @NotNull SubMode getSubMode() {
return currentModeState().getSubMode();
}
@@ -294,8 +288,7 @@ public class CommandState {
VimPlugin.showMode(msg.toString());
}
@NotNull
private String getStatusString(int pos) {
private @NotNull String getStatusString(int pos) {
ModeState modeState;
if (pos >= 0 && pos < modeStates.size()) {
modeState = modeStates.get(pos);
@@ -354,8 +347,8 @@ public class CommandState {
}
private static class ModeState {
@NotNull private final Mode myMode;
@NotNull private final SubMode mySubMode;
private final @NotNull Mode myMode;
private final @NotNull SubMode mySubMode;
@Contract(pure = true)
public ModeState(@NotNull Mode mode, @NotNull SubMode subMode) {
@@ -363,13 +356,11 @@ public class CommandState {
this.mySubMode = subMode;
}
@NotNull
public Mode getMode() {
public @NotNull Mode getMode() {
return myMode;
}
@NotNull
public SubMode getSubMode() {
public @NotNull SubMode getSubMode() {
return mySubMode;
}

View File

@@ -59,7 +59,7 @@ public class CommandParser {
*
* @return The singleton instance
*/
public synchronized static CommandParser getInstance() {
public static synchronized CommandParser getInstance() {
return CommandParserHolder.INSTANCE;
}
@@ -76,7 +76,7 @@ public class CommandParser {
//noinspection deprecation
EX_COMMAND_EP.getPoint(null).addExtensionPointListener(new ExtensionPointListener<ExBeanClass>() {
@Override
public void extensionAdded(@NotNull ExBeanClass extension, @NotNull PluginDescriptor pluginDescriptor) {
public void extensionAdded(@NotNull ExBeanClass extension, PluginDescriptor pluginDescriptor) {
// Suppress listener before the `VimPlugin.turnOn()` function execution. This logic should be rewritten after
// version update (or earlier).
if (!initialRegistration) return;
@@ -85,7 +85,7 @@ public class CommandParser {
}
@Override
public void extensionRemoved(@NotNull ExBeanClass extension, @NotNull PluginDescriptor pluginDescriptor) {
public void extensionRemoved(@NotNull ExBeanClass extension, PluginDescriptor pluginDescriptor) {
if (!initialRegistration) return;
unregisterHandlers();
registerHandlers();
@@ -215,8 +215,7 @@ public class CommandParser {
}
}
@Nullable
public CommandHandler getCommandHandler(@NotNull ExCommand command) {
public @Nullable CommandHandler getCommandHandler(@NotNull ExCommand command) {
final String cmd = command.getCommand();
// If there is no command, just a range, use the 'goto line' handler
if (cmd.length() == 0) {
@@ -241,8 +240,7 @@ public class CommandParser {
* @return The parse result
* @throws ExException if the text is syntactically incorrect
*/
@NotNull
public ExCommand parse(@NotNull String cmd) throws ExException {
public @NotNull ExCommand parse(@NotNull String cmd) throws ExException {
// This is a complicated state machine that should probably be rewritten
if (logger.isDebugEnabled()) {
logger.debug("processing `" + cmd + "'");
@@ -622,7 +620,7 @@ public class CommandParser {
}
}
@NotNull private final CommandNode root = new CommandNode();
private final @NotNull CommandNode root = new CommandNode();
private enum State {
START,

View File

@@ -0,0 +1,148 @@
/*
* 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.ex.handler
import com.intellij.openapi.actionSystem.DataContext
import com.intellij.openapi.actionSystem.PlatformDataKeys
import com.intellij.openapi.editor.Document
import com.intellij.openapi.editor.Editor
import com.intellij.openapi.fileEditor.FileEditorManager
import com.intellij.openapi.project.Project
import com.intellij.openapi.vfs.VirtualFile
import com.maddyhome.idea.vim.VimPlugin
import com.maddyhome.idea.vim.ex.CommandHandler
import com.maddyhome.idea.vim.ex.ExCommand
import com.maddyhome.idea.vim.ex.ExOutputModel
import com.maddyhome.idea.vim.ex.flags
import com.maddyhome.idea.vim.helper.EditorHelper
import java.io.File
/**
* Handles buffers, files, ls command. Supports +, =, a, %, # filters.
*
* @author John Weigel
*/
class BufferListHandler : CommandHandler.SingleExecution() {
override val argFlags = flags(RangeFlag.RANGE_FORBIDDEN, ArgumentFlag.ARGUMENT_OPTIONAL, Access.READ_ONLY)
companion object {
const val FILE_NAME_PAD = 30
val SUPPORTED_FILTERS = setOf('+', '=', 'a', '%', '#')
}
override fun execute(editor: Editor, context: DataContext, cmd: ExCommand): Boolean {
val arg = cmd.argument.trim()
val filter = pruneUnsupportedFilters(arg)
val bufferList = getBufferList(context, filter)
ExOutputModel.getInstance(editor).output(bufferList.joinToString(separator = "\n"))
return true
}
private fun pruneUnsupportedFilters(filter: String) = filter.filter { it in SUPPORTED_FILTERS }
private fun getBufferList(context: DataContext, filter: String): List<String> {
val bufferList = mutableListOf<String>()
val project = PlatformDataKeys.PROJECT.getData(context) ?: return emptyList()
val fem = FileEditorManager.getInstance(project)
val openFiles = fem.openFiles
val bufNumPad = openFiles.size.toString().length
val currentFile = fem.selectedFiles[0]
val previousFile = VimPlugin.getFile().getPreviousTab(context)
val virtualFileDisplayMap = buildVirtualFileDisplayMap(project)
var index = 1
for ((file, displayFileName) in virtualFileDisplayMap) {
val editor = EditorHelper.getEditor(file) ?: continue
val bufStatus = getBufferStatus(editor, file, currentFile, previousFile)
if (bufStatusMatchesFilter(filter, bufStatus)) {
val lineNum = editor.caretModel.currentCaret.logicalPosition.line + 1
val lineNumPad = if (displayFileName.length < FILE_NAME_PAD) (FILE_NAME_PAD - displayFileName.length).toString() else ""
bufferList.add(String.format(
" %${bufNumPad}s %s %s%${lineNumPad}s line: %d", index, bufStatus, displayFileName, "", lineNum)
)
}
index++
}
return bufferList
}
private fun buildVirtualFileDisplayMap(project: Project): Map<VirtualFile, String> {
val openFiles = FileEditorManager.getInstance(project).openFiles
val basePath = if (project.basePath != null) project.basePath + File.separator else ""
val filePaths = mutableMapOf<VirtualFile, String>()
for (file in openFiles) {
val filePath = file.path
// If the file is under the project path, then remove the project base path from the file.
val displayFilePath = if (basePath.isNotEmpty() && filePath.startsWith(basePath)) {
filePath.replace(basePath, "")
} else {
// File is not under the project base path so add the full path.
filePath
}
filePaths[file] = '"' + displayFilePath + '"'
}
return filePaths
}
private fun bufStatusMatchesFilter(filter: String, bufStatus: String) = filter.all { it in bufStatus }
}
private fun getBufferStatus(editor: Editor, file: VirtualFile, currentFile: VirtualFile, previousFile: VirtualFile?): String {
val bufStatus = StringBuilder()
when(file) {
currentFile -> bufStatus.append("%a ")
previousFile -> bufStatus.append("# ")
else -> bufStatus.append(" ")
}
if (!file.isWritable) {
bufStatus.setCharAt(2, '=')
}
if (isDocumentDirty(editor.document)) {
bufStatus.setCharAt(3, '+')
}
return bufStatus.toString()
}
private fun isDocumentDirty(document: Document): Boolean {
var line = 0
while (line < document.lineCount) {
if (document.isLineModified(line)) {
return true
}
line++
}
return false
}

View File

@@ -34,6 +34,7 @@ import com.maddyhome.idea.vim.ex.handler.MapHandler.SpecialArgument.EXPR
import com.maddyhome.idea.vim.ex.handler.MapHandler.SpecialArgument.SCRIPT
import com.maddyhome.idea.vim.ex.vimscript.VimScriptCommandHandler
import com.maddyhome.idea.vim.helper.StringHelper.parseKeys
import com.maddyhome.idea.vim.key.MappingOwner
import java.util.*
import javax.swing.KeyStroke
@@ -75,8 +76,7 @@ class MapHandler : CommandHandler.SingleExecution(), VimScriptCommandHandler, Co
throw ExException("Unsupported map argument: $unsupportedArgument")
}
}
VimPlugin.getKey().putKeyMapping(modes, arguments.fromKeys, arguments.toKeys, null,
commandInfo.isRecursive)
VimPlugin.getKey().putKeyMapping(modes, arguments.fromKeys, MappingOwner.IdeaVim, arguments.toKeys, commandInfo.isRecursive)
return true
}
}

View File

@@ -33,13 +33,11 @@ public class VimScriptGlobalEnvironment {
private VimScriptGlobalEnvironment() {}
@NotNull
public static VimScriptGlobalEnvironment getInstance() {
public static @NotNull VimScriptGlobalEnvironment getInstance() {
return ourInstance;
}
@NotNull
public Map<String, Object> getVariables() {
public @NotNull Map<String, Object> getVariables() {
return myVariables;
}
}

View File

@@ -52,8 +52,7 @@ public class VimScriptParser {
private VimScriptParser() {
}
@Nullable
public static File findIdeaVimRc() {
public static @Nullable File findIdeaVimRc() {
final String homeDirName = System.getProperty("user.home");
// Check whether file exists in home dir
if (homeDirName != null) {
@@ -82,8 +81,7 @@ public class VimScriptParser {
return null;
}
@Nullable
public static File findOrCreateIdeaVimRc() {
public static @Nullable File findOrCreateIdeaVimRc() {
final File found = findIdeaVimRc();
if (found != null) return found;
@@ -137,8 +135,7 @@ public class VimScriptParser {
}
}
@NotNull
public static Object evaluate(@NotNull String expression, @NotNull Map<String, Object> globals) throws ExException {
public static @NotNull Object evaluate(@NotNull String expression, @NotNull Map<String, Object> globals) throws ExException {
// This evaluator is very basic, no proper parsing whatsoever. It is here as the very first step necessary to
// support mapleader, VIM-650. See also VIM-669.
Matcher m;
@@ -168,8 +165,7 @@ public class VimScriptParser {
throw new ExException(String.format("Invalid expression: %s", expression));
}
@NotNull
public static String expressionToString(@NotNull Object value) throws ExException {
public static @NotNull String expressionToString(@NotNull Object value) throws ExException {
// TODO: Return meaningful value representations
if (value instanceof String) {
return (String)value;
@@ -179,8 +175,7 @@ public class VimScriptParser {
throw new ExException(String.format("Cannot convert '%s' to string", value));
}
@NotNull
private static String readFile(@NotNull File file) throws IOException {
private static @NotNull String readFile(@NotNull File file) throws IOException {
final BufferedReader reader = new BufferedReader(new FileReader(file));
final StringBuilder builder = new StringBuilder();
final char[] buffer = new char[BUFSIZE];

View File

@@ -19,6 +19,8 @@
package com.maddyhome.idea.vim.extension;
import com.intellij.openapi.extensions.ExtensionPointName;
import com.maddyhome.idea.vim.VimPlugin;
import com.maddyhome.idea.vim.key.MappingOwner;
import org.jetbrains.annotations.NotNull;
/**
@@ -30,7 +32,13 @@ public interface VimExtension {
@NotNull
String getName();
default MappingOwner getOwner() {
return MappingOwner.Plugin.Companion.get(getName());
}
void init();
void dispose();
default void dispose() {
VimPlugin.getKey().removeKeyMapping(getOwner());
};
}

View File

@@ -27,9 +27,11 @@ import com.maddyhome.idea.vim.helper.EditorDataContext
import com.maddyhome.idea.vim.helper.StringHelper
import com.maddyhome.idea.vim.helper.TestInputModel
import com.maddyhome.idea.vim.helper.commandState
import com.maddyhome.idea.vim.key.MappingOwner
import com.maddyhome.idea.vim.key.OperatorFunction
import com.maddyhome.idea.vim.ui.ExEntryPanel
import com.maddyhome.idea.vim.ui.ModalEntry
import org.jetbrains.annotations.ApiStatus.ScheduledForRemoval
import java.awt.event.KeyEvent
import javax.swing.KeyStroke
@@ -41,18 +43,34 @@ import javax.swing.KeyStroke
* @author vlan
*/
object VimExtensionFacade {
/** The 'map' command for mapping keys to handlers defined in extensions. */
@JvmStatic
@ScheduledForRemoval(inVersion = "0.57")
@Deprecated("Only for EasyMotion support")
fun putExtensionHandlerMapping(modes: Set<MappingMode>, fromKeys: List<KeyStroke>, extensionHandler: VimExtensionHandler, recursive: Boolean) {
VimPlugin.getKey().putKeyMapping(modes, fromKeys, MappingOwner.Plugin.get("easymotion"), extensionHandler, recursive)
}
@ScheduledForRemoval(inVersion = "0.57")
@Deprecated("Only for EasyMotion support")
@JvmStatic
fun putKeyMapping(modes: Set<MappingMode>, fromKeys: List<KeyStroke>,
toKeys: List<KeyStroke>, recursive: Boolean) {
VimPlugin.getKey().putKeyMapping(modes, fromKeys, MappingOwner.Plugin.get("easymotion"), toKeys, recursive)
}
/** The 'map' command for mapping keys to handlers defined in extensions. */
@JvmStatic
fun putExtensionHandlerMapping(modes: Set<MappingMode>, fromKeys: List<KeyStroke>,
extensionHandler: VimExtensionHandler, recursive: Boolean) {
VimPlugin.getKey().putKeyMapping(modes, fromKeys, null, extensionHandler, recursive)
pluginOwner: MappingOwner, extensionHandler: VimExtensionHandler, recursive: Boolean) {
VimPlugin.getKey().putKeyMapping(modes, fromKeys, pluginOwner, extensionHandler, recursive)
}
/** The 'map' command for mapping keys to other keys. */
@JvmStatic
fun putKeyMapping(modes: Set<MappingMode>, fromKeys: List<KeyStroke>,
toKeys: List<KeyStroke>, recursive: Boolean) {
VimPlugin.getKey().putKeyMapping(modes, fromKeys, toKeys, null, recursive)
pluginOwner: MappingOwner, toKeys: List<KeyStroke>, recursive: Boolean) {
VimPlugin.getKey().putKeyMapping(modes, fromKeys, pluginOwner, toKeys, recursive)
}
/** Sets the value of 'operatorfunc' to be used as the operator function in 'g@'. */
@@ -78,7 +96,8 @@ object VimExtensionFacade {
fun inputKeyStroke(editor: Editor): KeyStroke {
if (editor.commandState.isDotRepeatInProgress) {
val input = VimRepeater.Extension.consumeKeystroke()
return input ?: throw RuntimeException("Not enough keystrokes saved: ${VimRepeater.Extension.lastExtensionHandler}")
return input
?: throw RuntimeException("Not enough keystrokes saved: ${VimRepeater.Extension.lastExtensionHandler}")
}
val key: KeyStroke? = if (ApplicationManager.getApplication().isUnitTestMode) {

View File

@@ -21,6 +21,7 @@ package com.maddyhome.idea.vim.extension;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.extensions.ExtensionPointListener;
import com.intellij.openapi.extensions.PluginDescriptor;
import com.maddyhome.idea.vim.key.MappingOwner;
import com.maddyhome.idea.vim.option.OptionsManager;
import com.maddyhome.idea.vim.option.ToggleOption;
import org.jetbrains.annotations.NotNull;
@@ -34,24 +35,32 @@ import java.util.Set;
*/
public class VimExtensionRegistrar {
private static Set<String> registeredExtensions = new HashSet<>();
private static final Set<String> registeredExtensions = new HashSet<>();
private static boolean extensionRegistered = false;
private static final Logger logger = Logger.getInstance(VimExtensionRegistrar.class);
public static void registerExtensions() {
if (extensionRegistered) return;
extensionRegistered = true;
// TODO: [VERSION UPDATE] since 191 use
// ExtensionPoint.addExtensionPointListener(ExtensionPointListener<T>, boolean, Disposable)
//noinspection deprecation
VimExtension.EP_NAME.getPoint(null).addExtensionPointListener(new ExtensionPointListener<VimExtension>() {
@Override
public void extensionAdded(@NotNull VimExtension extension, @NotNull PluginDescriptor pluginDescriptor) {
public void extensionAdded(@NotNull VimExtension extension, PluginDescriptor pluginDescriptor) {
registerExtension(extension);
}
@Override
public void extensionRemoved(@NotNull VimExtension extension, PluginDescriptor pluginDescriptor) {
unregisterExtension(extension);
}
});
}
synchronized private static void registerExtension(@NotNull VimExtension extension) {
private static synchronized void registerExtension(@NotNull VimExtension extension) {
String name = extension.getName();
if (registeredExtensions.contains(name)) return;
@@ -74,5 +83,15 @@ public class VimExtensionRegistrar {
OptionsManager.INSTANCE.addOption(option);
}
private static Logger logger = Logger.getInstance(VimExtensionRegistrar.class);
private static synchronized void unregisterExtension(@NotNull VimExtension extension) {
String name = extension.getName();
if (!registeredExtensions.contains(name)) return;
registeredExtensions.remove(name);
extension.dispose();
OptionsManager.INSTANCE.removeOption(name);
MappingOwner.Plugin.Companion.remove(name);
logger.info("IdeaVim extension '" + name + "' disposed");
}
}

View File

@@ -18,29 +18,18 @@
package com.maddyhome.idea.vim.extension;
import com.intellij.openapi.application.ApplicationManager;
import org.jetbrains.annotations.ApiStatus;
/**
* @author vlan
*/
public abstract class VimNonDisposableExtension implements VimExtension {
private boolean myInitialized = false;
@ApiStatus.ScheduledForRemoval(inVersion = "0.57")
@Deprecated
public abstract class VimNonDisposableExtension implements VimExtension {
@Override
public final void init() {
if (ApplicationManager.getApplication().isUnitTestMode()) {
initOnce();
}
else {
if (!myInitialized) {
myInitialized = true;
initOnce();
}
}
}
@Override
public final void dispose() {
initOnce();
}
protected abstract void initOnce();

View File

@@ -0,0 +1,614 @@
package com.maddyhome.idea.vim.extension.argtextobj;
import com.intellij.openapi.actionSystem.DataContext;
import com.intellij.openapi.editor.Caret;
import com.intellij.openapi.editor.Document;
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.extension.VimExtension;
import com.maddyhome.idea.vim.extension.VimExtensionHandler;
import com.maddyhome.idea.vim.handler.TextObjectActionHandler;
import com.maddyhome.idea.vim.listener.SelectionVimListenerSuppressor;
import com.maddyhome.idea.vim.listener.VimListenerSuppressor;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.EnumSet;
import java.util.Stack;
import static com.maddyhome.idea.vim.extension.VimExtensionFacade.putExtensionHandlerMapping;
import static com.maddyhome.idea.vim.extension.VimExtensionFacade.putKeyMapping;
import static com.maddyhome.idea.vim.group.visual.VisualGroupKt.vimSetSelection;
import static com.maddyhome.idea.vim.helper.StringHelper.parseKeys;
import static java.util.Collections.emptyList;
/**
* @author igrekster
*/
public class VimArgTextObjExtension implements VimExtension {
@Override
public @NotNull String getName() {
return "argtextobj";
}
@Override
public void init() {
putExtensionHandlerMapping(MappingMode.XO, parseKeys("<Plug>InnerArgument"), getOwner(), new VimArgTextObjExtension.ArgumentHandler(true), false);
putExtensionHandlerMapping(MappingMode.XO, parseKeys("<Plug>OuterArgument"), getOwner(), new VimArgTextObjExtension.ArgumentHandler(false), false);
putKeyMapping(MappingMode.XO, parseKeys("ia"), getOwner(), parseKeys("<Plug>InnerArgument"), true);
putKeyMapping(MappingMode.XO, parseKeys("aa"), getOwner(), parseKeys("<Plug>OuterArgument"), true);
}
/**
* A text object for an argument to a function definition or a call.
*/
static class ArgumentHandler implements VimExtensionHandler {
final boolean isInner;
ArgumentHandler(boolean isInner) {
super();
this.isInner = isInner;
}
static class ArgumentTextObjectHandler extends TextObjectActionHandler {
private final boolean isInner;
ArgumentTextObjectHandler(boolean isInner) {
this.isInner = isInner;
}
@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());
int pos = caret.getOffset();
for (int i = 0; i < count; ++i) {
if (!finder.findBoundsAt(pos)) {
VimPlugin.showMessage(finder.errorMessage());
VimPlugin.indicateError();
return null;
}
if (i + 1 < count) {
finder.extendTillNext();
}
pos = finder.getRightBound();
}
if (isInner) {
finder.adjustForInner();
} else {
finder.adjustForOuter();
}
return new TextRange(finder.getLeftBound(), finder.getRightBound());
}
}
@Override
public void execute(@NotNull Editor editor, @NotNull DataContext context) {
@NotNull CommandState commandState = CommandState.getInstance(editor);
int count = Math.max(1, commandState.getCommandBuilder().getCount());
final ArgumentTextObjectHandler textObjectHandler = new ArgumentTextObjectHandler(isInner);
if (!commandState.isOperatorPending()) {
editor.getCaretModel().runForEachCaret((Caret caret) -> {
final TextRange range = textObjectHandler.getRange(editor, caret, context, count, 0, null);
if (range != null) {
try (VimListenerSuppressor.Locked ignored = SelectionVimListenerSuppressor.INSTANCE.lock()) {
if (commandState.getMode() == CommandState.Mode.VISUAL) {
vimSetSelection(caret, range.getStartOffset(), range.getEndOffset() - 1, true);
} else {
caret.moveToOffset(range.getStartOffset());
}
}
}
});
} else {
commandState.getCommandBuilder().completeCommandPart(new Argument(new Command(count,
textObjectHandler, Command.Type.MOTION, EnumSet.of(CommandFlags.FLAG_MOT_CHARACTERWISE),
emptyList()
)));
}
}
}
/**
* Helper class to find argument boundaries starting at the specified
* position
*/
private static class ArgBoundsFinder {
private final CharSequence text;
private final Document document;
private int leftBound = Integer.MAX_VALUE;
private int rightBound = Integer.MIN_VALUE;
private int leftBracket;
private int rightBracket;
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) {
this.text = document.getImmutableCharSequence();
this.document = document;
}
/**
* Finds left and right boundaries of an argument at the specified
* position. If successful @ref getLeftBound() will point to the left
* argument delimiter and @ref getRightBound() will point to the right
* argument delimiter. Use @ref adjustForInner or @ref adjustForOuter to
* fix the boundaries based on the type of text object.
*
* @param position starting position.
*/
boolean findBoundsAt(int position) throws IllegalStateException {
if (text.length() == 0) {
error = "empty document";
return false;
}
leftBound = Math.min(position, leftBound);
rightBound = Math.max(position, rightBound);
getOutOfQuotedText();
if (rightBound == leftBound) {
if (isCloseBracket(getCharAt(rightBound))) {
--leftBound;
} else {
++rightBound;
}
}
int nextLeft = leftBound;
int nextRight = rightBound;
final int leftLimit = leftLimit(position);
final int rightLimit = rightLimit(position);
//
// Try to extend the bounds until one of the bounds is a comma.
// This handles cases like: fun(a, (30 + <cursor>x) * 20, c)
//
boolean bothBrackets;
do {
leftBracket = nextLeft;
rightBracket = nextRight;
if (!findOuterBrackets(leftLimit, rightLimit)) {
error = "not inside argument list";
return false;
}
leftBound = nextLeft;
findLeftBound();
nextLeft = leftBound - 1;
rightBound = nextRight;
findRightBound();
nextRight = rightBound + 1;
//
// If reached text boundaries or there is nothing between delimiters.
//
if (nextLeft < leftLimit || nextRight > rightLimit || (rightBound - leftBound) == 1) {
error = "not an argument";
return false;
}
bothBrackets = getCharAt(leftBound) != ',' && getCharAt(rightBound) != ',';
if (bothBrackets && isIdentPreceding()) {
// Looking at a pair of brackets preceded by an
// identifier -- single argument function call.
break;
}
}
while (leftBound > leftLimit && rightBound < rightLimit && bothBrackets);
return true;
}
/**
* Skip left delimiter character and any following whitespace.
*/
void adjustForInner() {
++leftBound;
while (leftBound < rightBound && Character.isWhitespace(getCharAt(leftBound))) {
++leftBound;
}
}
/**
* Exclude left bound character for the first argument, include the
* right bound character and any following whitespace.
*/
void adjustForOuter() {
if (getCharAt(leftBound) != ',') {
++leftBound;
extendTillNext();
}
}
/**
* Extend the right bound to the beginning of the next argument (if any).
*/
void extendTillNext() {
if (rightBound + 1 < rightBracket && getCharAt(rightBound) == ',') {
++rightBound;
while (rightBound + 1 < rightBracket && Character.isWhitespace(getCharAt(rightBound))) {
++rightBound;
}
}
}
int getLeftBound() {
return leftBound;
}
int getRightBound() {
return rightBound;
}
private boolean isIdentPreceding() {
int i = leftBound - 1;
final int idEnd = i;
while (i > 0 && Character.isJavaIdentifierPart(getCharAt(i))) {
--i;
}
return (idEnd - i) > 0 && Character.isJavaIdentifierStart(getCharAt(i + 1));
}
/**
* Detects if current position is inside a quoted string and adjusts
* left and right bounds to the boundaries of the string.
*
* NOTE: Does not support line continuations for quoted string ('\' at the end of line).
*/
private void getOutOfQuotedText() {
// TODO this method should use IdeaVim methods to determine if the current position is in the string
final int lineNo = document.getLineNumber(leftBound);
final int lineStartOffset = document.getLineStartOffset(lineNo);
final int lineEndOffset = document.getLineEndOffset(lineNo);
int i = lineStartOffset;
while (i <= leftBound) {
if (isQuote(i)) {
final int endOfQuotedText = skipQuotedTextForward(i, lineEndOffset);
if (endOfQuotedText >= leftBound) {
leftBound = i - 1;
rightBound = endOfQuotedText + 1;
break;
} else {
i = endOfQuotedText;
}
}
++i;
}
}
private void findRightBound() {
while (rightBound < rightBracket) {
final char ch = getCharAt(rightBound);
if (ch == ',') {
break;
}
if (isOpenBracket(ch)) {
rightBound = skipSexp(rightBound, rightBracket, SexpDirection.FORWARD);
} else {
if (isQuoteChar(ch)) {
rightBound = skipQuotedTextForward(rightBound, rightBracket);
}
++rightBound;
}
}
}
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);
} else {
if (isQuoteChar(ch)) {
leftBound = skipQuotedTextBackward(leftBound, leftBracket);
}
--leftBound;
}
}
}
private boolean isQuote(final int i) {
return QUOTES.indexOf(getCharAt(i)) != -1;
}
private static boolean isQuoteChar(final int ch) {
return QUOTES.indexOf(ch) != -1;
}
private char getCharAt(int logicalOffset) {
assert logicalOffset < text.length();
return text.charAt(logicalOffset);
}
private int skipQuotedTextForward(final int start, final int end) {
assert start < end;
final char quoteChar = getCharAt(start);
boolean backSlash = false;
int i = start + 1;
while (i <= end) {
final char ch = getCharAt(i);
if (ch == quoteChar && !backSlash) {
// Found matching quote and it's not escaped.
break;
} else {
backSlash = ch == '\\' && !backSlash;
}
++i;
}
return i;
}
private int skipQuotedTextBackward(final int start, final int end) {
assert start > end;
final char quoteChar = getCharAt(start);
int i = start - 1;
while (i > end) {
final char ch = getCharAt(i);
final char prevChar = getCharAt(i - 1);
// 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.
break;
}
--i;
}
return i;
}
private int leftLimit(final int pos) {
final int offsetLimit = Math.max(pos - MAX_SEARCH_OFFSET, 0);
final int lineNo = document.getLineNumber(pos);
final int lineOffsetLimit = document.getLineStartOffset(Math.max(0, lineNo - MAX_SEARCH_LINES));
return Math.max(offsetLimit, lineOffsetLimit);
}
private int rightLimit(final int pos) {
final int offsetLimit = Math.min(pos + MAX_SEARCH_OFFSET, text.length());
final int lineNo = document.getLineNumber(pos);
final int lineOffsetLimit = document.getLineEndOffset(Math.min(document.getLineCount() - 1, lineNo + MAX_SEARCH_LINES));
return Math.min(offsetLimit, lineOffsetLimit);
}
String errorMessage() {
return error;
}
/**
* Interface to parametrise S-expression traversal direction.
*/
abstract static class SexpDirection {
abstract int delta();
abstract boolean isOpenBracket(char ch);
abstract boolean isCloseBracket(char ch);
abstract int skipQuotedText(int pos, int end, ArgBoundsFinder self);
static final SexpDirection FORWARD = new SexpDirection() {
@Override
int delta() {
return 1;
}
@Override
boolean isOpenBracket(char ch) {
return ArgBoundsFinder.isOpenBracket(ch);
}
@Override
boolean isCloseBracket(char ch) {
return ArgBoundsFinder.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
boolean isOpenBracket(char ch) {
return ArgBoundsFinder.isCloseBracket(ch);
}
@Override
boolean isCloseBracket(char ch) {
return ArgBoundsFinder.isOpenBracket(ch);
}
@Override
int skipQuotedText(int pos, int end, ArgBoundsFinder self) {
return self.skipQuotedTextBackward(pos, end);
}
};
}
/**
* Skip over an S-expression considering priorities when unbalanced.
*
* @param start position of the starting bracket.
* @param end maximum position
* @param dir direction instance
* @return position after the S-expression, or the next to the start position if
* unbalanced.
*/
private int skipSexp(final int start, final int end, SexpDirection dir) {
char lastChar = getCharAt(start);
assert dir.isOpenBracket(lastChar);
Stack<Character> bracketStack = new Stack<>();
bracketStack.push(lastChar);
int i = start + dir.delta();
while (!bracketStack.empty() && i != end) {
final char ch = getCharAt(i);
if (dir.isOpenBracket(ch)) {
bracketStack.push(ch);
} else {
if (dir.isCloseBracket(ch)) {
if (bracketStack.lastElement() == matchingBracket(ch)) {
bracketStack.pop();
} else {
//noinspection StatementWithEmptyBody
if (getBracketPrio(ch) < getBracketPrio(bracketStack.lastElement())) {
// (<...) -> (...)
bracketStack.pop();
// Retry the same character again for cases like (...<<...).
continue;
} else { // Unbalanced brackets -- check ranking.
// Ignore lower-priority closing brackets.
// (...> -> (....
}
}
} else {
if (isQuoteChar(ch)) {
i = dir.skipQuotedText(i, end, this);
}
}
}
i += dir.delta();
}
if (bracketStack.empty()) {
return i;
} else {
return start + dir.delta();
}
}
/**
* @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.
*
* @param start minimum position to look for
* @param end maximum position
* @return true if found
*/
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));
if (leftPrio == rightPrio) {
// matching brackets
return true;
} else {
if (leftPrio < rightPrio) {
if (rightBracket + 1 < end) {
++rightBracket;
hasNewBracket = findNextCloseBracket(end);
} else {
hasNewBracket = false;
}
} else {
if (leftBracket > 1) {
--leftBracket;
hasNewBracket = findPrevOpenBracket(start);
} else {
hasNewBracket = false;
}
}
}
}
return false;
}
/**
* Finds unmatched open bracket starting at @a leftBracket.
*
* @param start minimum position.
* @return true if found
*/
private boolean findPrevOpenBracket(final int start) {
char ch;
while (!isOpenBracket(ch = getCharAt(leftBracket))) {
if (isCloseBracket(ch)) {
leftBracket = skipSexp(leftBracket, start, SexpDirection.BACKWARD);
} else {
if (isQuoteChar(ch)) {
leftBracket = skipQuotedTextBackward(leftBracket, start);
} else {
if (leftBracket == start) {
return false;
}
}
--leftBracket;
}
}
return true;
}
/**
* Finds unmatched close bracket starting at @a rightBracket.
*
* @param end maximum position.
* @return true if found
*/
private boolean findNextCloseBracket(final int end) {
char ch;
while (!isCloseBracket(ch = getCharAt(rightBracket))) {
if (isOpenBracket(ch)) {
rightBracket = skipSexp(rightBracket, end, SexpDirection.FORWARD);
} else {
if (isQuoteChar(ch)) {
rightBracket = skipQuotedTextForward(rightBracket, end);
}
++rightBracket;
}
if (rightBracket >= end) {
return false;
}
}
return true;
}
}
}

View File

@@ -33,8 +33,8 @@ 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.common.TextRange;
import com.maddyhome.idea.vim.extension.VimExtension;
import com.maddyhome.idea.vim.extension.VimExtensionHandler;
import com.maddyhome.idea.vim.extension.VimNonDisposableExtension;
import com.maddyhome.idea.vim.key.OperatorFunction;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -45,23 +45,22 @@ import static com.maddyhome.idea.vim.helper.StringHelper.parseKeys;
/**
* @author dhleong
*/
public class CommentaryExtension extends VimNonDisposableExtension {
public class CommentaryExtension implements VimExtension {
@NotNull
@Override
public String getName() {
public @NotNull String getName() {
return "commentary";
}
@Override
protected void initOnce() {
putExtensionHandlerMapping(MappingMode.N, parseKeys("<Plug>(CommentMotion)"), new CommentMotionHandler(), false);
putExtensionHandlerMapping(MappingMode.N, parseKeys("<Plug>(CommentLine)"), new CommentLineHandler(), false);
putExtensionHandlerMapping(MappingMode.XO, parseKeys("<Plug>(CommentMotionV)"), new CommentMotionVHandler(), false);
public void init() {
putExtensionHandlerMapping(MappingMode.N, parseKeys("<Plug>(CommentMotion)"), getOwner(), new CommentMotionHandler(), false);
putExtensionHandlerMapping(MappingMode.N, parseKeys("<Plug>(CommentLine)"), getOwner(), new CommentLineHandler(), false);
putExtensionHandlerMapping(MappingMode.XO, parseKeys("<Plug>(CommentMotionV)"), getOwner(), new CommentMotionVHandler(), false);
putKeyMapping(MappingMode.N, parseKeys("gc"), parseKeys("<Plug>(CommentMotion)"), true);
putKeyMapping(MappingMode.N, parseKeys("gcc"), parseKeys("<Plug>(CommentLine)"), true);
putKeyMapping(MappingMode.XO, parseKeys("gc"), parseKeys("<Plug>(CommentMotionV)"), true);
putKeyMapping(MappingMode.N, parseKeys("gc"), getOwner(), parseKeys("<Plug>(CommentMotion)"), true);
putKeyMapping(MappingMode.N, parseKeys("gcc"), getOwner(), parseKeys("<Plug>(CommentLine)"), true);
putKeyMapping(MappingMode.XO, parseKeys("gc"), getOwner(), parseKeys("<Plug>(CommentMotionV)"), true);
}
private static class CommentMotionHandler implements VimExtensionHandler {
@@ -135,8 +134,7 @@ public class CommentaryExtension extends VimNonDisposableExtension {
});
}
@Nullable
private TextRange getCommentRange(@NotNull Editor editor) {
private @Nullable TextRange getCommentRange(@NotNull Editor editor) {
final CommandState.Mode mode = CommandState.getInstance(editor).getMode();
switch (mode) {
case COMMAND:

View File

@@ -27,10 +27,10 @@ import com.maddyhome.idea.vim.VimPlugin
import com.maddyhome.idea.vim.command.CommandFlags
import com.maddyhome.idea.vim.command.MappingMode
import com.maddyhome.idea.vim.common.TextRange
import com.maddyhome.idea.vim.extension.VimExtension
import com.maddyhome.idea.vim.extension.VimExtensionFacade.putExtensionHandlerMapping
import com.maddyhome.idea.vim.extension.VimExtensionFacade.putKeyMapping
import com.maddyhome.idea.vim.extension.VimExtensionHandler
import com.maddyhome.idea.vim.extension.VimNonDisposableExtension
import com.maddyhome.idea.vim.group.MotionGroup
import com.maddyhome.idea.vim.group.visual.vimSetSelection
import com.maddyhome.idea.vim.helper.EditorHelper
@@ -56,21 +56,21 @@ private const val ALL_OCCURRENCES = "<Plug>AllOccurrences"
*
* See https://github.com/terryma/vim-multiple-cursors
* */
class VimMultipleCursorsExtension : VimNonDisposableExtension() {
class VimMultipleCursorsExtension : VimExtension {
override fun getName() = "multiple-cursors"
override fun initOnce() {
putExtensionHandlerMapping(MappingMode.NXO, parseKeys(NEXT_WHOLE_OCCURRENCE), NextOccurrenceHandler(), false)
putExtensionHandlerMapping(MappingMode.NXO, parseKeys(NEXT_OCCURRENCE), NextOccurrenceHandler(whole = false), false)
putExtensionHandlerMapping(MappingMode.NXO, parseKeys(ALL_WHOLE_OCCURRENCES), AllOccurrencesHandler(), false)
putExtensionHandlerMapping(MappingMode.NXO, parseKeys(ALL_OCCURRENCES), AllOccurrencesHandler(whole = false), false)
putExtensionHandlerMapping(MappingMode.X, parseKeys(SKIP_OCCURRENCE), SkipOccurrenceHandler(), false)
putExtensionHandlerMapping(MappingMode.X, parseKeys(REMOVE_OCCURRENCE), RemoveOccurrenceHandler(), false)
override fun init() {
putExtensionHandlerMapping(MappingMode.NXO, parseKeys(NEXT_WHOLE_OCCURRENCE), owner, NextOccurrenceHandler(), false)
putExtensionHandlerMapping(MappingMode.NXO, parseKeys(NEXT_OCCURRENCE), owner, NextOccurrenceHandler(whole = false), false)
putExtensionHandlerMapping(MappingMode.NXO, parseKeys(ALL_WHOLE_OCCURRENCES), owner, AllOccurrencesHandler(), false )
putExtensionHandlerMapping(MappingMode.NXO, parseKeys(ALL_OCCURRENCES), owner, AllOccurrencesHandler(whole = false), false )
putExtensionHandlerMapping(MappingMode.X, parseKeys(SKIP_OCCURRENCE), owner, SkipOccurrenceHandler(), false )
putExtensionHandlerMapping(MappingMode.X, parseKeys(REMOVE_OCCURRENCE), owner, RemoveOccurrenceHandler(), false )
putKeyMapping(MappingMode.NXO, parseKeys("<A-n>"), parseKeys(NEXT_WHOLE_OCCURRENCE), true)
putKeyMapping(MappingMode.NXO, parseKeys("g<A-n>"), parseKeys(NEXT_OCCURRENCE), true)
putKeyMapping(MappingMode.X, parseKeys("<A-x>"), parseKeys(SKIP_OCCURRENCE), true)
putKeyMapping(MappingMode.X, parseKeys("<A-p>"), parseKeys(REMOVE_OCCURRENCE), true)
putKeyMapping(MappingMode.NXO, parseKeys("<A-n>"), owner, parseKeys(NEXT_WHOLE_OCCURRENCE), true)
putKeyMapping(MappingMode.NXO, parseKeys("g<A-n>"), owner, parseKeys(NEXT_OCCURRENCE), true)
putKeyMapping(MappingMode.X, parseKeys("<A-x>"), owner, parseKeys(SKIP_OCCURRENCE), true)
putKeyMapping(MappingMode.X, parseKeys("<A-p>"), owner, parseKeys(REMOVE_OCCURRENCE), true)
}
abstract class WriteActionHandler : VimExtensionHandler {

View File

@@ -25,6 +25,7 @@ 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.common.TextRange
import com.maddyhome.idea.vim.extension.VimExtension
import com.maddyhome.idea.vim.extension.VimExtensionFacade.executeNormal
import com.maddyhome.idea.vim.extension.VimExtensionFacade.getRegister
import com.maddyhome.idea.vim.extension.VimExtensionFacade.inputKeyStroke
@@ -34,7 +35,6 @@ import com.maddyhome.idea.vim.extension.VimExtensionFacade.putKeyMapping
import com.maddyhome.idea.vim.extension.VimExtensionFacade.setOperatorFunction
import com.maddyhome.idea.vim.extension.VimExtensionFacade.setRegister
import com.maddyhome.idea.vim.extension.VimExtensionHandler
import com.maddyhome.idea.vim.extension.VimNonDisposableExtension
import com.maddyhome.idea.vim.helper.EditorHelper
import com.maddyhome.idea.vim.helper.StringHelper
import com.maddyhome.idea.vim.helper.mode
@@ -51,19 +51,19 @@ import javax.swing.KeyStroke
* @author dhleong
* @author vlan
*/
class VimSurroundExtension : VimNonDisposableExtension() {
class VimSurroundExtension : VimExtension {
override fun getName() = "surround"
override fun initOnce() {
putExtensionHandlerMapping(MappingMode.N, StringHelper.parseKeys("<Plug>YSurround"), YSurroundHandler(), false)
putExtensionHandlerMapping(MappingMode.N, StringHelper.parseKeys("<Plug>CSurround"), CSurroundHandler(), false)
putExtensionHandlerMapping(MappingMode.N, StringHelper.parseKeys("<Plug>DSurround"), DSurroundHandler(), false)
putExtensionHandlerMapping(MappingMode.XO, StringHelper.parseKeys("<Plug>VSurround"), VSurroundHandler(), false)
override fun init() {
putExtensionHandlerMapping(MappingMode.N, StringHelper.parseKeys("<Plug>YSurround"), owner, YSurroundHandler(), false)
putExtensionHandlerMapping(MappingMode.N, StringHelper.parseKeys("<Plug>CSurround"), owner, CSurroundHandler(), false)
putExtensionHandlerMapping(MappingMode.N, StringHelper.parseKeys("<Plug>DSurround"), owner, DSurroundHandler(), false)
putExtensionHandlerMapping(MappingMode.XO, StringHelper.parseKeys("<Plug>VSurround"), owner, VSurroundHandler(), false)
putKeyMapping(MappingMode.N, StringHelper.parseKeys("ys"), StringHelper.parseKeys("<Plug>YSurround"), true)
putKeyMapping(MappingMode.N, StringHelper.parseKeys("cs"), StringHelper.parseKeys("<Plug>CSurround"), true)
putKeyMapping(MappingMode.N, StringHelper.parseKeys("ds"), StringHelper.parseKeys("<Plug>DSurround"), true)
putKeyMapping(MappingMode.XO, StringHelper.parseKeys("S"), StringHelper.parseKeys("<Plug>VSurround"), true)
putKeyMapping(MappingMode.N, StringHelper.parseKeys("ys"), owner, StringHelper.parseKeys("<Plug>YSurround"), true)
putKeyMapping(MappingMode.N, StringHelper.parseKeys("cs"), owner, StringHelper.parseKeys("<Plug>CSurround"), true)
putKeyMapping(MappingMode.N, StringHelper.parseKeys("ds"), owner, StringHelper.parseKeys("<Plug>DSurround"), true)
putKeyMapping(MappingMode.XO, StringHelper.parseKeys("S"), owner, StringHelper.parseKeys("<Plug>VSurround"), true)
}
private class YSurroundHandler : VimExtensionHandler {

View File

@@ -0,0 +1,152 @@
/*
* 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.textobjentire;
import com.intellij.openapi.actionSystem.DataContext;
import com.intellij.openapi.editor.Caret;
import com.intellij.openapi.editor.Editor;
import com.maddyhome.idea.vim.command.*;
import com.maddyhome.idea.vim.common.TextRange;
import com.maddyhome.idea.vim.extension.VimExtension;
import com.maddyhome.idea.vim.extension.VimExtensionHandler;
import com.maddyhome.idea.vim.handler.TextObjectActionHandler;
import com.maddyhome.idea.vim.listener.SelectionVimListenerSuppressor;
import com.maddyhome.idea.vim.listener.VimListenerSuppressor;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.EnumSet;
import static com.maddyhome.idea.vim.extension.VimExtensionFacade.putExtensionHandlerMapping;
import static com.maddyhome.idea.vim.extension.VimExtensionFacade.putKeyMapping;
import static com.maddyhome.idea.vim.group.visual.VisualGroupKt.vimSetSelection;
import static com.maddyhome.idea.vim.helper.StringHelper.parseKeys;
import static java.util.Collections.emptyList;
/**
* Port of vim-entire:
* https://github.com/kana/vim-textobj-entire
*
* <p>
* vim-textobj-entire provides two text objects:
* <ul>
* <li>ae targets the entire content of the current buffer.</li>
* <li>ie is similar to ae, but ie does not include leading and trailing empty lines. ie is handy for some situations. For example,</li>
* <ul>
* <li>Paste some text into a new buffer (<C-w>n"*P) -- note that the initial empty line is left as the last line.</li>
* <li>Edit the text (:%s/foo/bar/g etc)</li>
* <li>Then copy the resulting text to another application ("*yie)</li>
* </ul>
* </ul>
*
* See also the reference manual for more details at:
* https://github.com/kana/vim-textobj-entire/blob/master/doc/textobj-entire.txt
*
* @author Alexandre Grison (@agrison)
*/
public class VimTextObjEntireExtension implements VimExtension {
@Override
public @NotNull
String getName() {
return "textobj-entire";
}
@Override
public void init() {
putExtensionHandlerMapping(MappingMode.XO, parseKeys("<Plug>textobj-entire-a"), getOwner(),
new VimTextObjEntireExtension.EntireHandler(false), false);
putExtensionHandlerMapping(MappingMode.XO, parseKeys("<Plug>textobj-entire-i"), getOwner(),
new VimTextObjEntireExtension.EntireHandler(true), false);
putKeyMapping(MappingMode.XO, parseKeys("ae"), getOwner(), parseKeys("<Plug>textobj-entire-a"), true);
putKeyMapping(MappingMode.XO, parseKeys("ie"), getOwner(), parseKeys("<Plug>textobj-entire-i"), true);
}
static class EntireHandler implements VimExtensionHandler {
final boolean ignoreLeadingAndTrailing;
EntireHandler(boolean ignoreLeadingAndTrailing) {
this.ignoreLeadingAndTrailing = ignoreLeadingAndTrailing;
}
static class EntireTextObjectHandler extends TextObjectActionHandler {
final boolean ignoreLeadingAndTrailing;
EntireTextObjectHandler(boolean ignoreLeadingAndTrailing) {
this.ignoreLeadingAndTrailing = ignoreLeadingAndTrailing;
}
@Override
public @Nullable
TextRange getRange(@NotNull Editor editor, @NotNull Caret caret, @NotNull DataContext context,
int count, int rawCount, @Nullable Argument argument) {
int start = 0, end = editor.getDocument().getTextLength();
// for the `ie` text object we don't want leading an trailing spaces
// so we have to scan the document text to find the correct start & end
if (ignoreLeadingAndTrailing) {
String content = editor.getDocument().getText();
for (int i = 0; i < content.length(); ++i) {
if (!Character.isWhitespace(content.charAt(i))) {
start = i;
break;
}
}
for (int i = content.length() - 1; i >= start; --i) {
if (!Character.isWhitespace(content.charAt(i))) {
end = i + 1;
break;
}
}
}
return new TextRange(start, end);
}
}
@Override
public void execute(@NotNull Editor editor, @NotNull DataContext context) {
@NotNull CommandState commandState = CommandState.getInstance(editor);
int count = Math.max(1, commandState.getCommandBuilder().getCount());
final EntireTextObjectHandler textObjectHandler = new EntireTextObjectHandler(ignoreLeadingAndTrailing);
if (!commandState.isOperatorPending()) {
editor.getCaretModel().runForEachCaret((Caret caret) -> {
final TextRange range = textObjectHandler.getRange(editor, caret, context, count, 0, null);
if (range != null) {
try (VimListenerSuppressor.Locked ignored = SelectionVimListenerSuppressor.INSTANCE.lock()) {
if (commandState.getMode() == CommandState.Mode.VISUAL) {
vimSetSelection(caret, range.getStartOffset(), range.getEndOffset() - 1, true);
} else {
caret.moveToOffset(range.getStartOffset());
}
}
}
});
} else {
commandState.getCommandBuilder().completeCommandPart(new Argument(new Command(count,
textObjectHandler, Command.Type.MOTION,
EnumSet.of(CommandFlags.FLAG_MOT_CHARACTERWISE),
emptyList())));
}
}
}
}

View File

@@ -85,7 +85,7 @@ public class ChangeGroup {
private static final String VIM_MOTION_BIG_WORD_END_RIGHT = "VimMotionBigWordEndRightAction";
private static final String VIM_MOTION_CAMEL_END_RIGHT = "VimMotionCamelEndRightAction";
@Nullable private Command lastInsert;
private @Nullable Command lastInsert;
private void setInsertRepeat(int lines, int column, boolean append) {
repeatLines = lines;
@@ -154,7 +154,7 @@ public class ChangeGroup {
*
* @param editor The editor to insert into
*/
public void insertNewLineAbove(@NotNull final Editor editor, @NotNull DataContext context) {
public void insertNewLineAbove(final @NotNull Editor editor, @NotNull DataContext context) {
if (editor.isOneLineMode()) return;
Set<Caret> firstLiners = new HashSet<>();
@@ -214,7 +214,7 @@ public class ChangeGroup {
* @param editor The editor to insert into
* @param context The data context
*/
public void insertNewLineBelow(@NotNull final Editor editor, @NotNull final DataContext context) {
public void insertNewLineBelow(final @NotNull Editor editor, final @NotNull DataContext context) {
if (editor.isOneLineMode()) return;
for (Caret caret : editor.getCaretModel().getAllCarets()) {
@@ -308,7 +308,7 @@ public class ChangeGroup {
return false;
}
@Nullable private DocumentListener documentListener;
private @Nullable DocumentListener documentListener;
/**
* If the cursor is currently after the start of the current insert this deletes all the newly inserted text.
@@ -421,7 +421,7 @@ public class ChangeGroup {
// Workaround for VIM-1546. Another solution is highly appreciated.
public boolean tabAction = false;
@NotNull private final EditorMouseListener listener = new EditorMouseListener() {
private final @NotNull EditorMouseListener listener = new EditorMouseListener() {
@Override
public void mouseClicked(@NotNull EditorMouseEvent event) {
Editor editor = event.getEditor();
@@ -737,9 +737,9 @@ public class ChangeGroup {
* @param key The user entered keystroke
* @param plan the current action plan draft
*/
public void beforeProcessKey(@NotNull final Editor editor,
@NotNull final DataContext context,
@NotNull final KeyStroke key,
public void beforeProcessKey(final @NotNull Editor editor,
final @NotNull DataContext context,
final @NotNull KeyStroke key,
@NotNull ActionPlan plan) {
final TypedActionHandler originalHandler = KeyHandler.getInstance().getOriginalHandler();
@@ -805,9 +805,9 @@ public class ChangeGroup {
* @param key The user entered keystroke
* @return true if this was a regular character, false if not
*/
public boolean processKey(@NotNull final Editor editor,
@NotNull final DataContext context,
@NotNull final KeyStroke key) {
public boolean processKey(final @NotNull Editor editor,
final @NotNull DataContext context,
final @NotNull KeyStroke key) {
if (logger.isDebugEnabled()) {
logger.debug("processKey(" + key + ")");
}
@@ -824,9 +824,9 @@ public class ChangeGroup {
return false;
}
public boolean processKeyInSelectMode(@NotNull final Editor editor,
@NotNull final DataContext context,
@NotNull final KeyStroke key) {
public boolean processKeyInSelectMode(final @NotNull Editor editor,
final @NotNull DataContext context,
final @NotNull KeyStroke key) {
boolean res;
try (VimListenerSuppressor.Locked ignored = SelectionVimListenerSuppressor.INSTANCE.lock()) {
res = processKey(editor, context, key);
@@ -1061,14 +1061,13 @@ public class ChangeGroup {
return true;
}
@Nullable
public Pair<TextRange, SelectionType> getDeleteRangeAndType(@NotNull Editor editor,
@NotNull Caret caret,
@NotNull DataContext context,
int count,
int rawCount,
@NotNull final Argument argument,
boolean isChange) {
public @Nullable Pair<TextRange, SelectionType> getDeleteRangeAndType(@NotNull Editor editor,
@NotNull Caret caret,
@NotNull DataContext context,
int count,
int rawCount,
final @NotNull Argument argument,
boolean isChange) {
final TextRange range = MotionGroup.getMotionRange(editor, caret, context, count, rawCount, argument);
if (range == null) return null;
@@ -1163,7 +1162,8 @@ public class ChangeGroup {
final LogicalPosition lp =
editor.offsetToLogicalPosition(VimPlugin.getMotion().moveCaretToLineStartSkipLeading(editor, caret));
if (editor.getDocument().getText().isEmpty()) {
// Please don't use `getDocument().getText().isEmpty()`
if (editor.getDocument().getTextLength() == 0) {
insertBeforeCursor(editor, context);
return true;
}
@@ -1456,7 +1456,8 @@ public class ChangeGroup {
boolean res = deleteRange(editor, caret, range, type, true);
if (res) {
if (type == SelectionType.LINE_WISE) {
if (editor.getDocument().getText().isEmpty()) {
// Please don't use `getDocument().getText().isEmpty()`
if (editor.getDocument().getTextLength() == 0) {
insertBeforeCursor(editor, context);
}
else if (after) {
@@ -1729,8 +1730,8 @@ public class ChangeGroup {
* @param type The type of deletion
* @return true if able to delete the text, false if not
*/
private boolean deleteText(@NotNull final Editor editor,
@NotNull final TextRange range,
private boolean deleteText(final @NotNull Editor editor,
final @NotNull TextRange range,
@Nullable SelectionType type) {
// Fix for https://youtrack.jetbrains.net/issue/VIM-35
if (!range.normalize(EditorHelper.getFileSize(editor, true))) {
@@ -1838,7 +1839,7 @@ public class ChangeGroup {
*
* @return true
*/
public boolean changeNumberVisualMode(@NotNull final Editor editor,
public boolean changeNumberVisualMode(final @NotNull Editor editor,
@NotNull Caret caret,
@NotNull TextRange selectedRange,
final int count,
@@ -1882,7 +1883,7 @@ public class ChangeGroup {
private int repeatCharsCount;
private List<Object> lastStrokes;
public boolean changeNumber(@NotNull final Editor editor, @NotNull Caret caret, final int count) {
public boolean changeNumber(final @NotNull Editor editor, @NotNull Caret caret, final int count) {
final BoundListOption nf = OptionsManager.INSTANCE.getNrformats();
final boolean alpha = nf.contains("alpha");
final boolean hex = nf.contains("hex");
@@ -1911,13 +1912,12 @@ public class ChangeGroup {
private boolean lastLower = true;
private Document document;
@Nullable
public String changeNumberInRange(@NotNull final Editor editor,
@NotNull TextRange range,
final int count,
boolean alpha,
boolean hex,
boolean octal) {
public @Nullable String changeNumberInRange(final @NotNull Editor editor,
@NotNull TextRange range,
final int count,
boolean alpha,
boolean hex,
boolean octal) {
String text = EditorHelper.getText(editor, range);
if (logger.isDebugEnabled()) {
logger.debug("found range " + range);
@@ -2035,8 +2035,7 @@ public class ChangeGroup {
oldOffset = e.getOffset() + newFragmentLength;
}
@NotNull
private List<EditorActionHandlerBase> getAdjustCaretActions(@NotNull DocumentEvent e) {
private @NotNull List<EditorActionHandlerBase> getAdjustCaretActions(@NotNull DocumentEvent e) {
final int delta = e.getOffset() - oldOffset;
if (oldOffset >= 0 && delta != 0) {
final List<EditorActionHandlerBase> positionCaretActions = new ArrayList<>();

View File

@@ -1749,8 +1749,8 @@ public class DigraphGroup {
'f', 't', '\ufb05', // LATIN SMALL LIGATURE FT
's', 't', '\ufb06', // LATIN SMALL LIGATURE ST
};
@NotNull private final HashMap<String, Character> digraphs = new HashMap<>(defaultDigraphs.length);
@NotNull private final TreeMap<Character, String> keys = new TreeMap<>();
private final @NotNull HashMap<String, Character> digraphs = new HashMap<>(defaultDigraphs.length);
private final @NotNull TreeMap<Character, String> keys = new TreeMap<>();
private static final Logger logger = Logger.getInstance(DigraphGroup.class.getName());
}

View File

@@ -21,6 +21,9 @@ package com.maddyhome.idea.vim.group;
import com.intellij.find.EditorSearchSession;
import com.intellij.openapi.actionSystem.AnAction;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.components.PersistentStateComponent;
import com.intellij.openapi.components.State;
import com.intellij.openapi.components.Storage;
import com.intellij.openapi.editor.*;
import com.intellij.openapi.editor.colors.ColorKey;
import com.intellij.openapi.editor.colors.EditorColors;
@@ -47,9 +50,11 @@ import java.util.List;
/**
* @author vlan
*/
public class EditorGroup {
@State(name = "VimEditorSettings", storages = {@Storage(value = "$APP_CONFIG$/vim_settings.xml")})
public class EditorGroup implements PersistentStateComponent<Element> {
private static final boolean ANIMATED_SCROLLING_VIM_VALUE = false;
private static final boolean REFRAIN_FROM_SCROLLING_VIM_VALUE = true;
public static final String EDITOR_STORE_ELEMENT = "editor";
private boolean isBlockCursor = false;
private boolean isAnimatedScrolling = false;
@@ -76,7 +81,7 @@ public class EditorGroup {
}
}
private void initLineNumbers(@NotNull final Editor editor) {
private void initLineNumbers(final @NotNull Editor editor) {
if (!supportsVimLineNumbers(editor) || UserDataManager.getVimEditorGroup(editor)) {
return;
}
@@ -107,13 +112,13 @@ public class EditorGroup {
}
}
private static boolean supportsVimLineNumbers(@NotNull final Editor editor) {
private static boolean supportsVimLineNumbers(final @NotNull Editor editor) {
// We only support line numbers in editors that are file based, and that aren't for diffs, which control their
// own line numbers, often using EditorGutterComponentEx#setLineNumberConvertor
return EditorHelper.isFileEditor(editor) && !EditorHelper.isDiffEditor(editor);
}
private static void updateLineNumbers(@NotNull final Editor editor, final boolean requiresRepaint) {
private static void updateLineNumbers(final @NotNull Editor editor, final boolean requiresRepaint) {
final boolean relativeNumber = OptionsManager.INSTANCE.getRelativenumber().isSet();
final boolean number = OptionsManager.INSTANCE.getNumber().isSet();
@@ -142,7 +147,7 @@ public class EditorGroup {
}
}
private static boolean shouldShowBuiltinLineNumbers(@NotNull final Editor editor, boolean number, boolean relativeNumber) {
private static boolean shouldShowBuiltinLineNumbers(final @NotNull Editor editor, boolean number, boolean relativeNumber) {
final boolean initialState = UserDataManager.getVimLineNumbersInitialState(editor);
// Builtin relative line numbers requires EditorGutterComponentEx#setLineNumberConvertor. If we don't have that,
@@ -155,15 +160,15 @@ public class EditorGroup {
return (initialState || number) && !relativeNumber;
}
private static void setBuiltinLineNumbers(@NotNull final Editor editor, boolean show) {
private static void setBuiltinLineNumbers(final @NotNull Editor editor, boolean show) {
editor.getSettings().setLineNumbersShown(show);
}
private static boolean hasRelativeLineNumbersInstalled(@NotNull final Editor editor) {
private static boolean hasRelativeLineNumbersInstalled(final @NotNull Editor editor) {
return UserDataManager.getVimHasRelativeLineNumbersInstalled(editor);
}
private static void installRelativeLineNumbers(@NotNull final Editor editor) {
private static void installRelativeLineNumbers(final @NotNull Editor editor) {
if (!hasRelativeLineNumbersInstalled(editor)) {
final EditorGutter gutter = editor.getGutter();
if (gutter instanceof EditorGutterComponentEx) {
@@ -176,7 +181,7 @@ public class EditorGroup {
}
}
private static void removeRelativeLineNumbers(@NotNull final Editor editor) {
private static void removeRelativeLineNumbers(final @NotNull Editor editor) {
if (hasRelativeLineNumbersInstalled(editor)) {
final EditorGutter gutter = editor.getGutter();
if (gutter instanceof EditorGutterComponentEx) {
@@ -190,7 +195,7 @@ public class EditorGroup {
}
}
private static void repaintRelativeLineNumbers(@NotNull final Editor editor) {
private static void repaintRelativeLineNumbers(final @NotNull Editor editor) {
final EditorGutter gutter = editor.getGutter();
final EditorGutterComponentEx gutterComponent = gutter instanceof EditorGutterComponentEx ? (EditorGutterComponentEx) gutter : null;
if (gutterComponent != null) {
@@ -210,7 +215,7 @@ public class EditorGroup {
}
public void readData(@NotNull Element element) {
final Element editor = element.getChild("editor");
final Element editor = element.getChild(EDITOR_STORE_ELEMENT);
if (editor != null) {
final Element keyRepeat = editor.getChild("key-repeat");
if (keyRepeat != null) {
@@ -222,8 +227,7 @@ public class EditorGroup {
}
}
@Nullable
public Boolean isKeyRepeat() {
public @Nullable Boolean isKeyRepeat() {
return isKeyRepeat;
}
@@ -275,6 +279,19 @@ public class EditorGroup {
VimPlugin.getNotifications(project).notifyAboutIdeaJoin();
}
@Nullable
@Override
public Element getState() {
Element element = new Element("editor");
saveData(element);
return element;
}
@Override
public void loadState(@NotNull Element state) {
readData(state);
}
public static class NumberChangeListener implements OptionChangeListener<Boolean> {
public static NumberChangeListener INSTANCE = new NumberChangeListener();
@@ -293,11 +310,10 @@ public class EditorGroup {
}
private static class RelativeLineNumberConverter implements TIntFunction {
@NotNull
private final Editor editor;
private final @NotNull Editor editor;
@Contract(pure = true)
RelativeLineNumberConverter(@NotNull final Editor editor) {
RelativeLineNumberConverter(final @NotNull Editor editor) {
this.editor = editor;
}
@@ -322,17 +338,15 @@ public class EditorGroup {
}
private static class RelativeLineNumberGutterProvider implements TextAnnotationGutterProvider {
@NotNull
private final Editor editor;
private final @NotNull Editor editor;
@Contract(pure = true)
RelativeLineNumberGutterProvider(@NotNull final Editor editor) {
RelativeLineNumberGutterProvider(final @NotNull Editor editor) {
this.editor = editor;
}
@Nullable
@Override
public String getLineText(int line, @NotNull Editor editor) {
public @Nullable String getLineText(int line, @NotNull Editor editor) {
final boolean number = OptionsManager.INSTANCE.getNumber().isSet();
if (number && isCaretLine(line, editor)) {
return lineNumberToString(line + 1, editor, true);
@@ -360,9 +374,8 @@ public class EditorGroup {
: StringsKt.padStart(Integer.toString(lineNumber), digitsCount, ' ');
}
@Nullable
@Override
public String getToolTip(int line, Editor editor) {
public @Nullable String getToolTip(int line, Editor editor) {
return null;
}
@@ -371,15 +384,13 @@ public class EditorGroup {
return isCaretLine(line, editor) ? EditorFontType.BOLD: null;
}
@Nullable
@Override
public ColorKey getColor(int line, Editor editor) {
public @Nullable ColorKey getColor(int line, Editor editor) {
return isCaretLine(line, editor) ? EditorColors.LINE_NUMBER_ON_CARET_ROW_COLOR : EditorColors.LINE_NUMBERS_COLOR;
}
@Nullable
@Override
public Color getBgColor(int line, Editor editor) {
public @Nullable Color getBgColor(int line, Editor editor) {
return null;
}

View File

@@ -118,8 +118,7 @@ public class FileGroup {
return found;
}
@Nullable
private VirtualFile findFile(@NotNull VirtualFile root, @NotNull String filename) {
private @Nullable VirtualFile findFile(@NotNull VirtualFile root, @NotNull String filename) {
VirtualFile res = root.findFileByRelativePath(filename);
if (res != null) {
return res;
@@ -223,6 +222,20 @@ public class FileGroup {
}
}
/**
* Returns the previous tab.
*/
public @Nullable VirtualFile getPreviousTab(@NotNull DataContext context) {
Project project = PlatformDataKeys.PROJECT.getData(context);
if (project == null) return null;
FileEditorManager fem = FileEditorManager.getInstance(project); // API change - don't merge
VirtualFile vf = lastSelections.get(fem);
if (vf != null && vf.isValid()) {
return vf;
}
return null;
}
@Nullable
Editor selectEditor(Project project, @NotNull VirtualFile file) {
FileEditorManager fMgr = FileEditorManager.getInstance(project);
@@ -382,10 +395,10 @@ public class FileGroup {
VimPlugin.showMessage(msg.toString());
}
@NotNull private static final String disposableKey = "VimFileGroupDisposable";
private static final @NotNull String disposableKey = "VimFileGroupDisposable";
@NotNull private static final HashMap<FileEditorManager, VirtualFile> lastSelections = new HashMap<>();
@NotNull private static final Logger logger = Logger.getInstance(FileGroup.class.getName());
private static final @NotNull HashMap<FileEditorManager, VirtualFile> lastSelections = new HashMap<>();
private static final @NotNull Logger logger = Logger.getInstance(FileGroup.class.getName());
/**
* This method listens for editor tab changes so any insert/replace modes that need to be reset can be.

View File

@@ -18,19 +18,27 @@
package com.maddyhome.idea.vim.group;
import com.intellij.openapi.components.PersistentStateComponent;
import com.intellij.openapi.components.RoamingType;
import com.intellij.openapi.components.State;
import com.intellij.openapi.components.Storage;
import com.intellij.openapi.diagnostic.Logger;
import com.maddyhome.idea.vim.helper.StringHelper;
import com.maddyhome.idea.vim.option.NumberOption;
import com.maddyhome.idea.vim.option.OptionsManager;
import org.jdom.Element;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class HistoryGroup {
@State(name = "VimHistorySettings", storages = {
@Storage(value = "$APP_CONFIG$/vim_settings.xml", roamingType = RoamingType.DISABLED)
})
public class HistoryGroup implements PersistentStateComponent<Element> {
public static final String SEARCH = "search";
public static final String COMMAND = "cmd";
public static final String EXPRESSION = "expr";
@@ -45,8 +53,7 @@ public class HistoryGroup {
block.addEntry(text);
}
@NotNull
public List<HistoryEntry> getEntries(String key, int first, int last) {
public @NotNull List<HistoryEntry> getEntries(String key, int first, int last) {
HistoryBlock block = blocks(key);
List<HistoryEntry> entries = block.getEntries();
@@ -167,6 +174,19 @@ public class HistoryGroup {
return opt.value();
}
@Nullable
@Override
public Element getState() {
Element element = new Element("history");
saveData(element);
return element;
}
@Override
public void loadState(@NotNull Element state) {
readData(state);
}
private static class HistoryBlock {
public void addEntry(@NotNull String text) {
for (int i = 0; i < entries.size(); i++) {
@@ -184,12 +204,11 @@ public class HistoryGroup {
}
}
@NotNull
public List<HistoryEntry> getEntries() {
public @NotNull List<HistoryEntry> getEntries() {
return entries;
}
@NotNull private final List<HistoryEntry> entries = new ArrayList<>();
private final @NotNull List<HistoryEntry> entries = new ArrayList<>();
private int counter;
}
@@ -203,16 +222,15 @@ public class HistoryGroup {
return number;
}
@NotNull
public String getEntry() {
public @NotNull String getEntry() {
return entry;
}
private final int number;
@NotNull private final String entry;
private final @NotNull String entry;
}
@NotNull private final Map<String, HistoryBlock> histories = new HashMap<>();
private final @NotNull Map<String, HistoryBlock> histories = new HashMap<>();
private static final Logger logger = Logger.getInstance(HistoryGroup.class.getName());
}

View File

@@ -24,6 +24,9 @@ import com.intellij.openapi.actionSystem.*;
import com.intellij.openapi.actionSystem.ex.ActionManagerEx;
import com.intellij.openapi.actionSystem.ex.ActionUtil;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.components.PersistentStateComponent;
import com.intellij.openapi.components.State;
import com.intellij.openapi.components.Storage;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.editor.EditorFactory;
@@ -41,6 +44,7 @@ import com.maddyhome.idea.vim.handler.ActionBeanClass;
import com.maddyhome.idea.vim.handler.EditorActionHandlerBase;
import com.maddyhome.idea.vim.helper.StringHelper;
import com.maddyhome.idea.vim.key.*;
import kotlin.Pair;
import kotlin.text.StringsKt;
import org.jdom.Element;
import org.jetbrains.annotations.NotNull;
@@ -54,27 +58,30 @@ import java.util.*;
import static com.maddyhome.idea.vim.helper.StringHelper.toKeyNotation;
import static java.util.stream.Collectors.joining;
import static java.util.stream.Collectors.toList;
/**
* @author vlan
*/
public class KeyGroup {
private static final String SHORTCUT_CONFLICTS_ELEMENT = "shortcut-conflicts";
@State(name = "VimKeySettings", storages = {@Storage(value = "$APP_CONFIG$/vim_settings.xml")})
public class KeyGroup implements PersistentStateComponent<Element> {
public static final String SHORTCUT_CONFLICTS_ELEMENT = "shortcut-conflicts";
private static final String SHORTCUT_CONFLICT_ELEMENT = "shortcut-conflict";
private static final String OWNER_ATTRIBUTE = "owner";
private static final String TEXT_ELEMENT = "text";
private static final Logger logger = Logger.getInstance(KeyGroup.class);
@NotNull private final Map<KeyStroke, ShortcutOwner> shortcutConflicts = new LinkedHashMap<>();
@NotNull private final Set<KeyStroke> requiredShortcutKeys = new HashSet<>(300);
@NotNull private final Map<MappingMode, CommandPartNode> keyRoots = new EnumMap<>(MappingMode.class);
@NotNull private final Map<MappingMode, KeyMapping> keyMappings = new EnumMap<>(MappingMode.class);
@Nullable private OperatorFunction operatorFunction = null;
private final @NotNull Map<KeyStroke, ShortcutOwner> shortcutConflicts = new LinkedHashMap<>();
private final @NotNull Set<RequiredShortcut> requiredShortcutKeys = new HashSet<>(300);
private final @NotNull Map<MappingMode, CommandPartNode> keyRoots = new EnumMap<>(MappingMode.class);
private final @NotNull Map<MappingMode, KeyMapping> keyMappings = new EnumMap<>(MappingMode.class);
private @Nullable OperatorFunction operatorFunction = null;
void registerRequiredShortcutKeys(@NotNull Editor editor) {
EventFacade.getInstance().registerCustomShortcutSet(VimShortcutKeyAction.getInstance(),
toShortcutSet(requiredShortcutKeys), editor.getComponent());
EventFacade.getInstance()
.registerCustomShortcutSet(VimShortcutKeyAction.getInstance(), toShortcutSet(requiredShortcutKeys),
editor.getComponent());
}
public void registerShortcutsForLookup(@NotNull LookupImpl lookup) {
@@ -88,44 +95,75 @@ public class KeyGroup {
}
public boolean showKeyMappings(@NotNull Set<MappingMode> modes, @NotNull Editor editor) {
final List<MappingInfo> rows = getKeyMappingRows(modes);
List<Pair<EnumSet<MappingMode>, MappingInfo>> rows = getKeyMappingRows(modes);
final StringBuilder builder = new StringBuilder();
for (MappingInfo row : rows) {
builder.append(StringsKt.padEnd(getModesStringCode(row.getMappingModes()), 2, ' '));
for (Pair<EnumSet<MappingMode>, MappingInfo> row : rows) {
MappingInfo mappingInfo = row.getSecond();
builder.append(StringsKt.padEnd(getModesStringCode(row.getFirst()), 2, ' '));
builder.append(" ");
builder.append(StringsKt.padEnd(toKeyNotation(row.getFromKeys()), 11, ' '));
builder.append(StringsKt.padEnd(toKeyNotation(mappingInfo.getFromKeys()), 11, ' '));
builder.append(" ");
builder.append(row.isRecursive() ? " " : "*");
builder.append(mappingInfo.isRecursive() ? " " : "*");
builder.append(" ");
final List<KeyStroke> toKeys = row.getToKeys();
final VimExtensionHandler extensionHandler = row.getExtensionHandler();
if (toKeys != null) {
if (mappingInfo instanceof ToKeysMappingInfo) {
List<KeyStroke> toKeys = ((ToKeysMappingInfo)mappingInfo).getToKeys();
builder.append(toKeyNotation(toKeys));
}
else if (extensionHandler != null) {
else if (mappingInfo instanceof ToHandlerMappingInfo) {
final VimExtensionHandler extensionHandler = ((ToHandlerMappingInfo)mappingInfo).getExtensionHandler();
builder.append("call ");
builder.append(extensionHandler.getClass().getCanonicalName());
}
else {
builder.append("<Unknown>");
}
builder.append("\n");
}
ExOutputModel.getInstance(editor).output(builder.toString());
return true;
}
public void putKeyMapping(@NotNull Set<MappingMode> modes, @NotNull List<KeyStroke> fromKeys,
@Nullable List<KeyStroke> toKeys, @Nullable VimExtensionHandler extensionHandler,
public void removeKeyMapping(@NotNull MappingOwner owner) {
Arrays.stream(MappingMode.values()).map(this::getKeyMapping).forEach(o -> o.delete(owner));
unregisterKeyMapping(owner);
}
public void putKeyMapping(@NotNull Set<MappingMode> modes,
@NotNull List<KeyStroke> fromKeys,
@NotNull MappingOwner owner,
@NotNull VimExtensionHandler extensionHandler,
boolean recursive) {
for (MappingMode mode : modes) {
final KeyMapping mapping = getKeyMapping(mode);
mapping.put(EnumSet.of(mode), fromKeys, toKeys, extensionHandler, recursive);
modes.stream().map(this::getKeyMapping).forEach(o -> o.put(fromKeys, owner, extensionHandler, recursive));
registerKeyMapping(fromKeys, owner);
}
public void putKeyMapping(@NotNull Set<MappingMode> modes,
@NotNull List<KeyStroke> fromKeys,
@NotNull MappingOwner owner,
@NotNull List<KeyStroke> toKeys,
boolean recursive) {
modes.stream().map(this::getKeyMapping).forEach(o -> o.put(fromKeys, toKeys, owner, recursive));
registerKeyMapping(fromKeys, owner);
}
public List<Pair<List<KeyStroke>, MappingInfo>> getKeyMappingByOwner(@NotNull MappingOwner owner) {
return Arrays.stream(MappingMode.values()).map(this::getKeyMapping).flatMap(o -> o.getByOwner(owner).stream())
.collect(toList());
}
private void unregisterKeyMapping(MappingOwner owner) {
final int oldSize = requiredShortcutKeys.size();
requiredShortcutKeys.removeIf(requiredShortcut -> requiredShortcut.getOwner().equals(owner));
if (requiredShortcutKeys.size() != oldSize) {
for (Editor editor : EditorFactory.getInstance().getAllEditors()) {
unregisterShortcutKeys(editor);
registerRequiredShortcutKeys(editor);
}
}
}
private void registerKeyMapping(@NotNull List<KeyStroke> fromKeys, MappingOwner owner) {
final int oldSize = requiredShortcutKeys.size();
for (KeyStroke key : fromKeys) {
if (key.getKeyChar() == KeyEvent.CHAR_UNDEFINED) {
requiredShortcutKeys.add(key);
requiredShortcutKeys.add(new RequiredShortcut(key, owner));
}
}
if (requiredShortcutKeys.size() != oldSize) {
@@ -136,8 +174,7 @@ public class KeyGroup {
}
}
@Nullable
public OperatorFunction getOperatorFunction() {
public @Nullable OperatorFunction getOperatorFunction() {
return operatorFunction;
}
@@ -187,8 +224,7 @@ public class KeyGroup {
}
}
@NotNull
public List<AnAction> getKeymapConflicts(@NotNull KeyStroke keyStroke) {
public @NotNull List<AnAction> getKeymapConflicts(@NotNull KeyStroke keyStroke) {
final KeymapManagerEx keymapManager = KeymapManagerEx.getInstanceEx();
final Keymap keymap = keymapManager.getActiveKeymap();
final KeyboardShortcut shortcut = new KeyboardShortcut(keyStroke, null);
@@ -203,12 +239,12 @@ public class KeyGroup {
return actions;
}
@NotNull
public Map<KeyStroke, ShortcutOwner> getShortcutConflicts() {
final Set<KeyStroke> requiredShortcutKeys = this.requiredShortcutKeys;
public @NotNull Map<KeyStroke, ShortcutOwner> getShortcutConflicts() {
final Set<RequiredShortcut> requiredShortcutKeys = this.requiredShortcutKeys;
final Map<KeyStroke, ShortcutOwner> savedConflicts = getSavedShortcutConflicts();
final Map<KeyStroke, ShortcutOwner> results = new HashMap<>();
for (KeyStroke keyStroke : requiredShortcutKeys) {
for (RequiredShortcut requiredShortcut : requiredShortcutKeys) {
KeyStroke keyStroke = requiredShortcut.getKeyStroke();
if (!VimShortcutKeyAction.VIM_ONLY_EDITOR_KEYS.contains(keyStroke)) {
final List<AnAction> conflicts = getKeymapConflicts(keyStroke);
if (!conflicts.isEmpty()) {
@@ -220,13 +256,11 @@ public class KeyGroup {
return results;
}
@NotNull
public Map<KeyStroke, ShortcutOwner> getSavedShortcutConflicts() {
public @NotNull Map<KeyStroke, ShortcutOwner> getSavedShortcutConflicts() {
return shortcutConflicts;
}
@NotNull
public KeyMapping getKeyMapping(@NotNull MappingMode mode) {
public @NotNull KeyMapping getKeyMapping(@NotNull MappingMode mode) {
KeyMapping mapping = keyMappings.get(mode);
if (mapping == null) {
mapping = new KeyMapping();
@@ -245,8 +279,7 @@ public class KeyGroup {
* @param mappingMode The mapping mode
* @return The key mapping tree root
*/
@NotNull
public CommandPartNode getKeyRoot(@NotNull MappingMode mappingMode) {
public @NotNull CommandPartNode getKeyRoot(@NotNull MappingMode mappingMode) {
return keyRoots.computeIfAbsent(mappingMode, (key) -> new RootNode());
}
@@ -257,10 +290,11 @@ public class KeyGroup {
* Digraphs are handled directly by KeyHandler#handleKey instead of via an action, but we need to still make sure the
* shortcuts are registered, or the key handler won't see them
* </p>
*
* @param keyStroke The shortcut to register
*/
public void registerShortcutWithoutAction(KeyStroke keyStroke) {
registerRequiredShortcut(Collections.singletonList(keyStroke));
public void registerShortcutWithoutAction(KeyStroke keyStroke, MappingOwner owner) {
registerRequiredShortcut(Collections.singletonList(keyStroke), owner);
}
public void unregisterCommandActions() {
@@ -274,8 +308,9 @@ public class KeyGroup {
if (!VimPlugin.getPluginId().equals(actionHolder.getPluginId())) {
logger.error("IdeaVim doesn't accept contributions to `vimActions` extension points. " +
"Please create a plugin using `VimExtension`. " +
"Plugin to blame: " + actionHolder.getPluginId());
"Please create a plugin using `VimExtension`. " +
"Plugin to blame: " +
actionHolder.getPluginId());
return;
}
@@ -284,7 +319,8 @@ public class KeyGroup {
final EditorActionHandlerBase action = actionHolder.getAction();
if (action instanceof ComplicatedKeysAction) {
actionKeys = ((ComplicatedKeysAction)action).getKeyStrokesSet();
} else {
}
else {
throw new RuntimeException("Cannot register action: " + action.getClass().getName());
}
}
@@ -305,7 +341,7 @@ public class KeyGroup {
}
for (List<KeyStroke> keyStrokes : actionKeys) {
registerRequiredShortcut(keyStrokes);
registerRequiredShortcut(keyStrokes, MappingOwner.IdeaVim.INSTANCE);
for (MappingMode mappingMode : actionModes) {
Node node = getKeyRoot(mappingMode);
@@ -322,15 +358,17 @@ public class KeyGroup {
}
}
private void registerRequiredShortcut(@NotNull List<KeyStroke> keys) {
private void registerRequiredShortcut(@NotNull List<KeyStroke> keys, MappingOwner owner) {
for (KeyStroke key : keys) {
if (key.getKeyChar() == KeyEvent.CHAR_UNDEFINED) {
requiredShortcutKeys.add(key);
requiredShortcutKeys.add(new RequiredShortcut(key, owner));
}
}
}
private void checkCommand(@NotNull Set<MappingMode> mappingModes, EditorActionHandlerBase action, List<KeyStroke> keys) {
private void checkCommand(@NotNull Set<MappingMode> mappingModes,
EditorActionHandlerBase action,
List<KeyStroke> keys) {
for (MappingMode mappingMode : mappingModes) {
checkIdentity(mappingMode, action.getId(), keys);
}
@@ -340,8 +378,8 @@ public class KeyGroup {
private void checkIdentity(MappingMode mappingMode, String actName, List<KeyStroke> keys) {
Set<List<KeyStroke>> keySets = identityChecker.computeIfAbsent(mappingMode, k -> new HashSet<>());
if (keySets.contains(keys)) {
throw new RuntimeException("This keymap already exists: " + mappingMode + " keys: " +
keys + " action:" + actName);
throw new RuntimeException(
"This keymap already exists: " + mappingMode + " keys: " + keys + " action:" + actName);
}
keySets.add(keys);
}
@@ -356,7 +394,9 @@ public class KeyGroup {
if (!prefix.get(i).equals(keys.get(i))) break;
}
List<String> actionExceptions = Arrays.asList("VimInsertDeletePreviousWordAction", "VimInsertAfterCursorAction", "VimInsertBeforeCursorAction", "VimFilterVisualLinesAction", "VimAutoIndentMotionAction");
List<String> actionExceptions = Arrays
.asList("VimInsertDeletePreviousWordAction", "VimInsertAfterCursorAction", "VimInsertBeforeCursorAction",
"VimFilterVisualLinesAction", "VimAutoIndentMotionAction");
if (i == shortOne && !actionExceptions.contains(action.getId()) && !actionExceptions.contains(entry.getValue())) {
throw new RuntimeException("Prefix found! " +
keys +
@@ -371,47 +411,45 @@ public class KeyGroup {
prefixes.put(keys, action.getId());
}
@Nullable private Map<MappingMode, Set<List<KeyStroke>>> identityChecker;
@Nullable private Map<List<KeyStroke>, String> prefixes;
private @Nullable Map<MappingMode, Set<List<KeyStroke>>> identityChecker;
private @Nullable Map<List<KeyStroke>, String> prefixes;
@NotNull
private Node addMNode(@NotNull CommandPartNode base,
ActionBeanClass actionHolder,
@NotNull KeyStroke key,
boolean isLastInSequence) {
private @NotNull Node addMNode(@NotNull CommandPartNode base,
ActionBeanClass actionHolder,
@NotNull KeyStroke key,
boolean isLastInSequence) {
Node existing = base.get(key);
if (existing != null) return existing;
Node newNode;
if (isLastInSequence) {
newNode = new CommandNode(actionHolder);
} else {
}
else {
newNode = new CommandPartNode();
}
base.put(key, newNode);
return newNode;
}
@NotNull
private static ShortcutSet toShortcutSet(@NotNull Collection<KeyStroke> keyStrokes) {
final List<com.intellij.openapi.actionSystem.Shortcut> shortcuts = new ArrayList<>();
for (KeyStroke key : keyStrokes) {
shortcuts.add(new KeyboardShortcut(key, null));
private static @NotNull ShortcutSet toShortcutSet(@NotNull Collection<RequiredShortcut> requiredShortcuts) {
final List<Shortcut> shortcuts = new ArrayList<>();
for (RequiredShortcut key : requiredShortcuts) {
shortcuts.add(new KeyboardShortcut(key.getKeyStroke(), null));
}
return new CustomShortcutSet(shortcuts.toArray(new com.intellij.openapi.actionSystem.Shortcut[0]));
return new CustomShortcutSet(shortcuts.toArray(new Shortcut[0]));
}
@NotNull
private static List<MappingInfo> getKeyMappingRows(@NotNull Set<MappingMode> modes) {
final Map<ImmutableList<KeyStroke>, Set<MappingMode>> actualModes = new HashMap<>();
private static @NotNull List<Pair<EnumSet<MappingMode>, MappingInfo>> getKeyMappingRows(@NotNull Set<MappingMode> modes) {
final Map<ImmutableList<KeyStroke>, EnumSet<MappingMode>> actualModes = new HashMap<>();
for (MappingMode mode : modes) {
final KeyMapping mapping = VimPlugin.getKey().getKeyMapping(mode);
for (List<KeyStroke> fromKeys : mapping) {
final ImmutableList<KeyStroke> key = ImmutableList.copyOf(fromKeys);
final Set<MappingMode> value = actualModes.get(key);
final Set<MappingMode> newValue;
final EnumSet<MappingMode> value = actualModes.get(key);
final EnumSet<MappingMode> newValue;
if (value != null) {
newValue = new HashSet<>(value);
newValue = value.clone();
newValue.add(mode);
}
else {
@@ -420,26 +458,24 @@ public class KeyGroup {
actualModes.put(key, newValue);
}
}
final List<MappingInfo> rows = new ArrayList<>();
for (Map.Entry<ImmutableList<KeyStroke>, Set<MappingMode>> entry : actualModes.entrySet()) {
final List<Pair<EnumSet<MappingMode>, MappingInfo>> rows = new ArrayList<>();
for (Map.Entry<ImmutableList<KeyStroke>, EnumSet<MappingMode>> entry : actualModes.entrySet()) {
final ArrayList<KeyStroke> fromKeys = new ArrayList<>(entry.getKey());
final Set<MappingMode> mappingModes = entry.getValue();
final EnumSet<MappingMode> mappingModes = entry.getValue();
if (!mappingModes.isEmpty()) {
final MappingMode mode = mappingModes.iterator().next();
final KeyMapping mapping = VimPlugin.getKey().getKeyMapping(mode);
final MappingInfo mappingInfo = mapping.get(fromKeys);
if (mappingInfo != null) {
rows.add(new MappingInfo(mappingModes, mappingInfo.getFromKeys(), mappingInfo.getToKeys(),
mappingInfo.getExtensionHandler(), mappingInfo.isRecursive()));
rows.add(new Pair<>(mappingModes, mappingInfo));
}
}
}
Collections.sort(rows);
rows.sort(Comparator.comparing(Pair<EnumSet<MappingMode>, MappingInfo>::getSecond));
return rows;
}
@NotNull
private static String getModesStringCode(@NotNull Set<MappingMode> modes) {
private static @NotNull String getModesStringCode(@NotNull Set<MappingMode> modes) {
if (modes.equals(MappingMode.NVO)) {
return "";
}
@@ -453,16 +489,14 @@ public class KeyGroup {
return "";
}
@NotNull
public List<AnAction> getActions(@NotNull Component component, @NotNull KeyStroke keyStroke) {
public @NotNull List<AnAction> getActions(@NotNull Component component, @NotNull KeyStroke keyStroke) {
final List<AnAction> results = new ArrayList<>();
results.addAll(getLocalActions(component, keyStroke));
results.addAll(getKeymapActions(keyStroke));
return results;
}
@NotNull
private static List<AnAction> getLocalActions(@NotNull Component component, @NotNull KeyStroke keyStroke) {
private static @NotNull List<AnAction> getLocalActions(@NotNull Component component, @NotNull KeyStroke keyStroke) {
final List<AnAction> results = new ArrayList<>();
final KeyboardShortcut keyStrokeShortcut = new KeyboardShortcut(keyStroke, null);
for (Component c = component; c != null; c = c.getParent()) {
@@ -484,8 +518,7 @@ public class KeyGroup {
return results;
}
@NotNull
private static List<AnAction> getKeymapActions(@NotNull KeyStroke keyStroke) {
private static @NotNull List<AnAction> getKeymapActions(@NotNull KeyStroke keyStroke) {
final List<AnAction> results = new ArrayList<>();
final Keymap keymap = KeymapManager.getInstance().getActiveKeymap();
for (String id : keymap.getActionIds(keyStroke)) {
@@ -496,4 +529,17 @@ public class KeyGroup {
}
return results;
}
@Nullable
@Override
public Element getState() {
Element element = new Element("key");
saveData(element);
return element;
}
@Override
public void loadState(@NotNull Element state) {
readData(state);
}
}

View File

@@ -90,8 +90,8 @@ public class MacroGroup {
* @param cnt count
* @param total total
*/
public void playbackKeys(@NotNull final Editor editor, @NotNull final DataContext context, @Nullable final Project project,
@NotNull final List<KeyStroke> keys, final int pos, final int cnt, final int total) {
public void playbackKeys(final @NotNull Editor editor, final @NotNull DataContext context, final @Nullable Project project,
final @NotNull List<KeyStroke> keys, final int pos, final int cnt, final int total) {
if (logger.isDebugEnabled()) {
logger.debug("playbackKeys " + pos);
}
@@ -138,8 +138,7 @@ public class MacroGroup {
});
}
@NotNull
private KeyEvent createKeyEvent(@NotNull KeyStroke stroke, Component component) {
private @NotNull KeyEvent createKeyEvent(@NotNull KeyStroke stroke, Component component) {
return new KeyEvent(component,
stroke.getKeyChar() == KeyEvent.CHAR_UNDEFINED ? KeyEvent.KEY_PRESSED : KeyEvent.KEY_TYPED,
System.currentTimeMillis(), stroke.getModifiers(), stroke.getKeyCode(), stroke.getKeyChar());

View File

@@ -21,6 +21,10 @@ package com.maddyhome.idea.vim.group;
import com.intellij.ide.bookmarks.Bookmark;
import com.intellij.ide.bookmarks.BookmarkManager;
import com.intellij.ide.bookmarks.BookmarksListener;
import com.intellij.openapi.components.PersistentStateComponent;
import com.intellij.openapi.components.RoamingType;
import com.intellij.openapi.components.State;
import com.intellij.openapi.components.Storage;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.editor.Editor;
@@ -53,7 +57,10 @@ import java.util.stream.Collectors;
/**
* This class contains all the mark related functionality
*/
public class MarkGroup {
@State(name = "VimMarksSettings", storages = {
@Storage(value = "$APP_CONFIG$/vim_settings.xml", roamingType = RoamingType.DISABLED)
})
public class MarkGroup implements PersistentStateComponent<Element> {
public static final char MARK_VISUAL_START = '<';
public static final char MARK_VISUAL_END = '>';
public static final char MARK_CHANGE_START = '[';
@@ -83,8 +90,7 @@ public class MarkGroup {
* @param ch The desired mark
* @return The requested mark if set, null if not set
*/
@Nullable
public Mark getMark(@NotNull Editor editor, char ch) {
public @Nullable Mark getMark(@NotNull Editor editor, char ch) {
Mark mark = null;
if (ch == '`') ch = '\'';
@@ -135,8 +141,7 @@ public class MarkGroup {
* @param count Postive for next jump (Ctrl-I), negative for previous jump (Ctrl-O).
* @return The jump or null if out of range.
*/
@Nullable
public Jump getJump(int count) {
public @Nullable Jump getJump(int count) {
int index = jumps.size() - 1 - (jumpSpot - count);
if (index < 0 || index >= jumps.size()) {
return null;
@@ -154,8 +159,7 @@ public class MarkGroup {
* @param ch The mark to get
* @return The mark in the current file, if set, null if no such mark
*/
@Nullable
public Mark getFileMark(@NotNull Editor editor, char ch) {
public @Nullable Mark getFileMark(@NotNull Editor editor, char ch) {
if (ch == '`') ch = '\'';
final HashMap fmarks = getFileMarks(editor.getDocument());
if (fmarks == null) {
@@ -228,8 +232,7 @@ public class MarkGroup {
return true;
}
@Nullable
private Bookmark createOrGetSystemMark(char ch, int line, @NotNull Editor editor) {
private @Nullable Bookmark createOrGetSystemMark(char ch, int line, @NotNull Editor editor) {
if (!OptionsManager.INSTANCE.getIdeamarks().isSet()) return null;
final Project project = editor.getProject();
if (project == null) return null;
@@ -259,18 +262,15 @@ public class MarkGroup {
setMark(editor, MARK_CHANGE_END, range.getEndOffset());
}
@Nullable
public TextRange getChangeMarks(@NotNull Editor editor) {
public @Nullable TextRange getChangeMarks(@NotNull Editor editor) {
return getMarksRange(editor, MARK_CHANGE_START, MARK_CHANGE_END);
}
@Nullable
public TextRange getVisualSelectionMarks(@NotNull Editor editor) {
public @Nullable TextRange getVisualSelectionMarks(@NotNull Editor editor) {
return getMarksRange(editor, MARK_VISUAL_START, MARK_VISUAL_END);
}
@Nullable
private TextRange getMarksRange(@NotNull Editor editor, char startMark, char endMark) {
private @Nullable TextRange getMarksRange(@NotNull Editor editor, char startMark, char endMark) {
final Mark start = getMark(editor, startMark);
final Mark end = getMark(editor, endMark);
if (start != null && end != null) {
@@ -338,8 +338,7 @@ public class MarkGroup {
mark.clear();
}
@NotNull
public List<Mark> getMarks(@NotNull Editor editor) {
public @NotNull List<Mark> getMarks(@NotNull Editor editor) {
HashSet<Mark> res = new HashSet<>();
final FileMarks<Character, Mark> marks = getFileMarks(editor.getDocument());
@@ -355,8 +354,7 @@ public class MarkGroup {
return list;
}
@NotNull
public List<Jump> getJumps() {
public @NotNull List<Jump> getJumps() {
return jumps;
}
@@ -371,8 +369,7 @@ public class MarkGroup {
* @return The map of marks. The keys are <code>Character</code>s of the mark names, the values are
* <code>Mark</code>s.
*/
@Nullable
private FileMarks<Character, Mark> getFileMarks(@NotNull final Document doc) {
private @Nullable FileMarks<Character, Mark> getFileMarks(final @NotNull Document doc) {
VirtualFile vf = FileDocumentManager.getInstance().getFile(doc);
if (vf == null) {
return null;
@@ -381,8 +378,7 @@ public class MarkGroup {
return getFileMarks(vf.getPath());
}
@Nullable
private HashMap<Character, Mark> getAllFileMarks(@NotNull final Document doc) {
private @Nullable HashMap<Character, Mark> getAllFileMarks(final @NotNull Document doc) {
VirtualFile vf = FileDocumentManager.getInstance().getFile(doc);
if (vf == null) {
return null;
@@ -659,6 +655,19 @@ public class MarkGroup {
}
}
@Nullable
@Override
public Element getState() {
Element element = new Element("marks");
saveData(element);
return element;
}
@Override
public void loadState(@NotNull Element state) {
readData(state);
}
private static class FileMarks<K, V> extends HashMap<K, V> {
public void setTimestamp(Date timestamp) {
this.timestamp = timestamp;
@@ -723,8 +732,7 @@ public class MarkGroup {
// TODO - update jumps
}
@Nullable
private Editor getAnEditor(@NotNull Document doc) {
private @Nullable Editor getAnEditor(@NotNull Document doc) {
Editor[] editors = EditorFactory.getInstance().getEditors(doc);
if (editors.length > 0) {
@@ -785,9 +793,9 @@ public class MarkGroup {
}
}
@NotNull private final HashMap<String, FileMarks<Character, Mark>> fileMarks = new HashMap<>();
@NotNull private final HashMap<Character, Mark> globalMarks = new HashMap<>();
@NotNull private final List<Jump> jumps = new ArrayList<>();
private final @NotNull HashMap<String, FileMarks<Character, Mark>> fileMarks = new HashMap<>();
private final @NotNull HashMap<Character, Mark> globalMarks = new HashMap<>();
private final @NotNull List<Jump> jumps = new ArrayList<>();
private int jumpSpot = -1;
private static final int SAVE_MARK_COUNT = 20;

View File

@@ -102,8 +102,7 @@ public class MotionGroup {
* @param argument Any argument needed by the motion
* @return The motion's range
*/
@Nullable
public static TextRange getMotionRange(@NotNull Editor editor,
public static @Nullable TextRange getMotionRange(@NotNull Editor editor,
@NotNull Caret caret,
DataContext context,
int count,
@@ -239,32 +238,27 @@ public class MotionGroup {
}
}
@Nullable
public TextRange getBlockQuoteRange(@NotNull Editor editor, @NotNull Caret caret, char quote, boolean isOuter) {
public @Nullable TextRange getBlockQuoteRange(@NotNull Editor editor, @NotNull Caret caret, char quote, boolean isOuter) {
return SearchHelper.findBlockQuoteInLineRange(editor, caret, quote, isOuter);
}
@Nullable
public TextRange getBlockRange(@NotNull Editor editor, @NotNull Caret caret, int count, boolean isOuter, char type) {
public @Nullable TextRange getBlockRange(@NotNull Editor editor, @NotNull Caret caret, int count, boolean isOuter, char type) {
return SearchHelper.findBlockRange(editor, caret, type, count, isOuter);
}
@Nullable
public TextRange getBlockTagRange(@NotNull Editor editor, @NotNull Caret caret, int count, boolean isOuter) {
public @Nullable TextRange getBlockTagRange(@NotNull Editor editor, @NotNull Caret caret, int count, boolean isOuter) {
return SearchHelper.findBlockTagRange(editor, caret, count, isOuter);
}
@NotNull
public TextRange getSentenceRange(@NotNull Editor editor, @NotNull Caret caret, int count, boolean isOuter) {
public @NotNull TextRange getSentenceRange(@NotNull Editor editor, @NotNull Caret caret, int count, boolean isOuter) {
return SearchHelper.findSentenceRange(editor, caret, count, isOuter);
}
@Nullable
public TextRange getParagraphRange(@NotNull Editor editor, @NotNull Caret caret, int count, boolean isOuter) {
public @Nullable TextRange getParagraphRange(@NotNull Editor editor, @NotNull Caret caret, int count, boolean isOuter) {
return SearchHelper.findParagraphRange(editor, caret, count, isOuter);
}
private static int getScrollScreenTargetCaretVisualLine(@NotNull final Editor editor, int rawCount, boolean down) {
private static int getScrollScreenTargetCaretVisualLine(final @NotNull Editor editor, int rawCount, boolean down) {
final Rectangle visibleArea = editor.getScrollingModel().getVisibleArea();
final int caretVisualLine = editor.getCaretModel().getVisualPosition().line;
final int scrollOption = getScrollOption(rawCount);
@@ -296,7 +290,7 @@ public class MotionGroup {
return rawCount;
}
private static int getNormalizedScrollOffset(@NotNull final Editor editor) {
private static int getNormalizedScrollOffset(final @NotNull Editor editor) {
int scrollOffset = OptionsManager.INSTANCE.getScrolloff().value();
return EditorHelper.normalizeScrollOffset(editor, scrollOffset);
}
@@ -328,8 +322,7 @@ public class MotionGroup {
}
}
@Nullable
private Editor selectEditor(@NotNull Editor editor, @NotNull Mark mark) {
private @Nullable Editor selectEditor(@NotNull Editor editor, @NotNull Mark mark) {
final VirtualFile virtualFile = markToVirtualFile(mark);
if (virtualFile != null) {
return selectEditor(editor, virtualFile);
@@ -339,15 +332,13 @@ public class MotionGroup {
}
}
@Nullable
private VirtualFile markToVirtualFile(@NotNull Mark mark) {
private @Nullable VirtualFile markToVirtualFile(@NotNull Mark mark) {
String protocol = mark.getProtocol();
VirtualFileSystem fileSystem = VirtualFileManager.getInstance().getFileSystem(protocol);
return fileSystem.findFileByPath(mark.getFilename());
}
@Nullable
private Editor selectEditor(@NotNull Editor editor, @NotNull VirtualFile file) {
private @Nullable Editor selectEditor(@NotNull Editor editor, @NotNull VirtualFile file) {
return VimPlugin.getFile().selectEditor(editor.getProject(), file);
}
@@ -778,12 +769,11 @@ public class MotionGroup {
return true;
}
@NotNull
public TextRange getWordRange(@NotNull Editor editor,
@NotNull Caret caret,
int count,
boolean isOuter,
boolean isBig) {
public @NotNull TextRange getWordRange(@NotNull Editor editor,
@NotNull Caret caret,
int count,
boolean isOuter,
boolean isBig) {
int dir = 1;
boolean selection = false;
if (CommandState.getInstance(editor).getMode() == CommandState.Mode.VISUAL) {
@@ -1035,13 +1025,13 @@ public class MotionGroup {
public int moveCaretHorizontal(@NotNull Editor editor, @NotNull Caret caret, int count, boolean allowPastEnd) {
int oldOffset = caret.getOffset();
int diff = 0;
String text = editor.getDocument().getText();
CharSequence text = editor.getDocument().getCharsSequence();
int sign = (int)Math.signum(count);
for (Integer pointer : new IntProgression(0, count - sign, sign)) {
int textPointer = oldOffset + pointer;
if (textPointer < text.length() && textPointer >= 0) {
// Actual char size can differ from 1 if unicode characters are used (like 🐔)
diff += Character.charCount(text.codePointAt(textPointer));
diff += Character.charCount(Character.codePointAt(text, textPointer));
}
else {
diff += 1;
@@ -1108,7 +1098,7 @@ public class MotionGroup {
return editor.logicalPositionToOffset(newPos);
}
public boolean scrollScreen(@NotNull final Editor editor, int rawCount, boolean down) {
public boolean scrollScreen(final @NotNull Editor editor, int rawCount, boolean down) {
final CaretModel caretModel = editor.getCaretModel();
final int currentLogicalLine = caretModel.getLogicalPosition().line;

View File

@@ -33,6 +33,7 @@ import com.intellij.openapi.actionSystem.KeyboardShortcut
import com.intellij.openapi.keymap.Keymap
import com.intellij.openapi.keymap.KeymapUtil
import com.intellij.openapi.options.ShowSettingsUtil
import com.intellij.openapi.project.DumbAwareAction
import com.intellij.openapi.project.Project
import com.intellij.openapi.ui.Messages
import com.intellij.openapi.util.SystemInfo
@@ -48,6 +49,9 @@ import javax.swing.event.HyperlinkEvent
/**
* @author Alex Plate
*
* This service is can be used as application level and as project level service.
* If project is null, this means that this is an application level service and notification will be shown for all projects
*/
class NotificationService(private val project: Project?) {
// This constructor is used to create an applicationService
@@ -143,7 +147,7 @@ class NotificationService(private val project: Project?) {
NotificationType.INFORMATION).notify(project)
}
class OpenIdeaVimRcAction(private val notification: Notification?) : AnAction("Open ~/.ideavimrc") {
class OpenIdeaVimRcAction(private val notification: Notification?) : DumbAwareAction("Open ~/.ideavimrc") {
override fun actionPerformed(e: AnActionEvent) {
val eventProject = e.project
if (eventProject != null) {

View File

@@ -62,8 +62,7 @@ public class ProcessGroup {
return ExEntryPanel.getInstance().getLabel().equals("/");
}
@NotNull
public String endSearchCommand(@NotNull final Editor editor) {
public @NotNull String endSearchCommand(final @NotNull Editor editor) {
ExEntryPanel panel = ExEntryPanel.getInstance();
panel.deactivate(true);
@@ -100,7 +99,7 @@ public class ProcessGroup {
}
}
public boolean processExEntry(@NotNull final Editor editor, @NotNull final DataContext context) {
public boolean processExEntry(final @NotNull Editor editor, final @NotNull DataContext context) {
ExEntryPanel panel = ExEntryPanel.getInstance();
panel.deactivate(true);
boolean res = true;
@@ -143,7 +142,7 @@ public class ProcessGroup {
return res;
}
public void cancelExEntry(@NotNull final Editor editor, boolean resetCaret) {
public void cancelExEntry(final @NotNull Editor editor, boolean resetCaret) {
CommandState.getInstance(editor).popModes();
KeyHandler.getInstance().reset(editor);
ExEntryPanel panel = ExEntryPanel.getInstance();
@@ -163,8 +162,7 @@ public class ProcessGroup {
panel.activate(editor, context, ":", initText, 1);
}
@NotNull
private String getRange(Editor editor, @NotNull Command cmd) {
private @NotNull String getRange(Editor editor, @NotNull Command cmd) {
String initText = "";
if (CommandState.getInstance(editor).getMode() == CommandState.Mode.VISUAL) {
initText = "'<,'>";
@@ -191,8 +189,7 @@ public class ProcessGroup {
return true;
}
@NotNull
public String executeCommand(@NotNull String command, @Nullable CharSequence input) throws IOException {
public @NotNull String executeCommand(@NotNull String command, @Nullable CharSequence input) throws IOException {
if (logger.isDebugEnabled()) {
logger.debug("command=" + command);
}

View File

@@ -23,6 +23,10 @@ import com.intellij.codeInsight.editorActions.CopyPastePostProcessor;
import com.intellij.codeInsight.editorActions.CopyPastePreProcessor;
import com.intellij.codeInsight.editorActions.TextBlockTransferable;
import com.intellij.codeInsight.editorActions.TextBlockTransferableData;
import com.intellij.openapi.components.PersistentStateComponent;
import com.intellij.openapi.components.RoamingType;
import com.intellij.openapi.components.State;
import com.intellij.openapi.components.Storage;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.editor.CaretStateTransferableData;
import com.intellij.openapi.editor.Editor;
@@ -72,7 +76,10 @@ import java.util.stream.Collectors;
/**
* This group works with command associated with copying and pasting text
*/
public class RegisterGroup {
@State(name = "VimRegisterSettings", storages = {
@Storage(value = "$APP_CONFIG$/vim_settings.xml", roamingType = RoamingType.DISABLED)
})
public class RegisterGroup implements PersistentStateComponent<Element> {
private static final String WRITABLE_REGISTERS = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ-*+_/\"";
private static final String READONLY_REGISTERS = ":.%#=/";
private static final String RECORDABLE_REGISTER = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
@@ -83,9 +90,9 @@ public class RegisterGroup {
private char defaultRegister = '"';
private char lastRegister = defaultRegister;
@NotNull private final HashMap<Character, Register> registers = new HashMap<>();
private final @NotNull HashMap<Character, Register> registers = new HashMap<>();
private char recordRegister = 0;
@Nullable private List<KeyStroke> recordList = null;
private @Nullable List<KeyStroke> recordList = null;
public RegisterGroup() {
final ListOption clipboardOption = OptionsManager.INSTANCE.getClipboard();
@@ -246,10 +253,9 @@ public class RegisterGroup {
return true;
}
@NotNull
public List<TextBlockTransferableData> getTransferableData(@NotNull Editor editor,
@NotNull TextRange textRange,
String text) {
public @NotNull List<TextBlockTransferableData> getTransferableData(@NotNull Editor editor,
@NotNull TextRange textRange,
String text) {
final List<TextBlockTransferableData> transferableDatas = new ArrayList<>();
final Project project = editor.getProject();
if (project == null) return new ArrayList<>();
@@ -316,13 +322,11 @@ public class RegisterGroup {
*
* @return The register, null if no such register
*/
@Nullable
public Register getLastRegister() {
public @Nullable Register getLastRegister() {
return getRegister(lastRegister);
}
@Nullable
public Register getPlaybackRegister(char r) {
public @Nullable Register getPlaybackRegister(char r) {
if (PLAYBACK_REGISTER.indexOf(r) != 0) {
return getRegister(r);
}
@@ -331,8 +335,7 @@ public class RegisterGroup {
}
}
@Nullable
public Register getRegister(char r) {
public @Nullable Register getRegister(char r) {
// Uppercase registers actually get the lowercase register
if (Character.isUpperCase(r)) {
r = Character.toLowerCase(r);
@@ -356,8 +359,7 @@ public class RegisterGroup {
return defaultRegister;
}
@NotNull
public List<Register> getRegisters() {
public @NotNull List<Register> getRegisters() {
final List<Register> res = new ArrayList<>(registers.values());
for (Character r : CLIPBOARD_REGISTERS) {
final Register register = refreshClipboardRegister(r);
@@ -419,7 +421,7 @@ public class RegisterGroup {
recordRegister = 0;
}
public void saveData(@NotNull final Element element) {
public void saveData(final @NotNull Element element) {
logger.debug("saveData");
final Element registersElement = new Element("registers");
for (Character key : registers.keySet()) {
@@ -451,7 +453,7 @@ public class RegisterGroup {
element.addContent(registersElement);
}
public void readData(@NotNull final Element element) {
public void readData(final @NotNull Element element) {
logger.debug("readData");
final Element registersElement = element.getChild("registers");
if (registersElement != null) {
@@ -491,8 +493,7 @@ public class RegisterGroup {
}
}
@Nullable
private Register refreshClipboardRegister(char r) {
private @Nullable Register refreshClipboardRegister(char r) {
final Pair<String, List<TextBlockTransferableData>> clipboardData = ClipboardHandler.getClipboardTextAndTransferableData();
final Register currentRegister = registers.get(r);
final String text = clipboardData.getFirst();
@@ -506,8 +507,7 @@ public class RegisterGroup {
return null;
}
@NotNull
private SelectionType guessSelectionType(@NotNull String text) {
private @NotNull SelectionType guessSelectionType(@NotNull String text) {
if (text.endsWith("\n")) {
return SelectionType.LINE_WISE;
}
@@ -515,4 +515,17 @@ public class RegisterGroup {
return SelectionType.CHARACTER_WISE;
}
}
@Nullable
@Override
public Element getState() {
Element element = new Element("registers");
saveData(element);
return element;
}
@Override
public void loadState(@NotNull Element state) {
readData(state);
}
}

View File

@@ -19,6 +19,10 @@ package com.maddyhome.idea.vim.group;
import com.google.common.collect.Lists;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.components.PersistentStateComponent;
import com.intellij.openapi.components.RoamingType;
import com.intellij.openapi.components.State;
import com.intellij.openapi.components.Storage;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.editor.*;
import com.intellij.openapi.editor.colors.EditorColors;
@@ -60,7 +64,10 @@ import java.text.ParsePosition;
import java.util.List;
import java.util.*;
public class SearchGroup {
@State(name = "VimSearchSettings", storages = {
@Storage(value = "$APP_CONFIG$/vim_settings.xml", roamingType = RoamingType.DISABLED)
})
public class SearchGroup implements PersistentStateComponent<Element> {
public SearchGroup() {
final OptionsManager options = OptionsManager.INSTANCE;
options.getHlsearch().addOptionChangeListener((oldValue, newValue) -> {
@@ -90,8 +97,7 @@ public class SearchGroup {
showSearchHighlight = show;
}
@Nullable
public String getLastSearch() {
public @Nullable String getLastSearch() {
return lastSearch;
}
@@ -101,8 +107,7 @@ public class SearchGroup {
return lastDir;
}
@Nullable
public String getLastPattern() {
public @Nullable String getLastPattern() {
return lastPattern;
}
@@ -123,8 +128,7 @@ public class SearchGroup {
// This method should not be private because it's used in external plugins
@SuppressWarnings("WeakerAccess")
@NotNull
public static List<TextRange> findAll(@NotNull Editor editor,
public static @NotNull List<TextRange> findAll(@NotNull Editor editor,
@NotNull String pattern,
int startLine,
int endLine,
@@ -172,8 +176,7 @@ public class SearchGroup {
return results;
}
@NotNull
private static ReplaceConfirmationChoice confirmChoice(@NotNull Editor editor, @NotNull String match, @NotNull Caret caret, int startoff) {
private static @NotNull ReplaceConfirmationChoice confirmChoice(@NotNull Editor editor, @NotNull String match, @NotNull Caret caret, int startoff) {
final Ref<ReplaceConfirmationChoice> result = Ref.create(ReplaceConfirmationChoice.QUIT);
final Function1<KeyStroke, Boolean> keyStrokeProcessor = key -> {
final ReplaceConfirmationChoice choice;
@@ -520,8 +523,7 @@ public class SearchGroup {
return max.getStartOffset();
}
@Nullable
public TextRange getNextSearchRange(@NotNull Editor editor, int count, boolean forwards) {
public @Nullable TextRange getNextSearchRange(@NotNull Editor editor, int count, boolean forwards) {
editor.getCaretModel().removeSecondaryCarets();
TextRange current = findUnderCaret(editor);
@@ -544,8 +546,7 @@ public class SearchGroup {
}
}
@Nullable
private TextRange findNextSearchForGn(@NotNull Editor editor, int count, boolean forwards) {
private @Nullable TextRange findNextSearchForGn(@NotNull Editor editor, int count, boolean forwards) {
if (forwards) {
final EnumSet<SearchOptions> searchOptions = EnumSet.of(SearchOptions.WRAP, SearchOptions.WHOLE_FILE);
return findIt(editor, lastSearch, editor.getCaretModel().getOffset(), count, searchOptions);
@@ -554,15 +555,13 @@ public class SearchGroup {
}
}
@Nullable
private TextRange findUnderCaret(@NotNull Editor editor) {
private @Nullable TextRange findUnderCaret(@NotNull Editor editor) {
final TextRange backSearch = searchBackward(editor, editor.getCaretModel().getOffset() + 1, 1);
if (backSearch == null) return null;
return backSearch.contains(editor.getCaretModel().getOffset()) ? backSearch : null;
}
@Nullable
private TextRange searchBackward(@NotNull Editor editor, int offset, int count) {
private @Nullable TextRange searchBackward(@NotNull Editor editor, int offset, int count) {
// Backward search returns wrongs end offset for some cases. That's why we should perform additional forward search
final EnumSet<SearchOptions> searchOptions = EnumSet.of(SearchOptions.WRAP, SearchOptions.WHOLE_FILE, SearchOptions.BACKWARDS);
final TextRange foundBackward = findIt(editor, lastSearch, offset, count, searchOptions);
@@ -1320,8 +1319,7 @@ public class SearchGroup {
return true;
}
@NotNull
private RangeHighlighter highlightConfirm(@NotNull Editor editor, int start, int end) {
private @NotNull RangeHighlighter highlightConfirm(@NotNull Editor editor, int start, int end) {
TextAttributes color = new TextAttributes(
editor.getColorsScheme().getColor(EditorColors.SELECTION_FOREGROUND_COLOR),
editor.getColorsScheme().getColor(EditorColors.SELECTION_BACKGROUND_COLOR),
@@ -1332,8 +1330,7 @@ public class SearchGroup {
color, HighlighterTargetArea.EXACT_RANGE);
}
@NotNull
private static RangeHighlighter highlightMatch(@NotNull Editor editor, int start, int end, boolean current, String tooltip) {
private static @NotNull RangeHighlighter highlightMatch(@NotNull Editor editor, int start, int end, boolean current, String tooltip) {
TextAttributes attributes = editor.getColorsScheme().getAttributes(EditorColors.TEXT_SEARCH_RESULT_ATTRIBUTES);
if (current) {
// This mimics what IntelliJ does with the Find live preview
@@ -1411,8 +1408,7 @@ public class SearchGroup {
element.addContent(search);
}
@NotNull
private static Element createElementWithText(@NotNull String name, @NotNull String text) {
private static @NotNull Element createElementWithText(@NotNull String name, @NotNull String text) {
return StringHelper.setSafeXmlText(new Element(name), text);
}
@@ -1442,8 +1438,7 @@ public class SearchGroup {
}
}
@Nullable
private static String getSafeChildText(@NotNull Element element, @NotNull String name) {
private static @Nullable String getSafeChildText(@NotNull Element element, @NotNull String name) {
final Element child = element.getChild(name);
return child != null ? StringHelper.getSafeXmlText(child) : null;
}
@@ -1456,6 +1451,19 @@ public class SearchGroup {
VimPlugin.getSearch().updateSearchHighlights();
}
@Nullable
@Override
public Element getState() {
Element element = new Element("search");
saveData(element);
return element;
}
@Override
public void loadState(@NotNull Element state) {
readData(state);
}
public static class DocumentSearchListener implements DocumentListener {
public static DocumentSearchListener INSTANCE = new DocumentSearchListener();
@@ -1524,11 +1532,11 @@ public class SearchGroup {
IGNORE_SMARTCASE,
}
@Nullable private String lastSearch;
@Nullable private String lastPattern;
@Nullable private String lastSubstitute;
@Nullable private String lastReplace;
@Nullable private String lastOffset;
private @Nullable String lastSearch;
private @Nullable String lastPattern;
private @Nullable String lastSubstitute;
private @Nullable String lastReplace;
private @Nullable String lastOffset;
private boolean lastIgnoreSmartCase;
private int lastDir;
private boolean showSearchHighlight = OptionsManager.INSTANCE.getHlsearch().isSet();

View File

@@ -114,9 +114,8 @@ public class WindowGroup {
windows.get(normalized).setAsCurrentWindow(true);
}
@NotNull
private static List<EditorWindow> findWindowsInRow(@NotNull EditorWindow anchor,
@NotNull List<EditorWindow> windows, final boolean vertical) {
private static @NotNull List<EditorWindow> findWindowsInRow(@NotNull EditorWindow anchor,
@NotNull List<EditorWindow> windows, final boolean vertical) {
final Rectangle anchorRect = getEditorWindowRectangle(anchor);
if (anchorRect != null) {
final List<EditorWindow> result = new ArrayList<>();
@@ -145,8 +144,7 @@ public class WindowGroup {
return Collections.singletonList(anchor);
}
@NotNull
private static FileEditorManagerEx getFileEditorManager(@NotNull DataContext context) {
private static @NotNull FileEditorManagerEx getFileEditorManager(@NotNull DataContext context) {
final Project project = PlatformDataKeys.PROJECT.getData(context);
return FileEditorManagerEx.getInstanceEx(Objects.requireNonNull(project));
}
@@ -171,8 +169,7 @@ public class WindowGroup {
}
}
@Nullable
private static Rectangle getEditorWindowRectangle(@NotNull EditorWindow window) {
private static @Nullable Rectangle getEditorWindowRectangle(@NotNull EditorWindow window) {
final EditorWithProviderComposite editor = window.getSelectedEditor();
if (editor != null) {
final Point point = editor.getComponent().getLocationOnScreen();

View File

@@ -40,6 +40,12 @@ import javax.swing.KeyStroke
* If you want to use exactly `<` character, replace it with `&lt;`. E.g. `i&lt;` - i<
* If you want to use comma in mapping, use `«COMMA»`
* Do not place a whitespace around the comma!
*
*
* !! IMPORTANT !!
* You may wonder why the extension points are used instead of any other approach to register actions.
* The reason is startup performance. Using the extension points you don't even have to load classes of actions.
* So, all actions are loaded on demand, including classes in classloader.
*/
class ActionBeanClass : AbstractExtensionPointBean() {
@Attribute("implementation")

View File

@@ -39,8 +39,7 @@ public class DigraphResult {
return new DigraphResult(RES_HANDLED, promptCharacter);
}
@Nullable
public KeyStroke getStroke() {
public @Nullable KeyStroke getStroke() {
return stroke;
}
@@ -53,6 +52,6 @@ public class DigraphResult {
}
private final int result;
@Nullable private final KeyStroke stroke;
private final @Nullable KeyStroke stroke;
private char promptCharacter;
}

View File

@@ -55,8 +55,7 @@ public class DigraphSequence {
return DigraphResult.HANDLED_LITERAL;
}
@NotNull
public DigraphResult processKey(@NotNull KeyStroke key, @NotNull Editor editor) {
public @NotNull DigraphResult processKey(@NotNull KeyStroke key, @NotNull Editor editor) {
switch (digraphState) {
case DIG_STATE_PENDING:
logger.debug("DIG_STATE_PENDING");

View File

@@ -42,18 +42,18 @@ import static java.lang.Integer.max;
* This is a set of helper methods for working with editors. All line and column values are zero based.
*/
public class EditorHelper {
public static int getVisualLineAtTopOfScreen(@NotNull final Editor editor) {
public static int getVisualLineAtTopOfScreen(final @NotNull Editor editor) {
final Rectangle visibleArea = editor.getScrollingModel().getVisibleArea();
return getFullVisualLine(editor, visibleArea.y, visibleArea.y, visibleArea.y + visibleArea.height);
}
public static int getVisualLineAtMiddleOfScreen(@NotNull final Editor editor) {
public static int getVisualLineAtMiddleOfScreen(final @NotNull Editor editor) {
final ScrollingModel scrollingModel = editor.getScrollingModel();
final Rectangle visibleArea = scrollingModel.getVisibleArea();
return editor.yToVisualLine(visibleArea.y + (visibleArea.height / 2));
}
public static int getVisualLineAtBottomOfScreen(@NotNull final Editor editor) {
public static int getVisualLineAtBottomOfScreen(final @NotNull Editor editor) {
final Rectangle visibleArea = editor.getScrollingModel().getVisibleArea();
return getFullVisualLine(editor, visibleArea.y + visibleArea.height, visibleArea.y, visibleArea.y + visibleArea.height);
}
@@ -65,7 +65,7 @@ public class EditorHelper {
* @param editor The editor
* @return The number of characters in the current line
*/
public static int getLineLength(@NotNull final Editor editor) {
public static int getLineLength(final @NotNull Editor editor) {
return getLineLength(editor, editor.getCaretModel().getLogicalPosition().line);
}
@@ -77,7 +77,7 @@ public class EditorHelper {
* @param line The logical line within the file
* @return The number of characters in the specified line
*/
public static int getLineLength(@NotNull final Editor editor, final int line) {
public static int getLineLength(final @NotNull Editor editor, final int line) {
if (getLineCount(editor) == 0) {
return 0;
}
@@ -94,7 +94,7 @@ public class EditorHelper {
* @param line The visual line within the file
* @return The number of characters in the specified line
*/
public static int getVisualLineLength(@NotNull final Editor editor, final int line) {
public static int getVisualLineLength(final @NotNull Editor editor, final int line) {
return getLineLength(editor, visualLineToLogicalLine(editor, line));
}
@@ -105,7 +105,7 @@ public class EditorHelper {
* @param editor The editor
* @return The number of visible lines in the file
*/
public static int getVisualLineCount(@NotNull final Editor editor) {
public static int getVisualLineCount(final @NotNull Editor editor) {
int count = getLineCount(editor);
return count == 0 ? 0 : logicalLineToVisualLine(editor, count - 1) + 1;
}
@@ -116,7 +116,7 @@ public class EditorHelper {
* @param editor The editor
* @return The file line count
*/
public static int getLineCount(@NotNull final Editor editor) {
public static int getLineCount(final @NotNull Editor editor) {
int len = editor.getDocument().getLineCount();
if (editor.getDocument().getTextLength() > 0 &&
editor.getDocument().getCharsSequence().charAt(editor.getDocument().getTextLength() - 1) == '\n') {
@@ -132,7 +132,7 @@ public class EditorHelper {
* @param editor The editor
* @return The file's character count
*/
public static int getFileSize(@NotNull final Editor editor) {
public static int getFileSize(final @NotNull Editor editor) {
return getFileSize(editor, false);
}
@@ -143,7 +143,7 @@ public class EditorHelper {
* @param includeEndNewLine True include newline
* @return The file's character count
*/
public static int getFileSize(@NotNull final Editor editor, final boolean includeEndNewLine) {
public static int getFileSize(final @NotNull Editor editor, final boolean includeEndNewLine) {
final int len = editor.getDocument().getTextLength();
return includeEndNewLine || len == 0 || editor.getDocument().getCharsSequence().charAt(len - 1) != '\n' ? len : len - 1;
}
@@ -163,7 +163,7 @@ public class EditorHelper {
* @param scrollOffset The value of the 'scrolloff' option
* @return The scroll offset value to use
*/
public static int normalizeScrollOffset(@NotNull final Editor editor, int scrollOffset) {
public static int normalizeScrollOffset(final @NotNull Editor editor, int scrollOffset) {
return Math.min(scrollOffset, getApproximateScreenHeight(editor) / 2);
}
@@ -176,7 +176,7 @@ public class EditorHelper {
* @param editor The editor
* @return The number of screen lines
*/
private static int getApproximateScreenHeight(@NotNull final Editor editor) {
private static int getApproximateScreenHeight(final @NotNull Editor editor) {
int lh = editor.getLineHeight();
int height = editor.getScrollingModel().getVisibleArea().y +
editor.getScrollingModel().getVisibleArea().height -
@@ -190,7 +190,7 @@ public class EditorHelper {
* @param editor The editor
* @return The number of screen columns
*/
public static int getScreenWidth(@NotNull final Editor editor) {
public static int getScreenWidth(final @NotNull Editor editor) {
Rectangle rect = editor.getScrollingModel().getVisibleArea();
Point pt = new Point(rect.width, 0);
VisualPosition vp = editor.xyToVisualPosition(pt);
@@ -204,7 +204,7 @@ public class EditorHelper {
* @param editor The editor
* @return The number of pixels
*/
public static int getColumnWidth(@NotNull final Editor editor) {
public static int getColumnWidth(final @NotNull Editor editor) {
Rectangle rect = editor.getScrollingModel().getVisibleArea();
if (rect.width == 0) return 0;
Point pt = new Point(rect.width, 0);
@@ -220,7 +220,7 @@ public class EditorHelper {
* @param editor The editor
* @return The column number
*/
public static int getVisualColumnAtLeftOfScreen(@NotNull final Editor editor) {
public static int getVisualColumnAtLeftOfScreen(final @NotNull Editor editor) {
int cw = getColumnWidth(editor);
if (cw == 0) return 0;
return (editor.getScrollingModel().getHorizontalScrollOffset() + cw - 1) / cw;
@@ -233,7 +233,7 @@ public class EditorHelper {
* @param line The visual line number to convert
* @return The logical line number
*/
public static int visualLineToLogicalLine(@NotNull final Editor editor, final int line) {
public static int visualLineToLogicalLine(final @NotNull Editor editor, final int line) {
int logicalLine = editor.visualToLogicalPosition(new VisualPosition(line, 0)).line;
return normalizeLine(editor, logicalLine);
}
@@ -246,7 +246,7 @@ public class EditorHelper {
* @param line The logical line number to convert
* @return The visual line number
*/
public static int logicalLineToVisualLine(@NotNull final Editor editor, final int line) {
public static int logicalLineToVisualLine(final @NotNull Editor editor, final int line) {
if (editor instanceof EditorImpl) {
// This is faster than simply calling Editor#logicalToVisualPosition
return ((EditorImpl) editor).offsetToVisualLine(editor.getDocument().getLineStartOffset(line));
@@ -254,7 +254,7 @@ public class EditorHelper {
return editor.logicalToVisualPosition(new LogicalPosition(line, 0)).line;
}
public static int getOffset(@NotNull final Editor editor, final int line, final int column) {
public static int getOffset(final @NotNull Editor editor, final int line, final int column) {
return editor.logicalPositionToOffset(new LogicalPosition(line, column));
}
@@ -265,7 +265,7 @@ public class EditorHelper {
* @param line The logical line to get the start offset for.
* @return 0 if line is &lt 0, file size of line is bigger than file, else the start offset for the line
*/
public static int getLineStartOffset(@NotNull final Editor editor, final int line) {
public static int getLineStartOffset(final @NotNull Editor editor, final int line) {
if (line < 0) {
return 0;
}
@@ -285,7 +285,7 @@ public class EditorHelper {
* @param allowEnd True include newline
* @return 0 if line is &lt 0, file size of line is bigger than file, else the end offset for the line
*/
public static int getLineEndOffset(@NotNull final Editor editor, final int line, final boolean allowEnd) {
public static int getLineEndOffset(final @NotNull Editor editor, final int line, final boolean allowEnd) {
if (line < 0) {
return 0;
}
@@ -307,7 +307,7 @@ public class EditorHelper {
* @param line The visual line number to normalize
* @return The normalized visual line number
*/
public static int normalizeVisualLine(@NotNull final Editor editor, final int line) {
public static int normalizeVisualLine(final @NotNull Editor editor, final int line) {
return Math.max(0, Math.min(line, getVisualLineCount(editor) - 1));
}
@@ -319,7 +319,7 @@ public class EditorHelper {
* @param line The logical line number to normalize
* @return The normalized logical line number
*/
public static int normalizeLine(@NotNull final Editor editor, final int line) {
public static int normalizeLine(final @NotNull Editor editor, final int line) {
return Math.max(0, Math.min(line, getLineCount(editor) - 1));
}
@@ -333,7 +333,7 @@ public class EditorHelper {
* @param allowEnd True if newline allowed
* @return The normalized column number
*/
public static int normalizeVisualColumn(@NotNull final Editor editor, final int line, final int col, final boolean allowEnd) {
public static int normalizeVisualColumn(final @NotNull Editor editor, final int line, final int col, final boolean allowEnd) {
return Math.max(0, Math.min(col, getVisualLineLength(editor, line) - (allowEnd ? 0 : 1)));
}
@@ -347,7 +347,7 @@ public class EditorHelper {
* @param allowEnd True if newline allowed
* @return The normalized column number
*/
public static int normalizeColumn(@NotNull final Editor editor, final int line, final int col, final boolean allowEnd) {
public static int normalizeColumn(final @NotNull Editor editor, final int line, final int col, final boolean allowEnd) {
return Math.min(Math.max(0, getLineLength(editor, line) - (allowEnd ? 0 : 1)), col);
}
@@ -361,7 +361,7 @@ public class EditorHelper {
* @param allowEnd true if the offset can be one past the last character on the line, false if not
* @return The normalized column number
*/
public static int normalizeOffset(@NotNull final Editor editor, final int line, final int offset, final boolean allowEnd) {
public static int normalizeOffset(final @NotNull Editor editor, final int line, final int offset, final boolean allowEnd) {
if (getFileSize(editor, allowEnd) == 0) {
return 0;
}
@@ -371,11 +371,11 @@ public class EditorHelper {
return Math.max(Math.min(offset, max), min);
}
public static int normalizeOffset(@NotNull final Editor editor, final int offset) {
public static int normalizeOffset(final @NotNull Editor editor, final int offset) {
return normalizeOffset(editor, offset, true);
}
public static int normalizeOffset(@NotNull final Editor editor, int offset, final boolean allowEnd) {
public static int normalizeOffset(final @NotNull Editor editor, int offset, final boolean allowEnd) {
if (offset <= 0) {
offset = 0;
}
@@ -388,11 +388,11 @@ public class EditorHelper {
}
public static int getLeadingCharacterOffset(@NotNull final Editor editor, final int line) {
public static int getLeadingCharacterOffset(final @NotNull Editor editor, final int line) {
return getLeadingCharacterOffset(editor, line, 0);
}
public static int getLeadingCharacterOffset(@NotNull final Editor editor, final int line, final int col) {
public static int getLeadingCharacterOffset(final @NotNull Editor editor, final int line, final int col) {
int start = getLineStartOffset(editor, line) + col;
int end = getLineEndOffset(editor, line, true);
CharSequence chars = editor.getDocument().getCharsSequence();
@@ -411,8 +411,7 @@ public class EditorHelper {
return pos;
}
@NotNull
public static String getLeadingWhitespace(@NotNull final Editor editor, final int line) {
public static @NotNull String getLeadingWhitespace(final @NotNull Editor editor, final int line) {
int start = getLineStartOffset(editor, line);
int end = getLeadingCharacterOffset(editor, line);
@@ -425,8 +424,7 @@ public class EditorHelper {
* @param file The virtual file get the editor for
* @return The matching editor or null if no match was found
*/
@Nullable
public static Editor getEditor(@Nullable final VirtualFile file) {
public static @Nullable Editor getEditor(final @Nullable VirtualFile file) {
if (file == null) {
return null;
}
@@ -450,7 +448,7 @@ public class EditorHelper {
* @param pos The visual position to convert
* @return The file offset of the visual position
*/
public static int visualPositionToOffset(@NotNull final Editor editor, @NotNull final VisualPosition pos) {
public static int visualPositionToOffset(final @NotNull Editor editor, final @NotNull VisualPosition pos) {
return editor.logicalPositionToOffset(editor.visualToLogicalPosition(pos));
}
@@ -462,15 +460,13 @@ public class EditorHelper {
* @param end The ending offset (exclusive)
* @return The string, never null but empty if start == end
*/
@NotNull
public static String getText(@NotNull final Editor editor, final int start, final int end) {
public static @NotNull String getText(final @NotNull Editor editor, final int start, final int end) {
if (start == end) return "";
final CharSequence documentChars = editor.getDocument().getCharsSequence();
return documentChars.subSequence(normalizeOffset(editor, start), normalizeOffset(editor, end)).toString();
}
@NotNull
public static String getText(@NotNull final Editor editor, @NotNull final TextRange range) {
public static @NotNull String getText(final @NotNull Editor editor, final @NotNull TextRange range) {
int len = range.size();
if (len == 1) {
return getText(editor, range.getStartOffset(), range.getEndOffset());
@@ -505,7 +501,7 @@ public class EditorHelper {
* @param offset The offset within the line
* @return The offset of the line start
*/
public static int getLineStartForOffset(@NotNull final Editor editor, final int offset) {
public static int getLineStartForOffset(final @NotNull Editor editor, final int offset) {
LogicalPosition pos = editor.offsetToLogicalPosition(normalizeOffset(editor, offset));
return editor.getDocument().getLineStartOffset(pos.line);
}
@@ -517,12 +513,12 @@ public class EditorHelper {
* @param offset The offset within the line
* @return The offset of the line end
*/
public static int getLineEndForOffset(@NotNull final Editor editor, final int offset) {
public static int getLineEndForOffset(final @NotNull Editor editor, final int offset) {
LogicalPosition pos = editor.offsetToLogicalPosition(normalizeOffset(editor, offset));
return editor.getDocument().getLineEndOffset(pos.line);
}
private static int getLineCharCount(@NotNull final Editor editor, final int line) {
private static int getLineCharCount(final @NotNull Editor editor, final int line) {
return getLineEndOffset(editor, line, true) - getLineStartOffset(editor, line);
}
@@ -533,18 +529,16 @@ public class EditorHelper {
* @param line The logical line to get the text for
* @return The requested line
*/
@NotNull
public static String getLineText(@NotNull final Editor editor, final int line) {
public static @NotNull String getLineText(final @NotNull Editor editor, final int line) {
return getText(editor, getLineStartOffset(editor, line), getLineEndOffset(editor, line, true));
}
@NotNull
public static CharBuffer getLineBuffer(@NotNull final Editor editor, final int line) {
public static @NotNull CharBuffer getLineBuffer(final @NotNull Editor editor, final int line) {
int start = getLineStartOffset(editor, line);
return CharBuffer.wrap(editor.getDocument().getCharsSequence(), start, start + getLineCharCount(editor, line));
}
public static boolean isLineEmpty(@NotNull final Editor editor, final int line, final boolean allowBlanks) {
public static boolean isLineEmpty(final @NotNull Editor editor, final int line, final boolean allowBlanks) {
CharSequence chars = editor.getDocument().getCharsSequence();
if (chars.length() == 0) return true;
int offset = getLineStartOffset(editor, line);
@@ -565,8 +559,7 @@ public class EditorHelper {
return false;
}
@NotNull
public static String pad(@NotNull final Editor editor, @NotNull DataContext context, int line, final int to) {
public static @NotNull String pad(final @NotNull Editor editor, @NotNull DataContext context, int line, final int to) {
final int len = getLineLength(editor, line);
if(len >= to) return "";
@@ -579,8 +572,7 @@ public class EditorHelper {
*
* @param editor The editor from which the carets are taken
*/
@NotNull
public static List<Caret> getOrderedCaretsList(@NotNull Editor editor) {
public static @NotNull List<Caret> getOrderedCaretsList(@NotNull Editor editor) {
@NotNull List<Caret> carets = editor.getCaretModel().getAllCarets();
carets.sort(Comparator.comparingInt(Caret::getOffset));
@@ -603,7 +595,7 @@ public class EditorHelper {
* @param editor The editor to scroll
* @param visualLine The visual line to scroll to the current caret location
*/
public static void scrollVisualLineToCaretLocation(@NotNull final Editor editor, int visualLine) {
public static void scrollVisualLineToCaretLocation(final @NotNull Editor editor, int visualLine) {
final ScrollingModel scrollingModel = editor.getScrollingModel();
final Rectangle visibleArea = scrollingModel.getVisibleArea();
final int caretScreenOffset = editor.visualLineToY(editor.getCaretModel().getVisualPosition().line) - visibleArea.y;
@@ -634,7 +626,7 @@ public class EditorHelper {
* @param visualLine The visual line to place at the top of the current window
* @return Returns true if the window was moved
*/
public static boolean scrollVisualLineToTopOfScreen(@NotNull final Editor editor, int visualLine) {
public static boolean scrollVisualLineToTopOfScreen(final @NotNull Editor editor, int visualLine) {
final ScrollingModel scrollingModel = editor.getScrollingModel();
int inlayHeight = getHeightOfVisualLineInlays(editor, visualLine, true);
int y = editor.visualLineToY(visualLine) - inlayHeight;
@@ -697,7 +689,7 @@ public class EditorHelper {
* @param pages The number of pages to scroll. Positive is scroll down (lines move up). Negative is scroll up.
* @return The visual line to place the caret on. -1 if the page wasn't scrolled at all.
*/
public static int scrollFullPage(@NotNull final Editor editor, int pages) {
public static int scrollFullPage(final @NotNull Editor editor, int pages) {
if (pages > 0) {
return scrollFullPageDown(editor, pages);
}
@@ -707,7 +699,7 @@ public class EditorHelper {
return -1; // visual lines are 1-based
}
public static int lastColumnForLine(@NotNull final Editor editor, int line, boolean allowEnd) {
public static int lastColumnForLine(final @NotNull Editor editor, int line, boolean allowEnd) {
return editor.offsetToVisualPosition(EditorHelper.getLineEndOffset(editor, line, allowEnd)).column;
}
@@ -741,7 +733,7 @@ public class EditorHelper {
UserDataManager.setVimLastColumn(caret, targetColumn);
}
private static int scrollFullPageDown(@NotNull final Editor editor, int pages) {
private static int scrollFullPageDown(final @NotNull Editor editor, int pages) {
final Rectangle visibleArea = editor.getScrollingModel().getVisibleArea();
final int lineCount = getVisualLineCount(editor);
@@ -784,7 +776,7 @@ public class EditorHelper {
return caretLine;
}
private static int scrollFullPageUp(@NotNull final Editor editor, int pages) {
private static int scrollFullPageUp(final @NotNull Editor editor, int pages) {
final Rectangle visibleArea = editor.getScrollingModel().getVisibleArea();
final int lineHeight = editor.getLineHeight();
@@ -813,7 +805,7 @@ public class EditorHelper {
return caretLine;
}
private static int getFullVisualLine(@NotNull final Editor editor, int y, int topBound, int bottomBound) {
private static int getFullVisualLine(final @NotNull Editor editor, int y, int topBound, int bottomBound) {
int line = editor.yToVisualLine(y);
int yActual = editor.visualLineToY(line);
if (yActual < topBound) {
@@ -825,7 +817,7 @@ public class EditorHelper {
return line;
}
private static int getHeightOfVisualLineInlays(@NotNull final Editor editor, int visualLine, boolean above) {
private static int getHeightOfVisualLineInlays(final @NotNull Editor editor, int visualLine, boolean above) {
InlayModel inlayModel = editor.getInlayModel();
List<Inlay> inlays = inlayModel.getBlockElementsForVisualLine(visualLine, above);
int inlayHeight = 0;
@@ -841,8 +833,7 @@ public class EditorHelper {
* @param editor The editor
* @return The virtual file for the editor
*/
@Nullable
public static VirtualFile getVirtualFile(@NotNull Editor editor) {
public static @Nullable VirtualFile getVirtualFile(@NotNull Editor editor) {
return FileDocumentManager.getInstance().getFile(editor.getDocument());
}

View File

@@ -31,15 +31,13 @@ import java.io.InputStreamReader;
*/
public class MacKeyRepeat {
public static final String FMT = "defaults %s -globalDomain ApplePressAndHoldEnabled";
@NotNull private static final MacKeyRepeat INSTANCE = new MacKeyRepeat();
private static final @NotNull MacKeyRepeat INSTANCE = new MacKeyRepeat();
@NotNull
public static MacKeyRepeat getInstance() {
public static @NotNull MacKeyRepeat getInstance() {
return INSTANCE;
}
@Nullable
public Boolean isEnabled() {
public @Nullable Boolean isEnabled() {
final String command = String.format(FMT, "read");
try {
final Process process = Runtime.getRuntime().exec(command);
@@ -77,8 +75,7 @@ public class MacKeyRepeat {
}
}
@NotNull
private static String read(@NotNull InputStream stream) throws IOException {
private static @NotNull String read(@NotNull InputStream stream) throws IOException {
return CharStreams.toString(new InputStreamReader(stream));
}
}

View File

@@ -30,7 +30,7 @@ import java.util.ResourceBundle;
public class MessageHelper {
@Nullable private static Reference<ResourceBundle> ourBundle;
private static @Nullable Reference<ResourceBundle> ourBundle;
@NonNls
private static final String BUNDLE = "messages";
@@ -38,21 +38,18 @@ public class MessageHelper {
private MessageHelper() {
}
@NotNull
public static String message(@NotNull @PropertyKey(resourceBundle = BUNDLE)String key, Object... params) {
public static @NotNull String message(@NotNull @PropertyKey(resourceBundle = BUNDLE)String key, Object... params) {
return CommonBundle.message(getBundle(), key, params);
}
/*
* This method added for jruby access
*/
@NotNull
public static String message(@NotNull @PropertyKey(resourceBundle = BUNDLE)String key) {
public static @NotNull String message(@NotNull @PropertyKey(resourceBundle = BUNDLE)String key) {
return CommonBundle.message(getBundle(), key);
}
@NotNull
protected static ResourceBundle getBundle() {
protected static @NotNull ResourceBundle getBundle() {
ResourceBundle bundle = null;
if (ourBundle != null) bundle = ourBundle.get();
if (bundle == null) {

View File

@@ -95,6 +95,7 @@ public class PsiHelper {
if (element.getLanguage().getID().equals("JAVA")) {
// HACK: for Java classes and methods, we want to jump to the opening brace
int textOffset = element.getTextOffset();
// TODO: Try to get rid of `getText()` because it takes a lot of time to calculate the string
int braceIndex = element.getText().indexOf('{', textOffset - offset);
if (braceIndex >= 0) {
offset += braceIndex;
@@ -113,8 +114,7 @@ public class PsiHelper {
}
}
@Nullable
public static PsiFile getFile(@NotNull Editor editor) {
public static @Nullable PsiFile getFile(@NotNull Editor editor) {
VirtualFile vf = EditorHelper.getVirtualFile(editor);
if (vf != null) {
Project proj = editor.getProject();

View File

@@ -1,76 +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.helper;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.command.CommandProcessor;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.project.Project;
import com.maddyhome.idea.vim.KeyHandler;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/**
* This provides some helper methods to run code as a command and an application write action
*/
public class RunnableHelper {
private static final Logger logger = Logger.getInstance(KeyHandler.class.getName());
private RunnableHelper() {}
public static void runReadCommand(@Nullable Project project, @NotNull Runnable cmd, @Nullable String name, @Nullable Object groupId) {
if (logger.isDebugEnabled()) {
logger.debug("Run read command: " + name);
}
CommandProcessor.getInstance().executeCommand(project, new ReadAction(cmd), name, groupId);
}
public static void runWriteCommand(@Nullable Project project, @NotNull Runnable cmd, @Nullable String name, @Nullable Object groupId) {
if (logger.isDebugEnabled()) {
logger.debug("Run write command " + name);
}
CommandProcessor.getInstance().executeCommand(project, new WriteAction(cmd), name, groupId);
}
static class ReadAction implements Runnable {
@NotNull private final Runnable cmd;
ReadAction(@NotNull Runnable cmd) {
this.cmd = cmd;
}
@Override
public void run() {
ApplicationManager.getApplication().runReadAction(cmd);
}
}
static class WriteAction implements Runnable {
@NotNull private final Runnable cmd;
WriteAction(@NotNull Runnable cmd) {
this.cmd = cmd;
}
@Override
public void run() {
ApplicationManager.getApplication().runWriteAction(cmd);
}
}
}

View File

@@ -0,0 +1,43 @@
/*
* 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.helper
import com.intellij.openapi.application.ApplicationManager
import com.intellij.openapi.command.CommandProcessor
import com.intellij.openapi.diagnostic.debug
import com.intellij.openapi.diagnostic.logger
import com.intellij.openapi.project.Project
/**
* This provides some helper methods to run code as a command and an application write action
*/
object RunnableHelper {
private val logger = logger<RunnableHelper>()
@JvmStatic
fun runReadCommand(project: Project?, cmd: Runnable, name: String?, groupId: Any?) {
logger.debug { "Run read command: $name" }
CommandProcessor.getInstance().executeCommand(project, { ApplicationManager.getApplication().runReadAction(cmd) }, name, groupId)
}
@JvmStatic
fun runWriteCommand(project: Project?, cmd: Runnable, name: String?, groupId: Any?) {
logger.debug { "Run write command: $name" }
CommandProcessor.getInstance().executeCommand(project, { ApplicationManager.getApplication().runWriteAction(cmd) }, name, groupId)
}
}

View File

@@ -37,12 +37,13 @@ import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.List;
import java.util.Stack;
import java.util.*;
import java.util.function.Function;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import static com.maddyhome.idea.vim.helper.SearchHelperKtKt.checkInString;
/**
* Helper methods for searching text
*/
@@ -98,17 +99,16 @@ public class SearchHelper {
int pos = caret.getOffset();
int loc = blockChars.indexOf(type);
// What direction should we go now (-1 is backward, 1 is forward)
int dir = loc % 2 == 0 ? -1 : 1;
Direction dir = loc % 2 == 0 ? Direction.BACK : Direction.FORWARD;
// Which character did we find and which should we now search for
char match = blockChars.charAt(loc);
char found = blockChars.charAt(loc - dir);
char found = blockChars.charAt(loc - dir.toInt());
return findBlockLocation(chars, found, match, dir, pos, count, false);
}
@Nullable
public static TextRange findBlockRange(@NotNull Editor editor, @NotNull Caret caret, char type, int count,
boolean isOuter) {
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();
int start = caret.getSelectionStart();
@@ -153,10 +153,10 @@ public class SearchHelper {
int endOffset = quoteRange.getEndOffset();
CharSequence subSequence = chars.subSequence(startOffset, endOffset);
int inQuotePos = pos - startOffset;
int inQuoteStart = findBlockLocation(subSequence, close, type, -1, inQuotePos, count, false);
int inQuoteStart = findBlockLocation(subSequence, close, type, Direction.BACK, inQuotePos, count, false);
if (inQuoteStart != -1) {
startPosInStringFound = true;
int inQuoteEnd = findBlockLocation(subSequence, type, close, 1, inQuoteStart, 1, false);
int inQuoteEnd = findBlockLocation(subSequence, type, close, Direction.FORWARD, inQuoteStart, 1, false);
if (inQuoteEnd != -1) {
bstart = inQuoteStart + startOffset;
bend = inQuoteEnd + startOffset;
@@ -166,9 +166,9 @@ public class SearchHelper {
}
if (!startPosInStringFound) {
bstart = findBlockLocation(chars, close, type, -1, pos, count, false);
bstart = findBlockLocation(chars, close, type, Direction.BACK, pos, count, false);
if (bstart != -1) {
bend = findBlockLocation(chars, type, close, 1, bstart, 1, false);
bend = findBlockLocation(chars, type, close, Direction.FORWARD, bstart, 1, false);
}
}
@@ -206,6 +206,7 @@ public class SearchHelper {
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
final String commentText = comment.getText();
if (commentText.startsWith(prefix) && commentText.endsWith(suffix)) {
final int endOffset = comment.getTextOffset() + comment.getTextLength();
@@ -281,10 +282,10 @@ public class SearchHelper {
// If we found one ...
if (loc >= 0) {
// What direction should we go now (-1 is backward, 1 is forward)
int dir = loc % 2 == 0 ? 1 : -1;
Direction dir = loc % 2 == 0 ? Direction.FORWARD : Direction.BACK;
// Which character did we find and which should we now search for
char found = getPairChars().charAt(loc);
char match = getPairChars().charAt(loc + dir);
char match = getPairChars().charAt(loc + dir.toInt());
res = findBlockLocation(chars, found, match, dir, pos, 1, true);
}
@@ -308,21 +309,29 @@ public class SearchHelper {
private static int findBlockLocation(@NotNull CharSequence chars,
char found,
char match,
int dir,
@NotNull Direction dir,
int pos,
int cnt,
boolean allowInString) {
int res = -1;
final int inCheckPos = dir < 0 && pos > 0 ? pos - 1 : pos;
int initialPos = pos;
Function<Integer, Integer> inCheckPosF = x -> dir == Direction.BACK && x > 0 ? x - 1 : x + 1;
final int inCheckPos = inCheckPosF.apply(pos);
boolean inString = checkInString(chars, inCheckPos, true);
boolean initialInString = inString;
boolean inChar = checkInString(chars, inCheckPos, false);
boolean initial = true;
int stack = 0;
// 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);
if (ci == null) {
return -1;
}
Character c = ci.first;
pos = ci.second;
// If we found a match and we're not in a string...
if (chars.charAt(pos) == match && (allowInString ? initialInString == inString : !inString) && !inChar) {
if (c == match && (allowInString ? initialInString == inString : !inString) && !inChar) {
// We found our match
if (stack == 0) {
res = pos;
@@ -334,26 +343,24 @@ public class SearchHelper {
}
}
// End of line - mark not in a string any more (in case we started in the middle of one
else if (chars.charAt(pos) == '\n') {
else if (c == '\n') {
inString = false;
inChar = false;
}
else if (!initial) {
else if (pos != initialPos) {
// We found another character like our original - belongs to another pair
if (!inString && !inChar && chars.charAt(pos) == found) {
if (!inString && !inChar && c == found) {
stack++;
}
// We found the start/end of a string
else if (!inChar && isQuoteWithoutEscape(chars, pos, '"')) {
inString = !inString;
else if (!inChar) {
inString = checkInString(chars, inCheckPosF.apply(pos), true);
}
else if (!inString && isQuoteWithoutEscape(chars, pos, '\'')) {
inChar = !inChar;
else if (!inString) {
inChar = checkInString(chars, inCheckPosF.apply(pos), false);
}
}
pos += dir;
initial = false;
pos += dir.toInt();
}
return res;
@@ -366,18 +373,13 @@ public class SearchHelper {
if (chars.charAt(pos) != quote) return false;
int backslashCounter = 0;
while (pos-- > 0) {
if (chars.charAt(pos) == '\\') {
backslashCounter++;
}
else {
break;
}
while (pos-- > 0 && chars.charAt(pos) == '\\') {
backslashCounter++;
}
return backslashCounter % 2 == 0;
}
private enum Direction {
public enum Direction {
BACK(-1), FORWARD(1);
private final int value;
@@ -386,7 +388,7 @@ public class SearchHelper {
value = i;
}
private int toInt() {
public int toInt() {
return value;
}
}
@@ -419,10 +421,28 @@ 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
) {
int dir = direction.toInt();
while (pos >= 0 && pos < chars.length()) {
final char c = chars.charAt(pos);
if (needles.contains(c) && (pos == 0 || searchEscaped || isQuoteWithoutEscape(chars, pos, c))) {
return Pair.create(c, pos);
}
pos += dir;
}
return null;
}
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 || chars.charAt(pos - 1) != '\\')) {
if (chars.charAt(pos) == c && (pos == 0 || searchEscaped || isQuoteWithoutEscape(chars, pos, c))) {
return pos;
}
pos += direction.toInt();
@@ -441,8 +461,7 @@ public class SearchHelper {
}
@Nullable
public static 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();
@@ -519,7 +538,7 @@ public class SearchHelper {
/**
* Returns true if there is a html at the given position. Ignores tags with a trailing slash like <aaa/>.
*/
private static boolean isInHTMLTag(@NotNull final CharSequence sequence, final int position, final boolean isEndtag) {
private static boolean isInHTMLTag(final @NotNull CharSequence sequence, final int position, final boolean isEndtag) {
int openingBracket = -1;
for (int i = position; i >= 0 && i < sequence.length(); i--) {
if (sequence.charAt(i) == '<') {
@@ -551,8 +570,7 @@ public class SearchHelper {
return closingBracket != -1 && sequence.charAt(closingBracket - 1) != '/';
}
@Nullable
private static Pair<TextRange,String> findUnmatchedClosingTag(@NotNull final 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 '>'
@@ -588,8 +606,7 @@ public class SearchHelper {
return null;
}
@Nullable
private static 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
@@ -619,9 +636,8 @@ public class SearchHelper {
}
@Nullable
public static TextRange findBlockQuoteInLineRange(@NotNull Editor editor, @NotNull Caret caret, char quote,
boolean isOuter) {
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();
if (pos >= chars.length() || chars.charAt(pos) == '\n') {
@@ -664,27 +680,6 @@ public class SearchHelper {
return new TextRange(start, end + 1);
}
private static boolean checkInString(@NotNull CharSequence chars, int pos, boolean str) {
if (chars.length() == 0) return false;
int offset = pos;
while (offset > 0 && chars.charAt(offset) != '\n') {
offset--;
}
boolean inString = false;
boolean inChar = false;
for (int i = offset; i <= pos; i++) {
if (!inChar && isQuoteWithoutEscape(chars, i, '"')) {
inString = !inString;
}
else if (!inString && isQuoteWithoutEscape(chars, i, '\'')) {
inChar = !inChar;
}
}
return str ? inString : inChar;
}
public static int findNextCamelStart(@NotNull Editor editor, @NotNull Caret caret, int count) {
CharSequence chars = editor.getDocument().getCharsSequence();
int pos = caret.getOffset();
@@ -779,8 +774,7 @@ public class SearchHelper {
/**
* This counts all the words in the file.
*/
@NotNull
public static CountPosition countWords(@NotNull Editor editor) {
public static @NotNull CountPosition countWords(@NotNull Editor editor) {
int size = EditorHelper.getFileSize(editor);
return countWords(editor, 0, size);
@@ -789,16 +783,14 @@ public class SearchHelper {
/**
* This counts all the words in the file.
*/
@NotNull
public static CountPosition countWords(@NotNull Editor editor, int start, int end) {
public static @NotNull CountPosition countWords(@NotNull Editor editor, int start, int end) {
CharSequence chars = editor.getDocument().getCharsSequence();
int offset = editor.getCaretModel().getOffset();
return countWords(chars, start, end, offset);
}
@NotNull
public static CountPosition countWords(@NotNull CharSequence chars, int start, int end, int offset) {
public static @NotNull CountPosition countWords(@NotNull CharSequence chars, int start, int end, int offset) {
int count = 1;
int position = 0;
int last = -1;
@@ -924,9 +916,8 @@ public class SearchHelper {
return res;
}
@NotNull
public static List<TextRange> findNumbersInRange(@NotNull final 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;
@@ -950,9 +941,8 @@ public class SearchHelper {
return result;
}
@Nullable
public static TextRange findNumberUnderCursor(@NotNull final 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);
@@ -974,9 +964,8 @@ public class SearchHelper {
* @param startPosOnLine - start offset to search
* @return - text range with number
*/
@Nullable
public static TextRange findNumberInText(@NotNull final 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);
@@ -1066,9 +1055,8 @@ public class SearchHelper {
/**
* Searches for digits block that matches parameters
*/
@NotNull
private static Pair<Integer, Integer> findRange(@NotNull final 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++;
@@ -1108,8 +1096,7 @@ public class SearchHelper {
* @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
*/
@Nullable
public static TextRange findWordUnderCursor(@NotNull Editor editor, @NotNull Caret caret) {
public static @Nullable TextRange findWordUnderCursor(@NotNull Editor editor, @NotNull Caret caret) {
CharSequence chars = editor.getDocument().getCharsSequence();
int stop = EditorHelper.getLineEndOffset(editor, caret.getLogicalPosition().line, true);
@@ -1159,9 +1146,8 @@ public class SearchHelper {
}
@Contract("_, _, _, _, _, _, _ -> new")
@NotNull
public static 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);
@@ -1892,8 +1878,7 @@ public class SearchHelper {
}
@Contract("_, _, _, _ -> new")
@NotNull
public static 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);
@@ -2030,8 +2015,7 @@ public class SearchHelper {
return line;
}
@Nullable
public static 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);
@@ -2150,8 +2134,7 @@ public class SearchHelper {
return PsiHelper.findMethodEnd(editor, caret.getOffset(), count);
}
@NotNull
private static String getPairChars() {
private static @NotNull String getPairChars() {
if (pairsChars == null) {
ListOption lo = OptionsManager.INSTANCE.getMatchpairs();
lo.addOptionChangeListenerAndExecute((oldValue, newValue) -> pairsChars = parseOption(lo));
@@ -2160,8 +2143,7 @@ public class SearchHelper {
return pairsChars;
}
@NotNull
private static String parseOption(@NotNull ListOption option) {
private static @NotNull String parseOption(@NotNull ListOption option) {
List<String> vals = option.values();
StringBuilder res = new StringBuilder();
for (String s : vals) {
@@ -2191,8 +2173,8 @@ public class SearchHelper {
private final int position;
}
@Nullable private static String pairsChars = null;
@NotNull private static final String blockChars = "{}()[]<>";
private static @Nullable String pairsChars = null;
private static final @NotNull String blockChars = "{}()[]<>";
private static final Logger logger = Logger.getInstance(SearchHelper.class.getName());
}

View File

@@ -0,0 +1,163 @@
/*
* 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.helper
import com.maddyhome.idea.vim.helper.SearchHelper.Direction
import com.maddyhome.idea.vim.helper.SearchHelper.findPositionOfFirstCharacter
private data class State(val position: Int, val trigger: Char, val inQuote: Boolean?, val lastOpenSingleQuotePos: Int)
// bounds are considered inside corresponding quotes
fun checkInString(chars: CharSequence, currentPos: Int, str: Boolean): Boolean {
val begin = findPositionOfFirstCharacter(chars, currentPos, setOf('\n'), false, Direction.BACK)?.second ?: 0
val changes = quoteChanges(chars, begin)
// TODO: here we need to keep only the latest element in beforePos (if any) and
// don't need atAndAfterPos to be eagerly collected
var (beforePos, atAndAfterPos) = changes.partition { it.position < currentPos }
var (atPos, afterPos) = atAndAfterPos.partition { it.position == currentPos }
assert(atPos.size <= 1) { "Multiple characters at position $currentPos in string $chars" }
if (atPos.isNotEmpty()) {
val atPosChange = atPos[0]
if (afterPos.isEmpty()) {
// it is situation when cursor is on closing quote, so we must consider that we are inside quotes pair
afterPos = afterPos.toMutableList()
afterPos.add(atPosChange)
} else {
// it is situation when cursor is on opening quote, so we must consider that we are inside quotes pair
beforePos = beforePos.toMutableList()
beforePos.add(atPosChange)
}
}
val lastBeforePos = beforePos.lastOrNull()
// if opening quote was found before pos (inQuote=true), it doesn't mean pos is in string, we need
// to find closing quote to be sure
var posInQuote = lastBeforePos?.inQuote?.let { if (it) null else it }
val lastOpenSingleQuotePosBeforeCurrentPos = lastBeforePos?.lastOpenSingleQuotePos ?: -1
var posInChar = if (lastOpenSingleQuotePosBeforeCurrentPos == -1) false else null
var inQuote: Boolean? = null
for((_, trigger, inQuoteAfter, lastOpenSingleQuotePosAfter) in afterPos) {
inQuote = inQuoteAfter
if (posInQuote != null && posInChar != null) break
if (posInQuote == null && inQuoteAfter != null) {
// if we found double quote
if (trigger == '"') {
// then previously it has opposite value
posInQuote = !inQuoteAfter
// if we found single quote
} else if (trigger == '\'') {
// then we found closing single quote
posInQuote = inQuoteAfter
}
}
if (posInChar == null && lastOpenSingleQuotePosAfter != lastOpenSingleQuotePosBeforeCurrentPos) {
// if we found double quote and we reset position of last single quote
if (trigger == '"' && lastOpenSingleQuotePosAfter == -1) {
// then it means previously there supposed to be open single quote
posInChar = false
// if we found single quote
} else if (trigger == '\'') {
// if we reset position of last single quote
// it means we found closing single quote
// else it means we found opening single quote
posInChar = lastOpenSingleQuotePosAfter == -1
}
}
}
return if (str) posInQuote != null && posInQuote && (inQuote == null || !inQuote) else posInChar != null && posInChar
}
// yields changes of inQuote and lastOpenSingleQuotePos during while iterating over chars
// rules are that:
// - escaped quotes are skipped
// - single quoted group may enclose only one character, maybe escaped,
// - so distance between opening and closing single quotes cannot be more than 3
// - bounds are considered inside corresponding quotes
private fun quoteChanges(chars: CharSequence, begin: Int) = sequence {
// position of last found unpaired single quote
var lastOpenSingleQuotePos = -1
// whether we are in double quotes
// true - definitely yes
// false - definitely no
// null - maybe yes, in case we found such combination: '"
// in that situation it may be double quote inside single quotes, so we cannot threat it as double quote pair open/close
var inQuote: Boolean? = false
val charsToSearch = setOf('\'', '"', '\n')
var found = findPositionOfFirstCharacter(chars, begin, charsToSearch, false, Direction.FORWARD)
while (found != null && found.first != '\n') {
val i = found.second
val c = found.first
when (c) {
'"' -> {
// if [maybe] in quote, then we know we found closing quote, so now we surely are not in quote
if (inQuote == null || inQuote) {
// we just found closing double quote
inQuote = false
// reset last found single quote, as it was in string literal
lastOpenSingleQuotePos = -1
// if we previously found unclosed single quote
} else if (lastOpenSingleQuotePos >= 0) {
// ...but we are too far from it
if (i - lastOpenSingleQuotePos > 2) {
// then it definitely was not opening single quote
lastOpenSingleQuotePos = -1
// and we found opening double quote
inQuote = true
} else {
// else we don't know if we inside double or single quotes or not
inQuote = null
}
// we were not in double nor in single quote, so now we are in double quote
} else {
inQuote = true
}
}
'\'' -> {
// if we previously found unclosed single quote
if (lastOpenSingleQuotePos >= 0) {
// ...but we are too far from it
if (i - lastOpenSingleQuotePos > 3) {
// ... forget about it and threat current one as unclosed
lastOpenSingleQuotePos = i
} else {
// else we found closing single quote
lastOpenSingleQuotePos = -1
// and if we didn't know whether we are in double quote or not
if (inQuote == null) {
// then now we are definitely not in
inQuote = false
}
}
} else {
// we found opening single quote
lastOpenSingleQuotePos = i
}
}
}
yield(State(i, c, inQuote, lastOpenSingleQuotePos))
found = findPositionOfFirstCharacter(chars, i + Direction.FORWARD.toInt(), charsToSearch, false, Direction.FORWARD)
}
}

View File

@@ -46,8 +46,7 @@ public class StringHelper {
private StringHelper() {}
@Nullable
private static String toEscapeNotation(@NotNull KeyStroke key) {
private static @Nullable String toEscapeNotation(@NotNull KeyStroke key) {
final char c = key.getKeyChar();
if (isControlCharacter(c)) {
return "^" + (char)(c + 'A' - 1);
@@ -58,8 +57,7 @@ public class StringHelper {
return null;
}
@NotNull
public static List<KeyStroke> stringToKeys(@NotNull String s) {
public static @NotNull List<KeyStroke> stringToKeys(@NotNull String s) {
final List<KeyStroke> res = new ArrayList<>();
for (int i = 0; i < s.length(); i++) {
res.add(getKeyStroke(s.charAt(i)));
@@ -67,8 +65,7 @@ public class StringHelper {
return res;
}
@NotNull
public static final KeyStroke PlugKeyStroke = parseKeys("<Plug>").get(0);
public static final @NotNull KeyStroke PlugKeyStroke = parseKeys("<Plug>").get(0);
private enum KeyParserState {
INIT,
@@ -82,8 +79,7 @@ public class StringHelper {
* @throws java.lang.IllegalArgumentException if the mapping doesn't make sense for Vim emulation
* @see :help <>
*/
@NotNull
public static List<KeyStroke> parseKeys(@NotNull String... strings) {
public static @NotNull List<KeyStroke> parseKeys(@NotNull String... strings) {
final List<KeyStroke> result = new ArrayList<>();
for (String s : strings) {
KeyParserState state = KeyParserState.INIT;
@@ -167,8 +163,7 @@ public class StringHelper {
return result;
}
@Nullable
private static List<KeyStroke> parseMapLeader(@NotNull String s) {
private static @Nullable List<KeyStroke> parseMapLeader(@NotNull String s) {
if ("leader".equals(s.toLowerCase())) {
final Object mapLeader = VimScriptGlobalEnvironment.getInstance().getVariables().get("mapleader");
if (mapLeader instanceof String) {
@@ -189,8 +184,7 @@ public class StringHelper {
return key.getKeyChar() == CHAR_UNDEFINED && key.getKeyCode() < 0x20 && key.getModifiers() == 0;
}
@NotNull
public static String toKeyNotation(@NotNull List<KeyStroke> keys) {
public static @NotNull String toKeyNotation(@NotNull List<KeyStroke> keys) {
if (keys.isEmpty()) {
return "<Nop>";
}
@@ -201,8 +195,7 @@ public class StringHelper {
return builder.toString();
}
@NotNull
public static String toKeyNotation(@NotNull KeyStroke key) {
public static @NotNull String toKeyNotation(@NotNull KeyStroke key) {
final char c = key.getKeyChar();
final int keyCode = key.getKeyCode();
final int modifiers = key.getModifiers();
@@ -318,8 +311,7 @@ public class StringHelper {
/**
* Set the text of an XML element, safely encode it if needed.
*/
@NotNull
public static Element setSafeXmlText(@NotNull Element element, @NotNull String text) {
public static @NotNull Element setSafeXmlText(@NotNull Element element, @NotNull String text) {
final Character first = firstCharacter(text);
final Character last = lastCharacter(text);
if (!StringHelper.isXmlCharacterData(text) ||
@@ -338,8 +330,7 @@ public class StringHelper {
/**
* Get the (potentially safely encoded) text of an XML element.
*/
@Nullable
public static String getSafeXmlText(@NotNull Element element) {
public static @Nullable String getSafeXmlText(@NotNull Element element) {
final String text = element.getText();
final String encoding = element.getAttributeValue("encoding");
if (encoding == null) {
@@ -365,8 +356,7 @@ public class StringHelper {
return true;
}
@Nullable
private static KeyStroke parseSpecialKey(@NotNull String s, int modifiers) {
private static @Nullable KeyStroke parseSpecialKey(@NotNull String s, int modifiers) {
final String lower = s.toLowerCase();
final Integer keyCode = getVimKeyName(lower);
final Character typedChar = getVimTypedKeyName(lower);
@@ -541,8 +531,7 @@ public class StringHelper {
}
}
@NotNull
private static KeyStroke getTypedOrPressedKeyStroke(char c, int modifiers) {
private static @NotNull KeyStroke getTypedOrPressedKeyStroke(char c, int modifiers) {
if (modifiers == 0) {
return getKeyStroke(c);
}
@@ -554,13 +543,11 @@ public class StringHelper {
}
}
@Nullable
private static Character lastCharacter(@NotNull String text) {
private static @Nullable Character lastCharacter(@NotNull String text) {
return text.length() > 0 ? text.charAt(text.length() - 1) : null;
}
@Nullable
private static Character firstCharacter(@NotNull String text) {
private static @Nullable Character firstCharacter(@NotNull String text) {
return text.length() > 0 ? text.charAt(0) : null;
}

View File

@@ -30,7 +30,7 @@ import java.util.List;
* @author vlan
*/
public class TestInputModel {
@NotNull private final List<KeyStroke> myKeyStrokes = Lists.newArrayList();
private final @NotNull List<KeyStroke> myKeyStrokes = Lists.newArrayList();
private TestInputModel() {}
@@ -48,8 +48,7 @@ public class TestInputModel {
myKeyStrokes.addAll(keyStrokes);
}
@Nullable
public KeyStroke nextKeyStroke() {
public @Nullable KeyStroke nextKeyStroke() {
if (!myKeyStrokes.isEmpty()) {
return myKeyStrokes.remove(0);
}

View File

@@ -20,13 +20,14 @@ package com.maddyhome.idea.vim.key;
import com.google.common.collect.HashMultiset;
import com.google.common.collect.Multiset;
import com.maddyhome.idea.vim.command.MappingMode;
import com.maddyhome.idea.vim.extension.VimExtensionHandler;
import kotlin.Pair;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.swing.*;
import java.util.*;
import java.util.stream.Collectors;
/**
* Container for key mappings for some mode
@@ -35,36 +36,46 @@ import java.util.*;
* @author vlan
*/
public class KeyMapping implements Iterable<List<KeyStroke>> {
/** Contains all key mapping for some mode. */
@NotNull private final Map<List<KeyStroke>, MappingInfo> myKeys = new HashMap<>();
/**
* Contains all key mapping for some mode.
*/
private final @NotNull Map<List<KeyStroke>, MappingInfo> myKeys = new HashMap<>();
/**
* Set the contains all possible prefixes for mappings.
* E.g. if there is mapping for "hello", this set will contain "h", "he", "hel", etc.
* Multiset is used to correctly remove the mappings.
*/
@NotNull private final Multiset<List<KeyStroke>> myPrefixes = HashMultiset.create();
private final @NotNull Multiset<List<KeyStroke>> myPrefixes = HashMultiset.create();
@NotNull
@Override
public Iterator<List<KeyStroke>> iterator() {
public @NotNull Iterator<List<KeyStroke>> iterator() {
return new ArrayList<>(myKeys.keySet()).iterator();
}
@Nullable
public MappingInfo get(@NotNull Iterable<KeyStroke> keys) {
public @Nullable MappingInfo get(@NotNull Iterable<KeyStroke> keys) {
// Having a parameter of Iterable allows for a nicer API, because we know when a given list is immutable.
// TODO: Should we change this to be a trie?
assert (keys instanceof List) : "keys must be of type List<KeyStroke>";
return myKeys.get(keys);
}
public void put(@NotNull Set<MappingMode> mappingModes,
@NotNull List<KeyStroke> fromKeys,
@Nullable List<KeyStroke> toKeys,
@Nullable VimExtensionHandler extensionHandler,
public void put(@NotNull List<KeyStroke> fromKeys,
@NotNull MappingOwner owner,
@NotNull VimExtensionHandler extensionHandler,
boolean recursive) {
myKeys.put(new ArrayList<>(fromKeys),
new MappingInfo(mappingModes, fromKeys, toKeys, extensionHandler, recursive));
myKeys.put(new ArrayList<>(fromKeys), new ToHandlerMappingInfo(extensionHandler, fromKeys, recursive, owner));
fillPrefixes(fromKeys);
}
public void put(@NotNull List<KeyStroke> fromKeys,
@NotNull List<KeyStroke> toKeys,
@NotNull MappingOwner owner,
boolean recursive) {
myKeys.put(new ArrayList<>(fromKeys), new ToKeysMappingInfo(toKeys, fromKeys, recursive, owner));
fillPrefixes(fromKeys);
}
private void fillPrefixes(@NotNull List<KeyStroke> fromKeys) {
List<KeyStroke> prefix = new ArrayList<>();
final int prefixLength = fromKeys.size() - 1;
for (int i = 0; i < prefixLength; i++) {
@@ -73,14 +84,24 @@ public class KeyMapping implements Iterable<List<KeyStroke>> {
}
}
public void delete(@NotNull List<KeyStroke> keys) {
myKeys.remove(keys);
List<KeyStroke> prefix = new ArrayList<>();
final int prefixLength = keys.size() - 1;
for (int i = 0; i < prefixLength; i++) {
prefix.add(keys.get(i));
myPrefixes.remove(prefix);
}
public void delete(@NotNull MappingOwner owner) {
List<Map.Entry<List<KeyStroke>, MappingInfo>> toRemove =
myKeys.entrySet().stream().filter(o -> o.getValue().getOwner().equals(owner)).collect(Collectors.toList());
toRemove.forEach(o -> myKeys.remove(o.getKey(), o.getValue()));
toRemove.stream().map(Map.Entry::getKey).forEach(keys -> {
List<KeyStroke> prefix = new ArrayList<>();
final int prefixLength = keys.size() - 1;
for (int i = 0; i < prefixLength; i++) {
prefix.add(keys.get(i));
myPrefixes.remove(prefix);
}
});
}
public List<Pair<List<KeyStroke>, MappingInfo>> getByOwner(@NotNull MappingOwner owner) {
return myKeys.entrySet().stream().filter(o -> o.getValue().getOwner().equals(owner))
.map(o -> new Pair<>(o.getKey(), o.getValue())).collect(Collectors.toList());
}
public boolean isPrefix(@NotNull Iterable<KeyStroke> keys) {

View File

@@ -1,113 +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.key;
import com.maddyhome.idea.vim.command.MappingMode;
import com.maddyhome.idea.vim.extension.VimExtensionHandler;
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.swing.*;
import java.awt.event.KeyEvent;
import java.util.List;
import java.util.Set;
/**
* @author vlan
*/
public class MappingInfo implements Comparable<MappingInfo> {
@NotNull private final Set<MappingMode> myMappingModes;
@NotNull private final List<KeyStroke> myFromKeys;
@Nullable private final List<KeyStroke> myToKeys;
@Nullable private final VimExtensionHandler myExtensionHandler;
private final boolean myRecursive;
@Contract(pure = true)
public MappingInfo(@NotNull Set<MappingMode> mappingModes,
@NotNull List<KeyStroke> fromKeys,
@Nullable List<KeyStroke> toKeys,
@Nullable VimExtensionHandler extensionHandler,
boolean recursive) {
myMappingModes = mappingModes;
myFromKeys = fromKeys;
myToKeys = toKeys;
myExtensionHandler = extensionHandler;
myRecursive = recursive;
}
@Override
public int compareTo(@NotNull MappingInfo other) {
final int size = myFromKeys.size();
final int otherSize = other.myFromKeys.size();
final int n = Math.min(size, otherSize);
for (int i = 0; i < n; i++) {
final int diff = compareKeys(myFromKeys.get(i), other.myFromKeys.get(i));
if (diff != 0) {
return diff;
}
}
return size - otherSize;
}
@NotNull
public Set<MappingMode> getMappingModes() {
return myMappingModes;
}
@NotNull
public List<KeyStroke> getFromKeys() {
return myFromKeys;
}
@Nullable
public List<KeyStroke> getToKeys() {
return myToKeys;
}
@Nullable
public VimExtensionHandler getExtensionHandler() {
return myExtensionHandler;
}
public boolean isRecursive() {
return myRecursive;
}
private int compareKeys(@NotNull KeyStroke key1, @NotNull KeyStroke key2) {
final char c1 = key1.getKeyChar();
final char c2 = key2.getKeyChar();
if (c1 == KeyEvent.CHAR_UNDEFINED && c2 == KeyEvent.CHAR_UNDEFINED) {
final int keyCodeDiff = key1.getKeyCode() - key2.getKeyCode();
if (keyCodeDiff != 0) {
return keyCodeDiff;
}
return key1.getModifiers() - key2.getModifiers();
}
else if (c1 == KeyEvent.CHAR_UNDEFINED) {
return -1;
}
else if (c2 == KeyEvent.CHAR_UNDEFINED) {
return 1;
}
else {
return c1 - c2;
}
}
}

View File

@@ -0,0 +1,67 @@
/*
* 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.key
import com.maddyhome.idea.vim.extension.VimExtensionHandler
import java.awt.event.KeyEvent
import javax.swing.KeyStroke
import kotlin.math.min
/**
* @author vlan
*/
sealed class MappingInfo(val fromKeys: List<KeyStroke>, val isRecursive: Boolean, val owner: MappingOwner) : Comparable<MappingInfo> {
override fun compareTo(other: MappingInfo): Int {
val size = fromKeys.size
val otherSize = other.fromKeys.size
val n = min(size, otherSize)
for (i in 0 until n) {
val diff = compareKeys(fromKeys[i], other.fromKeys[i])
if (diff != 0) return diff
}
return size - otherSize
}
private fun compareKeys(key1: KeyStroke, key2: KeyStroke): Int {
val c1 = key1.keyChar
val c2 = key2.keyChar
return when {
c1 == KeyEvent.CHAR_UNDEFINED && c2 == KeyEvent.CHAR_UNDEFINED -> {
val keyCodeDiff = key1.keyCode - key2.keyCode
if (keyCodeDiff != 0) keyCodeDiff else key1.modifiers - key2.modifiers
}
c1 == KeyEvent.CHAR_UNDEFINED -> -1
c2 == KeyEvent.CHAR_UNDEFINED -> 1
else -> c1 - c2
}
}
}
class ToKeysMappingInfo(
val toKeys: List<KeyStroke>,
fromKeys: List<KeyStroke>,
isRecursive: Boolean,
owner: MappingOwner
) : MappingInfo(fromKeys, isRecursive, owner)
class ToHandlerMappingInfo(
val extensionHandler: VimExtensionHandler,
fromKeys: List<KeyStroke>,
isRecursive: Boolean,
owner: MappingOwner
) : MappingInfo(fromKeys, isRecursive, owner)

View File

@@ -0,0 +1,38 @@
/*
* 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.key
import javax.swing.KeyStroke
class RequiredShortcut(val keyStroke: KeyStroke, val owner: MappingOwner)
sealed class MappingOwner {
object IdeaVim : MappingOwner()
@Suppress("DataClassPrivateConstructor")
data class Plugin private constructor(val name: String) : MappingOwner() {
companion object {
fun get(name: String): Plugin = allOwners.computeIfAbsent(name) { Plugin(it) }
fun remove(name: String) = allOwners.remove(name)
private val allOwners: MutableMap<String, Plugin> = mutableMapOf()
}
}
}

View File

@@ -28,27 +28,24 @@ public enum ShortcutOwner {
IDE("ide", "IDE"),
VIM("vim", "Vim");
@NotNull private final String name;
@NotNull private final String title;
private final @NotNull String name;
private final @NotNull String title;
ShortcutOwner(@NotNull String name, @NotNull String title) {
this.name = name;
this.title = title;
}
@NotNull
@Override
public String toString() {
public @NotNull String toString() {
return title;
}
@NotNull
public String getName() {
public @NotNull String getName() {
return name;
}
@NotNull
public static ShortcutOwner fromString(@NotNull String s) {
public static @NotNull ShortcutOwner fromString(@NotNull String s) {
if ("ide".equals(s)) {
return IDE;
}

View File

@@ -72,5 +72,5 @@ public class BoundListOption extends ListOption {
return true;
}
@NotNull protected final List<String> values;
protected final @NotNull List<String> values;
}

View File

@@ -30,10 +30,10 @@ import java.util.regex.Pattern;
import java.util.stream.Collectors;
public final class KeywordOption extends ListOption {
@NotNull private final Pattern validationPattern;
private final @NotNull Pattern validationPattern;
// KeywordSpecs are the option values in reverse order
@NotNull private List<KeywordSpec> keywordSpecs = new ArrayList<>();
private @NotNull List<KeywordSpec> keywordSpecs = new ArrayList<>();
public KeywordOption(@NotNull String name, @NotNull String abbrev, @NotNull String[] defaultValue) {
super(name, abbrev, defaultValue,
@@ -116,8 +116,7 @@ public final class KeywordOption extends ListOption {
}
}
@NotNull
private List<KeywordSpec> valsToReversedSpecs(@NotNull List<String> vals) {
private @NotNull List<KeywordSpec> valsToReversedSpecs(@NotNull List<String> vals) {
final ArrayList<KeywordSpec> res = new ArrayList<>();
for (int i = vals.size() - 1; i >= 0; i--) {
res.add(new KeywordSpec(vals.get(i)));
@@ -125,8 +124,7 @@ public final class KeywordOption extends ListOption {
return res;
}
@Nullable
private List<KeywordSpec> valsToValidatedAndReversedSpecs(@Nullable List<String> vals) {
private @Nullable List<KeywordSpec> valsToValidatedAndReversedSpecs(@Nullable List<String> vals) {
final List<KeywordSpec> specs = new ArrayList<>();
if (vals != null) {
for (String val : vals) {
@@ -141,9 +139,8 @@ public final class KeywordOption extends ListOption {
return specs;
}
@Nullable
@Override
protected List<String> parseVals(@NotNull String content) {
protected @Nullable List<String> parseVals(@NotNull String content) {
if (!validationPattern.matcher(content).matches()) {
return null;
}

View File

@@ -30,7 +30,7 @@ import java.util.StringTokenizer;
* This is an option that accepts an arbitrary list of values
*/
public class ListOption extends TextOption {
@NotNull public final static ListOption empty = new ListOption("", "", new String[0], "");
public static final @NotNull ListOption empty = new ListOption("", "", new String[0], "");
/**
* Gets the value of the option as a comma separated list of values
@@ -38,8 +38,7 @@ public class ListOption extends TextOption {
* @return The option's value
*/
@Override
@NotNull
public String getValue() {
public @NotNull String getValue() {
StringBuilder res = new StringBuilder();
int cnt = 0;
for (String s : value) {
@@ -59,8 +58,7 @@ public class ListOption extends TextOption {
*
* @return The option's values
*/
@NotNull
public List<String> values() {
public @NotNull List<String> values() {
return value;
}
@@ -197,8 +195,7 @@ public class ListOption extends TextOption {
return dflt.equals(value);
}
@Nullable
protected List<String> parseVals(String val) {
protected @Nullable List<String> parseVals(String val) {
List<String> res = new ArrayList<>();
StringTokenizer tokenizer = new StringTokenizer(val, ",");
while (tokenizer.hasMoreTokens()) {
@@ -219,13 +216,12 @@ public class ListOption extends TextOption {
*
* @return The option as a string {name}={value list}
*/
@NotNull
public String toString() {
public @NotNull String toString() {
return " " + getName() + "=" + getValue();
}
@NotNull protected final List<String> dflt;
@NotNull protected List<String> value;
protected final @NotNull List<String> dflt;
protected @NotNull List<String> value;
protected final String pattern;
/**

View File

@@ -213,8 +213,7 @@ public class NumberOption extends TextOption {
}
}
@Nullable
protected Integer asNumber(String val) {
protected @Nullable Integer asNumber(String val) {
try {
return Integer.decode(val);
}
@@ -232,8 +231,7 @@ public class NumberOption extends TextOption {
*
* @return The option as a string
*/
@NotNull
public String toString() {
public @NotNull String toString() {
return " " + getName() + "=" + value;
}

View File

@@ -113,5 +113,5 @@ public abstract class Option<T> {
protected final String name;
protected final String abbrev;
@NotNull private final List<OptionChangeListener<T>> listeners = new ArrayList<>();
private final @NotNull List<OptionChangeListener<T>> listeners = new ArrayList<>();
}

View File

@@ -36,6 +36,7 @@ import com.maddyhome.idea.vim.helper.isBlockCaret
import com.maddyhome.idea.vim.helper.mode
import com.maddyhome.idea.vim.helper.subMode
import com.maddyhome.idea.vim.listener.SelectionVimListenerSuppressor
import org.jetbrains.annotations.ApiStatus
import org.jetbrains.annotations.Contract
import java.util.*
import kotlin.math.ceil
@@ -82,6 +83,10 @@ object OptionsManager {
val wrapscan = addOption(ToggleOption("wrapscan", "ws", true))
val visualEnterDelay = addOption(NumberOption("visualdelay", "visualdelay", 100, 0, Int.MAX_VALUE))
val idearefactormode = addOption(BoundStringOption(IdeaRefactorMode.name, IdeaRefactorMode.name, IdeaRefactorMode.select, IdeaRefactorMode.availableValues))
val ideastatusicon = addOption(BoundStringOption(IdeaStatusIcon.name, IdeaStatusIcon.name, IdeaStatusIcon.enabled, IdeaStatusIcon.allValues))
@ApiStatus.ScheduledForRemoval(inVersion = "0.58")
@Deprecated("please use ideastatusicon")
val ideastatusbar = addOption(ToggleOption("ideastatusbar", "ideastatusbar", true))
fun isSet(name: String): Boolean {
@@ -353,6 +358,11 @@ object OptionsManager {
abbrevs += option.abbrev to option
return option
}
fun removeOption(name: String) {
options.remove(name)
abbrevs.values.find { it.name == name }?.let { option -> abbrevs.remove(option.abbrev) }
}
}
object KeyModelOptionData {
@@ -379,8 +389,11 @@ object SelectModeOptionData {
const val key = "key"
const val cmd = "cmd"
@ApiStatus.ScheduledForRemoval(inVersion = "0.57")
@Deprecated("Please, use `idearefactormode` option")
const val template = "template"
@ApiStatus.ScheduledForRemoval(inVersion = "0.57")
@Deprecated("Please, use `ideaselection`")
const val refactoring = "refactoring"
@@ -520,3 +533,12 @@ object LookupKeysData {
"<C-Q>"
)
}
object IdeaStatusIcon {
const val enabled = "enabled"
const val gray = "gray"
const val disabled = "disabled"
val name = "ideastatusicon"
val allValues = arrayOf(enabled, gray, disabled)
}

View File

@@ -139,8 +139,7 @@ public class StringOption extends TextOption {
*
* @return The option as a string for display
*/
@NotNull
public String toString() {
public @NotNull String toString() {
return " " + getName() + "=" + value;
}

View File

@@ -86,8 +86,7 @@ public class ToggleOption extends Option<Boolean> {
*
* @return The option's display value
*/
@NotNull
public String toString() {
public @NotNull String toString() {
StringBuilder res = new StringBuilder();
if (!value) {
res.append("no");

View File

@@ -26,7 +26,7 @@ import java.util.Objects;
public class CharPointer {
@NotNull private CharSequence seq;
private @NotNull CharSequence seq;
private int pointer;
private boolean readonly;
@@ -55,13 +55,11 @@ public class CharPointer {
return pointer;
}
@NotNull
public CharPointer set(char ch) {
public @NotNull CharPointer set(char ch) {
return set(ch, 0);
}
@NotNull
public CharPointer set(char ch, int offset) {
public @NotNull CharPointer set(char ch, int offset) {
if (readonly) {
throw new IllegalStateException("readonly string");
}
@@ -94,32 +92,27 @@ public class CharPointer {
return seq.charAt(pointer + offset);
}
@NotNull
public CharPointer inc() {
public @NotNull CharPointer inc() {
return inc(1);
}
@NotNull
public CharPointer inc(int cnt) {
public @NotNull CharPointer inc(int cnt) {
pointer += cnt;
return this;
}
@NotNull
public CharPointer dec() {
public @NotNull CharPointer dec() {
return dec(1);
}
@NotNull
public CharPointer dec(int cnt) {
public @NotNull CharPointer dec(int cnt) {
pointer -= cnt;
return this;
}
@NotNull
public CharPointer assign(@NotNull CharPointer ptr) {
public @NotNull CharPointer assign(@NotNull CharPointer ptr) {
seq = ptr.seq;
pointer = ptr.pointer;
readonly = ptr.readonly;
@@ -127,13 +120,11 @@ public class CharPointer {
return this;
}
@NotNull
public CharPointer ref(int offset) {
public @NotNull CharPointer ref(int offset) {
return new CharPointer(this, offset);
}
@NotNull
public String substring(int len) {
public @NotNull String substring(int len) {
if (end()) return "";
int start = pointer;
@@ -188,8 +179,7 @@ public class CharPointer {
return 0;
}
@Nullable
public CharPointer strchr(char c) {
public @Nullable CharPointer strchr(char c) {
if (end()) {
return null;
}
@@ -208,8 +198,7 @@ public class CharPointer {
return null;
}
@Nullable
public CharPointer istrchr(char c) {
public @Nullable CharPointer istrchr(char c) {
if (end()) {
return null;
}
@@ -247,8 +236,7 @@ public class CharPointer {
return charAt();
}
@NotNull
public CharPointer OPERAND() {
public @NotNull CharPointer OPERAND() {
return ref(3);
}
@@ -306,8 +294,7 @@ public class CharPointer {
return Math.min(seq.length(), pos);
}
@NotNull
public String toString() {
public @NotNull String toString() {
return substring(strlen());
}
}

View File

@@ -29,8 +29,8 @@ import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
public class RegExp {
@Nullable public static reg_extmatch_T re_extmatch_out = null;
@Nullable public static reg_extmatch_T re_extmatch_in = null;
public static @Nullable reg_extmatch_T re_extmatch_out = null;
public static @Nullable reg_extmatch_T re_extmatch_in = null;
/*
* The opcodes are:
@@ -356,8 +356,7 @@ public class RegExp {
* "p" must point to the character after the '['.
* The returned pointer is on the matching ']', or the terminating NUL.
*/
@NotNull
private static CharPointer skip_anyof(@NotNull CharPointer p) {
private static @NotNull CharPointer skip_anyof(@NotNull CharPointer p) {
if (p.charAt() == '^') /* Complement of range. */ {
p.inc();
}
@@ -401,8 +400,7 @@ public class RegExp {
* Take care of characters with a backslash in front of it.
* Skip strings inside [ and ].
*/
@NotNull
public static CharPointer skip_regexp(@NotNull CharPointer p, char dirc, boolean magic) {
public static @NotNull CharPointer skip_regexp(@NotNull CharPointer p, char dirc, boolean magic) {
int mymagic;
if (magic) {
@@ -455,8 +453,7 @@ public class RegExp {
* Beware that the optimization-preparation code in here knows about some
* of the structure of the compiled regexp.
*/
@Nullable
public regprog_T vim_regcomp(@Nullable String expr, int magic) {
public @Nullable regprog_T vim_regcomp(@Nullable String expr, int magic) {
regprog_T r;
CharPointer scan;
CharPointer longest;
@@ -587,8 +584,7 @@ public class RegExp {
* is a trifle forced, but the need to tie the tails of the branches to what
* follows makes it hard to avoid.
*/
@Nullable
private CharPointer reg(int paren, @NotNull Flags flagp) {
private @Nullable CharPointer reg(int paren, @NotNull Flags flagp) {
CharPointer ret;
CharPointer br;
CharPointer ender;
@@ -708,8 +704,7 @@ public class RegExp {
*
* Implements the & operator.
*/
@Nullable
private CharPointer regbranch(@NotNull Flags flagp) {
private @Nullable CharPointer regbranch(@NotNull Flags flagp) {
CharPointer ret;
CharPointer chain = null;
CharPointer latest;
@@ -750,8 +745,7 @@ public class RegExp {
*
* Implements the concatenation operator.
*/
@Nullable
private CharPointer regconcat(@NotNull Flags flagp) {
private @Nullable CharPointer regconcat(@NotNull Flags flagp) {
CharPointer first = null;
CharPointer chain = null;
CharPointer latest;
@@ -831,8 +825,7 @@ public class RegExp {
* It might seem that this node could be dispensed with entirely, but the
* endmarker role is not redundant.
*/
@Nullable
private CharPointer regpiece(@NotNull Flags flagp) {
private @Nullable CharPointer regpiece(@NotNull Flags flagp) {
CharPointer ret;
int op;
CharPointer next;
@@ -992,8 +985,7 @@ public class RegExp {
* it can turn them into a single node, which is smaller to store and
* faster to run. Don't do this when one_exactly is set.
*/
@Nullable
private CharPointer regatom(@NotNull Flags flagp) {
private @Nullable CharPointer regatom(@NotNull Flags flagp) {
CharPointer ret = null;
Flags flags = new Flags();
boolean cpo_lit = false; /* 'cpoptions' contains 'l' flag */
@@ -1695,8 +1687,7 @@ public class RegExp {
/*
* Write a long as four bytes at "p" and return pointer to the next char.
*/
@NotNull
private CharPointer re_put_long(@NotNull CharPointer p, int val) {
private @NotNull CharPointer re_put_long(@NotNull CharPointer p, int val) {
p.set((char)((val >> 24) & 0xff)).inc();
p.set((char)((val >> 16) & 0xff)).inc();
p.set((char)((val >> 8) & 0xff)).inc();
@@ -1939,8 +1930,7 @@ public class RegExp {
* Should end with 'end'. If minval is missing, zero is default, if maxval is
* missing, a very big number is the default.
*/
@Nullable
private MinMax read_limits() {
private @Nullable MinMax read_limits() {
boolean reverse = false;
CharPointer first_char;
int minval;
@@ -2022,8 +2012,7 @@ public class RegExp {
/*
* Get pointer to the line "lnum", which is relative to "reg_firstlnum".
*/
@Nullable
private CharPointer reg_getline(int lnum) {
private @Nullable CharPointer reg_getline(int lnum) {
/* when looking behind for a match/no-match lnum is negative. But we
* can't go before line 1 */
if (reg_firstlnum + lnum < 0) {
@@ -2208,8 +2197,7 @@ public class RegExp {
/*
* Create a new extmatch and mark it as referenced once.
*/
@NotNull
private reg_extmatch_T make_extmatch() {
private @NotNull reg_extmatch_T make_extmatch() {
return new reg_extmatch_T();
}
@@ -3555,8 +3543,7 @@ public class RegExp {
/*
* regnext - dig the "next" pointer out of a node
*/
@Nullable
private CharPointer regnext(@NotNull CharPointer p) {
private @Nullable CharPointer regnext(@NotNull CharPointer p) {
int offset;
offset = p.NEXT();
@@ -3739,8 +3726,7 @@ public class RegExp {
/*
* cstrchr: This function is used a lot for simple searches, keep it fast!
*/
@Nullable
private CharPointer cstrchr(@NotNull CharPointer s, char c) {
private @Nullable CharPointer cstrchr(@NotNull CharPointer s, char c) {
if (!ireg_ic) {
return s.strchr(c);
}
@@ -3882,8 +3868,7 @@ public class RegExp {
* <p/>
* Returns the size of the replacement, including terminating '\u0000'.
*/
@Nullable
public String vim_regsub(regmatch_T rmp, CharPointer source, int magic, boolean backslash) {
public @Nullable String vim_regsub(regmatch_T rmp, CharPointer source, int magic, boolean backslash) {
reg_match = rmp;
reg_mmatch = null;
reg_maxline = 0;
@@ -3891,8 +3876,7 @@ public class RegExp {
return vim_regsub_both(source, magic, backslash);
}
@Nullable
public String vim_regsub_multi(regmmatch_T rmp, int lnum, CharPointer source, int magic, boolean backslash) {
public @Nullable String vim_regsub_multi(regmmatch_T rmp, int lnum, CharPointer source, int magic, boolean backslash) {
reg_match = null;
reg_mmatch = rmp;
//reg_buf = curbuf; /* always works on the current buffer! */
@@ -3924,8 +3908,7 @@ public class RegExp {
return mode;
}
@Nullable
private String vim_regsub_both(@Nullable CharPointer source, int magic, boolean backslash) {
private @Nullable String vim_regsub_both(@Nullable CharPointer source, int magic, boolean backslash) {
CharPointer src;
StringBuffer dst = new StringBuffer();
CharPointer s;
@@ -4211,8 +4194,7 @@ public class RegExp {
/*
* regdump - dump a regexp onto stdout in vaguely comprehensible form
*/
@NotNull
private String regdump(String pattern, @NotNull regprog_T r) {
private @NotNull String regdump(String pattern, @NotNull regprog_T r) {
CharPointer start;
CharPointer s;
int op = EXACTLY; /* Arbitrary non-END op. */
@@ -4290,8 +4272,7 @@ public class RegExp {
/*
* regprop - printable representation of opcode
*/
@NotNull
private String regprop(@NotNull CharPointer op) {
private @NotNull String regprop(@NotNull CharPointer op) {
String p;
StringBuffer buf = new StringBuffer();
@@ -4727,15 +4708,15 @@ public class RegExp {
}
}
@Nullable public regprog_T regprog;
@NotNull public lpos_T[] startpos = new lpos_T[NSUBEXP];
@NotNull public lpos_T[] endpos = new lpos_T[NSUBEXP];
public @Nullable regprog_T regprog;
public @NotNull lpos_T[] startpos = new lpos_T[NSUBEXP];
public @NotNull lpos_T[] endpos = new lpos_T[NSUBEXP];
public boolean rmm_ic;
}
private int reg_do_extmatch = 0;
@Nullable private CharPointer reg_prev_sub = null;
private @Nullable CharPointer reg_prev_sub = null;
private CharPointer regparse; /* Input-scan pointer. */
private int prevchr_len; /* byte length of previous char */
@@ -4744,11 +4725,11 @@ public class RegExp {
private int regnzpar; /* \z() count. */
private char re_has_z; /* \z item detected */
private CharPointer regcode; /* Code-emit pointer */
@NotNull private boolean[] had_endbrace = new boolean[NSUBEXP]; /* flags, true if end of () found */
private @NotNull boolean[] had_endbrace = new boolean[NSUBEXP]; /* flags, true if end of () found */
private int regflags; /* RF_ flags for prog */
@NotNull private int[] brace_min = new int[10]; /* Minimums for complex brace repeats */
@NotNull private int[] brace_max = new int[10]; /* Maximums for complex brace repeats */
@NotNull private int[] brace_count = new int[10]; /* Current counts for complex brace repeats */
private @NotNull int[] brace_min = new int[10]; /* Minimums for complex brace repeats */
private @NotNull int[] brace_max = new int[10]; /* Maximums for complex brace repeats */
private @NotNull int[] brace_count = new int[10]; /* Current counts for complex brace repeats */
private boolean had_eol; /* true when EOL found by vim_regcomp() */
private boolean one_exactly = false; /* only do one char for EXACTLY */
@@ -4779,7 +4760,7 @@ public class RegExp {
/* The current match-position is remembered with these variables: */
private int reglnum; /* line number, relative to first line */
@Nullable private CharPointer regline; /* start of current line */
private @Nullable CharPointer regline; /* start of current line */
private CharPointer reginput; /* current input, points into "regline" */
private boolean need_clear_subexpr; /* subexpressions still need to be
@@ -4801,7 +4782,7 @@ public class RegExp {
* slow, we keep one allocated piece of memory and only re-allocate it when
* it's too small. It's freed in vim_regexec_both() when finished.
*/
@Nullable private CharPointer reg_tofree;
private @Nullable CharPointer reg_tofree;
//private int reg_tofreelen;
/*
@@ -4820,12 +4801,12 @@ public class RegExp {
* reg_firstlnum <invalid> first line in which to search
* reg_maxline 0 last line nr
*/
@Nullable private regmatch_T reg_match;
@Nullable private regmmatch_T reg_mmatch;
@NotNull private CharPointer[] reg_startp = new CharPointer[NSUBEXP];
@NotNull private CharPointer[] reg_endp = new CharPointer[NSUBEXP];
@NotNull private lpos_T[] reg_startpos = new lpos_T[NSUBEXP];
@NotNull private lpos_T[] reg_endpos = new lpos_T[NSUBEXP];
private @Nullable regmatch_T reg_match;
private @Nullable regmmatch_T reg_mmatch;
private @NotNull CharPointer[] reg_startp = new CharPointer[NSUBEXP];
private @NotNull CharPointer[] reg_endp = new CharPointer[NSUBEXP];
private @NotNull lpos_T[] reg_startpos = new lpos_T[NSUBEXP];
private @NotNull lpos_T[] reg_endpos = new lpos_T[NSUBEXP];
//static win_T *reg_win;
private Editor reg_buf;
private int reg_firstlnum;
@@ -4833,10 +4814,10 @@ public class RegExp {
private regsave_T behind_pos;
@NotNull private CharPointer[] reg_startzp = new CharPointer[NSUBEXP]; /* Workspace to mark beginning */
@NotNull private CharPointer[] reg_endzp = new CharPointer[NSUBEXP]; /* and end of \z(...\) matches */
@NotNull private lpos_T[] reg_startzpos = new lpos_T[NSUBEXP]; /* idem, beginning pos */
@NotNull private lpos_T[] reg_endzpos = new lpos_T[NSUBEXP]; /* idem, end pos */
private @NotNull CharPointer[] reg_startzp = new CharPointer[NSUBEXP]; /* Workspace to mark beginning */
private @NotNull CharPointer[] reg_endzp = new CharPointer[NSUBEXP]; /* and end of \z(...\) matches */
private @NotNull lpos_T[] reg_startzpos = new lpos_T[NSUBEXP]; /* idem, beginning pos */
private @NotNull lpos_T[] reg_endzpos = new lpos_T[NSUBEXP]; /* idem, end pos */
private boolean got_int = false;

View File

@@ -45,8 +45,7 @@ public class ClipboardHandler {
*
* @return The clipboard string or null if data isn't plain text
*/
@NotNull
public static Pair<String, List<TextBlockTransferableData>> getClipboardTextAndTransferableData() {
public static @NotNull Pair<String, List<TextBlockTransferableData>> getClipboardTextAndTransferableData() {
String res = null;
List<TextBlockTransferableData> transferableData = new ArrayList<>();
try {

View File

@@ -50,8 +50,7 @@ public class ExEditorKit extends DefaultEditorKit {
* @return the type
*/
@Override
@NotNull
public String getContentType() {
public @NotNull String getContentType() {
return "text/ideavim";
}
@@ -77,13 +76,11 @@ public class ExEditorKit extends DefaultEditorKit {
* @return the model
*/
@Override
@NotNull
public Document createDefaultDocument() {
public @NotNull Document createDefaultDocument() {
return new ExDocument();
}
@Nullable
private static KeyStroke convert(@NotNull ActionEvent event) {
private static @Nullable KeyStroke convert(@NotNull ActionEvent event) {
String cmd = event.getActionCommand();
int mods = event.getModifiers();
if (cmd != null && cmd.length() > 0) {
@@ -114,7 +111,7 @@ public class ExEditorKit extends DefaultEditorKit {
static final String StartDigraph = "start-digraph";
static final String StartLiteral = "start-literal";
@NotNull private final Action[] exActions = new Action[]{
private final @NotNull Action[] exActions = new Action[]{
new CancelEntryAction(),
new CompleteEntryAction(),
new EscapeCharAction(),
@@ -217,7 +214,7 @@ public class ExEditorKit extends DefaultEditorKit {
WAIT_REGISTER,
}
@NotNull private State state = State.SKIP_CTRL_R;
private @NotNull State state = State.SKIP_CTRL_R;
InsertRegisterAction() {
super(InsertRegister);
@@ -308,7 +305,7 @@ public class ExEditorKit extends DefaultEditorKit {
}
@SuppressWarnings("BooleanMethodIsAlwaysInverted")
private static abstract class DeleteCharAction extends TextAction {
private abstract static class DeleteCharAction extends TextAction {
DeleteCharAction(String name) {
super(name);
@@ -488,9 +485,8 @@ public class ExEditorKit extends DefaultEditorKit {
}
}
private static abstract class StartDigraphLiteralActionBase extends TextAction implements MultiStepAction {
@Nullable
private DigraphSequence digraphSequence;
private abstract static class StartDigraphLiteralActionBase extends TextAction implements MultiStepAction {
private @Nullable DigraphSequence digraphSequence;
public StartDigraphLiteralActionBase(String name) {
super(name);

View File

@@ -236,7 +236,7 @@ public class ExEntryPanel extends JPanel implements LafManagerListener {
}
}
@NotNull private final DocumentListener incSearchDocumentListener = new DocumentAdapter() {
private final @NotNull DocumentListener incSearchDocumentListener = new DocumentAdapter() {
@Override
protected void textChanged(@NotNull DocumentEvent e) {
final Editor editor = entry.getEditor();
@@ -284,8 +284,7 @@ public class ExEntryPanel extends JPanel implements LafManagerListener {
}
@Contract("null -> null")
@Nullable
private ExCommand getIncsearchCommand(@Nullable String commandText) {
private @Nullable ExCommand getIncsearchCommand(@Nullable String commandText) {
if (commandText == null) return null;
try {
final ExCommand exCommand = CommandParser.getInstance().parse(commandText);
@@ -335,13 +334,11 @@ public class ExEntryPanel extends JPanel implements LafManagerListener {
*
* @return The user entered text
*/
@NotNull
public String getText() {
public @NotNull String getText() {
return entry.getActualText();
}
@NotNull
public ExTextField getEntry() {
public @NotNull ExTextField getEntry() {
return entry;
}
@@ -421,9 +418,9 @@ public class ExEntryPanel extends JPanel implements LafManagerListener {
private int count;
// UI stuff
@Nullable private JComponent parent;
@NotNull private final JLabel label;
@NotNull private final ExTextField entry;
private @Nullable JComponent parent;
private final @NotNull JLabel label;
private final @NotNull ExTextField entry;
private JComponent oldGlass;
private LayoutManager oldLayout;
private boolean wasOpaque;
@@ -433,7 +430,7 @@ public class ExEntryPanel extends JPanel implements LafManagerListener {
private int horizontalOffset;
private int caretOffset;
@NotNull private final ComponentListener resizePanelListener = new ComponentAdapter() {
private final @NotNull ComponentListener resizePanelListener = new ComponentAdapter() {
@Override
public void componentResized(ComponentEvent e) {
positionPanel();

View File

@@ -46,18 +46,18 @@ import java.util.List;
* This panel displays text in a <code>more</code> like window.
*/
public class ExOutputPanel extends JPanel implements LafManagerListener {
@NotNull private final Editor myEditor;
private final @NotNull Editor myEditor;
@NotNull private final JLabel myLabel = new JLabel("more");
@NotNull private final JTextArea myText = new JTextArea();
@NotNull private final JScrollPane myScrollPane =
private final @NotNull JLabel myLabel = new JLabel("more");
private final @NotNull JTextArea myText = new JTextArea();
private final @NotNull JScrollPane myScrollPane =
new JBScrollPane(myText, JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED, JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
@NotNull private final ComponentAdapter myAdapter;
private final @NotNull ComponentAdapter myAdapter;
private boolean myAtEnd = false;
private int myLineHeight = 0;
@Nullable private JComponent myOldGlass = null;
@Nullable private LayoutManager myOldLayout = null;
private @Nullable JComponent myOldGlass = null;
private @Nullable LayoutManager myOldLayout = null;
private boolean myWasOpaque = false;
private boolean myActive = false;
@@ -95,8 +95,7 @@ public class ExOutputPanel extends JPanel implements LafManagerListener {
updateUI();
}
@NotNull
public static ExOutputPanel getInstance(@NotNull Editor editor) {
public static @NotNull ExOutputPanel getInstance(@NotNull Editor editor) {
ExOutputPanel panel = UserDataManager.getVimMorePanel(editor);
if (panel == null) {
panel = new ExOutputPanel(editor);
@@ -298,7 +297,7 @@ public class ExOutputPanel extends JPanel implements LafManagerListener {
close(null);
}
private void close(@Nullable final KeyEvent e) {
private void close(final @Nullable KeyEvent e) {
ApplicationManager.getApplication().invokeLater(() -> {
deactivate(true);

View File

@@ -286,8 +286,7 @@ public class ExTextField extends JTextField {
* @return the default model implementation
*/
@Override
@NotNull
protected Document createDefaultModel() {
protected @NotNull Document createDefaultModel() {
return new ExDocument();
}
@@ -547,7 +546,7 @@ public class ExTextField extends JTextField {
private String actualText;
private List<HistoryGroup.HistoryEntry> history;
private int histIndex = 0;
@Nullable private ExEditorKit.MultiStepAction currentAction;
private @Nullable ExEditorKit.MultiStepAction currentAction;
private char currentActionPromptCharacter;
private int currentActionPromptCharacterOffset = -1;

View File

@@ -30,9 +30,9 @@ import java.awt.event.KeyListener;
* @author vlan
*/
public class ModalEntryDialog extends JDialog {
@NotNull private final JTextField myEntry;
@NotNull private final JLabel myLabel;
@NotNull private final JComponent myParent;
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);
@@ -89,8 +89,7 @@ public class ModalEntryDialog extends JDialog {
myEntry.addKeyListener(listener);
}
@NotNull
public String getText() {
public @NotNull String getText() {
return myEntry.getText();
}
}

View File

@@ -47,25 +47,22 @@ import java.util.*;
* @author vlan
*/
public class VimEmulationConfigurable implements Configurable {
@NotNull private final VimShortcutConflictsTable.Model myConflictsTableModel = new VimShortcutConflictsTable.Model();
@NotNull private final VimSettingsPanel myPanel = new VimSettingsPanel(myConflictsTableModel);
private final @NotNull VimShortcutConflictsTable.Model myConflictsTableModel = new VimShortcutConflictsTable.Model();
private final @NotNull VimSettingsPanel myPanel = new VimSettingsPanel(myConflictsTableModel);
@NotNull
@Nls
@Override
public String getDisplayName() {
public @NotNull String getDisplayName() {
return "Vim Emulation";
}
@Nullable
@Override
public String getHelpTopic() {
public @Nullable String getHelpTopic() {
return null;
}
@Nullable
@Override
public JComponent createComponent() {
public @Nullable JComponent createComponent() {
return myPanel;
}
@@ -89,7 +86,7 @@ public class VimEmulationConfigurable implements Configurable {
}
private static final class VimSettingsPanel extends JPanel {
@NotNull private final VimShortcutConflictsTable myShortcutConflictsTable;
private final @NotNull VimShortcutConflictsTable myShortcutConflictsTable;
public VimSettingsPanel(@NotNull VimShortcutConflictsTable.Model model) {
myShortcutConflictsTable = new VimShortcutConflictsTable(model);
@@ -116,20 +113,17 @@ public class VimEmulationConfigurable implements Configurable {
ownerColumn.setCellEditor(renderer);
}
@NotNull
@Override
public Dimension getMinimumSize() {
public @NotNull Dimension getMinimumSize() {
return calcSize(super.getMinimumSize());
}
@NotNull
@Override
public Dimension getPreferredSize() {
public @NotNull Dimension getPreferredSize() {
return calcSize(super.getPreferredSize());
}
@NotNull
private Dimension calcSize(@NotNull Dimension dimension) {
private @NotNull Dimension calcSize(@NotNull Dimension dimension) {
final Container container = getParent();
if (container != null) {
final Dimension size = container.getSize();
@@ -138,8 +132,7 @@ public class VimEmulationConfigurable implements Configurable {
return dimension;
}
@NotNull
private TableColumn getTableColumn(@NotNull Column column) {
private @NotNull TableColumn getTableColumn(@NotNull Column column) {
return getColumnModel().getColumn(column.getIndex());
}
@@ -167,7 +160,7 @@ public class VimEmulationConfigurable implements Configurable {
IDE_ACTION(1, "IDE Action"),
OWNER(2, "Handler");
@NotNull private static final Map<Integer, Column> ourMembers = new HashMap<>();
private static final @NotNull Map<Integer, Column> ourMembers = new HashMap<>();
static {
for (Column column : values()) {
@@ -176,15 +169,14 @@ public class VimEmulationConfigurable implements Configurable {
}
private final int myIndex;
@NotNull private final String myTitle;
private final @NotNull String myTitle;
Column(int index, @NotNull String title) {
myIndex = index;
myTitle = title;
}
@Nullable
public static Column fromIndex(int index) {
public static @Nullable Column fromIndex(int index) {
return ourMembers.get(index);
}
@@ -192,16 +184,15 @@ public class VimEmulationConfigurable implements Configurable {
return myIndex;
}
@NotNull
public String getTitle() {
public @NotNull String getTitle() {
return myTitle;
}
}
private static final class Row implements Comparable<Row> {
@NotNull private final KeyStroke myKeyStroke;
@NotNull private final AnAction myAction;
@NotNull private ShortcutOwner myOwner;
private final @NotNull KeyStroke myKeyStroke;
private final @NotNull AnAction myAction;
private @NotNull ShortcutOwner myOwner;
private Row(@NotNull KeyStroke keyStroke, @NotNull AnAction action, @NotNull ShortcutOwner owner) {
myKeyStroke = keyStroke;
@@ -209,18 +200,15 @@ public class VimEmulationConfigurable implements Configurable {
myOwner = owner;
}
@NotNull
public KeyStroke getKeyStroke() {
public @NotNull KeyStroke getKeyStroke() {
return myKeyStroke;
}
@NotNull
public AnAction getAction() {
public @NotNull AnAction getAction() {
return myAction;
}
@NotNull
public ShortcutOwner getOwner() {
public @NotNull ShortcutOwner getOwner() {
return myOwner;
}
@@ -237,7 +225,7 @@ public class VimEmulationConfigurable implements Configurable {
}
private static final class Model extends AbstractTableModel {
@NotNull private final List<Row> myRows = new ArrayList<>();
private final @NotNull List<Row> myRows = new ArrayList<>();
public Model() {
reset();
@@ -253,9 +241,8 @@ public class VimEmulationConfigurable implements Configurable {
return Column.values().length;
}
@Nullable
@Override
public Object getValueAt(int rowIndex, int columnIndex) {
public @Nullable Object getValueAt(int rowIndex, int columnIndex) {
final Column column = Column.fromIndex(columnIndex);
if (column != null && rowIndex >= 0 && rowIndex < myRows.size()) {
final Row row = myRows.get(rowIndex);
@@ -285,9 +272,8 @@ public class VimEmulationConfigurable implements Configurable {
return Column.fromIndex(columnIndex) == Column.OWNER;
}
@Nullable
@Override
public String getColumnName(int index) {
public @Nullable String getColumnName(int index) {
final Column column = Column.fromIndex(index);
return column != null ? column.getTitle() : null;
}
@@ -312,8 +298,7 @@ public class VimEmulationConfigurable implements Configurable {
Collections.sort(myRows);
}
@NotNull
private Map<KeyStroke, ShortcutOwner> getCurrentData() {
private @NotNull Map<KeyStroke, ShortcutOwner> getCurrentData() {
final Map<KeyStroke, ShortcutOwner> result = new HashMap<>();
for (Row row : myRows) {
result.put(row.getKeyStroke(), row.getOwner());

View File

@@ -21,7 +21,13 @@ package org.jetbrains.plugins.ideavim
import com.maddyhome.idea.vim.RegisterActions.VIM_ACTIONS_EP
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.handler.ActionBeanClass
import com.maddyhome.idea.vim.helper.StringHelper
import com.maddyhome.idea.vim.key.CommandNode
import com.maddyhome.idea.vim.key.CommandPartNode
import junit.framework.TestCase
import javax.swing.KeyStroke
class RegisterActionsTest : VimTestCase() {
fun `test simple action`() {
@@ -65,10 +71,24 @@ class RegisterActionsTest : VimTestCase() {
val keys = StringHelper.parseKeys("l")
val before = "I ${c}found it in a legendary land"
val after = "I f${c}ound it in a legendary land"
var motionRightAction: ActionBeanClass? = null
doTest(keys, before, after, CommandState.Mode.COMMAND, CommandState.SubMode.NONE) {
val motionRightAction = VIM_ACTIONS_EP.extensions().findAny().get();
motionRightAction = VIM_ACTIONS_EP.extensions().findAny().get();
TestCase.assertNotNull(getCommandNode())
@Suppress("DEPRECATION")
VIM_ACTIONS_EP.getPoint(null).unregisterExtension(motionRightAction)
VIM_ACTIONS_EP.getPoint(null).unregisterExtension(motionRightAction!!)
TestCase.assertNull(getCommandNode())
}
@Suppress("DEPRECATION")
VIM_ACTIONS_EP.getPoint(null).registerExtension(motionRightAction!!)
TestCase.assertNotNull(getCommandNode())
}
private fun getCommandNode(): CommandNode? {
// TODO: 08.02.2020 Sorry if your tests will fail because of this test
val node = VimPlugin.getKey().getKeyRoot(MappingMode.NORMAL)[KeyStroke.getKeyStroke('g')] as CommandPartNode
return node[KeyStroke.getKeyStroke('T')] as CommandNode?
}
}

View File

@@ -136,6 +136,12 @@ public abstract class VimTestCase extends UsefulTestCase {
return myFixture.getEditor();
}
@NotNull
protected Editor configureByFileName(@NotNull String fileName) {
myFixture.configureByText(fileName, "\n");
return myFixture.getEditor();
}
@NotNull
protected Editor configureByJavaText(@NotNull String content) {
myFixture.configureByText(JavaFileType.INSTANCE, content);

View File

@@ -292,6 +292,27 @@ public class MotionActionTest extends VimTestCase {
myFixture.checkResult("a{<caret>}b}");
}
// VIM-1008 |c| |v_i{|
public void testDeleteInsideDoubleQuotesSurroundedBlockWithSingleQuote() {
configureByText("\"{do<caret>esn't work}\"");
typeText(parseKeys("ci{"));
myFixture.checkResult("\"{<caret>}\"");
}
// VIM-1008 |c| |v_i{|
public void testDeleteInsideSingleQuotesSurroundedBlock() {
configureByText("'{does n<caret>ot work}'");
typeText(parseKeys("ci{"));
myFixture.checkResult("'{<caret>}'");
}
// VIM-1008 |c| |v_i{|
public void testDeleteInsideDoublySurroundedBlock() {
configureByText("<p class=\"{{ $ctrl.so<caret>meClassName }}\"></p>");
typeText(parseKeys("ci{"));
myFixture.checkResult("<p class=\"{{<caret>}}\"></p>");
}
// |d| |v_i>|
public void testDeleteInnerAngleBracketBlock() {
typeTextInFile(parseKeys("di>"),
@@ -348,6 +369,31 @@ public class MotionActionTest extends VimTestCase {
myFixture.checkResult("foo = [\"\", \"two\", \"three\"];\n");
}
public void testDeleteDoubleQuotedStringOddNumberOfQuotes() {
typeTextInFile(parseKeys("di\""),
"abc\"def<caret>\"gh\"i");
myFixture.checkResult("abc\"\"gh\"i");
}
public void testDeleteDoubleQuotedStringBetweenEvenNumberOfQuotes() {
typeTextInFile(parseKeys("di\""),
"abc\"def\"g<caret>h\"ijk\"l");
myFixture.checkResult("abc\"def\"\"ijk\"l");
}
public void testDeleteDoubleQuotedStringOddNumberOfQuotesOnLast() {
typeTextInFile(parseKeys("di\""),
"abcdef\"gh\"ij<caret>\"kl");
myFixture.checkResult("abcdef\"gh\"ij\"kl");
}
public void testDeleteDoubleQuotedStringEvenNumberOfQuotesOnLast() {
typeTextInFile(parseKeys("di\""),
"abc\"def\"gh\"ij<caret>\"kl");
myFixture.checkResult("abc\"def\"gh\"\"kl");
}
// VIM-132 |v_i"|
public void testInnerDoubleQuotedStringSelection() {
typeTextInFile(parseKeys("vi\""),

View File

@@ -102,6 +102,22 @@ class MotionInnerBlockParenActionTest : VimTestCase() {
myFixture.checkResult("foo()\n")
}
// VIM-1008 |d| |v_ib|
fun testDeleteInnerBlockWithQuote() {
typeTextInFile(parseKeys("di)"),
"(abc${c}def'ghi)"
)
myFixture.checkResult("()")
}
// VIM-1008 |d| |v_ib|
fun testDeleteInnerBlockWithDoubleQuote() {
typeTextInFile(parseKeys("di)"),
"""(abc${c}def"ghi)"""
)
myFixture.checkResult("()")
}
// VIM-326 |d| |v_ib|
fun testDeleteInnerBlockCaretBeforeString() {
typeTextInFile(parseKeys("di)"),

View File

@@ -20,8 +20,12 @@ package org.jetbrains.plugins.ideavim.ex
import com.maddyhome.idea.vim.VimPlugin
import com.maddyhome.idea.vim.command.CommandState
import com.maddyhome.idea.vim.ex.CommandParser
import com.maddyhome.idea.vim.ex.CommandParser.EX_COMMAND_EP
import com.maddyhome.idea.vim.ex.ExBeanClass
import com.maddyhome.idea.vim.ex.ExCommand
import com.maddyhome.idea.vim.ex.commands
import com.maddyhome.idea.vim.ex.ranges.Ranges
import junit.framework.TestCase
import org.jetbrains.plugins.ideavim.VimTestCase
@@ -91,10 +95,21 @@ class CommandParserTest : VimTestCase() {
val keys = commandToKeys(">>")
val before = "I ${c}found it in a legendary land"
val after = " ${c}I found it in a legendary land"
var extension: ExBeanClass? = null
doTest(keys, before, after, CommandState.Mode.COMMAND, CommandState.SubMode.NONE) {
val extension = EX_COMMAND_EP.extensions().findFirst().get()
extension = EX_COMMAND_EP.extensions().findFirst().get()
// TODO: 08.02.2020 I'm sorry if your tests have been failed because of this code. Please update it properly
TestCase.assertNotNull(CommandParser.getInstance().getCommandHandler(ExCommand(Ranges(), "actionlist", "")))
@Suppress("DEPRECATION")
EX_COMMAND_EP.getPoint(null).unregisterExtension(extension)
EX_COMMAND_EP.getPoint(null).unregisterExtension(extension!!)
TestCase.assertNull(CommandParser.getInstance().getCommandHandler(ExCommand(Ranges(), "actionlist", "")))
}
@Suppress("DEPRECATION")
EX_COMMAND_EP.getPoint(null).registerExtension(extension!!)
TestCase.assertNotNull(CommandParser.getInstance().getCommandHandler(ExCommand(Ranges(), "actionlist", "")))
}
}

View File

@@ -0,0 +1,109 @@
/*
* 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.ex.handler
import com.maddyhome.idea.vim.ex.ExOutputModel.Companion.getInstance
import com.maddyhome.idea.vim.helper.StringHelper
import junit.framework.TestCase
import org.jetbrains.plugins.ideavim.VimTestCase
/**
* @author John Weigel
*/
class BufferListHandlerTest : VimTestCase() {
companion object {
const val DEFAULT_LS_OUTPUT = " 1 %a \"/src/aaa.txt\" line: 1"
}
fun testLsAction() {
configureByText("\n")
typeText(commandToKeys("ls"))
val output = getInstance(myFixture.editor).text
TestCase.assertNotNull(output)
val displayedLines = output!!.split("\n".toRegex()).toTypedArray()
TestCase.assertEquals(DEFAULT_LS_OUTPUT, displayedLines[0])
assertPluginError(false)
}
fun testLsActionWithLongFileName() {
configureByFileName("aaaaaaaaaaaaaaaaaaaaaaaaaaaaa.txt")
typeText(commandToKeys("ls"))
val output = getInstance(myFixture.editor).text
TestCase.assertNotNull(output)
val displayedLines = output!!.split("\n".toRegex()).toTypedArray()
TestCase.assertEquals(" 1 %a \"/src/aaaaaaaaaaaaaaaaaaaaaaaaaaaaa.txt\" line: 1", displayedLines[0])
assertPluginError(false)
}
fun testFilesAction() {
configureByText("\n")
typeText(commandToKeys("files"))
assertPluginError(false)
}
fun testBuffersAction() {
configureByText("\n")
typeText(commandToKeys("buffers"))
assertPluginError(false)
}
fun testBuffersActionWithSupportedFilterMatch() {
configureByFileName("aaa.txt")
configureByFileName("bbb.txt")
typeText(StringHelper.parseKeys("aa<esc>:buffers +<enter>"))
val output = getInstance(myFixture.editor).text
TestCase.assertNotNull(output)
val displayedLines = output!!.split("\n".toRegex()).toTypedArray()
// Ignore buffer number because IJ sometimes returns different order of buffers
val line = displayedLines[0].replaceRange(3, 4, "_")
TestCase.assertEquals(" _ %a + \"/src/bbb.txt\" line: 1", line)
assertPluginError(false)
}
fun testBuffersActionWithSupportedFilterDoesNotMatch() {
configureByText("\n")
typeText(StringHelper.parseKeys("aa<esc>:buffers #<enter>"))
val output = getInstance(myFixture.editor).text
TestCase.assertNotNull(output)
val displayedLines = output!!.split("\n".toRegex()).toTypedArray()
TestCase.assertEquals("", displayedLines[0])
assertPluginError(false)
}
fun testBuffersActionWithUnSupportedFilter() {
configureByText("\n")
typeText(commandToKeys("buffers x"))
val output = getInstance(myFixture.editor).text
TestCase.assertNotNull(output)
val displayedLines = output!!.split("\n".toRegex()).toTypedArray()
TestCase.assertEquals(DEFAULT_LS_OUTPUT, displayedLines[0])
assertPluginError(false)
}
}

View File

@@ -20,13 +20,14 @@ package org.jetbrains.plugins.ideavim.extension
import com.intellij.openapi.actionSystem.DataContext
import com.intellij.openapi.editor.Editor
import com.intellij.testFramework.UsefulTestCase
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.extension.VimExtension
import com.maddyhome.idea.vim.extension.VimExtensionFacade.putExtensionHandlerMapping
import com.maddyhome.idea.vim.extension.VimExtensionFacade.putKeyMapping
import com.maddyhome.idea.vim.extension.VimExtensionHandler
import com.maddyhome.idea.vim.extension.VimNonDisposableExtension
import com.maddyhome.idea.vim.group.MotionGroup
import com.maddyhome.idea.vim.helper.StringHelper.parseKeys
import com.maddyhome.idea.vim.helper.isEndAllowed
@@ -36,11 +37,14 @@ import org.jetbrains.plugins.ideavim.VimTestCase
class OpMappingTest : VimTestCase() {
private var initialized = false
private val extension = TestExtension()
override fun setUp() {
super.setUp()
if (!initialized) {
initialized = true
TestExtension().init()
VimExtension.EP_NAME.getPoint(null).registerExtension(extension)
enableExtensions("TestExtension")
}
}
@@ -87,21 +91,70 @@ class OpMappingTest : VimTestCase() {
CommandState.Mode.COMMAND,
CommandState.SubMode.NONE)
}
fun `test disable extension via set`() {
configureByText("${c}I found it in a legendary land")
typeText(parseKeys("Q"))
myFixture.checkResult("I${c} found it in a legendary land")
enterCommand("set noTestExtension")
typeText(parseKeys("Q"))
myFixture.checkResult("I${c} found it in a legendary land")
enterCommand("set TestExtension")
typeText(parseKeys("Q"))
myFixture.checkResult("I ${c}found it in a legendary land")
}
fun `test disable extension as extension point`() {
configureByText("${c}I found it in a legendary land")
typeText(parseKeys("Q"))
myFixture.checkResult("I${c} found it in a legendary land")
VimExtension.EP_NAME.getPoint(null).unregisterExtension(TestExtension::class.java)
UsefulTestCase.assertEmpty(VimPlugin.getKey().getKeyMappingByOwner(extension.owner))
typeText(parseKeys("Q"))
myFixture.checkResult("I${c} found it in a legendary land")
VimExtension.EP_NAME.getPoint(null).registerExtension(extension)
UsefulTestCase.assertEmpty(VimPlugin.getKey().getKeyMappingByOwner(extension.owner))
enableExtensions("TestExtension")
typeText(parseKeys("Q"))
myFixture.checkResult("I ${c}found it in a legendary land")
}
fun `test disable disposed extension`() {
configureByText("${c}I found it in a legendary land")
typeText(parseKeys("Q"))
myFixture.checkResult("I${c} found it in a legendary land")
enterCommand("set noTestExtension")
VimExtension.EP_NAME.getPoint(null).unregisterExtension(TestExtension::class.java)
typeText(parseKeys("Q"))
myFixture.checkResult("I${c} found it in a legendary land")
VimExtension.EP_NAME.getPoint(null).registerExtension(extension)
enableExtensions("TestExtension")
typeText(parseKeys("Q"))
myFixture.checkResult("I ${c}found it in a legendary land")
}
}
private class TestExtension : VimNonDisposableExtension() {
private class TestExtension : VimExtension {
override fun getName(): String = "TestExtension"
override fun initOnce() {
putExtensionHandlerMapping(MappingMode.O, parseKeys("<Plug>TestExtensionEmulateInclusive"), MoveEmulateInclusive(), false)
putExtensionHandlerMapping(MappingMode.O, parseKeys("<Plug>TestExtensionBackwardsCharacter"), MoveBackwards(), false)
putExtensionHandlerMapping(MappingMode.O, parseKeys("<Plug>TestExtensionCharacter"), Move(), false)
putExtensionHandlerMapping(MappingMode.O, parseKeys("<Plug>TestExtensionLinewise"), MoveLinewise(), false)
override fun init() {
putExtensionHandlerMapping(MappingMode.O, parseKeys("<Plug>TestExtensionEmulateInclusive"), owner, MoveEmulateInclusive(), false)
putExtensionHandlerMapping(MappingMode.O, parseKeys("<Plug>TestExtensionBackwardsCharacter"), owner, MoveBackwards(), false)
putExtensionHandlerMapping(MappingMode.O, parseKeys("<Plug>TestExtensionCharacter"), owner, Move(), false)
putExtensionHandlerMapping(MappingMode.O, parseKeys("<Plug>TestExtensionLinewise"), owner, MoveLinewise(), false)
putExtensionHandlerMapping(MappingMode.N, parseKeys("<Plug>TestMotion"), owner, MoveLinewiseInNormal(), false)
putKeyMapping(MappingMode.O, parseKeys("U"), parseKeys("<Plug>TestExtensionEmulateInclusive"), true)
putKeyMapping(MappingMode.O, parseKeys("P"), parseKeys("<Plug>TestExtensionBackwardsCharacter"), true)
putKeyMapping(MappingMode.O, parseKeys("I"), parseKeys("<Plug>TestExtensionCharacter"), true)
putKeyMapping(MappingMode.O, parseKeys("O"), parseKeys("<Plug>TestExtensionLinewise"), true)
putKeyMapping(MappingMode.O, parseKeys("U"), owner, parseKeys("<Plug>TestExtensionEmulateInclusive"), true)
putKeyMapping(MappingMode.O, parseKeys("P"), owner, parseKeys("<Plug>TestExtensionBackwardsCharacter"), true)
putKeyMapping(MappingMode.O, parseKeys("I"), owner, parseKeys("<Plug>TestExtensionCharacter"), true)
putKeyMapping(MappingMode.O, parseKeys("O"), owner, parseKeys("<Plug>TestExtensionLinewise"), true)
putKeyMapping(MappingMode.N, parseKeys("Q"), owner, parseKeys("<Plug>TestMotion"), true)
}
private class MoveEmulateInclusive : VimExtensionHandler {
@@ -133,4 +186,12 @@ private class TestExtension : VimNonDisposableExtension() {
MotionGroup.moveCaret(editor, caret, newOffset)
}
}
private class MoveLinewiseInNormal : VimExtensionHandler {
override fun execute(editor: Editor, context: DataContext) {
val caret = editor.caretModel.currentCaret
val newOffset = VimPlugin.getMotion().moveCaretHorizontal(editor, caret, 1, true)
MotionGroup.moveCaret(editor, caret, newOffset)
}
}
}

View File

@@ -0,0 +1,144 @@
/*
* 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.entiretextobj
import com.maddyhome.idea.vim.command.CommandState
import com.maddyhome.idea.vim.helper.StringHelper
import org.jetbrains.plugins.ideavim.JavaVimTestCase
/**
* @author Alexandre Grison (@agrison)
*/
class VimTextObjEntireExtensionTest : JavaVimTestCase() {
override fun setUp() {
super.setUp()
enableExtensions("textobj-entire")
}
// |gU| |ae|
fun testUpperCaseEntireBuffer() {
doTest(StringHelper.parseKeys("gUae"), poem,"<caret>${poemUC}")
assertMode(CommandState.Mode.COMMAND)
assertSelection(null)
}
// |gu| |ae|
fun testLowerCaseEntireBuffer() {
doTest(StringHelper.parseKeys("guae"), poem, "<caret>${poemLC}");
assertMode(CommandState.Mode.COMMAND)
assertSelection(null)
}
// |c| |ae|
fun testChangeEntireBuffer() {
doTest(StringHelper.parseKeys("cae"), poem, "<caret>");
assertMode(CommandState.Mode.INSERT);
assertSelection(null);
}
// |d| |ae|
fun testDeleteEntireBuffer() {
doTest(StringHelper.parseKeys("dae"), poem, "<caret>");
assertMode(CommandState.Mode.COMMAND);
assertSelection(null);
}
// |y| |ae|
fun testYankEntireBuffer() {
doTest(StringHelper.parseKeys("yae"), poem, "<caret>${poemNoCaret}");
assertMode(CommandState.Mode.COMMAND);
myFixture.checkResult(poemNoCaret);
assertSelection(null);
}
// |gU| |ie|
fun testUpperCaseEntireBufferIgnoreLeadingTrailing() {
doTest(StringHelper.parseKeys("gUie"),
"\n \n \n${poem}\n \n \n",
"\n \n \n<caret>${poemUC}\n \n \n")
assertMode(CommandState.Mode.COMMAND)
assertSelection(null)
}
// |gu| |ae|
fun testLowerCaseEntireBufferIgnoreLeadingTrailing() {
doTest(StringHelper.parseKeys("guie"),
"\n \n \n${poem}\n \n \n",
"\n \n \n<caret>${poemLC}\n \n \n")
assertMode(CommandState.Mode.COMMAND)
assertSelection(null)
}
// |c| |ae|
fun testChangeEntireBufferIgnoreLeadingTrailing() {
doTest(StringHelper.parseKeys("cie"),
"\n \n \n${poem}\n \n \n",
"\n \n \n<caret>\n\n \n \n"); // additional \n because poem ends with a \n
assertMode(CommandState.Mode.INSERT);
assertSelection(null);
}
// |d| |ae|
fun testDeleteEntireBufferIgnoreLeadingTrailing() {
doTest(StringHelper.parseKeys("die"),
"\n \n \n${poem}\n \n \n",
"\n \n \n<caret>\n\n \n \n"); // additional \n because poem ends with a \n
assertMode(CommandState.Mode.COMMAND);
assertSelection(null);
}
// |y| |ae|
fun testYankEntireBufferIgnoreLeadingTrailing() {
doTest(StringHelper.parseKeys("yie"),
"\n \n \n${poem}\n \n \n",
"\n \n \n<caret>${poemNoCaret}\n \n \n");
assertMode(CommandState.Mode.COMMAND);
myFixture.checkResult("\n \n \n${poemNoCaret}\n \n \n");
assertSelection(null);
}
val poem: String = """Two roads diverged in a yellow wood,
And sorry I could not travel both
And be one traveler, long I stood
And looked down one as far as I could
To where it bent in the undergrowth;
Then took the other, as just as fair,
And having perhaps the better claim,
Because it was grassy and wanted wear;
Though as for that the passing there
Had worn them really about the same,
And both that morning equally lay
In leaves no step had trodden black.
Oh, I kept the first for another day!
Yet knowing how way leads on to way,
<caret>I doubted if I should ever come back.
I shall be telling this with a sigh
Somewhere ages and ages hence:
Two roads diverged in a wood, and I—
I took the one less traveled by,
And that has made all the difference.
"""
val poemNoCaret = poem.replace("<caret>", "")
val poemUC = poemNoCaret.toUpperCase()
val poemLC = poemNoCaret.toLowerCase()
}

View File

@@ -0,0 +1,299 @@
package org.jetbrains.plugins.ideavim.extesion.argtextobj;
import com.maddyhome.idea.vim.command.CommandState;
import com.maddyhome.idea.vim.helper.VimBehaviorDiffers;
import org.jetbrains.plugins.ideavim.VimTestCase;
import java.util.Collections;
import static com.maddyhome.idea.vim.helper.StringHelper.parseKeys;
public class VimArgTextObjExtensionTest extends VimTestCase {
@Override
protected void setUp() throws Exception {
super.setUp();
enableExtensions("argtextobj");
}
public void testDeleteAnArgument() {
doTest(parseKeys("daa"),
"function(int arg1, char<caret>* arg2=\"a,b,c(d,e)\")",
"function(int arg1<caret>)",
CommandState.Mode.COMMAND, CommandState.SubMode.NONE);
doTest(parseKeys("daa"),
"function(int arg1<caret>)",
"function(<caret>)",
CommandState.Mode.COMMAND, CommandState.SubMode.NONE);
}
public void testChangeInnerArgument() {
doTest(parseKeys("cia"),
"function(int arg1, char<caret>* arg2=\"a,b,c(d,e)\")",
"function(int arg1, <caret>)",
CommandState.Mode.INSERT, CommandState.SubMode.NONE);
}
public void testSmartArgumentRecognition() {
doTest(parseKeys("dia"),
"function(1, (20<caret>*30)+40, somefunc2(3, 4))",
"function(1, <caret>, somefunc2(3, 4))",
CommandState.Mode.COMMAND, CommandState.SubMode.NONE);
doTest(parseKeys("daa"),
"function(1, (20*30)+40, somefunc2(<caret>3, 4))",
"function(1, (20*30)+40, somefunc2(<caret>4))",
CommandState.Mode.COMMAND, CommandState.SubMode.NONE);
}
public void testIgnoreQuotedArguments() {
doTest(parseKeys("daa"),
"function(int arg1, char* arg2=a,b,c(<caret>arg,e))",
"function(int arg1, char* arg2=a,b,c(<caret>e))",
CommandState.Mode.COMMAND, CommandState.SubMode.NONE);
doTest(parseKeys("daa"),
"function(int arg1, char* arg2=\"a,b,c(<caret>arg,e)\")",
"function(int arg1<caret>)",
CommandState.Mode.COMMAND, CommandState.SubMode.NONE);
doTest(parseKeys("daa"),
"function(int arg1, char* arg2=\"a,b,c(arg,e\"<caret>)",
"function(int arg1<caret>)",
CommandState.Mode.COMMAND, CommandState.SubMode.NONE);
doTest(parseKeys("daa"),
"function(int arg1, char* a<caret>rg2={\"a,b},c(arg,e\"})",
"function(int arg1<caret>)",
CommandState.Mode.COMMAND, CommandState.SubMode.NONE);
}
public void testDeleteTwoArguments() {
doTest(parseKeys("d2aa"),
"function(int <caret>arg1, char* arg2=\"a,b,c(d,e)\")",
"function(<caret>)",
CommandState.Mode.COMMAND, CommandState.SubMode.NONE);
doTest(parseKeys("d2ia"),
"function(int <caret>arg1, char* arg2=\"a,b,c(d,e)\")",
"function(<caret>)",
CommandState.Mode.COMMAND, CommandState.SubMode.NONE);
doTest(parseKeys("d2aa"),
"function(int <caret>arg1, char* arg2=\"a,b,c(d,e)\", bool arg3)",
"function(<caret>bool arg3)",
CommandState.Mode.COMMAND, CommandState.SubMode.NONE);
doTest(parseKeys("d2ia"),
"function(int <caret>arg1, char* arg2=\"a,b,c(d,e)\", bool arg3)",
"function(<caret>, bool arg3)",
CommandState.Mode.COMMAND, CommandState.SubMode.NONE);
doTest(parseKeys("d2aa"),
"function(int arg1, char* arg<caret>2=\"a,b,c(d,e)\", bool arg3)",
"function(int arg1<caret>)",
CommandState.Mode.COMMAND, CommandState.SubMode.NONE);
doTest(parseKeys("d2ia"),
"function(int arg1, char* arg<caret>2=\"a,b,c(d,e)\", bool arg3)",
"function(int arg1, <caret>)",
CommandState.Mode.COMMAND, CommandState.SubMode.NONE);
}
public void testSelectTwoArguments() {
doTest(parseKeys("v2aa"),
"function(int <caret>arg1, char* arg2=\"a,b,c(d,e)\", bool arg3)",
"function(<selection>int arg1, char* arg2=\"a,b,c(d,e)\", </selection>bool arg3)",
CommandState.Mode.VISUAL, CommandState.SubMode.VISUAL_CHARACTER);
doTest(parseKeys("v2ia"),
"function(int <caret>arg1, char* arg2=\"a,b,c(d,e)\", bool arg3)",
"function(<selection>int arg1, char* arg2=\"a,b,c(d,e)\"</selection>, bool arg3)",
CommandState.Mode.VISUAL, CommandState.SubMode.VISUAL_CHARACTER);
}
// The original author of this extension wanted this case to work
/*
public void testArgumentsInsideAngleBrackets() {
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 testBracketPriorityToHangleShiftOperators() {
doTest(parseKeys("dia"),
"foo(30 << 10, 20 << <caret>3) >> 17",
"foo(30 << 10, <caret>) >> 17",
CommandState.Mode.COMMAND, CommandState.SubMode.NONE);
doTest(parseKeys("dia"),
"foo(30 << <caret>10, 20 * 3) >> 17",
"foo(<caret>, 20 * 3) >> 17",
CommandState.Mode.COMMAND, CommandState.SubMode.NONE);
doTest(parseKeys("dia"),
"foo(<caret>30 >> 10, 20 * 3) << 17",
"foo(<caret>, 20 * 3) << 17",
CommandState.Mode.COMMAND, CommandState.SubMode.NONE);
}
public void testEmptyFile() {
assertPluginError(false);
doTest(parseKeys("daa"),
"<caret>",
"<caret>",
CommandState.Mode.COMMAND, CommandState.SubMode.NONE);
assertPluginError(true);
doTest(parseKeys("dia"),
"<caret>",
"<caret>",
CommandState.Mode.COMMAND, CommandState.SubMode.NONE);
assertPluginError(true);
}
public void testEmptyLine() {
assertPluginError(false);
doTest(parseKeys("daa"),
"<caret>\n",
"<caret>\n",
CommandState.Mode.COMMAND, CommandState.SubMode.NONE);
assertPluginError(true);
doTest(parseKeys("dia"),
"<caret>\n",
"<caret>\n",
CommandState.Mode.COMMAND, CommandState.SubMode.NONE);
assertPluginError(true);
}
public void testEmptyArg() {
assertPluginError(false);
doTest(parseKeys("daa"),
"foo(<caret>)",
"foo(<caret>)",
CommandState.Mode.COMMAND, CommandState.SubMode.NONE);
assertPluginError(true);
doTest(parseKeys("dia"),
"foo(<caret>)",
"foo(<caret>)",
CommandState.Mode.COMMAND, CommandState.SubMode.NONE);
assertPluginError(true);
}
public void testSkipCommasInsideNestedPairs() {
final String before = "void foo(int arg1)\n{" +
" methodCall(arg1, \"{ arg1 , 2\");\n" +
" otherMeth<caret>odcall(arg, 3);\n" +
"}";
doTest(parseKeys("dia"), before, before,
CommandState.Mode.COMMAND, CommandState.SubMode.NONE);
assertPluginError(true);
}
public void testHandleNestedPairs() {
doTest(parseKeys("dia"),
"foo(arg1, arr<caret>ay[someexpr(Class{arg1 << 3, arg2})] + 3)\n{",
"foo(arg1, <caret>)\n{",
CommandState.Mode.COMMAND, CommandState.SubMode.NONE);
}
public void testHandleImbalancedPairs() {
doTest(parseKeys("dia"),
"foo(arg1, ba<caret>r(not-an-arg{body",
"foo(arg1, ba<caret>r(not-an-arg{body",
CommandState.Mode.COMMAND, CommandState.SubMode.NONE);
assertPluginError(true);
doTest(parseKeys("dia"),
"foo(arg1, ba<caret>r ( x > 3 )",
"foo(arg1, ba<caret>r ( x > 3 )",
CommandState.Mode.COMMAND, CommandState.SubMode.NONE);
assertPluginError(true);
doTest(parseKeys("dia"),
"foo(arg1, ba<caret>r + x >",
"foo(arg1, ba<caret>r + x >",
CommandState.Mode.COMMAND, CommandState.SubMode.NONE);
assertPluginError(true);
doTest(parseKeys("dia"),
"<arg1, ba<caret>r + x)",
"<arg1, ba<caret>r + x)",
CommandState.Mode.COMMAND, CommandState.SubMode.NONE);
assertPluginError(true);
}
public void testArgumentBoundsSearchIsLimitedByLineCount() {
final String before = "foo(\n" +
String.join("", Collections.nCopies(10, " arg,\n")) +
" last<caret>Arg" +
")";
doTest(parseKeys("dia"), before, before,
CommandState.Mode.COMMAND, CommandState.SubMode.NONE);
assertPluginError(true);
}
public void testExtendVisualSelection() {
doTest(parseKeys("vllia"),
"function(int arg1, ch<caret>ar* arg2=\"a,b,c(d,e)\")",
"function(int arg1, <selection>char* arg2=\"a,b,c(d,e)\"</selection>)",
CommandState.Mode.VISUAL, CommandState.SubMode.VISUAL_CHARACTER);
doTest(parseKeys("vhhia"),
"function(int arg1, char<caret>* arg2=\"a,b,c(d,e)\")",
"function(int arg1, <selection>char* arg2=\"a,b,c(d,e)\"</selection>)",
CommandState.Mode.VISUAL, CommandState.SubMode.VISUAL_CHARACTER);
}
public void testExtendVisualSelectionUsesCaretPos() {
doTest(parseKeys("vllia"),
"fu<caret>n(arg)",
"fun(<selection>arg</selection>)",
CommandState.Mode.VISUAL, CommandState.SubMode.VISUAL_CHARACTER);
}
public void testDeleteArrayArgument() {
doTest(parseKeys("dia"),
"function(int a, String[<caret>] b)",
"function(int a, <caret>)",
CommandState.Mode.COMMAND, CommandState.SubMode.NONE);
doTest(parseKeys("daa"),
"function(int a, String[<caret>] b)",
"function(int a)",
CommandState.Mode.COMMAND, CommandState.SubMode.NONE);
}
public void testDeleteInClass() {
doTest(parseKeys("dia"),
"class MyClass{ public int myFun() { some<caret>Call(); } }",
"class MyClass{ public int myFun() { some<caret>Call(); } }",
CommandState.Mode.COMMAND, CommandState.SubMode.NONE);
doTest(parseKeys("daa"),
"class MyClass{ public int myFun() { some<caret>Call(); } }",
"class MyClass{ public int myFun() { some<caret>Call(); } }",
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)",
"function (int <caret>a)",
CommandState.Mode.COMMAND, CommandState.SubMode.NONE);
doTest(parseKeys("daa"),
"function (int <caret>a)",
"function (int <caret>a)",
CommandState.Mode.COMMAND, CommandState.SubMode.NONE);
}
@VimBehaviorDiffers(
originalVimAfter = "function (int <caret>a, int b)",
description = "Should work the same as testFunctionWithSpaceAfterName"
)
public void testFunctionWithSpaceAfterNameWithTwoArgs() {
doTest(parseKeys("dia"),
"function (int <caret>a, int b)",
"function (, int b)",
CommandState.Mode.COMMAND, CommandState.SubMode.NONE);
doTest(parseKeys("daa"),
"function (int <caret>a, int b)",
"function (int b)",
CommandState.Mode.COMMAND, CommandState.SubMode.NONE);
}
public void testDeleteInIf() {
doTest(parseKeys("dia"),
"class MyClass{ public int myFun() { if (tr<caret>ue) { somFunction(); } } }",
"class MyClass{ public int myFun() { if (tr<caret>ue) { somFunction(); } } }",
CommandState.Mode.COMMAND, CommandState.SubMode.NONE);
doTest(parseKeys("daa"),
"class MyClass{ public int myFun() { if (tr<caret>ue) { somFunction(); } } }",
"class MyClass{ public int myFun() { if (tr<caret>ue) { somFunction(); } } }",
CommandState.Mode.COMMAND, CommandState.SubMode.NONE);
}
}

View File

@@ -0,0 +1,66 @@
/*
* 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.group
import com.maddyhome.idea.vim.VimPlugin
import com.maddyhome.idea.vim.command.MappingMode
import com.maddyhome.idea.vim.helper.StringHelper.parseKeys
import com.maddyhome.idea.vim.key.MappingOwner
import org.jetbrains.plugins.ideavim.VimTestCase
class KeyGroupTest : VimTestCase() {
private val owner = MappingOwner.Plugin.get("KeyGroupTest")
fun `test remove key mapping`() {
val keyGroup = VimPlugin.getKey()
val keys = parseKeys("<C-S-B>")
configureByText("I ${c}found it in a legendary land")
typeText(keys)
myFixture.checkResult("I ${c}found it in a legendary land")
keyGroup.putKeyMapping(MappingMode.N, keys, owner, parseKeys("h"), false)
typeText(keys)
myFixture.checkResult("I${c} found it in a legendary land")
keyGroup.removeKeyMapping(owner)
typeText(keys)
myFixture.checkResult("I${c} found it in a legendary land")
}
fun `test remove and add key mapping`() {
val keyGroup = VimPlugin.getKey()
val keys = parseKeys("<C-S-B>")
configureByText("I ${c}found it in a legendary land")
typeText(keys)
myFixture.checkResult("I ${c}found it in a legendary land")
keyGroup.putKeyMapping(MappingMode.N, keys, owner, parseKeys("h"), false)
typeText(keys)
myFixture.checkResult("I${c} found it in a legendary land")
repeat(10) {
keyGroup.removeKeyMapping(owner)
keyGroup.putKeyMapping(MappingMode.N, keys, owner, parseKeys("h"), false)
}
typeText(keys)
myFixture.checkResult("${c}I found it in a legendary land")
}
}

View File

@@ -1205,7 +1205,7 @@ class SearchGroupTest : VimTestCase() {
val project = myFixture.project
val searchGroup = VimPlugin.getSearch()
val ref = Ref.create(-1)
RunnableHelper.runReadCommand(project, {
RunnableHelper.runReadCommand(project, Runnable {
val n = searchGroup.search(editor, pattern, 1, EnumSet.of(CommandFlags.FLAG_SEARCH_FWD), false)
ref.set(n)
}, null, null)

View File

@@ -19,6 +19,7 @@
package org.jetbrains.plugins.ideavim.helper;
import com.maddyhome.idea.vim.helper.SearchHelper;
import com.maddyhome.idea.vim.helper.SearchHelperKtKt;
import org.jetbrains.plugins.ideavim.VimTestCase;
import static com.maddyhome.idea.vim.helper.StringHelper.parseKeys;
@@ -77,4 +78,100 @@ public class SearchHelperTest extends VimTestCase {
typeTextInFile(parseKeys("v", "a("), "((int) nu<caret>m)");
myFixture.checkResult("<selection>((int) num)</selection>");
}
public void testCheckInStringInsideDoubleQuotes() {
String text = "abc\"def\"ghi";
boolean inString = SearchHelperKtKt.checkInString(text, 5, true);
assertTrue(inString);
}
public void testCheckInStringWithoutClosingDoubleQuote() {
String text = "abcdef\"ghi";
boolean inString = SearchHelperKtKt.checkInString(text, 5, true);
assertFalse(inString);
}
public void testCheckInStringOnUnpairedSingleQuote() {
String text = "abc\"d'ef\"ghi";
boolean inString = SearchHelperKtKt.checkInString(text, 5, true);
assertTrue(inString);
}
public void testCheckInStringOutsideOfDoubleQuotesPair() {
String text = "abc\"def\"ghi";
boolean inString = SearchHelperKtKt.checkInString(text, 2, true);
assertFalse(inString);
}
public void testCheckInStringEscapedDoubleQuote() {
String text = "abc\\\"def\"ghi";
boolean inString = SearchHelperKtKt.checkInString(text, 5, true);
assertFalse(inString);
}
public void testCheckInStringOddNumberOfDoubleQuotes() {
String text = "abc\"def\"gh\"i";
boolean inString = SearchHelperKtKt.checkInString(text, 5, true);
assertFalse(inString);
}
public void testCheckInStringInsideSingleQuotesPair() {
String text = "abc\"d'e'f\"ghi";
boolean inString = SearchHelperKtKt.checkInString(text, 6, false);
assertTrue(inString);
}
public void testCheckInStringOnOpeningDoubleQuote() {
String text = "abc\"def\"ghi";
boolean inString = SearchHelperKtKt.checkInString(text, 3, true);
assertTrue(inString);
}
public void testCheckInStringOnClosingDoubleQuote() {
String text = "abc\"def\"ghi";
boolean inString = SearchHelperKtKt.checkInString(text, 7, true);
assertTrue(inString);
}
public void testCheckInStringWithoutQuotes() {
String text = "abcdefghi";
boolean inString = SearchHelperKtKt.checkInString(text, 5, true);
assertFalse(inString);
}
public void testCheckInStringDoubleQuoteInsideSingleQuotes() {
String text = "abc'\"'ef\"ghi";
boolean inString = SearchHelperKtKt.checkInString(text, 5, true);
assertFalse(inString);
}
public void testCheckInStringSingleQuotesAreTooFarFromEachOtherToMakePair() {
String text = "abc'\"de'f\"ghi";
boolean inString = SearchHelperKtKt.checkInString(text, 5, true);
assertTrue(inString);
}
public void testCheckInStringDoubleQuoteInsideSingleQuotesIsInsideSingleQuotedString() {
String text = "abc'\"'def\"ghi";
boolean inString = SearchHelperKtKt.checkInString(text, 4, false);
assertTrue(inString);
}
public void testCheckInStringAfterClosingDoubleQuote() {
String text = "abc\"def\"ghi";
boolean inString = SearchHelperKtKt.checkInString(text, 9, true);
assertFalse(inString);
}
public void testCheckInStringOnMiddleDoubleQuote() {
String text = "abc\"def\"gh\"i";
boolean inString = SearchHelperKtKt.checkInString(text, 7, true);
assertFalse(inString);
}
public void testCheckInStringBetweenPairs() {
String text = "abc\"def\"gh\"ij\"k";
boolean inString = SearchHelperKtKt.checkInString(text, 8, true);
assertFalse(inString);
}
}

View File

@@ -16,19 +16,17 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.maddyhome.idea.vim
package org.jetbrains.plugins.ideavim.key
import com.intellij.openapi.components.ProjectComponent
import com.intellij.openapi.project.Project
import com.maddyhome.idea.vim.listener.VimListenerManager
import com.maddyhome.idea.vim.key.MappingOwner
import junit.framework.TestCase
import org.jetbrains.plugins.ideavim.VimTestCase
/**
* @author Alex Plate
*/
class VimProjectComponent(private val project: Project) : ProjectComponent {
override fun projectOpened() {
if (!VimPlugin.isEnabled()) return
// Project listeners are self-disposable, so there is no need to unregister them on project close
VimListenerManager.ProjectListeners.add(project)
class MappingOwnerTest : VimTestCase() {
fun `test get two plugin owners`() {
val pluginName = "MyPlugin"
val firstOwner = MappingOwner.Plugin.get(pluginName)
val secondOwner = MappingOwner.Plugin.get(pluginName)
TestCase.assertSame(firstOwner, secondOwner)
}
}