1
0
mirror of https://github.com/chylex/IntelliJ-IdeaVim.git synced 2025-02-25 02:46:01 +01:00

Merge branch 'master' into refactor/command-state

This commit is contained in:
Matt Ellis 2019-12-30 16:31:24 +00:00
commit ebaeff9b4d
No known key found for this signature in database
GPG Key ID: FA6025D54131324B
29 changed files with 479 additions and 552 deletions

View File

@ -19,6 +19,8 @@ usual beta standards.
[To Be Released]
--------------
_Available since 0.54.1 EAP:_
**Features:**
* Surround and Commentary extensions can be repeated with a dot command ([VIM-1118](https://youtrack.jetbrains.com/issue/VIM-1118))
* Support XDG settings standard ([VIM-664](https://youtrack.jetbrains.com/issue/VIM-664))
@ -32,6 +34,7 @@ usual beta standards.
* [VIM-1325](https://youtrack.jetbrains.com/issue/VIM-1325)
[VIM-1050](https://youtrack.jetbrains.com/issue/VIM-1050)
[VIM-1627](https://youtrack.jetbrains.com/issue/VIM-1627)
[VIM-1867](https://youtrack.jetbrains.com/issue/VIM-1867)
Fix bindings for active lookup
* [VIM-1845](https://youtrack.jetbrains.com/issue/VIM-1845) Show ActionGroup popups
* [VIM-1424](https://youtrack.jetbrains.com/issue/VIM-1424) CTRL-A doesn't have any restrictions now
@ -40,7 +43,21 @@ usual beta standards.
* [VIM-1853](https://youtrack.jetbrains.com/issue/VIM-1853) Fix marks for disposed projects
* [VIM-1858](https://youtrack.jetbrains.com/issue/VIM-1858) Fix imap for autocomplete
* [VIM-1362](https://youtrack.jetbrains.com/issue/VIM-1362) Search with confirm doesn't scroll down far enough
_Available since 0.54.2 EAP:_
**Fixes:**
* [VIM-1875](https://youtrack.jetbrains.com/issue/VIM-1875) Fix `isk` in `~/.ideaivmrc`
* [VIM-1874](https://youtrack.jetbrains.com/issue/VIM-1874) Fix `set clipboard=unnamed` execution from `~/.ideavimrc`
* [VIM-1878](https://youtrack.jetbrains.com/issue/VIM-1878) Fix `c` command after extract method action
* [VIM-1884](https://youtrack.jetbrains.com/issue/VIM-1884) Show quickDoc during popup with `CTRL-J`
_Not yet released_
**Fixes:**
* [VIM-1284](https://youtrack.jetbrains.com/issue/VIM-1284) Fix mapping of digits
* Fix handling of counts on both operator and motion, e.g. `3d2w` deletes 6 words, instead of 32
* Allow mapping of `<C-K>` and `<C-V>`/`<C-Q>`
0.54, 2019-11-20
--------------

View File

@ -21,6 +21,7 @@
<vendor>JetBrains</vendor>
<!-- Please search for "[VERSION UPDATE]" in project in case you update the since-build version -->
<!-- Check for [Version Update] tag in YouTrack as well -->
<idea-version since-build="183.4284.148"/>
<!-- Mark the plugin as compatible with RubyMine and other products based on the IntelliJ platform -->

View File

@ -138,7 +138,9 @@ class VimShortcutKeyAction : AnAction(), DumbAware {
private fun isEnabledForLookup(keyStroke: KeyStroke): Boolean {
val notAllowedKeys = parseKeysSet(
"<TAB>", "<Down>", "<Up>", "<Enter>"
"<Tab>", "<Down>", "<Up>", "<Enter>", "<Left>", "<Right>",
// New line in vim, but QuickDoc on MacOs
"<C-J>"
)
for (keys in notAllowedKeys) {
if (keyStroke == keys[0]) {
@ -147,7 +149,7 @@ class VimShortcutKeyAction : AnAction(), DumbAware {
}
// We allow users to set custom keys that will work with lookup in case devs forgot something
val popupActions = lookupKeys
val values = popupActions.values() ?: return false
val values = popupActions.values()
for (value in values) {
val keys = StringHelper.parseKeys(value)
if (keys.size >= 1 && keyStroke == keys[0]) {

View File

@ -1,91 +0,0 @@
/*
* IdeaVim - Vim emulator for IDEs based on the IntelliJ platform
* Copyright (C) 2003-2019 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.command;
import org.jetbrains.annotations.NotNull;
import java.util.EnumSet;
/**
* @author vlan
*/
public enum SelectionType {
// Integer values for registers serialization in RegisterGroup.readData()
LINE_WISE(1 << 1),
CHARACTER_WISE(1 << 2),
BLOCK_WISE(1 << 3);
SelectionType(int value) {
this.value = value;
}
private final int value;
public int getValue() {
return value;
}
@NotNull
public static SelectionType fromValue(int value) {
for (SelectionType type : SelectionType.values()) {
if (type.getValue() == value) {
return type;
}
}
return CHARACTER_WISE;
}
@NotNull
public static SelectionType fromSubMode(@NotNull CommandState.SubMode subMode) {
switch (subMode) {
case VISUAL_LINE:
return LINE_WISE;
case VISUAL_BLOCK:
return BLOCK_WISE;
default:
return CHARACTER_WISE;
}
}
@NotNull
public CommandState.SubMode toSubMode() {
switch (this) {
case LINE_WISE:
return CommandState.SubMode.VISUAL_LINE;
case CHARACTER_WISE:
return CommandState.SubMode.VISUAL_CHARACTER;
case BLOCK_WISE:
return CommandState.SubMode.VISUAL_BLOCK;
default:
return CommandState.SubMode.VISUAL_CHARACTER;
}
}
public static SelectionType fromCommandFlags(EnumSet<CommandFlags> flags) {
if (flags.contains(CommandFlags.FLAG_MOT_LINEWISE)) {
return SelectionType.LINE_WISE;
}
else if (flags.contains(CommandFlags.FLAG_MOT_BLOCKWISE)) {
return SelectionType.BLOCK_WISE;
}
else {
return SelectionType.CHARACTER_WISE;
}
}
}

View File

@ -0,0 +1,63 @@
/*
* IdeaVim - Vim emulator for IDEs based on the IntelliJ platform
* Copyright (C) 2003-2019 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.command
import com.maddyhome.idea.vim.command.CommandState.SubMode
import java.util.*
/**
* @author vlan
*/
enum class SelectionType(val value: Int) {
// Integer values for registers serialization in RegisterGroup.readData()
LINE_WISE(1 shl 1),
CHARACTER_WISE(1 shl 2),
BLOCK_WISE(1 shl 3);
fun toSubMode() = when (this) {
LINE_WISE -> SubMode.VISUAL_LINE
CHARACTER_WISE -> SubMode.VISUAL_CHARACTER
BLOCK_WISE -> SubMode.VISUAL_BLOCK
}
companion object {
@JvmStatic
fun fromValue(value: Int): SelectionType {
for (type in values()) {
if (type.value == value) {
return type
}
}
return CHARACTER_WISE
}
@JvmStatic
fun fromSubMode(subMode: SubMode): SelectionType = when (subMode) {
SubMode.VISUAL_LINE -> LINE_WISE
SubMode.VISUAL_BLOCK -> BLOCK_WISE
else -> CHARACTER_WISE
}
@JvmStatic
fun fromCommandFlags(flags: EnumSet<CommandFlags>) = when {
CommandFlags.FLAG_MOT_LINEWISE in flags -> LINE_WISE
CommandFlags.FLAG_MOT_BLOCKWISE in flags -> BLOCK_WISE
else -> CHARACTER_WISE
}
}
}

View File

@ -1,76 +0,0 @@
package com.maddyhome.idea.vim.common;
import com.intellij.application.options.CodeStyle;
import com.intellij.openapi.actionSystem.DataContext;
import com.intellij.openapi.actionSystem.PlatformDataKeys;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.psi.codeStyle.CommonCodeStyleSettings;
public class IndentConfig {
private final int indentSize;
private final int tabSize;
private final boolean useTabs;
private IndentConfig(CommonCodeStyleSettings.IndentOptions indentOptions) {
this.indentSize = indentOptions.INDENT_SIZE;
this.tabSize = indentOptions.TAB_SIZE;
this.useTabs = indentOptions.USE_TAB_CHARACTER;
}
public static IndentConfig create(Editor editor) {
return create(editor, editor.getProject());
}
public static IndentConfig create(Editor editor, DataContext context) {
return create(editor, PlatformDataKeys.PROJECT.getData(context));
}
public static IndentConfig create(Editor editor, Project project) {
CommonCodeStyleSettings.IndentOptions indentOptions;
if(project != null) {
indentOptions = CodeStyle.getIndentOptions(project, editor.getDocument());
} else {
indentOptions = CodeStyle.getDefaultSettings().getIndentOptions();
}
return new IndentConfig(indentOptions);
}
public int getIndentSize() {
return indentSize;
}
public int getTabSize() {
return tabSize;
}
public boolean isUseTabs() {
return useTabs;
}
public int getTotalIndent(int count) {
return indentSize * count;
}
public String createIndentByCount(int count) {
return createIndentBySize(getTotalIndent(count));
}
public String createIndentBySize(int size) {
final int tabCount;
final int spaceCount;
if (useTabs) {
tabCount = size / tabSize;
spaceCount = size % tabSize;
}
else {
tabCount = 0;
spaceCount = size;
}
return StringUtil.repeat("\t", tabCount) + StringUtil.repeat(" ", spaceCount);
}
}

View File

@ -0,0 +1,51 @@
package com.maddyhome.idea.vim.common
import com.intellij.application.options.CodeStyle
import com.intellij.openapi.actionSystem.DataContext
import com.intellij.openapi.actionSystem.PlatformDataKeys
import com.intellij.openapi.editor.Editor
import com.intellij.openapi.project.Project
import com.intellij.psi.codeStyle.CommonCodeStyleSettings.IndentOptions
class IndentConfig private constructor(indentOptions: IndentOptions) {
private val indentSize = indentOptions.INDENT_SIZE
private val tabSize = indentOptions.TAB_SIZE
private val isUseTabs = indentOptions.USE_TAB_CHARACTER
fun getTotalIndent(count: Int): Int = indentSize * count
fun createIndentByCount(count: Int): String = createIndentBySize(getTotalIndent(count))
fun createIndentBySize(size: Int): String {
val tabCount: Int
val spaceCount: Int
if (isUseTabs) {
tabCount = size / tabSize
spaceCount = size % tabSize
} else {
tabCount = 0
spaceCount = size
}
return "\t".repeat(tabCount) + " ".repeat(spaceCount)
}
companion object {
@JvmStatic
fun create(editor: Editor, context: DataContext): IndentConfig {
return create(editor, PlatformDataKeys.PROJECT.getData(context))
}
@JvmStatic
@JvmOverloads
fun create(editor: Editor, project: Project? = editor.project): IndentConfig {
val indentOptions = if (project != null) {
CodeStyle.getIndentOptions(project, editor.document)
} else {
// [VERSION UPDATE] 191+
@Suppress("UNNECESSARY_NOT_NULL_ASSERTION")
CodeStyle.getDefaultSettings().indentOptions!!
}
return IndentConfig(indentOptions)
}
}
}

View File

@ -1,121 +0,0 @@
/*
* IdeaVim - Vim emulator for IDEs based on the IntelliJ platform
* Copyright (C) 2003-2019 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.common;
import com.intellij.codeInsight.editorActions.TextBlockTransferableData;
import com.maddyhome.idea.vim.command.SelectionType;
import com.maddyhome.idea.vim.helper.StringHelper;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.swing.*;
import java.awt.event.KeyEvent;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
/**
* Represents a register.
*/
public class Register {
private char name;
@NotNull private final SelectionType type;
@NotNull private final List<KeyStroke> keys;
@NotNull private List<? extends TextBlockTransferableData> transferableData = new ArrayList<>();
public Register(char name, @NotNull SelectionType type, @NotNull List<KeyStroke> keys) {
this.name = name;
this.type = type;
this.keys = keys;
}
public Register(char name, @NotNull SelectionType type, @NotNull String text, @NotNull List<? extends TextBlockTransferableData> transferableData) {
this.name = name;
this.type = type;
this.keys = StringHelper.stringToKeys(text);
this.transferableData = transferableData;
}
public void rename(char name) {
this.name = name;
}
/**
* Get the name the register is assigned to.
*/
public char getName() {
return name;
}
@NotNull
public List<? extends TextBlockTransferableData> getTransferableData() {
return transferableData;
}
/**
* Get the register type.
*/
@NotNull
public SelectionType getType() {
return type;
}
/**
* Get the text in the register.
*/
@Nullable
public String getText() {
final StringBuilder builder = new StringBuilder();
for (KeyStroke key : keys) {
final char c = key.getKeyChar();
if (c == KeyEvent.CHAR_UNDEFINED) {
return null;
}
builder.append(c);
}
return builder.toString();
}
/**
* Get the sequence of keys in the register.
*/
@NotNull
public List<KeyStroke> getKeys() {
return keys;
}
/**
* Append the supplied text to any existing text.
*/
public void addTextAndResetTransferableData(@NotNull String text) {
addKeys(StringHelper.stringToKeys(text));
transferableData.clear();
}
public void addKeys(@NotNull List<KeyStroke> keys) {
this.keys.addAll(keys);
}
public static class KeySorter implements Comparator<Register> {
@Override
public int compare(@NotNull Register o1, @NotNull Register o2) {
return Character.compare(o1.name, o2.name);
}
}
}

View File

@ -0,0 +1,75 @@
/*
* IdeaVim - Vim emulator for IDEs based on the IntelliJ platform
* Copyright (C) 2003-2019 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.common
import com.intellij.codeInsight.editorActions.TextBlockTransferableData
import com.maddyhome.idea.vim.command.SelectionType
import com.maddyhome.idea.vim.helper.StringHelper
import java.awt.event.KeyEvent
import java.util.*
import javax.swing.KeyStroke
class Register {
var name: Char
val type: SelectionType
val keys: MutableList<KeyStroke>
val transferableData: MutableList<out TextBlockTransferableData>
constructor(name: Char, type: SelectionType, keys: MutableList<KeyStroke>) {
this.name = name
this.type = type
this.keys = keys
this.transferableData = mutableListOf()
}
constructor(name: Char, type: SelectionType, text: String, transferableData: MutableList<out TextBlockTransferableData>) {
this.name = name
this.type = type
this.keys = StringHelper.stringToKeys(text)
this.transferableData = transferableData
}
val text: String?
get() {
val builder = StringBuilder()
for (key in keys) {
val c = key.keyChar
if (c == KeyEvent.CHAR_UNDEFINED) {
return null
}
builder.append(c)
}
return builder.toString()
}
/**
* Append the supplied text to any existing text.
*/
fun addTextAndResetTransferableData(text: String) {
addKeys(StringHelper.stringToKeys(text))
transferableData.clear()
}
fun addKeys(keys: List<KeyStroke>) {
this.keys.addAll(keys)
}
object KeySorter : Comparator<Register> {
override fun compare(o1: Register, o2: Register): Int = o1.name.compareTo(o2.name)
}
}

View File

@ -1,133 +0,0 @@
/*
* IdeaVim - Vim emulator for IDEs based on the IntelliJ platform
* Copyright (C) 2003-2019 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.common;
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.NotNull;
/**
* Please prefer {@link com.maddyhome.idea.vim.group.visual.VimSelection} for visual selection
*/
public class TextRange {
@Contract(pure = true)
public TextRange(int start, int end) {
this(new int[]{start}, new int[]{end});
}
@Contract(pure = true)
public TextRange(int[] starts, int[] ends) {
this.starts = starts;
this.ends = ends;
}
public boolean isMultiple() {
return starts != null && starts.length > 1;
}
public int getMaxLength() {
int max = 0;
for (int i = 0; i < size(); i++) {
max = Math.max(max, getEndOffsets()[i] - getStartOffsets()[i]);
}
return max;
}
public int getSelectionCount() {
int res = 0;
for (int i = 0; i < size(); i++) {
res += getEndOffsets()[i] - getStartOffsets()[i];
}
return res;
}
public int size() {
return starts.length;
}
public int getStartOffset() {
return starts[0];
}
public int getEndOffset() {
return ends[ends.length - 1];
}
public int[] getStartOffsets() {
return starts;
}
public int[] getEndOffsets() {
return ends;
}
@NotNull
public TextRange normalize() {
normalizeIndex(0);
return this;
}
private void normalizeIndex(final int index) {
if (index < size() && ends[index] < starts[index]) {
int t = starts[index];
starts[index] = ends[index];
ends[index] = t;
}
}
@Contract(mutates = "this")
public boolean normalize(final int fileSize) {
for (int i = 0; i < size(); i++) {
normalizeIndex(i);
starts[i] = Math.max(0, Math.min(starts[i], fileSize));
if (starts[i] == fileSize && fileSize != 0) {
return false;
}
ends[i] = Math.max(0, Math.min(ends[i], fileSize));
}
return true;
}
public boolean contains(final int offset) {
if (isMultiple()) {
return false;
}
return this.getStartOffset() <= offset && offset < this.getEndOffset();
}
@NotNull
public String toString() {
final StringBuilder sb = new StringBuilder();
sb.append("TextRange");
sb.append("{starts=").append(starts == null ? "null" : "");
for (int i = 0; starts != null && i < starts.length; ++i) {
sb.append(i == 0 ? "" : ", ").append(starts[i]);
}
sb.append(", ends=").append(ends == null ? "null" : "");
for (int i = 0; ends != null && i < ends.length; ++i) {
sb.append(i == 0 ? "" : ", ").append(ends[i]);
}
sb.append('}');
return sb.toString();
}
private final int[] starts;
private final int[] ends;
}

View File

@ -0,0 +1,107 @@
/*
* IdeaVim - Vim emulator for IDEs based on the IntelliJ platform
* Copyright (C) 2003-2019 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.common
import org.jetbrains.annotations.Contract
import kotlin.math.max
import kotlin.math.min
/**
* Please prefer [com.maddyhome.idea.vim.group.visual.VimSelection] for visual selection
*/
class TextRange(val startOffsets: IntArray, val endOffsets: IntArray) {
constructor(start: Int, end: Int) : this(intArrayOf(start), intArrayOf(end))
val isMultiple
get() = startOffsets.size > 1
val maxLength: Int
get() {
var max = 0
for (i in 0 until size()) {
max = max(max, endOffsets[i] - startOffsets[i])
}
return max
}
val selectionCount: Int
get() {
var res = 0
for (i in 0 until size()) {
res += endOffsets[i] - startOffsets[i]
}
return res
}
fun size(): Int = startOffsets.size
val startOffset: Int
get() = startOffsets.first()
val endOffset: Int
get() = endOffsets.last()
fun normalize(): TextRange {
normalizeIndex(0)
return this
}
private fun normalizeIndex(index: Int) {
if (index < size() && endOffsets[index] < startOffsets[index]) {
val t = startOffsets[index]
startOffsets[index] = endOffsets[index]
endOffsets[index] = t
}
}
@Contract(mutates = "this")
fun normalize(fileSize: Int): Boolean {
for (i in 0 until size()) {
normalizeIndex(i)
startOffsets[i] = max(0, min(startOffsets[i], fileSize))
if (startOffsets[i] == fileSize && fileSize != 0) {
return false
}
endOffsets[i] = max(0, min(endOffsets[i], fileSize))
}
return true
}
operator fun contains(offset: Int): Boolean = if (isMultiple) false else offset in startOffset until endOffset
override fun toString(): String {
val sb = StringBuilder()
sb.append("TextRange")
sb.append("{starts=")
var i = 0
while (i < startOffsets.size) {
sb.append(if (i == 0) "" else ", ").append(startOffsets[i])
++i
}
sb.append(", ends=")
i = 0
while (i < endOffsets.size) {
sb.append(if (i == 0) "" else ", ").append(endOffsets[i])
++i
}
sb.append('}')
return sb.toString()
}
}

View File

@ -41,7 +41,7 @@ import java.util.regex.Pattern;
public class VimScriptParser {
public static final String VIMRC_FILE_NAME = "ideavimrc";
public static final String[] HOME_VIMRC_PATHS = {"." + VIMRC_FILE_NAME, "_" + VIMRC_FILE_NAME};
public static final String XDG_VIMRC_PATH = "ideavim" + File.pathSeparator + VIMRC_FILE_NAME;
public static final String XDG_VIMRC_PATH = "ideavim" + File.separator + VIMRC_FILE_NAME;
public static final int BUFSIZE = 4096;
private static final Pattern EOL_SPLIT_PATTERN = Pattern.compile(" *(\r\n|\n)+ *");
private static final Pattern DOUBLE_QUOTED_STRING = Pattern.compile("\"([^\"]*)\"");
@ -55,7 +55,6 @@ public class VimScriptParser {
@Nullable
public static File findIdeaVimRc() {
final String homeDirName = System.getProperty("user.home");
// Check whether file exists in home dir
if (homeDirName != null) {
for (String fileName : HOME_VIMRC_PATHS) {

View File

@ -40,7 +40,7 @@ public class VimExtensionRegistrar {
registeredExtensions.add(name);
ToggleOption option = new ToggleOption(name, name, false);
option.addOptionChangeListener(event -> {
option.addOptionChangeListener((oldValue, newValue) -> {
for (VimExtension extensionInListener : VimExtension.EP_NAME.getExtensionList()) {
if (name.equals(extensionInListener.getName())) {
if (OptionsManager.INSTANCE.isSet(name)) {

View File

@ -32,7 +32,6 @@ import com.intellij.openapi.project.Project;
import com.maddyhome.idea.vim.KeyHandler;
import com.maddyhome.idea.vim.VimPlugin;
import com.maddyhome.idea.vim.helper.*;
import com.maddyhome.idea.vim.option.OptionChangeEvent;
import com.maddyhome.idea.vim.option.OptionChangeListener;
import com.maddyhome.idea.vim.option.OptionsManager;
import gnu.trove.TIntFunction;
@ -276,7 +275,7 @@ public class EditorGroup {
VimPlugin.getNotifications(project).notifyAboutIdeaJoin();
}
public static class NumberChangeListener implements OptionChangeListener {
public static class NumberChangeListener implements OptionChangeListener<Boolean> {
public static NumberChangeListener INSTANCE = new NumberChangeListener();
@Contract(pure = true)
@ -284,7 +283,7 @@ public class EditorGroup {
}
@Override
public void valueChange(OptionChangeEvent event) {
public void valueChange(Boolean oldValue, Boolean newValue) {
for (Editor editor : EditorFactory.getInstance().getAllEditors()) {
if (UserDataManager.getVimEditorGroup(editor) && supportsVimLineNumbers(editor)) {
updateLineNumbers(editor, true);

View File

@ -89,7 +89,7 @@ public class RegisterGroup {
public RegisterGroup() {
final ListOption clipboardOption = OptionsManager.INSTANCE.getClipboard();
clipboardOption.addOptionChangeListener(event -> {
clipboardOption.addOptionChangeListenerAndExecute((oldValue, newValue) -> {
if (clipboardOption.contains("unnamed")) {
defaultRegister = '*';
}
@ -221,7 +221,7 @@ public class RegisterGroup {
for (char d = '8'; d >= '1'; d--) {
Register t = registers.get(d);
if (t != null) {
t.rename((char)(d + 1));
t.setName((char)(d + 1));
registers.put((char)(d + 1), t);
}
}
@ -365,7 +365,7 @@ public class RegisterGroup {
res.add(register);
}
}
res.sort(new Register.KeySorter());
res.sort(Register.KeySorter.INSTANCE);
return res;
}

View File

@ -63,12 +63,12 @@ import java.util.*;
public class SearchGroup {
public SearchGroup() {
final OptionsManager options = OptionsManager.INSTANCE;
options.getHlsearch().addOptionChangeListener(event -> {
options.getHlsearch().addOptionChangeListener((oldValue, newValue) -> {
resetShowSearchHighlight();
forceUpdateSearchHighlights();
});
final OptionChangeListener updateHighlightsIfVisible = event -> {
final OptionChangeListener<Boolean> updateHighlightsIfVisible = (oldValue, newValue) -> {
if (showSearchHighlight) {
forceUpdateSearchHighlights();
}

View File

@ -2154,9 +2154,7 @@ public class SearchHelper {
private static String getPairChars() {
if (pairsChars == null) {
ListOption lo = OptionsManager.INSTANCE.getMatchpairs();
pairsChars = parseOption(lo);
lo.addOptionChangeListener(event -> pairsChars = parseOption((ListOption)event.getOption()));
lo.addOptionChangeListenerAndExecute((oldValue, newValue) -> pairsChars = parseOption(lo));
}
return pairsChars;
@ -2165,9 +2163,6 @@ public class SearchHelper {
@NotNull
private static String parseOption(@NotNull ListOption option) {
List<String> vals = option.values();
if (vals == null) {
return "";
}
StringBuilder res = new StringBuilder();
for (String s : vals) {
if (s.length() == 3) {

View File

@ -44,19 +44,21 @@ public final class KeywordOption extends ListOption {
@Override
public boolean append(@NotNull String val) {
String oldValue = getValue();
final List<String> vals = parseVals(val);
final List<KeywordSpec> specs = valsToValidatedAndReversedSpecs(vals);
if (vals == null || specs == null) {
return false;
}
value.addAll(vals);
this.value.addAll(vals);
keywordSpecs.addAll(0, specs);
fireOptionChangeEvent();
fireOptionChangeEvent(oldValue, getValue());
return true;
}
@Override
public boolean prepend(@NotNull String val) {
String oldValue = getValue();
final List<String> vals = parseVals(val);
final List<KeywordSpec> specs = valsToValidatedAndReversedSpecs(vals);
if (vals == null || specs == null) {
@ -64,13 +66,14 @@ public final class KeywordOption extends ListOption {
}
value.addAll(0, vals);
keywordSpecs.addAll(specs);
fireOptionChangeEvent();
fireOptionChangeEvent(oldValue, getValue());
return true;
}
@Override
public boolean remove(@NotNull String val) {
String oldValue = getValue();
final List<String> vals = parseVals(val);
final List<KeywordSpec> specs = valsToValidatedAndReversedSpecs(vals);
if (vals == null || specs == null) {
@ -78,20 +81,22 @@ public final class KeywordOption extends ListOption {
}
value.removeAll(vals);
keywordSpecs.removeAll(specs);
fireOptionChangeEvent();
fireOptionChangeEvent(oldValue, getValue());
return true;
}
private void initialSet(String[] values) {
final List<String> vals = Arrays.asList(values);
String oldValue = getValue();
final List<String> vals = new ArrayList<>(Arrays.asList(values));
final List<KeywordSpec> specs = valsToReversedSpecs(vals);
value = vals;
keywordSpecs = specs;
fireOptionChangeEvent();
fireOptionChangeEvent(oldValue, getValue());
}
@Override
public boolean set(@NotNull String val) {
String oldValue = getValue();
final List<String> vals = parseVals(val);
final List<KeywordSpec> specs = valsToValidatedAndReversedSpecs(vals);
if (vals == null || specs == null) {
@ -99,7 +104,7 @@ public final class KeywordOption extends ListOption {
}
value = vals;
keywordSpecs = specs;
fireOptionChangeEvent();
fireOptionChangeEvent(oldValue, getValue());
return true;
}

View File

@ -128,8 +128,9 @@ public class ListOption extends TextOption {
return false;
}
value = vals;
fireOptionChangeEvent();
String oldValue = getValue();
this.value = vals;
fireOptionChangeEvent(oldValue, getValue());
return true;
}
@ -139,8 +140,9 @@ public class ListOption extends TextOption {
return false;
}
String oldValue = getValue();
value.addAll(vals);
fireOptionChangeEvent();
fireOptionChangeEvent(oldValue, getValue());
return true;
}
@ -150,8 +152,9 @@ public class ListOption extends TextOption {
return false;
}
String oldValue = getValue();
value.addAll(0, vals);
fireOptionChangeEvent();
fireOptionChangeEvent(oldValue, getValue());
return true;
}
@ -161,8 +164,9 @@ public class ListOption extends TextOption {
return false;
}
String oldValue = getValue();
value.removeAll(vals);
fireOptionChangeEvent();
fireOptionChangeEvent(oldValue, getValue());
return true;
}
@ -230,8 +234,9 @@ public class ListOption extends TextOption {
@Override
public void resetDefault() {
if (!dflt.equals(value)) {
String oldValue = getValue();
value = new ArrayList<>(dflt);
fireOptionChangeEvent();
fireOptionChangeEvent(oldValue, getValue());
}
}
}

View File

@ -99,8 +99,10 @@ public class NumberOption extends TextOption {
}
if (inRange(num)) {
value = num;
fireOptionChangeEvent();
String oldValue = getValue();
this.value = num;
fireOptionChangeEvent(oldValue, getValue());
return true;
}
@ -125,8 +127,9 @@ public class NumberOption extends TextOption {
}
if (inRange(value + num)) {
String oldValue = getValue();
value += num;
fireOptionChangeEvent();
fireOptionChangeEvent(oldValue, getValue());
return true;
}
@ -151,8 +154,9 @@ public class NumberOption extends TextOption {
}
if (inRange(value * num)) {
String oldValue = getValue();
value *= num;
fireOptionChangeEvent();
fireOptionChangeEvent(oldValue, getValue());
return true;
}
@ -177,8 +181,9 @@ public class NumberOption extends TextOption {
}
if (inRange(value - num)) {
String oldValue = getValue();
value -= num;
fireOptionChangeEvent();
fireOptionChangeEvent(oldValue, getValue());
return true;
}
@ -202,8 +207,9 @@ public class NumberOption extends TextOption {
@Override
public void resetDefault() {
if (dflt != value) {
String oldValue = getValue();
value = dflt;
fireOptionChangeEvent();
fireOptionChangeEvent(oldValue, getValue());
}
}

View File

@ -21,14 +21,13 @@ package com.maddyhome.idea.vim.option;
import org.jetbrains.annotations.NotNull;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
/**
* Represents an VIM options that can be set with the :set command. Listeners can be set that are interested in knowing
* when the value of the option changes.
*/
public abstract class Option {
public abstract class Option<T> {
/**
* Create the option
*
@ -46,16 +45,27 @@ public abstract class Option {
*
* @param listener The listener
*/
public void addOptionChangeListener(OptionChangeListener listener) {
public void addOptionChangeListener(OptionChangeListener<T> listener) {
listeners.add(listener);
}
/**
* Registers an option change listener and fire an event.
*
* @param listener The listener
*/
public void addOptionChangeListenerAndExecute(OptionChangeListener<T> listener) {
addOptionChangeListener(listener);
T value = getValue();
fireOptionChangeEvent(value, value);
}
/**
* Removes the listener from the list.
*
* @param listener The listener
*/
public void removeOptionChangeListener(OptionChangeListener listener) {
public void removeOptionChangeListener(OptionChangeListener<T> listener) {
listeners.remove(listener);
}
@ -93,24 +103,15 @@ public abstract class Option {
* Lets all listeners know that the value has changed. Subclasses are responsible for calling this when their
* value changes.
*/
protected void fireOptionChangeEvent() {
OptionChangeEvent event = new OptionChangeEvent(this);
for (OptionChangeListener listener : listeners) {
listener.valueChange(event);
protected void fireOptionChangeEvent(T oldValue, T newValue) {
for (OptionChangeListener<T> listener : listeners) {
listener.valueChange(oldValue, newValue);
}
}
/**
* Helper method used to sort lists of options by their name
*/
static class NameSorter<V> implements Comparator<V> {
@Override
public int compare(@NotNull V o1, @NotNull V o2) {
return ((Option)o1).name.compareTo(((Option)o2).name);
}
}
public abstract T getValue();
protected final String name;
protected final String abbrev;
@NotNull protected final List<OptionChangeListener> listeners = new ArrayList<>();
@NotNull private final List<OptionChangeListener<T>> listeners = new ArrayList<>();
}

View File

@ -1,42 +0,0 @@
/*
* IdeaVim - Vim emulator for IDEs based on the IntelliJ platform
* Copyright (C) 2003-2019 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.option;
import org.jetbrains.annotations.NotNull;
import java.util.EventObject;
/**
* This event indicates that the value of the option has changed
*/
public class OptionChangeEvent extends EventObject {
public OptionChangeEvent(Option option) {
super(option);
}
/**
* Gets the changed option.
*
* @return The changed option
*/
@NotNull
public Option getOption() {
return (Option)getSource();
}
}

View File

@ -18,16 +18,9 @@
package com.maddyhome.idea.vim.option;
import java.util.EventListener;
/**
* This interface is used for classes that wish to be notified whenever the value of an option has changed
*/
public interface OptionChangeListener extends EventListener {
/**
* The value of the option has changed.
*
* @param event The change event
*/
void valueChange(OptionChangeEvent event);
public interface OptionChangeListener<T> {
void valueChange(T oldValue, T newValue);
}

View File

@ -34,6 +34,7 @@ import com.maddyhome.idea.vim.helper.Msg
import com.maddyhome.idea.vim.helper.hasVisualSelection
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.Contract
import java.util.*
@ -44,8 +45,8 @@ import kotlin.math.min
object OptionsManager {
private val logger = Logger.getInstance(OptionsManager::class.java)
private val options: MutableMap<String, Option> = mutableMapOf()
private val abbrevs: MutableMap<String, Option> = mutableMapOf()
private val options: MutableMap<String, Option<*>> = mutableMapOf()
private val abbrevs: MutableMap<String, Option<*>> = mutableMapOf()
val clipboard = addOption(ListOption(ClipboardOptionsData.name, ClipboardOptionsData.abbr, arrayOf(ClipboardOptionsData.ideaput, "autoselect,exclude:cons\\|linux"), null))
val digraph = addOption(ToggleOption("digraph", "dg", false))
@ -95,7 +96,7 @@ object OptionsManager {
/**
* Gets an option by the supplied name or short name.
*/
fun getOption(name: String): Option? = options[name] ?: abbrevs[name]
fun getOption(name: String): Option<*>? = options[name] ?: abbrevs[name]
/**
* This parses a set of :set commands. The following types of commands are supported:
@ -144,7 +145,7 @@ object OptionsManager {
var error: String? = null
var token = ""
val tokenizer = StringTokenizer(args)
val toShow = mutableListOf<Option>()
val toShow = mutableListOf<Option<*>>()
while (tokenizer.hasMoreTokens()) {
token = tokenizer.nextToken()
// See if a space has been backslashed, if no get the rest of the text
@ -290,11 +291,11 @@ object OptionsManager {
* @param opts The list of options to display
* @param showIntro True if intro is displayed, false if not
*/
private fun showOptions(editor: Editor?, opts: Collection<Option>, showIntro: Boolean) {
private fun showOptions(editor: Editor?, opts: Collection<Option<*>>, showIntro: Boolean) {
if (editor == null) return
val cols = mutableListOf<Option>()
val extra = mutableListOf<Option>()
val cols = mutableListOf<Option<*>>()
val extra = mutableListOf<Option<*>>()
for (option in opts) {
if (option.toString().length > 19) extra.add(option) else cols.add(option)
}
@ -347,7 +348,7 @@ object OptionsManager {
}
@Contract("_ -> param1")
fun <T : Option> addOption(option: T): T {
fun <T : Option<*>> addOption(option: T): T {
options += option.name to option
abbrevs += option.abbrev to option
return option
@ -468,6 +469,13 @@ object IdeaRefactorMode {
editor.selectionModel.removeSelection()
}
}
if (editor.mode.hasVisualSelection && editor.selectionModel.hasSelection()) {
val autodetectedSubmode = VimPlugin.getVisualMotion().autodetectVisualSubmode(editor)
if (editor.subMode != autodetectedSubmode) {
// Update the submode
editor.subMode = autodetectedSubmode
}
}
if (editor.mode.isBlockCaret) {
TemplateManagerImpl.getTemplateState(editor)?.currentVariableRange?.let { segmentRange ->

View File

@ -55,8 +55,9 @@ public class StringOption extends TextOption {
*/
@Override
public boolean set(String val) {
String oldValue = getValue();
value = val;
fireOptionChangeEvent();
fireOptionChangeEvent(oldValue, getValue());
return true;
}
@ -69,8 +70,9 @@ public class StringOption extends TextOption {
*/
@Override
public boolean append(String val) {
String oldValue = getValue();
value += val;
fireOptionChangeEvent();
fireOptionChangeEvent(oldValue, getValue());
return true;
}
@ -83,8 +85,9 @@ public class StringOption extends TextOption {
*/
@Override
public boolean prepend(String val) {
String oldValue = getValue();
value = val + value;
fireOptionChangeEvent();
fireOptionChangeEvent(oldValue, getValue());
return true;
}
@ -99,8 +102,9 @@ public class StringOption extends TextOption {
public boolean remove(@NotNull String val) {
int pos = value.indexOf(val);
if (pos != -1) {
String oldValue = getValue();
value = value.substring(0, pos) + value.substring(pos + val.length());
fireOptionChangeEvent();
fireOptionChangeEvent(oldValue, getValue());
return true;
}
@ -124,8 +128,9 @@ public class StringOption extends TextOption {
@Override
public void resetDefault() {
if (!dflt.equals(value)) {
String oldValue = getValue();
value = dflt;
fireOptionChangeEvent();
fireOptionChangeEvent(oldValue, getValue());
}
}

View File

@ -18,13 +18,11 @@
package com.maddyhome.idea.vim.option;
public abstract class TextOption extends Option {
public abstract class TextOption extends Option<String> {
TextOption(String name, String abbrev) {
super(name, abbrev);
}
public abstract String getValue();
public abstract boolean set(String val);
public abstract boolean append(String val);

View File

@ -23,7 +23,7 @@ import org.jetbrains.annotations.NotNull;
/**
* Represents a boolean option
*/
public class ToggleOption extends Option {
public class ToggleOption extends Option<Boolean> {
/**
* Creates the option
*
@ -38,12 +38,8 @@ public class ToggleOption extends Option {
this.value = dflt;
}
/**
* The option's value
*
* @return The value
*/
public boolean getValue() {
@Override
public Boolean getValue() {
return value;
}
@ -81,7 +77,7 @@ public class ToggleOption extends Option {
boolean old = value;
value = val;
if (val != old) {
fireOptionChangeEvent();
fireOptionChangeEvent(old, val);
}
}

View File

@ -24,6 +24,7 @@ import com.maddyhome.idea.vim.command.CommandState
import com.maddyhome.idea.vim.group.visual.IdeaSelectionControl
import com.maddyhome.idea.vim.helper.StringHelper.parseKeys
import com.maddyhome.idea.vim.helper.VimBehaviorDiffers
import com.maddyhome.idea.vim.helper.subMode
import com.maddyhome.idea.vim.listener.VimListenerManager
import com.maddyhome.idea.vim.option.SelectModeOptionData
import org.jetbrains.plugins.ideavim.VimOptionDefaultAll
@ -31,6 +32,7 @@ import org.jetbrains.plugins.ideavim.VimOptionTestCase
import org.jetbrains.plugins.ideavim.VimOptionTestConfiguration
import org.jetbrains.plugins.ideavim.VimTestOption
import org.jetbrains.plugins.ideavim.VimTestOptionType
import org.jetbrains.plugins.ideavim.waitAndAssert
import org.jetbrains.plugins.ideavim.waitAndAssertMode
/**
@ -649,4 +651,61 @@ class IdeaVisualControlTest : VimOptionTestCase(SelectModeOptionData.name) {
waitAndAssertMode(myFixture, CommandState.Mode.VISUAL)
}
@VimOptionTestConfiguration(VimTestOption(SelectModeOptionData.name, VimTestOptionType.LIST, [""]))
fun `test control selection from line to char visual modes`() {
configureByText("""
A Discovery
I ${c}found it in a legendary land
all rocks and lavender and tufted grass,
""".trimIndent())
typeText(parseKeys("V"))
assertMode(CommandState.Mode.VISUAL)
assertSubMode(CommandState.SubMode.VISUAL_LINE)
myFixture.editor.selectionModel.setSelection(2, 5)
IdeaSelectionControl.controlNonVimSelectionChange(myFixture.editor)
waitAndAssert { myFixture.editor.subMode == CommandState.SubMode.VISUAL_CHARACTER }
assertMode(CommandState.Mode.VISUAL)
assertSubMode(CommandState.SubMode.VISUAL_CHARACTER)
assertCaretsColour()
}
/*
// [VERSION UPDATE] 191+ Open this test
@VimOptionTestConfiguration(VimTestOption(SelectModeOptionData.name, VimTestOptionType.LIST, [""]))
fun `test control selection from line to char visual modes in keep mode`() {
configureByText("""
A Discovery
I ${c}found it in a legendary land
all rocks and lavender and tufted grass,
""".trimIndent())
startDummyTemplate()
typeText(parseKeys("V"))
assertMode(CommandState.Mode.VISUAL)
assertSubMode(CommandState.SubMode.VISUAL_LINE)
myFixture.editor.selectionModel.setSelection(2, 5)
IdeaSelectionControl.controlNonVimSelectionChange(myFixture.editor)
waitAndAssert { myFixture.editor.subMode == CommandState.SubMode.VISUAL_CHARACTER }
assertMode(CommandState.Mode.VISUAL)
assertSubMode(CommandState.SubMode.VISUAL_CHARACTER)
assertCaretsColour()
}
private fun startDummyTemplate() {
TemplateManagerImpl.setTemplateTesting(myFixture.testRootDisposable)
val templateManager = TemplateManager.getInstance(myFixture.project)
val createdTemplate = templateManager.createTemplate("", "")
createdTemplate.addVariable(ConstantNode("1"), true)
templateManager.startTemplate(myFixture.editor, createdTemplate)
}
*/
}

View File

@ -56,6 +56,11 @@ public class KeywordOptionTest extends VimTestCase {
assertEquals(",", option.values().get(0));
}
public void testSingleCommaIsAValueAsAppend() throws ExException {
option.append(",");
assertTrue(option.values().contains(","));
}
public void testSingleNegatedCommaIsAValue() throws ExException {
option.set("^,");
assertEquals("^,", option.values().get(0));