mirror of
https://github.com/chylex/IntelliJ-IdeaVim.git
synced 2025-08-17 16:31:45 +02:00
Compare commits
11 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
6a90f95712 | ||
![]() |
910c51004d | ||
![]() |
bdb05d1dee | ||
![]() |
abc68eb900 | ||
![]() |
5a50c9e5ac | ||
![]() |
1d15417a83 | ||
![]() |
233d318e48 | ||
![]() |
a0c52f017c | ||
![]() |
f89d824367 | ||
![]() |
d5daf5de67 | ||
![]() |
49459b2d81 |
11
CHANGES.md
11
CHANGES.md
@@ -3,6 +3,17 @@ The Changelog
|
||||
|
||||
History of changes in IdeaVim for the IntelliJ platform.
|
||||
|
||||
|
||||
0.25, 2012-12-19
|
||||
------------------
|
||||
|
||||
A bugfix release.
|
||||
|
||||
* VIM-400 Fixed saving characters with key modifiers in plugin settings
|
||||
* VIM-319 Fixed saving plugin settings when registers contain the null
|
||||
character
|
||||
|
||||
|
||||
0.24, 2012-12-03
|
||||
----------------
|
||||
|
||||
|
32
README.md
32
README.md
@@ -9,6 +9,7 @@ Resources:
|
||||
|
||||
* [Plugin homepage](http://plugins.intellij.net/plugin/?id=164)
|
||||
* [Changelog](https://github.com/JetBrains/ideavim/blob/master/CHANGES.md)
|
||||
* [Bug tracker](http://youtrack.jetbrains.com/issues/VIM)
|
||||
* [Continuous integration builds](http://teamcity.jetbrains.com/project.html?projectId=project55)
|
||||
|
||||
|
||||
@@ -127,6 +128,37 @@ keyboard shortcuts, and their new VIM keystrokes.
|
||||
Check In Project Ctrl-K <None>
|
||||
|
||||
|
||||
Development
|
||||
-----------
|
||||
|
||||
### Development Environment
|
||||
|
||||
1. Fork IdeaVim on GitHub and clone the repository on your local machine.
|
||||
|
||||
2. Open the project in IntelliJ IDEA 11+ (Community or Ultimate) using "File |
|
||||
Open... | /path/to/ideavim".
|
||||
|
||||
3. Set up a JDK if you haven't got it yet. Use "File | Project Structure | SDKs
|
||||
| Add new JDK".
|
||||
|
||||
4. Set up an IntelliJ plugin SDK using "File | Project Structure | SDKs | Add
|
||||
new IntelliJ IDEA Plugin SDK". The correct path to your current installation
|
||||
of IntelliJ will be suggested automatically. You will be prompted to select a
|
||||
JDK for your plugin SDK. Select the JDK from the previous step. You
|
||||
**should** name your plugin SDK `IntelliJ Plugin SDK` in order to match the
|
||||
name in the project settings stored in the Git repository.
|
||||
|
||||
5. Select a project SDK for your project using "File | Project Structure |
|
||||
Project | Project SDK". Choose the plugin SDK you have created at the
|
||||
previous step.
|
||||
|
||||
6. Build IdeaVim and run IntelliJ with IdeaVim enabled using the "IdeaVim" run
|
||||
configuration (use "Run | Run... | IdeaVim").
|
||||
|
||||
7. In order to be able to run tests in your IntelliJ edition uncomment the
|
||||
appropriate lines in the constructor of the `VimTestCase` class.
|
||||
|
||||
|
||||
Authors
|
||||
-------
|
||||
|
||||
|
@@ -1,4 +1,4 @@
|
||||
version-id:0.24
|
||||
version-id:0.25
|
||||
platform-version:110.0
|
||||
idea.download.url=http://download.jetbrains.com/idea/ideaIU-11.zip
|
||||
idea.download.url=http://download.jetbrains.com/idea/ideaIU-12.0.zip
|
||||
build.number=x
|
||||
|
@@ -27,7 +27,7 @@
|
||||
<property name="idea.home" value="${idea}/unzip"/>
|
||||
<property environment="env"/>
|
||||
<property name="tools.jar" value="${env.JAVA_HOME}/lib/tools.jar"/>
|
||||
<property name="version" value="${version-id}-${build.number}"/>
|
||||
<property name="version" value="${version-id}"/>
|
||||
<property name="filename" value="ideavim-${version}"/>
|
||||
|
||||
<!--Output-->
|
||||
|
@@ -22,8 +22,10 @@ package com.maddyhome.idea.vim.common;
|
||||
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.Comparator;
|
||||
import java.util.List;
|
||||
|
||||
@@ -31,24 +33,19 @@ import java.util.List;
|
||||
* Represents a register.
|
||||
*/
|
||||
public class Register {
|
||||
/**
|
||||
* Create a register of the specified type for the given text
|
||||
*
|
||||
* @param name The character
|
||||
* @param type The register type
|
||||
* @param text The text to store
|
||||
*/
|
||||
public Register(char name, @NotNull SelectionType type, String text) {
|
||||
private char name;
|
||||
@NotNull private SelectionType type;
|
||||
@NotNull private List<KeyStroke> keys;
|
||||
|
||||
public Register(char name, @NotNull SelectionType type, @NotNull String text) {
|
||||
this.name = name;
|
||||
this.type = type;
|
||||
this.text = text;
|
||||
this.keys = null;
|
||||
this.keys = StringHelper.stringToKeys(text);
|
||||
}
|
||||
|
||||
public Register(char name, @NotNull SelectionType type, List<KeyStroke> keys) {
|
||||
public Register(char name, @NotNull SelectionType type, @NotNull List<KeyStroke> keys) {
|
||||
this.name = name;
|
||||
this.type = type;
|
||||
this.text = null;
|
||||
this.keys = keys;
|
||||
}
|
||||
|
||||
@@ -57,18 +54,14 @@ public class Register {
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the name the register is assigned to
|
||||
*
|
||||
* @return The register name
|
||||
* Get the name the register is assigned to.
|
||||
*/
|
||||
public char getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the register type
|
||||
*
|
||||
* @return The register type
|
||||
* Get the register type.
|
||||
*/
|
||||
@NotNull
|
||||
public SelectionType getType() {
|
||||
@@ -76,65 +69,38 @@ public class Register {
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the text in the register
|
||||
*
|
||||
* @return The register text
|
||||
* Get the text in the register.
|
||||
*/
|
||||
@Nullable
|
||||
public String getText() {
|
||||
if (text == null && keys != null) {
|
||||
return StringHelper.keysToString(keys);
|
||||
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 text;
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the sequence of keys in the register
|
||||
*
|
||||
* @return The register keys
|
||||
* Get the sequence of keys in the register.
|
||||
*/
|
||||
@NotNull
|
||||
public List<KeyStroke> getKeys() {
|
||||
if (keys == null && text != null) {
|
||||
return StringHelper.stringToKeys(text);
|
||||
}
|
||||
|
||||
return keys;
|
||||
}
|
||||
|
||||
/**
|
||||
* Appends the supplied text to any existing text
|
||||
*
|
||||
* @param text The text to add
|
||||
* Append the supplied text to any existing text.
|
||||
*/
|
||||
public void addText(String text) {
|
||||
if (this.text != null) {
|
||||
this.text = this.text + text;
|
||||
}
|
||||
else if (this.keys != null) {
|
||||
addKeys(StringHelper.stringToKeys(text));
|
||||
}
|
||||
else {
|
||||
this.text = text;
|
||||
}
|
||||
public void addText(@NotNull String text) {
|
||||
addKeys(StringHelper.stringToKeys(text));
|
||||
}
|
||||
|
||||
public void addKeys(List<KeyStroke> keys) {
|
||||
if (this.keys != null) {
|
||||
this.keys.addAll(keys);
|
||||
}
|
||||
else if (this.text != null) {
|
||||
this.text = this.text + StringHelper.keysToString(keys);
|
||||
}
|
||||
else {
|
||||
this.keys = keys;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isText() {
|
||||
return text != null;
|
||||
}
|
||||
|
||||
public boolean isKeys() {
|
||||
return keys != null;
|
||||
public void addKeys(@NotNull List<KeyStroke> keys) {
|
||||
this.keys.addAll(keys);
|
||||
}
|
||||
|
||||
public static class KeySorter<V> implements Comparator<V> {
|
||||
@@ -152,9 +118,4 @@ public class Register {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private char name;
|
||||
@NotNull private SelectionType type;
|
||||
private String text;
|
||||
private List<KeyStroke> keys;
|
||||
}
|
||||
|
@@ -28,6 +28,7 @@ import com.maddyhome.idea.vim.group.CommandGroups;
|
||||
import com.maddyhome.idea.vim.group.HistoryGroup;
|
||||
import com.maddyhome.idea.vim.helper.MessageHelper;
|
||||
import com.maddyhome.idea.vim.helper.Msg;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
* Maintains a tree of Ex commands based on the required and optional parts of the command names. Parses and
|
||||
@@ -124,14 +125,15 @@ public class CommandParser {
|
||||
* @throws ExException if any part of the command was invalid
|
||||
*/
|
||||
public boolean processLastCommand(Editor editor, DataContext context, int count) throws ExException {
|
||||
Register reg = CommandGroups.getInstance().getRegister().getRegister(':');
|
||||
if (reg == null) {
|
||||
return false;
|
||||
final Register reg = CommandGroups.getInstance().getRegister().getRegister(':');
|
||||
if (reg != null) {
|
||||
final String text = reg.getText();
|
||||
if (text != null) {
|
||||
processCommand(editor, context, text, count);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
processCommand(editor, context, reg.getText(), count);
|
||||
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -144,7 +146,7 @@ public class CommandParser {
|
||||
* @return A bitwise collection of flags, if any, from the result of running the command.
|
||||
* @throws ExException if any part of the command is invalid or unknown
|
||||
*/
|
||||
public int processCommand(Editor editor, DataContext context, String cmd, int count) throws ExException {
|
||||
public int processCommand(Editor editor, DataContext context, @NotNull String cmd, int count) throws ExException {
|
||||
// Nothing entered
|
||||
int result = 0;
|
||||
if (cmd.length() == 0) {
|
||||
@@ -193,7 +195,7 @@ public class CommandParser {
|
||||
boolean ok = handler.process(editor, context, new ExCommand(res.getRanges(), command, res.getArgument()), count);
|
||||
if (ok && (handler.getArgFlags() & CommandHandler.DONT_SAVE_LAST) == 0) {
|
||||
CommandGroups.getInstance().getRegister().storeTextInternal(editor, new TextRange(-1, -1), cmd,
|
||||
SelectionType.CHARACTER_WISE, ':', false, false);
|
||||
SelectionType.CHARACTER_WISE, ':', false);
|
||||
}
|
||||
|
||||
if (ok && (handler.getArgFlags() & CommandHandler.KEEP_FOCUS) != 0) {
|
||||
|
@@ -53,15 +53,13 @@ public class RegistersHandler extends CommandHandler {
|
||||
text.append(reg.getName());
|
||||
|
||||
text.append(" ");
|
||||
text.append(StringHelper.escape(reg.getText()));
|
||||
text.append(StringHelper.escape(reg.getKeys()));
|
||||
text.append("\n");
|
||||
}
|
||||
|
||||
MorePanel panel = MorePanel.getInstance(editor);
|
||||
panel.setText(text.toString());
|
||||
|
||||
//panel.setVisible(true);
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
@@ -20,26 +20,25 @@ package com.maddyhome.idea.vim.group;
|
||||
*/
|
||||
|
||||
import org.jdom.Element;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
* This base class provides empty implemtations for the interface methods.
|
||||
* This base class provides empty implementations for the interface methods.
|
||||
*/
|
||||
public abstract class AbstractActionGroup implements ActionGroup {
|
||||
/**
|
||||
* Allows the group to save its state and any configuration. This does nothing.
|
||||
* Allows the group to save its state and any configuration.
|
||||
*
|
||||
* @param element The plugin's root XML element that this group can add a child to
|
||||
* @param element The root XML element of the plugin that this group can add a child to
|
||||
*/
|
||||
public void saveData(Element element) {
|
||||
// no-op
|
||||
public void saveData(@NotNull Element element) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows the group to restore its state and any configuration. This does nothing.
|
||||
* Allows the group to restore its state and any configuration.
|
||||
*
|
||||
* @param element The plugin's root XML element that this group can add a child to
|
||||
* @param element The root XML element of the plugin that this group can add a child to
|
||||
*/
|
||||
public void readData(Element element) {
|
||||
// no-op
|
||||
public void readData(@NotNull Element element) {
|
||||
}
|
||||
}
|
||||
|
@@ -20,15 +20,16 @@ package com.maddyhome.idea.vim.group;
|
||||
*/
|
||||
|
||||
import org.jdom.Element;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
* This is the base for all key mapping groups
|
||||
* The base for all key mapping groups.
|
||||
*/
|
||||
public interface ActionGroup {
|
||||
/**
|
||||
* Allows the group to save its state and any configuration.
|
||||
*
|
||||
* @param element The plugin's root XML element that this group can add a child to
|
||||
* @param element The root XML element of the plugin that this group can add a child to
|
||||
*/
|
||||
void saveData(Element element);
|
||||
void saveData(@NotNull Element element);
|
||||
}
|
||||
|
@@ -1428,7 +1428,7 @@ public class ChangeGroup extends AbstractActionGroup {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (type == null || CommandGroups.getInstance().getRegister().storeText(editor, context, range, type, true, false)) {
|
||||
if (type == null || CommandGroups.getInstance().getRegister().storeText(editor, range, type, true)) {
|
||||
final Document document = editor.getDocument();
|
||||
final int[] startOffsets = range.getStartOffsets();
|
||||
final int[] endOffsets = range.getEndOffsets();
|
||||
|
@@ -21,6 +21,7 @@ package com.maddyhome.idea.vim.group;
|
||||
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import org.jdom.Element;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
* This singleton maintains the instances of all the key groups. All the key/action mappings get created the first
|
||||
@@ -149,9 +150,9 @@ public class CommandGroups {
|
||||
/**
|
||||
* Tells each group to save its data.
|
||||
*
|
||||
* @param element The plugin's root element
|
||||
* @param element The root XML element of the plugin
|
||||
*/
|
||||
public void saveData(Element element) {
|
||||
public void saveData(@NotNull Element element) {
|
||||
motion.saveData(element);
|
||||
change.saveData(element);
|
||||
copy.saveData(element);
|
||||
@@ -168,9 +169,9 @@ public class CommandGroups {
|
||||
/**
|
||||
* Tells each group to read its data.
|
||||
*
|
||||
* @param element The plugin's root element
|
||||
* @param element The root XML element of the plugin
|
||||
*/
|
||||
public void readData(Element element) {
|
||||
public void readData(@NotNull Element element) {
|
||||
logger.debug("readData");
|
||||
motion.readData(element);
|
||||
change.readData(element);
|
||||
|
@@ -94,7 +94,7 @@ public class CopyGroup extends AbstractActionGroup {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("yanking range: " + range);
|
||||
}
|
||||
boolean res = CommandGroups.getInstance().getRegister().storeText(editor, context, range, type, false, true);
|
||||
boolean res = CommandGroups.getInstance().getRegister().storeText(editor, range, type, false);
|
||||
if (moveCursor) {
|
||||
MotionGroup.moveCaret(editor, range.normalize().getStartOffset());
|
||||
}
|
||||
|
@@ -23,8 +23,8 @@ import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.maddyhome.idea.vim.helper.StringHelper;
|
||||
import com.maddyhome.idea.vim.option.NumberOption;
|
||||
import com.maddyhome.idea.vim.option.Options;
|
||||
import org.jdom.CDATA;
|
||||
import org.jdom.Element;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
@@ -37,7 +37,7 @@ public class HistoryGroup extends AbstractActionGroup {
|
||||
public static final String EXPRESSION = "expr";
|
||||
public static final String INPUT = "input";
|
||||
|
||||
public void addEntry(String key, String text) {
|
||||
public void addEntry(String key, @NotNull String text) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Add entry '" + text + "' to " + key);
|
||||
}
|
||||
@@ -46,12 +46,6 @@ public class HistoryGroup extends AbstractActionGroup {
|
||||
block.addEntry(text);
|
||||
}
|
||||
|
||||
public String getEntry(String key, int index) {
|
||||
HistoryBlock block = blocks(key);
|
||||
|
||||
return block.getEntry(index);
|
||||
}
|
||||
|
||||
public List<HistoryEntry> getEntries(String key, int first, int last) {
|
||||
HistoryBlock block = blocks(key);
|
||||
|
||||
@@ -103,12 +97,7 @@ public class HistoryGroup extends AbstractActionGroup {
|
||||
return block;
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows the group to save its state and any configuration. This does nothing.
|
||||
*
|
||||
* @param element The plugin's root XML element that this group can add a child to
|
||||
*/
|
||||
public void saveData(Element element) {
|
||||
public void saveData(@NotNull Element element) {
|
||||
logger.debug("saveData");
|
||||
Element hist = new Element("history");
|
||||
|
||||
@@ -121,30 +110,23 @@ public class HistoryGroup extends AbstractActionGroup {
|
||||
}
|
||||
|
||||
private void saveData(Element element, String key) {
|
||||
HistoryBlock block = histories.get(key);
|
||||
final HistoryBlock block = histories.get(key);
|
||||
if (block == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
Element root = new Element("history-" + key);
|
||||
final Element root = new Element("history-" + key);
|
||||
|
||||
List<HistoryEntry> elems = block.getEntries();
|
||||
for (HistoryEntry entry : elems) {
|
||||
Element text = new Element("entry");
|
||||
CDATA data = new CDATA(StringHelper.entities(entry.getEntry()));
|
||||
text.addContent(data);
|
||||
root.addContent(text);
|
||||
for (HistoryEntry entry : block.getEntries()) {
|
||||
final Element entryElement = new Element("entry");
|
||||
StringHelper.setSafeXmlText(entryElement, entry.getEntry());
|
||||
root.addContent(entryElement);
|
||||
}
|
||||
|
||||
element.addContent(root);
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows the group to restore its state and any configuration. This does nothing.
|
||||
*
|
||||
* @param element The plugin's root XML element that this group can add a child to
|
||||
*/
|
||||
public void readData(Element element) {
|
||||
public void readData(@NotNull Element element) {
|
||||
logger.debug("readData");
|
||||
Element hist = element.getChild("history");
|
||||
if (hist == null) {
|
||||
@@ -166,11 +148,15 @@ public class HistoryGroup extends AbstractActionGroup {
|
||||
block = new HistoryBlock();
|
||||
histories.put(key, block);
|
||||
|
||||
Element root = element.getChild("history-" + key);
|
||||
final Element root = element.getChild("history-" + key);
|
||||
if (root != null) {
|
||||
List items = root.getChildren("entry");
|
||||
for (Object item : items) {
|
||||
block.addEntry(StringHelper.unentities(((Element)item).getText()));
|
||||
//noinspection unchecked
|
||||
List<Element> items = root.getChildren("entry");
|
||||
for (Element item : items) {
|
||||
final String text = StringHelper.getSafeXmlText(item);
|
||||
if (text != null) {
|
||||
block.addEntry(text);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -182,7 +168,7 @@ public class HistoryGroup extends AbstractActionGroup {
|
||||
}
|
||||
|
||||
private static class HistoryBlock {
|
||||
public void addEntry(String text) {
|
||||
public void addEntry(@NotNull String text) {
|
||||
for (int i = 0; i < entries.size(); i++) {
|
||||
HistoryEntry entry = entries.get(i);
|
||||
if (text.equals(entry.getEntry())) {
|
||||
@@ -198,26 +184,17 @@ public class HistoryGroup extends AbstractActionGroup {
|
||||
}
|
||||
}
|
||||
|
||||
public String getEntry(int index) {
|
||||
if (index < entries.size()) {
|
||||
HistoryEntry entry = entries.get(index);
|
||||
return entry.getEntry();
|
||||
}
|
||||
else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public List<HistoryEntry> getEntries() {
|
||||
return entries;
|
||||
}
|
||||
|
||||
private List<HistoryEntry> entries = new ArrayList<HistoryEntry>();
|
||||
@NotNull private final List<HistoryEntry> entries = new ArrayList<HistoryEntry>();
|
||||
private int counter;
|
||||
}
|
||||
|
||||
public static class HistoryEntry {
|
||||
public HistoryEntry(int number, String entry) {
|
||||
public HistoryEntry(int number, @NotNull String entry) {
|
||||
this.number = number;
|
||||
this.entry = entry;
|
||||
}
|
||||
@@ -226,12 +203,13 @@ public class HistoryGroup extends AbstractActionGroup {
|
||||
return number;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public String getEntry() {
|
||||
return entry;
|
||||
}
|
||||
|
||||
private int number;
|
||||
private String entry;
|
||||
@NotNull private String entry;
|
||||
}
|
||||
|
||||
private Map<String, HistoryBlock> histories = new HashMap<String, HistoryBlock>();
|
||||
|
@@ -347,12 +347,7 @@ public class MarkGroup extends AbstractActionGroup {
|
||||
return marks;
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows the group to save its state and any configuration.
|
||||
*
|
||||
* @param element The plugin's root XML element that this group can add a child to
|
||||
*/
|
||||
public void saveData(Element element) {
|
||||
public void saveData(@NotNull Element element) {
|
||||
Element marksElem = new Element("globalmarks");
|
||||
for (Mark mark : globalMarks.values()) {
|
||||
if (!mark.isClear()) {
|
||||
@@ -423,12 +418,7 @@ public class MarkGroup extends AbstractActionGroup {
|
||||
element.addContent(jumpsElem);
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows the group to restore its state and any configuration.
|
||||
*
|
||||
* @param element The plugin's root XML element that this group can add a child to
|
||||
*/
|
||||
public void readData(Element element) {
|
||||
public void readData(@NotNull Element element) {
|
||||
// We need to keep the filename for now and create the virtual file later. Any attempt to call
|
||||
// LocalFileSystem.getInstance().findFileByPath() results in the following error:
|
||||
// Read access is allowed from event dispatch thread or inside read-action only
|
||||
|
@@ -19,7 +19,6 @@ package com.maddyhome.idea.vim.group;
|
||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
|
||||
import com.intellij.openapi.actionSystem.DataContext;
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.openapi.editor.Editor;
|
||||
import com.maddyhome.idea.vim.command.CommandState;
|
||||
@@ -29,9 +28,9 @@ import com.maddyhome.idea.vim.common.TextRange;
|
||||
import com.maddyhome.idea.vim.helper.EditorHelper;
|
||||
import com.maddyhome.idea.vim.helper.StringHelper;
|
||||
import com.maddyhome.idea.vim.ui.ClipboardHandler;
|
||||
import org.jdom.CDATA;
|
||||
import org.jdom.Element;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.awt.event.KeyEvent;
|
||||
@@ -45,27 +44,33 @@ import java.util.List;
|
||||
*/
|
||||
public class RegisterGroup extends AbstractActionGroup {
|
||||
/**
|
||||
* The regsister key for the default register
|
||||
* The register key for the default register
|
||||
*/
|
||||
public static final char REGISTER_DEFAULT = '"';
|
||||
|
||||
/**
|
||||
* Creates the group
|
||||
*/
|
||||
public RegisterGroup() {
|
||||
}
|
||||
private static final String WRITABLE_REGISTERS = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ-*+_/\"";
|
||||
private static final String READONLY_REGISTERS = ":.%#=/";
|
||||
private static final String RECORDABLE_REGISTER = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
|
||||
private static final String PLAYBACK_REGISTER = RECORDABLE_REGISTER + "\".*+";
|
||||
private static final String VALID_REGISTERS = WRITABLE_REGISTERS + READONLY_REGISTERS;
|
||||
|
||||
private static final Logger logger = Logger.getInstance(RegisterGroup.class.getName());
|
||||
|
||||
private char lastRegister = REGISTER_DEFAULT;
|
||||
@NotNull private HashMap<Character, Register> registers = new HashMap<Character, Register>();
|
||||
private char recordRegister = 0;
|
||||
@Nullable private List<KeyStroke> recordList = null;
|
||||
public RegisterGroup() {}
|
||||
|
||||
/**
|
||||
* Check to see if the last selected register can be written to
|
||||
*
|
||||
* @return true if writable register, false if not
|
||||
* Check to see if the last selected register can be written to.
|
||||
*/
|
||||
public boolean isRegisterWritable() {
|
||||
return READONLY_REGISTERS.indexOf(lastRegister) < 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Stores which register the user wishes to work with
|
||||
* Store which register the user wishes to work with.
|
||||
*
|
||||
* @param reg The register name
|
||||
* @return true if a valid register name, false if not
|
||||
@@ -83,7 +88,7 @@ public class RegisterGroup extends AbstractActionGroup {
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets the selected register back to the default register
|
||||
* Reset the selected register back to the default register.
|
||||
*/
|
||||
public void resetRegister() {
|
||||
lastRegister = REGISTER_DEFAULT;
|
||||
@@ -91,32 +96,27 @@ public class RegisterGroup extends AbstractActionGroup {
|
||||
}
|
||||
|
||||
/**
|
||||
* Stores text into the last register
|
||||
* Store text into the last register.
|
||||
*
|
||||
* @param editor The editor to get the text from
|
||||
* @param context The data context
|
||||
* @param range The range of the text to store
|
||||
* @param type The type of copy
|
||||
* @param isDelete is from a delete
|
||||
* @param isYank is from a yank
|
||||
* @return true if able to store the text into the register, false if not
|
||||
*/
|
||||
public boolean storeText(Editor editor, DataContext context, TextRange range, @NotNull SelectionType type, boolean isDelete, boolean isYank) {
|
||||
public boolean storeText(@NotNull Editor editor, @NotNull TextRange range, @NotNull SelectionType type,
|
||||
boolean isDelete) {
|
||||
if (isRegisterWritable()) {
|
||||
String text = EditorHelper.getText(editor, range);
|
||||
|
||||
return storeTextInternal(editor, range, text, type, lastRegister, isDelete, isYank);
|
||||
return storeTextInternal(editor, range, text, type, lastRegister, isDelete);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public void storeKeys(List<KeyStroke> strokes, @NotNull SelectionType type, char register) {
|
||||
registers.put(register, new Register(register, type, strokes));
|
||||
}
|
||||
|
||||
public boolean storeTextInternal(Editor editor, TextRange range, String text, @NotNull SelectionType type,
|
||||
char register, boolean isDelete, boolean isYank) {
|
||||
public boolean storeTextInternal(@NotNull Editor editor, @NotNull TextRange range, @NotNull String text,
|
||||
@NotNull SelectionType type, char register, boolean isDelete) {
|
||||
// Null register doesn't get saved
|
||||
if (lastRegister == '_') return true;
|
||||
|
||||
@@ -199,10 +199,12 @@ public class RegisterGroup extends AbstractActionGroup {
|
||||
*
|
||||
* @return The register, null if no such register
|
||||
*/
|
||||
@Nullable
|
||||
public Register getLastRegister() {
|
||||
return getRegister(lastRegister);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public Register getPlaybackRegister(char r) {
|
||||
if (PLAYBACK_REGISTER.indexOf(r) != 0) {
|
||||
return getRegister(r);
|
||||
@@ -212,6 +214,7 @@ public class RegisterGroup extends AbstractActionGroup {
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public Register getRegister(char r) {
|
||||
// Uppercase registers actually get the lowercase register
|
||||
if (Character.isUpperCase(r)) {
|
||||
@@ -241,6 +244,7 @@ public class RegisterGroup extends AbstractActionGroup {
|
||||
return lastRegister;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public List<Register> getRegisters() {
|
||||
ArrayList<Register> res = new ArrayList<Register>(registers.values());
|
||||
Collections.sort(res, new Register.KeySorter<Register>());
|
||||
@@ -261,13 +265,13 @@ public class RegisterGroup extends AbstractActionGroup {
|
||||
}
|
||||
|
||||
public void addKeyStroke(KeyStroke key) {
|
||||
if (recordRegister != 0) {
|
||||
if (recordRegister != 0 && recordList != null) {
|
||||
recordList.add(key);
|
||||
}
|
||||
}
|
||||
|
||||
public void addText(String text) {
|
||||
if (recordRegister != 0) {
|
||||
public void addText(@NotNull String text) {
|
||||
if (recordRegister != 0 && recordList != null) {
|
||||
recordList.addAll(StringHelper.stringToKeys(text));
|
||||
}
|
||||
}
|
||||
@@ -279,12 +283,14 @@ public class RegisterGroup extends AbstractActionGroup {
|
||||
reg = getRegister(recordRegister);
|
||||
}
|
||||
|
||||
if (reg == null) {
|
||||
reg = new Register(Character.toLowerCase(recordRegister), SelectionType.CHARACTER_WISE, recordList);
|
||||
registers.put(Character.toLowerCase(recordRegister), reg);
|
||||
}
|
||||
else {
|
||||
reg.addKeys(recordList);
|
||||
if (recordList != null) {
|
||||
if (reg == null) {
|
||||
reg = new Register(Character.toLowerCase(recordRegister), SelectionType.CHARACTER_WISE, recordList);
|
||||
registers.put(Character.toLowerCase(recordRegister), reg);
|
||||
}
|
||||
else {
|
||||
reg.addKeys(recordList);
|
||||
}
|
||||
}
|
||||
CommandState.getInstance(editor).setRecording(false);
|
||||
}
|
||||
@@ -292,100 +298,77 @@ public class RegisterGroup extends AbstractActionGroup {
|
||||
recordRegister = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Save all the registers
|
||||
*
|
||||
* @param element The plugin's root XML element that this group can add a child to
|
||||
*/
|
||||
public void saveData(Element element) {
|
||||
public void saveData(@NotNull final Element element) {
|
||||
logger.debug("saveData");
|
||||
Element regs = new Element("registers");
|
||||
final Element registersElement = new Element("registers");
|
||||
for (Character key : registers.keySet()) {
|
||||
Register register = registers.get(key);
|
||||
|
||||
Element reg = new Element("register");
|
||||
reg.setAttribute("name", String.valueOf(key));
|
||||
reg.setAttribute("type", Integer.toString(register.getType().getValue()));
|
||||
if (register.isText()) {
|
||||
Element text = new Element("text");
|
||||
CDATA data = new CDATA(/*CDATA.normalizeString*/(StringHelper.entities(register.getText())));
|
||||
text.addContent(data);
|
||||
reg.addContent(text);
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("register='" + register.getText() + "'");
|
||||
logger.debug("data=" + data);
|
||||
logger.debug("text=" + text);
|
||||
}
|
||||
final Register register = registers.get(key);
|
||||
final Element registerElement = new Element("register");
|
||||
registerElement.setAttribute("name", String.valueOf(key));
|
||||
registerElement.setAttribute("type", Integer.toString(register.getType().getValue()));
|
||||
final String text = register.getText();
|
||||
if (text != null) {
|
||||
final Element textElement = new Element("text");
|
||||
StringHelper.setSafeXmlText(textElement, text);
|
||||
registerElement.addContent(textElement);
|
||||
}
|
||||
else {
|
||||
Element keys = new Element("keys");
|
||||
List<KeyStroke> list = register.getKeys();
|
||||
final Element keys = new Element("keys");
|
||||
final List<KeyStroke> list = register.getKeys();
|
||||
for (KeyStroke stroke : list) {
|
||||
Element k = new Element("key");
|
||||
final Element k = new Element("key");
|
||||
k.setAttribute("char", Integer.toString(stroke.getKeyChar()));
|
||||
k.setAttribute("code", Integer.toString(stroke.getKeyCode()));
|
||||
k.setAttribute("mods", Integer.toString(stroke.getModifiers()));
|
||||
keys.addContent(k);
|
||||
}
|
||||
reg.addContent(keys);
|
||||
registerElement.addContent(keys);
|
||||
}
|
||||
regs.addContent(reg);
|
||||
registersElement.addContent(registerElement);
|
||||
}
|
||||
|
||||
element.addContent(regs);
|
||||
element.addContent(registersElement);
|
||||
}
|
||||
|
||||
/**
|
||||
* Restore all the registers
|
||||
*
|
||||
* @param element The plugin's root XML element that this group can add a child to
|
||||
*/
|
||||
public void readData(Element element) {
|
||||
public void readData(@NotNull final Element element) {
|
||||
logger.debug("readData");
|
||||
Element regs = element.getChild("registers");
|
||||
if (regs != null) {
|
||||
List<Element> list = regs.getChildren("register");
|
||||
for (Element reg : list) {
|
||||
Character key = reg.getAttributeValue("name").charAt(0);
|
||||
Register register;
|
||||
if (reg.getChild("text") != null) {
|
||||
final SelectionType type = SelectionType.fromValue(Integer.parseInt(reg.getAttributeValue("type")));
|
||||
register = new Register(key, type != null ? type : SelectionType.CHARACTER_WISE,
|
||||
StringHelper.unentities(reg.getChild("text").getText/*Normalize*/()));
|
||||
final Element registersElement = element.getChild("registers");
|
||||
if (registersElement != null) {
|
||||
//noinspection unchecked
|
||||
final List<Element> registerElements = registersElement.getChildren("register");
|
||||
for (Element registerElement : registerElements) {
|
||||
final char key = registerElement.getAttributeValue("name").charAt(0);
|
||||
final Register register;
|
||||
final Element textElement = registerElement.getChild("text");
|
||||
final String typeText = registerElement.getAttributeValue("type");
|
||||
final SelectionType type = SelectionType.fromValue(Integer.parseInt(typeText));
|
||||
if (textElement != null) {
|
||||
final String text = StringHelper.getSafeXmlText(textElement);
|
||||
if (text != null) {
|
||||
register = new Register(key, type != null ? type : SelectionType.CHARACTER_WISE, text);
|
||||
}
|
||||
else {
|
||||
register = null;
|
||||
}
|
||||
}
|
||||
else {
|
||||
Element keys = reg.getChild("keys");
|
||||
List<Element> klist = keys.getChildren("key");
|
||||
List<KeyStroke> strokes = new ArrayList<KeyStroke>();
|
||||
for (Element kelem : klist) {
|
||||
int code = Integer.parseInt(kelem.getAttributeValue("code"));
|
||||
int mods = Integer.parseInt(kelem.getAttributeValue("mods"));
|
||||
char ch = (char)Integer.parseInt(kelem.getAttributeValue("char"));
|
||||
if (ch == KeyEvent.CHAR_UNDEFINED) {
|
||||
strokes.add(KeyStroke.getKeyStroke(code, mods));
|
||||
}
|
||||
else {
|
||||
strokes.add(KeyStroke.getKeyStroke(new Character(ch), mods));
|
||||
}
|
||||
final Element keysElement = registerElement.getChild("keys");
|
||||
//noinspection unchecked
|
||||
final List<Element> keyElements = keysElement.getChildren("key");
|
||||
final List<KeyStroke> strokes = new ArrayList<KeyStroke>();
|
||||
for (Element keyElement : keyElements) {
|
||||
final int code = Integer.parseInt(keyElement.getAttributeValue("code"));
|
||||
final int modifiers = Integer.parseInt(keyElement.getAttributeValue("mods"));
|
||||
final char c = (char)Integer.parseInt(keyElement.getAttributeValue("char"));
|
||||
//noinspection MagicConstant
|
||||
strokes.add(c == KeyEvent.CHAR_UNDEFINED ?
|
||||
KeyStroke.getKeyStroke(code, modifiers) :
|
||||
KeyStroke.getKeyStroke(c));
|
||||
}
|
||||
final SelectionType type = SelectionType.fromValue(Integer.parseInt(reg.getAttributeValue("type")));
|
||||
register = new Register(key, type != null ? type : SelectionType.CHARACTER_WISE, strokes);
|
||||
}
|
||||
registers.put(key, register);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private char lastRegister = REGISTER_DEFAULT;
|
||||
private HashMap<Character, Register> registers = new HashMap<Character, Register>();
|
||||
private char recordRegister = 0;
|
||||
private List<KeyStroke> recordList = null;
|
||||
|
||||
private static final String WRITABLE_REGISTERS = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ-*+_/\"";
|
||||
private static final String READONLY_REGISTERS = ":.%#=/";
|
||||
private static final String RECORDABLE_REGISTER = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
|
||||
private static final String PLAYBACK_REGISTER = RECORDABLE_REGISTER + "\".*+";
|
||||
private static final String VALID_REGISTERS = WRITABLE_REGISTERS + READONLY_REGISTERS;
|
||||
|
||||
private static Logger logger = Logger.getInstance(RegisterGroup.class.getName());
|
||||
}
|
||||
|
@@ -45,8 +45,9 @@ import com.maddyhome.idea.vim.regexp.CharHelper;
|
||||
import com.maddyhome.idea.vim.regexp.CharPointer;
|
||||
import com.maddyhome.idea.vim.regexp.CharacterClasses;
|
||||
import com.maddyhome.idea.vim.regexp.RegExp;
|
||||
import org.jdom.CDATA;
|
||||
import org.jdom.Element;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.awt.event.ActionEvent;
|
||||
@@ -87,10 +88,10 @@ public class SearchGroup extends AbstractActionGroup {
|
||||
return lastPattern;
|
||||
}
|
||||
|
||||
private void setLastPattern(Editor editor, String lastPattern) {
|
||||
private void setLastPattern(Editor editor, @NotNull String lastPattern) {
|
||||
this.lastPattern = lastPattern;
|
||||
CommandGroups.getInstance().getRegister().storeTextInternal(editor, new TextRange(-1, -1),
|
||||
lastPattern, SelectionType.CHARACTER_WISE, '/', false, false);
|
||||
lastPattern, SelectionType.CHARACTER_WISE, '/', false);
|
||||
|
||||
CommandGroups.getInstance().getHistory().addEntry(HistoryGroup.SEARCH, lastPattern);
|
||||
}
|
||||
@@ -266,7 +267,9 @@ public class SearchGroup extends AbstractActionGroup {
|
||||
}
|
||||
|
||||
lastSubstitute = pattern;
|
||||
setLastPattern(editor, pattern);
|
||||
if (pattern != null) {
|
||||
setLastPattern(editor, pattern);
|
||||
}
|
||||
|
||||
//int start = editor.logicalPositionToOffset(new LogicalPosition(line1, 0));
|
||||
//int end = editor.logicalPositionToOffset(new LogicalPosition(line2, EditorHelper.getLineLength(editor, line2)));
|
||||
@@ -559,7 +562,9 @@ public class SearchGroup extends AbstractActionGroup {
|
||||
}
|
||||
|
||||
lastSearch = pattern;
|
||||
setLastPattern(editor, pattern);
|
||||
if (pattern != null) {
|
||||
setLastPattern(editor, pattern);
|
||||
}
|
||||
lastOffset = offset;
|
||||
lastDir = dir;
|
||||
|
||||
@@ -1107,43 +1112,23 @@ public class SearchGroup extends AbstractActionGroup {
|
||||
EditorData.setLastSearch(editor, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows the group to save its state and any configuration. This does nothing.
|
||||
*
|
||||
* @param element The plugin's root XML element that this group can add a child to
|
||||
*/
|
||||
public void saveData(Element element) {
|
||||
public void saveData(@NotNull Element element) {
|
||||
logger.debug("saveData");
|
||||
Element search = new Element("search");
|
||||
if (lastSearch != null) {
|
||||
Element text = new Element("last-search");
|
||||
CDATA data = new CDATA(/*CDATA.normalizeString*/StringHelper.entities(lastSearch));
|
||||
text.addContent(data);
|
||||
search.addContent(text);
|
||||
search.addContent(createElementWithText("last-search", lastSearch));
|
||||
}
|
||||
if (lastOffset != null) {
|
||||
Element text = new Element("last-offset");
|
||||
CDATA data = new CDATA(/*CDATA.normalizeString*/StringHelper.entities(lastOffset));
|
||||
text.addContent(data);
|
||||
search.addContent(text);
|
||||
search.addContent(createElementWithText("last-offset", lastOffset));
|
||||
}
|
||||
if (lastPattern != null) {
|
||||
Element text = new Element("last-pattern");
|
||||
CDATA data = new CDATA(/*CDATA.normalizeString*/StringHelper.entities(lastPattern));
|
||||
text.addContent(data);
|
||||
search.addContent(text);
|
||||
search.addContent(createElementWithText("last-pattern", lastPattern));
|
||||
}
|
||||
if (lastReplace != null) {
|
||||
Element text = new Element("last-replace");
|
||||
CDATA data = new CDATA(/*CDATA.normalizeString*/StringHelper.entities(lastReplace));
|
||||
text.addContent(data);
|
||||
search.addContent(text);
|
||||
search.addContent(createElementWithText("last-replace", lastReplace));
|
||||
}
|
||||
if (lastSubstitute != null) {
|
||||
Element text = new Element("last-substitute");
|
||||
CDATA data = new CDATA(/*CDATA.normalizeString*/StringHelper.entities(lastSubstitute));
|
||||
text.addContent(data);
|
||||
search.addContent(text);
|
||||
search.addContent(createElementWithText("last-substitute", lastSubstitute));
|
||||
}
|
||||
Element text = new Element("last-dir");
|
||||
text.addContent(Integer.toString(lastDir));
|
||||
@@ -1157,33 +1142,23 @@ public class SearchGroup extends AbstractActionGroup {
|
||||
element.addContent(search);
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows the group to restore its state and any configuration. This does nothing.
|
||||
*
|
||||
* @param element The plugin's root XML element that this group can add a child to
|
||||
*/
|
||||
public void readData(Element element) {
|
||||
@NotNull
|
||||
private static Element createElementWithText(@NotNull String name, @NotNull String text) {
|
||||
return StringHelper.setSafeXmlText(new Element(name), text);
|
||||
}
|
||||
|
||||
public void readData(@NotNull Element element) {
|
||||
logger.debug("readData");
|
||||
Element search = element.getChild("search");
|
||||
if (search == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (search.getChild("last-search") != null) {
|
||||
lastSearch = StringHelper.unentities(search.getChildText/*Normalize*/("last-search"));
|
||||
}
|
||||
if (search.getChild("last-offset") != null) {
|
||||
lastOffset = StringHelper.unentities(search.getChildText/*Normalize*/("last-offset"));
|
||||
}
|
||||
if (search.getChild("last-pattern") != null) {
|
||||
lastPattern = StringHelper.unentities(search.getChildText/*Normalize*/("last-pattern"));
|
||||
}
|
||||
if (search.getChild("last-replace") != null) {
|
||||
lastReplace = StringHelper.unentities(search.getChildText/*Normalize*/("last-replace"));
|
||||
}
|
||||
if (search.getChild("last-substitute") != null) {
|
||||
lastSubstitute = StringHelper.unentities(search.getChildText/*Normalize*/("last-substitute"));
|
||||
}
|
||||
lastSearch = getSafeChildText(search, "last-search");
|
||||
lastOffset = getSafeChildText(search, "last-offset");
|
||||
lastPattern = getSafeChildText(search, "last-pattern");
|
||||
lastReplace = getSafeChildText(search, "last-replace");
|
||||
lastSubstitute = getSafeChildText(search, "last-substitute");
|
||||
|
||||
Element dir = search.getChild("last-dir");
|
||||
lastDir = Integer.parseInt(dir.getText());
|
||||
@@ -1196,6 +1171,12 @@ public class SearchGroup extends AbstractActionGroup {
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static String getSafeChildText(@NotNull Element element, @NotNull String name) {
|
||||
final Element child = element.getChild(name);
|
||||
return child != null ? StringHelper.getSafeXmlText(child) : null;
|
||||
}
|
||||
|
||||
private class ButtonActionListener implements ActionListener {
|
||||
public ButtonActionListener(int i) {
|
||||
index = i;
|
||||
|
@@ -19,17 +19,21 @@ package com.maddyhome.idea.vim.helper;
|
||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
|
||||
import org.apache.commons.codec.binary.Base64;
|
||||
import org.jdom.CDATA;
|
||||
import org.jdom.Content;
|
||||
import org.jdom.Element;
|
||||
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.List;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public class StringHelper {
|
||||
private StringHelper() {}
|
||||
|
||||
public static String pad(String text, int len, char ch) {
|
||||
int l = text.length();
|
||||
StringBuffer res = new StringBuffer(text);
|
||||
@@ -40,67 +44,46 @@ public class StringHelper {
|
||||
return res.toString();
|
||||
}
|
||||
|
||||
public static String escape(String text) {
|
||||
StringBuffer res = new StringBuffer(text.length());
|
||||
@NotNull
|
||||
public static String escape(@NotNull String s) {
|
||||
return escape(stringToKeys(s));
|
||||
}
|
||||
|
||||
for (int i = 0; i < text.length(); i++) {
|
||||
char ch = text.charAt(i);
|
||||
if (ch < ' ') {
|
||||
res.append('^').append((char)(ch + 'A' - 1));
|
||||
@NotNull
|
||||
public static String escape(@NotNull List<KeyStroke> keys) {
|
||||
final StringBuffer res = new StringBuffer();
|
||||
for (KeyStroke key : keys) {
|
||||
final char c = key.getKeyChar();
|
||||
final int modifiers = key.getModifiers();
|
||||
final int code = key.getKeyCode();
|
||||
if (c < ' ') {
|
||||
res.append('^').append((char)(c + 'A' - 1));
|
||||
}
|
||||
else if (ch == '\n') {
|
||||
else if (c == '\n') {
|
||||
res.append("^J");
|
||||
}
|
||||
else if (ch == '\t') {
|
||||
else if (c == '\t') {
|
||||
res.append("^I");
|
||||
}
|
||||
else if (c == '\u0000') {
|
||||
res.append("^@");
|
||||
}
|
||||
else if ((modifiers & KeyEvent.CTRL_DOWN_MASK) != 0) {
|
||||
final char[] chars = Character.toChars(code);
|
||||
if (chars.length == 1) {
|
||||
res.append("^");
|
||||
res.append(chars);
|
||||
}
|
||||
}
|
||||
else {
|
||||
res.append(ch);
|
||||
res.append(c);
|
||||
}
|
||||
}
|
||||
|
||||
return res.toString();
|
||||
}
|
||||
|
||||
public static String entities(String text) {
|
||||
StringBuffer res = new StringBuffer(text.length());
|
||||
|
||||
for (int i = 0; i < text.length(); i++) {
|
||||
char ch = text.charAt(i);
|
||||
switch (ch) {
|
||||
case '!':
|
||||
res.append("!");
|
||||
break;
|
||||
case '[':
|
||||
res.append("[");
|
||||
break;
|
||||
case ']':
|
||||
res.append("]");
|
||||
break;
|
||||
case ' ':
|
||||
res.append(" ");
|
||||
break;
|
||||
case '&':
|
||||
res.append("&");
|
||||
break;
|
||||
case '\t':
|
||||
res.append("	");
|
||||
break;
|
||||
case '\n':
|
||||
res.append(" ");
|
||||
break;
|
||||
case '\r':
|
||||
res.append(" ");
|
||||
break;
|
||||
default:
|
||||
res.append(ch);
|
||||
}
|
||||
}
|
||||
|
||||
return res.toString();
|
||||
}
|
||||
|
||||
public static String unentities(String text) {
|
||||
@Deprecated
|
||||
private static String unentities(String text) {
|
||||
StringBuffer res = new StringBuffer(text.length());
|
||||
|
||||
for (int i = 0; i < text.length(); i++) {
|
||||
@@ -163,56 +146,6 @@ public class StringHelper {
|
||||
return res;
|
||||
}
|
||||
|
||||
public static String keysToString(List<KeyStroke> keys) {
|
||||
StringBuffer res = new StringBuffer();
|
||||
for (KeyStroke key : keys) {
|
||||
if (key.getKeyChar() != KeyEvent.CHAR_UNDEFINED) {
|
||||
res.append(key.getKeyChar());
|
||||
}
|
||||
else {
|
||||
switch (key.getKeyCode()) {
|
||||
case KeyEvent.VK_TAB:
|
||||
res.append("\t");
|
||||
break;
|
||||
case KeyEvent.VK_ENTER:
|
||||
res.append("\n");
|
||||
break;
|
||||
case KeyEvent.VK_BACK_SPACE:
|
||||
res.append("\b");
|
||||
break;
|
||||
default:
|
||||
res.append('<');
|
||||
res.append(getModifiersText(key.getModifiers()));
|
||||
res.append(KeyEvent.getKeyText(key.getKeyCode()));
|
||||
res.append('>');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return res.toString();
|
||||
}
|
||||
|
||||
public static String getModifiersText(int modifiers) {
|
||||
StringBuffer buf = new StringBuffer();
|
||||
if ((modifiers & KeyEvent.META_MASK) != 0) {
|
||||
buf.append("M-");
|
||||
}
|
||||
if ((modifiers & KeyEvent.CTRL_MASK) != 0) {
|
||||
buf.append("C-");
|
||||
}
|
||||
if ((modifiers & KeyEvent.ALT_MASK) != 0) {
|
||||
buf.append("A-");
|
||||
}
|
||||
if ((modifiers & KeyEvent.SHIFT_MASK) != 0) {
|
||||
buf.append("S-");
|
||||
}
|
||||
if ((modifiers & KeyEvent.ALT_GRAPH_MASK) != 0) {
|
||||
buf.append("G-");
|
||||
}
|
||||
|
||||
return buf.toString();
|
||||
}
|
||||
|
||||
public static boolean containsUpperCase(String text) {
|
||||
for (int i = 0; i < text.length(); i++) {
|
||||
if (Character.isUpperCase(text.charAt(i)) && (i == 0 || text.charAt(i - 1) == '\\')) {
|
||||
@@ -223,6 +156,79 @@ public class StringHelper {
|
||||
return false;
|
||||
}
|
||||
|
||||
private StringHelper() {
|
||||
/**
|
||||
* Set the text of an XML element, safely encode it if needed.
|
||||
*/
|
||||
public static Element setSafeXmlText(@NotNull Element element, @NotNull String text) {
|
||||
final Character first = firstCharacter(text);
|
||||
final Character last = lastCharacter(text);
|
||||
if (!StringHelper.isXmlCharacterData(text) ||
|
||||
first != null && Character.isWhitespace(first) ||
|
||||
last != null && Character.isWhitespace(last)) {
|
||||
element.setAttribute("encoding", "base64");
|
||||
final String encoded = new String(Base64.encodeBase64(text.getBytes()));
|
||||
element.setText(encoded);
|
||||
}
|
||||
else {
|
||||
element.setText(text);
|
||||
}
|
||||
return element;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static Character lastCharacter(@NotNull String text) {
|
||||
return text.length() > 0 ? text.charAt(text.length() - 1) : null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static Character firstCharacter(@NotNull String text) {
|
||||
return text.length() > 0 ? text.charAt(0) : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the (potentially safely encoded) text of an XML element.
|
||||
*/
|
||||
@Nullable
|
||||
public static String getSafeXmlText(@NotNull Element element) {
|
||||
final String text = element.getText();
|
||||
//noinspection unchecked
|
||||
final List<Content> contentItems = element.getContent();
|
||||
for (Content content : contentItems) {
|
||||
if (content instanceof CDATA) {
|
||||
// TODO: Remove compatibility with the IdeaVim <= 0.24 settings style in the next versions
|
||||
return unentities(text);
|
||||
}
|
||||
}
|
||||
final String encoding = element.getAttributeValue("encoding");
|
||||
if (encoding == null) {
|
||||
return text;
|
||||
}
|
||||
else if (encoding.equals("base64")) {
|
||||
return new String(Base64.decodeBase64(text.getBytes()));
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the text matches the CharData production from the XML grammar.
|
||||
*
|
||||
* This check is more restricted than CharData as it completely forbids '>'.
|
||||
*/
|
||||
public static boolean isXmlCharacterData(@NotNull String text) {
|
||||
for (char c : text.toCharArray()) {
|
||||
if (!isXmlChar(c) || c == '<' || c == '>' || c == '&') {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a char matches the Char production from the XML grammar.
|
||||
*
|
||||
* Characters beyond the Basic Multilingual Plane are not supported.
|
||||
*/
|
||||
private static boolean isXmlChar(char c) {
|
||||
return '\u0001' <= c && c <= '\uD7FF' || '\uE000' <= c && c <= '\uFFFD';
|
||||
}
|
||||
}
|
||||
|
@@ -23,6 +23,7 @@ public class MacroActionTest extends VimTestCase {
|
||||
assertFalse(commandState.isRecording());
|
||||
final RegisterGroup registerGroup = CommandGroups.getInstance().getRegister();
|
||||
final Register register = registerGroup.getRegister('a');
|
||||
assertNotNull(register);
|
||||
assertEquals("3l", register.getText());
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user