Skip to content

Commit

Permalink
improve parsing of arrs/objs missing ']' or '}'
Browse files Browse the repository at this point in the history
1. now the parser is capable of doing some backtracking
    to recover in most scenarios where an object or array
    is missing its closing '}' or ']' even in the middle of the doc.
    Previously the parser could only handle a missing ']' or '}'
    if it was at the end of the document.
2. Added dropdown menus for refreshing error form
    and exporting errors to JSON.
  • Loading branch information
molsonkiko committed Nov 28, 2023
1 parent 783a114 commit 04393e3
Show file tree
Hide file tree
Showing 10 changed files with 243 additions and 56 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,8 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
2. [For loops in RemesPath](/docs/RemesPath.md#for-loopsloop-variables-added-in-v60)
3. [`bool, s_csv` and `s_fa` vectorized arg functions](/docs/RemesPath.md#vectorized-functions) and [`randint` non-vectorized arg function](/docs/RemesPath.md#non-vectorized-functions) to RemesPath.
4. Make second argument of [`s_split` RemesPath function](/docs/RemesPath.md#vectorized-functions) optional; 1-argument variant splits on whitespace.
5. Right-click dropdown menu in [error form](/docs/README.md#error-form-and-status-bar), allowing export of errors to JSON or refreshing the form.
6. The parser is now much better at recovering when an object is missing its closing `'}'` or an array is missing its closing `']'`.

### Changed

Expand Down
33 changes: 33 additions & 0 deletions JsonToolsNppPlugin/Forms/ErrorForm.Designer.cs

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

42 changes: 42 additions & 0 deletions JsonToolsNppPlugin/Forms/ErrorForm.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
using Kbg.NppPluginNET;
using JSON_Tools.JSON_Tools;
using JSON_Tools.Utils;
using System.Runtime;

namespace JSON_Tools.Forms
{
Expand Down Expand Up @@ -137,6 +138,47 @@ private void ChangeSelectedRow(int oldIndex, int newIndex)
HandleCellOrRowClick(newRow); // move to location of error
}

/// <summary>
/// right-clicking the grid shows a drop-down where the user can choose to refresh the form or export the lints as JSON.
/// </summary>
private void ErrorForm_RightClick(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Right)
{
errorGridRightClickStrip.Show(MousePosition);
}
}

private void RefreshMenuItem_Click(object sender, EventArgs e)
{
Npp.notepad.HideDockingForm(this);
Main.OpenErrorForm(Npp.notepad.GetCurrentFilePath(), false);
}

private void ExportLintsToJsonMenuItem_Click(object sender, EventArgs e)
{
int lintCount = lints == null ? 0 : lints.Count;
if (lintCount == 0)
{
MessageBox.Show($"No JSON syntax errors (at or below {Main.settings.logger_level} level) for {fname}",
"No JSON syntax errors for this file",
MessageBoxButtons.OK, MessageBoxIcon.Information);
return;
}
var lintArrChildren = new List<JNode>();
foreach (JsonLint lint in lints)
{
var lintObj = lint.ToJson();
lintArrChildren.Add(lintObj);
}
var lintArr = new JArray(0, lintArrChildren);
Main.PrettyPrintJsonInNewFile(lintArr);
}

/// <summary>
/// hitting enter refreshes<br></br>
/// hitting the first letter of any error description goes to that error description
/// </summary>
private void ErrorForm_KeyDown(object sender, KeyEventArgs e)
{
if (e.KeyCode == Keys.Escape)
Expand Down
12 changes: 12 additions & 0 deletions JsonToolsNppPlugin/Forms/ErrorForm.resx
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,18 @@
<metadata name="Position.UserAddedColumn" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
<value>True</value>
</metadata>
<metadata name="Severity.UserAddedColumn" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
<value>True</value>
</metadata>
<metadata name="Description.UserAddedColumn" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
<value>True</value>
</metadata>
<metadata name="Position.UserAddedColumn" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
<value>True</value>
</metadata>
<metadata name="errorGridRightClickStrip.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
<value>17, 17</value>
</metadata>
<assembly alias="System.Drawing" name="System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />
<data name="$this.Icon" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>
Expand Down
46 changes: 42 additions & 4 deletions JsonToolsNppPlugin/JSONTools/JsonParser.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
using System.Text;
using System.Text.RegularExpressions;
using JSON_Tools.Utils;
using static System.Windows.Forms.LinkLabel;

namespace JSON_Tools.JSON_Tools
{
Expand Down Expand Up @@ -74,6 +75,19 @@ public static string CharDisplay(char c)
default: return $"'{c}'";
}
}

/// <summary>
/// the object {"message": this.message, "position": this.pos, "severity": this.severity}
/// </summary>
public JNode ToJson()
{
return new JObject(0, new Dictionary<string, JNode>
{
["message"] = new JNode(message),
["position"] = new JNode((long)pos),
["severity"] = new JNode(severity.ToString()),
});
}
}

/// <summary>
Expand Down Expand Up @@ -762,9 +776,9 @@ public string ParseKey(string inp)
public string ParseUnquotedKey(string inp)
{
var match = UNQUOTED_KEY_REGEX.Match(inp, ii);
if (!match.Success)
if (!match.Success || match.Index != ii)
{
HandleError($"No valid unquoted key beginning at {ii}", inp, ii, ParserState.FATAL);
HandleError($"No valid unquoted key beginning at {ii}", inp, ii, ParserState.BAD);
return null;
}
HandleError("Unquoted keys are only supported in JSON5", inp, ii, ParserState.JSON5);
Expand Down Expand Up @@ -1120,7 +1134,17 @@ public JArray ParseArray(string inp, int recursionDepth)
// a new array member of some sort
alreadySeenComma = false;
JNode newObj;
int iiBeforeParse = ii;
int utf8ExtraBeforeParse = utf8ExtraBytes;
newObj = ParseSomething(inp, recursionDepth);
if (newObj.type == Dtype.STR && ii < inp.Length && inp[ii] == ':')
{
// maybe the user forgot the closing ']' of an array that's the child of an object.
HandleError("':' (key-value separator) where ',' between array members expected. Maybe you forgot to close the array?", inp, ii, ParserState.BAD);
ii = iiBeforeParse;
utf8ExtraBytes = utf8ExtraBeforeParse;
return arr;
}
//if (includeExtraProperties)
//{
// newObj.extras = new ExtraJNodeProperties(arr, ii, children.Count);
Expand Down Expand Up @@ -1212,9 +1236,13 @@ public JObject ParseObject(string inp, int recursionDepth)
return obj;
}
// a new key-value pair
int iiBeforeKey = ii;
int utf8ExtraBeforeKey = utf8ExtraBytes;
string key = ParseKey(inp);
if (fatal || key == null)
{
// key could be null if there's a valid JSON there that is not a valid key
// this covers the possibility that the user forgot to close the object before this (presumed) key, and in fact it's meant to be a value in a parent array
return obj;
}
if (ii >= inp.Length)
Expand All @@ -1233,10 +1261,20 @@ public JObject ParseObject(string inp, int recursionDepth)
{
break;
}
if (inp[ii] == ':')
char c = inp[ii];
if (c == ':')
{
ii++;
}
else if (c == ',' || c == ']')
{
// comma or ']' after key instead of value could mean that this is supposed to be a value in a parent array,
// so we'll try bailing out here and reinterpreting the key as such
HandleError($"Found '{c}' after key {childCount} when colon expected", inp, ii, ParserState.BAD);
ii = iiBeforeKey;
utf8ExtraBytes = utf8ExtraBeforeKey;
return obj;
}
else HandleError($"No ':' between key {childCount} and value {childCount} of object", inp, ii, ParserState.BAD);
}
if (!ConsumeInsignificantChars(inp))
Expand Down Expand Up @@ -1376,7 +1414,7 @@ public JNode ParseSomething(string inp, int recursionDepth)
inp, ii + 1, ParserState.FATAL);
return new JNode(null, Dtype.NULL, startUtf8Pos);
}
HandleError("Badly located character", inp, ii, ParserState.FATAL);
HandleError("Badly located character " + (ii >= inp.Length ? "\"\\x00\"" : JNode.StrToString(inp.Substring(ii, 1), true)), inp, ii, ParserState.FATAL);
return new JNode(null, Dtype.NULL, startUtf8Pos);
}

Expand Down
2 changes: 1 addition & 1 deletion JsonToolsNppPlugin/Main.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1112,7 +1112,7 @@ private static void RestyleEverything()
/// </summary>
/// <param name="fname"></param>
/// <param name="wasAutoTriggered"></param>
private static void OpenErrorForm(string fname, bool wasAutoTriggered)
public static void OpenErrorForm(string fname, bool wasAutoTriggered)
{
bool wasVisible = errorForm != null && errorForm.Visible;
if ((!TryGetInfoForFile(fname, out JsonFileInfo info)
Expand Down
4 changes: 2 additions & 2 deletions JsonToolsNppPlugin/Properties/AssemblyInfo.cs
Original file line number Diff line number Diff line change
Expand Up @@ -29,5 +29,5 @@
// Build Number
// Revision
//
[assembly: AssemblyVersion("5.8.0.11")]
[assembly: AssemblyFileVersion("5.8.0.11")]
[assembly: AssemblyVersion("5.8.0.12")]
[assembly: AssemblyFileVersion("5.8.0.12")]
Loading

0 comments on commit 04393e3

Please sign in to comment.