m4p85r
m4p85r

Reputation: 402

Quotation mark completion with f-strings in Sublime Text's Python 3

When I type a quotation mark in Sublime Text 3 (3176) it auto completes with a closing quotation mark.

e.g. I type " I get "<cursor>"

This is great and I now expect it all the time. However with the introduction of f-strings into Python if I type f" I get f"<cursor> rather than f"<cursor>". This is not a large problem by any means but it's not as fluid as I feel it could be.

I think the autocompletion rules doesn't add an extra quotation mark if there is a character to the left of the cursor as that's generally when you're trying to input a closing quotation mark.

Is there a way to modify the rules so that it will input the closing quotation mark if the character to the left is an "f"? To avoid a weird use case when you are genuinely trying to end a string finishing with an "f" perhaps there could be an and condition to check for an opening bracket, white space, or equality sign to the left of the "f" for the high-use cases of print(f"string", foo = f"string", and foo =f"string".

Upvotes: 0

Views: 238

Answers (1)

Keith Hall
Keith Hall

Reputation: 16085

Yes, this is possible. Simply add the following to your User keymap file (Preferences menu -> Key Bindings. The User keymap is the one on the right hand side):

// Auto-pair quotes even after string modifiers.
// Copied over from the default bindings with modifications to `preceding_text`
// and an added selector condition.
{ "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": "^(?:\t| |\\)|]|\\}|>|$)", "match_all": true },
        { "key": "preceding_text", "operator": "regex_contains", "operand": "(?i)\\b[bfru]+$", "match_all": true },
        { "key": "selector", "operator": "equal", "operand": "source.python" },
        { "key": "eol_selector", "operator": "not_equal", "operand": "string.quoted.double - punctuation.definition.string.end", "match_all": true }
    ]
},
{ "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": "^(?:\t| |\\)|]|\\}|>|$)", "match_all": true },
        { "key": "preceding_text", "operator": "regex_contains", "operand": "(?i)\\b[bfru]+$", "match_all": true },
        { "key": "selector", "operator": "equal", "operand": "source.python" },
        { "key": "eol_selector", "operator": "not_equal", "operand": "string.quoted.single - punctuation.definition.string.end", "match_all": true }
    ]
},

This will be shipped by default in a future build of ST.

It uses a scope selector to determine whether the caret is already inside a string or not, so the case of finishing a string with an f character isn't a problem.

Upvotes: 2

Related Questions