diff --git a/README.md b/README.md index fd3dfe2..0b11b6d 100644 --- a/README.md +++ b/README.md @@ -7,10 +7,10 @@ Allows to disable some conversions or change their order in the preferences. Please see this example for a demonstration: -![Demonstration](https://raw.githubusercontent.com/netnexus/camelcaseplugin/assets/example.gif) +![Demonstration](https://github.com/user-attachments/assets/72001e9b-402d-4971-8a82-3375c70d858d) ## Install Use your IDE. Preferences/Plugins/Browse repositories and search for "camelcase". ## Build -Just clone this repo and open the project it in IntelliJ IDEA. \ No newline at end of file +Just clone this repo and open the project it in IntelliJ IDEA. diff --git a/src/de/netnexus/CamelCasePlugin/CamelCaseConfig.java b/src/de/netnexus/CamelCasePlugin/CamelCaseConfig.java index 058f66d..e48c346 100644 --- a/src/de/netnexus/CamelCasePlugin/CamelCaseConfig.java +++ b/src/de/netnexus/CamelCasePlugin/CamelCaseConfig.java @@ -28,7 +28,9 @@ public class CamelCaseConfig implements PersistentStateComponent extends EditorActionHandler { CamelCaseEditorActionHandler() { - super(true); - } - - private static void replaceText(final Editor editor, final String replacement) { - - try { - WriteAction.run((ThrowableRunnable) () -> { - int start = editor.getSelectionModel().getSelectionStart(); - EditorModificationUtil.insertStringAtCaret(editor, replacement); - editor.getSelectionModel().setSelection(start, start + replacement.length()); - }); - } catch (Throwable ignored) { - - } + super(false); } @Override protected final void doExecute(@NotNull final Editor editor, @Nullable final Caret caret, final DataContext dataContext) { - final Pair additionalParameter = beforeWriteAction(editor); if (!additionalParameter.first) { return; @@ -57,81 +41,102 @@ public void executeWriteAction(@NotNull Editor editor1, @Nullable Caret caret1, @NotNull private Pair beforeWriteAction(Editor editor) { - String text = editor.getSelectionModel().getSelectedText(); + String[] conversionList = ConversionList.toArray(new String[0]); + String[] activeConversionList = ConversionList.toArray(new String[0]); + Project project = editor.getProject(); + if (project != null) { + CamelCaseConfig config = CamelCaseConfig.getInstance(project); + if (config != null && (config.getcb1State() || config.getcb2State() || config.getcb3State() || config.getcb4State() || config.getcb5State() || config.getcb6State())) { + activeConversionList = config.getActiveModel(); + } + } + + // Determine the target case type for the selected text(s). + // If all selected text with the same case type, target is set to the next case type in activeConversionList. + // Otherwise, target is set to the case which at last of selected text case type. + String target; + { + String commonCaseType = null; + String lastCaseType = null; + int lastIdx = -1; + for (Caret caret : editor.getCaretModel().getAllCarets()) { + String caseType = CaseType(selectedText(editor, caret)); + commonCaseType = caseType; + int idx = Arrays.asList(conversionList).indexOf(caseType); + if (idx > lastIdx) { + lastCaseType = caseType; + lastIdx = idx; + } + } + if (lastCaseType == null) { + assert commonCaseType != null; + target = getNext(CaseType(commonCaseType), conversionList); + } else { + target = getNext(lastCaseType, conversionList); + } + + // Make sure target is in activeConversionList + for (int i = 0; i < conversionList.length; i++) { + int idx = Arrays.asList(activeConversionList).indexOf(target); + if (idx != -1) { + break; + } + target = getNext(target, conversionList); + } + } + + Document document = editor.getDocument(); + for (Caret caret : editor.getCaretModel().getAllCarets()) { + String selected = caret.getSelectedText(); + if (selected != null && !selected.isEmpty()) { + String newText = Conversion.transform(selected, target); + + Runnable runnable = () -> document.replaceString(caret.getSelectionStart(), caret.getSelectionEnd(), newText); + ApplicationManager.getApplication().runWriteAction(getRunnableWrapper(project, runnable)); + } + } + + return continueExecution(); + } + + private String selectedText(Editor editor, Caret caret) { + String text = caret.getSelectedText(); if (text == null || text.isEmpty()) { - editor.getSelectionModel().selectWordAtCaret(true); + int start = caret.getOffset(); + int end = start; boolean moveLeft = true; boolean moveRight = true; - int start = editor.getSelectionModel().getSelectionStart(); - int end = editor.getSelectionModel().getSelectionEnd(); - Pattern p = Pattern.compile("[^A-z0-9.\\-]"); + Pattern p = Pattern.compile("[^A-Za-z0-9.\\-]"); // move caret left - while (moveLeft && start != 0) { + while (moveLeft && start > 0) { start--; - EditorActionUtil.moveCaret(editor.getCaretModel().getCurrentCaret(), start, true); - Matcher m = p.matcher(Objects.requireNonNull(editor.getSelectionModel().getSelectedText())); - if (m.find()) { + caret.setSelection(start, end); + String selected = caret.getSelectedText(); + if (selected == null || p.matcher(selected).find()) { start++; moveLeft = false; } } - editor.getSelectionModel().setSelection(end - 1, end); - // move caret right - while (moveRight && end != editor.getDocument().getTextLength()) { + while (moveRight && end < editor.getDocument().getTextLength()) { end++; - EditorActionUtil.moveCaret(editor.getCaretModel().getCurrentCaret(), end, true); - Matcher m = p.matcher(Objects.requireNonNull(editor.getSelectionModel().getSelectedText())); - if (m.find()) { + caret.setSelection(start, end); + String selected = caret.getSelectedText(); + if (selected == null || p.matcher(selected).find()) { end--; moveRight = false; } } - editor.getSelectionModel().setSelection(start, end); - text = editor.getSelectionModel().getSelectedText(); - } - Project project = editor.getProject(); - - String newText; - if (project != null) { - CamelCaseConfig config = CamelCaseConfig.getInstance(project); - if (config != null && (config.getcb1State() || config.getcb2State() || config.getcb3State() || config.getcb4State() || config.getcb5State() || config.getcb6State())) { - newText = Conversion.transform(text, config.getcb7State(), // pascal case with space - config.getcb6State(), // space case - config.getcb1State(), // kebab case - config.getcb2State(), // upper snake case - config.getcb3State(), // pascal case - config.getcb4State(), // camel case - config.getcb5State(), // lower snake case - config.getmodel()); - } else { - newText = this.runWithoutConfig(text); - } - } else { - newText = this.runWithoutConfig(text); + caret.setSelection(start, end); + text = caret.getSelectedText(); } - final Editor fEditor = editor; - final String fReplacement = newText; - Runnable runnable = () -> CamelCaseEditorActionHandler.replaceText(fEditor, fReplacement); - ApplicationManager.getApplication().runWriteAction(getRunnableWrapper(fEditor.getProject(), runnable)); - return continueExecution(); + return text; } - private String runWithoutConfig(String text) { - String[] conversionList = {"kebab-case", "SNAKE_CASE", "CamelCase", "camelCase", "snake_case", "space case", "Camel Case"}; - return Conversion.transform(text, true, // pascal case with space - true, // space case - true, // kebab case - true, // upper snake case - true, // pascal case - true, // camel case - true, // lower snake case - conversionList); - } private Pair continueExecution() { return new Pair<>(true, null); diff --git a/src/de/netnexus/CamelCasePlugin/Conversion.java b/src/de/netnexus/CamelCasePlugin/Conversion.java index cd38041..692c8b7 100644 --- a/src/de/netnexus/CamelCasePlugin/Conversion.java +++ b/src/de/netnexus/CamelCasePlugin/Conversion.java @@ -4,6 +4,8 @@ import org.jetbrains.annotations.NotNull; import java.util.Arrays; +import java.util.List; +import java.util.Map; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -12,132 +14,57 @@ class Conversion { - private static final String CONVERSION_SPACE_CASE = "space case"; - private static final String CONVERSION_KEBAB_CASE = "kebab-case"; - private static final String CONVERSION_UPPER_SNAKE_CASE = "SNAKE_CASE"; - private static final String CONVERSION_PASCAL_CASE = "CamelCase"; - private static final String CONVERSION_CAMEL_CASE = "camelCase"; - private static final String CONVERSION_PASCAL_CASE_SPACE = "Camel Case"; - private static final String CONVERSION_LOWER_SNAKE_CASE = "snake_case"; + static final String CONVERSION_SPACE_CASE = "space case"; + static final String CONVERSION_KEBAB_CASE = "kebab-case"; + static final String CONVERSION_UPPER_SNAKE_CASE = "SNAKE_CASE"; + static final String CONVERSION_PASCAL_CASE = "CamelCase"; + static final String CONVERSION_CAMEL_CASE = "camelCase"; + static final String CONVERSION_PASCAL_CASE_SPACE = "Camel Case"; + static final String CONVERSION_LOWER_SNAKE_CASE = "snake_case"; + static final List ConversionList = List.of(new String[]{"kebab-case", "SNAKE_CASE", "CamelCase", "camelCase", "snake_case", "space case", "Camel Case"}); @NotNull - static String transform(String text, - boolean usePascalCaseWithSpace, - boolean useSpaceCase, - boolean useKebabCase, - boolean useUpperSnakeCase, - boolean usePascalCase, - boolean useCamelCase, - boolean useLowerSnakeCase, - String[] conversionList) { - String newText, appendText = ""; - boolean repeat = true; - int iterations = 0; - String next = null; + static String transform(String text, String target) { + String appendText = ""; Pattern p = Pattern.compile("^\\W+"); Matcher m = p.matcher(text); if (m.find()) { appendText = m.group(0); } - //remove all special chars - text = text.replaceAll("^\\W+", ""); - do { - newText = text; - boolean isLowerCase = text.equals(text.toLowerCase()); - boolean isUpperCase = text.equals(text.toUpperCase()); + int iterations = 0; + text = text.replaceAll("^\\W+", ""); //remove all special chars + String current = CaseType(text); + + int idxCurrent = ConversionList.indexOf(current); + int idxObjectCase = ConversionList.indexOf(target); + int repeatedTimes = (idxObjectCase + ConversionList.size() - idxCurrent) % ConversionList.size(); - if (isLowerCase && text.contains("_")) { + while (iterations++ < repeatedTimes) { + switch (ConversionList.get(idxCurrent)) { // snake_case to space case - if (next == null) { - next = getNext(CONVERSION_LOWER_SNAKE_CASE, conversionList); - } else { - if (next.equals(CONVERSION_SPACE_CASE)) { - repeat = !useSpaceCase; - next = getNext(CONVERSION_SPACE_CASE, conversionList); - } - } - newText = text.replace('_', ' '); - - } else if (isLowerCase && text.contains(" ")) { + case CONVERSION_LOWER_SNAKE_CASE -> text = text.replace('_', ' '); // space case to Camel Case - if (next == null) { - next = getNext(CONVERSION_SPACE_CASE, conversionList); - } else { - newText = WordUtils.capitalize(text); - if (next.equals(CONVERSION_PASCAL_CASE_SPACE)) { - repeat = !usePascalCaseWithSpace; - next = getNext(CONVERSION_PASCAL_CASE_SPACE, conversionList); - } - } - - } else if (isUpperCase(text.charAt(0)) && isLowerCase(text.charAt(1)) && text.contains(" ")) { + case CONVERSION_SPACE_CASE -> text = WordUtils.capitalize(text); // Camel Case to kebab-case - if (next == null) { - next = getNext(CONVERSION_PASCAL_CASE_SPACE, conversionList); - } else { - newText = text.toLowerCase().replace(' ', '-'); - if (next.equals(CONVERSION_KEBAB_CASE)) { - repeat = !useKebabCase; - next = getNext(CONVERSION_KEBAB_CASE, conversionList); - } - } - - } else if (isLowerCase && text.contains("-") || (isLowerCase && !text.contains(" "))) { + case CONVERSION_PASCAL_CASE_SPACE -> text = text.toLowerCase().replace(' ', '-'); // kebab-case to SNAKE_CASE - if (next == null) { - next = getNext(CONVERSION_KEBAB_CASE, conversionList); - } else { - newText = text.replace('-', '_').toUpperCase(); - if (next.equals(CONVERSION_UPPER_SNAKE_CASE)) { - repeat = !useUpperSnakeCase; - next = getNext(CONVERSION_UPPER_SNAKE_CASE, conversionList); - } - } - - } else if ((isUpperCase && text.contains("_")) || (isLowerCase && !text.contains("_") && !text.contains(" ")) || (isUpperCase && !text.contains(" "))) { + case CONVERSION_KEBAB_CASE -> text = text.replace('-', '_').toUpperCase(); // SNAKE_CASE to PascalCase - if (next == null) { - next = getNext(CONVERSION_UPPER_SNAKE_CASE, conversionList); - } else { - newText = Conversion.toCamelCase(text.toLowerCase()); - if (next.equals(CONVERSION_PASCAL_CASE)) { - repeat = !usePascalCase; - next = getNext(CONVERSION_PASCAL_CASE, conversionList); - } - } - - } else if (!isUpperCase && text.substring(0, 1).equals(text.substring(0, 1).toUpperCase()) && !text.contains("_")) { + case CONVERSION_UPPER_SNAKE_CASE -> text = Conversion.toCamelCase(text.toLowerCase()); // PascalCase to camelCase - if (next == null) { - next = getNext(CONVERSION_PASCAL_CASE, conversionList); - } else { - newText = text.substring(0, 1).toLowerCase() + text.substring(1); - if (next.equals(CONVERSION_CAMEL_CASE)) { - repeat = !useCamelCase; - next = getNext(CONVERSION_CAMEL_CASE, conversionList); - } - } - } else { + case CONVERSION_PASCAL_CASE -> text = text.substring(0, 1).toLowerCase() + text.substring(1); // camelCase to snake_case - if (next == null) { - next = getNext(CONVERSION_CAMEL_CASE, conversionList); - } else { - newText = Conversion.toSnakeCase(text); - if (next.equals(CONVERSION_LOWER_SNAKE_CASE)) { - repeat = !useLowerSnakeCase; - next = getNext(CONVERSION_LOWER_SNAKE_CASE, conversionList); - } - } + case CONVERSION_CAMEL_CASE -> text = Conversion.toSnakeCase(text); + default -> iterations = 8; } - if (iterations++ > 20) { - repeat = false; - } - text = newText; - } while (repeat); - return appendText + newText; + + idxCurrent = (idxCurrent+1)%ConversionList.size(); + } + + return appendText + text; } /** @@ -147,7 +74,7 @@ static String transform(String text, * @param conversions Array of strings * @return next conversion */ - private static String getNext(String conversion, String[] conversions) { + static String getNext(String conversion, String[] conversions) { int index; index = Arrays.asList(conversions).indexOf(conversion) + 1; if (index < conversions.length) { @@ -187,7 +114,7 @@ private static String toCamelCase(String in) { StringBuilder camelCased = new StringBuilder(); String[] tokens = in.split("_"); for (String token : tokens) { - if (token.length() >= 1) { + if (!token.isEmpty()) { camelCased.append(token.substring(0, 1).toUpperCase()).append(token.substring(1)); } else { camelCased.append("_"); @@ -195,4 +122,31 @@ private static String toCamelCase(String in) { } return camelCased.toString(); } + + /** + * Get a string case type + * + * @param text snake_case String + * @return CaseType string + */ + static String CaseType(String text) { + boolean isLowerCase = text.equals(text.toLowerCase()); + boolean isUpperCase = text.equals(text.toUpperCase()); + + if (isLowerCase && text.contains("_")) { + return CONVERSION_LOWER_SNAKE_CASE; + } else if (isLowerCase && text.contains(" ")) { + return CONVERSION_SPACE_CASE; + } else if (isUpperCase(text.charAt(0)) && text.contains(" ")) { + return CONVERSION_PASCAL_CASE_SPACE; + } else if (isLowerCase && text.contains("-") || (isLowerCase && !text.contains(" "))) { + return CONVERSION_KEBAB_CASE; + } else if ((isUpperCase && text.contains("_")) || (isLowerCase && !text.contains("_") && !text.contains(" ")) || (isUpperCase && !text.contains(" "))) { + return CONVERSION_UPPER_SNAKE_CASE; + } else if (!isUpperCase && isUpperCase(text.charAt(0)) && !text.contains("_") && !text.contains(" ")) { + return CONVERSION_PASCAL_CASE; + } else { + return CONVERSION_CAMEL_CASE; + } + } } diff --git a/src/de/netnexus/CamelCasePlugin/OptionGui.java b/src/de/netnexus/CamelCasePlugin/OptionGui.java index 66a2627..70666b5 100644 --- a/src/de/netnexus/CamelCasePlugin/OptionGui.java +++ b/src/de/netnexus/CamelCasePlugin/OptionGui.java @@ -8,8 +8,9 @@ import java.awt.*; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; -import java.awt.event.FocusAdapter; -import java.awt.event.FocusEvent; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; public class OptionGui { @@ -107,6 +108,33 @@ public void apply() { sValues[index] = oValues[index].toString(); } mConfig.setListModel(sValues); + + String[] activeModel = mConfig.model; + List list = new ArrayList<>(Arrays.asList(activeModel)); + + if (!mConfig.getcb1State()) { + list.removeIf(e -> e.equals("kebab-case")); + } + if (!mConfig.getcb2State()) { + list.removeIf(e -> e.equals("SNAKE_CASE")); + } + if (!mConfig.getcb3State()) { + list.removeIf(e -> e.equals("CamelCase")); + } + if (!mConfig.getcb4State()) { + list.removeIf(e -> e.equals("camelCase")); + } + if (!mConfig.getcb5State()) { + list.removeIf(e -> e.equals("snake_case")); + } + if (!mConfig.getcb6State()) { + list.removeIf(e -> e.equals("space case")); + } + if (!mConfig.getcb7State()) { + list.removeIf(e -> e.equals("Camel Case")); + } + + mConfig.setActiveModel(list.toArray(new String[0])); } {