Sublime Forum

Macros don't record Find/Replace?

#63

Changelog (Build 4107) 20 May 2021
Text Commands

  • Macros now record Find commands

Nice - Thanks! … but with a macro such as:

F3 (Find Next)
Ctrl+Shift+K (Delete Line)

… when nothing is found, the current line is deleted for each Playback without notice until you realise what’s happening :face_with_raised_eyebrow:

Ctrl+Z, Ctrl+Z, Ctrl+Shift+Z :grimacing:

0 Likes

#64

Macros are plain lists of commands being executed after each other. There’s no control flow to stop in the middle of anywhere if something strange happens.

0 Likes

#65

I accept most of the blame :wink: . I could have put the find at the end and done the first F3, manually:

Ctrl+Shift+K (Delete Line)
F3 (Find Next)

I notice there is the usual feedback on the status bar ("Unable to find ") but, by that time, the later operations could need undoing.

But I wonder if this is an implementation consideration.
Clearly, the team originally excluded Find from macros for a reason (perhaps this reason?).
I’m cautious about trying to anticipate a user’s intentions but, surely, terminating macro playback at the point of “Unable to find” would be no worse than excluding Find operations altogether (prior to ST4)?

Either that or provide a “Find or Halt macro” checkbox in the Tools menu (Macro section) or a separate keybinding (Ctrl+Alt+F3 ?) for “Find or Halt macro playback” (Ctrl+Alt+Shift+F3 = reverse Find ?).

I hope someone can prove me wrong by providing any useful case where a macro contains a Find followed by any operation(s) not dependent on Find success. Then, I’ll shut up :relaxed:

0 Likes

#66

@deathaxe. It is a great Plugin, I can’t get the error @acsr gets:

Finding and replacing “.” with “#FULLSTOP#” turns all the fullstops into #FULLSTOP#. However that’s in code not written text.
Perhaps I’m missing something and that’s not to say that the error doesn’t exist, not at all. Is it worth changing the plugin I wonder ?

Maybe it is, how would I implement the above plugin so that it just does find and replace for seleted text only. If you can point me in the right direction, I’m having a crack at learning Python (should be easy after C++ :crazy_face:) so no need to do all the heavy lifting :dizzy:

In summary only in an area bounded by Selected_Text does the find and replace occur, leaving the character matches outside the area untouched. Hint only required (though a large hint would be great) !

Thanks again…

0 Likes

#67

The original plugin above is a quite dump straight forward wrapper for the view.replace() API function. It can be improved by several means, of course.

According to your description I came up with the following proposal:

The start_pt argument is removed as the command uses selections to look for regions to replace tokens within. If all selections are empty, view.replace() is performed within the whole document. Otherwise only tokens within a non-empty selection are replaced.

It provides a selector argument which can be used to replace tokens only if they match certain scopes. A "selector": "text.html.markdown - markup.raw - source" for instance would replace a token in a Markdown file but leave all fenced codeblocks untouched.

class FindReplaceSelectedCommand(sublime_plugin.TextCommand):
    """The implementation of 'find_replace_selected' text command.

    Example:

        view.run_command(
            "find_replace", {
                "pattern": "the",
                "replace_by": "THE",
                "selector": "text - source",
                "flags": ["LITERAL"]
            }
        )
    """

    FLAGS = {
        "LITERAL": sublime.LITERAL,
        "IGNORECASE": sublime.IGNORECASE
    }

    def run(self, edit, pattern, replace_by, selector=None, flags=[]):
        """Find and replace all patterns.

        Arguments:
            edit (sublime.Edit):
                The edit token used to undo this command.
            pattern (string):
                The regex pattern to use for finding.
            replace_by (string):
                The text to replace all found words by.
            selector (string):
                The selector to match a found token against.
                Can be used to replace tokens of certain scope only.
            flags (list):
                The flags to pass to view.find()
                ["LITERAL", "IGNORECASE"]
        """
        view = self.view
        selections = view.sel()
        selection_empty = all(len(sel) == 0 for sel in selections)

        for found in reversed(view.find_all(pattern, self._flags(flags))):
            # skip all tokens which don't match the selector
            if selector and not view.match_selector(found.begin(), selector):
                continue
            # skip all tokens not within any selection if non-empty selections exist
            if selection_empty or any(sel.contains(found) for sel in selections):
                view.replace(edit, found, replace_by)

    def _flags(self, flags):
        """Translate list of flags."""
        result = 0
        for flag in flags:
            result |= self.FLAGS.get(flag, 0)
        return result
