3

To customize Sublime Text's behaviour on when to indent newlines, depending on the current line, one can change the whatever.tmPreferences file appropriately setting the increaseIndentPattern and decreaseIndentPattern options, like shown for example in this other answer.

However, I can't seem to work out how to generate the following behaviour: given a line like

[<cursor here>]

with the cursor between the square brakets, pressing enter I want the following result:

[
    <cursor here>
]

This is for example what happens when modifying an xml file one presses enter between two brackets, like in <sometag><cursor here></sometag>.

I tried to look into the tmPreferences files for the xml but to no avail.

A similar question has been asked here, but the present one is different for several reasons:

  1. I want this behaviour to be implemented only for specific file extensions, and to be shipped with a package. So I'm also asking where exactly I should put the instructions for this custom keybinding.
  2. In the linked question the matter is simpler: to just correctly add and indent newlines between some kind of braces. It is not straightforward (to me) how to generalize this behaviour as in the example cited above in which we want a newline between XML-like tags, as in this case we will have to somehow deal with regexes and verify that the left and right patterns match.

How can this behaviour be implemented?

Community
  • 1
  • 1
glS
  • 1,209
  • 6
  • 18
  • 45
  • Possible duplicate of [How to set bracket indentation behavior in ST3](http://stackoverflow.com/questions/41456641/how-to-set-bracket-indentation-behavior-in-st3) – Keith Hall Feb 05 '17 at 15:56
  • @KeithHall thanks for the link. That is indeed very related, but not really a direct answer. In the case of `xml` shown in the post for example, I cannot find any `.sublime-keymap` file into the `XML` folder in `Packages`, so I'm not sure where that keybinding has been set. – glS Feb 05 '17 at 16:05
  • Your user key bindings are always in your `User` package, they're not in the package related to where you want to use them. In order to make the bindings as linked work only for an XML file, you would just need to add an extra context that checks that it's an XML file. Look at the very last binding in the default key bindings file for an example. – OdatNurd Feb 06 '17 at 04:55
  • @OdatNurd well but that is not always true, is it? Just look at any installed package that integrates a number of customized key bindings for some target file types. – glS Feb 06 '17 at 11:10
  • True, but you didn't originally mention that you wanted to ship it with a plugin. ;) – OdatNurd Feb 06 '17 at 14:22

1 Answers1

3

To make a keybinding that will be shipped with a package, create a Default.sublime-keymap file in your package.

Normally Sublime Text looks at the syntax being used to highlight the document, as opposed to the file extensions used, to determine whether keybindings/plugins should be active etc. This is mainly so that it will work on files that haven't been saved yet. If you want to follow this guideline, you can use the selector keybinding context. In the case of XML files, you would probably want to use source.xml. Otherwise, you will need to create an EventListener that defines a on_query_context method to check the view.file_name(). You could use the os.path.splitext method to retrieve the file extension.

If you are truly dealing with XML, then you could use the default auto_indent_tag keybinding as inspiration:

{ "keys": ["enter"], "command": "auto_indent_tag", "context":
    [
        { "key": "setting.auto_indent", "operator": "equal", "operand": true },
        { "key": "selection_empty", "operator": "equal", "operand": true, "match_all": true },
        { "key": "selector", "operator": "equal", "operand": "punctuation.definition.tag.begin", "match_all": true },
        { "key": "preceding_text", "operator": "regex_contains", "operand": ">$", "match_all": true },
        { "key": "following_text", "operator": "regex_contains", "operand": "^</", "match_all": true },
    ]
},

to build something like:

{ "keys": ["enter"], "command": "insert_snippet", "args": { "contents": "\n\t$1\n" }, "context":
    [
        { "key": "setting.auto_indent", "operator": "equal", "operand": true },
        { "key": "selection_empty", "operator": "equal", "operand": true, "match_all": true },
        { "key": "selector", "operator": "equal", "operand": "text.xml punctuation.definition.tag.begin", "match_all": true },
        { "key": "preceding_text", "operator": "regex_contains", "operand": ">$", "match_all": true },
        { "key": "following_text", "operator": "regex_contains", "operand": "^</", "match_all": true },
    ]
},

The regular expressions used here are very simple, just checking the the text immediately before the caret is > and the text immediately after the caret is </. This is possible because the selector checks that a) we are in an XML syntax, and b) the text immediately after the caret is scoped as punctuation.definition.tag.begin. (You can manually check the scope immediately to the right of the caret from the Tools menu -> Developer -> Show Scope Name.) If you are using a custom syntax, you will need to ensure you adjust these accordingly.

In this case, because we are using a keybinding on the Enter key, the indentation rules specified in tmPreferences files are ignored.

Keith Hall
  • 15,362
  • 3
  • 53
  • 71