Sublime Forum

Remove spaces before punctuation

#1

Is there a way to remove spaces before punctuation during typing ?
I have tried several ways with key commands, without success.

Example
“This is my text .” (without the “”)
should automatically change to
“This is my text.”

0 Likes

#2

You could create a number of keybinds that require preceding text, perhaps. I have something that does something a bit different, but checks for a space in front of the character.

        // This keybind makes it so you can do continuous commenting if you end
	// have already started a comment line, and the preceding character is a
	// space.
	{
		"keys": ["enter"],
		"command": "insert",
		"args": {"characters": "\n-- "},
		"context": [
			{"key": "selector", "operand": "comment.line.vhdl"},
			{"key": "preceding_text", "operator": "regex_contains", "operand": " $", "match_all": true}
		]
	},

Note, this is an example only. It’s not exactly what you want to do.

You’d want to make a keybind that triggered on your desired punctuation key and then checked to see if a space was there.

Another possibility would be doing a regex search for space-punctuation and replace with punctuation only?

0 Likes

#3

Thanks. It’s a good starting and almost doing what I am looking for.

{
	"keys": [","], "command": "insert", "args": {"characters": "left_delete,"}, "context": 
	[
		{"key": "preceding_text", "operator": "regex_contains", "operand": " $", "match_all": true}
	]
},

In the first line, I have no glue how I could under “characters” do a backspace, to delete the space before. I have seen under key commands, that left_delete is the command, but it doesn’t work under “characters”. Any idea from someone?

Your proposal to replace the space-punctuation with punctuation only is also a good idea. Could you please give me any simple example line of principle how I could replace with regex characters? Thank you.

0 Likes

#4

You can’t directly replace with regex characters in this case; the regex in the context of the key binding is only used to determine if the binding is active or not, but you can’t directly capture the results of the match for use in your binding.

The left_delete command will remove the character to the left of the cursor and the insert command will insert some text, so what you need is a way to run both commands one after the other in response to your key binding.

One way to do that would be to create a sublime-macro file that contains the two commands and then bind the key to the run_macro_file command instead.

For example, if you save the following text in a file named something like delete_insert.sublime-macro in your User package (use Preferences > Browse Packages... to find it if you don’t know where it is):

[
    { "command": "left_delete" },
    { "command": "insert", "args": {"characters": ","}}
]

You could trigger it with a key binding like this:

{
    "keys": [","],
    "command": "run_macro_file",
    "args": {"file": "res://Packages/User/delete_insert.sublime-macro"},
    "context":
    [
        {"key": "preceding_text", "operator": "regex_contains", "operand": " $", "match_all": true}
    ]
},

That works, but you would need to create a separate sublime-macro for every punctuation character, which is a bit of a pain in the butt.

Alternatively, you could roll up both commands into a simple plugin, such as by selecting Tools > Developer > New Plugin... from the menu and replacing the contents with the following, then saving it as something like delete_insert,py:

import sublime
import sublime_plugin


class LeftDeleteInsertCommand(sublime_plugin.TextCommand):
    def run(self, edit, **kwargs):
        self.view.run_command("left_delete")
        self.view.run_command("insert", args=kwargs)

That implements a command called left_delete_insert that first does a left_delete and then runs the insert command. Using that, your key binding would look something like this:

{
    "keys": [","],
    "command": "left_delete_insert",
    "args": {"characters": ","},
    "context":
    [
        {"key": "preceding_text", "operator": "regex_contains", "operand": " $", "match_all": true}
    ]
},

Now for every key binding you can specify the character(s) to insert and the left delete will trigger automatically.

Lastly, you could use a pre built package for running multiple commands, such as Chain of Command. If you install that package, then you don’t need a plugin and you could instead use a key binding like the following:

{
    "keys": [","],
    "command": "chain",
    "args": {"commands": [
        ["left_delete"],
        ["insert", {"characters": ","}]
    ]},
    "context":
    [
        {"key": "preceding_text", "operator": "regex_contains", "operand": " $", "match_all": true}
    ]
},

Using Chain of Command has the highest utility factor for this, since you could potentially use it in other cases where you want to bind a key to multiple commands in a row.

2 Likes

#5

WOW. Thanks a lot. I am sure I can solve it now. :slight_smile:

0 Likes