diff --git a/CHANGELOG.md b/CHANGELOG.md index ddb2fdd..f429a87 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -55,6 +55,8 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ### Added +1. When a file is renamed, the name of a [tree view](/docs/README.md#json-tools-overview) associated with that file also changes to match the new name. + ### Changed ### Fixed @@ -65,6 +67,9 @@ and this project adheres to [Semantic Versioning](http://semver.org/). 2. Fix issue where RemesPath incorrectly inferred the type of (a [function](/docs/RemesPath.md#functions) `fun` followed by [indexers](/docs/RemesPath.md#indexing-and-selecting-keys)) to be the return type of `fun`. For example, running the query `sum(dict(items(@)).a)` on the JSON `{"a": [1]}` now correctly returns `1.0`, but RemesPath *used to raise an error because it assumed that `dict(items(@)).a` had the same type as `dict(items(@))`* 3. Fix very rare crash bug when using the `Value to clipboard` option of the [tree node right-click context menu](/docs/README.md#get-info-about-tree-nodes). 4. Fix bug where some invalid JSON Lines documents (for example, `[1, \n2][3]`) would be accepted by the [JSON Lines parser](/docs/README.md#json-lines-documents) despite having elements that span multiple lines. +5. Fix minor bugs with how headers are formatted in [the `s_csv` RemesPath function](/docs/RemesPath.md#vectorized-functions) and [JSON-to-CSV form](/docs/README.md#json-to-csv). +6. Fix bug where renaming a file subject to [schema validation based on filename patterns](/docs/README.md#automatic-validation-of-json-against-json-schema) would cause its tree view to be lost. +7. Fix bug where plugin actions (mainly RemesPath queries in [regex mode](/docs/README.md#regex-search-form)) that set the text of the entire document to an empty string would not do anything. Those actions will now correctly remove all the text in the document. ## [8.1.0] - 2024-08-23 diff --git a/JsonToolsNppPlugin/Forms/ErrorForm.Designer.cs b/JsonToolsNppPlugin/Forms/ErrorForm.Designer.cs index 8afb177..729e5a2 100644 --- a/JsonToolsNppPlugin/Forms/ErrorForm.Designer.cs +++ b/JsonToolsNppPlugin/Forms/ErrorForm.Designer.cs @@ -1,4 +1,7 @@ -namespace JSON_Tools.Forms +using System.Runtime.InteropServices; +using System; + +namespace JSON_Tools.Forms { partial class ErrorForm { @@ -18,6 +21,16 @@ protected override void Dispose(bool disposing) NppFormHelper.UnregisterFormIfModeless(this, false); components.Dispose(); } + if (ptrTitleBuf != IntPtr.Zero) + { + Marshal.FreeHGlobal(ptrTitleBuf); + ptrTitleBuf = IntPtr.Zero; + } + if (ptrNppTbData != IntPtr.Zero) + { + Marshal.FreeHGlobal(ptrNppTbData); + ptrNppTbData = IntPtr.Zero; + } base.Dispose(disposing); } diff --git a/JsonToolsNppPlugin/Forms/ErrorForm.cs b/JsonToolsNppPlugin/Forms/ErrorForm.cs index 6ceb4c9..cd8f4c9 100644 --- a/JsonToolsNppPlugin/Forms/ErrorForm.cs +++ b/JsonToolsNppPlugin/Forms/ErrorForm.cs @@ -20,6 +20,8 @@ public partial class ErrorForm : Form public string fname; public List lints; private bool isRepopulatingErrorGrid; + public IntPtr ptrNppTbData = IntPtr.Zero; + public IntPtr ptrTitleBuf = IntPtr.Zero; public ErrorForm(string fname, List lints) { diff --git a/JsonToolsNppPlugin/Forms/TreeViewer.Designer.cs b/JsonToolsNppPlugin/Forms/TreeViewer.Designer.cs index 8f6e469..55a2af0 100644 --- a/JsonToolsNppPlugin/Forms/TreeViewer.Designer.cs +++ b/JsonToolsNppPlugin/Forms/TreeViewer.Designer.cs @@ -1,4 +1,7 @@ -namespace JSON_Tools.Forms +using System; +using System.Runtime.InteropServices; + +namespace JSON_Tools.Forms { partial class TreeViewer { @@ -24,6 +27,16 @@ protected override void Dispose(bool disposing) findReplaceForm.Dispose(); findReplaceForm = null; } + if (ptrTitleBuf != IntPtr.Zero) + { + Marshal.FreeHGlobal(ptrTitleBuf); + ptrTitleBuf = IntPtr.Zero; + } + if (ptrNppTbData != IntPtr.Zero) + { + Marshal.FreeHGlobal(ptrNppTbData); + ptrNppTbData = IntPtr.Zero; + } if (disposing && (components != null)) { NppFormHelper.UnregisterFormIfModeless(this, false); diff --git a/JsonToolsNppPlugin/Forms/TreeViewer.cs b/JsonToolsNppPlugin/Forms/TreeViewer.cs index 239f8bf..f101015 100644 --- a/JsonToolsNppPlugin/Forms/TreeViewer.cs +++ b/JsonToolsNppPlugin/Forms/TreeViewer.cs @@ -2,12 +2,14 @@ using System.Collections.Generic; using System.Drawing; using System.Linq; +using System.Runtime.InteropServices; using System.Text; using System.Text.RegularExpressions; using System.Windows.Forms; using JSON_Tools.JSON_Tools; using JSON_Tools.Utils; using Kbg.NppPluginNET; +using Kbg.NppPluginNET.PluginInfrastructure; namespace JSON_Tools.Forms { @@ -23,6 +25,15 @@ public partial class TreeViewer : Form /// public string fname; + private int MAX_LEN_TITLE_BUFFER = 256; + + /// + /// a pointer to the buffer containing the name displayed at the top of this docking form + /// + public IntPtr ptrTitleBuf = IntPtr.Zero; + + public IntPtr ptrNppTbData = IntPtr.Zero; + /// /// Maps TreeNode.FullPath to the TreeNode's corresponding JNode /// @@ -1454,25 +1465,40 @@ private void DocumentTypeComboBox_SelectedIndexChanged(object sender, EventArgs } /// - /// Just the filename, no directory information.

- /// If no fname supplied, gets the relative filename for this TreeViewer's fname. + /// Change the fname attribute of this.

+ /// Also change the title of the UI element (the docking form that the user actually sees) ///
- public string RelativeFilename(string fname = null) + /// + public void Rename(string newFname, bool isFilename) { - if (fname == null) fname = this.fname; - string[] fnameSplit = fname.Split('\\'); - return fnameSplit[fnameSplit.Length - 1]; + fname = newFname; + SetTitleBuffer(newFname, isFilename); + Marshal.WriteIntPtr(ptrNppTbData, IntPtr.Size, ptrTitleBuf); + Win32.SendMessage(PluginBase.nppData._nppHandle, (uint)NppMsg.NPPM_DMMUPDATEDISPINFO, 0, Handle); } /// - /// Change the fname attribute of this.

- /// We would like to be able to change the title of the UI element as well, - /// but it seems pretty hard to do from C#. + /// sets this.ptrTitleBuf to a pointer to an unmanaged Unicode char array containing the title of this TreeViewer's docking form.

