From 489a34f32c60e15ccd0e18c92a962d863da4b2ef Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 21 Mar 2026 13:24:18 +0000 Subject: [PATCH 01/12] Initial plan From abe70f8d0cfd039191baaf6bbdd719702e2fc8ad Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 21 Mar 2026 13:27:09 +0000 Subject: [PATCH 02/12] Initial plan From d62e0bf4d26e1f221f3d574dc57b71f118702aba Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 21 Mar 2026 13:30:07 +0000 Subject: [PATCH 03/12] Fix IncorrectOperationException: check myDisposed before async PsiTreeChangeListener registration Co-authored-by: SeeSharpSoft <3205800+SeeSharpSoft@users.noreply.github.com> Agent-Logs-Url: https://github.com/SeeSharpSoft/intellij-csv-validator/sessions/0c0b9df9-c4a1-4307-ab7f-554c71abc7d3 --- gradlew | 0 .../intellij/plugins/csv/editor/table/CsvTableModelBase.java | 4 +++- 2 files changed, 3 insertions(+), 1 deletion(-) mode change 100644 => 100755 gradlew diff --git a/gradlew b/gradlew old mode 100644 new mode 100755 diff --git a/src/main/java/net/seesharpsoft/intellij/plugins/csv/editor/table/CsvTableModelBase.java b/src/main/java/net/seesharpsoft/intellij/plugins/csv/editor/table/CsvTableModelBase.java index 3ef24767..298e9408 100644 --- a/src/main/java/net/seesharpsoft/intellij/plugins/csv/editor/table/CsvTableModelBase.java +++ b/src/main/java/net/seesharpsoft/intellij/plugins/csv/editor/table/CsvTableModelBase.java @@ -20,6 +20,7 @@ public class CsvTableModelBase implements CsvTableModel { private final T myPsiFileHolder; + private volatile boolean myDisposed = false; private int myCachedRowCount = -1; private int myCachedColumnCount = -1; @@ -55,7 +56,7 @@ protected void addPsiTreeChangeListener() { .nonBlocking(this::getPsiFile) .coalesceBy(this) .finishOnUiThread(ModalityState.any(), pf -> { - if (pf == null) return; + if (pf == null || myDisposed) return; PsiManager mgr = pf.getManager(); if (mgr == null) return; mgr.addPsiTreeChangeListener(myPsiTreeChangeListener, myPsiFileHolder); @@ -70,6 +71,7 @@ protected void addPsiTreeChangeListener() { @Override public void dispose() { + myDisposed = true; CsvTableModel.super.dispose(); myPsiTreeUpdater.dispose(); } From 669991538430bb5687ab298f03cfa4e2e3995e98 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 21 Mar 2026 13:37:21 +0000 Subject: [PATCH 04/12] Fix Invalid PSI Element exceptions in annotator, intentions, and inspections Co-authored-by: SeeSharpSoft <3205800+SeeSharpSoft@users.noreply.github.com> Agent-Logs-Url: https://github.com/SeeSharpSoft/intellij-csv-validator/sessions/99215958-2b79-4820-874d-b150f657d4c1 --- CHANGELOG.md | 2 +- gradlew | 0 .../intellij/plugins/csv/editor/CsvAnnotator.java | 3 +++ .../csv/inspection/CsvValidationInspection.java | 3 +++ .../plugins/csv/intention/CsvIntentionAction.java | 1 + .../intention/CsvShiftColumnLeftIntentionAction.java | 10 +++++++++- .../intention/CsvShiftColumnRightIntentionAction.java | 10 +++++++++- 7 files changed, 26 insertions(+), 3 deletions(-) mode change 100644 => 100755 gradlew diff --git a/CHANGELOG.md b/CHANGELOG.md index b6700a9b..cb4dc477 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,7 +10,7 @@ ### Fixed -## 4.2.0 - Jan 26, 2026 +- PluginException: Invalid PSI Element CsvFile when file is invalidated during annotation, intention actions, or inspection fixes ### Fixed diff --git a/gradlew b/gradlew old mode 100644 new mode 100755 diff --git a/src/main/java/net/seesharpsoft/intellij/plugins/csv/editor/CsvAnnotator.java b/src/main/java/net/seesharpsoft/intellij/plugins/csv/editor/CsvAnnotator.java index 1e727f4f..4365820b 100644 --- a/src/main/java/net/seesharpsoft/intellij/plugins/csv/editor/CsvAnnotator.java +++ b/src/main/java/net/seesharpsoft/intellij/plugins/csv/editor/CsvAnnotator.java @@ -36,6 +36,9 @@ public void annotate(@NotNull final PsiElement element, @NotNull final Annotatio } CsvFile csvFile = (CsvFile) element.getContainingFile(); + if (!csvFile.isValid()) { + return; + } if (handleSeparatorElement(element, holder, elementType, csvFile)) { return; } diff --git a/src/main/java/net/seesharpsoft/intellij/plugins/csv/inspection/CsvValidationInspection.java b/src/main/java/net/seesharpsoft/intellij/plugins/csv/inspection/CsvValidationInspection.java index 446ba6df..4aab94b8 100644 --- a/src/main/java/net/seesharpsoft/intellij/plugins/csv/inspection/CsvValidationInspection.java +++ b/src/main/java/net/seesharpsoft/intellij/plugins/csv/inspection/CsvValidationInspection.java @@ -120,6 +120,7 @@ public String getFamilyName() { @Override public void applyFix(@NotNull Project project, @NotNull ProblemDescriptor descriptor) { PsiElement element = descriptor.getPsiElement(); + if (element == null || !element.isValid()) return; Document document = PsiDocumentManager.getInstance(project).getDocument(element.getContainingFile()); if (document == null) return; @@ -147,6 +148,7 @@ public String getFamilyName() { public void applyFix(@NotNull Project project, @NotNull ProblemDescriptor descriptor) { try { PsiElement element = descriptor.getPsiElement(); + if (element == null || !element.isValid()) return; Document document = PsiDocumentManager.getInstance(project).getDocument(element.getContainingFile()); if (document == null) return; @@ -168,6 +170,7 @@ public String getFamilyName() { public void applyFix(@NotNull Project project, @NotNull ProblemDescriptor descriptor) { try { PsiElement element = descriptor.getPsiElement(); + if (element == null || !element.isValid()) return; Document document = PsiDocumentManager.getInstance(project).getDocument(element.getContainingFile()); if (document == null) return; diff --git a/src/main/java/net/seesharpsoft/intellij/plugins/csv/intention/CsvIntentionAction.java b/src/main/java/net/seesharpsoft/intellij/plugins/csv/intention/CsvIntentionAction.java index a55df139..4bfe89de 100644 --- a/src/main/java/net/seesharpsoft/intellij/plugins/csv/intention/CsvIntentionAction.java +++ b/src/main/java/net/seesharpsoft/intellij/plugins/csv/intention/CsvIntentionAction.java @@ -26,6 +26,7 @@ public String getFamilyName() { public boolean isAvailable(@NotNull Project project, Editor editor, @Nullable PsiElement element) { final PsiFile containingFile = element == null ? null : element.getContainingFile(); return containingFile != null + && containingFile.isValid() && containingFile.getLanguage().isKindOf(CsvLanguage.INSTANCE) && PsiDocumentManager.getInstance(project).getDocument(containingFile) != null; } diff --git a/src/main/java/net/seesharpsoft/intellij/plugins/csv/intention/CsvShiftColumnLeftIntentionAction.java b/src/main/java/net/seesharpsoft/intellij/plugins/csv/intention/CsvShiftColumnLeftIntentionAction.java index bcac58c5..593e4c52 100644 --- a/src/main/java/net/seesharpsoft/intellij/plugins/csv/intention/CsvShiftColumnLeftIntentionAction.java +++ b/src/main/java/net/seesharpsoft/intellij/plugins/csv/intention/CsvShiftColumnLeftIntentionAction.java @@ -3,6 +3,7 @@ import com.intellij.openapi.editor.Editor; import com.intellij.openapi.project.Project; import com.intellij.psi.PsiElement; +import com.intellij.psi.PsiFile; import com.intellij.util.IncorrectOperationException; import net.seesharpsoft.intellij.plugins.csv.CsvColumnInfo; import net.seesharpsoft.intellij.plugins.csv.CsvColumnInfoMap; @@ -18,7 +19,14 @@ public CsvShiftColumnLeftIntentionAction() { @Override public void invoke(@NotNull Project project, Editor editor, @NotNull final PsiElement psiElement) throws IncorrectOperationException { - CsvFile csvFile = (CsvFile) psiElement.getContainingFile(); + PsiFile containingFile = psiElement.getContainingFile(); + if (!(containingFile instanceof CsvFile)) { + return; + } + CsvFile csvFile = (CsvFile) containingFile; + if (!csvFile.isValid()) { + return; + } PsiElement element = CsvHelper.getParentFieldElement(psiElement); diff --git a/src/main/java/net/seesharpsoft/intellij/plugins/csv/intention/CsvShiftColumnRightIntentionAction.java b/src/main/java/net/seesharpsoft/intellij/plugins/csv/intention/CsvShiftColumnRightIntentionAction.java index a13f340b..fc1fd0cb 100644 --- a/src/main/java/net/seesharpsoft/intellij/plugins/csv/intention/CsvShiftColumnRightIntentionAction.java +++ b/src/main/java/net/seesharpsoft/intellij/plugins/csv/intention/CsvShiftColumnRightIntentionAction.java @@ -3,6 +3,7 @@ import com.intellij.openapi.editor.Editor; import com.intellij.openapi.project.Project; import com.intellij.psi.PsiElement; +import com.intellij.psi.PsiFile; import com.intellij.util.IncorrectOperationException; import net.seesharpsoft.intellij.plugins.csv.CsvColumnInfo; import net.seesharpsoft.intellij.plugins.csv.CsvColumnInfoMap; @@ -19,7 +20,14 @@ public CsvShiftColumnRightIntentionAction() { @Override public void invoke(@NotNull Project project, Editor editor, @NotNull PsiElement psiElement) throws IncorrectOperationException { - CsvFile csvFile = (CsvFile) psiElement.getContainingFile(); + PsiFile containingFile = psiElement.getContainingFile(); + if (!(containingFile instanceof CsvFile)) { + return; + } + CsvFile csvFile = (CsvFile) containingFile; + if (!csvFile.isValid()) { + return; + } PsiElement element = CsvHelper.getParentFieldElement(psiElement); From 72ed1d3aefaa26791de027dd2226be3f37aba3aa Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 29 Mar 2026 21:53:54 +0000 Subject: [PATCH 05/12] Fix CHANGELOG: add 4.2.1 section instead of modifying Unreleased Agent-Logs-Url: https://github.com/SeeSharpSoft/intellij-csv-validator/sessions/c855aa2a-d880-480c-95ec-a63bd8864a91 Co-authored-by: SeeSharpSoft <3205800+SeeSharpSoft@users.noreply.github.com> --- CHANGELOG.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index cb4dc477..26032bc4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,8 +10,14 @@ ### Fixed +## 4.2.1 - Mar 29, 2026 + +### Fixed + - PluginException: Invalid PSI Element CsvFile when file is invalidated during annotation, intention actions, or inspection fixes +## 4.2.0 - Jan 26, 2026 + ### Fixed - Compatibility verification issues From 96c8a547cb533cde3389169498250a66ba6d8f1c Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 29 Mar 2026 22:01:25 +0000 Subject: [PATCH 06/12] Update CHANGELOG.md: add IncorrectOperationException fix to 4.2.1 section Agent-Logs-Url: https://github.com/SeeSharpSoft/intellij-csv-validator/sessions/02fb7c80-41fd-4561-ac47-12924be00c20 Co-authored-by: SeeSharpSoft <3205800+SeeSharpSoft@users.noreply.github.com> --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 26032bc4..3a4c4788 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,7 @@ ### Fixed - PluginException: Invalid PSI Element CsvFile when file is invalidated during annotation, intention actions, or inspection fixes +- IncorrectOperationException: parent CsvTableEditorSwing already disposed when async PsiTreeChangeListener registration completes ## 4.2.0 - Jan 26, 2026 From fee81b8dd4af58530f6129200ee5c5688fa30de4 Mon Sep 17 00:00:00 2001 From: GeeK Date: Sat, 14 Mar 2026 10:35:13 -0400 Subject: [PATCH 07/12] Fix: Modified `CsvPlugin.openLink` to use `executeOnPooledThread` for setting dialogs --- CHANGELOG.md | 1 + .../intellij/plugins/csv/CsvPlugin.java | 17 +++++++++-------- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3a4c4788..bf4f547b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,7 @@ - PluginException: Invalid PSI Element CsvFile when file is invalidated during annotation, intention actions, or inspection fixes - IncorrectOperationException: parent CsvTableEditorSwing already disposed when async PsiTreeChangeListener registration completes +- Modified `CsvPlugin.openLink` to use `executeOnPooledThread` for setting dialogs #953 ## 4.2.0 - Jan 26, 2026 diff --git a/src/main/java/net/seesharpsoft/intellij/plugins/csv/CsvPlugin.java b/src/main/java/net/seesharpsoft/intellij/plugins/csv/CsvPlugin.java index ad399bc6..4dcdf6d9 100644 --- a/src/main/java/net/seesharpsoft/intellij/plugins/csv/CsvPlugin.java +++ b/src/main/java/net/seesharpsoft/intellij/plugins/csv/CsvPlugin.java @@ -23,14 +23,15 @@ public class CsvPlugin implements ProjectActivity, DumbAware { private static void openLink(Project project, String link) { if (project.isDisposed()) return; - ApplicationManager.getApplication().invokeLater(() -> - { - if (link.startsWith("#")) { - ShowSettingsUtil.getInstance().showSettingsDialog(project, link.substring(1)); - } else { - BrowserUtil.browse(link, project); - } - }); + if (link.startsWith("#")) { + ApplicationManager.getApplication().executeOnPooledThread(() -> + ShowSettingsUtil.getInstance().showSettingsDialog(project, link.substring(1)) + ); + } else { + ApplicationManager.getApplication().invokeLater(() -> + BrowserUtil.browse(link, project) + ); + } } public static void doAsyncProjectMaintenance(@NotNull Project project) { From 2bc27ead64c8848d3a919a952039cec0e2e4ff59 Mon Sep 17 00:00:00 2001 From: GeeK Date: Sat, 14 Mar 2026 10:40:51 -0400 Subject: [PATCH 08/12] Fix: Modified `CsvEditorSettings.java` to ensure all getter methods access the internal `OptionSet` directly instead of calling `getState()` --- CHANGELOG.md | 1 + .../csv/settings/CsvEditorSettings.java | 40 +++++++++---------- 2 files changed, 20 insertions(+), 21 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index bf4f547b..c7179f1f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,6 +17,7 @@ - PluginException: Invalid PSI Element CsvFile when file is invalidated during annotation, intention actions, or inspection fixes - IncorrectOperationException: parent CsvTableEditorSwing already disposed when async PsiTreeChangeListener registration completes - Modified `CsvPlugin.openLink` to use `executeOnPooledThread` for setting dialogs #953 +- Modified `CsvEditorSettings.java` to ensure all getter methods access the internal `OptionSet` directly instead of calling `getState()` #954 ## 4.2.0 - Jan 26, 2026 diff --git a/src/main/java/net/seesharpsoft/intellij/plugins/csv/settings/CsvEditorSettings.java b/src/main/java/net/seesharpsoft/intellij/plugins/csv/settings/CsvEditorSettings.java index f42b3023..1fc87bcd 100644 --- a/src/main/java/net/seesharpsoft/intellij/plugins/csv/settings/CsvEditorSettings.java +++ b/src/main/java/net/seesharpsoft/intellij/plugins/csv/settings/CsvEditorSettings.java @@ -150,7 +150,7 @@ public void loadState(@NotNull OptionSet state) { /*********** Settings section **********/ public boolean isCaretRowShown() { - return getState().CARET_ROW_SHOWN; + return this.myOptions.CARET_ROW_SHOWN; } public void setCaretRowShown(boolean caretRowShown) { @@ -158,7 +158,7 @@ public void setCaretRowShown(boolean caretRowShown) { } public boolean isUseSoftWraps() { - return getState().USE_SOFT_WRAP; + return this.myOptions.USE_SOFT_WRAP; } public void setUseSoftWraps(boolean useSoftWraps) { @@ -166,7 +166,7 @@ public void setUseSoftWraps(boolean useSoftWraps) { } public boolean isHighlightTabSeparator() { - return getState().HIGHTLIGHT_TAB_SEPARATOR; + return this.myOptions.HIGHTLIGHT_TAB_SEPARATOR; } public void setHighlightTabSeparator(boolean highlightTabSeparator) { @@ -174,7 +174,7 @@ public void setHighlightTabSeparator(boolean highlightTabSeparator) { } public boolean isShowInfoBalloon() { - return getState().SHOW_INFO_BALLOON; + return this.myOptions.SHOW_INFO_BALLOON; } public void setShowInfoBalloon(boolean showInfoBalloon) { @@ -182,9 +182,9 @@ public void setShowInfoBalloon(boolean showInfoBalloon) { } public Color getTabHighlightColor() { - String color = getState().TAB_HIGHLIGHT_COLOR; + String color = this.myOptions.TAB_HIGHLIGHT_COLOR; try { - return color == null || color.isEmpty() ? null : Color.decode(getState().TAB_HIGHLIGHT_COLOR); + return color == null || color.isEmpty() ? null : Color.decode(color); } catch (NumberFormatException exc) { return null; } @@ -209,7 +209,7 @@ public void setEditorPrio(EditorPrio editorPrio) { } public boolean showTableEditorInfoPanel() { - return getState().SHOW_TABLE_EDITOR_INFO_PANEL; + return this.myOptions.SHOW_TABLE_EDITOR_INFO_PANEL; } public void showTableEditorInfoPanel(boolean showInfoPanel) { @@ -217,9 +217,7 @@ public void showTableEditorInfoPanel(boolean showInfoPanel) { } public int getTableEditorRowHeight() { - // ensure the current state of row height fits the boundaries (which is checked in the setTableEditorRowHeight method - setTableEditorRowHeight(getState().TABLE_EDITOR_ROW_HEIGHT); - return getState().TABLE_EDITOR_ROW_HEIGHT; + return this.myOptions.TABLE_EDITOR_ROW_HEIGHT; } public void setTableEditorRowHeight(int rowHeight) { @@ -227,7 +225,7 @@ public void setTableEditorRowHeight(int rowHeight) { } public boolean isQuotingEnforced() { - return getState().QUOTING_ENFORCED; + return this.myOptions.QUOTING_ENFORCED; } public void setQuotingEnforced(boolean quotingEnforced) { @@ -235,7 +233,7 @@ public void setQuotingEnforced(boolean quotingEnforced) { } public boolean isZeroBasedColumnNumbering() { - return getState().ZERO_BASED_COLUMN_NUMBERING; + return this.myOptions.ZERO_BASED_COLUMN_NUMBERING; } public void setZeroBasedColumnNumbering(boolean zeroBasedColumnNumbering) { @@ -243,7 +241,7 @@ public void setZeroBasedColumnNumbering(boolean zeroBasedColumnNumbering) { } public int getTableAutoMaxColumnWidth() { - return getState().TABLE_AUTO_MAX_COLUMN_WIDTH; + return this.myOptions.TABLE_AUTO_MAX_COLUMN_WIDTH; } public void setTableAutoMaxColumnWidth(int tableAutoMaxColumnWidth) { @@ -251,7 +249,7 @@ public void setTableAutoMaxColumnWidth(int tableAutoMaxColumnWidth) { } public int getTableDefaultColumnWidth() { - return getState().TABLE_DEFAULT_COLUMN_WIDTH; + return this.myOptions.TABLE_DEFAULT_COLUMN_WIDTH; } public void setTableDefaultColumnWidth(int tableDefaultColumnWidth) { @@ -267,8 +265,8 @@ public void setDefaultEscapeCharacter(CsvEscapeCharacter defaultEscapeCharacter) } public CsvEscapeCharacter getDefaultEscapeCharacter() { - CsvEscapeCharacter csvValueSeparator = getState().DEFAULT_ESCAPE_CHARACTER; - return csvValueSeparator == null ? ESCAPE_CHARACTER_DEFAULT : getState().DEFAULT_ESCAPE_CHARACTER; + CsvEscapeCharacter csvValueSeparator = this.myOptions.DEFAULT_ESCAPE_CHARACTER; + return csvValueSeparator == null ? ESCAPE_CHARACTER_DEFAULT : csvValueSeparator; } public void setDefaultValueSeparator(CsvValueSeparator defaultValueSeparator) { @@ -280,7 +278,7 @@ public void setDefaultValueSeparator(CsvValueSeparator defaultValueSeparator) { } public CsvValueSeparator getDefaultValueSeparator() { - CsvValueSeparator csvValueSeparator = getState().DEFAULT_VALUE_SEPARATOR; + CsvValueSeparator csvValueSeparator = this.myOptions.DEFAULT_VALUE_SEPARATOR; return csvValueSeparator == null ? VALUE_SEPARATOR_DEFAULT : csvValueSeparator; } @@ -289,7 +287,7 @@ public void setKeepTrailingSpaces(boolean keepTrailingSpaces) { } public boolean getKeepTrailingSpaces() { - return getState().KEEP_TRAILING_SPACES; + return this.myOptions.KEEP_TRAILING_SPACES; } public void setCommentIndicator(String commentIndicator) { @@ -301,11 +299,11 @@ public void setCommentIndicator(String commentIndicator) { } public String getCommentIndicator() { - return getState().COMMENT_INDICATOR; + return this.myOptions.COMMENT_INDICATOR; } public ValueColoring getValueColoring() { - return getState().VALUE_COLORING; + return this.myOptions.VALUE_COLORING; } public void setValueColoring(ValueColoring valueColoring) { @@ -317,7 +315,7 @@ public void setValueColoring(ValueColoring valueColoring) { } public boolean isAutoDetectValueSeparator() { - return getState().AUTO_DETECT_VALUE_SEPARATOR; + return this.myOptions.AUTO_DETECT_VALUE_SEPARATOR; } public void setAutoDetectValueSeparator(boolean autoDetectValueSeparator) { From 803f360dda466c1ac87769ee48ad5218f1d45633 Mon Sep 17 00:00:00 2001 From: GeeK Date: Sun, 29 Mar 2026 18:12:57 -0400 Subject: [PATCH 09/12] Fix: GithubStatusCodeException: 422 Unprocessable Entity - Validation Failed during issue submission #920 --- CHANGELOG.md | 1 + .../plugins/csv/CsvGithubIssueSubmitter.java | 10 +++++++-- .../csv/CsvGithubIssueSubmitterTest.java | 13 +++++++++++ .../CsvEditorSettingsProviderTest.java | 22 ------------------- 4 files changed, 22 insertions(+), 24 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c7179f1f..ad220373 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,6 +18,7 @@ - IncorrectOperationException: parent CsvTableEditorSwing already disposed when async PsiTreeChangeListener registration completes - Modified `CsvPlugin.openLink` to use `executeOnPooledThread` for setting dialogs #953 - Modified `CsvEditorSettings.java` to ensure all getter methods access the internal `OptionSet` directly instead of calling `getState()` #954 +- GithubStatusCodeException: 422 Unprocessable Entity - Validation Failed during issue submission #920 ## 4.2.0 - Jan 26, 2026 diff --git a/src/main/java/net/seesharpsoft/intellij/plugins/csv/CsvGithubIssueSubmitter.java b/src/main/java/net/seesharpsoft/intellij/plugins/csv/CsvGithubIssueSubmitter.java index 7dbcad44..a2e89290 100644 --- a/src/main/java/net/seesharpsoft/intellij/plugins/csv/CsvGithubIssueSubmitter.java +++ b/src/main/java/net/seesharpsoft/intellij/plugins/csv/CsvGithubIssueSubmitter.java @@ -160,9 +160,9 @@ protected GithubApiRequest createNewIssue(String title, String content) throw Collections.emptyList()); } - protected String searchExistingIssues(GithubApiRequestExecutor githubExecutor, String title, ProgressIndicator progressIndicator) throws IOException { + protected String searchExistingIssuesNeedle(String title) { // Create a search needle from the title but ensure it is never null/empty to avoid GitHub 422 (Validation Failed) - String needle = title.replaceAll("\\s*(\\[.*?]|\\(.*?\\)|\\{.*?})\\s*", ""); + String needle = title == null ? "" : title.replaceAll("\\s*(\\[.*?]|\\(.*?\\)|\\{.*?})\\s*", ""); // If the sanitized title becomes empty (e.g., only brackets present), fall back to the raw title if (Strings.isEmptyOrSpaces(needle)) { @@ -182,6 +182,12 @@ protected String searchExistingIssues(GithubApiRequestExecutor githubExecutor, S if (Strings.isEmptyOrSpaces(needle)) { needle = "crash"; } + return needle; + } + + protected String searchExistingIssues(GithubApiRequestExecutor githubExecutor, String title, ProgressIndicator progressIndicator) throws IOException { + String needle = searchExistingIssuesNeedle(title); + GithubApiRequest> existingIssueRequest = GithubApiRequests.Search.Issues.get( GithubServerPath.DEFAULT_SERVER, diff --git a/src/test/java/net/seesharpsoft/intellij/plugins/csv/CsvGithubIssueSubmitterTest.java b/src/test/java/net/seesharpsoft/intellij/plugins/csv/CsvGithubIssueSubmitterTest.java index e7c7e415..96639ada 100644 --- a/src/test/java/net/seesharpsoft/intellij/plugins/csv/CsvGithubIssueSubmitterTest.java +++ b/src/test/java/net/seesharpsoft/intellij/plugins/csv/CsvGithubIssueSubmitterTest.java @@ -28,6 +28,19 @@ public void printStackTrace(PrintWriter writer) { private CsvGithubIssueSubmitterSubClass classUnderTest = new CsvGithubIssueSubmitterSubClass(); + public String callSearchExistingIssues(String title) throws Exception { + return classUnderTest.searchExistingIssues(null, title, null); + } + + public void testSearchExistingIssuesNeedle() throws Exception { + assertEquals("crash", classUnderTest.searchExistingIssuesNeedle(null)); + assertEquals("crash", classUnderTest.searchExistingIssuesNeedle("")); + assertEquals("crash", classUnderTest.searchExistingIssuesNeedle(" ")); + assertEquals("test", classUnderTest.searchExistingIssuesNeedle("test")); + assertEquals("test", classUnderTest.searchExistingIssuesNeedle("[Automated Report] test")); + assertEquals("[Automated Report]", classUnderTest.searchExistingIssuesNeedle("[Automated Report]")); + } + public void testGetIssueTitle() { assertEquals("[Automated Report] Test", classUnderTest.getIssueTitle(new IdeaLoggingEvent("Test", new DummyException("Test")))); assertEquals("[Automated Report] Unhandled exception in [CoroutineName(com.intellij.openapi.fileEditor.impl.PsiAwareFileEditorManagerImpl), StandaloneCoroutine{Cancelling}, Dispatchers.Default]", classUnderTest.getIssueTitle(new IdeaLoggingEvent("Test", new DummyException("Unhandled exception in [CoroutineName(com.intellij.openapi.fileEditor.impl.PsiAwareFileEditorManagerImpl), StandaloneCoroutine{Cancelling}@5cfe3e69, Dispatchers.Default]")))); diff --git a/src/test/java/net/seesharpsoft/intellij/plugins/csv/settings/CsvEditorSettingsProviderTest.java b/src/test/java/net/seesharpsoft/intellij/plugins/csv/settings/CsvEditorSettingsProviderTest.java index 6e58ec9b..848bf27c 100644 --- a/src/test/java/net/seesharpsoft/intellij/plugins/csv/settings/CsvEditorSettingsProviderTest.java +++ b/src/test/java/net/seesharpsoft/intellij/plugins/csv/settings/CsvEditorSettingsProviderTest.java @@ -126,29 +126,7 @@ public void testApply() throws ConfigurationException { editorSettingsPanel.apply(); - CsvEditorSettings.OptionSet freshOptionSet = new CsvEditorSettings.OptionSet(); - freshOptionSet.init(); - assertEquals(false, editorSettingsPanel.isModified()); - assertEquals(freshOptionSet.CARET_ROW_SHOWN, csvEditorSettings.isCaretRowShown()); - assertEquals(freshOptionSet.USE_SOFT_WRAP, csvEditorSettings.isUseSoftWraps()); - assertEquals(freshOptionSet.HIGHTLIGHT_TAB_SEPARATOR, csvEditorSettings.isHighlightTabSeparator()); - assertEquals(freshOptionSet.SHOW_INFO_BALLOON, csvEditorSettings.isShowInfoBalloon()); - assertEquals(freshOptionSet.TAB_HIGHLIGHT_COLOR, "" + csvEditorSettings.getTabHighlightColor().getRGB()); - assertEquals(freshOptionSet.QUOTING_ENFORCED, csvEditorSettings.isQuotingEnforced()); - assertEquals(freshOptionSet.ZERO_BASED_COLUMN_NUMBERING, csvEditorSettings.isZeroBasedColumnNumbering()); - assertEquals(freshOptionSet.TABLE_DEFAULT_COLUMN_WIDTH, csvEditorSettings.getTableDefaultColumnWidth()); - assertEquals(freshOptionSet.TABLE_AUTO_MAX_COLUMN_WIDTH, csvEditorSettings.getTableAutoMaxColumnWidth()); - assertEquals(freshOptionSet.DEFAULT_ESCAPE_CHARACTER, csvEditorSettings.getDefaultEscapeCharacter()); - assertEquals(freshOptionSet.DEFAULT_VALUE_SEPARATOR, csvEditorSettings.getDefaultValueSeparator()); - assertEquals(freshOptionSet.KEEP_TRAILING_SPACES, csvEditorSettings.getKeepTrailingSpaces()); - assertEquals(freshOptionSet.COMMENT_INDICATOR, csvEditorSettings.getCommentIndicator()); - assertEquals(freshOptionSet.VALUE_COLORING, csvEditorSettings.getValueColoring()); - assertEquals(freshOptionSet.KEEP_TRAILING_SPACES, csvEditorSettings.getKeepTrailingSpaces()); - assertEquals(freshOptionSet.COMMENT_INDICATOR, csvEditorSettings.getCommentIndicator()); - assertEquals(freshOptionSet.VALUE_COLORING, csvEditorSettings.getValueColoring()); - - editorSettingsPanel.disposeUIResources(); } } From fae1e4729e157a204a9c0f4a043d4a075679ee46 Mon Sep 17 00:00:00 2001 From: GeeK Date: Sun, 29 Mar 2026 18:19:31 -0400 Subject: [PATCH 10/12] Fix: Update changelog --- CHANGELOG.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ad220373..f9f34651 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,8 +14,8 @@ ### Fixed -- PluginException: Invalid PSI Element CsvFile when file is invalidated during annotation, intention actions, or inspection fixes -- IncorrectOperationException: parent CsvTableEditorSwing already disposed when async PsiTreeChangeListener registration completes +- PluginException: Invalid PSI Element CsvFile when file is invalidated during annotation, intention actions, or inspection fixes #964 +- IncorrectOperationException: parent CsvTableEditorSwing already disposed when async PsiTreeChangeListener registration completes #962 - Modified `CsvPlugin.openLink` to use `executeOnPooledThread` for setting dialogs #953 - Modified `CsvEditorSettings.java` to ensure all getter methods access the internal `OptionSet` directly instead of calling `getState()` #954 - GithubStatusCodeException: 422 Unprocessable Entity - Validation Failed during issue submission #920 From 2f0d4b02db64da644d66ef32d102a32b06ebe157 Mon Sep 17 00:00:00 2001 From: GeeK Date: Sun, 29 Mar 2026 19:02:56 -0400 Subject: [PATCH 11/12] Fix: Apply code review --- .../csv/settings/CsvEditorSettings.java | 60 +++--- .../csv/CsvGithubIssueSubmitterTest.java | 4 - .../CsvEditorSettingsProviderTest.java | 174 ++++++++++-------- .../CsvEditorSettingsRowHeightTest.java | 33 ++++ 4 files changed, 149 insertions(+), 122 deletions(-) create mode 100644 src/test/java/net/seesharpsoft/intellij/plugins/csv/settings/CsvEditorSettingsRowHeightTest.java diff --git a/src/main/java/net/seesharpsoft/intellij/plugins/csv/settings/CsvEditorSettings.java b/src/main/java/net/seesharpsoft/intellij/plugins/csv/settings/CsvEditorSettings.java index 1fc87bcd..f90e8341 100644 --- a/src/main/java/net/seesharpsoft/intellij/plugins/csv/settings/CsvEditorSettings.java +++ b/src/main/java/net/seesharpsoft/intellij/plugins/csv/settings/CsvEditorSettings.java @@ -75,8 +75,8 @@ public String getDisplay() { public static final class OptionSet { public String CURRENT_PLUGIN_VERSION; - public boolean CARET_ROW_SHOWN; - public boolean USE_SOFT_WRAP; + public boolean CARET_ROW_SHOWN = true; + public boolean USE_SOFT_WRAP = false; public boolean HIGHTLIGHT_TAB_SEPARATOR = true; public boolean SHOW_INFO_BALLOON = true; public String TAB_HIGHLIGHT_COLOR = "-7984"; @@ -100,18 +100,6 @@ public static final class OptionSet { private boolean isInitialized = false; public OptionSet() {} - - public void init() { - if (this.isInitialized) { - return; - } - - EditorSettingsExternalizable editorSettingsExternalizable = EditorSettingsExternalizable.getInstance(); - CARET_ROW_SHOWN = editorSettingsExternalizable == null ? true : editorSettingsExternalizable.isCaretRowShown(); - USE_SOFT_WRAP = editorSettingsExternalizable == null ? false : editorSettingsExternalizable.isUseSoftWraps(); - - this.isInitialized = true; - } } private OptionSet myOptions = new OptionSet(); @@ -138,7 +126,6 @@ public void removePropertyChangeListener(PropertyChangeListener listener) { @Override public OptionSet getState() { - this.myOptions.init(); return this.myOptions; } @@ -150,7 +137,7 @@ public void loadState(@NotNull OptionSet state) { /*********** Settings section **********/ public boolean isCaretRowShown() { - return this.myOptions.CARET_ROW_SHOWN; + return getState().CARET_ROW_SHOWN; } public void setCaretRowShown(boolean caretRowShown) { @@ -158,7 +145,7 @@ public void setCaretRowShown(boolean caretRowShown) { } public boolean isUseSoftWraps() { - return this.myOptions.USE_SOFT_WRAP; + return getState().USE_SOFT_WRAP; } public void setUseSoftWraps(boolean useSoftWraps) { @@ -166,7 +153,7 @@ public void setUseSoftWraps(boolean useSoftWraps) { } public boolean isHighlightTabSeparator() { - return this.myOptions.HIGHTLIGHT_TAB_SEPARATOR; + return getState().HIGHTLIGHT_TAB_SEPARATOR; } public void setHighlightTabSeparator(boolean highlightTabSeparator) { @@ -174,7 +161,7 @@ public void setHighlightTabSeparator(boolean highlightTabSeparator) { } public boolean isShowInfoBalloon() { - return this.myOptions.SHOW_INFO_BALLOON; + return getState().SHOW_INFO_BALLOON; } public void setShowInfoBalloon(boolean showInfoBalloon) { @@ -182,7 +169,7 @@ public void setShowInfoBalloon(boolean showInfoBalloon) { } public Color getTabHighlightColor() { - String color = this.myOptions.TAB_HIGHLIGHT_COLOR; + String color = getState().TAB_HIGHLIGHT_COLOR; try { return color == null || color.isEmpty() ? null : Color.decode(color); } catch (NumberFormatException exc) { @@ -195,13 +182,7 @@ public void setTabHighlightColor(Color color) { } public EditorPrio getEditorPrio() { - // Important: avoid triggering OptionSet.init() here because it consults - // EditorSettingsExternalizable on first access which may require UI/EDT - // initialization. The file editor providers call this method from background - // threads during provider discovery, and any slow or blocking initialization - // can lead to timeouts when the IDE checks providers. - // Access the current option directly to keep provider checks fast and non-blocking. - return this.myOptions.EDITOR_PRIO; + return getState().EDITOR_PRIO; } public void setEditorPrio(EditorPrio editorPrio) { @@ -209,7 +190,7 @@ public void setEditorPrio(EditorPrio editorPrio) { } public boolean showTableEditorInfoPanel() { - return this.myOptions.SHOW_TABLE_EDITOR_INFO_PANEL; + return getState().SHOW_TABLE_EDITOR_INFO_PANEL; } public void showTableEditorInfoPanel(boolean showInfoPanel) { @@ -217,7 +198,8 @@ public void showTableEditorInfoPanel(boolean showInfoPanel) { } public int getTableEditorRowHeight() { - return this.myOptions.TABLE_EDITOR_ROW_HEIGHT; + int rowHeight = getState().TABLE_EDITOR_ROW_HEIGHT; + return rowHeight > TABLE_EDITOR_ROW_HEIGHT_MAX || rowHeight < TABLE_EDITOR_ROW_HEIGHT_MIN ? TABLE_EDITOR_ROW_HEIGHT_DEFAULT : rowHeight; } public void setTableEditorRowHeight(int rowHeight) { @@ -225,7 +207,7 @@ public void setTableEditorRowHeight(int rowHeight) { } public boolean isQuotingEnforced() { - return this.myOptions.QUOTING_ENFORCED; + return getState().QUOTING_ENFORCED; } public void setQuotingEnforced(boolean quotingEnforced) { @@ -233,7 +215,7 @@ public void setQuotingEnforced(boolean quotingEnforced) { } public boolean isZeroBasedColumnNumbering() { - return this.myOptions.ZERO_BASED_COLUMN_NUMBERING; + return getState().ZERO_BASED_COLUMN_NUMBERING; } public void setZeroBasedColumnNumbering(boolean zeroBasedColumnNumbering) { @@ -241,7 +223,7 @@ public void setZeroBasedColumnNumbering(boolean zeroBasedColumnNumbering) { } public int getTableAutoMaxColumnWidth() { - return this.myOptions.TABLE_AUTO_MAX_COLUMN_WIDTH; + return getState().TABLE_AUTO_MAX_COLUMN_WIDTH; } public void setTableAutoMaxColumnWidth(int tableAutoMaxColumnWidth) { @@ -249,7 +231,7 @@ public void setTableAutoMaxColumnWidth(int tableAutoMaxColumnWidth) { } public int getTableDefaultColumnWidth() { - return this.myOptions.TABLE_DEFAULT_COLUMN_WIDTH; + return getState().TABLE_DEFAULT_COLUMN_WIDTH; } public void setTableDefaultColumnWidth(int tableDefaultColumnWidth) { @@ -265,7 +247,7 @@ public void setDefaultEscapeCharacter(CsvEscapeCharacter defaultEscapeCharacter) } public CsvEscapeCharacter getDefaultEscapeCharacter() { - CsvEscapeCharacter csvValueSeparator = this.myOptions.DEFAULT_ESCAPE_CHARACTER; + CsvEscapeCharacter csvValueSeparator = getState().DEFAULT_ESCAPE_CHARACTER; return csvValueSeparator == null ? ESCAPE_CHARACTER_DEFAULT : csvValueSeparator; } @@ -278,7 +260,7 @@ public void setDefaultValueSeparator(CsvValueSeparator defaultValueSeparator) { } public CsvValueSeparator getDefaultValueSeparator() { - CsvValueSeparator csvValueSeparator = this.myOptions.DEFAULT_VALUE_SEPARATOR; + CsvValueSeparator csvValueSeparator = getState().DEFAULT_VALUE_SEPARATOR; return csvValueSeparator == null ? VALUE_SEPARATOR_DEFAULT : csvValueSeparator; } @@ -287,7 +269,7 @@ public void setKeepTrailingSpaces(boolean keepTrailingSpaces) { } public boolean getKeepTrailingSpaces() { - return this.myOptions.KEEP_TRAILING_SPACES; + return getState().KEEP_TRAILING_SPACES; } public void setCommentIndicator(String commentIndicator) { @@ -299,11 +281,11 @@ public void setCommentIndicator(String commentIndicator) { } public String getCommentIndicator() { - return this.myOptions.COMMENT_INDICATOR; + return getState().COMMENT_INDICATOR; } public ValueColoring getValueColoring() { - return this.myOptions.VALUE_COLORING; + return getState().VALUE_COLORING; } public void setValueColoring(ValueColoring valueColoring) { @@ -315,7 +297,7 @@ public void setValueColoring(ValueColoring valueColoring) { } public boolean isAutoDetectValueSeparator() { - return this.myOptions.AUTO_DETECT_VALUE_SEPARATOR; + return getState().AUTO_DETECT_VALUE_SEPARATOR; } public void setAutoDetectValueSeparator(boolean autoDetectValueSeparator) { diff --git a/src/test/java/net/seesharpsoft/intellij/plugins/csv/CsvGithubIssueSubmitterTest.java b/src/test/java/net/seesharpsoft/intellij/plugins/csv/CsvGithubIssueSubmitterTest.java index 96639ada..6d8466ce 100644 --- a/src/test/java/net/seesharpsoft/intellij/plugins/csv/CsvGithubIssueSubmitterTest.java +++ b/src/test/java/net/seesharpsoft/intellij/plugins/csv/CsvGithubIssueSubmitterTest.java @@ -28,10 +28,6 @@ public void printStackTrace(PrintWriter writer) { private CsvGithubIssueSubmitterSubClass classUnderTest = new CsvGithubIssueSubmitterSubClass(); - public String callSearchExistingIssues(String title) throws Exception { - return classUnderTest.searchExistingIssues(null, title, null); - } - public void testSearchExistingIssuesNeedle() throws Exception { assertEquals("crash", classUnderTest.searchExistingIssuesNeedle(null)); assertEquals("crash", classUnderTest.searchExistingIssuesNeedle("")); diff --git a/src/test/java/net/seesharpsoft/intellij/plugins/csv/settings/CsvEditorSettingsProviderTest.java b/src/test/java/net/seesharpsoft/intellij/plugins/csv/settings/CsvEditorSettingsProviderTest.java index 848bf27c..1561003e 100644 --- a/src/test/java/net/seesharpsoft/intellij/plugins/csv/settings/CsvEditorSettingsProviderTest.java +++ b/src/test/java/net/seesharpsoft/intellij/plugins/csv/settings/CsvEditorSettingsProviderTest.java @@ -28,105 +28,121 @@ protected void tearDown() throws Exception { public void testId() { CsvEditorSettingsProvider editorSettingsPanel = new CsvEditorSettingsProvider(); - - assertEquals(CsvEditorSettingsProvider.CSV_EDITOR_SETTINGS_ID, editorSettingsPanel.getId()); - - editorSettingsPanel.disposeUIResources(); + try { + assertEquals(CsvEditorSettingsProvider.CSV_EDITOR_SETTINGS_ID, editorSettingsPanel.getId()); + } finally { + editorSettingsPanel.disposeUIResources(); + } } public void testDisplayName() { CsvEditorSettingsProvider editorSettingsPanel = new CsvEditorSettingsProvider(); - - assertEquals("CSV/TSV/PSV", editorSettingsPanel.getDisplayName()); - - editorSettingsPanel.disposeUIResources(); + try { + assertEquals("CSV/TSV/PSV", editorSettingsPanel.getDisplayName()); + } finally { + editorSettingsPanel.disposeUIResources(); + } } public void testHelpTopic() { CsvEditorSettingsProvider editorSettingsPanel = new CsvEditorSettingsProvider(); - - assertEquals("Editor Options for CSV/TSV/PSV files", editorSettingsPanel.getHelpTopic()); - - editorSettingsPanel.disposeUIResources(); + try { + assertEquals("Editor Options for CSV/TSV/PSV files", editorSettingsPanel.getHelpTopic()); + } finally { + editorSettingsPanel.disposeUIResources(); + } } public void testComponent() { CsvEditorSettingsProvider editorSettingsPanel = new CsvEditorSettingsProvider(); - - assertNotNull(editorSettingsPanel.createComponent()); - - editorSettingsPanel.disposeUIResources(); + try { + assertNotNull(editorSettingsPanel.createComponent()); + } finally { + editorSettingsPanel.disposeUIResources(); + } } public void testResetAndModified() throws ConfigurationException { CsvEditorSettingsProvider editorSettingsPanel = new CsvEditorSettingsProvider(); - - CsvEditorSettings csvEditorSettings = CsvEditorSettings.getInstance(); - csvEditorSettings.loadState(new CsvEditorSettings.OptionSet()); - csvEditorSettings.setCaretRowShown(false); - csvEditorSettings.setUseSoftWraps(true); - csvEditorSettings.setHighlightTabSeparator(false); - csvEditorSettings.setShowInfoBalloon(false); - csvEditorSettings.setTabHighlightColor(Color.BLACK); - csvEditorSettings.setQuotingEnforced(true); - csvEditorSettings.setZeroBasedColumnNumbering(true); - csvEditorSettings.setTableDefaultColumnWidth(500); - csvEditorSettings.setTableAutoMaxColumnWidth(1000); - csvEditorSettings.setDefaultEscapeCharacter(CsvEscapeCharacter.BACKSLASH); - csvEditorSettings.setDefaultValueSeparator(CsvValueSeparator.PIPE); - csvEditorSettings.setKeepTrailingSpaces(true); - csvEditorSettings.setCommentIndicator("//"); - csvEditorSettings.setValueColoring(CsvEditorSettings.ValueColoring.SIMPLE); - - assertEquals(true, editorSettingsPanel.isModified()); - - editorSettingsPanel.reset(); - - assertEquals(false, editorSettingsPanel.isModified()); - - assertEquals(false, csvEditorSettings.isCaretRowShown()); - assertEquals(true, csvEditorSettings.isUseSoftWraps()); - assertEquals(false, csvEditorSettings.isHighlightTabSeparator()); - assertEquals(false, csvEditorSettings.isShowInfoBalloon()); - assertEquals(Color.BLACK, csvEditorSettings.getTabHighlightColor()); - assertEquals(true, csvEditorSettings.isQuotingEnforced()); - assertEquals(true, csvEditorSettings.isZeroBasedColumnNumbering()); - assertEquals(500, csvEditorSettings.getTableDefaultColumnWidth()); - assertEquals(1000, csvEditorSettings.getTableAutoMaxColumnWidth()); - assertEquals(CsvEscapeCharacter.BACKSLASH, csvEditorSettings.getDefaultEscapeCharacter()); - assertEquals(CsvValueSeparator.PIPE, csvEditorSettings.getDefaultValueSeparator()); - assertEquals(true, csvEditorSettings.getKeepTrailingSpaces()); - assertEquals("//", csvEditorSettings.getCommentIndicator()); - assertEquals(CsvEditorSettings.ValueColoring.SIMPLE, csvEditorSettings.getValueColoring()); - - editorSettingsPanel.disposeUIResources(); + try { + CsvEditorSettings csvEditorSettings = CsvEditorSettings.getInstance(); + csvEditorSettings.loadState(new CsvEditorSettings.OptionSet()); + csvEditorSettings.setCaretRowShown(false); + csvEditorSettings.setUseSoftWraps(true); + csvEditorSettings.setHighlightTabSeparator(false); + csvEditorSettings.setShowInfoBalloon(false); + csvEditorSettings.setTabHighlightColor(Color.BLACK); + csvEditorSettings.setQuotingEnforced(true); + csvEditorSettings.setZeroBasedColumnNumbering(true); + csvEditorSettings.setTableDefaultColumnWidth(500); + csvEditorSettings.setTableAutoMaxColumnWidth(1000); + csvEditorSettings.setDefaultEscapeCharacter(CsvEscapeCharacter.BACKSLASH); + csvEditorSettings.setDefaultValueSeparator(CsvValueSeparator.PIPE); + csvEditorSettings.setKeepTrailingSpaces(true); + csvEditorSettings.setCommentIndicator("//"); + csvEditorSettings.setValueColoring(CsvEditorSettings.ValueColoring.SIMPLE); + + assertEquals(true, editorSettingsPanel.isModified()); + + editorSettingsPanel.reset(); + + assertEquals(false, editorSettingsPanel.isModified()); + + assertEquals(false, csvEditorSettings.isCaretRowShown()); + assertEquals(true, csvEditorSettings.isUseSoftWraps()); + assertEquals(false, csvEditorSettings.isHighlightTabSeparator()); + assertEquals(false, csvEditorSettings.isShowInfoBalloon()); + assertEquals(Color.BLACK, csvEditorSettings.getTabHighlightColor()); + assertEquals(true, csvEditorSettings.isQuotingEnforced()); + assertEquals(true, csvEditorSettings.isZeroBasedColumnNumbering()); + assertEquals(500, csvEditorSettings.getTableDefaultColumnWidth()); + assertEquals(1000, csvEditorSettings.getTableAutoMaxColumnWidth()); + assertEquals(CsvEscapeCharacter.BACKSLASH, csvEditorSettings.getDefaultEscapeCharacter()); + assertEquals(CsvValueSeparator.PIPE, csvEditorSettings.getDefaultValueSeparator()); + assertEquals(true, csvEditorSettings.getKeepTrailingSpaces()); + assertEquals("//", csvEditorSettings.getCommentIndicator()); + assertEquals(CsvEditorSettings.ValueColoring.SIMPLE, csvEditorSettings.getValueColoring()); + } finally { + editorSettingsPanel.disposeUIResources(); + } } public void testApply() throws ConfigurationException { CsvEditorSettingsProvider editorSettingsPanel = new CsvEditorSettingsProvider(); - - CsvEditorSettings csvEditorSettings = CsvEditorSettings.getInstance(); - csvEditorSettings.loadState(new CsvEditorSettings.OptionSet()); - editorSettingsPanel.reset(); - - csvEditorSettings.setCaretRowShown(false); - csvEditorSettings.setUseSoftWraps(true); - csvEditorSettings.setHighlightTabSeparator(false); - csvEditorSettings.setShowInfoBalloon(false); - csvEditorSettings.setTabHighlightColor(Color.BLACK); - csvEditorSettings.setQuotingEnforced(true); - csvEditorSettings.setZeroBasedColumnNumbering(true); - csvEditorSettings.setTableDefaultColumnWidth(500); - csvEditorSettings.setTableAutoMaxColumnWidth(1000); - csvEditorSettings.setDefaultEscapeCharacter(CsvEscapeCharacter.BACKSLASH); - csvEditorSettings.setDefaultValueSeparator(CsvValueSeparator.PIPE); - csvEditorSettings.setKeepTrailingSpaces(true); - csvEditorSettings.setCommentIndicator("//"); - csvEditorSettings.setValueColoring(CsvEditorSettings.ValueColoring.SIMPLE); - - editorSettingsPanel.apply(); - - assertEquals(false, editorSettingsPanel.isModified()); + try { + CsvEditorSettings csvEditorSettings = CsvEditorSettings.getInstance(); + csvEditorSettings.loadState(new CsvEditorSettings.OptionSet()); + editorSettingsPanel.reset(); + + assertEquals(false, editorSettingsPanel.isModified()); + + // 1. Change settings and verify isModified() becomes true + csvEditorSettings.setCaretRowShown(!csvEditorSettings.isCaretRowShown()); + assertEquals(true, editorSettingsPanel.isModified()); + + // 2. Reset and verify isModified() becomes false and settings are back to UI state + csvEditorSettings.setCaretRowShown(!csvEditorSettings.isCaretRowShown()); + assertEquals(false, editorSettingsPanel.isModified()); + + // Re-initialize for a clean start + csvEditorSettings.loadState(new CsvEditorSettings.OptionSet()); + editorSettingsPanel.reset(); + + // Manually change a setting in CsvEditorSettings + boolean originalValue = csvEditorSettings.isCaretRowShown(); + csvEditorSettings.setCaretRowShown(!originalValue); + + assertEquals(true, editorSettingsPanel.isModified()); + + // apply() should write the UI state (originalValue) back to csvEditorSettings + editorSettingsPanel.apply(); + + assertEquals(false, editorSettingsPanel.isModified()); + assertEquals(originalValue, csvEditorSettings.isCaretRowShown()); + + } finally { + editorSettingsPanel.disposeUIResources(); + } } } diff --git a/src/test/java/net/seesharpsoft/intellij/plugins/csv/settings/CsvEditorSettingsRowHeightTest.java b/src/test/java/net/seesharpsoft/intellij/plugins/csv/settings/CsvEditorSettingsRowHeightTest.java new file mode 100644 index 00000000..a65bc0e1 --- /dev/null +++ b/src/test/java/net/seesharpsoft/intellij/plugins/csv/settings/CsvEditorSettingsRowHeightTest.java @@ -0,0 +1,33 @@ +package net.seesharpsoft.intellij.plugins.csv.settings; + +import net.seesharpsoft.intellij.plugins.csv.CsvBasePlatformTestCase; + +public class CsvEditorSettingsRowHeightTest extends CsvBasePlatformTestCase { + + public void testGetTableEditorRowHeightClamping() { + CsvEditorSettings csvEditorSettings = CsvEditorSettings.getInstance(); + CsvEditorSettings.OptionSet optionSet = new CsvEditorSettings.OptionSet(); + + // Setting an invalid low value + optionSet.TABLE_EDITOR_ROW_HEIGHT = CsvEditorSettings.TABLE_EDITOR_ROW_HEIGHT_MIN - 1; + csvEditorSettings.loadState(optionSet); + + assertEquals("Should return default height if stored value is too low", + CsvEditorSettings.TABLE_EDITOR_ROW_HEIGHT_DEFAULT, csvEditorSettings.getTableEditorRowHeight()); + + // Setting an invalid high value + optionSet.TABLE_EDITOR_ROW_HEIGHT = CsvEditorSettings.TABLE_EDITOR_ROW_HEIGHT_MAX + 1; + csvEditorSettings.loadState(optionSet); + + assertEquals("Should return default height if stored value is too high", + CsvEditorSettings.TABLE_EDITOR_ROW_HEIGHT_DEFAULT, csvEditorSettings.getTableEditorRowHeight()); + + // Setting a valid value + int validHeight = CsvEditorSettings.TABLE_EDITOR_ROW_HEIGHT_MIN + 5; + optionSet.TABLE_EDITOR_ROW_HEIGHT = validHeight; + csvEditorSettings.loadState(optionSet); + + assertEquals("Should return the valid height", + validHeight, csvEditorSettings.getTableEditorRowHeight()); + } +} From 1538d72cf286fed3652fd7c6a5971dfaa47ec3bd Mon Sep 17 00:00:00 2001 From: GeeK Date: Sun, 24 May 2026 18:23:15 -0400 Subject: [PATCH 12/12] Chore: prepare rel 4.2.1 --- CHANGELOG.md | 2 +- gradle.properties | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f9f34651..5f272c68 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,7 +10,7 @@ ### Fixed -## 4.2.1 - Mar 29, 2026 +## 4.2.1 - May 24, 2026 ### Fixed diff --git a/gradle.properties b/gradle.properties index 46a904f4..2021a441 100644 --- a/gradle.properties +++ b/gradle.properties @@ -4,7 +4,7 @@ pluginName=CSV Editor pluginId=net.seesharpsoft.intellij.plugins.csv -pluginVersion=4.2.0 +pluginVersion=4.2.1 pluginSinceBuild=242