Skip to content

Commit

Permalink
fix error form crash bug; add syntax check command
Browse files Browse the repository at this point in the history
ADDED:
1. "Check JSON syntax now" command.
FIXED:
1. Bug where opening error form could sometimes cause Notepad++ to crash
    if the `auto_validate` setting was true.
CHANGED:
1. Made it so that reloading the error form
    by pressing `Enter` would not cause certain message boxes to appear,
    to eliminate a potential "infinite" loop where the user would hit
    `Enter` to close the message box, and that moved focus back to the error form,
    which then repeated the cycle when they lifted the `Enter` key.
  • Loading branch information
molsonkiko committed Apr 18, 2024
1 parent c82851c commit b1f2f3f
Show file tree
Hide file tree
Showing 7 changed files with 150 additions and 91 deletions.
13 changes: 11 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,10 +52,19 @@ and this project adheres to [Semantic Versioning](http://semver.org/).

## [7.2.0] - (UNRELEASED) YYYY-MM-DD

### Added

1. [`Check JSON syntax now` command](/docs/README.md#check-json-syntax-now).

### Changed

1. Made it so that reloading the [error form](/docs/README.md#error-form-and-status-bar) by pressing `Enter` would not cause certain message boxes to appear, to eliminate a potential "infinite" loop where the user would hit `Enter` to close the message box, and that moved focus back to the error form, which then repeated the cycle when they lifted the `Enter` key.

### Fixed

1. Minor bug in [PPrint remembering comments](/docs/README.md#remember_comments) algorithm implementation that caused some arrays and objects to be compressed when they should have been pretty-printed.
2. Fix bug where tests could crash under some circumstances due to filesystem weirdness making it impossible to find test files.
1. Bug where opening error form could sometimes cause Notepad++ to crash if the [`auto_validate` setting](/docs/README.md#automatically-check-for-errors-after-editing) was true.
2. Minor bug in [PPrint remembering comments](/docs/README.md#remember_comments) algorithm implementation that caused some arrays and objects to be compressed when they should have been pretty-printed.
3. Fix bug where tests could crash under some circumstances due to filesystem weirdness making it impossible to find test files.

## [7.1.0] - 2024-02-28

Expand Down
8 changes: 7 additions & 1 deletion JsonToolsNppPlugin/Forms/ErrorForm.cs
Original file line number Diff line number Diff line change
Expand Up @@ -181,19 +181,25 @@ private void ErrorForm_KeyUp(object sender, KeyEventArgs e)
{
// refresh error form based on current contents of current file
e.Handled = true;
Main.errorFormTriggeredParse = true;
// temporarily turn off offer_to_show_lint prompt, because the user obviously wants to see it
bool previousOfferToShowLint = Main.settings.offer_to_show_lint;
Main.settings.offer_to_show_lint = false;
Main.TryParseJson(preferPreviousDocumentType:true);
Main.settings.offer_to_show_lint = previousOfferToShowLint;
if (Main.TryGetInfoForFile(Main.activeFname, out JsonFileInfo info)
&& info.lints != null)
{
if (info.filenameOfMostRecentValidatingSchema is null)
Reload(Main.activeFname, info.lints);
else
{
Main.ValidateJson(info.filenameOfMostRecentValidatingSchema, false);
if (Main.TryGetInfoForFile(Main.activeFname, out info) && info.lints != null && info.statusBarSection != null && info.statusBarSection.Contains("fatal errors"))
Reload(Main.activeFname, info.lints);
}
}
Main.settings.offer_to_show_lint = previousOfferToShowLint;
Main.errorFormTriggeredParse = false;
return;
}
else if (e.KeyCode == Keys.Escape)
Expand Down
116 changes: 76 additions & 40 deletions JsonToolsNppPlugin/Main.cs
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,16 @@ class Main
public static SortForm sortForm = null;
// error form stuff
public static ErrorForm errorForm = null;
/// <summary>
/// we need this boolean to avoid "infinite" loops as follows:<br></br>
/// 1. The user presses Enter in the error form to parse the document.<br></br>
/// 2. the parse fails<br></br>
/// 3. the user is shown a dialog box warning them that the parse failed.<br></br>
/// 4. the user presses Enter to close the dialog box<br></br>
/// 5. focus returns to the error form<br></br>
/// 6. the user releases the Enter key, triggering the ErrorForm_KeyUp event again and returning to step 1.
/// </summary>
public static bool errorFormTriggeredParse = false;
// regex search to json stuff
public static RegexSearchForm regexSearchForm = null;
// schema auto-validation stuff
Expand Down Expand Up @@ -108,32 +118,33 @@ static internal void CommandMenuInit()
PluginBase.SetCommand(2, "&Compress current JSON file", CompressJson, new ShortcutKey(true, true, true, Keys.C)); compressId = 2;
PluginBase.SetCommand(3, "Path to current p&osition", CopyPathToCurrentPosition, new ShortcutKey(true, true, true, Keys.L)); pathToPositionId = 3;
PluginBase.SetCommand(4, "Select every val&id JSON in selection", SelectEveryValidJson);
PluginBase.SetCommand(5, "Chec&k JSON syntax now", CheckJsonSyntaxNow);
// Here you insert a separator
PluginBase.SetCommand(5, "---", null);
PluginBase.SetCommand(6, "Open &JSON tree viewer", () => OpenJsonTree(), new ShortcutKey(true, true, true, Keys.J)); jsonTreeId = 6;
PluginBase.SetCommand(7, "&Get JSON from files and APIs", OpenGrepperForm, new ShortcutKey(true, true, true, Keys.G)); grepperFormId = 7;
PluginBase.SetCommand(8, "Sort arra&ys", OpenSortForm); sortFormId = 8;
PluginBase.SetCommand(9, "&Settings", OpenSettings, new ShortcutKey(true, true, true, Keys.S));
PluginBase.SetCommand(10, "---", null);
PluginBase.SetCommand(11, "&Validate JSON against JSON schema", () => ValidateJson());
PluginBase.SetCommand(12, "Choose schemas to automatically validate &filename patterns", MapSchemasToFnamePatterns);
PluginBase.SetCommand(13, "Generate sc&hema from JSON", GenerateJsonSchema);
PluginBase.SetCommand(14, "Generate &random JSON from schema", GenerateRandomJson);
PluginBase.SetCommand(15, "---", null);
PluginBase.SetCommand(16, "Run &tests", async () => await TestRunner.RunAll());
PluginBase.SetCommand(17, "A&bout", ShowAboutForm); AboutFormId = 17;
PluginBase.SetCommand(18, "See most recent syntax &errors in this file", () => OpenErrorForm(activeFname, false)); errorFormId = 18;
PluginBase.SetCommand(19, "JSON to YAML", DumpYaml);
PluginBase.SetCommand(20, "---", null);
PluginBase.SetCommand(21, "Parse JSON Li&nes document", () => OpenJsonTree(DocumentType.JSONL));
PluginBase.SetCommand(22, "&Array to JSON Lines", DumpJsonLines);
PluginBase.SetCommand(23, "---", null);
PluginBase.SetCommand(24, "D&ump selected text as JSON string(s)", DumpSelectedTextAsJsonString);
PluginBase.SetCommand(25, "Dump JSON string(s) as ra&w text", DumpSelectedJsonStringsAsText);
PluginBase.SetCommand(26, "---", null);
PluginBase.SetCommand(27, "Open tree for &INI file", () => OpenJsonTree(DocumentType.INI));
PluginBase.SetCommand(28, "---", null);
PluginBase.SetCommand(29, "Rege&x search to JSON", RegexSearchToJson);
PluginBase.SetCommand(6, "---", null);
PluginBase.SetCommand(7, "Open &JSON tree viewer", () => OpenJsonTree(), new ShortcutKey(true, true, true, Keys.J)); jsonTreeId = 7;
PluginBase.SetCommand(8, "&Get JSON from files and APIs", OpenGrepperForm, new ShortcutKey(true, true, true, Keys.G)); grepperFormId = 8;
PluginBase.SetCommand(9, "Sort arra&ys", OpenSortForm); sortFormId = 9;
PluginBase.SetCommand(10, "&Settings", OpenSettings, new ShortcutKey(true, true, true, Keys.S));
PluginBase.SetCommand(11, "---", null);
PluginBase.SetCommand(12, "&Validate JSON against JSON schema", () => ValidateJson());
PluginBase.SetCommand(13, "Choose schemas to automatically validate &filename patterns", MapSchemasToFnamePatterns);
PluginBase.SetCommand(14, "Generate sc&hema from JSON", GenerateJsonSchema);
PluginBase.SetCommand(15, "Generate &random JSON from schema", GenerateRandomJson);
PluginBase.SetCommand(16, "---", null);
PluginBase.SetCommand(17, "Run &tests", async () => await TestRunner.RunAll());
PluginBase.SetCommand(18, "A&bout", ShowAboutForm); AboutFormId = 18;
PluginBase.SetCommand(19, "See most recent syntax &errors in this file", () => OpenErrorForm(activeFname, false)); errorFormId = 19;
PluginBase.SetCommand(20, "JSON to YAML", DumpYaml);
PluginBase.SetCommand(21, "---", null);
PluginBase.SetCommand(22, "Parse JSON Li&nes document", () => OpenJsonTree(DocumentType.JSONL));
PluginBase.SetCommand(23, "&Array to JSON Lines", DumpJsonLines);
PluginBase.SetCommand(24, "---", null);
PluginBase.SetCommand(25, "D&ump selected text as JSON string(s)", DumpSelectedTextAsJsonString);
PluginBase.SetCommand(26, "Dump JSON string(s) as ra&w text", DumpSelectedJsonStringsAsText);
PluginBase.SetCommand(27, "---", null);
PluginBase.SetCommand(28, "Open tree for &INI file", () => OpenJsonTree(DocumentType.INI));
PluginBase.SetCommand(29, "---", null);
PluginBase.SetCommand(30, "Rege&x search to JSON", RegexSearchToJson);

// write the schema to fname patterns file if it doesn't exist, then parse it
SetSchemasToFnamePatternsFname();
Expand Down Expand Up @@ -660,7 +671,9 @@ public static (ParserState parserState, JNode node, bool usesSelections, Documen
else
info.usesSelections |= !noTextSelected;
info.lints = lints;
if (lintCount > 0 && settings.offer_to_show_lint)
ParserState parserStateToSet = fatal ? ParserState.FATAL : jsonParser.state;
SetStatusBarSection(parserStateToSet, fname, info, documentType, lintCount);
if (lintCount > 0 && settings.offer_to_show_lint && !wasAutotriggered)
{
string msg = $"There were {lintCount} syntax errors in the document. Would you like to see them?\r\n(You can turn off these prompts in the settings (offer_to_show_lint setting))";
if (MessageBox.Show(msg, "View syntax errors in document?", MessageBoxButtons.YesNo, MessageBoxIcon.Question)
Expand All @@ -677,13 +690,12 @@ public static (ParserState parserState, JNode node, bool usesSelections, Documen
if (lintCount > 0 && noTextSelected && !wasAutotriggered)
Npp.editor.GoToLegalPos(lints[lintCount - 1].pos);
// unacceptable error, show message box
MessageBox.Show($"Could not parse the document because of error\n{errorMessage}",
$"Error while trying to parse {Npp.DocumentTypeSuperTypeName(documentType)}",
MessageBoxButtons.OK,
MessageBoxIcon.Error);
if (!errorFormTriggeredParse)
MessageBox.Show($"Could not parse the document because of error\n{errorMessage}",
$"Error while trying to parse {Npp.DocumentTypeSuperTypeName(documentType)}",
MessageBoxButtons.OK,
MessageBoxIcon.Error);
}
ParserState parserStateToSet = fatal ? ParserState.FATAL : jsonParser.state;
SetStatusBarSection(parserStateToSet, fname, info, documentType, lintCount);
if (info.tv != null)
{
info.tv.Invoke(new Action(() =>
Expand Down Expand Up @@ -981,6 +993,14 @@ public static JNode RenameAll(Dictionary<string, (string, JNode)> keyChanges, JO
return obj;
}

public static void CheckJsonSyntaxNow()
{
string curFname = Npp.notepad.GetCurrentFilePath();
(ParserState parserState, _, _, _) = TryParseJson(Npp.FileExtension(curFname) == "jsonl" ? DocumentType.JSONL : DocumentType.JSON);
if (parserState == ParserState.FATAL)
RefreshErrorFormInOwnThread(curFname);
}

/// <summary>
/// if there are one or more selections, dump each selection as a JSON string on a separate line.<br></br>
/// Otherwise just dump the whole document on the same line.
Expand Down Expand Up @@ -1251,6 +1271,19 @@ private static void DisplayErrorForm(ErrorForm form)
Npp.notepad.ShowDockingForm(form);
}

private static void RefreshErrorFormInOwnThread(string fname)
{
if (errorForm == null)
return;
errorForm.Invoke(new Action(() =>
{
if (errorForm.IsDisposed || !TryGetInfoForFile(fname, out JsonFileInfo info) || info.lints == null)
return;
errorForm.Reload(fname, info.lints);
Npp.editor.GrabFocus();
}));
}

public static void CopyPathToCurrentPosition()
{
int pos = Npp.editor.GetCurrentPos();
Expand Down Expand Up @@ -1515,12 +1548,15 @@ static void ShowAboutForm()
/// or if it failed, where the first error was.<br></br>
/// If ignoreSelections, always validate the entire file even the selected text is valid JSON.
/// </summary>
public static void ValidateJson(string schemaPath = null, bool messageOnSuccess = true, bool ignoreSelections = false)
public static void ValidateJson(string schemaPath = null, bool messageOnSuccess = true, bool ignoreSelections = false, bool wasAutotriggered = false)
{
(ParserState parserState, JNode json, _, DocumentType documentType) = TryParseJson(preferPreviousDocumentType:true, ignoreSelections: ignoreSelections);
(ParserState parserState, JNode json, _, DocumentType documentType) = TryParseJson(wasAutotriggered: wasAutotriggered, preferPreviousDocumentType:true, ignoreSelections: ignoreSelections);
string curFname = Npp.notepad.GetCurrentFilePath();
if (parserState == ParserState.FATAL || json == null)
{
RefreshErrorFormInOwnThread(curFname);
return;
string curFname = Npp.notepad.GetCurrentFilePath();
}
if (schemaPath == null)
{
FileDialog openFileDialog = new OpenFileDialog();
Expand Down Expand Up @@ -1551,8 +1587,7 @@ public static void ValidateJson(string schemaPath = null, bool messageOnSuccess
info.filenameOfMostRecentValidatingSchema = schemaPath;
info.lints = info.lints is null ? problems : info.lints.Concat(problems).ToList();
int lintCount = info.lints.Count;
if (errorForm != null && !errorForm.IsDisposed)
errorForm.Invoke(new Action(() => errorForm.Reload(curFname, info.lints)));
RefreshErrorFormInOwnThread(curFname);
SetStatusBarSection(parserState, curFname, info, documentType, lintCount);
if (!validates && problems.Count > 0)
{
Expand Down Expand Up @@ -1753,7 +1788,7 @@ static void SetSchemasToFnamePatternsFname()
/// if the filename doesn't match, return false.
/// </summary>
/// <param name="fname"></param>
static bool ValidateIfFilenameMatches(string fname, bool wasAutotriggered = false, bool wasTriggeredByParseTimer = false)
static bool ValidateIfFilenameMatches(string fname, bool wasAutotriggered = false)
{
if (wasAutotriggered && Npp.editor.GetLength() > Main.settings.max_file_size_MB_slow_actions * 1e6)
return false;
Expand All @@ -1765,7 +1800,7 @@ static bool ValidateIfFilenameMatches(string fname, bool wasAutotriggered = fals
var regex = ((JRegex)pat).regex;
if (!regex.IsMatch(fname)) continue;
// the filename matches a pattern for this schema, so we'll try to validate it.
ValidateJson(schemaFname, false, true);
ValidateJson(schemaFname, false, true, wasAutotriggered);
return true;
}
}
Expand Down Expand Up @@ -1918,11 +1953,12 @@ private static void DelayedParseAfterEditing(object s)
if ((TryGetInfoForFile(fname, out JsonFileInfo info)
&& (info.documentType == DocumentType.INI || info.documentType == DocumentType.REGEX // file is already being parsed as regex or ini, so stop
|| info.usesSelections)) // file uses selections, so stop (because that could change the user's selections unexpectedly)
|| ValidateIfFilenameMatches(fname, wasTriggeredByParseTimer: true) // if filename is associated with a schema, it will be parsed during the schema validation, so stop
|| ValidateIfFilenameMatches(fname, true) // if filename is associated with a schema, it will be parsed during the schema validation, so stop
|| !fileExtensionsToAutoParse.Contains(ext)) // extension is not marked for auto-parsing, so stop
return;
// filename matches but it's not associated with a schema or being parsed as non-JSON/JSONL, so just parse normally
TryParseJson(ext == "jsonl" ? DocumentType.JSONL : DocumentType.JSON, true, ignoreSelections:true);
RefreshErrorFormInOwnThread(fname);
}
#endregion
}
Expand Down
8 changes: 4 additions & 4 deletions JsonToolsNppPlugin/PluginInfrastructure/NotepadPPGateway.cs
Original file line number Diff line number Diff line change
Expand Up @@ -170,16 +170,16 @@ public bool SaveCurrentFile()

public void HideDockingForm(System.Windows.Forms.Form form)
{
Win32.SendMessage(PluginBase.nppData._nppHandle,
form.Invoke(new Action(() => Win32.SendMessage(PluginBase.nppData._nppHandle,
(uint)(NppMsg.NPPM_DMMHIDE),
0, form.Handle);
0, form.Handle)));
}

public void ShowDockingForm(System.Windows.Forms.Form form)
{
Win32.SendMessage(PluginBase.nppData._nppHandle,
form.Invoke(new Action(() => Win32.SendMessage(PluginBase.nppData._nppHandle,
(uint)(NppMsg.NPPM_DMMSHOW),
0, form.Handle);
0, form.Handle)));
}

public Color GetDefaultForegroundColor()
Expand Down
4 changes: 2 additions & 2 deletions JsonToolsNppPlugin/Properties/AssemblyInfo.cs
Original file line number Diff line number Diff line change
Expand Up @@ -28,5 +28,5 @@
// Build Number
// Revision
//
[assembly: AssemblyVersion("7.1.0.2")]
[assembly: AssemblyFileVersion("7.1.0.2")]
[assembly: AssemblyVersion("7.1.0.3")]
[assembly: AssemblyFileVersion("7.1.0.3")]
Loading

0 comments on commit b1f2f3f

Please sign in to comment.