+ /// If isFilename, strips the directory name away from the beginning of title. ///
- /// - public void Rename(string newFname) + public IntPtr SetTitleBuffer(string title, bool isFilename) { - fname = newFname; + if (isFilename) + { + string[] fnameSplit = fname.Split('\\'); + title = fnameSplit[fnameSplit.Length - 1]; + } + string defaultNameFormat = "Json Tree View for {0}"; + string nameFormat = (Translator.TryGetTranslationAtPath(new string[] { "forms", "TreeViewer", "title" }, out JNode node) && node.value is string s && s.Contains("{0}")) ? s : defaultNameFormat; + string fullTitle = nameFormat.Replace("{0}", title); + int maxCharsTitleBuf = MAX_LEN_TITLE_BUFFER / Marshal.SystemDefaultCharSize - 1; + if (fullTitle.Length > maxCharsTitleBuf) + fullTitle = fullTitle.Substring(0, maxCharsTitleBuf - 3) + "..."; + if (ptrTitleBuf == IntPtr.Zero) + ptrTitleBuf = Marshal.AllocHGlobal(MAX_LEN_TITLE_BUFFER); + Marshal.Copy(new byte[MAX_LEN_TITLE_BUFFER], 0, ptrTitleBuf, MAX_LEN_TITLE_BUFFER); + Marshal.Copy(fullTitle.ToCharArray(), 0, ptrTitleBuf, fullTitle.Length); + return ptrTitleBuf; } } } diff --git a/JsonToolsNppPlugin/JSONTools/JsonTabularize.cs b/JsonToolsNppPlugin/JSONTools/JsonTabularize.cs index e594e93..65e0f60 100644 --- a/JsonToolsNppPlugin/JSONTools/JsonTabularize.cs +++ b/JsonToolsNppPlugin/JSONTools/JsonTabularize.cs @@ -136,8 +136,8 @@ public JsonFormat outputFormat } public JsonTabularizer(JsonTabularizerStrategy strategy = JsonTabularizerStrategy.DEFAULT) // , - // JsonFormat outputFormat = JsonFormat.REC) - // don't worry about outputFormat, because other output formats aren't supported + // JsonFormat outputFormat = JsonFormat.REC) + // don't worry about outputFormat, because other output formats aren't supported { this.strategy = strategy; //if ((outputFormat & JsonFormat.ANY_TABLE) != 0) _outputFormat = outputFormat; @@ -446,14 +446,14 @@ private void AnyTableToRecord(JNode obj, } notArrDict = ResolveHang(notArrDict, keySep); if (lenArr == 0) - { + { // A 0-length array should be dealt with by adding a single row with an empty value // for the array's parent key foreach (string k in arrDict.Keys) notArrDict[k] = new JNode("", Dtype.STR, 0); result.Add(new JObject(0, notArrDict)); return; - } + } for (int ii = 0; ii < lenArr; ii++) { Dictionary newrec = new Dictionary(restOfRow); @@ -650,7 +650,7 @@ private void BuildTableHelper_FULL_RECURSIVE(JNode obj, private void BuildTableHelper_STRINGIFY_ITERABLES(JNode obj, List result, string keySep = ".") { if (obj is JArray) - { + { foreach (JNode child in ((JArray)obj).children) { Dictionary row = new Dictionary(); @@ -724,8 +724,8 @@ private void BuildTableHelper_STRINGIFY_ITERABLES(JNode obj, List result, // are filled by empty strings if (ii >= v.Length) newrec[k] = new JNode("", Dtype.STR, 0); - else - { + else + { JNode subchild = v[ii]; if (subchild is JArray subarr) newrec[k] = new JNode(subarr.ToString(), Dtype.STR, 0); @@ -733,7 +733,7 @@ private void BuildTableHelper_STRINGIFY_ITERABLES(JNode obj, List result, newrec[k] = new JNode(subobj.ToString(), Dtype.STR, 0); else newrec[k] = subchild; - } + } } // now add in all the non-array values foreach (string k in notArrDict.Keys) @@ -795,17 +795,17 @@ public JArray BuildTable(JNode obj, Dictionary schema, string ke return new JArray(0, result); } - /// - /// If string s contains the delimiter, '\r', '\n', or a literal quote character, append (the string wrapped in quotes) to sb.

- /// If s contains literal quote character, it is escaped by doubling it up according to the CSV RFC 4180 (https://www.ietf.org/rfc/rfc4180.txt)

- /// Otherwise, append s to sb unchanged - ///
- /// CSV delimiter ('\x00' if not specified) - /// CSV quote char ('\x00' if not specified) - /// - public static void ApplyQuotesIfNeeded(StringBuilder sb, string s, char delim, char quote) + /// + /// If string s contains the delimiter, '\r', '\n', or a literal quote character, append (the string wrapped in quotes) to sb.

+ /// If s contains literal quote character, it is escaped by doubling it up according to the CSV RFC 4180 (https://www.ietf.org/rfc/rfc4180.txt)

+ /// Otherwise, append s to sb unchanged + ///
+ /// CSV delimiter ('\x00' if not specified) + /// CSV quote char ('\x00' if not specified) + /// + public static void ApplyQuotesIfNeeded(StringBuilder sb, string s, char delim, char quote) { - if (s.IndexOfAny(new char[] {delim, '\r', '\n', quote}) >= 0) + if (s.IndexOfAny(new char[] { delim, '\r', '\n', quote }) >= 0) { sb.Append(quote); for (int ii = 0; ii < s.Length; ii++) @@ -813,46 +813,46 @@ public static void ApplyQuotesIfNeeded(StringBuilder sb, string s, char delim, c char c = s[ii]; sb.Append(c); if (c == quote) - sb.Append(quote); + sb.Append(quote); } sb.Append(quote); } else sb.Append(s); } - /// - /// append the CSV (RFC 4180 adherent, using quote character = quote, delimiter = delim) representation of jnode's value to sb. - /// - /// CSV delimiter ('\x00' if not specified) - /// CSV quote char ('\x00' if not specified) - /// if true, represent true as "1" and false as "0". If false, represent them as "true" and "false" respectively - public static void CsvStringToSb(StringBuilder sb, JNode jnode, char delim, char quote, bool boolsAsInts) + /// + /// append the CSV (RFC 4180 adherent, using quote character = quote, delimiter = delim) representation of jnode's value to sb. + /// + /// CSV delimiter ('\x00' if not specified) + /// CSV quote char ('\x00' if not specified) + /// if true, represent true as "1" and false as "0". If false, represent them as "true" and "false" respectively + public static void CsvStringToSb(StringBuilder sb, JNode jnode, char delim, char quote, bool boolsAsInts) { string val; - switch (jnode.type) - { - case Dtype.STR: - val = (string)jnode.value; - break; // only apply quotes if internal delims, quotes, or newlines - //case Dtype.DATE: - // val = ((DateTime)jnode.value).ToString("yyyy-MM-dd"); - // break; - //case Dtype.DATETIME: - // val = ((DateTime)jnode.value).ToString("yyyy-MM-dd hh:mm:ss"); - // break; - case Dtype.NULL: - return; // nulls should be empty entries - case Dtype.BOOL: + switch (jnode.type) + { + case Dtype.STR: + val = (string)jnode.value; + break; // only apply quotes if internal delims, quotes, or newlines + //case Dtype.DATE: + // val = ((DateTime)jnode.value).ToString("yyyy-MM-dd"); + // break; + //case Dtype.DATETIME: + // val = ((DateTime)jnode.value).ToString("yyyy-MM-dd hh:mm:ss"); + // break; + case Dtype.NULL: + return; // nulls should be empty entries + case Dtype.BOOL: sb.Append((bool)jnode.value ? (boolsAsInts ? "1" : "true") : (boolsAsInts ? "0" : "false")); - return; - default: - val = jnode.ToString(); - break; - } + return; + default: + val = jnode.ToString(); + break; + } ApplyQuotesIfNeeded(sb, val, delim, quote); - } + } public string TableToCsv(JArray table, char delim = ',', char quote = '"', string newline = "\n", string[] header = null, bool boolsAsInts = false) { @@ -878,8 +878,7 @@ public string TableToCsv(JArray table, char delim = ',', char quote = '"', strin StringBuilder sb = new StringBuilder(); for (int ii = 0; ii < header.Length; ii++) { - string col = header[ii]; - ApplyQuotesIfNeeded(sb, JNode.StrToString(col, false), delim, quote); + ApplyQuotesIfNeeded(sb, header[ii], delim, quote); if (ii < header.Length - 1) sb.Append(delim); } sb.Append(newline); diff --git a/JsonToolsNppPlugin/JSONTools/RemesPathFunctions.cs b/JsonToolsNppPlugin/JSONTools/RemesPathFunctions.cs index 3d86b32..feff352 100644 --- a/JsonToolsNppPlugin/JSONTools/RemesPathFunctions.cs +++ b/JsonToolsNppPlugin/JSONTools/RemesPathFunctions.cs @@ -2735,6 +2735,24 @@ private static void CacheResultsOfRegexSearch(string input, Regex rex, HeaderHan regexSearchResultCache[(input, argsAsJArrayString)] = output; } + /// + /// Unquote a string that is quoted according to RFC 4180 conventions, where str is enclosed in quoteChars and each literal quoteChar is doubled.

