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

VIM-439: Implement :sort command.

- Supports "n", "i" and "!" arguments.
This commit is contained in:
Alex Selesse 2014-02-05 21:23:53 -05:00
parent 58ab3ab1bb
commit 6a7135d2bc
4 changed files with 249 additions and 0 deletions
src/com/maddyhome/idea/vim
test/org/jetbrains/plugins/ideavim/ex

View File

@ -104,6 +104,7 @@ public class CommandParser {
new SetHandler();
new ShiftLeftHandler();
new ShiftRightHandler();
new SortHandler();
new SubstituteHandler();
new UndoHandler();
new WriteAllHandler();

View File

@ -0,0 +1,100 @@
package com.maddyhome.idea.vim.ex.handler;
import com.intellij.openapi.actionSystem.DataContext;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.util.text.StringUtil;
import com.maddyhome.idea.vim.ex.CommandHandler;
import com.maddyhome.idea.vim.ex.ExCommand;
import com.maddyhome.idea.vim.ex.ExException;
import com.maddyhome.idea.vim.ex.LineRange;
import com.maddyhome.idea.vim.group.CommandGroups;
import java.util.Comparator;
public class SortHandler extends CommandHandler {
public SortHandler() {
super("sor", "t", RANGE_OPTIONAL | ARGUMENT_OPTIONAL | WRITABLE);
}
@Override
public boolean execute(Editor editor, DataContext context, ExCommand cmd) throws ExException {
String arg = cmd.getArgument();
boolean reverse = false;
boolean ignoreCase = false;
boolean number = false;
if (arg != null && arg.trim().length() > 0) {
number = arg.contains("n");
reverse = arg.contains("!");
ignoreCase = arg.contains("i");
}
LineRange range = cmd.getLineRange(editor, context, false);
// Something like "30,20sort" gets converted to "20,30sort"
if (range.getEndLine() < range.getStartLine()) {
range = new LineRange(range.getEndLine(), range.getStartLine());
}
// If we don't have a range, we either have "sort", a selection, or a block
if (range.getEndLine() - range.getStartLine() == 0) {
// If we have a selection.
if (editor.getSelectionModel().hasSelection()) {
int start = editor.getSelectionModel().getSelectionStart();
int end = editor.getSelectionModel().getSelectionEnd();
int startLine = editor.offsetToLogicalPosition(start).line;
int endLine = editor.offsetToLogicalPosition(end).line;
range = new LineRange(startLine, endLine);
}
// If we have a block selection
else if (editor.getSelectionModel().hasBlockSelection()) {
range = new LineRange(editor.getSelectionModel().getBlockStart().line, editor.getSelectionModel().getBlockEnd().line);
}
// If we have a generic selection, i.e. "sort" entire document
else {
range = new LineRange(0, editor.getDocument().getLineCount() - 1);
}
}
Comparator<String> lineComparator = new VimLineComparator(ignoreCase, number, reverse);
return CommandGroups.getInstance().getChange().sortRange(editor, range, lineComparator);
}
private class VimLineComparator implements Comparator<String> {
public VimLineComparator(boolean ignoreCase, boolean number, boolean reverse) {
this.ignoreCase = ignoreCase;
this.number = number;
this.reverse = reverse;
}
@Override
public int compare(String o1, String o2) {
int comparison;
if (reverse) {
String tmp = o2;
o2 = o1;
o1 = tmp;
}
if (ignoreCase) {
o1 = o1.toUpperCase();
o2 = o2.toUpperCase();
}
if (number) {
comparison = StringUtil.naturalCompare(o1, o2);
}
else {
comparison = o1.compareTo(o2);
}
return comparison;
}
private final boolean ignoreCase;
private final boolean number;
private final boolean reverse;
}
}

View File

@ -17,7 +17,10 @@
*/
package com.maddyhome.idea.vim.group;
import com.google.common.base.Joiner;
import com.google.common.base.Splitter;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Lists;
import com.intellij.openapi.actionSystem.ActionManager;
import com.intellij.openapi.actionSystem.AnAction;
import com.intellij.openapi.actionSystem.DataContext;
@ -28,6 +31,7 @@ import com.intellij.openapi.command.UndoConfirmationPolicy;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.editor.*;
import com.intellij.openapi.editor.event.*;
import com.intellij.openapi.editor.impl.TextRangeInterval;
import com.intellij.openapi.fileEditor.FileDocumentManager;
import com.intellij.openapi.fileTypes.FileType;
import com.intellij.openapi.fileTypes.FileTypeManager;
@ -43,6 +47,7 @@ import com.maddyhome.idea.vim.command.CommandState;
import com.maddyhome.idea.vim.command.SelectionType;
import com.maddyhome.idea.vim.common.Register;
import com.maddyhome.idea.vim.common.TextRange;
import com.maddyhome.idea.vim.ex.LineRange;
import com.maddyhome.idea.vim.helper.*;
import com.maddyhome.idea.vim.key.KeyParser;
import com.maddyhome.idea.vim.option.BoundListOption;
@ -53,6 +58,8 @@ import org.jetbrains.annotations.Nullable;
import javax.swing.*;
import java.awt.event.KeyEvent;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
/**
@ -1498,6 +1505,52 @@ public class ChangeGroup extends AbstractActionGroup {
CommandGroups.getInstance().getMark().setMark(editor, '.', start + str.length());
}
/**
* Sort range of text with a given comparator
*
* @param editor The editor to replace text in
* @param range The range to sort
* @param lineComparator The comparator to use to sort
* @return true if able to sort the text, false if not
*/
public boolean sortRange(@NotNull Editor editor, LineRange range, Comparator<String> lineComparator) {
int startLine = range.getStartLine();
int endLine = range.getEndLine();
int count = endLine - startLine + 1;
if (count < 2)
return false;
int startOffset = editor.getDocument().getLineStartOffset(startLine);
int endOffset = editor.getDocument().getLineEndOffset(endLine);
return sortTextRange(editor, startOffset, endOffset, lineComparator);
}
/**
* Sorts a text range with a comparator. Returns true if a replace was performed, false otherwise.
*
* @param editor The editor to replace text in
* @param start The starting position for the sort
* @param end The ending position for the sort
* @param lineComparator The comparator to use to sort
* @return true if able to sort the text, false if not
*/
private boolean sortTextRange(Editor editor, int start, int end, Comparator<String> lineComparator) {
String selectedText = editor.getDocument().getText(new TextRangeInterval(start, end));
String lineSeparator = CodeStyleSettingsManager.getSettings(editor.getProject()).getLineSeparator();
List<String> lines = Lists.newArrayList(Splitter.on(lineSeparator).split(selectedText));
if (lines.size() < 1) {
return false;
}
Collections.sort(lines, lineComparator);
replaceText(editor, start, end, Joiner.on(lineSeparator).join(lines));
return true;
}
public static void resetCursor(@NotNull Editor editor, boolean insert) {
Document doc = editor.getDocument();
VirtualFile vf = FileDocumentManager.getInstance().getFile(doc);

View File

@ -0,0 +1,95 @@
package org.jetbrains.plugins.ideavim.ex;
import com.google.common.collect.Lists;
import org.jetbrains.plugins.ideavim.VimTestCase;
import javax.swing.*;
import java.util.List;
import static com.maddyhome.idea.vim.helper.StringHelper.stringToKeys;
/**
* @author Alex Selesse
*/
public class SortCommandTest extends VimTestCase {
public void testBasicSort() {
myFixture.configureByText("a.txt", "Test\n" + "Hello World!\n");
final List<KeyStroke> keys = Lists.newArrayList(KeyStroke.getKeyStroke("control V"));
keys.addAll(stringToKeys("$j"));
typeText(keys);
runExCommand("sort");
myFixture.checkResult("Hello World!\n" + "Test\n");
}
public void testMultipleSortLine() {
myFixture.configureByText("a.txt", "zee\nyee\na\nb\n");
final List<KeyStroke> keys = Lists.newArrayList(KeyStroke.getKeyStroke("control V"));
keys.addAll(stringToKeys("$3j"));
typeText(keys);
runExCommand("sort");
myFixture.checkResult("a\nb\nyee\nzee\n");
}
public void testInverseSort() {
myFixture.configureByText("a.txt", "kay\nzee\nyee\na\nb\n");
final List<KeyStroke> keys = Lists.newArrayList(KeyStroke.getKeyStroke("control V"));
keys.addAll(stringToKeys("$4j"));
typeText(keys);
runExCommand("sort !");
myFixture.checkResult("zee\nyee\nkay\nb\na\n");
}
public void testCaseSensitiveSort() {
myFixture.configureByText("a.txt", "apple\nAppetite\nApp\napparition\n");
final List<KeyStroke> keys = Lists.newArrayList(KeyStroke.getKeyStroke("control V"));
keys.addAll(stringToKeys("$3j"));
typeText(keys);
runExCommand("sort");
myFixture.checkResult("App\nAppetite\napparition\napple\n");
}
public void testCaseInsensitiveSort() {
myFixture.configureByText("a.txt", "apple\nAppetite\nApp\napparition\n");
final List<KeyStroke> keys = Lists.newArrayList(KeyStroke.getKeyStroke("control V"));
keys.addAll(stringToKeys("$3j"));
typeText(keys);
runExCommand("sort i");
myFixture.checkResult("App\napparition\nAppetite\napple\n");
}
public void testRangeSort() {
myFixture.configureByText("a.txt", "zee\nc\na\nb\nwhatever\n");
runExCommand("2,4sort");
myFixture.checkResult("zee\na\nb\nc\nwhatever\n");
}
public void testNumberSort() {
myFixture.configureByText("a.txt", "120\n70\n30\n2000");
runExCommand("sort n");
myFixture.checkResult("30\n70\n120\n2000");
}
public void testNaturalOrderSort() {
myFixture.configureByText("a.txt", "hello1000\nhello102\nhello70000\nhello1001");
runExCommand("sort n");
myFixture.checkResult("hello102\nhello1000\nhello1001\nhello70000");
}
public void testNaturalOrderReverseSort() {
myFixture.configureByText("a.txt", "hello1000\nhello102\nhello70000\nhello1001");
runExCommand("sort n!");
myFixture.checkResult("hello70000\nhello1001\nhello1000\nhello102");
}
public void testNaturalOrderInsensitiveReverseSort() {
myFixture.configureByText("a.txt", "Hello1000\nhello102\nhEllo70000\nhello1001");
runExCommand("sort ni!");
myFixture.checkResult("hEllo70000\nhello1001\nHello1000\nhello102");
}
public void testGlobalSort() {
myFixture.configureByText("a.txt", "zee\nc\na\nb\nwhatever");
runExCommand("sort");
myFixture.checkResult("a\nb\nc\nwhatever\nzee");
}
}