How it works? If your carret is sitting in between any type of brackets, it finds the innermost it sits in. It finds the list element (text, that can contain spaces, other closed brackets, comas inside those brackets). With Ctrl+Alt+L it swaps it to the right, with Ctrl+Alt+K, it swaps it to the left. The carret moves with the item.
Known limitations. If there is an unclosed backet (syntactically incorret code), it doesn't handle it well. If the carret is not inside a backet, the behaviour is unpredicted.
The rest of the post is just source code. The Java fragment:
int start; int end; int mid; int midLeft; int midRight; public String doTheFlop(final String input, final int carretPos) { start = carretPos - 1; mid = carretPos; travelToBeginning(input); travelToMid(input); if (isRightLimiter(input.charAt(mid))) { return null; } midLeft = mid ; midRight = mid ; travelToMidLeft(input); travelToMidRight(input); end = midRight; travelToEnd(input); return input.substring(midRight, end) + input.substring(midLeft, midRight) + input.substring(start, midLeft); } private void travelToBeginning(final String input) { int groupDepth = 0; while (!((input.charAt(start) == ',' && groupDepth == 0) || (isLeftLimiter(input.charAt(start)) && groupDepth == 0))) { if (isRightLimiter(input.charAt(start))) { groupDepth++; } if (isLeftLimiter(input.charAt(start))) { groupDepth--; } start--; } start++; while (Character.isWhitespace(input.charAt(start))) { start++; } } private void travelToMid(final String input) { int groupDepth = 0; while (!((input.charAt(mid) == ',' && groupDepth == 0) || (isRightLimiter(input.charAt(mid)) && groupDepth == 0))) { if (isLeftLimiter(input.charAt(mid))) { groupDepth++; } if (isRightLimiter(input.charAt(mid))) { groupDepth--; } mid++; } } private void travelToMidLeft(final String input) { // this currently points to a coma or bracket midLeft--; while (Character.isWhitespace(input.charAt(midLeft))) { midLeft--; } midLeft++; } private void travelToMidRight(final String input) { // this currently points to a coma or bracket midRight++; while (Character.isWhitespace(input.charAt(midRight))) { midRight++; } } private void travelToEnd(final String input) { int groupDepth = 0; while (!((input.charAt(end) == ',' && groupDepth == 0) || (isRightLimiter(input.charAt(end)) && groupDepth == 0))) { if (isLeftLimiter(input.charAt(end))) { groupDepth++; } if (isRightLimiter(input.charAt(end))) { groupDepth--; } end++; } end--; while (Character.isWhitespace(input.charAt(end))) { end--; } end++; } private boolean isLeftLimiter(final char c) { return c == '(' || c == '<' || c == '[' || c == '{'; } private boolean isRightLimiter(final char c) { return c == ')' || c == '>' || c == ']' || c == '}'; }
Test for this:
public static class Stuff extends SimpleTestVectors { @Override protected Object[][] generateTestVectors() { return new Object[][] { //"( final Broad broadcast, final Frame frame,final Logger logger)" //(Logger logger, Frame< String, Integer > frame, final @Named ( "quickDepositStatusIntentTranslator" ) Broad broadcast) {"( ", "final Broad broadcast, final Frame frame,final Logger logger)", "final Frame frame, final Broad broadcast"}, //, "( final Frame frame, final Broad broadcast,final Logger logger)" {"( final Broad br", "oadcast, final Frame frame,final Logger logger)", "final Frame frame, final Broad broadcast"}, //, "( final Frame frame, final Broad broadcast,final Logger logger)" {"( final Broad br", "oadcast, final Frame frame,final Logger logger)", "final Frame frame, final Broad broadcast"}, //, "( final Frame frame, final Broad broadcast,final Logger logger)" {"( final Broad broadcast", ", final Frame frame,final Logger logger)", "final Frame frame, final Broad broadcast"}, //, "( final Frame frame, final Broad broadcast,final Logger logger)" {"( final Broad broadcast,", " final Frame frame,final Logger logger)", "final Logger logger,final Frame frame"}, //, "( final Broad broadcast, final Logger logger,final Frame frame)" {"( final Broad broadcast, final Fr", "ame frame,final Logger logger)", "final Logger logger,final Frame frame"}, //, "( final Broad broadcast, final Logger logger,final Frame frame)" {"( final Broad broadcast, final Frame frame,final ", "Logger logger)", null}, //, "( final Broad broadcast, final Frame frame,final Logger logger)" {"(Broad broadcast, Fr", "ame frame , Logger logger )", "Logger logger , Frame frame"}, //, "(final Broad broadcast, final Logger logger , final Frame frame )" {"(final @", "Named ( \"quickDepositStatusIntentTranslator\" ) Broad broadcast, Frame< String, Integer > frame, Logger logger)", "Frame< String, Integer > frame, final @Named ( \"quickDepositStatusIntentTranslator\" ) Broad broadcast"}, //"(Frame< String, Integer > frame, final @Named ( \"quickDepositStatusIntentTranslator\" ) Broad broadcast, Logger logger)"}, {"(final @Named ( \"quickDepositS", "tatusIntentTranslator\" ) Broad broadcast, Frame< String, Integer > frame, Logger logger)", null}, //"(final @Named ( \"quickDepositStatusIntentTranslator\" ) Broad broadcast, Frame< String, Integer > frame, Logger logger)"}, {"(final @Named ( \"quickDepositStatusIntentTranslator\" ", ") Broad broadcast, Frame< String, Integer > frame, Logger logger)", null}, //"(final @Named ( \"quickDepositStatusIntentTranslator\" ) Broad broadcast, Frame< String, Integer > frame, Logger logger)"}, {"(final @Named ( \"quickDepositStatusIntentTranslator\" ) Broad", " broadcast, Frame< String, Integer > frame, Logger logger)", "Frame< String, Integer > frame, final @Named ( \"quickDepositStatusIntentTranslator\" ) Broad broadcast"}, //"(Frame< String, Integer > frame, final @Named ( \"quickDepositStatusIntentTranslator\" ) Broad broadcast, Logger logger)"}, {"(final @Named ( \"quickDepositStatusIntentTranslator\" ) Broad broadcast, ", "Frame< String, Integer > frame, Logger logger)", "Logger logger, Frame< String, Integer > frame"}, //"(final @Named ( \"quickDepositStatusIntentTranslator\" ) Broad broadcast, Logger logger, Frame< String, Integer > frame)"}, {"(final @Named ( \"quickDepositStatusIntentTranslator\" ) Broad broadcast, Frame< S", "tring, Integer > frame, Logger logger)", "Integer, String"}, //"(final @Named ( \"quickDepositStatusIntentTranslator\" ) Broad broadcast, Frame< Integer, String > frame, Logger logger)"}, {"(final @Named ( \"", " foo () \" , \" bar () \" ) Broad broadcast, Frame< String, Integer > frame, Logger logger)", "\" bar () \" , \" foo () \""}, //"(final @Named( \" bar () \" , \" foo () \" ) Broad broadcast, Frame< String, Integer > frame, Logger logger)""}, }; } } @Test @DataSet(testData = Stuff.class) public void dummy_FIXME() throws InvalidDataSetException { // init // run final String result = sut.doTheFlop(rule.getString(0) + rule.getString(1), rule.getString(0).length()); // verify assertThat(result, equalTo(rule.getString(2))); }
And the final Groovyscript refined:
import com.intellij.openapi.actionSystem.AnActionEvent import com.intellij.openapi.application.ApplicationManager import com.intellij.openapi.editor.ScrollType import com.intellij.openapi.editor.SelectionModel import com.intellij.openapi.util.TextRange import static liveplugin.PluginUtil.* import static liveplugin.PluginUtil.showInConsole import static liveplugin.PluginUtil.showInConsole import static liveplugin.PluginUtil.showInConsole registerAction("swap to right", "alt ctrl L") { AnActionEvent event -> def editor = currentEditorIn(event.project) def input = editor.getDocument().getChars() def caretModel = editor.getCaretModel() def selectionModel = editor.getSelectionModel() int start = caretModel.offset - 1; if (start < 0) return; int mid = start + 1; int end; int midLeft; int midRight; //travelToBeginning int groupDepth = 0; while (!((input[start] == ',' && groupDepth == 0) || (isLeftLimiter(input[start]) && groupDepth == 0))) { if (isRightLimiter(input[start])) { groupDepth++; } if (isLeftLimiter(input[start])) { groupDepth--; } start--; } // fallback to first non-whitespace char start++; while (isWhitespace(input[start])) { start++; } //travelToMid groupDepth = 0; while (!((input[mid] == ',' && groupDepth == 0) || (isRightLimiter(input[mid]) && groupDepth == 0))) { if (isLeftLimiter(input[mid])) { groupDepth++; } if (isRightLimiter(input[mid])) { groupDepth--; } mid++; } // this is already the last element if (isRightLimiter(input[mid])) { return; } midLeft = mid; midRight = mid; //travelToMidLeft // this currently points to a coma or bracket midLeft--; while (isWhitespace(input[midLeft])) { midLeft--; } midLeft++; //travelToMidRight // this currently points to a coma or bracket midRight++; while (isWhitespace(input[midRight])) { midRight++; } //travelToEnd end = midRight; groupDepth = 0; while (!((input[end] == ',' && groupDepth == 0) || (isRightLimiter(input[end]) && groupDepth == 0))) { if (isLeftLimiter(input[end])) { groupDepth++; } if (isRightLimiter(input[end])) { groupDepth--; } end++; } // fallback to last non-whitespace char end--; while (isWhitespace(input[end])) { end--; } end++; // do the swap String text = editor.document.getText(new TextRange(start, end)) String swappedText = text.substring(midRight - start, end - start) + text.substring(midLeft - start, midRight - start) + text.substring(0, midLeft - start) runDocumentWriteAction(event.project, editor.document, "Swap to right a list element", "Permute plugin (mnw)") { editor.document.replaceString(start, end, swappedText) } caretModel.moveToOffset(end) editor.scrollingModel.scrollToCaret(ScrollType.MAKE_VISIBLE) } registerAction("swap to left", "alt ctrl K") { AnActionEvent event -> def editor = currentEditorIn(event.project) def input = editor.getDocument().getChars() def caretModel = editor.getCaretModel() def selectionModel = editor.getSelectionModel() int start = caretModel.offset - 1; if (start < 0) return; int carret = start; int lastComa; int mid; int end; int midLeft; int midRight; //travelToBeginning int groupDepth = 0; while (!(isLeftLimiter(input[start]) && groupDepth == 0)) { if (isRightLimiter(input[start])) { groupDepth++; } if (isLeftLimiter(input[start])) { groupDepth--; } start--; } lastComa = start; //travel backwards ToMid from start keeping track if we find coma (only the last is important) groupDepth = 0; mid = start + 1; while (mid <= carret) { if (isLeftLimiter(input[mid])) { groupDepth++; } if (isRightLimiter(input[mid])) { groupDepth--; } if (input[mid] == ',' && groupDepth == 0) { start = lastComa; lastComa = mid; } mid++; } // this is already the first element if (lastComa == start) { return; } // skip whitespaces from start start++; while (isWhitespace(input[start])) { start++; } midLeft = lastComa; midRight = lastComa; // skipp whitespaces around the coma // this currently points to a coma or bracket midLeft--; while (isWhitespace(input[midLeft])) { midLeft--; } midLeft++; // this currently points to a coma or bracket midRight++; while (isWhitespace(input[midRight])) { midRight++; } //travelToEnd end = midRight; groupDepth = 0; while (!((input[end] == ',' && groupDepth == 0) || (isRightLimiter(input[end]) && groupDepth == 0))) { if (isLeftLimiter(input[end])) { groupDepth++; } if (isRightLimiter(input[end])) { groupDepth--; } end++; } // skip ending whitespaces from end end--; while (isWhitespace(input[end])) { end--; } end++; // do the swap String text = editor.document.getText(new TextRange(start, end)) String swappedText = text.substring(midRight - start, end - start) + text.substring(midLeft - start, midRight - start) + text.substring(0, midLeft - start) runDocumentWriteAction(event.project, editor.document, "Swap to left a list element", "Permute plugin (mnw)") { editor.document.replaceString(start, end, swappedText) } caretModel.moveToOffset(start + end - midRight) editor.scrollingModel.scrollToCaret(ScrollType.MAKE_VISIBLE) } private boolean isWhitespace(char charAt) { return charAt == " " || charAt == "\t" || charAt == "\n" || charAt == "\r" || charAt == "\f"; } private boolean isLeftLimiter(final char c) { return c == '(' || c == '<' || c == '[' || c == '{'; } private boolean isRightLimiter(final char c) { return c == ')' || c == '>' || c == ']' || c == '}'; } show("permute Loaded")
No comments:
Post a Comment