+ /// If str is not wrapped in quoteChars, or if quoteChar is '\x00', return str. + /// EXAMPLES:

+ /// * UnquoteCsvQuotedString("|foo||bar|", '|', "|", "||") would return "foo|bar"

+ /// * UnquoteCsvQuotedString("^^^quz^", '^', "^", "^^") would return "^quz"

+ /// * UnquoteCsvQuotedString("bllo", '"', "\"", "\"\"") would return "bllo" (because it's not wrapped in quotes)

+ /// * UnquoteCsvQuotedString("^bllo^", '\x00', "\x00", "\x00\x00") would return "bllo" (because quoteChar is '\x00') + ///
+ /// unless quoteChar == '\x00', must be new string(quoteChar, 1) + /// unless quoteChar == '\x00', must be new string(quoteChar, 2) + private static string UnquoteCsvQuotedString(string str, char quoteChar, string quoteStr, string doubleQuoteStr) + { + if (quoteChar > 0 && str.Length > 0 && str[0] == quoteChar) + return str.Substring(1, str.Length - 2).Replace(doubleQuoteStr, quoteStr); + return str; + } + /// /// return an array of strings(if rex has 0 or 1 capture group(s)) or an array of arrays of strings (if there are multiple capture groups)

/// The arguments in args at index firstOptionalArgNum onward will be treated as the 0-based indices of capture groups to parse as numbers

