Search code examples
sublimetext3sublimetext2sublimetextsublime-text-plugin

How to automatically add an increased number inside of a string on a new line with SublimeText2 or SublimeText3?


I'm working on some mIRC script that required a prepend string on each line followed with a line number increased from the number contained on the previous line, if there is one.

example:

[ips]

[urls]
n0=1:mIRC NewsURL:http://www.mirc.com/news.html
n1=2:mIRC RegisterURL:http://www.mirc.com/register.html
n2=3:mIRC HelpURL:http://www.mirc.com/help.html

So, if I am on the first line: [ips] (which does not start with a pattern n*=) and I press ENTER, I would like the next line to be prepended with n0=

But, if I am on the last line n2=3:mIRC HelpURL:http://www.mirc.com/help.html (which start with a pattern n*=) and I press ENTER, I would like the next line to be prepended with n3=

Is there's a way to make it happens?


Solution

  • A plugin can do this kind of thing. Basically what we want is to override the normal behavior of enter when the line contains n*= at the beginning where * is a number. For this, we require a custom EventListener that implements a on_query_context method & a custom command that runs when the context is fulfilled.

    import re
    import sublime
    import sublime_plugin
    
    class MrcScriptEventListener(sublime_plugin.EventListener):
        """ A custom event listener that implements an on_query_context method which checks to see if
            the start of the line if of the form n*= where * = number.  
        """
    
        def on_query_context(self, view, key, operator, operand, match_all):
            current_pt = view.sel()[0].begin()
            desired = view.substr(view.line(view.sel()[0].begin()))
    
            if key != "mrc_script":
                return None 
    
            if operator != sublime.OP_REGEX_MATCH:
                return None
    
            if operator == sublime.OP_REGEX_MATCH:
                return re.search(operand, desired)
    
            return None
    
    
    class MrcScriptCommand(sublime_plugin.TextCommand):
        """ A custom command that is executed when the context set by the MrcScript event listener
            is fulfilled.  
        """
    
        def run(self, edit):
            current_line = self.view.substr(self.view.line(self.view.sel()[0].begin()))
            match_pattern = r"^(n\d+=)"
            if re.search(match_pattern, current_line):
                num = int(re.match(match_pattern, current_line).groups()[0][1:-1]) + 1
                self.view.run_command("insert", {
                        "characters": "\nn{}=".format(num)
                })
            else:
                return
    

    The key binding is as follows:-

    {
        "keys": ["enter"],
        "command": "mrc_script",
        "context": [
            {
                "key": "mrc_script",
                "operator": "regex_match",
                "operand": "^(n\\d+=)"
            }
        ],
    }
    

    I'll not go into the details of how this plugin works. All that is required to make this work is to follow the instructions given in this gist.

    Here's a gif of it in action :-

    enter image description here The caveats are :-

    1. It doesn't respect the [ips] part of your request as I think that'll make the plugin unnecessarily complicated.
    2. It just looks at the current line, see's the number between n & = & increments it accordingly for the next line. So it's not smart about whether such a line is already present or not.

    Hopefully, this meets your requirements.