1
0
mirror of https://github.com/chylex/IntelliJ-IdeaVim.git synced 2025-03-06 00:32:52 +01:00

Merge remote-tracking branch 'upstream/master' into buffer_command

This commit is contained in:
John Weigel 2020-03-03 23:10:00 -06:00
commit cb00b8b335
21 changed files with 526 additions and 201 deletions

View File

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

View File

@ -33,6 +33,8 @@ _To Be Released:_
**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.

View File

@ -98,6 +98,7 @@ Emulated Vim plugins:
* vim-multiple-cursors
* vim-commentary
* argtextobj.vim [To Be Released]
* vim-textobj-entire [To Be Released]
Not supported (yet):
@ -170,6 +171,11 @@ Available extensions:
* 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

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

@ -3,6 +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

@ -4,6 +4,8 @@
<change-notes><![CDATA[
<ul>
<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>
@ -32,22 +34,16 @@
<component>
<implementation-class>com.maddyhome.idea.vim.DynamicLoaderStopper</implementation-class>
</component>
<component>
<implementation-class>com.maddyhome.idea.vim.VimPlugin</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" 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>
@ -59,6 +55,9 @@
<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

@ -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;
@ -822,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();

View File

@ -18,17 +18,31 @@
package com.maddyhome.idea.vim
import com.intellij.openapi.components.ProjectComponent
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 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 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

@ -83,7 +83,10 @@ public class RegisterActions {
}
public static void unregisterActions() {
VimPlugin.getKey().unregisterCommandActions();
KeyGroup keyGroup = VimPlugin.getKeyIfCreated();
if (keyGroup != null) {
keyGroup.unregisterCommandActions();
}
}
private static void registerVimCommandActions() {

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;
@ -78,11 +82,10 @@ import static com.maddyhome.idea.vim.group.KeyGroup.SHORTCUT_CONFLICTS_ELEMENT;
* 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 = 6;
private static final int STATE_VERSION = 6;
private static long lastBeepTimeMillis;
@ -97,25 +100,24 @@ public class VimPlugin implements BaseComponent, PersistentStateComponent<Elemen
private static final Logger LOG = Logger.getInstance(VimPlugin.class);
@Override
public @NotNull String getComponentName() {
return IDEAVIM_COMPONENT_NAME;
}
private final @NotNull 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");
// Initialize legacy local config.
// Initialize a legacy local config.
if (previousStateVersion == 5) {
//noinspection deprecation
VimLocalConfig.Companion.initialize();
}
if (enabled) turnOnPlugin();
if (enabled) {
Application application = ApplicationManager.getApplication();
if (application.isUnitTestMode()) {
application.invokeAndWait(this::turnOnPlugin);
} else {
application.invokeLater(this::turnOnPlugin);
}
}
LOG.debug("done");
}
@ -133,7 +135,8 @@ public class VimPlugin implements BaseComponent, PersistentStateComponent<Elemen
public static @NotNull NotificationService getNotifications(@Nullable Project project) {
if (project == null) {
return ServiceManager.getService(NotificationService.class);
} else {
}
else {
return ServiceManager.getService(project, NotificationService.class);
}
}
@ -155,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=" +
@ -211,6 +213,10 @@ public class VimPlugin implements BaseComponent, PersistentStateComponent<Elemen
return ServiceManager.getService(RegisterGroup.class);
}
public static @Nullable RegisterGroup getRegisterIfCreated() {
return ServiceManager.getServiceIfCreated(RegisterGroup.class);
}
public static @NotNull FileGroup getFile() {
return ServiceManager.getService(FileGroup.class);
}
@ -219,6 +225,10 @@ public class VimPlugin implements BaseComponent, PersistentStateComponent<Elemen
return ServiceManager.getService(SearchGroup.class);
}
public static @Nullable SearchGroup getSearchIfCreated() {
return ServiceManager.getServiceIfCreated(SearchGroup.class);
}
public static @NotNull ProcessGroup getProcess() {
return ServiceManager.getService(ProcessGroup.class);
}
@ -239,6 +249,10 @@ public class VimPlugin implements BaseComponent, PersistentStateComponent<Elemen
return ServiceManager.getService(KeyGroup.class);
}
public static @Nullable KeyGroup getKeyIfCreated() {
return ServiceManager.getServiceIfCreated(KeyGroup.class);
}
public static @NotNull WindowGroup getWindow() {
return ServiceManager.getService(WindowGroup.class);
}
@ -247,6 +261,10 @@ public class VimPlugin implements BaseComponent, PersistentStateComponent<Elemen
return ServiceManager.getService(EditorGroup.class);
}
public static @Nullable EditorGroup getEditorIfCreated() {
return ServiceManager.getServiceIfCreated(EditorGroup.class);
}
public static @NotNull VisualMotionGroup getVisualMotion() {
return ServiceManager.getService(VisualMotionGroup.class);
}
@ -259,22 +277,6 @@ public class VimPlugin implements BaseComponent, PersistentStateComponent<Elemen
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);
this.state.saveData(element);
return element;
}
private static @NotNull NotificationService getNotifications() {
return getNotifications(null);
}
@ -383,8 +385,8 @@ public class VimPlugin implements BaseComponent, PersistentStateComponent<Elemen
}
}
private static @NotNull VimPlugin getInstance() {
return ApplicationManager.getApplication().getComponent(VimPlugin.class);
public static @NotNull VimPlugin getInstance() {
return ServiceManager.getService(VimPlugin.class);
}
private void turnOnPlugin() {
@ -416,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();
}
@ -486,6 +494,22 @@ public class VimPlugin implements BaseComponent, PersistentStateComponent<Elemen
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

View File

@ -56,50 +56,34 @@ class BufferListHandler : CommandHandler.SingleExecution() {
return true
}
private fun pruneUnsupportedFilters(filter: String): String {
val sb = StringBuilder()
for (f in filter.asIterable()) {
if (SUPPORTED_FILTERS.contains(f)) {
sb.append(f)
}
}
return sb.toString()
}
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)
val project = PlatformDataKeys.PROJECT.getData(context) ?: return emptyList()
if (project != null) {
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)
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 (entry in virtualFileDisplayMap.entries) {
val file = entry.key
val displayFileName = entry.value
val editor = EditorHelper.getEditor(file)
var index = 1
for ((file, displayFileName) in virtualFileDisplayMap) {
val editor = EditorHelper.getEditor(file) ?: continue
if (editor != null) {
val bufStatus = getBufferStatus(editor, file, currentFile, previousFile)
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 ""
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++
}
bufferList.add(String.format(
" %${bufNumPad}s %s %s%${lineNumPad}s line: %d", index, bufStatus, displayFileName, "", lineNum)
)
}
index++
}
return bufferList
@ -127,32 +111,16 @@ class BufferListHandler : CommandHandler.SingleExecution() {
return filePaths
}
private fun bufStatusMatchesFilter(filter: String, bufStatus: String): Boolean {
if (filter.isNotEmpty()) {
for (f in filter.asIterable()) {
if (!bufStatus.contains(f)) {
return false
}
}
}
return true
}
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 {
currentFile == file -> {
bufStatus.append("%a ")
}
previousFile == file -> {
bufStatus.append("# ")
}
else -> {
bufStatus.append(" ")
}
when(file) {
currentFile -> bufStatus.append("%a ")
previousFile -> bufStatus.append("# ")
else -> bufStatus.append(" ")
}
if (!file.isWritable) {

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

@ -225,7 +225,7 @@ public class FileGroup {
/**
* Returns the previous tab.
*/
public VirtualFile getPreviousTab(@NotNull DataContext context) {
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

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
@ -146,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

@ -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 {
private final @NotNull Runnable cmd;
ReadAction(@NotNull Runnable cmd) {
this.cmd = cmd;
}
@Override
public void run() {
ApplicationManager.getApplication().runReadAction(cmd);
}
}
static class WriteAction implements Runnable {
private final @NotNull 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

@ -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

@ -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)