@@ -2772,9 +2790,7 @@ JNode matchEvaluator(string mValue, bool tryParseAsNumber, int jnodePosition) parsed.value = parsedStr.Substring(1, parsedStr.Length - 2).Replace(doubleQuoteStr, quoteStr); return parsed; } - if (csvQuoteChar > 0 && mValue.Length > 0 && mValue[0] == csvQuoteChar) - return new JNode(mValue.Substring(1, mValue.Length - 2).Replace(doubleQuoteStr, quoteStr), jnodePosition); - return new JNode(mValue, jnodePosition); + return new JNode(UnquoteCsvQuotedString(mValue, csvQuoteChar, quoteStr, doubleQuoteStr), jnodePosition); } int minGroupNum = headerHandling == HeaderHandlingInCsv.INCLUDE_FULL_MATCH_AS_FIRST_ITEM ? 0 : 1; int nColumns = minGroupNum >= maxGroupNum ? 1 : maxGroupNum + 1 - minGroupNum; @@ -2828,7 +2844,7 @@ JNode matchEvaluator(string mValue, bool tryParseAsNumber, int jnodePosition) { if (headerHandling == HeaderHandlingInCsv.MAP_HEADER_TO_ROWS) { - header = new List { mValue }; + header = new List { UnquoteCsvQuotedString(mValue, csvQuoteChar, quoteStr, doubleQuoteStr) }; } } if (parseMatchesAsRow) @@ -2852,7 +2868,7 @@ JNode matchEvaluator(string mValue, bool tryParseAsNumber, int jnodePosition) { header = new List(nColumns); for (int ii = minGroupNum; ii <= maxGroupNum; ii++) - header.Add(m.Groups[ii].Value); + header.Add(UnquoteCsvQuotedString(m.Groups[ii].Value, csvQuoteChar, quoteStr, doubleQuoteStr)); } } if (parseMatchesAsRow) diff --git a/JsonToolsNppPlugin/Main.cs b/JsonToolsNppPlugin/Main.cs index 8558d7f..aec1c8f 100644 --- a/JsonToolsNppPlugin/Main.cs +++ b/JsonToolsNppPlugin/Main.cs @@ -412,7 +412,6 @@ static internal void FileBeforeRename(IntPtr bufferRenamedId) static internal void FileRenamed(IntPtr bufferRenamedId) { string bufferNewName = Npp.notepad.GetFilePath(bufferRenamedId); - ValidateIfFilenameMatches(bufferNewName); if (bufferNewName == schemasToFnamePatternsFname) { ParseSchemasToFnamePatternsFile(); @@ -424,7 +423,7 @@ static internal void FileRenamed(IntPtr bufferRenamedId) if (TryGetInfoForFile(bufferOldName, out JsonFileInfo oldInfo)) { jsonFilesRenamed.Remove(bufferRenamedId); - oldInfo.tv?.Rename(bufferNewName); + oldInfo.tv?.Rename(bufferNewName, true); jsonFileInfos.Remove(bufferOldName); jsonFileInfos[bufferNewName] = oldInfo; } @@ -434,6 +433,7 @@ static internal void FileRenamed(IntPtr bufferRenamedId) grepperForm.tv.fname = bufferNewName; shouldRenameGrepperForm = false; } + ValidateIfFilenameMatches(bufferNewName); } static internal void FileRenameCancel(IntPtr bufferRenamedId) @@ -1340,7 +1340,9 @@ private static void DisplayErrorForm(ErrorForm form) NppTbData _nppTbData = new NppTbData(); _nppTbData.hClient = form.Handle; - _nppTbData.pszName = form.Text; + IntPtr ptrTitleBuf = Marshal.StringToHGlobalUni(form.Text); + errorForm.ptrTitleBuf = ptrTitleBuf; + _nppTbData.pszName = ptrTitleBuf; // the dlgDlg should be the index of funcItem where the current function pointer is in // this case is 15.. so the initial value of funcItem[15]._cmdID - not the updated internal one ! _nppTbData.dlgID = errorFormId; @@ -1349,8 +1351,8 @@ private static void DisplayErrorForm(ErrorForm form) _nppTbData.hIconTab = (uint)dockingFormIcon.Handle; _nppTbData.pszModuleName = PluginName; IntPtr _ptrNppTbData = Marshal.AllocHGlobal(Marshal.SizeOf(_nppTbData)); + errorForm.ptrNppTbData = _ptrNppTbData; Marshal.StructureToPtr(_nppTbData, _ptrNppTbData, false); - Win32.SendMessage(PluginBase.nppData._nppHandle, (uint)NppMsg.NPPM_DMMREGASDCKDLG, 0, _ptrNppTbData); Npp.notepad.ShowDockingForm(form); } @@ -1580,14 +1582,7 @@ public static void OpenJsonTree(DocumentType documentType = DocumentType.JSON) openTreeViewer = new TreeViewer(json); info.tv = openTreeViewer; jsonFileInfos[activeFname] = info; - DisplayJsonTree(openTreeViewer, json, GetNameForJsonTree(openTreeViewer), usesSelections, documentType, parserState == ParserState.FATAL); - } - - private static string GetNameForJsonTree(TreeViewer tv) - { - string defaultNameFormat = "Json Tree View for {0}"; - string nameFormat = (Translator.TryGetTranslationAtPath(new string[] { "forms", "TreeViewer", "title" }, out JNode node) && node.value is string s) ? s : defaultNameFormat; - return Translator.TryTranslateWithFormatting(defaultNameFormat, nameFormat, tv.RelativeFilename()); + DisplayJsonTree(openTreeViewer, json, activeFname, usesSelections, documentType, parserState == ParserState.FATAL); } static void OpenGrepperForm() @@ -1618,17 +1613,18 @@ public static void DisplayJsonTree(TreeViewer treeViewer, JNode json, string tit NppTbData _nppTbData = new NppTbData(); _nppTbData.hClient = treeViewer.Handle; - _nppTbData.pszName = title; + _nppTbData.pszName = treeViewer.SetTitleBuffer(title, grepperForm == null || grepperForm.tv == null || treeViewer != grepperForm.tv); // the dlgDlg should be the index of funcItem where the current function pointer is in // this case is 15.. so the initial value of funcItem[15]._cmdID - not the updated internal one ! _nppTbData.dlgID = jsonTreeId; // define the default docking behaviour - _nppTbData.uMask = NppTbMsg.DWS_DF_CONT_RIGHT | NppTbMsg.DWS_ICONTAB | NppTbMsg.DWS_ICONBAR; + _nppTbData.uMask = NppTbMsg.DWS_DF_CONT_RIGHT | NppTbMsg.DWS_ICONTAB | NppTbMsg.DWS_ICONBAR | NppTbMsg.DWS_ADDINFO; // DWS_ADDINFO necessary to allow changing name later _nppTbData.hIconTab = (uint)dockingFormIcon.Handle; _nppTbData.pszModuleName = PluginName; IntPtr _ptrNppTbData = Marshal.AllocHGlobal(Marshal.SizeOf(_nppTbData)); Marshal.StructureToPtr(_nppTbData, _ptrNppTbData, false); - + treeViewer.ptrNppTbData = _ptrNppTbData; + Npp.SetLangBasedOnDocType(fatalParserError, usesSelections, documentType); Win32.SendMessage(PluginBase.nppData._nppHandle, (uint)NppMsg.NPPM_DMMREGASDCKDLG, 0, _ptrNppTbData); // Following message will toogle both menu item state and toolbar button diff --git a/JsonToolsNppPlugin/PluginInfrastructure/Docking_h.cs b/JsonToolsNppPlugin/PluginInfrastructure/Docking_h.cs index bb256da..d6c7e20 100644 --- a/JsonToolsNppPlugin/PluginInfrastructure/Docking_h.cs +++ b/JsonToolsNppPlugin/PluginInfrastructure/Docking_h.cs @@ -43,7 +43,7 @@ public enum NppTbMsg : uint public struct NppTbData { public IntPtr hClient; // HWND: client Window Handle - public string pszName; // TCHAR*: name of plugin (shown in window) + public IntPtr pszName; // TCHAR*: name of plugin (shown in window) public int dlgID; // int: a funcItem provides the function pointer to start a dialog. Please parse here these ID // user modifications public NppTbMsg uMask; // UINT: mask params: look to above defines diff --git a/JsonToolsNppPlugin/PluginInfrastructure/ScintillaGateway.cs b/JsonToolsNppPlugin/PluginInfrastructure/ScintillaGateway.cs index ea5a126..63f1770 100644 --- a/JsonToolsNppPlugin/PluginInfrastructure/ScintillaGateway.cs +++ b/JsonToolsNppPlugin/PluginInfrastructure/ScintillaGateway.cs @@ -1781,6 +1781,11 @@ public void Clear() /// Replace the contents of the document with the argument text. (Scintilla feature 2181) public unsafe void SetText(string text) { + if (text.Length == 0) + { + ClearAll(); + return; + } fixed (byte* textPtr = Encoding.UTF8.GetBytes(text)) { Win32.SendMessage(scintilla, SciMsg.SCI_SETTEXT, (IntPtr) Unused, (IntPtr) textPtr); diff --git a/JsonToolsNppPlugin/Properties/AssemblyInfo.cs b/JsonToolsNppPlugin/Properties/AssemblyInfo.cs index 4f42941..3f213e2 100644 --- a/JsonToolsNppPlugin/Properties/AssemblyInfo.cs +++ b/JsonToolsNppPlugin/Properties/AssemblyInfo.cs @@ -28,5 +28,5 @@ // Build Number // Revision // -[assembly: AssemblyVersion("8.1.0.3")] -[assembly: AssemblyFileVersion("8.1.0.3")] +[assembly: AssemblyVersion("8.1.0.4")] +[assembly: AssemblyFileVersion("8.1.0.4")] diff --git a/JsonToolsNppPlugin/Tests/JsonTabularizerTests.cs b/JsonToolsNppPlugin/Tests/JsonTabularizerTests.cs index fb2d5ab..30de5ed 100644 --- a/JsonToolsNppPlugin/Tests/JsonTabularizerTests.cs +++ b/JsonToolsNppPlugin/Tests/JsonTabularizerTests.cs @@ -503,12 +503,15 @@ public static bool Test() ( "[{\"a\": 1, \"b\": \"a,b\"}, {\"a\": 2, \"b\": \"c\"}]", "a,b\n1,'a,b'\n2,c\n", ',', '\'', null, false, "\n" ), // delims in values ( "[{\"a,b\": 1, \"b\": \"a\"}, {\"a,b\": 2, \"b\": \"b\"}]", "\"a,b\",b\r\n1,a\r\n2,b\r\n", ',', '"', null, false, "\r\n" ), ( "[{\"a,b\": 1, \"b\": \"a\"}, {\"a,b\": 2, \"b\": \"b\"}]", "'a,b',b\r\n1,a\r\n2,b\r\n", ',', '\'', null, false, "\r\n" ), - ( "[{\"a,b\": 1, \"b\": \"a\"}, {\"a,b\": 2, \"b\": \"b\"}]", // internal delims in column header + ("[{\"a\\\"b\": 1.5, \"c\": \"a\"}, {\"a\\\"b\": true, \"c\": null}]", "\"a\"\"b\"\tc\r1.5\ta\rtrue\t\r", '\t', '"', null, false, "\r"), // internal '"' in column header when '"' is quote char + ("[{\"a\\\"b\": true, \"c\": false}]", "c,\"a\"\"b\"\r\n0,1\r\n", ',', '"', new string[]{"c", "a\"b"}, true, "\r\n"), // internal '"' in column header when '"' is quote char, and header is user-supplied + ("[{\"a'b\": 1.5, \"c\": \"a\"}, {\"c\": null, \"a'b\": true}]", "'a''b'\tc\n1.5\ta\ntrue\t\n", '\t', '\'', null, false, "\n"), // internal quote char in column header (when '"' is not quote char) + ( "[{\"a,b\": 1, \"b\": \"a\"}, {\"a,b\": 2, \"b\": \"b\"}]", // internal delims in column header "b,\"a,b\"\r\na,1\r\nb,2\r\n", ',', '"', new string[]{"b", "a,b"}, false, "\r\n" ), ( "[{\"a\\tb\": 1, \"b\": \"a\"}, {\"a\\tb\": 2, \"b\": \"b\"}]", // \t in column header when \t is delim - "a\\tb\tb\r\n1\ta\r\n2\tb\r\n", + "\"a\tb\"\tb\r\n1\ta\r\n2\tb\r\n", '\t', '"', null, false, "\r\n" ), ( "[{\"a\": 1, \"b\": \"a\\tb\"}, {\"a\": 2, \"b\": \"c\"}]", diff --git a/JsonToolsNppPlugin/Tests/RemesPathTests.cs b/JsonToolsNppPlugin/Tests/RemesPathTests.cs index 44052a6..bd95dff 100644 --- a/JsonToolsNppPlugin/Tests/RemesPathTests.cs +++ b/JsonToolsNppPlugin/Tests/RemesPathTests.cs @@ -607,13 +607,13 @@ public static bool Test() ", 3, null, null, null, n, 0, -1)", "[[\"a\", \"-3\", NaN], [\"baz\", \"quz\", -Infinity]]"), // 4-column, map header to values, '\t' separator, '\n' newline, parse 2nd-to-last column as numbers - new Query_DesiredResult("s_csv(`col1\\tcol2\\tcol3\\tcol4\\n" + + new Query_DesiredResult("s_csv(`col1\\tcol2\\t\"col\"\"3\"\\tcol4\\n" + "ab\\tcd\\t0xfa\\tefg\\n" + "hi\\tjk\\t-5.3E000\\tlmn\\n" + "opq\\trs\\t.7e-11\\ttuv\\n" + "wx\\ty\\t+85\\tz`" + ", 4, `\\t`, `\\n`, , d, -2)", - "[{\"col1\":\"ab\",\"col2\":\"cd\",\"col3\":250,\"col4\":\"efg\"},{\"col1\":\"hi\",\"col2\":\"jk\",\"col3\":-5.3,\"col4\":\"lmn\"},{\"col1\":\"opq\",\"col2\":\"rs\",\"col3\":7E-12,\"col4\":\"tuv\"},{\"col1\":\"wx\",\"col2\":\"y\",\"col3\":85,\"col4\":\"z\"}]"), + "[{\"col1\":\"ab\",\"col2\":\"cd\",\"col\\\"3\":250,\"col4\":\"efg\"},{\"col1\":\"hi\",\"col2\":\"jk\",\"col\\\"3\":-5.3,\"col4\":\"lmn\"},{\"col1\":\"opq\",\"col2\":\"rs\",\"col\\\"3\":7E-12,\"col4\":\"tuv\"},{\"col1\":\"wx\",\"col2\":\"y\",\"col\\\"3\":85,\"col4\":\"z\"}]"), // 1-column, map header to values, '^' delimiter, '$' quote character, '\r' newline new Query_DesiredResult("s_csv(`a\\r" + "$b^c$\\r" + @@ -631,19 +631,22 @@ public static bool Test() ", 1, `^`, `\\r`, `$`, , 0)", // omit header arg because it is default "[1, 0.3, 5, -7.2]"), // 1-column, map header to values, '^' delimiter, '$' quote character, '\r' newline, parse as numbers - new Query_DesiredResult("s_csv(`foo\\r" + + new Query_DesiredResult("s_csv(`$foo$\\r" + "1\\r" + ".3\\r" + "5\\r" + "$-7.2$`" + ", 1, `^`, `\\r`, `$`, d, 0)", "[{\"foo\": 1}, {\"foo\": 0.3}, {\"foo\": 5}, {\"foo\": \"-7.2\"}]"), + // 1-column, map header to values, ',' delimiter, '\'' quote char, '\n' newline + new Query_DesiredResult("s_csv(`ba\\tz\\n'bl,ar'\\n5.75 qun`, 1, `,`, `\\n`, `'`, d)", + "[{\"ba\\tz\": \"bl,ar\"}, {\"ba\\tz\": \"5.75 qun\"}]"), // 2-column, map header to values, '|' delimiter, '$' quote char, '\r\n' newline, parse first col as numbers - new Query_DesiredResult("s_csv(`foo|bar\\r\\n" + + new Query_DesiredResult("s_csv(`foo|$$$bar$\\r\\n" + "1|3\\r\\n" + "-5.5|$3|4$\\r\\n" + "$$|`, 2, `|`, `\\r\\n`, `$`, d, 0)", - "[{\"foo\":1,\"bar\":\"3\"},{\"foo\":-5.5,\"bar\":\"3|4\"},{\"foo\":\"\",\"bar\":\"\"}]"), + "[{\"foo\":1,\"$bar\":\"3\"},{\"foo\":-5.5,\"$bar\":\"3|4\"},{\"foo\":\"\",\"$bar\":\"\"}]"), // 2-column, skip header, ']' delimiter, '\'' quote char, '\r\n' newline new Query_DesiredResult("s_csv(`foo]bar\\r\\n" + "1]3\\r\\n" + diff --git a/JsonToolsNppPlugin/Tests/UserInterfaceTests.cs b/JsonToolsNppPlugin/Tests/UserInterfaceTests.cs index d633bff..12cce84 100644 --- a/JsonToolsNppPlugin/Tests/UserInterfaceTests.cs +++ b/JsonToolsNppPlugin/Tests/UserInterfaceTests.cs @@ -776,6 +776,8 @@ public static bool Test() ("tree_query", new object[]{"s_csv(@, 1,`\\t`,`\\r\\n`,`'` ,h)"}), ("treenode_click", new object[]{new string[] { "1 : \"{\\\"ö\\\": \\\"3 4\\\"}\"" } }), ("treenode_click", new object[]{new string[] { "3 : \"{\\\"5\\\": [6]}\"" } }), + ("tree_query", new object[]{"@ = ``"}), // test that using RemesPath to clear text of document works + ("compare_text", new object[]{""}), }; var messages = new List(); diff --git a/docs/README.md b/docs/README.md index 2733cc3..c8a82b2 100644 --- a/docs/README.md +++ b/docs/README.md @@ -366,7 +366,7 @@ Below is an example of searching *only direct children or grandchildren* (by unc *Added in version 1.2.0* -Some JSON also has a somewhat __tabular__ format, such that it is amenable to conversion to a CSV file. The JSON in this example is a particularly simple case of this. +Some JSON also has a somewhat __tabular__ format, such that it is amenable to conversion to a CSV file. The JSON in [this example](#json-tools-overview) is a particularly simple case of this. This app has a [form](/docs/json-to-csv.md) that allows conversion of such JSON to a tabular format. Remember that even if the JSON file as a whole can't be "tabularized" (or *can*, but you don't *want* to), you can use a RemesPath query to select the part that you want to tabularize. diff --git a/most recent errors.txt b/most recent errors.txt index eac6f8b..a79b785 100644 --- a/most recent errors.txt +++ b/most recent errors.txt @@ -1,4 +1,4 @@ -Test results for JsonTools v8.1.0.3 on Notepad++ 8.6.9 64bit +Test results for JsonTools v8.1.0.4 on Notepad++ 8.6.9 64bit NOTE: Ctrl-F (regular expressions *on*) for "Failed [1-9]\d*" to find all failed tests Tests failed: YAML dumper ========================= @@ -107,7 +107,7 @@ Testing RemesPath parser and compiler The queried JSON in the RemesParser tests is named foo:{"foo": [[0, 1, 2], [3.0, 4.0, 5.0], [6.0, 7.0, 8.0]], "bar": {"a": false, "b": ["a`g", "bah"]}, "baz": "z", "quz": {}, "jub": [], "guzo": [[[1]], [[2], [3]]], "7": [{"foo": 2}, 1], "_": {"0": 0}} Failed 0 tests. -Passed 529 tests. +Passed 530 tests. ========================= Testing RemesPath throws errors on bad inputs ========================= @@ -152,7 +152,7 @@ Testing JSON tabularizer ========================= Failed 0 tests. -Passed 61 tests. +Passed 64 tests. ========================= Testing CSV sniffer ========================= @@ -199,40 +199,40 @@ Testing UI tests ========================= Failed 0 tests -Passed 346 tests +Passed 348 tests ========================= Testing JsonParser performance ========================= Preview of json: [{"A": "Ky'c^g#~)0", "a": 1850111954, "b": 9318359041, "B": "Oyi:/ xxe2", "C": "sKCSa_^7Gg", "c": 7974777124, "d": 2670309238, "D": "0d_K)HmX!.", "E": ".uM*Z{0EJ_", "e": 6958410336, "f": 8050244728, "F": "1%SG_A!xB\t", "g": 3799657125, "G": "il1^k\\\nat*", "H": {"a": 6079042826, "b": 7292804611, "c" ... -To convert JSON string of size 89556 into JNode took 2.928 +/- 2.167 ms over 32 trials -Load times (ms): 2, 1, 1, 5, 1, 2, 7, 9, 1, 1, 3, 1, 1, 6, 1, 1, 1, 3, 1, 1, 6, 1, 1, 1, 4, 1, 1, 6, 1, 1, 1, 4 +To convert JSON string of size 89556 into JNode took 3.052 +/- 2.149 ms over 32 trials +Load times (ms): 2, 1, 2, 6, 2, 1, 1, 4, 1, 1, 7, 1, 1, 1, 4, 1, 1, 6, 1, 1, 1, 5, 1, 2, 6, 1, 1, 1, 7, 2, 1, 7 ========================= Performance tests for RemesPath (float arithmetic) ========================= -Compiling query "@[@[:].a * @[:].t < @[:].e]" took 0.064 ms the first time, including approximately 0.124 ms to tokenize the query. Subsequent executions are effectively free due to caching. -To run pre-compiled query "@[@[:].a * @[:].t < @[:].e]" on JNode from JSON of size 89556 into took 0.024 +/- 0.007 ms over 40 trials -Query times (ms): 0.063, 0.033, 0.022, 0.023, 0.022, 0.022, 0.022, 0.028, 0.023, 0.022, 0.022, 0.022, 0.022, 0.025, 0.022, 0.022, 0.023, 0.022, 0.022, 0.024, 0.023, 0.022, 0.022, 0.023, 0.022, 0.024, 0.022, 0.023, 0.022, 0.022, 0.022, 0.025, 0.022, 0.022, 0.022, 0.023, 0.022, 0.026, 0.022, 0.022 +Compiling query "@[@[:].a * @[:].t < @[:].e]" took 0.056 ms the first time, including approximately 0.092 ms to tokenize the query. Subsequent executions are effectively free due to caching. +To run pre-compiled query "@[@[:].a * @[:].t < @[:].e]" on JNode from JSON of size 89556 into took 0.045 +/- 0.013 ms over 40 trials +Query times (ms): 0.106, 0.072, 0.052, 0.04, 0.04, 0.041, 0.042, 0.058, 0.039, 0.037, 0.039, 0.038, 0.038, 0.051, 0.039, 0.038, 0.044, 0.062, 0.042, 0.062, 0.042, 0.044, 0.04, 0.041, 0.038, 0.059, 0.037, 0.038, 0.039, 0.039, 0.039, 0.054, 0.04, 0.038, 0.038, 0.041, 0.041, 0.059, 0.035, 0.037 Preview of result: [{"A": "Ky'c^g#~)0", "a": 1850111954, "b": 9318359041, "B": "Oyi:/ xxe2", "C": "sKCSa_^7Gg", "c": 7974777124, "d": 2670309238, "D": "0d_K)HmX!.", "E": ".uM*Z{0EJ_", "e": 6958410336, "f": 8050244728, "F": "1%SG_A!xB\t", "g": 3799657125, "G": "il1^k\\\nat*", "H": {"a": 6079042826, "b": 7292804611, "c" ... ========================= Performance tests for RemesPath (string operations) ========================= -Compiling query "@[@[:].z =~ `(?i)[a-z]{5}`]" took 0.072 ms the first time, including approximately 0.155 ms to tokenize the query. Subsequent executions are effectively free due to caching. -To run pre-compiled query "@[@[:].z =~ `(?i)[a-z]{5}`]" on JNode from JSON of size 89556 into took 0.055 +/- 0.008 ms over 40 trials -Query times (ms): 0.098, 0.057, 0.054, 0.053, 0.055, 0.052, 0.053, 0.053, 0.052, 0.052, 0.054, 0.053, 0.053, 0.054, 0.053, 0.053, 0.053, 0.053, 0.053, 0.053, 0.053, 0.054, 0.053, 0.053, 0.078, 0.056, 0.053, 0.053, 0.052, 0.052, 0.053, 0.052, 0.053, 0.053, 0.052, 0.053, 0.052, 0.052, 0.052, 0.053 +Compiling query "@[@[:].z =~ `(?i)[a-z]{5}`]" took 0.053 ms the first time, including approximately 0.057 ms to tokenize the query. Subsequent executions are effectively free due to caching. +To run pre-compiled query "@[@[:].z =~ `(?i)[a-z]{5}`]" on JNode from JSON of size 89556 into took 0.087 +/- 0.028 ms over 40 trials +Query times (ms): 0.179, 0.149, 0.101, 0.1, 0.121, 0.105, 0.102, 0.107, 0.102, 0.1, 0.099, 0.1, 0.106, 0.109, 0.102, 0.104, 0.108, 0.105, 0.103, 0.108, 0.086, 0.058, 0.073, 0.057, 0.056, 0.059, 0.07, 0.066, 0.06, 0.072, 0.058, 0.059, 0.059, 0.057, 0.071, 0.059, 0.064, 0.073, 0.057, 0.055 Preview of result: [{"A": "\n]o1VQ5t6g", "a": 4710024278, "b": 3268860721, "B": "g4Y7+ew^.v", "C": "NK nmax_notq, `when q=true, nmax = ` + str(nmax_q), `when q=false, nmax= ` + str(nmax_notq))" took 0.139 ms the first time, including approximately 0.177 ms to tokenize the query. Subsequent executions are effectively free due to caching. +ifelse(nmax_q > nmax_notq, `when q=true, nmax = ` + str(nmax_q), `when q=false, nmax= ` + str(nmax_notq))" took 0.254 ms the first time, including approximately 0.226 ms to tokenize the query. Subsequent executions are effectively free due to caching. To run pre-compiled query "var qmask = @[:].q; var nmax_q = max(@[qmask].n); var nmax_notq = max(@[not qmask].n); -ifelse(nmax_q > nmax_notq, `when q=true, nmax = ` + str(nmax_q), `when q=false, nmax= ` + str(nmax_notq))" on JNode from JSON of size 89556 into took 0.036 +/- 0.057 ms over 40 trials -Query times (ms): 0.101, 0.031, 0.017, 0.016, 0.028, 0.015, 0.016, 0.019, 0.233, 0.016, 0.023, 0.015, 0.082, 0.015, 0.017, 0.016, 0.015, 0.015, 0.015, 0.024, 0.014, 0.027, 0.015, 0.015, 0.015, 0.015, 0.015, 0.016, 0.016, 0.119, 0.048, 0.016, 0.015, 0.015, 0.015, 0.015, 0.015, 0.015, 0.293, 0.017 +ifelse(nmax_q > nmax_notq, `when q=true, nmax = ` + str(nmax_q), `when q=false, nmax= ` + str(nmax_notq))" on JNode from JSON of size 89556 into took 0.028 +/- 0.02 ms over 40 trials +Query times (ms): 0.139, 0.034, 0.029, 0.028, 0.028, 0.043, 0.058, 0.034, 0.028, 0.028, 0.031, 0.022, 0.019, 0.017, 0.017, 0.017, 0.017, 0.017, 0.016, 0.017, 0.017, 0.016, 0.017, 0.022, 0.022, 0.019, 0.018, 0.02, 0.017, 0.018, 0.027, 0.024, 0.024, 0.03, 0.027, 0.031, 0.039, 0.03, 0.028, 0.028 Preview of result: "when q=false, nmax= 9830935647.0" ... ========================= @@ -271,11 +271,11 @@ Performance tests for RemesPath (references to compile-time constant variables) Compiling query "var X = X; var onetwo = j`[1, 2]`; -@[:]->at(@, X)->at(@, onetwo)" took 0.074 ms the first time, including approximately 0.078 ms to tokenize the query. Subsequent executions are effectively free due to caching. +@[:]->at(@, X)->at(@, onetwo)" took 0.076 ms the first time, including approximately 0.101 ms to tokenize the query. Subsequent executions are effectively free due to caching. To run pre-compiled query "var X = X; var onetwo = j`[1, 2]`; -@[:]->at(@, X)->at(@, onetwo)" on JNode from JSON of size 89556 into took 0.013 +/- 0.006 ms over 40 trials -Query times (ms): 0.05, 0.013, 0.012, 0.012, 0.017, 0.012, 0.012, 0.012, 0.012, 0.011, 0.012, 0.012, 0.012, 0.012, 0.012, 0.012, 0.012, 0.012, 0.012, 0.012, 0.012, 0.012, 0.012, 0.012, 0.013, 0.012, 0.012, 0.012, 0.012, 0.013, 0.012, 0.012, 0.012, 0.012, 0.012, 0.012, 0.012, 0.012, 0.012, 0.012 +@[:]->at(@, X)->at(@, onetwo)" on JNode from JSON of size 89556 into took 0.032 +/- 0.065 ms over 40 trials +Query times (ms): 0.077, 0.046, 0.024, 0.02, 0.023, 0.025, 0.433, 0.029, 0.022, 0.022, 0.012, 0.011, 0.012, 0.012, 0.012, 0.012, 0.013, 0.012, 0.012, 0.021, 0.022, 0.019, 0.011, 0.012, 0.012, 0.02, 0.021, 0.022, 0.024, 0.023, 0.021, 0.03, 0.024, 0.024, 0.023, 0.024, 0.024, 0.022, 0.022, 0.023 Preview of result: [[1695727848, 0.28756263873668497], [2126430375, 0.0076779412970817704], [5310550656, 0.38076977264568701], [2519183283, 0.15317622093055799], [6610062385, 0.66299622587066598], [987168256, 0.92441018999992797], [6615003609, 0.91711269122594696], [4465232046, 0.68431193185153605], [8654414565, 0.631 ... ========================= @@ -284,29 +284,29 @@ Performance tests for RemesPath (references to variables that are not compile-ti Compiling query "var X = @->`X`; var onetwo = @{1, 2}; -@[:]->at(@, X)->at(@, onetwo)" took 0.104 ms the first time, including approximately 0.168 ms to tokenize the query. Subsequent executions are effectively free due to caching. +@[:]->at(@, X)->at(@, onetwo)" took 0.109 ms the first time, including approximately 0.113 ms to tokenize the query. Subsequent executions are effectively free due to caching. To run pre-compiled query "var X = @->`X`; var onetwo = @{1, 2}; -@[:]->at(@, X)->at(@, onetwo)" on JNode from JSON of size 89556 into took 0.021 +/- 0.017 ms over 40 trials -Query times (ms): 0.114, 0.016, 0.023, 0.016, 0.015, 0.016, 0.027, 0.015, 0.016, 0.016, 0.015, 0.016, 0.017, 0.053, 0.016, 0.016, 0.022, 0.024, 0.05, 0.015, 0.015, 0.025, 0.016, 0.016, 0.016, 0.015, 0.016, 0.016, 0.016, 0.015, 0.016, 0.015, 0.016, 0.016, 0.015, 0.015, 0.016, 0.015, 0.016, 0.016 +@[:]->at(@, X)->at(@, onetwo)" on JNode from JSON of size 89556 into took 0.03 +/- 0.019 ms over 40 trials +Query times (ms): 0.094, 0.033, 0.034, 0.115, 0.058, 0.029, 0.03, 0.028, 0.028, 0.032, 0.029, 0.031, 0.032, 0.027, 0.017, 0.017, 0.015, 0.016, 0.017, 0.016, 0.016, 0.016, 0.016, 0.017, 0.017, 0.018, 0.018, 0.017, 0.028, 0.03, 0.032, 0.032, 0.029, 0.032, 0.029, 0.031, 0.03, 0.033, 0.031, 0.031 Preview of result: [[1695727848, 0.28756263873668497], [2126430375, 0.0076779412970817704], [5310550656, 0.38076977264568701], [2519183283, 0.15317622093055799], [6610062385, 0.66299622587066598], [987168256, 0.92441018999992797], [6615003609, 0.91711269122594696], [4465232046, 0.68431193185153605], [8654414565, 0.631 ... ========================= Performance tests for RemesPath (simple string mutations) ========================= -Compiling query "@[:].z = s_sub(@, g, B)" took 0.048 ms the first time, including approximately 0.049 ms to tokenize the query. Subsequent executions are effectively free due to caching. -To run pre-compiled query "@[:].z = s_sub(@, g, B)" on JNode from JSON of size 89556 into took 0.013 +/- 0.004 ms over 40 trials -Query times (ms): 0.027, 0.015, 0.012, 0.009, 0.009, 0.011, 0.015, 0.015, 0.02, 0.017, 0.015, 0.015, 0.014, 0.013, 0.025, 0.013, 0.01, 0.01, 0.01, 0.011, 0.01, 0.011, 0.01, 0.011, 0.009, 0.012, 0.013, 0.015, 0.016, 0.013, 0.01, 0.01, 0.012, 0.012, 0.011, 0.012, 0.011, 0.011, 0.011, 0.009 +Compiling query "@[:].z = s_sub(@, g, B)" took 0.082 ms the first time, including approximately 0.116 ms to tokenize the query. Subsequent executions are effectively free due to caching. +To run pre-compiled query "@[:].z = s_sub(@, g, B)" on JNode from JSON of size 89556 into took 0.04 +/- 0.021 ms over 40 trials +Query times (ms): 0.05, 0.044, 0.034, 0.034, 0.045, 0.044, 0.047, 0.033, 0.042, 0.036, 0.04, 0.044, 0.039, 0.047, 0.046, 0.038, 0.026, 0.155, 0.035, 0.029, 0.025, 0.017, 0.026, 0.025, 0.023, 0.036, 0.027, 0.042, 0.047, 0.042, 0.027, 0.028, 0.025, 0.038, 0.036, 0.045, 0.039, 0.078, 0.035, 0.029 Preview of result: [{"A": "Ky'c^g#~)0", "a": 1850111954, "b": 9318359041, "B": "Oyi:/ xxe2", "C": "sKCSa_^7Gg", "c": 7974777124, "d": 2670309238, "D": "0d_K)HmX!.", "E": ".uM*Z{0EJ_", "e": 6958410336, "f": 8050244728, "F": "1%SG_A!xB\t", "g": 3799657125, "G": "il1^k\\\nat*", "H": {"a": 6079042826, "b": 7292804611, "c" ... ========================= Performance tests for RemesPath (simple number mutations) ========================= -Compiling query "@[:].x = ifelse(@ < 0.5, @ + 3, @ - 3)" took 0.084 ms the first time, including approximately 0.114 ms to tokenize the query. Subsequent executions are effectively free due to caching. -To run pre-compiled query "@[:].x = ifelse(@ < 0.5, @ + 3, @ - 3)" on JNode from JSON of size 89556 into took 0.023 +/- 0.01 ms over 40 trials -Query times (ms): 0.035, 0.019, 0.018, 0.018, 0.019, 0.018, 0.018, 0.019, 0.018, 0.018, 0.019, 0.019, 0.075, 0.033, 0.02, 0.019, 0.019, 0.02, 0.021, 0.022, 0.038, 0.022, 0.019, 0.022, 0.023, 0.025, 0.024, 0.025, 0.019, 0.018, 0.024, 0.026, 0.019, 0.023, 0.02, 0.019, 0.018, 0.019, 0.019, 0.025 +Compiling query "@[:].x = ifelse(@ < 0.5, @ + 3, @ - 3)" took 0.104 ms the first time, including approximately 0.122 ms to tokenize the query. Subsequent executions are effectively free due to caching. +To run pre-compiled query "@[:].x = ifelse(@ < 0.5, @ + 3, @ - 3)" on JNode from JSON of size 89556 into took 0.035 +/- 0.011 ms over 40 trials +Query times (ms): 0.068, 0.054, 0.041, 0.044, 0.021, 0.036, 0.037, 0.029, 0.02, 0.025, 0.04, 0.022, 0.042, 0.039, 0.037, 0.031, 0.02, 0.021, 0.023, 0.03, 0.037, 0.045, 0.038, 0.036, 0.038, 0.036, 0.05, 0.048, 0.035, 0.026, 0.023, 0.026, 0.026, 0.023, 0.021, 0.034, 0.04, 0.043, 0.046, 0.047 Preview of result: [{"A": "Ky'c^g#~)0", "a": 1850111954, "b": 9318359041, "B": "Oyi:/ xxe2", "C": "sKCSa_^7Gg", "c": 7974777124, "d": 2670309238, "D": "0d_K)HmX!.", "E": ".uM*Z{0EJ_", "e": 6958410336, "f": 8050244728, "F": "1%SG_A!xB\t", "g": 3799657125, "G": "il1^k\\\nat*", "H": {"a": 6079042826, "b": 7292804611, "c" ... ========================= @@ -316,12 +316,12 @@ Performance tests for RemesPath (mutations with a for loop) Compiling query "var xhalf = @[:].x < 0.5; for lx = zip(@[:].l, xhalf); lx[0] = ifelse(lx[1], foo, bar); -end for;" took 0.164 ms the first time, including approximately 0.109 ms to tokenize the query. Subsequent executions are effectively free due to caching. +end for;" took 0.211 ms the first time, including approximately 0.162 ms to tokenize the query. Subsequent executions are effectively free due to caching. To run pre-compiled query "var xhalf = @[:].x < 0.5; for lx = zip(@[:].l, xhalf); lx[0] = ifelse(lx[1], foo, bar); -end for;" on JNode from JSON of size 89556 into took 0.044 +/- 0.019 ms over 40 trials -Query times (ms): 0.097, 0.04, 0.04, 0.038, 0.038, 0.038, 0.037, 0.039, 0.037, 0.058, 0.133, 0.079, 0.039, 0.036, 0.036, 0.036, 0.036, 0.035, 0.035, 0.036, 0.05, 0.036, 0.034, 0.049, 0.042, 0.036, 0.035, 0.035, 0.036, 0.035, 0.035, 0.037, 0.038, 0.038, 0.036, 0.057, 0.041, 0.037, 0.039, 0.037 +end for;" on JNode from JSON of size 89556 into took 0.074 +/- 0.024 ms over 40 trials +Query times (ms): 0.083, 0.05, 0.083, 0.085, 0.044, 0.062, 0.061, 0.074, 0.046, 0.058, 0.067, 0.106, 0.096, 0.084, 0.071, 0.079, 0.115, 0.092, 0.113, 0.105, 0.081, 0.05, 0.064, 0.08, 0.057, 0.071, 0.072, 0.048, 0.042, 0.076, 0.057, 0.043, 0.071, 0.066, 0.094, 0.072, 0.07, 0.06, 0.16, 0.05 Preview of result: [["bar", false], ["bar", false], ["foo", true], ["foo", true], ["foo", true], ["foo", true], ["foo", true], ["bar", false], ["bar", false], ["bar", false], ["foo", true], ["foo", true], ["bar", false], ["bar", false], ["foo", true], ["bar", false], ["bar", false], ["bar", false], ["foo", true], ["ba ... ========================= @@ -330,32 +330,32 @@ Testing performance of JSON compression and pretty-printing Preview of json: [{"A": "Ky'c^g#~)0", "a": 1850111954, "b": 9318359041, "B": "Oyi:/ xxe2", "C": "sKCSa_^7Gg", "c": 7974777124, "d": 2670309238, "D": "0d_K)HmX!.", "E": ".uM*Z{0EJ_", "e": 6958410336, "f": 8050244728, "F": "1%SG_A!xB\t", "g": 3799657125, "G": "il1^k\\\nat*", "H": {"a": 6079042826, "b": 7292804611, "c" ... -To compress JNode from JSON string of 89556 took 4.404 +/- 1.087 ms over 64 trials (minimal whitespace, sortKeys=TRUE) -To compress JNode from JSON string of 89556 took 2.481 +/- 0.335 ms over 64 trials (minimal whitespace, sortKeys=FALSE) -To Google-style pretty-print JNode from JSON string of 89556 took 4.836 +/- 0.491 ms over 64 trials (sortKeys=true, indent=4) -To Whitesmith-style pretty-print JNode from JSON string of 89556 took 4.591 +/- 0.862 ms over 64 trials (sortKeys=true, indent=4) -To PPrint-style pretty-print JNode from JSON string of 89556 took 6.066 +/- 0.637 ms over 64 trials (sortKeys=true, indent=4) +To compress JNode from JSON string of 89556 took 4.684 +/- 1.108 ms over 64 trials (minimal whitespace, sortKeys=TRUE) +To compress JNode from JSON string of 89556 took 2.109 +/- 0.329 ms over 64 trials (minimal whitespace, sortKeys=FALSE) +To Google-style pretty-print JNode from JSON string of 89556 took 4.286 +/- 0.266 ms over 64 trials (sortKeys=true, indent=4) +To Whitesmith-style pretty-print JNode from JSON string of 89556 took 4.44 +/- 0.558 ms over 64 trials (sortKeys=true, indent=4) +To PPrint-style pretty-print JNode from JSON string of 89556 took 6.317 +/- 0.445 ms over 64 trials (sortKeys=true, indent=4) ========================= Testing performance of JsonSchemaValidator and random JSON creation ========================= -To create a random set of JSON from file at path C:\Program Files\Notepad++\plugins\JsonTools\testfiles\tweet_schema.json of size 184326 (array of 15 items) based on the matching schema took 6.595 +/- 2.757 ms over 25 trials -To compile the schema to a validation function took 0.33 +/- 0.544 ms over 25 trials -To validate JSON of size 184326 (array of 15 items) based on the compiled schema took 1.051 +/- 0.2 ms over 25 trials +To create a random set of JSON from file at path C:\Program Files\Notepad++\plugins\JsonTools\testfiles\tweet_schema.json of size 159902 (array of 15 items) based on the matching schema took 7.084 +/- 3.314 ms over 25 trials +To compile the schema to a validation function took 0.219 +/- 0.028 ms over 25 trials +To validate JSON of size 159902 (array of 15 items) based on the compiled schema took 1.062 +/- 0.161 ms over 25 trials ========================= Testing performance of random JSON from schema with patterns and patternProperties ========================= -To create a random set of JSON from string (see TestRunner.cs) of size 29841 (array of 120 items) based on the matching schema took 1.438 +/- 0.669 ms over 25 trials -To compile the schema to a validation function took 0.348 +/- 0.508 ms over 25 trials -To validate JSON of size 29841 (array of 120 items) based on the compiled schema took 8.877 +/- 0.255 ms over 25 trials +To create a random set of JSON from string (see TestRunner.cs) of size 29973 (array of 120 items) based on the matching schema took 1.52 +/- 0.7 ms over 25 trials +To compile the schema to a validation function took 0.267 +/- 0.024 ms over 25 trials +To validate JSON of size 29973 (array of 120 items) based on the compiled schema took 9.55 +/- 0.397 ms over 25 trials ========================= Testing performance of random JSON from schema *ignoring* patterns and patternProperties ========================= -To create a random set of JSON from string (see TestRunner.cs) of size 10853 (array of 120 items) based on the matching schema took 0.831 +/- 0.516 ms over 25 trials -To compile the schema to a validation function took 0.21 +/- 0.007 ms over 25 trials -To validate JSON of size 10853 (array of 120 items) based on the compiled schema took 5.334 +/- 0.223 ms over 25 trials +To create a random set of JSON from string (see TestRunner.cs) of size 10800 (array of 120 items) based on the matching schema took 0.866 +/- 0.468 ms over 25 trials +To compile the schema to a validation function took 0.242 +/- 0.036 ms over 25 trials +To validate JSON of size 10800 (array of 120 items) based on the compiled schema took 6.161 +/- 0.989 ms over 25 trials ========================= Testing JSON grepper's API request tool =========================