Community
    • Login

    MacroInspect which comments macros

    Scheduled Pinned Locked Moved General Discussion
    36 Posts 4 Posters 1.3k Views
    Loading More Posts
    • Oldest to Newest
    • Newest to Oldest
    • Most Votes
    Reply
    • Reply as topic
    Log in to reply
    This topic has been deleted. Only users with topic management privileges can see it.
    • mpheathM
      mpheath
      last edited by mpheath

      Created a gist of a recent created script that can read shortcut.xml read from a tab or a file. It requires PythonScript 3. Add it to the scripts directory and run it from the Plugins menu.

      View help with PythonScript if needed in the FAQ.

      The script can download from the Notepad++ repository files named FindReplaceDlg_rc.h and Scintilla.h which are read to create json files. The json files will contain the ID numbers and constant names to be used in the comments of the new opened tab with the shortcut.xml content.

      The local nativeLang.xml file will be read to create a dictionary with ID numbers and names. So hopefully non-english users may see these comments in their native language.

      With these dictionaries, an example of just macro’s:

              <Macro name="Trim Trailing Space and Save" Ctrl="no" Alt="yes" Shift="yes" Key="83">
                  <Action type="2" message="0" wParam="42024" lParam="0" sParam="" />
                  <Action type="2" message="0" wParam="41006" lParam="0" sParam="" />
              </Macro>
      

      is displayed as:

              <Macro name="Trim Trailing Space and Save" Ctrl="no" Alt="yes" Shift="yes" Key="83">
                  <!-- Trim Trailing Space -->
                  <Action type="2" message="0" wParam="42024" lParam="0" sParam="" />
                  <!-- &amp;Save -->
                  <Action type="2" message="0" wParam="41006" lParam="0" sParam="" />
              </Macro>
      

      Another example that @PeterJones posted recently with comments added by the script:

              <Macro name="DelUnderBookmarks" Ctrl="yes" Alt="no" Shift="yes" Key="121">
                  <!-- Next Bookmark -->
                  <Action type="2" message="0" wParam="43006" lParam="0" sParam="Next Bookmark" />
                  <!-- Toggle Bookmark -->
                  <Action type="2" message="0" wParam="43005" lParam="0" sParam="Toggle Bookmark" />
                  <!-- SCI_LINEDOWN -->
                  <Action type="0" message="2300" wParam="0" lParam="0" sParam="Line Down" />
                  <!-- SCI_LINEDELETE -->
                  <Action type="0" message="2338" wParam="0" lParam="0" sParam="Line Delete" />
              </Macro>
      

      So the ID numbers … are used to create XML comments so the macros are more understandable to read.

      Some other various comments seen though testing:

      <!-- Clear All Bookmarks -->
      <!-- Remove Bookmarked Lines -->
      <!-- IDC_FRCOMMAND_INIT -->
      <!-- IDFINDWHAT -->
      <!-- IDNORMAL [Normal] -->
      <!-- IDNORMAL [Extended] -->
      <!-- IDNORMAL [RegEx] -->
      <!-- IDC_FRCOMMAND_BOOLEANS -->
      <!-- IDC_FRCOMMAND_EXEC -->
      <!-- SCI_REPLACESEL -->
      <!-- SCI_TAB -->
      

      I have tested with portable Notepad++ so if have problems with installed Notepad++ then let me know and I will look into it. The script is quite new so perhaps some bugs exist.

      Let me know how the experience is with testing the script. The files are downloaded to the plugins Config directory and that is where the json files will be created so that you know where they are if an inspection or cleanup is wanted.

      1 Reply Last reply Reply Quote 8
      • Alan KilbornA
        Alan Kilborn
        last edited by Alan Kilborn

        Nice job!
        Very nice indeed!
        It’s right up there with @PeterJones “config updater”; you should consider having it turned into an official PythonScript “Sample” script (like Peter did recently for his).

        Let me know how the experience is with testing the script.

        It went well! :-)

        The files are downloaded to the plugins Config directory and that is where the json files will be created so that you know where they are if an inspection or cleanup is wanted.

        I did add that cleanup as a rough hack, at the very bottom of the file, after main():

        cleanup_file_list = [
            'FindReplaceDlg_rc.h',
            'FindReplaceDlg_rc.h.json',
            'Scintilla.h',
            'Scintilla.h.json',
        ]
        cfg_dir = notepad.getPluginConfigDir()
        for f in cleanup_file_list:
            p = os.path.join(cfg_dir, f)
            if os.path.isfile(p): os.remove(p)
        

        Some time ago I started scripting what I was calling a “macro disassembler”.
        I hinted about it HERE and HERE.
        Although it took a different approach from yours, the result was similar (but still, different).

        Looking at my old notes on it, I had quit working on it because it had some big (they must have been “big”) issues handling this sort of string in shortcuts.xml: sParam="&#xD83D;&#xDC99;".
        I believe I was trying to do this pre-8.5.2, where such sequences were used, rather than post-8.5.2 where they were handled by N++ a bit differently (which may have allowed my code to work, if I’d gotten back to checking it – it seems to work NOW).

        I use a lot of “searching” macros, so I wrote some extra code to break them down a tad further than you did; here’s a sample of one of them, “disassembled”:

        <Macro name="Mark DUPLICATE LINES (except last one)" Ctrl="no" Alt="no" Shift="no" Key="0">
            KEYCOMBO:none
            <Action type="3" message="1700" wParam="0" lParam="0" sParam="b''" />
                SEARCH:initialize
            <Action type="3" message="1601" wParam="0" lParam="0" sParam="b'(?-s)^(.*)\\R(?s)(?=.*^\\1(?:\\R|\\z))'" />
                SEARCH:find_what:"(?-s)^(.*)\R(?s)(?=.*^\1(?:\R|\z))"
            <Action type="3" message="1602" wParam="0" lParam="0" sParam="b''" />
                SEARCH:replace_with:""
            <Action type="3" message="1625" wParam="0" lParam="2" sParam="b''" />
                SEARCH:mode:regex
            <Action type="3" message="1702" wParam="0" lParam="276" sParam="b''" />
                SEARCH:options:purge_marks_before_new_search/bookmark_line/wrap_around__OR__project2
            <Action type="3" message="1701" wParam="0" lParam="1615" sParam="b''" />
                SEARCH:execute:mark
            <Action type="0" message="2316" wParam="0" lParam="0" sParam="b''" />
                SCI_DOCUMENTSTART(0,0)
            <Action type="2" message="0" wParam="43006" lParam="0" sParam="b''" />
                IDM_SEARCH_NEXT_BOOKMARK
        

        Here the 1702 and 1701 commands are “explained” a bit further than in your output:

        SEARCH:options:purge_marks_before_new_search/bookmark_line/wrap_around__OR__project2

        SEARCH:execute:mark

        If you want to make improvements to your inspector, something like that might be a nice addition.

        EDIT: I just noticed in my disassembler output that sParam= values that should have been empty actually contain b''. This is very likely from the fact that when I first developed the script I was using PythonScript2, and since then I’ve switched to PythonScript3, and so running it now to produce the output I posted here likely contains some parts that need to be reevaluated (because Python2 and Python3 use different definitions for what a string is).

        mpheathM 1 Reply Last reply Reply Quote 4
        • mpheathM
          mpheath @Alan Kilborn
          last edited by

          @Alan-Kilborn Glad it worked well for you.

          I rushed this release as I had not done a gist before so thought I’d give it a try and so here it is as an early release. So good to hear no hiccups yet from the script as 1/1 is perfect score so far. :)

          The json files should be good for several Notepad++ versions until a header file gets updated. Downloading them each use will slow down the run of the script. The header files are temporary to create the json files so the header files can be deleted and if an update to the script is done, I may add the code in to delete the header files asap. I just want to know it works for everyone before getting rid of the evidence that might help to solve any bugs.

          The IDC_FRCOMMAND_BOOLEANS lParam value could be bit down into it’s settings which is the checkbox states … I never got that far yet and may consider it.

          I had not seen or heard of your macro disassembler before now. Quite similar approach with commenting the file. I have considered a Gui as a alternative recorder though I consider it would be a very involving task to do so doubt I will do that. So, commenting the file is probably the best option IMO.

          I notice mention of SCI_PASTE with \r value in your link. I’m not sure if running the macro would know how to handle that as paste where at which point. Doesn’t matter to me at the moment as may figure it out later.

          If you may notice in the code, I do not handle sParam so the code happily ignores it. sParam can be multi-line so off-limits sounds good to me.

          Alan KilbornA 1 Reply Last reply Reply Quote 2
          • Alan KilbornA
            Alan Kilborn @mpheath
            last edited by

            @mpheath said in MacroInspect which comments macros:

            The json files should be good for several Notepad++ versions until a header file gets updated. Downloading them each use will slow down the run of the script.

            I’d have been more apt to leave them if the code had created a subdir and put them in that.
            As is, I don’t like misc files sitting in the root of my config dir.

            mpheathM 1 Reply Last reply Reply Quote 1
            • guy038G
              guy038
              last edited by

              Hello, @mpheath, @alan-kilborn and All,

              I do not wish to minimize your approaches and works, on this matter, but I’m wondering :

              Wouldn’t it be better to simply add a comment attribute to any section of the shortcuts.xml file ?

              Perhaps, I’m not seeing all the implications or drawbacks of this possible improvement !

              BR

              guy038

              Alan KilbornA 1 Reply Last reply Reply Quote 0
              • Alan KilbornA
                Alan Kilborn @guy038
                last edited by

                @guy038 said in MacroInspect which comments macros:

                drawbacks of this possible improvement

                The next time that N++ itself needs to update shortcuts.xml, say goodbye to the comments.

                1 Reply Last reply Reply Quote 0
                • guy038G
                  guy038
                  last edited by

                  Hi, @mpheath, @alan-kilborn and All,

                  Well, I meant something like this :

                              <Action type="0" message 2170" wParam="0" lParam="0", sParam="" comments="Scintilla Message SCI_COPY" />
                              <Action type="2" message="0" wParam="41001" lParam="0" sParam="" comments="Menu command FILE_NEW" />
                  

                  Best Regards,

                  guy038

                  PeterJonesP 1 Reply Last reply Reply Quote 1
                  • PeterJonesP
                    PeterJones @guy038
                    last edited by PeterJones

                    @guy038 said in MacroInspect which comments macros:

                    Well, I meant something like this :

                    Unfortunately, that still won’t work: Manually create that macro with the comments attributes, then restart: the comments attributes are still there, because Notepad++ hasn’t tried to write that file yet; then use Macro > Modify Shortcut/Delete Macro to change the shortcut for any macro (even a different macro), exit Notepad++, and restart: the comments attributes are gone, because Notepad++ re-wrote the file during the exit.

                    When Notepad++ needs to write out shortcuts.xml – which it needs to do if you use the GUI to change something in Shortcut Mapper, or record or delete a Macro, or add a new Run menu command – then it will only include the attributes that Notepad++ tracks, so the comments attribute will go away; since Notepad++ doesn’t track <!-- ... --> comments either, those will also go away.

                    This is why, if I want a permanent comment, I either use the sParam attribute to hold a command that doesn’t use the sParam value, or I add dummy commands using the SCI_NULL command using type="0" message="2172" `, as in:

                    <Action type="0" message="2172" wParam="0" lParam="0" sParam="This Comment Will Be Kept" />
                    
                    1 Reply Last reply Reply Quote 2
                    • guy038G
                      guy038
                      last edited by guy038

                      Hello, @mpheath, @alan-kilborn, @peterjones and All,

                      Peter, I still don’t understand ! Of course, without some modifications in N++ code, this method would obviously not work !

                      But, if the structure of the Shortcuts.xml file is globally modified in order to include an empty comments attribute, by default, I do not see why this new behavior wouldn’t work !

                      I also understand that, if a macro would be deleted, from the Shortcut Mapper, every part of this specific macro would be deleted, including the comments !

                      BR

                      guy038

                      PeterJonesP 1 Reply Last reply Reply Quote 0
                      • PeterJonesP
                        PeterJones @guy038
                        last edited by

                        @guy038 ,

                        Sorry, I did not realize you were suggesting modifying Notepad++ to be able to allow a comment="..." attribute. That’s something completely different.

                        My comments about Shortcut Mapper were not trying to say that you should delete the macro under investigation for the experiment; my comments were that, for all existing versions of Notepad++, if you change the keyboard shortcut for any macro (or if you record a new macro, or add a new Run menu command, or change the keyboard shortcut for any menu entry), then when the current version of Notepad++ writes the file, it will write it without <!-- comments --> and without comments="pseudocomment" attributes included, thus erasing it. I was talking about existing behavior.

                        For a feature request: I doubt Don would be interested in allowing the new attribute, because it’s not something that the Macro Recorder will use, and probably not something that even most people manually editing macros would use; he doesn’t often add (or allow others to add) features that would just help the select few power-users.

                        And without a feature request, my workaround examples of using sParam – either on actions that just leave it sParam="", or on the SCI_NULL action – seems a good workaround for power users who want to document their macros under existing conditions.

                        Alan KilbornA 1 Reply Last reply Reply Quote 3
                        • Alan KilbornA
                          Alan Kilborn @PeterJones
                          last edited by

                          This post is deleted!
                          1 Reply Last reply Reply Quote 0
                          • guy038G
                            guy038
                            last edited by guy038

                            Hello, @mpheath, @alan-kilborn, @peterjones and All,

                            Note that, if this new attribute would exist, then, using the @mpheath’s solution or any other one, it could be possible to fill in all the comments zones automatically, when N++ exits and the shortcuts.xml file is saved !

                            On restarting N++, the display of the shortcuts.xml file would produce good pieces of information, in the user language !

                            However, this would probably need a lot of coding for, I must admit, a little benefice !

                            Best Regards,

                            guy038

                            1 Reply Last reply Reply Quote 0
                            • mpheathM
                              mpheath @Alan Kilborn
                              last edited by

                              To All, I have updated the gist with revision 2.
                              Minor changes. The header files will be removed if json files are created or already exist.


                              @Alan-Kilborn said in MacroInspect which comments macros:

                              I’d have been more apt to leave them if the code had created a subdir and put them in that.
                              As is, I don’t like misc files sitting in the root of my config dir.

                              Agree. It could get very messy if scripts follow the trend including this script.

                              The name of this subdir could be Config\MacroInspect though I consider that as starting an ugly trend. Perhaps within the Config\PythonScript\scripts\MacroInspect though still the files are not scripts as they are data files.

                              Perhaps a central subdir like Config\Temp and temporary data files go there. So a user can clean that directory periodically and the scripts can remake the data files from latest version content as needed. To save name collisions could possibly prefix the files with script name like MacroInspect-Type0.json for example. If a script does not remake the data files then this temp directory would not be good to use and so may need a subdir like Config\Static for example.

                              The concept may need some confirmation before I modify the script to alter where the data files go else the script may make a mess with obsolete data files in the Config directory.

                              Alan KilbornA 1 Reply Last reply Reply Quote 2
                              • Alan KilbornA
                                Alan Kilborn @mpheath
                                last edited by Alan Kilborn

                                @mpheath

                                When I write a script that needs a permanent data file, I put the data file in the same folder as the script with a common prefix on the name. An example probably helps:

                                Script: ...\plugins\Config\PythonScript\scripts\fubar.py
                                Data: ...\plugins\Config\PythonScript\scripts\fubar_data.json

                                It’s not too messy; locate the script (e.g. Windows Explorer) and the data file is visually adjacent to it.


                                If I need more than one data file (i.e., your situation), I give everything its own folder:

                                Folder: ...\plugins\Config\PythonScript\scripts\foo\
                                Script: ...\plugins\Config\PythonScript\scripts\foo\foo.py
                                Data1: ...\plugins\Config\PythonScript\scripts\foo\foo_data1.json
                                Data2: ...\plugins\Config\PythonScript\scripts\foo\foo_data2.txt

                                In this case it isn’t terribly important to name the data files with the common prefix…


                                YMMV. I’m sure you’ll decide on a workable solution.

                                mpheathM 1 Reply Last reply Reply Quote 2
                                • mpheathM
                                  mpheath @Alan Kilborn
                                  last edited by

                                  To All, I have updated the gist with revision 3.

                                  Implemented the @Alan-Kilborn idea of single json file saved next to the script.

                                  Please manually remove existing data files from the Config directory:

                                  FindReplaceDlg_rc.h
                                  FindReplaceDlg_rc.h.json
                                  Scintilla.h
                                  Scintilla.h.json
                                  

                                  Update MacroInspect.py in the Config\PythonScript\scripts directory. On first run should download the header data and save to Config\PythonScript\scripts\MacroInspect_data.json. No temporary header files as the data will all be processed in memory.

                                  Improved message 1701 comment which is IDC_FRCOMMAND_EXEC will now show it’s lParam related constant.
                                  Some examples:

                                  <!-- IDC_FRCOMMAND_EXEC  [IDCMARKALL] -->
                                  <!-- IDC_FRCOMMAND_EXEC  [IDD_FINDINFILES_FIND_BUTTON] -->
                                  <!-- IDC_FRCOMMAND_EXEC  [IDD_FINDINFILES_FINDINPROJECTS] -->
                                  
                                  1 Reply Last reply Reply Quote 0
                                  • Alan KilbornA
                                    Alan Kilborn
                                    last edited by Alan Kilborn

                                    I ran revision 3 and it seemed to go as desired.

                                    Afterward I looked for the MacroInspect_data.json file and curiously I did not find it anywhere on my system.

                                    After changing the code to add print('json_file:', json_file) I then saw the problem.
                                    I use a script to “run the script in the active tab” so that I can tie it to a keycombo for quickly executing a script under development, over and over again as development proceeds. I think I would go mad if I had to navigate the menus to repeatedly run a script I’m actively working on. (Yes, there are other alternatives…)

                                    Anyway, I found that the data file for your script was being created for me as RunCurrentPyFileAsPythonscript_data.json because my “script runner” script is named RunCurrentPyFileAsPythonscript.py. This results (apparently) from the use of __file__ in your code.

                                    No big deal, I can compensate for this… :-)

                                    Again, great job on a great script.

                                    mpheathM 1 Reply Last reply Reply Quote 2
                                    • mpheathM
                                      mpheath @Alan Kilborn
                                      last edited by

                                      @Alan-Kilborn

                                      Sorry to hear. It’s still a bug even if it is a script running a script. I just tested sys.argv and thought argument 0 might be OK though it returns ['C:\\Programs\\Notepad++\\notepad++.exe']. If you can test something that works for you and everyone else then please share. I may test some alternatives though I am not sure if I can reproduce your environment to be sure of a fix.

                                      1 Reply Last reply Reply Quote 0
                                      • Alan KilbornA
                                        Alan Kilborn
                                        last edited by

                                        @mpheath

                                        In general, a way I’ve found to do it is:

                                        inspect.getframeinfo(inspect.currentframe()).filename

                                        after importing inspect, and then getting the directory part of the path from that.

                                        This worked great in PythonScript2, as the path to the file was returned from the inspect call. In PythonScript3, however, all that call obtains is a filename without the complete path.

                                        You can experiment with that if you like, or I’ll post some more complete code later.

                                        mpheathM 1 Reply Last reply Reply Quote 1
                                        • mpheathM
                                          mpheath @Alan Kilborn
                                          last edited by mpheath

                                          @Alan-Kilborn

                                          Just tested in a Python interpreter. __file__ can be set to a value. So perhaps your RunCurrentPyFileAsPythonscript.py can set __file__ to the absolute path of the child script before it runs the subprocess, so then the child process use of __file__ is inherited like as if it is the main process.

                                          Alan KilbornA 2 Replies Last reply Reply Quote 2
                                          • Alan KilbornA
                                            Alan Kilborn @mpheath
                                            last edited by Alan Kilborn

                                            @mpheath said:

                                            perhaps your RunCurrentPyFileAsPythonscript.py can set file to the absolute path of the child script before it runs …

                                            A good idea, however it doesn’t work (I still get the calling script’s name/path), at least the way I’m having a script run a script; there are many ways to do it. It’s actually surprisingly difficult to have a script run a script (and have things all turn out like you’d like).


                                            LATER EDIT:
                                            Actually, it did work; what caused it to not work the first time I tried it was programmer error, i.e., me!
                                            I may have to look further into using the __file__ variable technique instead of my inspect-based technique, as it seems like it could simplify things under Python3.

                                            1 Reply Last reply Reply Quote 2
                                            • First post
                                              Last post
                                            The Community of users of the Notepad++ text editor.
                                            Powered by NodeBB | Contributors