From b1b6180255f8bb599892cd31038d7b3381b3ebfa Mon Sep 17 00:00:00 2001 From: bfelder Date: Tue, 25 Jul 2017 23:03:02 +0200 Subject: [PATCH 01/14] Additional improvements - Fixed unhandled exception when None was passed to is_theme_file function - Color schemes now get automatically updated upon saving of a tmTheme file. Great! --- ColorSchemeEditor-ST2.py | 475 ++++++++++++++++++++------------------- 1 file changed, 246 insertions(+), 229 deletions(-) diff --git a/ColorSchemeEditor-ST2.py b/ColorSchemeEditor-ST2.py index 172eeeb..828c946 100644 --- a/ColorSchemeEditor-ST2.py +++ b/ColorSchemeEditor-ST2.py @@ -1,229 +1,246 @@ -import sublime, sublime_plugin, os.path - -# globals suck, but don't know how to pass data between the classes -_schemeEditor = None -_skipOne = 0 -_wasSingleLayout = None -_lastScope = None -_lastScopeIndex = 0 - -def find_matches ( scope, founds ): - global _schemeEditor - - ret = [] - maxscore = 0 - # find the scope in the xml that matches the most - - for found in founds: - foundstr = _schemeEditor.substr( found ) - pos = foundstr.find( '' ) + 8 - foundstr = foundstr[ pos : -9 ] - foundstrs = foundstr.split( ',' ) - fstrlen = 0 - for fstr in foundstrs: - fstrlen = len( fstr ) - fstr = fstr.lstrip( ' ' ) - padleft = fstrlen - len( fstr ) - fstr = fstr.rstrip( ' ' ) - score = sublime.score_selector( scope, fstr ) - # print( fstr, score ) - if score > 0: - a = found.a + pos + padleft - ret.append( [ score, sublime.Region( a, a + len( fstr ) ) ] ) - pos += fstrlen + 1 - - if len( ret ) == 0: - return None - else: - return ret - -def display_scope ( region ): - global _schemeEditor - # doest change the selection if previous selection was on the same line - sel = _schemeEditor.sel() - sel.clear() - sel.add( region ) - _schemeEditor.show_at_center( region ) - - -def update_view_status ( view ): - - global _lastScope, _lastScopeIndex - - found = None - _lastScope = [] - _lastScopeIndex = 0 - - # find the scope under the cursor - scope_name = view.scope_name( view.sel()[0].a ) - pretty_scope = scope_name.strip( ' ' ).replace( ' ', ' > ' ) - scopes = reversed( pretty_scope.split( ' > ' ) ) - - # convert to regex and look for the scope in the scheme editor - for scope in scopes: - if len( scope ) == 0: - continue - dots = scope.count( '.' ) - regex = 'scope\\s*([a-z\\.\\-]* ?, ?)*([a-z\\.\\- ]*' - regex += scope.replace( '.', '(\\.' ) - while dots > 0: - regex += ')?' - dots -= 1 - regex += ')( ?, ?[a-z\\.\\-]*)*' - - # print( regex ) - found = _schemeEditor.find_all( regex, 0 ) - found = find_matches( scope_name, found ) - # print( found ) - if found != None: - _lastScope += found - - # print( _lastScope ) - scopes = len( _lastScope ) - sublime.status_message( 'matches ' + str( scopes ) + ': ' + pretty_scope ) - if scopes == 0: - _lastScope = None - display_scope( sublime.Region( 0, 0 ) ) - else: - _lastScope.sort( key = lambda f: f[1].a ) - _lastScope.sort( key = lambda f: f[0], reverse = True ) - display_scope( _lastScope[0][1] ) - - -def kill_scheme_editor (): - global _schemeEditor, _skipOne, _wasSingleLayout, _lastScope, _lastScopeIndex - if int( sublime.version() ) > 3000 and _wasSingleLayout != None: - _wasSingleLayout.set_layout( { - 'cols': [0.0, 1.0], - 'rows': [0.0, 1.0], - 'cells': [[0, 0, 1, 1]] - } ) - _skipOne = 0 - _wasSingleLayout = None - _schemeEditor = None - _lastScope = None - _lastScopeIndex = 0 - - -# listeners to update our scheme editor -class NavigationListener ( sublime_plugin.EventListener ): - - def on_close ( self, view ): - global _schemeEditor - if _schemeEditor != None: - if _schemeEditor.id() == view.id(): - kill_scheme_editor() - - def on_selection_modified ( self, view ): - global _schemeEditor, _skipOne - if _schemeEditor != None: - if _schemeEditor.id() != view.id() and not view.settings().get( 'is_widget' ): - # for some reason this callback is called twice - for mouse down and mouse up - if _skipOne == 1: - _skipOne = 0 - else: - _skipOne = 1 - update_view_status( view ) - - -class EditColorSchemeNextScopeCommand ( sublime_plugin.TextCommand ): - def run ( self, edit ): - global _schemeEditor, _lastScope, _lastScopeIndex - - if _schemeEditor != None and _lastScope != None: - scopes = len( _lastScope ) - if scopes > 1: - _lastScopeIndex += 1 - if _lastScopeIndex == scopes: - _lastScopeIndex = 0 - display_scope( _lastScope[_lastScopeIndex][1] ) - sublime.status_message( 'Scope ' + str( _lastScopeIndex + 1 ) + ' of ' + str( scopes ) ) - - - -class EditColorSchemePrevScopeCommand ( sublime_plugin.TextCommand ): - def run ( self, edit ): - global _schemeEditor, _lastScope, _lastScopeIndex - - if _schemeEditor != None and _lastScope != None: - scopes = len( _lastScope ) - if scopes > 1: - if _lastScopeIndex == 0: - _lastScopeIndex = scopes - 1 - else: - _lastScopeIndex -= 1 - display_scope( _lastScope[_lastScopeIndex][1] ) - sublime.status_message( 'Scope ' + str( _lastScopeIndex + 1 ) + ' of ' + str( scopes ) ) - - -class EditCurrentColorSchemeCommand ( sublime_plugin.TextCommand ): - - def run ( self, edit ): - global _schemeEditor, _wasSingleLayout - - view = self.view - viewid = view.id() - window = view.window() - if _schemeEditor == None: - - # see if not trying to edit on the scheme file - path = os.path.abspath( sublime.packages_path() + '/../' + view.settings().get( 'color_scheme' ) ) - if path == view.file_name(): - sublime.status_message( 'Select different file from the scheme you want to edit' ) - _schemeEditor = None - return - - # see if we openeded a new view - views = len( window.views() ) - _schemeEditor = window.open_file( path ) - if _schemeEditor == None: - sublime.status_message( 'Could not open the scheme file' ) - return - if views == len( window.views() ): - views = 0 - else: - views = 1 - - # if we have only one splitter, open new one - groups = window.num_groups() - group = -1 - index = 0 - if groups == 1: - _wasSingleLayout = window - group = 1 - window.set_layout( { - 'cols': [0.0, 0.5, 1.0], - 'rows': [0.0, 1.0], - 'cells': [[0, 0, 1, 1], [1, 0, 2, 1]] - } ) - elif views == 1: - activegrp = window.active_group() + 1 - if activegrp == groups: - group = activegrp - 2 - index = len( window.views_in_group( group ) ) - else: - group = activegrp - - if groups == 1 or views == 1: - # move the editor to another splitter - window.set_view_index( _schemeEditor, group, index ) - else: - #if the editor is in different splitter already focus it - window.focus_view( _schemeEditor ) - - window.focus_view( view ) - update_view_status( view ) - - else: - # if it was us who created the other splitter close it - if _wasSingleLayout != None: - _wasSingleLayout.set_layout( { - 'cols': [0.0, 1.0], - 'rows': [0.0, 1.0], - 'cells': [[0, 0, 1, 1]] - } ) - kill_scheme_editor() - - - - +import sublime +import sublime_plugin +import os.path + + +def is_theme_file(view): + if not view: + return False + active_view = view.window().active_view() + file_name = active_view.file_name() + if not file_name: + return False + file_ext = file_name.lower().split('.')[-1] + return "tmtheme" in file_ext + + +def find_matches(scope, founds): + """ find best matching scope in theme file """ + ret = [] + + for found in founds: + foundstr = Vars.schemeEditor.substr(found) + pos = foundstr.find('') + 8 + foundstr = foundstr[pos:-9] + foundstrs = foundstr.split(',') + fstrlen = 0 + for fstr in foundstrs: + fstrlen = len(fstr) + fstr = fstr.lstrip(' ') + padleft = fstrlen - len(fstr) + fstr = fstr.rstrip(' ') + score = sublime.score_selector(scope, fstr) + if score > 0: + a = found.a + pos + padleft + ret.append([score, sublime.Region(a, a + len(fstr))]) + pos += fstrlen + 1 + + if len(ret) != 0: + return ret + + +def display_scope(region): + """ do not change the selection if previous one was on the same line """ + sel = Vars.schemeEditor.sel() + sel.clear() + sel.add(region) + Vars.schemeEditor.show_at_center(region) + + +def update_view_status(view): + found = None + Vars.lastScope = [] + Vars.lastScopeIndex = 0 + + # find the scope under the cursor + scope_name = view.scope_name(view.sel()[0].a) + pretty_scope = scope_name.strip(' ').replace(' ', ' > ') + scopes = reversed(pretty_scope.split(' > ')) + + # convert to regex and look for the scope in the scheme editor + for scope in scopes: + if len(scope) == 0: + continue + dots = scope.count('.') + regex = 'scope\\s*([a-z\\.\\-]* ?, ?)*([a-z\\.\\- ]*' + regex += scope.replace('.', '(\\.') + while dots > 0: + regex += ')?' + dots -= 1 + regex += ')( ?, ?[a-z\\.\\-]*)*' + found = Vars.schemeEditor.find_all(regex, 0) + found = find_matches(scope_name, found) + if found: + Vars.lastScope += found + + scopes = len(Vars.lastScope) + sublime.status_message('matches ' + str(scopes) + ': ' + pretty_scope) + if scopes == 0: + Vars.lastScope = None + display_scope(sublime.Region(0, 0)) + else: + Vars.lastScope.sort(key=lambda f: f[1].a) + Vars.lastScope.sort(key=lambda f: f[0], reverse=True) + display_scope(Vars.lastScope[0][1]) + + +def kill_scheme_editor(): + if int(sublime.version()) > 3000 and Vars.wasSingleLayout: + Vars.wasSingleLayout.set_layout({ + 'cols': [0.0, 1.0], + 'rows': [0.0, 1.0], + 'cells': [[0, 0, 1, 1]] + }) + Vars.skipOne = 0 + Vars.wasSingleLayout = None + Vars.schemeEditor = None + Vars.lastScope = None + Vars.lastScopeIndex = 0 + + +def adjacent_scope(func): + if Vars.schemeEditor and Vars.lastScope: + scopes = len(Vars.lastScope) + if scopes > 1: + func(scopes) + last_scope = Vars.lastScope[Vars.lastScopeIndex][1] + display_scope(last_scope) + msg = "Scope {} of {}".format(Vars.lastScopeIndex + 1, scopes) + sublime.status_message(msg) + + +class Vars(object): + """ variables used throughout this plugin """ + schemeEditor = None + skipOne = 0 + wasSingleLayout = None + lastScope = None + lastScopeIndex = 0 + + +class NavigationListener(sublime_plugin.EventListener): + """ listeners to update our scheme editor """ + + def on_close(self, view): + if Vars.schemeEditor: + if Vars.schemeEditor.id() == view.id(): + kill_scheme_editor() + + def on_selection_modified(self, view): + if not view: + return + elif is_theme_file(view): + # prevent jumping around inside the theme file itself + return + + if Vars.schemeEditor: + is_widget = view.settings().get('is_widget') + if Vars.schemeEditor.id() != view.id() and not is_widget: + # for some reason this callback is called twice - for mouse + # down and mouse up + if Vars.skipOne == 1: + Vars.skipOne = 0 + else: + Vars.skipOne = 1 + update_view_status(view) + + +class SaveListener(sublime_plugin.EventListener): + """Reloads color scheme after saving theme file.""" + def on_post_save_async(self, view): + if is_theme_file(view): + [v.settings().erase("color_scheme") + for views in [w.views() for w in sublime.windows()] + for v in views] + + +class EditColorSchemeNextScopeCommand(sublime_plugin.TextCommand): + def next_scope(self, scopes): + Vars.lastScopeIndex += 1 + if Vars.lastScopeIndex == scopes: + Vars.lastScopeIndex = 0 + + def run(self, edit): + adjacent_scope(self.next_scope) + + +class EditColorSchemePrevScopeCommand(sublime_plugin.TextCommand): + def prev_scope(self, scopes): + if Vars.lastScopeIndex == 0: + Vars.lastScopeIndex = scopes - 1 + else: + Vars.lastScopeIndex -= 1 + + def run(self, edit): + adjacent_scope(self.prev_scope) + + +class EditCurrentColorSchemeCommand(sublime_plugin.TextCommand): + def run(self, edit): + view = self.view + window = view.window() + if not Vars.schemeEditor: + color_scheme = view.settings().get('color_scheme') + path_str = sublime.packages_path() + '/../' + color_scheme + path = os.path.abspath(path_str) + + if not view: + sublime.status_message( + 'Select a file for the scheme you want to edit') + return + elif is_theme_file(view): + sublime.status_message( + 'Select different file for the scheme you want to edit') + Vars.schemeEditor = None + return + + # see if we opened a new view + views = len(window.views()) + Vars.schemeEditor = window.open_file(path) + if not Vars.schemeEditor: + sublime.status_message('Could not open the scheme file') + return + if views == len(window.views()): + views = 0 + else: + views = 1 + + # if we got only one group, open new one + groups = window.num_groups() + group = -1 + index = 0 + if groups == 1: + Vars.wasSingleLayout = window + group = 1 + window.set_layout({ + 'cols': [0.0, 0.5, 1.0], + 'rows': [0.0, 1.0], + 'cells': [[0, 0, 1, 1], [1, 0, 2, 1]] + }) + elif views == 1: + activegrp = window.active_group() + 1 + if activegrp == groups: + group = activegrp - 2 + index = len(window.views_in_group(group)) + else: + group = activegrp + + if (groups or views) == 1: + # move the editor to another group + window.set_view_index(Vars.schemeEditor, group, index) + else: + # if the editor is in different group already focus it + window.focus_view(Vars.schemeEditor) + + window.focus_view(view) + update_view_status(view) + + else: + # if we created the other group then close it + if Vars.wasSingleLayout: + Vars.wasSingleLayout.set_layout({ + 'cols': [0.0, 1.0], + 'rows': [0.0, 1.0], + 'cells': [[0, 0, 1, 1]] + }) + kill_scheme_editor() From 65adaf87adc018034f33f36bd785e333b439b228 Mon Sep 17 00:00:00 2001 From: bfelder Date: Wed, 26 Jul 2017 20:58:02 +0200 Subject: [PATCH 02/14] Handling for other NoneType exception in is_theme_file --- ColorSchemeEditor-ST2.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ColorSchemeEditor-ST2.py b/ColorSchemeEditor-ST2.py index 828c946..87f3069 100644 --- a/ColorSchemeEditor-ST2.py +++ b/ColorSchemeEditor-ST2.py @@ -6,6 +6,8 @@ def is_theme_file(view): if not view: return False + elif not view.window(): + return False active_view = view.window().active_view() file_name = active_view.file_name() if not file_name: From 57092e61bbc855ac06fad428443f87a8ec4505c1 Mon Sep 17 00:00:00 2001 From: Drew Pirrone-Brusse Date: Sat, 29 Jul 2017 15:11:55 -0400 Subject: [PATCH 03/14] Only pass current scope to `find_matches` Was passing all scopes, making `print` debugs somewhat confusing --- ColorSchemeEditor-ST2.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ColorSchemeEditor-ST2.py b/ColorSchemeEditor-ST2.py index 87f3069..3568c37 100644 --- a/ColorSchemeEditor-ST2.py +++ b/ColorSchemeEditor-ST2.py @@ -71,7 +71,7 @@ def update_view_status(view): dots -= 1 regex += ')( ?, ?[a-z\\.\\-]*)*' found = Vars.schemeEditor.find_all(regex, 0) - found = find_matches(scope_name, found) + found = find_matches(scope, found) if found: Vars.lastScope += found From b7713621338d486efa264c3400afa7d7eab26316 Mon Sep 17 00:00:00 2001 From: Drew Pirrone-Brusse Date: Sat, 29 Jul 2017 15:14:19 -0400 Subject: [PATCH 04/14] Rename Vars.skipOne -> Vars.skipNext, change it to a bool --- ColorSchemeEditor-ST2.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/ColorSchemeEditor-ST2.py b/ColorSchemeEditor-ST2.py index 3568c37..efa775f 100644 --- a/ColorSchemeEditor-ST2.py +++ b/ColorSchemeEditor-ST2.py @@ -93,7 +93,7 @@ def kill_scheme_editor(): 'rows': [0.0, 1.0], 'cells': [[0, 0, 1, 1]] }) - Vars.skipOne = 0 + Vars.skipNext = False Vars.wasSingleLayout = None Vars.schemeEditor = None Vars.lastScope = None @@ -114,7 +114,7 @@ def adjacent_scope(func): class Vars(object): """ variables used throughout this plugin """ schemeEditor = None - skipOne = 0 + skipNext = False wasSingleLayout = None lastScope = None lastScopeIndex = 0 @@ -140,10 +140,10 @@ def on_selection_modified(self, view): if Vars.schemeEditor.id() != view.id() and not is_widget: # for some reason this callback is called twice - for mouse # down and mouse up - if Vars.skipOne == 1: - Vars.skipOne = 0 + if Vars.skipNext: + Vars.skipNext = False else: - Vars.skipOne = 1 + Vars.skipNext = True update_view_status(view) From ce45de1ed176813b8e987f5e40ef672d5ee11129 Mon Sep 17 00:00:00 2001 From: Drew Pirrone-Brusse Date: Sat, 29 Jul 2017 15:17:08 -0400 Subject: [PATCH 05/14] Better matching for '*.c++' scopes * Use Raw String Literals for regex construction * Include '+' as a valid character in scope names This change is to allow scopes ending in '.c++' (and scope sets that contain those scopes) to be correctly parsed. e.g. scope meta.preprocessor.c, meta.preprocessor.c++, keyword.control.c, keyword.control.c++ --- ColorSchemeEditor-ST2.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/ColorSchemeEditor-ST2.py b/ColorSchemeEditor-ST2.py index efa775f..887f6ef 100644 --- a/ColorSchemeEditor-ST2.py +++ b/ColorSchemeEditor-ST2.py @@ -64,12 +64,12 @@ def update_view_status(view): if len(scope) == 0: continue dots = scope.count('.') - regex = 'scope\\s*([a-z\\.\\-]* ?, ?)*([a-z\\.\\- ]*' - regex += scope.replace('.', '(\\.') + regex = r'scope\s*([a-z\.\-\+]* ?, ?)*([a-z\.\-\+ ]*' + regex += scope.replace('.', r'(\.') while dots > 0: regex += ')?' dots -= 1 - regex += ')( ?, ?[a-z\\.\\-]*)*' + regex += r')( ?, ?[a-z\.\-\+]*)*' found = Vars.schemeEditor.find_all(regex, 0) found = find_matches(scope, found) if found: From d4d35508ab48811a6cef8004ce83d65f99b06845 Mon Sep 17 00:00:00 2001 From: Drew Pirrone-Brusse Date: Sat, 29 Jul 2017 15:48:37 -0400 Subject: [PATCH 06/14] Better skipping logic Only trigger a skip on mouse-down, rather than skipping every other `on_selection_modified`. This improves keyboard-driven detection accuracy. --- ColorSchemeEditor-ST2.py | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/ColorSchemeEditor-ST2.py b/ColorSchemeEditor-ST2.py index 887f6ef..0412f9f 100644 --- a/ColorSchemeEditor-ST2.py +++ b/ColorSchemeEditor-ST2.py @@ -128,6 +128,14 @@ def on_close(self, view): if Vars.schemeEditor.id() == view.id(): kill_scheme_editor() + def on_text_command(self, view, command_name, args): + # `on_selection_modified` will fire twice every time a left-click + # occurs; once on mouse-down, once on mouse-up. `on_text_command` will + # only fire on mouse-down (as a `drag_select` event), so we can use that + # signal to flag a skip. + if Vars.schemeEditor != None and command_name == "drag_select": + Vars.skipNext = True + def on_selection_modified(self, view): if not view: return @@ -138,12 +146,9 @@ def on_selection_modified(self, view): if Vars.schemeEditor: is_widget = view.settings().get('is_widget') if Vars.schemeEditor.id() != view.id() and not is_widget: - # for some reason this callback is called twice - for mouse - # down and mouse up if Vars.skipNext: Vars.skipNext = False else: - Vars.skipNext = True update_view_status(view) From 1414ba6e7a6281d41a05a94119eba404b7172594 Mon Sep 17 00:00:00 2001 From: Drew Pirrone-Brusse Date: Sat, 29 Jul 2017 16:55:04 -0400 Subject: [PATCH 07/14] Always redraw selected scope Previously, the selected scope would only change when 1) the new scope was on a different line than the current scope or 2) the schemeEditor View was given focus manually. This change adds a momentary `focus_view(Vars.schemeEditor)` to force the redraw. --- ColorSchemeEditor-ST2.py | 29 +++++++++++++++++++++++++---- 1 file changed, 25 insertions(+), 4 deletions(-) diff --git a/ColorSchemeEditor-ST2.py b/ColorSchemeEditor-ST2.py index 0412f9f..72bb23d 100644 --- a/ColorSchemeEditor-ST2.py +++ b/ColorSchemeEditor-ST2.py @@ -42,12 +42,25 @@ def find_matches(scope, founds): def display_scope(region): - """ do not change the selection if previous one was on the same line """ - sel = Vars.schemeEditor.sel() - sel.clear() - sel.add(region) + """ display the currently viewed scope in the Scheme Editor """ + selection = Vars.schemeEditor.sel() + selection.clear() + selection.add(region) Vars.schemeEditor.show_at_center(region) + # Without window focus, the above `show_at_center` will not visibly modify + # the current selection unless the new region is on a different line than + # the old. The work-aroundis to momentarily give focus to the schemeEditor + # View, which will force the re-draw. + # We have to capture the current View and Window to make sure the correct + # View receives focus after the re-draw. + current_window = sublime.active_window() + current_view = current_window.active_view() + scheme_window = Vars.schemeEditor.window() + + scheme_window.focus_view(Vars.schemeEditor) + current_window.focus_view(current_view) + def update_view_status(view): found = None @@ -168,6 +181,10 @@ def next_scope(self, scopes): Vars.lastScopeIndex = 0 def run(self, edit): + # Giving the schemeEditor focus (as part of the `adjacent_scope` -> + # `display_scope` code path) will trigger an `on_selection_modified` + # event that interrupts cycling through scopes. Skip that event. + Vars.skipNext = True adjacent_scope(self.next_scope) @@ -179,6 +196,10 @@ def prev_scope(self, scopes): Vars.lastScopeIndex -= 1 def run(self, edit): + # Giving the schemeEditor focus (as part of the `adjacent_scope` -> + # `display_scope` code path) will trigger an `on_selection_modified` + # event that interrupts cycling through scopes. Skip that event. + Vars.skipNext = True adjacent_scope(self.prev_scope) From 31a00d81735c2ea5413c4153123f90a1cd388bb3 Mon Sep 17 00:00:00 2001 From: Drew Pirrone-Brusse Date: Sat, 29 Jul 2017 17:05:21 -0400 Subject: [PATCH 08/14] Add a check for, and skip to, `show_scope_name` popup events --- ColorSchemeEditor-ST2.py | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/ColorSchemeEditor-ST2.py b/ColorSchemeEditor-ST2.py index 72bb23d..095857c 100644 --- a/ColorSchemeEditor-ST2.py +++ b/ColorSchemeEditor-ST2.py @@ -142,12 +142,19 @@ def on_close(self, view): kill_scheme_editor() def on_text_command(self, view, command_name, args): - # `on_selection_modified` will fire twice every time a left-click - # occurs; once on mouse-down, once on mouse-up. `on_text_command` will - # only fire on mouse-down (as a `drag_select` event), so we can use that - # signal to flag a skip. - if Vars.schemeEditor != None and command_name == "drag_select": - Vars.skipNext = True + if Vars.schemeEditor != None: + if command_name == "drag_select": + # Left clicks trigger two `on_selection_modified` events; one on + # mouse-down, one on mouse-up. `on_text_command` will only fire + # on mouse-down, as a `drag_select` event, so we can use that + # signal to flag a skip. + Vars.skipNext = True + elif command_name == "show_scope_name": + # `show_scope_name` triggers both an `on_text_command` and an + # `on_selection_modified` event. The later will momentarily give + # the schemeEditor focus, which will cause the Scope Names popup + # to lose focus and close. As such, skip that update. + Vars.skipNext = True def on_selection_modified(self, view): if not view: From 18dd4ba96e6a7cbb376535ca61a22c309b4b16e5 Mon Sep 17 00:00:00 2001 From: Drew Pirrone-Brusse Date: Sun, 5 Jun 2016 14:45:58 -0400 Subject: [PATCH 09/14] Add Preferences -> Package Settings -> Color Scheme Editor menu items --- Main.sublime-menu | 56 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 56 insertions(+) create mode 100644 Main.sublime-menu diff --git a/Main.sublime-menu b/Main.sublime-menu new file mode 100644 index 0000000..ef42ec6 --- /dev/null +++ b/Main.sublime-menu @@ -0,0 +1,56 @@ +[ + + { + "caption": "Preferences", + "mnemonic": "n", + "id": "preferences", + "children": + [ + { + "caption": "Package Settings", + "mnemonic": "P", + "id": "package-settings", + "children": + [ + { + "caption": "Color Scheme Editor", + "children": + [ + { + "command": "open_file", + "args": { + "file": "${packages}/ColorSchemeEditor/Default.sublime-keymap", + }, + "caption": "Key Bindings – Default" + }, + { + "command": "open_file", + "args": { + "file": "${packages}/User/Default (Windows).sublime-keymap", + "platform": "Windows" + }, + "caption": "Key Bindings – User" + }, + { + "command": "open_file", + "args": { + "file": "${packages}/User/Default (OSX).sublime-keymap", + "platform": "OSX" + }, + "caption": "Key Bindings – User" + }, + { + "command": "open_file", + "args": { + "file": "${packages}/User/Default (Linux).sublime-keymap", + "platform": "Linux" + }, + "caption": "Key Bindings – User" + } + ] + } + ] + } + ] + } +] From b9df4c91d9981e4853ad54fd8545a338cea49456 Mon Sep 17 00:00:00 2001 From: Drew Pirrone-Brusse Date: Sat, 29 Jul 2017 17:34:16 -0400 Subject: [PATCH 10/14] Simplify and improve EditCurrentColorSchemeCommand teardown logic --- ColorSchemeEditor-ST2.py | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/ColorSchemeEditor-ST2.py b/ColorSchemeEditor-ST2.py index 095857c..6032c2a 100644 --- a/ColorSchemeEditor-ST2.py +++ b/ColorSchemeEditor-ST2.py @@ -214,6 +214,8 @@ class EditCurrentColorSchemeCommand(sublime_plugin.TextCommand): def run(self, edit): view = self.view window = view.window() + + # If the schemeEditor is currently closed, initialize this plugin. if not Vars.schemeEditor: color_scheme = view.settings().get('color_scheme') path_str = sublime.packages_path() + '/../' + color_scheme @@ -270,12 +272,7 @@ def run(self, edit): window.focus_view(view) update_view_status(view) + # If there is a schemeEditor open, simply close it and allow the + # `NavigationListener`s `on_close` logic to tear down the plugin. else: - # if we created the other group then close it - if Vars.wasSingleLayout: - Vars.wasSingleLayout.set_layout({ - 'cols': [0.0, 1.0], - 'rows': [0.0, 1.0], - 'cells': [[0, 0, 1, 1]] - }) - kill_scheme_editor() + Vars.schemeEditor.close() From 6dd7e6a8e56dbe37790745b644d5781b1d389eae Mon Sep 17 00:00:00 2001 From: bfelder Date: Wed, 2 Aug 2017 23:01:24 +0200 Subject: [PATCH 11/14] Moved vars container class to top A better place to put your variables. --- ColorSchemeEditor-ST2.py | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/ColorSchemeEditor-ST2.py b/ColorSchemeEditor-ST2.py index 6032c2a..f073abd 100644 --- a/ColorSchemeEditor-ST2.py +++ b/ColorSchemeEditor-ST2.py @@ -3,6 +3,15 @@ import os.path +class Vars(object): + """ variables used throughout this plugin """ + schemeEditor = None + skipNext = False + wasSingleLayout = None + lastScope = None + lastScopeIndex = 0 + + def is_theme_file(view): if not view: return False @@ -124,15 +133,6 @@ def adjacent_scope(func): sublime.status_message(msg) -class Vars(object): - """ variables used throughout this plugin """ - schemeEditor = None - skipNext = False - wasSingleLayout = None - lastScope = None - lastScopeIndex = 0 - - class NavigationListener(sublime_plugin.EventListener): """ listeners to update our scheme editor """ From 0cd6623b8460c638ea400332120c3e5dd130db54 Mon Sep 17 00:00:00 2001 From: bfelder Date: Sun, 27 Aug 2017 23:57:23 +0200 Subject: [PATCH 12/14] Removed a common anti-patterrn --- ColorSchemeEditor-ST2.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ColorSchemeEditor-ST2.py b/ColorSchemeEditor-ST2.py index f073abd..78c009c 100644 --- a/ColorSchemeEditor-ST2.py +++ b/ColorSchemeEditor-ST2.py @@ -142,7 +142,7 @@ def on_close(self, view): kill_scheme_editor() def on_text_command(self, view, command_name, args): - if Vars.schemeEditor != None: + if Vars.schemeEditor: if command_name == "drag_select": # Left clicks trigger two `on_selection_modified` events; one on # mouse-down, one on mouse-up. `on_text_command` will only fire From 67f123e0b5edee3c70cc66c09a249cd6b0b6f4b0 Mon Sep 17 00:00:00 2001 From: bfelder Date: Sun, 10 Sep 2017 20:58:13 +0200 Subject: [PATCH 13/14] Rename file removed '-ST2' tag as plugin works for ST3 as well --- ColorSchemeEditor-ST2.py => ColorSchemeEditor.py | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename ColorSchemeEditor-ST2.py => ColorSchemeEditor.py (100%) diff --git a/ColorSchemeEditor-ST2.py b/ColorSchemeEditor.py similarity index 100% rename from ColorSchemeEditor-ST2.py rename to ColorSchemeEditor.py From f8c40b504b631edf1ce81a126e404068a5837f3d Mon Sep 17 00:00:00 2001 From: bfelder Date: Mon, 11 Sep 2017 21:54:23 +0200 Subject: [PATCH 14/14] Minor cleanup and refactoring --- ColorSchemeEditor.py | 30 +++++++++++------------------- 1 file changed, 11 insertions(+), 19 deletions(-) diff --git a/ColorSchemeEditor.py b/ColorSchemeEditor.py index 78c009c..a3449cf 100644 --- a/ColorSchemeEditor.py +++ b/ColorSchemeEditor.py @@ -10,7 +10,7 @@ class Vars(object): wasSingleLayout = None lastScope = None lastScopeIndex = 0 - + def is_theme_file(view): if not view: @@ -56,17 +56,15 @@ def display_scope(region): selection.clear() selection.add(region) Vars.schemeEditor.show_at_center(region) - # Without window focus, the above `show_at_center` will not visibly modify # the current selection unless the new region is on a different line than - # the old. The work-aroundis to momentarily give focus to the schemeEditor - # View, which will force the re-draw. + # the old. The work-around is to momentarily give focus to the schemeEditor + # view, which will force the re-draw. # We have to capture the current View and Window to make sure the correct # View receives focus after the re-draw. current_window = sublime.active_window() current_view = current_window.active_view() scheme_window = Vars.schemeEditor.window() - scheme_window.focus_view(Vars.schemeEditor) current_window.focus_view(current_view) @@ -142,18 +140,9 @@ def on_close(self, view): kill_scheme_editor() def on_text_command(self, view, command_name, args): + """skipping unwanted updates""" if Vars.schemeEditor: - if command_name == "drag_select": - # Left clicks trigger two `on_selection_modified` events; one on - # mouse-down, one on mouse-up. `on_text_command` will only fire - # on mouse-down, as a `drag_select` event, so we can use that - # signal to flag a skip. - Vars.skipNext = True - elif command_name == "show_scope_name": - # `show_scope_name` triggers both an `on_text_command` and an - # `on_selection_modified` event. The later will momentarily give - # the schemeEditor focus, which will cause the Scope Names popup - # to lose focus and close. As such, skip that update. + if command_name == ("drag_select" or "show_scope_name"): Vars.skipNext = True def on_selection_modified(self, view): @@ -174,11 +163,14 @@ def on_selection_modified(self, view): class SaveListener(sublime_plugin.EventListener): """Reloads color scheme after saving theme file.""" + def on_post_save_async(self, view): if is_theme_file(view): - [v.settings().erase("color_scheme") - for views in [w.views() for w in sublime.windows()] - for v in views] + [ + v.settings().erase("color_scheme") + for views in [w.views() for w in sublime.windows()] + for v in views + ] class EditColorSchemeNextScopeCommand(sublime_plugin.TextCommand):