0 Likes

Variable substitution ignoring `g` flag?
#68

Thank you. I’ll get back to you a couple of weeks, got a busy week so have to crack on with other things ATM.

But thanks again, it’s a great plugin !

0 Likes

#69

You are welcome.

0 Likes

#70

Sublime Text 4 now supports Find commands in macros.

0 Likes

#71

@Raikkonen, I’ve got build 4126 but find commands seem still to be not recorded, are they? What should a find command look like in a macro syntax?

Any help on this matter is appreciated!

1 Like

#73

We don’t need Plugins. Here is the sublime text macro code to find “War” and repalce it with “Love”.
[
{
“args”: {“to”: “bof”}, “command”: “move_to”
},
{
“args”: {“characters”: “War”}, “command”: “insert”
},
{
“args”: {“extend”: true, “to”: “bol”}, “command”: “move_to”
},
{
“command”: “slurp_find_string”
},
{
“args”: {“characters”: “Love”}, “command”: “insert”
},
{
“args”: {“extend”: true, “to”: “bol”}, “command”: “move_to”
},
{
“command”: “slurp_replace_string”
},
{
“args”: null, “command”: “left_delete”
},
{
“args”: {“close_panel”: true }, “command”: “replace_all”
},
]

1 Like

#74

Edit /Packages/RegReplace/reg_replace.sublime-settings (you can access this in ST2 at Preferences > Package Settings > Reg Replace > Settings – Default). Create new entries in the “replacements” object, each one being a single regex find/replace. Note that you have to doubly escape special characters.

String those individual replacements together into a command for the Command Palette. To do this, edit the file /RegReplace/Default.sublime-commands (Preferences > Package Settings > Reg Replace > Commands – Default). Add something like this:

0 Likes

#75

That’s really clever!
I was looking for a way to change the search buffer, and this does just that.
Thank you so much!

0 Likes

#76

Build 4152 also does not record find commands. I think either the fix in Sublime Text 4 was reverted, or never made it in in the first place.

0 Likes

#77

It’s working fine for me on build 4155. What exactly are you doing that isn’t working for you?

0 Likes

#78

On build 4155 now, steps:

  1. Tools -> Record Macro
  2. Find -> Replace… and replace text in entire file.
  3. Find -> Replace… possibly again more times
  4. Tools -> Stop Recording Macro

Results:

  • Tools -> Playback Macro stays greyed out
  • Tools -> Save Macro not greyed out but choosing it does not present any dialog box or any other feedback.

If I add another editing step while recording a macro, such as entering some text, then macro containing only those actions is recorded. No Find -> Replace… actions recorded.

0 Likes

#79

I found the issue here: The buttons in the find/replace panel don’t run any commands - they execute find operations directly. As such they can’t be recorded. If you use the shortcuts it’ll work.

0 Likes

#80

Thank you for the reply. By using the shortcuts I do get the commands in the macro, but the search and replace regex/replacement arguments don’t make it into the recording. Performing 5 replacements produced this macro:

[
	{
		"args":
		{
			"close_panel": true
		},
		"command": "replace_all"
	},
	{
		"args":
		{
			"close_panel": true
		},
		"command": "replace_all"
	},
	{
		"args":
		{
			"close_panel": true
		},
		"command": "replace_all"
	},
	{
		"args":
		{
			"close_panel": true
		},
		"command": "replace_all"
	},
	{
		"args":
		{
			"close_panel": true
		},
		"command": "replace_all"
	}
]
0 Likes

#81

The replace_all command doesn’t take any additional arguments. What are you expecting it to record?

0 Likes

#82

I am being the naïve user who thought that the text I enter to search for, and the text I enter to replace it with, would be recorded in the macro I had started recording just before, so that my editing actions could be re-run at a future date on a new file. Is that not the basic value proposition of recording macros from the user’s perspective? Thanks for your input!

0 Likes

#83

The primary use of find in macros is to search for some text that’s already in the document, or to do actions involving search using a query already in the find panel.

0 Likes