Sublime Forum

Add space inside parentheses and move cursor

#1

I got the below snippet:
function $1 ($2) { $0 }

How do I add two spaces and move the cursor between the spaces when it’s on the field $2 after I press the space key like below ( assume | is the cursor )?
function example ( | ) { }

To clarify a bit, originally it’s function example (|) { }, when the cursor is on the field $2 and only after I press the space key, it should be like this: function example ( | ) { }

Then after I press tab, it should act like normal which jump to the final field $0

0 Likes

#2

I’ve been thinking about this, but I may have mis-understood your question…

Do you want the parentheses to go from this ( ) to this ( ) (sorry the mark down has got rid of my spaces with in the brackets, there should be three instead of one) after the snippet has been applied or before ?

I’m not sure if Sublime has a specialised scope for ‘being in’ a snippet whilst it’s being applied to the text (as opposed to when it’s being written). You can get the scope of the current cursor with ctrl+shift+alt+p and this is what I got when the cursor was in the middle of a snippet in some code:

source.python meta.function-call.arguments.python punctuation.separator.arguments.python

You could not worry about context and just have a keybinding that inserted a couple of spaces either side of your cursor, either a macro or muilti_command eg:


     { "keys": ["super+space"],
    "command": "multicommand",
    "args": {
      "commands": [
        {
          "command": "insert", "args": {"characters": "   "}
        },
        {
          "command":"move", "args": {"by": "characters", "forward": false}
        }
      ]
    }},

Basically inserts 3 spaces and moves the cursor back one place. You would need to install the multi-command package (which is really handy)

Now if you wanted that command to only work within a snippet (that’s the writting of a snippet, not ‘tabbing through’ when your snippet is being applied to some text) you’d add

"context":
[
  {"key": "eol_selector", "operator": "equal", "operand":"source.sublime.snippet"}
]

Giving:

  { "keys": ["super+4"],
    "command": "multicommand",
    "args": {
      "commands": [
        {
          "command": "insert", "args": {"characters": "${}"}
        },
        {
          "command":"move", "args": {"by": "characters", "forward": false}
        }
      ]
    },
    "context":
    [
      {"key": "eol_selector", "operator": "equal", "operand":"source.sublime.snippet"}
    ]
  },

Hopefully this’ll give you something to get started with…

:rabbit::hocho:

0 Likes

#3

I think it’s close but it is lacking one thing. It should only add spaces and move the cursor back in the middle when we’re in the parenthesis. How do we do that?..

To clarify a bit, my intention is to add spaces and move the cursor back in the middle when I press space in the parenthesis instead of pressing two keys like triggering a shortcut.

0 Likes

#4

Are you probably looking for something like …


	// Expand (|) to ( | ) when space is pressed
	{ "keys": [" "], "command": "insert_snippet", "args": {"contents": " $0 "}, "context":
		[
			{ "key": "setting.auto_match_enabled" },
			{ "key": "terminus_view", "operator": "not_equal", "operand": true },
			{ "key": "selection_empty", "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 }
		]
	},
	{ "keys": ["backspace"], "command": "run_macro_file", "args": {"file": "res://Packages/Default/Delete Left Right.sublime-macro"}, "context":
		[
			{ "key": "setting.auto_match_enabled" },
			{ "key": "terminus_view", "operator": "not_equal", "operand": true },
			{ "key": "selection_empty", "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 }
		]
	},

	// Expand [|] to [ | ] when space is pressed
	{ "keys": [" "], "command": "insert_snippet", "args": {"contents": " $0 "}, "context":
		[
			{ "key": "setting.auto_match_enabled" },
			{ "key": "terminus_view", "operator": "not_equal", "operand": true },
			{ "key": "selection_empty", "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 }
		]
	},
	{ "keys": ["backspace"], "command": "run_macro_file", "args": {"file": "res://Packages/Default/Delete Left Right.sublime-macro"}, "context":
		[
			{ "key": "setting.auto_match_enabled" },
			{ "key": "terminus_view", "operator": "not_equal", "operand": true },
			{ "key": "selection_empty", "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 }
		]
	},

	// Expand {|} to { | } when space is pressed
	{ "keys": [" "], "command": "insert_snippet", "args": {"contents": " $0 "}, "context":
		[
			{ "key": "setting.auto_match_enabled" },
			{ "key": "terminus_view", "operator": "not_equal", "operand": true },
			{ "key": "selection_empty", "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 }
		]
	},
	{ "keys": ["backspace"], "command": "run_macro_file", "args": {"file": "res://Packages/Default/Delete Left Right.sublime-macro"}, "context":
		[
			{ "key": "setting.auto_match_enabled" },
			{ "key": "terminus_view", "operator": "not_equal", "operand": true },
			{ "key": "selection_empty", "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 }
		]
	},

	// Expand /*|*/ to /* | */ when space is pressed
	{ "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": "terminus_view", "operator": "not_equal", "operand": true },
			{ "key": "preceding_text", "operator": "regex_contains", "operand": "/\\*$", "match_all": true },
			{ "key": "following_text", "operator": "regex_contains", "operand": "^\\*/", "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": "terminus_view", "operator": "not_equal", "operand": true },
			{ "key": "preceding_text", "operator": "regex_contains", "operand": "/\\* $", "match_all": true },
			{ "key": "following_text", "operator": "regex_contains", "operand": "^ \\*/", "match_all": true }
		]
	},
2 Likes

#5

Hi, thank you. May I know what is setting.auto_match_enabled, terminus_view, selection_empty because I cannot find relevant documentation.

For regex, what is the \\ means? And what is ($ and ^)? Because they are not proper regex.

0 Likes

#6

setting.auto_match_enabled causes the key binding to be enabled only, if "auto_match_enabled": true is set in Preferences.sublime-settings (it is by default). I’ve just added it as this kind of key binding belongs to ST’s auto-pairing functionality which is controlled by the mensioned setting.

terminus_view just makes sure not to override any custom key binding of the great and popular Terminus package. It would otherwise break and typing space wouldn’t be possible anymore. At least it used to be the case when I created those bindings several years ago.

You need to read \\)$ or ^\\( as a whole. In JSON a \ needs to be escaped hence \\ is required. The patterns translate to

  • \($ = if string ends with (
  • ^\) = if string starts with )

Both patterns are used to check whether the text in front of the caret ends with ( and the text after it starts with ), which literly means “Is caret within two parentheses?”

3 Likes

#7

It means setting.auto_match_enabled just for understanding? It’s still working without this line because as you said it’s already enabled by default.

What about selection_empty?

I’m confused about the regex, shouldn’t it start with ( and end with )? Why it’s opposite in your explanation which start with ) and end with (

0 Likes

#8

It’s still working without this line

Sure. But then the key binding could not be disabled by mensioned setting.

What about selection_empty ?

Each item in the context list is a condition need to be met to enable the binding. One of them is no text to be selected as the result of applying the snippet would be unclear then.

Note: all those lines are basically copied from default auto-pairing bindings.

shouldn’t it start with ( and end with ) ?

Have a look at the operand:

  • previous_text == "\($" < the text in front of the caret should end with (
  • following_text == "^\)" < the text after the caret should start with )

The operator "regex_contains" in conjunction with the ^ and $ parts of the pattern causes only the first or last character of the following or preceding text being checked/matched.

1 Like

#9

Best detailed answer I have ever seen!

0 Likes