Skip to content

Commit

Permalink
recurse_until rpath func; fix GetOpenFileNames
Browse files Browse the repository at this point in the history
ADD: recurse_until RemesPath function
    for recursively searching for children that satisfy a condition.
FIX: remove reference to badly implemented NPPM_GETOPENFILENAMES
    to fix a crash that suddenly started happening whenever I ran UI tests
  • Loading branch information
molsonkiko committed Dec 29, 2024
1 parent 0cb8dc8 commit f2d8ad2
Show file tree
Hide file tree
Showing 10 changed files with 144 additions and 142 deletions.
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,9 +50,14 @@ and this project adheres to [Semantic Versioning](http://semver.org/).

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

### Added

1. Add [`recurse_until` RemesPath function](/docs/RemesPath.md#non-vectorized-functions) to recursively search arrays and objects for anything inside that satisfies a condition.

### Fixed

1. Mostly fix bug where icons would disappear when going from light to dark mode. As a side effect, the icons when using "standard icons: small" are smaller, but the icons under other conditions are larger.
2. Fix [intermittent crash during UI tests in 64-bit Notepad++](https://community.notepad-plus-plus.org/topic/26509/intermittent-notepad-crash-related-to-nppm_getopenfilenames-only-on-64-bit-npp) by eliminating reference to the dangerous `NPPM_GETOPENFILENAMES` message.

## [8.3.1] - 2024-12-22

Expand Down
35 changes: 35 additions & 0 deletions JsonToolsNppPlugin/JSONTools/RemesPathFunctions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1746,6 +1746,40 @@ public static JNode Range(List<JNode> args)
return nums;
}

public static JNode RecurseUntil(List<JNode> args)
{
JNode node = args[0];
if (!(args[1] is CurJson cj))
throw new RemesPathArgumentException("second argument must be a functiont that returns a boolean", 1, FUNCTIONS["recurse_until"], args[1].type);
Func<JNode, JNode> func = cj.function;
var results = new List<JNode>();
RecurseUntilHelper(node, func, results);
return new JArray(0, results);
}

private static void RecurseUntilHelper(JNode node, Func<JNode, JNode> func, List<JNode> results)
{
try
{
if (func(node).value is bool b && b)
{
results.Add(node);
return;
}
}
catch { }
if (node is JArray jar && jar.children is List<JNode> car)
{
for (int ii = 0; ii < car.Count; ii++)
RecurseUntilHelper(car[ii], func, results);
}
else if (node is JObject obj && obj.children is Dictionary<string, JNode> cobj)
{
foreach (JNode child in cobj.Values)
RecurseUntilHelper(child, func, results);
}
}

public static JNode Values(List<JNode> args)
{
var vals = new JArray(0, ((JObject)args[0]).children.Values.ToList());
Expand Down Expand Up @@ -3688,6 +3722,7 @@ public static JNode ObjectsToJNode(object obj)
["rand_schema"] = new ArgFunction(RandomFromSchema, "rand_schema", Dtype.ANYTHING, 1, 5, false, new Dtype[] { Dtype.OBJ, Dtype.INT, Dtype.INT, Dtype.BOOL, Dtype.BOOL }, isDeterministic: false),
["randint"] = new ArgFunction(RandomInteger, "randint", Dtype.INT, 1, 2, false, new Dtype[] {Dtype.INT, Dtype.INT}, false),
["range"] = new ArgFunction(Range, "range", Dtype.ARR, 1, 3, false, new Dtype[] {Dtype.INT, Dtype.INT, Dtype.INT}),
["recurse_until"] = new ArgFunction(RecurseUntil, "recurse_until", Dtype.ARR, 2, 2, false, new Dtype[] { Dtype.ARR_OR_OBJ, Dtype.FUNCTION | Dtype.ANYTHING }, conditionalExecution: true),
["s_cat"] = new ArgFunction(StrCat, "s_cat", Dtype.STR, 1, int.MaxValue, false, new Dtype[] {Dtype.ANYTHING, /* any # of args */ Dtype.ANYTHING}),
["s_join"] = new ArgFunction(StringJoin, "s_join", Dtype.STR, 2, 2, false, new Dtype[] {Dtype.STR, Dtype.ARR}),
["set"] = new ArgFunction(Set, "set", Dtype.OBJ, 1, 1, false, new Dtype[] {Dtype.ARR}),
Expand Down
1 change: 0 additions & 1 deletion JsonToolsNppPlugin/JsonToolsNppPlugin.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,6 @@
</Compile>
<Compile Include="JSONTools\CsvSniffer.cs" />
<Compile Include="JSONTools\Dson.cs" />
<Compile Include="PluginInfrastructure\ClikeStringArray.cs" />
<Compile Include="PluginInfrastructure\DllExport\DllExportAttribute.cs" />
<Compile Include="PluginInfrastructure\Docking_h.cs" />
<Compile Include="PluginInfrastructure\GatewayDomain.cs" />
Expand Down
77 changes: 0 additions & 77 deletions JsonToolsNppPlugin/PluginInfrastructure/ClikeStringArray.cs

This file was deleted.

4 changes: 3 additions & 1 deletion JsonToolsNppPlugin/PluginInfrastructure/Msgs_h.cs
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,9 @@ public enum NppMsg : uint
ALL_OPEN_FILES = 0,
PRIMARY_VIEW = 1,
SECOND_VIEW = 2,

/// <summary>
/// DO NOT USE! The internal Notepad++ code for this message uses lstrcpy, which is explicitly deprecated by Microsoft.
/// </summary>
NPPM_GETOPENFILENAMES = Constants.NPPMSG + 8,

NPPM_MODELESSDIALOG = Constants.NPPMSG + 12,
Expand Down
25 changes: 17 additions & 8 deletions JsonToolsNppPlugin/PluginInfrastructure/NotepadPPGateway.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
// NPP plugin platform for .Net v0.94.00 by Kasper B. Graversen etc.
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Runtime.InteropServices;
using System.Text;
Expand Down Expand Up @@ -222,16 +223,24 @@ public int[] GetNppVersion()
return new int[] { major, minor, bugfix };
}

/// <summary>
/// Get all open filenames in both views (all in first view, then all in second view).<br></br>
/// Note that if the second view is not open, the last name in the array will be "new 1" because that is the name of the placeholder buffer that is always in an empty view.
/// </summary>
/// <returns></returns>
public string[] GetOpenFileNames()
{
int nbFile = (int)Win32.SendMessage(PluginBase.nppData._nppHandle, (uint)NppMsg.NPPM_GETNBOPENFILES, 0, 0);

using (ClikeStringArray cStrArray = new ClikeStringArray(nbFile, Win32.MAX_PATH))
{
if (Win32.SendMessage(PluginBase.nppData._nppHandle, (uint)NppMsg.NPPM_GETOPENFILENAMES, cStrArray.NativePointer, nbFile) != IntPtr.Zero)
return cStrArray.ManagedStringsUnicode.ToArray();
}
return null;
var bufs = new List<string>();
for (int view = 0; view < 2; view++)
{
int nbOpenFiles = (int)Win32.SendMessage(PluginBase.nppData._nppHandle, (uint)NppMsg.NPPM_GETNBOPENFILES, 0, view + 1);
for (int ii = 0; ii < nbOpenFiles; ii++)
{
IntPtr bufId = Win32.SendMessage(PluginBase.nppData._nppHandle, (uint)NppMsg.NPPM_GETBUFFERIDFROMPOS, ii, view);
bufs.Add(Npp.notepad.GetFilePath(bufId));
}
}
return bufs.ToArray();
}

public void AddModelessDialog(IntPtr formHandle)
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("8.3.1.1")]
[assembly: AssemblyFileVersion("8.3.1.1")]
[assembly: AssemblyVersion("8.3.1.2")]
[assembly: AssemblyFileVersion("8.3.1.2")]
6 changes: 6 additions & 0 deletions JsonToolsNppPlugin/Tests/RemesPathTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -742,6 +742,12 @@ public static bool Test()
new Query_DesiredResult("items(items(@)[1][1])", "[[\"a\",false],[\"b\",[\"a`g\",\"bah\"]]]"),
new Query_DesiredResult("items(items(@)[1][1][a,b])", "[[\"a\",false],[\"b\",[\"a`g\",\"bah\"]]]"),
new Query_DesiredResult("items(items(@)[1][1].g`[ab]`)", "[[\"a\",false],[\"b\",[\"a`g\",\"bah\"]]]"),
// ====================== recurse_until function for recursively searching for JNodes that satisfy a condition ======================
new Query_DesiredResult("recurse_until(@, sum(@) <= 3)", "[[0, 1, 2], [], [1], [2], [3]]"),
new Query_DesiredResult("recurse_until(@.`7`, in(foo, @))", "[{\"foo\": 2}]"),
new Query_DesiredResult("recurse_until(@, in(foo, @))", $"[{fooStr}]"),
new Query_DesiredResult("recurse_until(j`[{\"Value\":100,\"Name\":\"GOOD\"},{\"Value\":200,\"Name\":\"GOOD\"}, {\"Value\":50,\"Name\":\"BAD\"},{\"Value\":150,\"Name\":\"BAD\"}]`, and(@.Name == GOOD, @.Value > 100))", "[{\"Value\": 200, \"Name\": \"GOOD\"}]"),
new Query_DesiredResult("recurse_until(@.bar, is_str(@))", "[\"a`g\", \"bah\"]"),
};
int ii = 0;
int testsFailed = 0;
Expand Down
23 changes: 23 additions & 0 deletions docs/RemesPath.md
Original file line number Diff line number Diff line change
Expand Up @@ -565,6 +565,29 @@ Returns an array of integers.
* `range(3, 1, -1)` returns `[3, 2]`.
* `range(0, 6, 3)` returns `[0, 3]`.

---
`recurse_until(node: anything, condition: function) -> array`

Recursively searches `node` for any children `child` such that `condition(child)` returns `true`.

Consider the input
```json
{
"foo": {"value": 1, "name": "GOOD"},
"bar_array": [
{"name": "BAD", "value": 2},
{"name": "GOOD", "value": -1},
{"name": "GOOD", "value": 3}
],
"name": "blah"
}
```

__Examples:__
1. `recurse_until(@, in(name, @))` returns an array containing the input as its first and only element, because the recursive search halts as soon as `in(name, @)` is satisfied by the input.
2. `recurse_until(@, and(@.name == GOOD, @.value >= 0))` will return `[{"value":1,"name":"GOOD"},{"name":"GOOD","value":3}]`, because those are the two `name,value` arrays such that `name=GOOD` and `value>=0`
3. ``recurse_until(@, @ =~ `^[bB]`)`` will return `["BAD","blah"]`, because those are the two string values that start with `b` or `B`.

---
`s_cat(x: anything, ...: anything) -> string`

Expand Down
Loading

0 comments on commit f2d8ad2

Please sign in to comment.