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:
commit
ebaeff9b4d
CHANGES.md
resources/META-INF
src/com/maddyhome/idea/vim
action
command
common
ex/vimscript
extension
group
helper
option
test/org/jetbrains/plugins/ideavim
17
CHANGES.md
17
CHANGES.md
@ -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
|
||||
--------------
|
||||
|
@ -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 -->
|
||||
|
@ -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]) {
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
63
src/com/maddyhome/idea/vim/command/SelectionType.kt
Normal file
63
src/com/maddyhome/idea/vim/command/SelectionType.kt
Normal 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
|
||||
}
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
51
src/com/maddyhome/idea/vim/common/IndentConfig.kt
Normal file
51
src/com/maddyhome/idea/vim/common/IndentConfig.kt
Normal 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)
|
||||
}
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
75
src/com/maddyhome/idea/vim/common/Register.kt
Normal file
75
src/com/maddyhome/idea/vim/common/Register.kt
Normal 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)
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
107
src/com/maddyhome/idea/vim/common/TextRange.kt
Normal file
107
src/com/maddyhome/idea/vim/common/TextRange.kt
Normal 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()
|
||||
}
|
||||
}
|
@ -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) {
|
||||
|
@ -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)) {
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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) {
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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<>();
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
|
@ -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 ->
|
||||
|
@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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)
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
@ -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));
|
||||
|
Loading…
Reference in New Issue
Block a user