Here's my version. It's also based on the built-in auto pair key bindings, but with some tweaks.
[
{ "keys": ["*"], "command": "insert_snippet", "args": {"contents": "*$0*"}, "context":
[
{ "key": "setting.auto_match_enabled", "operator": "equal", "operand": true },
{ "key": "selection_empty", "operator": "equal", "operand": true, "match_all": true },
{ "key": "following_text", "operator": "regex_contains", "operand": "^(?:\\s|\\.|,|:|;|!|\\?|'|\"|‐|-|—|\\)|]|\\}|⟩|>|›|»|$)", "match_all": true },
{ "key": "preceding_text", "operator": "not_regex_contains", "operand": "\\S$", "match_all": true },
{ "key": "selector", "operator": "equal", "operand": "text.plain, text.html.markdown", "match_all": true },
]
},
{ "keys": ["*"], "command": "insert_snippet", "args": {"contents": "*${0:$SELECTION}*"}, "context":
[
{ "key": "setting.auto_match_enabled", "operator": "equal", "operand": true },
{ "key": "selection_empty", "operator": "equal", "operand": false, "match_all": true },
{ "key": "selector", "operator": "equal", "operand": "text.plain, text.html.markdown", "match_all": true },
]
},
{ "keys": ["*"], "command": "move", "args": {"by": "characters", "forward": true}, "context":
[
{ "key": "setting.auto_match_enabled", "operator": "equal", "operand": true },
{ "key": "selection_empty", "operator": "equal", "operand": true, "match_all": true },
{ "key": "following_text", "operator": "regex_contains", "operand": "^\\*", "match_all": true },
{ "key": "selector", "operator": "equal", "operand": "text.plain, text.html.markdown", "match_all": true },
]
},
{ "keys": ["backspace"], "command": "run_macro_file", "args": {"file": "res://Packages/Default/Delete Left Right.sublime-macro"}, "context":
[
{ "key": "setting.auto_match_enabled", "operator": "equal", "operand": true },
{ "key": "selection_empty", "operator": "equal", "operand": true, "match_all": true },
{ "key": "preceding_text", "operator": "regex_contains", "operand": "\\*$", "match_all": true },
{ "key": "following_text", "operator": "regex_contains", "operand": "^\\*", "match_all": true },
{ "key": "selector", "operator": "equal", "operand": "text.plain, text.html.markdown", "match_all": true },
]
},
]
Unlike the default, this version will:
- make character pairs only when the caret is preceded by a whitespace character or beginning of a line (e.g. the default doesn't allow auto pairing after
o
, but does after ö
or $
- this version excludes everything)
- make pairs when the caret is followed by various characters (
.
, :
, !
, "
and more - the default only allows pairs to be made before a space, tab and some brackets)
- only work in plain text Markdown files (.txt and .md)
I use this version for quotation marks too, replacing the default (of course allowing quotation marks globally).
Double characters
My version of double character functionality (similar to OP's/based on the default, but with a tweak):
[
{ "keys": ["*"], "command": "insert_snippet", "args": {"contents": "$0**"}, "context":
[
{ "key": "preceding_text", "operator": "regex_contains", "operand": "\\*\\*", "match_all": true },
{ "key": "following_text", "operator": "not_regex_contains", "operand": "\\*", "match_all": true },
{ "key": "selector", "operator": "equal", "operand": "text.plain, text.html.markdown", "match_all": true },
]
},
]
This will make double characters, in this case asterisks (*
), if there are already two consecutive asterisks anywhere in the line preceding the caret.
The tweak allows skipping asterisks (one at a time) when the caret is followed by any asterisk.
OP's version always adds two new asterisks if there are already two consecutive asterisks in the line preceding the caret, even if the caret is followed by an asterisk, which is undesired behaviour (for example just after adding a second double asterisk, pressing the asterisk key would add two more instead of skipping the new asterisks).
Explanations
From Sublime Text's default key bindings:
One rule states that in order to make a pair the caret must be preceded by lowercase letters, uppercase letters, numbers, the same symbol or an underscore:
{ "key": "preceding_text", "operator": ^"not_regex_contains", "operand": "[\"a-zA-Z0-9_]$"^, "match_all": true },
However, it still allow a pair if the caret is preceded by other letters or symbols (ö
, ž
, đ
, ç
, 蘋
, Ψ
, ?
, ~
, [
, •
, $
, etc.)
It shouldn't do that so I tweaked it.
The following requires the caret to be preceded by a whitespace character or beginning of a line (it excludes all other symbols, not just basic letters and numbers):
"not_regex_contains", "operand": "\\S$"
It's a double negative to include beginnings of lines.
The following would only work after whitespace characters (space, tab, other) but NOT at the beginnings of lines:
"regex_contains", "operand": "\\s$"
Another rule states that in order to make a pair the caret must also be followed by certain characters: tab, space, ), ], }, >, end of line:
{ "key": "following_text", "operator": "regex_contains", "operand": "^(?:\t| |\\)|]|\\}|>|$)", "match_all": true },
I thought that character pairing should also be allowed if other characters follow the caret, namely punctuation marks and other whitespace characters not included in the default.
The following includes more punctuation marks (.
, ,
, :
, ;
, !
, ?
, '
, "
, ‐
, -
, —
, ⟩
, ›
, »
) and all whitespace characters:
"^(?:\\s|\\.|,|:|;|!|\\?|'|\"|‐|-|—|\\)|]|\\}|⟩|>|›|»|$)"
Note that a whitespace character preceding the caret is required, meaning that a pair would not be made when not wanted.
This would help in a situation where one wants to start writing formatted text at the end of a sentence but before the punctuation at the end of that sentence, for example (|
represents the caret):
"This is a sentence|"
"This is a sentence |" // user types a space
"This is a sentence *
|*
" // User types a character and it gets a pair instead of staying single
"This is a sentence *
with more words|*
"
This question was asked several years ago, but hopefully my answer will be useful to everyone in the future who is interested.