Automatically set SQL keywords to upper case
This feature may be found on some SQL tools as pgAdmin:
Similar requests:
This feature may be found on some SQL tools as pgAdmin:
Similar requests:
Here you go, this will uppercase the preceding SQL keyword when you press Space:
keybinding:
{ "keys": [" "], "command": "upper_case_previous_item_and_insert_space",
"context":
[
{ "key": "selector", "operator": "equal", "operand": "source.sql", "match_all": true },
{ "key": "selection_empty", "operator": "equal", "operand": true, "match_all": true },
{ "key": "scope_before_cursor", "operator": "equal", "operand": "keyword", "match_all": true },
//{ "key": "preceding_text", "operator": "regex_match", "operand": "[a-z]+$", "match_all": true },
]
},
sql_keyword_uppercase.py
:
import sublime
import sublime_plugin
class UpperCasePreviousItemAndInsertSpaceCommand(sublime_plugin.TextCommand):
def run(self, edit):
selections = [cursor for cursor in self.view.sel()] # can't use `view.sel()[:]` because it gives an error `TypeError: an integer is required`
to_uppercase = []
for sel in selections:
if sel.end() > 0:
#prev_item = self.view.extract_scope(sel.end() - 1)
scope = self.view.scope_name(sel.end() - 1)
begin = sel.end() - 1
while self.view.scope_name(begin) == scope and begin > 0:
begin -= 1
prev_item = sublime.Region(begin, sel.end())
to_uppercase.append(prev_item)
self.view.sel().clear()
self.view.sel().add_all(to_uppercase)
self.view.run_command('upper_case')
self.view.sel().clear()
self.view.sel().add_all(selections)
self.view.run_command('insert', { 'characters': ' ' })
class ScopeBeforeCursorEventListener(sublime_plugin.EventListener):
def on_query_context(self, view, key, operator, operand, match_all):
if key != 'scope_before_cursor':
return None
if operator not in (sublime.OP_EQUAL, sublime.OP_NOT_EQUAL):
return None
match = False
for sel in view.sel():
match = view.match_selector(max(0, sel.end() - 1), operand)
if operator == sublime.OP_NOT_EQUAL:
match = not match
if match != match_all:
break
return match
@addons_zz I just fixed a “small” bug, that would make ST hang if you are typing in a blank file
i.e. open a new tab, set syntax to SQL and type select
and press Space and it would make ST unresponsive - oops!
In other news, it is a shame that plugins can do that…
I released a new version 1.1:
"""
Automatically set all SQL keywords to upper case after the space keystroke.
Originally created by `kingkeith` on `https://forum.sublimetext.com/t/automatically-set-sql-keywords-to-upper-case/23760`.
Change log:
1.1 - Added multiple scopes support for the SQL's `support` scope on the `sublime.keymap` file.
1.0 - Initial release by `kingkeith`.
"""
import sublime
import sublime_plugin
class UpperCasePreviousItemAndInsertSpaceCommand( sublime_plugin.TextCommand ):
"""
Called each time the space is pressed to process the preceding keyword.
Set the space key bind to:
{ "keys": [" "], "command": "upper_case_previous_item_and_insert_space",
"context":
[
{ "key": "selector", "operator": "equal", "operand": "source.sql", "match_all": true },
{ "key": "selection_empty", "operator": "equal", "operand": true, "match_all": true },
{ "key": "scope_before_cursor", "operator": "equal", "operand": ["keyword", "support"], "match_all": true },
]
},
This class extends the `sublime_plugin.TextCommand` class.
"""
def run( self, edit ):
# can't use `view.sel()[:]` because it gives an error `TypeError: an integer is required`
selections = [ cursor for cursor in self.view.sel() ]
to_uppercase = []
for sel in selections:
if sel.end() > 0:
if self.view.substr( sel.end() - 1 ) == '(':
offset = 2
else:
offset = 1
#prev_item = self.view.extract_scope( sel.end() - offset )
scope = self.view.scope_name( sel.end() - offset )
begin = sel.end() - offset
while self.view.scope_name( begin ) == scope and begin > 0:
begin -= 1
prev_item = sublime.Region( begin, sel.end() )
to_uppercase.append( prev_item )
self.view.sel().clear()
self.view.sel().add_all( to_uppercase )
self.view.run_command( 'upper_case' )
self.view.sel().clear()
self.view.sel().add_all( selections )
self.view.run_command( 'insert', { 'characters': ' ' } )
class ScopeBeforeCursorEventListener( sublime_plugin.EventListener ):
"""
Event listener used on the key binding `scope_before_cursor` available on the class's
`UpperCasePreviousItemAndInsertSpaceCommand` documentation.
"""
def on_query_context( self, view, key, operator, operand, match_all ):
if key != 'scope_before_cursor':
return None
if operator not in ( sublime.OP_EQUAL, sublime.OP_NOT_EQUAL ):
return None
match = False
for sel in view.sel():
#print( max(0, sel.end() - 1) )
if view.substr(sel.end() - 1) == '(':
offset = 2
else:
offset = 1
for op in operand:
match = view.match_selector( max(0, sel.end() - offset ), op )
#print( 'On 1Âş match: ', match )
if match:
break
if operator == sublime.OP_NOT_EQUAL:
match = not match
if match != match_all:
break
#print( 'match: ', match )
return match
the normal way of specifying multiple scopes is just using a scope selector, i.e. in the keybindings:
{ "key": "scope_before_cursor", "operator": "equal", "operand": "keyword | support", "match_all": true },
that way, the plugin code doesn’t even have to change, and it doesn’t change the semantics of the operand
such that an array is needed/expected by the plugin code.
more info on scope selectors here: