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:
parent
58ab3ab1bb
commit
6a7135d2bc
src/com/maddyhome/idea/vim
test/org/jetbrains/plugins/ideavim/ex
@ -104,6 +104,7 @@ public class CommandParser {
|
||||
new SetHandler();
|
||||
new ShiftLeftHandler();
|
||||
new ShiftRightHandler();
|
||||
new SortHandler();
|
||||
new SubstituteHandler();
|
||||
new UndoHandler();
|
||||
new WriteAllHandler();
|
||||
|
100
src/com/maddyhome/idea/vim/ex/handler/SortHandler.java
Normal file
100
src/com/maddyhome/idea/vim/ex/handler/SortHandler.java
Normal 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;
|
||||
}
|
||||
}
|
@ -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);
|
||||
|
95
test/org/jetbrains/plugins/ideavim/ex/SortCommandTest.java
Normal file
95
test/org/jetbrains/plugins/ideavim/ex/SortCommandTest.java
Normal 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");
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user