How dynamic-ID menu commands are executed and relation to macros
-
The internal ID for a menu command in the “Run”, “Plugin” or “Macro” menus changes when another preceding subentry in that menu is removed by the user, because N++ re-assigns IDs by renumbering in ascending order starting from a particular number.
This is can be discovered using the NppUISpy plugin (thanks to @dinkumoil ) and explained in https://community.notepad-plus-plus.org/topic/27104/macro-does-not-record-my-run-action/7?_=1756653972969When that ID changes, how N++ knows what command to execute when you click on that menu subentry?
What does it get translated to? Is there a deeper-level ID which does not change?Whatever it gets translated to, must be something constant. Perhaps that could be an alternative “newID” to be stored in “shortcuts.xml” when saving a macro involving those dynamic ID commands.
This, if true, may open 2 possibilities:- a way to remove the limitation that the Run/Plugin/Macro - menu commands are not currently recordable
- develop an alternative macro system as a plugin, which would be more free to evolve
EDIT:
Perhaps that could be an alternative “newID” to be stored in “shortcuts.xml” when saving a macro involving those dynamic ID commands.
For example, a saved macro including a such a dynamic-ID command can have an action like:
<Action type="2" message="0" wParam="21004" lParam="0" sParam="" />
where 21004 is that ID in question, that when it changes, it invalidates the saved macro.
-
@Victorel-Petrovich said in How dynamic-ID menu commands are executed and relation to macros:
When that ID changes, how N++ knows what command to execute when you click on that menu subentry?
What does it get translated to? Is there a deeper-level ID which does not change?It knows because it builds the menus and assigns the identifiers at the same time, during startup. They can’t change without closing and restarting Notepad++. Each identifier is linked for that session of Notepad++ to internal descriptors that tell it what to do. There is no persistent ID that lasts from session to session.
-
@Coises Actually, those IDs do change during the same session. You can test it by:
- creating & saving either 2 macro commands, or 2 run-menu commands,
- check the IDs with NppUISpy,
- then remove the first command , using “Modify Shortcut/Delete macro (command)”
- check again the ID.
All the while the 2nd command left there runs correctly.
Each identifier is linked […] to internal descriptors that tell it what to do.
That’s what I imagine too; those “internal descriptors” must be constant.
-
@Victorel-Petrovich said in How dynamic-ID menu commands are executed and relation to macros:
@Coises Actually, those IDs do change during the same session. You can test it by:
- creating & saving either 2 macro commands, or 2 run-menu commands,
- check the IDs with NppUISpy,
- then remove the first command , using “Modify Shortcut/Delete macro (command)”
- check again the ID.
All the while the 2nd command left there runs correctly.
Good catch. That behavior is really easy to implement by putting the elements in a linked list: here’s an example, to make it easy to understand:
- XML has three macros:
one
,two
,three
- Notepad++ parses them into linked list:
one -> two -> three
(presumably, each element of the list is really more complicated, with the actual command sequence, rather than just storing its name; I am using the name as the standin for the complicated structure) - Notepad++ receives menuCmdID=20001
- it subtracts 20000 from that, to get the index=1.
- Looking at the linked list, index=1 is
two
- Run the sequence of codes stored for that macro
- Delete macro
one
, meaning the linked list is nowtwo -> three
- Notepad++ receives the menuCmdID=20001
- it subtracts 20000 from that, to get the index=1
- Looking at the updated linked list, index=1 is
three
- Run the sequence of codes stored for that macro
Each identifier is linked […] to internal descriptors that tell it what to do.
That’s what I imagine too; those “internal descriptors” must be constant.
There doesn’t even have to be an identifier. The linked list that I described, instead of storing the name and commands together, could really just store the commands.
So, in my example:
- assume notepad++ stores each action as a structure like
action[0] = {type=2, message=0, wParam=42024, lParam=0, sParam=""}
- assume notepad++ stores each list of actions (ie, macro), like
[ action[0], action[1], ..., action[n] ]
- then the linked list could be something like:
[ {type=2, message=0, wParam=42024, lParam=0, sParam-""}, {...}, ..., {...} ] -> [ {...}, {...}, ..., {...} ] -> [ {...}, {...}, ..., {...} ]
- then that first
[...]
would be assigned 20000, the second 20001, and so on. And if the first is deleted, then 20000 would be the new first, etc.
There is no “universally constant value” necessary for Notepad++ to store for it to be working the way it is.
And creating a new identifier rather than using menuCmdID is not likely to happen (nor should it, IMO)
develop an alternative macro system as a plugin, which would be more free to evolve
That’s literally the niche that NppExec plugin, PythonScript plugin, LuaScript plugin, jN Notepad++ Plugin, and maybe others all fill. If you want something more advanced than macros, you pick a programming language to implement the same sequence in. If you want to develop a plugin that is simpler than a scripting plugin but more more feature-rich than Notepad++'s built-in macros, go right ahead; someone might actually use it, if you do. But don’t pretend the niche doesn’t already have a plethora of entries.
-
@PeterJones said in How dynamic-ID menu commands are executed and relation to macros:
That behavior is really easy to implement by putting the elements in a linked list
Interesting, and it makes sense.
Is that how N++ does it? If you know.
-
@Victorel-Petrovich said in How dynamic-ID menu commands are executed and relation to macros:
Is that how N++ does it? If you know.
I’ve never read that section of the codebase, sorry.
-
@PeterJones said in How dynamic-ID menu commands are executed and relation to macros:
There is no “universally constant value” necessary for Notepad++ to store for it to be working the way it is.
And creating a new identifier rather than using menuCmdID is not likely to happen (nor should it, IMO)
That scheme (even if hypothetical) seems to work very well as long as it’s not a problem that the IDs change when you remove menu entries.
But we do know that it is in fact a problem for recording macros.
Here are 2 approaches I can think of (besides the “shut up and hack the XML files” one :) ) :A. Constant IDs version
Still have a linked list to represent the structure of the (dynamic) menus, but whose contents are just the IDs of those commands. Can still be in same range as current IDs, if needed.
Add a dictionary (hashmap) to map from those IDs to the respective actual commands, or in case of macros - list of actions , as you describe in your post above.
Npp getting command 20001 for ex, involves looking up in the dictionary - most often a constant time operation.Deleting an menu entry involves deleting the respective entry in the linked list and also the ID-value pair in the dictionary.
In addition to that, to ensure that generated IDs stay within a given range: Several schemes are possible, one of the simplest can be just generate a key in range and test that it is not present in the dictionary.
B. Dynamic IDs, but synchronized in every saved macro
Basically, keep the scheme as it is at present, but add a way to update, for each macro, every stored referenced menu command ID in an Action record:
at every removal of a menu entry, compute how the rest of entries’ IDs would change (if located After those removed, the ID would decrease by one) and traverse the linked list of all macros and update that ID in every action that refers to a menu-command in the range of the dynamic-IDs types (Run, Plugin and Macro commands).
Thus on Npp closing, the macros written in XML will also contain updated IDs.
But don’t pretend the niche [for a more powerful macro system ] doesn’t already have a plethora of entries.
I think there is plenty of room for a macro system to grow in power and STILL be much easier to do a lot of common tasks, than the same tasks in any scripting language.
Because with macros you don’t have to learn and remember syntax.