diff --git a/plugins/cmds.py b/plugins/cmds.py index de2fe0b..d16daa2 100644 --- a/plugins/cmds.py +++ b/plugins/cmds.py @@ -16,9 +16,15 @@ import sublime_plugin from sublime import status_message, error_message -from .ctags import (FILENAME, PATH_ORDER, SYMBOL, - build_ctags, parse_tag_lines, - TagElements, TagFile) +from .ctags import ( + FILENAME, + PATH_ORDER, + SYMBOL, + build_ctags, + parse_tag_lines, + TagElements, + TagFile, +) from .edit import Edit from .ranking.parse import Parser @@ -30,16 +36,16 @@ # OBJECT_PUNCTUATORS = { - 'class': '.', - 'struct': '::', - 'function': '/', + "class": ".", + "struct": "::", + "function": "/", } -ENTITY_SCOPE = 'entity.name.function, entity.name.type, meta.toc-list' +ENTITY_SCOPE = "entity.name.function, entity.name.type, meta.toc-list" -RUBY_SPECIAL_ENDINGS = r'\?|!' +RUBY_SPECIAL_ENDINGS = r"\?|!" -ON_LOAD = sublime_plugin.all_callbacks['on_load'] +ON_LOAD = sublime_plugin.all_callbacks["on_load"] # @@ -61,10 +67,11 @@ def done_in_main(*args, **kw): return done_in_main + # TODO: allow thread per tag file. That makes more sense. -def threaded(finish=None, msg='Thread already running'): +def threaded(finish=None, msg="Thread already running"): def decorator(func): func.running = 0 @@ -77,13 +84,15 @@ def run(): result = () elif not isinstance(result, tuple): - result = (result, ) + result = (result,) if finish: sublime.set_timeout( - functools.partial(finish, args[0], *result), 0) + functools.partial(finish, args[0], *result), 0 + ) finally: func.running = 0 + if not func.running: func.running = 1 t = threading.Thread(target=run) @@ -91,6 +100,7 @@ def run(): t.start() else: status_message(msg) + threaded.func = func return threaded @@ -136,7 +146,8 @@ def wrapped(): # if buffer is still loading, wait for it to complete then proceed if view.is_loading(): - class set_on_load(): + + class set_on_load: callbacks = ON_LOAD def __init__(self): @@ -187,12 +198,14 @@ def find_tags_relative_to(path, tag_file): return None + def read_opts(view): # the first one is useful to change opts only on a specific project # (by adding ctags.opts to a project settings file) - if not view: - return setting('opts') - return view.settings().get('ctags.opts') or setting('opts') + if not view: + return setting("opts") + return view.settings().get("ctags.opts") or setting("opts") + def get_alternate_tags_paths(view, tags_file): """ @@ -208,41 +221,36 @@ def get_alternate_tags_paths(view, tags_file): :returns: list of valid, existing paths to additional tag files to search """ - tags_paths = '%s_search_paths' % tags_file + tags_paths = "%s_search_paths" % tags_file search_paths = [tags_file] # read and add additional tag file paths from file if os.path.exists(tags_paths): search_paths.extend( - codecs.open(tags_paths, encoding='utf-8').read().split('\n')) + codecs.open(tags_paths, encoding="utf-8").read().split("\n") + ) # read and add additional tag file paths from 'extra_tag_paths' setting try: - for (selector, platform), path in setting('extra_tag_paths'): + for (selector, platform), path in setting("extra_tag_paths"): if view.match_selector(view.sel()[0].begin(), selector): if sublime.platform() == platform: - search_paths.append( - os.path.join( - path, setting('tag_file'))) + search_paths.append(os.path.join(path, setting("tag_file"))) except Exception as e: print(e) if os.path.exists(tags_paths): - for extrafile in setting('extra_tag_files'): + for extrafile in setting("extra_tag_files"): search_paths.append( - os.path.normpath( - os.path.join(os.path.dirname(tags_file), extrafile))) + os.path.normpath(os.path.join(os.path.dirname(tags_file), extrafile)) + ) # ok, didn't find the tags file under the viewed file. # let's look in the currently opened folder for folder in view.window().folders(): - search_paths.append( - os.path.normpath( - os.path.join(folder, setting('tag_file')))) - for extrafile in setting('extra_tag_files'): - search_paths.append( - os.path.normpath( - os.path.join(folder, extrafile))) + search_paths.append(os.path.normpath(os.path.join(folder, setting("tag_file")))) + for extrafile in setting("extra_tag_files"): + search_paths.append(os.path.normpath(os.path.join(folder, extrafile))) # use list instead of set for keep order ret = [] @@ -261,7 +269,7 @@ def get_common_ancestor_folder(path, folders): :returns: path to common ancestor for files and folders file """ - old_path = '' # must initialise to nothing due to lack of do...while + old_path = "" # must initialise to nothing due to lack of do...while path = os.path.dirname(path) while path != old_path: # prevent continuing past root directory @@ -275,14 +283,15 @@ def get_common_ancestor_folder(path, folders): return path # return the root directory + # Scrolling functions def find_with_scope(view, pattern, scope, start_pos=0, cond=True, flags=0): max_pos = view.size() while start_pos < max_pos: - estrs = pattern.split(r'\ufffd') - if(len(estrs) > 1): + estrs = pattern.split(r"\ufffd") + if len(estrs) > 1: pattern = estrs[0] f = view.find(pattern, start_pos, flags) @@ -295,8 +304,7 @@ def find_with_scope(view, pattern, scope, start_pos=0, cond=True, flags=0): def find_source(view, pattern, start_at, flags=sublime.LITERAL): - return find_with_scope(view, pattern, 'string', - start_at, False, flags) + return find_with_scope(view, pattern, "string", start_at, False, flags) def follow_tag_path(view, tag_path, pattern): @@ -306,21 +314,21 @@ def follow_tag_path(view, tag_path, pattern): while True: # .end() is BUG! regions.append(find_source(view, p, regions[-1].begin())) - if ((regions[-1] in (None, regions[-2]) or - view.match_selector(regions[-1].begin(), ENTITY_SCOPE))): + if regions[-1] in (None, regions[-2]) or view.match_selector( + regions[-1].begin(), ENTITY_SCOPE + ): regions = [r for r in regions if r is not None] break start_at = max(regions, key=lambda r: r.begin()).begin() - 1 # find the ex_command pattern - pattern_region = find_source( - view, r'^' + escape_regex(pattern), start_at, flags=0) + pattern_region = find_source(view, r"^" + escape_regex(pattern), start_at, flags=0) - if setting('debug'): # leave a visual trail for easy debugging + if setting("debug"): # leave a visual trail for easy debugging regions = regions + ([pattern_region] if pattern_region else []) - view.erase_regions('tag_path') - view.add_regions('tag_path', regions, 'comment', '', 1) + view.erase_regions("tag_path") + view.add_regions("tag_path", regions, "comment", "", 1) return pattern_region.begin() - 1 if pattern_region else None @@ -338,24 +346,27 @@ def and_then(view): do_find = False if do_find: - search_symbol = tag.get('def_symbol', tag.symbol) + search_symbol = tag.get("def_symbol", tag.symbol) symbol_region = view.find( - escape_regex(search_symbol) + r"(?:[^_]|$)", look_from, 0) + escape_regex(search_symbol) + r"(?:[^_]|$)", look_from, 0 + ) if do_find and symbol_region: # Using reversed symbol_region so cursor stays in front of the # symbol. - 1 to discard the additional regex part. select_region = sublime.Region( - symbol_region.end() - 1, symbol_region.begin()) + symbol_region.end() - 1, symbol_region.begin() + ) select(view, select_region) - if not setting('select_searched_symbol'): - view.run_command('exit_visual_mode') + if not setting("select_searched_symbol"): + view.run_command("exit_visual_mode") else: status_message('Can\'t find "%s"' % tag.symbol) if hook: hook(view) + # Formatting helper functions @@ -370,13 +381,12 @@ def format_tag_for_quickopen(tag, show_path=True): """ format_ = [] tag = TagElements(tag) - f = '' + f = "" - for field in getattr(tag, 'field_keys', []): + for field in getattr(tag, "field_keys", []): if field in PATH_ORDER: - punct = OBJECT_PUNCTUATORS.get(field, ' -> ') - f += string.Template( - ' %($field)s$punct%(symbol)s').substitute(locals()) + punct = OBJECT_PUNCTUATORS.get(field, " -> ") + f += string.Template(" %($field)s$punct%(symbol)s").substitute(locals()) format_ = [f % tag if f else tag.symbol, tag.ex_command] format_[1] = format_[1].strip() @@ -395,6 +405,7 @@ def prepare_for_quickpanel(formatter=format_tag_for_quickopen): :returns: tuple containing tag and formatted string representation of tag """ + def compile_lists(sorter): args, display = [], [] @@ -406,6 +417,7 @@ def compile_lists(sorter): return compile_lists + # File collection helper functions @@ -438,6 +450,7 @@ def get_current_file_suffix(path): return file_suffix + # # Sublime Commands # @@ -455,6 +468,7 @@ class JumpPrev(sublime_plugin.WindowCommand): This is functionality supported natively by ST3 but not by ST2. It is therefore included for legacy purposes. """ + buf = deque(maxlen=100) # virtually a "ring buffer" def is_enabled(self): @@ -462,11 +476,11 @@ def is_enabled(self): return len(self.buf) > 0 def is_visible(self): - return setting('show_context_menus') + return setting("show_context_menus") def run(self): if not self.buf: - return status_message('JumpPrev buffer empty') + return status_message("JumpPrev buffer empty") file_name, sel = self.buf.pop() self.jump(file_name, sel) @@ -484,6 +498,7 @@ def append(cls, view): sel = [s for s in view.sel()][0] cls.buf.append((name, sel)) + # CTags commands @@ -497,32 +512,37 @@ def show_build_panel(view): display = [] if view.file_name() is not None: - if not setting('recursive'): - display.append(['Open File', view.file_name()]) + if not setting("recursive"): + display.append(["Open File", view.file_name()]) else: - display.append([ - 'Open File\'s Directory', os.path.dirname(view.file_name())]) + display.append(["Open File's Directory", os.path.dirname(view.file_name())]) if len(view.window().folders()) > 0: # append option to build for all open folders display.append( - ['All Open Folders', '; '.join( - ['\'{0}\''.format(os.path.split(x)[1]) - for x in view.window().folders()])]) + [ + "All Open Folders", + "; ".join( + [ + "'{0}'".format(os.path.split(x)[1]) + for x in view.window().folders() + ] + ), + ] + ) # Append options to build for each open folder - display.extend( - [[os.path.split(x)[1], x] for x in view.window().folders()]) + display.extend([[os.path.split(x)[1], x] for x in view.window().folders()]) def on_select(i): if i != -1: - if display[i][0] == 'All Open Folders': + if display[i][0] == "All Open Folders": paths = view.window().folders() else: paths = display[i][1:] - command = setting('command') - recursive = setting('recursive') - tag_file = setting('tag_file') + command = setting("command") + recursive = setting("recursive") + tag_file = setting("tag_file") opts = read_opts(view) rebuild_tags = RebuildTags(False) @@ -549,7 +569,7 @@ def on_select(i): # Work around bug in ST3 where the quick panel keeps focus after # selecting an entry. # See https://github.com/SublimeText/Issues/issues/39 - view.window().run_command('hide_overlay') + view.window().run_command("hide_overlay") scroll_to_tag(view, args[i]) if jump_directly and len(args) == 1: @@ -564,20 +584,21 @@ def ctags_goto_command(jump_directly=False): Allow jump to a ctags entry, directly or otherwise """ + def wrapper(func): def command(self, edit, **args): view = self.view - tags_file = find_tags_relative_to( - view.file_name(), setting('tag_file')) + tags_file = find_tags_relative_to(view.file_name(), setting("tag_file")) if not tags_file: - status_message('Can\'t find any relevant tags file') + status_message("Can't find any relevant tags file") return result = func(self, self.view, args, tags_file) show_tag_panel(self.view, result, jump_directly) return command + return wrapper @@ -586,19 +607,21 @@ def check_if_building(self, **args): Check if ctags are currently being built. """ if RebuildTags.build_ctags.func.running: - status_message('Tags not available until built') - if setting('display_rebuilding_message'): - error_message('Please wait while tags are built') + status_message("Tags not available until built") + if setting("display_rebuilding_message"): + error_message("Please wait while tags are built") return False return True # Goto definition under cursor commands + class JumpToDefinition: """ Provider for NavigateToDefinition and SearchForDefinition commands. """ + @staticmethod def run(symbol, region, sym_line, mbrParts, view, tags_file): # print('JumpToDefinition') @@ -606,8 +629,7 @@ def run(symbol, region, sym_line, mbrParts, view, tags_file): tags = {} for tags_file in get_alternate_tags_paths(view, tags_file): with TagFile(tags_file, SYMBOL) as tagfile: - tags = tagfile.get_tags_dict( - symbol, filters=compile_filters(view)) + tags = tagfile.get_tags_dict(symbol, filters=compile_filters(view)) if tags: break @@ -637,6 +659,7 @@ class NavigateToDefinition(sublime_plugin.TextCommand): Command navigates to the definition for a symbol in the open file(s) or folder(s). """ + is_enabled = check_if_building def __init__(self, args): @@ -644,7 +667,7 @@ def __init__(self, args): self.endings = re.compile(RUBY_SPECIAL_ENDINGS) def is_visible(self): - return setting('show_context_menus') + return setting("show_context_menus") @ctags_goto_command(jump_directly=True) def run(self, view, args, tags_file): @@ -653,29 +676,22 @@ def run(self, view, args, tags_file): region = view.word(region) # handle special line endings for Ruby - language = view.settings().get('syntax') - endings = view.substr( - sublime.Region( - region.end(), - region.end() + 1)) + language = view.settings().get("syntax") + endings = view.substr(sublime.Region(region.end(), region.end() + 1)) - if 'Ruby' in language and self.endings.match(endings): + if "Ruby" in language and self.endings.match(endings): region = sublime.Region(region.begin(), region.end() + 1) symbol = view.substr(region) sym_line = view.substr(view.line(region)) (row, col) = view.rowcol(region.begin()) line_to_symbol = sym_line[:col] - #print ("line_to_symbol %s" % line_to_symbol) + # print ("line_to_symbol %s" % line_to_symbol) source = get_source(view) arrMbrParts = Parser.extract_member_exp(line_to_symbol, source) return JumpToDefinition.run( - symbol, - region, - sym_line, - arrMbrParts, - view, - tags_file) + symbol, region, sym_line, arrMbrParts, view, tags_file + ) class SearchForDefinition(sublime_plugin.WindowCommand): @@ -685,22 +701,23 @@ class SearchForDefinition(sublime_plugin.WindowCommand): Command searches for definition for a symbol in the open file(s) or folder(s). """ + is_enabled = check_if_building def is_visible(self): - return setting('show_context_menus') + return setting("show_context_menus") def run(self): self.window.show_input_panel( - '', '', self.on_done, self.on_change, self.on_cancel) + "", "", self.on_done, self.on_change, self.on_cancel + ) def on_done(self, symbol): view = self.window.active_view() - tags_file = find_tags_relative_to( - view.file_name(), setting('tag_file')) + tags_file = find_tags_relative_to(view.file_name(), setting("tag_file")) if not tags_file: - status_message('Can\'t find any relevant tags file') + status_message("Can't find any relevant tags file") return result = JumpToDefinition.run(symbol, None, "", [], view, tags_file) @@ -712,6 +729,7 @@ def on_change(self, text): def on_cancel(self): pass + # Show Symbol commands tags_cache = defaultdict(dict) @@ -723,19 +741,20 @@ class ShowSymbols(sublime_plugin.TextCommand): Command shows all symbols for the open file(s) or folder(s). """ + is_enabled = check_if_building def is_visible(self): - return setting('show_context_menus') + return setting("show_context_menus") @ctags_goto_command() def run(self, view, args, tags_file): if not tags_file: return - symbol_type = args.get('type') - multi = symbol_type == 'multi' - lang = symbol_type == 'lang' + symbol_type = args.get("type") + multi = symbol_type == "multi" + lang = symbol_type == "lang" if lang: # filter and cache by file suffix @@ -751,14 +770,14 @@ def run(self, view, args, tags_file): if not key: return key = get_rel_path_to_source(key, tags_file) - key = key.replace('\\', '/') + key = key.replace("\\", "/") files = [key] # Note: Help migrating existing tags files to new file name. # Needed, because this plugin now sorts .tags file in-place, # while former versions used to create a dedicated file. - sorted_tags_file = tags_file + '_sorted_by_file' + sorted_tags_file = tags_file + "_sorted_by_file" if os.path.isfile(sorted_tags_file): try: os.remove(tags_file) @@ -767,49 +786,52 @@ def run(self, view, args, tags_file): pass base_path = get_common_ancestor_folder( - view.file_name(), view.window().folders()) + view.file_name(), view.window().folders() + ) def get_tags(): with TagFile(tags_file, FILENAME) as tagfile: if lang: return tagfile.get_tags_dict_by_suffix( - suffix, filters=compile_filters(view)) + suffix, filters=compile_filters(view) + ) elif multi: - return tagfile.get_tags_dict( - filters=compile_filters(view)) + return tagfile.get_tags_dict(filters=compile_filters(view)) else: - return tagfile.get_tags_dict( - *files, filters=compile_filters(view)) + return tagfile.get_tags_dict(*files, filters=compile_filters(view)) if key in tags_cache[base_path]: - print('loading symbols from cache') + print("loading symbols from cache") tags = tags_cache[base_path][key] else: - print('loading symbols from file') + print("loading symbols from file") tags = get_tags() tags_cache[base_path][key] = tags - print(('loaded [%d] symbols' % len(tags))) + print(("loaded [%d] symbols" % len(tags))) if not tags: if multi: sublime.status_message( - 'No symbols found **FOR CURRENT FOLDERS**; Try Rebuild?') + "No symbols found **FOR CURRENT FOLDERS**; Try Rebuild?" + ) else: sublime.status_message( - 'No symbols found **FOR CURRENT FILE**; Try Rebuild?') + "No symbols found **FOR CURRENT FILE**; Try Rebuild?" + ) - path_cols = (0, ) if len(files) > 1 or multi else () + path_cols = (0,) if len(files) > 1 or multi else () formatting = functools.partial( - format_tag_for_quickopen, show_path=bool(path_cols)) + format_tag_for_quickopen, show_path=bool(path_cols) + ) @prepare_for_quickpanel(formatting) def sorted_tags(): - return sorted( - chain(*(tags[k] for k in tags)), key=iget('tag_path')) + return sorted(chain(*(tags[k] for k in tags)), key=iget("tag_path")) return sorted_tags + # Rebuild CTags commands @@ -823,7 +845,7 @@ class RebuildTags(sublime_plugin.WindowCommand): def run(self, dirs=None, files=None): """Handler for ``rebuild_tags`` command""" - view = self.window.active_view(); + view = self.window.active_view() paths = [] if dirs: @@ -833,20 +855,22 @@ def run(self, dirs=None, files=None): if paths: self.build_ctags( - paths, - command=setting('command'), - tag_file=setting('tag_file'), - recursive=setting('recursive'), - opts=read_opts(view) + paths, + command=setting("command"), + tag_file=setting("tag_file"), + recursive=setting("recursive"), + opts=read_opts(view), ) - elif view is None or view.file_name() is None and len(self.window.folders()) <= 0: - status_message('Cannot build CTags: No file or folder open.') + elif ( + view is None or view.file_name() is None and len(self.window.folders()) <= 0 + ): + status_message("Cannot build CTags: No file or folder open.") else: show_build_panel(view) - @threaded(msg='Already running CTags!') + @threaded(msg="Already running CTags!") def build_ctags(self, paths, command, tag_file, recursive, opts): """ Build tags for the open file or folder(s). @@ -861,42 +885,46 @@ def build_ctags(self, paths, command, tag_file, recursive, opts): :returns: None """ + def tags_building(tag_file): """Display 'Building CTags' message in all views""" - print(('Building CTags for %s: Please be patient' % tag_file)) - in_main(lambda: status_message('Building CTags for {0}: Please be' - ' patient'.format(tag_file)))() + print(("Building CTags for %s: Please be patient" % tag_file)) + in_main( + lambda: status_message( + "Building CTags for {0}: Please be" " patient".format(tag_file) + ) + )() def tags_built(tag_file): """Display 'Finished Building CTags' message in all views""" - print(('Finished building %s' % tag_file)) - in_main(lambda: status_message('Finished building {0}' - .format(tag_file)))() + print(("Finished building %s" % tag_file)) + in_main(lambda: status_message("Finished building {0}".format(tag_file)))() in_main(lambda: tags_cache[os.path.dirname(tag_file)].clear())() for path in paths: tags_building(path) try: - result = build_ctags(path=path, tag_file=tag_file, - recursive=recursive, opts=opts, - cmd=command) + result = build_ctags( + path=path, + tag_file=tag_file, + recursive=recursive, + opts=opts, + cmd=command, + ) except IOError as e: error_message(e.strerror) return except subprocess.CalledProcessError as e: - if sublime.platform() == 'windows': - str_err = ' '.join( - e.output.decode('windows-1252').splitlines()) + if sublime.platform() == "windows": + str_err = " ".join(e.output.decode("windows-1252").splitlines()) else: - str_err = e.output.decode( - locale.getpreferredencoding()).rstrip() + str_err = e.output.decode(locale.getpreferredencoding()).rstrip() error_message(str_err) return except Exception as e: - error_message( - "An unknown error occured.\nCheck the console for info.") + error_message("An unknown error occured.\nCheck the console for info.") raise e tags_built(result) @@ -904,6 +932,7 @@ def tags_built(tag_file): if tag_file in ctags_completions: del ctags_completions[tag_file] # clear the cached ctags list + # Autocomplete commands @@ -911,15 +940,13 @@ def tags_built(tag_file): class CTagsAutoComplete(sublime_plugin.EventListener): - def on_query_completions(self, view, prefix, locations): - if not setting('autocomplete'): + if not setting("autocomplete"): return None prefix = prefix.lower() - tags_path = find_tags_relative_to( - view.file_name(), setting('tag_file')) + tags_path = find_tags_relative_to(view.file_name(), setting("tag_file")) if not tags_path: return None @@ -943,8 +970,11 @@ def on_query_completions(self, view, prefix, locations): ctags_completions[tags_path] = tags - return [tag for tag in ctags_completions[tags_path] - if tag.lower().startswith(prefix)] + return [ + tag + for tag in ctags_completions[tags_path] + if tag.lower().startswith(prefix) + ] # Test CTags commands @@ -966,13 +996,12 @@ def __next__(self): self.routine = None def co_routine(self, view): - tag_file = find_tags_relative_to( - view.file_name(), setting('tag_file')) + tag_file = find_tags_relative_to(view.file_name(), setting("tag_file")) - with codecs.open(tag_file, encoding='utf-8') as tf: + with codecs.open(tag_file, encoding="utf-8") as tf: tags = parse_tag_lines(tf, tag_class=TagElements) - print('Starting Test') + print("Starting Test") ex_failures = [] line_failures = [] @@ -991,16 +1020,17 @@ def hook(av): test_context = av.line(test_context) if not av.substr(test_context).startswith(test_string): - failure = 'FAILURE %s' % pprint.pformat(tag) + failure = "FAILURE %s" % pprint.pformat(tag) failure += av.file_name() - if setting('debug'): - if not sublime.question_box('%s\n\n\n' % failure): + if setting("debug"): + if not sublime.question_box("%s\n\n\n" % failure): self.routine = None return sublime.set_clipboard(failure) ex_failures.append(tag) sublime.set_timeout(self.__next__, 5) + scroll_to_tag(view, tag, hook) yield @@ -1010,11 +1040,11 @@ def hook(av): view = sublime.active_window().new_file() with Edit(view) as edit: - edit.insert(view.size(), '%s Tags Tested OK\n' % tags_tested) - edit.insert(view.size(), '%s Tags Failed' % len(failures)) + edit.insert(view.size(), "%s Tags Tested OK\n" % tags_tested) + edit.insert(view.size(), "%s Tags Failed" % len(failures)) view.set_scratch(True) - view.set_name('CTags Test Results') + view.set_name("CTags Test Results") if failures: sublime.set_clipboard(pprint.pformat(failures)) diff --git a/plugins/ctags.py b/plugins/ctags.py index 695cb2c..a9df10b 100644 --- a/plugins/ctags.py +++ b/plugins/ctags.py @@ -16,27 +16,28 @@ # TAGS_RE = re.compile( - r'(?P[^\t]+)\t' - r'(?P[^\t]+)\t' + r"(?P[^\t]+)\t" + r"(?P[^\t]+)\t" r'(?P(/.+/|\?.+\?|\d+));"\t' - r'(?P[^\t\r\n]+)' - r'(?:\t(?P.*))?' + r"(?P[^\t\r\n]+)" + r"(?:\t(?P.*))?" ) # column indexes SYMBOL = 0 FILENAME = 1 -MATCHES_STARTWITH = 'starts_with' +MATCHES_STARTWITH = "starts_with" PATH_ORDER = [ - 'function', 'class', 'struct', + "function", + "class", + "struct", ] -PATH_IGNORE_FIELDS = ( - 'file', 'access', 'signature', 'language', 'line', 'inherits') +PATH_IGNORE_FIELDS = ("file", "access", "signature", "language", "line", "inherits") -TAG_PATH_SPLITTERS = ('/', '.', '::', ':') +TAG_PATH_SPLITTERS = ("/", ".", "::", ":") # # Functions @@ -44,6 +45,7 @@ # Helper functions + def splits(string, *splitters): """ Split a string on a number of splitters. @@ -61,9 +63,11 @@ def splits(string, *splitters): if string: yield string + # Tag processing functions -def parse_tag_lines(lines, order_by='symbol', tag_class=None, filters=None): + +def parse_tag_lines(lines, order_by="symbol", tag_class=None, filters=None): """ Parse and sort a list of tags. @@ -88,7 +92,7 @@ def parse_tag_lines(lines, order_by='symbol', tag_class=None, filters=None): if isinstance(line, Tag): # handle both text and tag objects line = line.line - line = line.rstrip('\r\n') + line = line.rstrip("\r\n") search_obj = TAGS_RE.search(line) @@ -116,6 +120,7 @@ def parse_tag_lines(lines, order_by='symbol', tag_class=None, filters=None): return tags_lookup + def post_process_tag(tag): """ Process 'EX Command'-related elements of a tag. @@ -160,12 +165,13 @@ def post_process_tag(tag): """ tag.update(process_fields(tag)) - tag['ex_command'] = process_ex_cmd(tag) + tag["ex_command"] = process_ex_cmd(tag) tag.update(create_tag_path(tag)) return tag + def process_ex_cmd(tag): """ Process the 'ex_command' element of a tag dictionary. @@ -177,12 +183,13 @@ def process_ex_cmd(tag): :returns: updated 'ex_command' dictionary entry """ - ex_cmd = tag.get('ex_command') + ex_cmd = tag.get("ex_command") if ex_cmd.isdigit(): # if a line number, do nothing return ex_cmd - else: # else a regex, so unescape - return re.sub(r"\\(\$|/|\^|\\)", r'\1', ex_cmd[2:-2]) # unescape regex + else: # else a regex, so unescape + return re.sub(r"\\(\$|/|\^|\\)", r"\1", ex_cmd[2:-2]) # unescape regex + def process_fields(tag): """ @@ -197,19 +204,20 @@ def process_fields(tag): :returns: dict containing the key-value pairs from the field element, plus a list of keys for said pairs """ - fields = tag.get('fields') + fields = tag.get("fields") if not fields: # do nothing return {} # split the fields string into a dictionary of key-value pairs - result = dict(f.split(':', 1) for f in fields.split('\t')) + result = dict(f.split(":", 1) for f in fields.split("\t")) # append all keys to the dictionary - result['field_keys'] = sorted(result.keys()) + result["field_keys"] = sorted(result.keys()) return result + def create_tag_path(tag): """ Create a tag path entry for a tag dictionary. @@ -226,9 +234,9 @@ def create_tag_path(tag): :returns: dict containing the 'tag_path' entry """ - field_keys = tag.get('field_keys', [])[:] + field_keys = tag.get("field_keys", [])[:] fields = [] - tag_path = '' + tag_path = "" # sort field arguments related to path order in correct order for field in PATH_ORDER: @@ -242,22 +250,23 @@ def create_tag_path(tag): # convert list of fields to dot-joined string, dropping any "ignore" fields for field in fields: if field not in PATH_IGNORE_FIELDS: - tag_path += (tag.get(field) + '.') + tag_path += tag.get(field) + "." # append symbol as last item in string - tag_path += tag.get('symbol') + tag_path += tag.get("symbol") # split string on seperators and append tag filename to resulting list - splitup = ([tag.get('filename')] + - list(splits(tag_path, *TAG_PATH_SPLITTERS))) + splitup = [tag.get("filename")] + list(splits(tag_path, *TAG_PATH_SPLITTERS)) # convert list to tuple - result = {'tag_path': tuple(splitup)} + result = {"tag_path": tuple(splitup)} return result + # Tag building/sorting functions + def build_ctags(path, cmd=None, tag_file=None, recursive=False, opts=None): """ Execute the ``ctags`` command using ``Popen``. @@ -275,11 +284,12 @@ def build_ctags(path, cmd=None, tag_file=None, recursive=False, opts=None): if cmd: cmd = [cmd] else: - cmd = ['ctags'] + cmd = ["ctags"] if not os.path.exists(path): - raise IOError('\'path\' is not at valid directory or file path, or ' - 'is not accessible') + raise IOError( + "'path' is not at valid directory or file path, or " "is not accessible" + ) if os.path.isfile(path): cwd = os.path.dirname(path) @@ -287,7 +297,7 @@ def build_ctags(path, cmd=None, tag_file=None, recursive=False, opts=None): cwd = path if tag_file: - cmd.append('-f {0}'.format(tag_file)) + cmd.append("-f {0}".format(tag_file)) if opts: if type(opts) == list: @@ -296,24 +306,25 @@ def build_ctags(path, cmd=None, tag_file=None, recursive=False, opts=None): cmd.append(opts) if recursive: # ignore any file specified in path if recursive set - cmd.append('-R') + cmd.append("-R") elif os.path.isfile(path): filename = os.path.basename(path) cmd.append(filename) else: # search all files in current directory - cmd.append(os.path.join(path, '*')) + cmd.append(os.path.join(path, "*")) # workaround for the issue described here: # http://bugs.python.org/issue6689 - if os.name == 'posix': - cmd = ' '.join(cmd) + if os.name == "posix": + cmd = " ".join(cmd) # execute the command - check_output(cmd, cwd=cwd, shell=True, stdin=subprocess.PIPE, - stderr=subprocess.STDOUT) + check_output( + cmd, cwd=cwd, shell=True, stdin=subprocess.PIPE, stderr=subprocess.STDOUT + ) if not tag_file: # Exuberant ctags defaults to ``tags`` filename. - tag_file = os.path.join(cwd, 'tags') + tag_file = os.path.join(cwd, "tags") else: if os.path.dirname(tag_file) != cwd: tag_file = os.path.join(cwd, tag_file) @@ -323,6 +334,7 @@ def build_ctags(path, cmd=None, tag_file=None, recursive=False, opts=None): return tag_file + def resort_ctags(tag_file): """ Rearrange ctags file for speed. @@ -351,17 +363,17 @@ def resort_ctags(tag_file): """ meta = [] symbols = [] - tmp_file = tag_file + '.tmp' + tmp_file = tag_file + ".tmp" - with codecs.open(tag_file, encoding='utf-8', errors='replace') as file_: + with codecs.open(tag_file, encoding="utf-8", errors="replace") as file_: for line in file_: - if line.startswith('!_TAG'): + if line.startswith("!_TAG"): meta.append(line) continue - # read all valid symbol tags, which contain at least + # read all valid symbol tags, which contain at least # symbol name and containing file and build a list of tuples - split = line.split('\t') + split = line.split("\t") if len(split) > FILENAME: symbols.append((split[FILENAME], split)) @@ -369,42 +381,45 @@ def resort_ctags(tag_file): meta.sort() symbols.sort() - with codecs.open(tmp_file, 'w', encoding='utf-8', - errors='replace') as file_: - + with codecs.open(tmp_file, "w", encoding="utf-8", errors="replace") as file_: # write sourted metadata file_.writelines(meta) # followed by sorted list of symbols for _, split in symbols: - split[FILENAME] = split[FILENAME].lstrip('.\\') - file_.write('\t'.join(split)) + split[FILENAME] = split[FILENAME].lstrip(".\\") + file_.write("\t".join(split)) os.remove(tag_file) os.rename(tmp_file, tag_file) + # # Models # + class TagElements(dict): """ Model the entries of a tag file. """ + def __init__(self, *args, **kw): """Initialise Tag object""" dict.__init__(self, *args, **kw) self.__dict__ = self + class Tag(object): """ Model a tag. This exists mainly to enable different types of sorting. """ + def __init__(self, line, column=0): if isinstance(line, bytes): # python 3 compatibility - line = line.decode('utf-8', 'replace') + line = line.decode("utf-8", "replace") self.line = line self.column = column @@ -421,15 +436,16 @@ def __gt__(self, other): return False def __getitem__(self, index): - return self.line.split('\t', self.column + 1)[index] + return self.line.split("\t", self.column + 1)[index] def __len__(self): - return self.line.count('\t') + 1 + return self.line.count("\t") + 1 @property def key(self): return self[self.column] + class TagFile(object): """ Model a tag file. @@ -440,6 +456,7 @@ class TagFile(object): searching for a retrieving tags, finding tags based on given criteria (prefix, suffix, exact), getting the directory of a tag and so forth. """ + file_o = None mapped = None @@ -504,9 +521,8 @@ def open(self): """ Open file. """ - self.file_o = codecs.open(self.path, 'r+b', encoding='utf-8') - self.mapped = mmap.mmap(self.file_o.fileno(), 0, - access=mmap.ACCESS_READ) + self.file_o = codecs.open(self.path, "r+b", encoding="utf-8") + self.mapped = mmap.mmap(self.file_o.fileno(), 0, access=mmap.ACCESS_READ) def close(self): """ @@ -570,20 +586,22 @@ def tag_class(self): accessed as class variables (i.e. ``class.variable``, rather than ``dict['variable']) """ - return type('TagElements', (TagElements,), dict(root_dir=self.dir)) + return type("TagElements", (TagElements,), dict(root_dir=self.dir)) def get_tags_dict(self, *tags, **kw): """ Return the tags from a tag file as a dict. """ - filters = kw.get('filters', []) - return parse_tag_lines(self.search(True, *tags), - tag_class=self.tag_class(), filters=filters) + filters = kw.get("filters", []) + return parse_tag_lines( + self.search(True, *tags), tag_class=self.tag_class(), filters=filters + ) def get_tags_dict_by_suffix(self, suffix, **kw): """ Return the tags with the given suffix of a tag file as a dict. """ - filters = kw.get('filters', []) - return parse_tag_lines(self.search_by_suffix(suffix), - tag_class=self.tag_class(), filters=filters) + filters = kw.get("filters", []) + return parse_tag_lines( + self.search_by_suffix(suffix), tag_class=self.tag_class(), filters=filters + ) diff --git a/plugins/edit.py b/plugins/edit.py index 436395d..bb84160 100644 --- a/plugins/edit.py +++ b/plugins/edit.py @@ -13,6 +13,7 @@ except AttributeError: sublime.edit_storage = {} + def run_callback(func, *args, **kwargs): spec = inspect.getfullargspec(func) if spec.args or spec.varargs: @@ -20,6 +21,7 @@ def run_callback(func, *args, **kwargs): else: func() + class EditFuture: def __init__(self, func): self.func = func @@ -27,19 +29,20 @@ def __init__(self, func): def resolve(self, view, edit): return self.func(view, edit) + class EditStep: def __init__(self, cmd, *args): self.cmd = cmd self.args = args def run(self, view, edit): - if self.cmd == 'callback': + if self.cmd == "callback": return run_callback(self.args[0], view, edit) funcs = { - 'insert': view.insert, - 'erase': view.erase, - 'replace': view.replace, + "insert": view.insert, + "erase": view.erase, + "replace": view.replace, } func = funcs.get(self.cmd) if func: @@ -54,6 +57,7 @@ def resolve_args(self, view, edit): args.append(arg) return args + class Edit: def __init__(self, view): self.view = view @@ -71,21 +75,21 @@ def step(self, cmd, *args): self.steps.append(step) def insert(self, point, string): - self.step('insert', point, string) + self.step("insert", point, string) def erase(self, region): - self.step('erase', region) + self.step("erase", region) def replace(self, region, string): - self.step('replace', region, string) + self.step("replace", region, string) def sel(self, start, end=None): if end is None: end = start - self.step('sel', start, end) + self.step("sel", start, end) def callback(self, func): - self.step('callback', func) + self.step("callback", func) def run(self, view, edit): for step in self.steps: @@ -96,14 +100,15 @@ def __enter__(self): def __exit__(self, type_, value, traceback): view = self.view - if sublime.version().startswith('2'): + if sublime.version().startswith("2"): edit = view.begin_edit() self.run(view, edit) view.end_edit(edit) else: key = str(hash(tuple(self.steps))) sublime.edit_storage[key] = self.run - view.run_command('apply_edit', {'key': key}) + view.run_command("apply_edit", {"key": key}) + class apply_edit(sublime_plugin.TextCommand): def run(self, edit, key): diff --git a/plugins/ranking/parse.py b/plugins/ranking/parse.py index 9cc939e..1cc6798 100644 --- a/plugins/ranking/parse.py +++ b/plugins/ranking/parse.py @@ -1,6 +1,7 @@ import re from ..utils import * -#import spdb + +# import spdb # spdb.start() @@ -8,6 +9,7 @@ class Parser: """ Parses tag references and tag definitions. Used for ranking """ + @staticmethod def extract_member_exp(line_to_symbol, source): """ @@ -21,23 +23,25 @@ def extract_member_exp(line_to_symbol, source): return [line_to_symbol] # Get per-language syntax regex of brackets, splitters etc. - mbr_exp = lang.get('member_exp') + mbr_exp = lang.get("member_exp") if mbr_exp is None: return [line_to_symbol] - lstStop = mbr_exp.get('stop', []) - if (not lstStop): - print('warning!: language has member_exp setting but it is ineffective: Must have "stop" key with array of regex to stop search backward from identifier') + lstStop = mbr_exp.get("stop", []) + if not lstStop: + print( + 'warning!: language has member_exp setting but it is ineffective: Must have "stop" key with array of regex to stop search backward from identifier' + ) return [line_to_symbol] - lstClose = mbr_exp.get('close', []) + lstClose = mbr_exp.get("close", []) reClose = concat_re(lstClose) - lstOpen = mbr_exp.get('open', []) + lstOpen = mbr_exp.get("open", []) reOpen = concat_re(lstOpen) - lstIgnore = mbr_exp.get('ignore', []) + lstIgnore = mbr_exp.get("ignore", []) reIgnore = concat_re(lstIgnore) if len(lstOpen) != len(lstClose): - print('warning!: extract_member_exp: settings lstOpen must match lstClose') + print("warning!: extract_member_exp: settings lstOpen must match lstClose") matchOpenClose = dict(zip(lstOpen, lstClose)) # Construct | regex from all open and close strings with capture (..) splex = concat_re(lstOpen + lstClose + lstIgnore + lstStop) @@ -45,7 +49,7 @@ def extract_member_exp(line_to_symbol, source): reStop = concat_re(lstStop) splex = "({0}|{1})".format(splex, reIgnore) splat = re.split(splex, line_to_symbol) - #print('splat=%s' % splat) + # print('splat=%s' % splat) # Stack iter reverse(splat) for detecting unbalanced e.g 'func(obj.yyy' # while skipping balanced brackets in getSlow(a && b).mtd() stack = [] @@ -68,8 +72,9 @@ def extract_member_exp(line_to_symbol, source): tokCloseCur = matchOpenClose.get(cur) if tokClose != tokCloseCur: print( - 'non-matching brackets at the same nesting level: %s %s' % - (tokCloseCur, tokClose)) + "non-matching brackets at the same nesting level: %s %s" + % (tokCloseCur, tokClose) + ) break insideExp = False # If white space --> stop. Do not stop for whitespace inside @@ -84,7 +89,7 @@ def extract_member_exp(line_to_symbol, source): strMbrExp = "".join(lstMbr) - lstSplit = mbr_exp.get('splitters', []) + lstSplit = mbr_exp.get("splitters", []) reSplit = concat_re(lstSplit) # Split member deref per-lang (-> and :: in PHP and C++) - use base if # not found diff --git a/plugins/ranking/rank.py b/plugins/ranking/rank.py index 9b99d27..12685e4 100644 --- a/plugins/ranking/rank.py +++ b/plugins/ranking/rank.py @@ -14,10 +14,8 @@ def compile_definition_filters(view): filters = [] - for selector, regexes in list( - get_setting('definition_filters', {}).items()): - if view.match_selector(view.sel() and view.sel()[0].begin() or 0, - selector): + for selector, regexes in list(get_setting("definition_filters", {}).items()): + if view.match_selector(view.sel() and view.sel()[0].begin() or 0, selector): filters.append(regexes) return filters @@ -45,19 +43,18 @@ def __init__(self, region, mbrParts, view, symbol, sym_line): self.sym_line = sym_line self.lang = get_lang_setting(get_source(view)) - self.mbr_exp = self.lang.get('member_exp', {}) + self.mbr_exp = self.lang.get("member_exp", {}) self.def_filters = compile_definition_filters(view) - self.fname_abs = view.file_name().lower() if not( - view.file_name() is None) else None + self.fname_abs = ( + view.file_name().lower() if not (view.file_name() is None) else None + ) mbrGrams = [get_grams(part) for part in mbrParts] self.setMbrGrams = ( - reduce( - lambda s, - t: s.union(t), - mbrGrams) if mbrGrams else set()) + reduce(lambda s, t: s.union(t), mbrGrams) if mbrGrams else set() + ) def pass_def_filter(self, o): for f in self.def_filters: @@ -70,7 +67,7 @@ def pass_def_filter(self, o): def eq_filename(self, rel_path): if self.fname_abs is None or rel_path is None: return False - return self.fname_abs.endswith(rel_path.lstrip('.').lower()) + return self.fname_abs.endswith(rel_path.lstrip(".").lower()) def scope_filter(self, taglist): """ @@ -83,15 +80,19 @@ def scope_filter(self, taglist): in_scope = [] no_scope = [] for tag in taglist: - if self.region is None or tag.get( - 'scope') is None or tag.scope is None or tag.scope == 'global': + if ( + self.region is None + or tag.get("scope") is None + or tag.scope is None + or tag.scope == "global" + ): no_scope.append(tag) continue if not self.eq_filename(tag.filename): continue - mch = re.search(get_setting('scope_re'), tag.scope) + mch = re.search(get_setting("scope_re"), tag.scope) if mch: # .tags file is 1 based and region.begin() is 0 based @@ -118,11 +119,11 @@ def get_type_rank(self, tag): # Try all regex to build a list of preferred / higher rank tag types if self.tag_types is None: self.tag_types = set() - reference_types = self.lang.get('reference_types', {}) + reference_types = self.lang.get("reference_types", {}) for re_ref, lstTypes in reference_types.items(): # replace special keyword __symbol__ with our reference symbol - cur_re = re_ref.replace('__symbol__', self.symbol) - if (re.search(cur_re, self.sym_line)): + cur_re = re_ref.replace("__symbol__", self.symbol) + if re.search(cur_re, self.sym_line): self.tag_types = self.tag_types.union(lstTypes) return self.RANK_MATCH_TYPE if tag.type in self.tag_types else 0 @@ -138,18 +139,18 @@ def get_samefile_rank(self, rel_path, mbrParts): Note: Inheritence model (base class in different file) is not yet supported. """ if self.reThis is None: - lstThis = self.mbr_exp.get('this') + lstThis = self.mbr_exp.get("this") if lstThis: self.reThis = re.compile(concat_re(lstThis), re.IGNORECASE) elif self.mbr_exp: print( - 'Warning! Language that has syntax settings is expected to define this|self expression syntax') + "Warning! Language that has syntax settings is expected to define this|self expression syntax" + ) rank = 0 if self.eq_filename(rel_path): rank += self.RANK_EQ_FILENAME_RANK - if len(mbrParts) == 1 and self.reThis and self.reThis.match( - mbrParts[-1]): + if len(mbrParts) == 1 and self.reThis and self.reThis.match(mbrParts[-1]): # this.mtd() - rank candidate from current file very high. rank += self.RANK_EQ_FILENAME_RANK return rank @@ -170,11 +171,14 @@ def get_mbr_exp_match_tagfile_rank(self, rel_path, mbrParts): if len(mbrParts) == 0: return rank - rel_path_no_ext = rel_path.lstrip('.' + os.sep) + rel_path_no_ext = rel_path.lstrip("." + os.sep) rel_path_no_ext = os.path.splitext(rel_path_no_ext)[0] pathParts = rel_path_no_ext.split(os.sep) - if len(pathParts) >= 1 and len( - mbrParts) >= 1 and pathParts[-1].lower() == mbrParts[-1].lower(): + if ( + len(pathParts) >= 1 + and len(mbrParts) >= 1 + and pathParts[-1].lower() == mbrParts[-1].lower() + ): rank += self.RANK_EXACT_MATCH_RIGHTMOST_MBR_PART_TO_FILENAME # Prepare dict of , where weight decays are we move @@ -208,7 +212,7 @@ def get_combined_rank(self, tag, mbrParts): # Object Member Expression File Ranking rank += self.get_mbr_exp_match_tagfile_rank(rel_path, mbrParts) -# print('rank = %d' % rank); + # print('rank = %d' % rank); return rank def sort_tags(self, taglist): @@ -218,14 +222,17 @@ def sort_tags(self, taglist): # If object-receiver (someobj.symbol) --> refer to as global tag --> # filter out local-scope tags (in_scope, no_scope) = self.scope_filter(taglist) - if (len(self.setMbrGrams) == 0 and len(in_scope) > - 0): # TODO:Config: @symbol - in Ruby instance var (therefore never local var) + if ( + len(self.setMbrGrams) == 0 and len(in_scope) > 0 + ): # TODO:Config: @symbol - in Ruby instance var (therefore never local var) p_tags = in_scope else: p_tags = no_scope p_tags = list(filter(lambda tag: self.pass_def_filter(tag), p_tags)) p_tags = sorted( - p_tags, key=lambda tag: self.get_combined_rank( - tag, self.mbrParts), reverse=True) + p_tags, + key=lambda tag: self.get_combined_rank(tag, self.mbrParts), + reverse=True, + ) return p_tags diff --git a/plugins/tests/__init__.py b/plugins/tests/__init__.py index d6ddff9..8d15a1a 100644 --- a/plugins/tests/__init__.py +++ b/plugins/tests/__init__.py @@ -3,5 +3,5 @@ from . import mock_sublime from . import mock_sublime_plugin -sys.modules['sublime'] = mock_sublime -sys.modules['sublime_plugin'] = mock_sublime_plugin +sys.modules["sublime"] = mock_sublime +sys.modules["sublime_plugin"] = mock_sublime_plugin diff --git a/plugins/tests/mock_sublime.py b/plugins/tests/mock_sublime.py index d22c237..8afc014 100644 --- a/plugins/tests/mock_sublime.py +++ b/plugins/tests/mock_sublime.py @@ -13,19 +13,19 @@ def arch(): - return 'x64' + return "x64" def platform(): - if sys.platform == 'darwin': - return 'osx' - if sys.platform == 'win32': - return 'windows' - return 'linux' + if sys.platform == "darwin": + return "osx" + if sys.platform == "win32": + return "windows" + return "linux" def version(): - return '4126' + return "4126" def load_settings(self, **kargs): diff --git a/plugins/tests/mock_sublime_plugin.py b/plugins/tests/mock_sublime_plugin.py index af16313..db50fae 100644 --- a/plugins/tests/mock_sublime_plugin.py +++ b/plugins/tests/mock_sublime_plugin.py @@ -2,9 +2,7 @@ Mock module for ``sublime_plugin`` in Sublime Text. """ -all_callbacks = { - 'on_load': [] -} +all_callbacks = {"on_load": []} class WindowCommand(object): diff --git a/plugins/tests/test_ctags.py b/plugins/tests/test_ctags.py index e9ce9a1..64a0f30 100644 --- a/plugins/tests/test_ctags.py +++ b/plugins/tests/test_ctags.py @@ -24,17 +24,20 @@ def build_python_file(self): :returns: Path to a constructed, valid Python source file """ - path = '' + path = "" # the file created here is locked while open, hence we can't delete # similarly, ctags appears to require an extension hence the suffix - with tempfile.NamedTemporaryFile(delete=False, suffix='.py') as temp: + with tempfile.NamedTemporaryFile(delete=False, suffix=".py") as temp: try: path = temp.name # store name for later use - temp.writelines([ - b'def my_definition():\n', - b'\toutput = "Hello, world!"\n', - b'\tprint(output)\n']) + temp.writelines( + [ + b"def my_definition():\n", + b'\toutput = "Hello, world!"\n', + b"\tprint(output)\n", + ] + ) finally: temp.close() @@ -55,28 +58,31 @@ def build_python_file__extended(self): :returns: Path to a constructed, valid Python source file """ - path = '' + path = "" # the file created here is locked while open, hence we can't delete # similarly, ctags appears to require an extension hence the suffix - with tempfile.NamedTemporaryFile(delete=False, suffix='.py') as temp: + with tempfile.NamedTemporaryFile(delete=False, suffix=".py") as temp: try: path = temp.name # store name for later use - temp.writelines([ - b'import os\n', - b'\n', - b'COLOR_RED = "\\c800080FF;"\t#red\n', - b'\n', - b'def my_function(first_name):\n', - b'\tprint("Hello {0}".format(first_name))\n', - b'\n', - b'class MyClass(object):\n', - b'\tlast_name = None\n', - b'\taddress = None\t# comment preceded by a tab\n', - b'\n', - b'\tdef my_method(self, last_name):\n', - b'\t\tself.last_name = last_name\n', - b'\t\tprint("Hello again, {0}".format(self.last_name))\n']) + temp.writelines( + [ + b"import os\n", + b"\n", + b'COLOR_RED = "\\c800080FF;"\t#red\n', + b"\n", + b"def my_function(first_name):\n", + b'\tprint("Hello {0}".format(first_name))\n', + b"\n", + b"class MyClass(object):\n", + b"\tlast_name = None\n", + b"\taddress = None\t# comment preceded by a tab\n", + b"\n", + b"\tdef my_method(self, last_name):\n", + b"\t\tself.last_name = last_name\n", + b'\t\tprint("Hello again, {0}".format(self.last_name))\n', + ] + ) finally: temp.close() @@ -91,26 +97,29 @@ def build_java_file(self): :returns: Path to a constructed, valid Java source file """ - path = '' + path = "" # the file created here is locked while open, hence we can't delete # similarly, ctags appears to require an extension hence the suffix - with tempfile.NamedTemporaryFile(delete=False, suffix='.java') as temp: + with tempfile.NamedTemporaryFile(delete=False, suffix=".java") as temp: try: path = temp.name # store name for later use - temp.writelines([ - b'public class DemoClass {\n', - b'\tpublic static void main(String args[]) {\n', - b'\t\tSystem.out.println("Hello, World");\n', - b'\n', - b'\t\tDemoClass demo = new DemoClass();\n', - b'\t\tSystem.out.printf("Sum %d\n", demo.getSum(5,6));\n', - b'\t}\n', - b'\n', - b'\tprivate int getSum(int a, int b) {\n', - b'\t\treturn (a + b);\n', - b'\t}\n', - b'}\n']) + temp.writelines( + [ + b"public class DemoClass {\n", + b"\tpublic static void main(String args[]) {\n", + b'\t\tSystem.out.println("Hello, World");\n', + b"\n", + b"\t\tDemoClass demo = new DemoClass();\n", + b'\t\tSystem.out.printf("Sum %d\n", demo.getSum(5,6));\n', + b"\t}\n", + b"\n", + b"\tprivate int getSum(int a, int b) {\n", + b"\t\treturn (a + b);\n", + b"\t}\n", + b"}\n", + ] + ) finally: temp.close() @@ -124,24 +133,27 @@ def build_c_file(self): :returns: Path to a constructed, valid C source file """ - path = '' + path = "" # the file created here is locked while open, hence we can't delete # similarly, ctags appears to require an extension hence the suffix - with tempfile.NamedTemporaryFile(delete=False, suffix='.c') as temp: + with tempfile.NamedTemporaryFile(delete=False, suffix=".c") as temp: try: path = temp.name # store name for later use - temp.writelines([ - b'#define foo(x,y) x+y\n' - b'#define foobar 1\n' - b'\n' - b'void bar()\n' - b'{\n' - b'\tfoo(10,2);' - b'\n' - b'#if foobar\n' - b'\tfoo(2,3); \n' - b'}\n']) + temp.writelines( + [ + b"#define foo(x,y) x+y\n" + b"#define foobar 1\n" + b"\n" + b"void bar()\n" + b"{\n" + b"\tfoo(10,2);" + b"\n" + b"#if foobar\n" + b"\tfoo(2,3); \n" + b"}\n" + ] + ) finally: temp.close() @@ -173,8 +185,7 @@ def test_build_ctags__ctags_on_path(self): try: ctags.build_ctags(path=temp.name) except EnvironmentError: - self.fail('build_ctags() raised EnvironmentError. ctags not' - ' on path') + self.fail("build_ctags() raised EnvironmentError. ctags not" " on path") def test_build_ctags__custom_command(self): """ @@ -184,10 +195,9 @@ def test_build_ctags__custom_command(self): # cross-platform way to get the temp directory with tempfile.NamedTemporaryFile() as temp: try: - ctags.build_ctags(path=temp.name, cmd='ctags') + ctags.build_ctags(path=temp.name, cmd="ctags") except EnvironmentError: - self.fail('build_ctags() raised EnvironmentError. ctags not' - ' on path') + self.fail("build_ctags() raised EnvironmentError. ctags not" " on path") def test_build_ctags__invalid_custom_command(self): """ @@ -197,7 +207,7 @@ def test_build_ctags__invalid_custom_command(self): # cross-platform way to get the temp directory with tempfile.NamedTemporaryFile() as temp: with self.assertRaises(CalledProcessError): - ctags.build_ctags(path=temp.name, cmd='ccttaaggss') + ctags.build_ctags(path=temp.name, cmd="ccttaaggss") def test_build_ctags__single_file(self): """ @@ -207,14 +217,15 @@ def test_build_ctags__single_file(self): tag_file = ctags.build_ctags(path=path) - with codecs.open(tag_file, encoding='utf-8') as output: + with codecs.open(tag_file, encoding="utf-8") as output: try: content = output.readlines() filename = os.path.basename(path) self.assertEqual( content[-1], - 'my_definition\t{0}\t/^def my_definition()' - ':$/;"\tf{1}'.format(filename, os.linesep)) + "my_definition\t{0}\t/^def my_definition()" + ':$/;"\tf{1}'.format(filename, os.linesep), + ) finally: output.close() os.remove(path) # clean up @@ -226,16 +237,17 @@ def test_build_ctags__custom_tag_file(self): """ path = self.build_python_file() - tag_file = ctags.build_ctags(path=path, tag_file='my_tag_file') + tag_file = ctags.build_ctags(path=path, tag_file="my_tag_file") - with codecs.open(tag_file, encoding='utf-8') as output: + with codecs.open(tag_file, encoding="utf-8") as output: try: content = output.readlines() filename = os.path.basename(path) self.assertEqual( content[-1], - 'my_definition\t{0}\t/^def my_definition()' - ':$/;"\tf{1}'.format(filename, os.linesep)) + "my_definition\t{0}\t/^def my_definition()" + ':$/;"\tf{1}'.format(filename, os.linesep), + ) finally: output.close() os.remove(path) # clean up @@ -247,17 +259,18 @@ def test_build_ctags__additional_options(self): """ path = self.build_python_file() - tag_file = ctags.build_ctags(path=path, tag_file='my_tag_file', - opts="--language-force=java") + tag_file = ctags.build_ctags( + path=path, tag_file="my_tag_file", opts="--language-force=java" + ) - with codecs.open(tag_file, encoding='utf-8') as output: + with codecs.open(tag_file, encoding="utf-8") as output: try: content = output.readlines() # there should be nothing in the file but headers (due to the # Java 'language-force' option on a Python file) self.assertEqual( - content[-1][:2], # all comments start with '!_' - confirm - '!_') + content[-1][:2], "!_" # all comments start with '!_' - confirm + ) finally: output.close() os.remove(path) # clean up @@ -273,19 +286,21 @@ def test_post_process_tag__line_numbers(self): the supporting functions. """ tag = { - 'symbol': 'acme_function', - 'filename': '.\\a_folder\\a_script.py', - 'ex_command': '99', - 'type': 'f', - 'fields': None} + "symbol": "acme_function", + "filename": ".\\a_folder\\a_script.py", + "ex_command": "99", + "type": "f", + "fields": None, + } expected_output = { - 'symbol': 'acme_function', - 'filename': '.\\a_folder\\a_script.py', - 'tag_path': ('.\\a_folder\\a_script.py', 'acme_function'), - 'ex_command': '99', - 'type': 'f', - 'fields': None} + "symbol": "acme_function", + "filename": ".\\a_folder\\a_script.py", + "tag_path": (".\\a_folder\\a_script.py", "acme_function"), + "ex_command": "99", + "type": "f", + "fields": None, + } result = ctags.post_process_tag(tag) @@ -299,19 +314,21 @@ def test_post_process_tag__regex_no_fields(self): the supporting functions. """ tag = { - 'symbol': 'acme_function', - 'filename': '.\\a_folder\\a_script.py', - 'ex_command': '/^def acme_function(tag):$/', - 'type': 'f', - 'fields': None} + "symbol": "acme_function", + "filename": ".\\a_folder\\a_script.py", + "ex_command": "/^def acme_function(tag):$/", + "type": "f", + "fields": None, + } expected_output = { - 'symbol': 'acme_function', - 'filename': '.\\a_folder\\a_script.py', - 'tag_path': ('.\\a_folder\\a_script.py', 'acme_function'), - 'ex_command': 'def acme_function(tag):', - 'type': 'f', - 'fields': None} + "symbol": "acme_function", + "filename": ".\\a_folder\\a_script.py", + "tag_path": (".\\a_folder\\a_script.py", "acme_function"), + "ex_command": "def acme_function(tag):", + "type": "f", + "fields": None, + } result = ctags.post_process_tag(tag) @@ -325,22 +342,24 @@ def test_post_process_tag__fields(self): the supporting functions. """ tag = { - 'symbol': 'getSum', - 'filename': '.\\a_folder\\DemoClass.java', - 'ex_command': '/^\tprivate int getSum(int a, int b) {$/', - 'type': 'm', - 'fields': 'class:DemoClass\tfile:'} + "symbol": "getSum", + "filename": ".\\a_folder\\DemoClass.java", + "ex_command": "/^\tprivate int getSum(int a, int b) {$/", + "type": "m", + "fields": "class:DemoClass\tfile:", + } expected_output = { - 'symbol': 'getSum', - 'filename': '.\\a_folder\\DemoClass.java', - 'tag_path': ('.\\a_folder\\DemoClass.java', 'DemoClass', 'getSum'), - 'ex_command': '\tprivate int getSum(int a, int b) {', - 'type': 'm', - 'fields': 'class:DemoClass\tfile:', - 'field_keys': ['class', 'file'], - 'class': 'DemoClass', - 'file': ''} + "symbol": "getSum", + "filename": ".\\a_folder\\DemoClass.java", + "tag_path": (".\\a_folder\\DemoClass.java", "DemoClass", "getSum"), + "ex_command": "\tprivate int getSum(int a, int b) {", + "type": "m", + "fields": "class:DemoClass\tfile:", + "field_keys": ["class", "file"], + "class": "DemoClass", + "file": "", + } result = ctags.post_process_tag(tag) @@ -354,9 +373,9 @@ def test_parse_tag_lines__python(self): """ path = self.build_python_file__extended() - tag_file = ctags.build_ctags(path=path, opts=['--python-kinds=-i']) + tag_file = ctags.build_ctags(path=path, opts=["--python-kinds=-i"]) - with codecs.open(tag_file, encoding='utf-8') as output: + with codecs.open(tag_file, encoding="utf-8") as output: try: content = output.readlines() filename = os.path.basename(path) @@ -368,55 +387,73 @@ def test_parse_tag_lines__python(self): os.remove(tag_file) expected_outputs = { - 'MyClass': [{ - 'symbol': 'MyClass', - 'filename': filename, - 'ex_command': 'class MyClass(object):', - 'tag_path': (filename, 'MyClass'), - 'type': 'c', - 'fields': None}], - 'address': [{ - 'symbol': 'address', - 'filename': filename, - 'ex_command': '\taddress = None\t# comment preceded by a tab', - 'tag_path': (filename, 'MyClass', 'address'), - 'type': 'v', - 'fields': 'class:MyClass', - 'field_keys': ['class'], - 'class': 'MyClass'}], - 'last_name': [{ - 'symbol': 'last_name', - 'filename': filename, - 'ex_command': '\tlast_name = None', - 'tag_path': (filename, 'MyClass', 'last_name'), - 'type': 'v', - 'fields': 'class:MyClass', - 'field_keys': ['class'], - 'class': 'MyClass'}], - 'my_function': [{ - 'symbol': 'my_function', - 'filename': filename, - 'ex_command': 'def my_function(first_name):', - 'tag_path': (filename, 'my_function'), - 'type': 'f', - 'fields': None}], - 'my_method': [{ - 'symbol': 'my_method', - 'filename': filename, - 'ex_command': '\tdef my_method(self, last_name):', - 'tag_path': (filename, 'MyClass', 'my_method'), - 'type': 'm', - 'fields': 'class:MyClass', - 'field_keys': ['class'], - 'class': 'MyClass'}], - 'COLOR_RED': [{ - 'symbol': 'COLOR_RED', - 'filename': filename, - 'ex_command': 'COLOR_RED = "\\c800080FF;"\t#red', - 'tag_path': (filename, 'COLOR_RED'), - 'type': 'v', - 'fields': None}], - } + "MyClass": [ + { + "symbol": "MyClass", + "filename": filename, + "ex_command": "class MyClass(object):", + "tag_path": (filename, "MyClass"), + "type": "c", + "fields": None, + } + ], + "address": [ + { + "symbol": "address", + "filename": filename, + "ex_command": "\taddress = None\t# comment preceded by a tab", + "tag_path": (filename, "MyClass", "address"), + "type": "v", + "fields": "class:MyClass", + "field_keys": ["class"], + "class": "MyClass", + } + ], + "last_name": [ + { + "symbol": "last_name", + "filename": filename, + "ex_command": "\tlast_name = None", + "tag_path": (filename, "MyClass", "last_name"), + "type": "v", + "fields": "class:MyClass", + "field_keys": ["class"], + "class": "MyClass", + } + ], + "my_function": [ + { + "symbol": "my_function", + "filename": filename, + "ex_command": "def my_function(first_name):", + "tag_path": (filename, "my_function"), + "type": "f", + "fields": None, + } + ], + "my_method": [ + { + "symbol": "my_method", + "filename": filename, + "ex_command": "\tdef my_method(self, last_name):", + "tag_path": (filename, "MyClass", "my_method"), + "type": "m", + "fields": "class:MyClass", + "field_keys": ["class"], + "class": "MyClass", + } + ], + "COLOR_RED": [ + { + "symbol": "COLOR_RED", + "filename": filename, + "ex_command": 'COLOR_RED = "\\c800080FF;"\t#red', + "tag_path": (filename, "COLOR_RED"), + "type": "v", + "fields": None, + } + ], + } result = ctags.parse_tag_lines(content) @@ -434,7 +471,7 @@ def test_parse_tag_lines__c(self): tag_file = ctags.build_ctags(path=path) - with codecs.open(tag_file, encoding='utf-8') as output: + with codecs.open(tag_file, encoding="utf-8") as output: try: content = output.readlines() filename = os.path.basename(path) @@ -446,35 +483,43 @@ def test_parse_tag_lines__c(self): os.remove(tag_file) expected_outputs = { - 'bar': [{ - 'symbol': 'bar', - 'filename': filename, - 'ex_command': 'void bar()', - 'tag_path': (filename, 'typename', 'void', 'bar'), - 'type': 'f', - 'fields': 'typeref:typename:void', - 'field_keys': ['typeref'], - 'typeref': 'typename:void', - }], - 'foo': [{ - 'symbol': 'foo', - 'filename': filename, - 'ex_command': '#define foo', - 'tag_path': (filename, 'foo'), - 'type': 'd', - 'fields': 'file:', - 'field_keys': ['file'], - 'file': ''}], - 'foobar': [{ - 'symbol': 'foobar', - 'filename': filename, - 'ex_command': '#define foobar', - 'tag_path': (filename, 'foobar'), - 'type': 'd', - 'fields': 'file:', - 'field_keys': ['file'], - 'file': ''}] - } + "bar": [ + { + "symbol": "bar", + "filename": filename, + "ex_command": "void bar()", + "tag_path": (filename, "typename", "void", "bar"), + "type": "f", + "fields": "typeref:typename:void", + "field_keys": ["typeref"], + "typeref": "typename:void", + } + ], + "foo": [ + { + "symbol": "foo", + "filename": filename, + "ex_command": "#define foo", + "tag_path": (filename, "foo"), + "type": "d", + "fields": "file:", + "field_keys": ["file"], + "file": "", + } + ], + "foobar": [ + { + "symbol": "foobar", + "filename": filename, + "ex_command": "#define foobar", + "tag_path": (filename, "foobar"), + "type": "d", + "fields": "file:", + "field_keys": ["file"], + "file": "", + } + ], + } result = ctags.parse_tag_lines(content) @@ -484,5 +529,6 @@ def test_parse_tag_lines__c(self): for key in result: # don't forget - we might have missed something! self.assertEqual(expected_outputs[key], result[key]) -if __name__ == '__main__': + +if __name__ == "__main__": unittest.main() diff --git a/plugins/tests/test_ctagsplugin.py b/plugins/tests/test_ctagsplugin.py index 330db34..3341095 100644 --- a/plugins/tests/test_ctagsplugin.py +++ b/plugins/tests/test_ctagsplugin.py @@ -34,18 +34,20 @@ def build_python_file(self, pwd=None): :returns: Path to a constructed, valid Java source file """ - path = '' + path = "" # the file created here is locked while open, hence we can't delete # similarly, ctags appears to require an extension hence the suffix - with tempfile.NamedTemporaryFile( - delete=False, suffix='.py', dir=pwd) as temp: + with tempfile.NamedTemporaryFile(delete=False, suffix=".py", dir=pwd) as temp: try: path = temp.name # store name for later use - temp.writelines([ - b'def my_definition():\n', - b'\toutput = "Hello, world!"\n', - b'\tprint(output)\n']) + temp.writelines( + [ + b"def my_definition():\n", + b'\toutput = "Hello, world!"\n', + b"\tprint(output)\n", + ] + ) finally: temp.close() @@ -60,27 +62,29 @@ def build_java_file(self, pwd=None): :returns: Path to a constructed, valid Java source file """ - path = '' + path = "" # the file created here is locked while open, hence we can't delete # similarly, ctags appears to require an extension hence the suffix - with tempfile.NamedTemporaryFile( - delete=False, suffix='.java', dir=pwd) as temp: + with tempfile.NamedTemporaryFile(delete=False, suffix=".java", dir=pwd) as temp: try: path = temp.name # store name for later use - temp.writelines([ - b'public class DemoClass {\n', - b'\tpublic static void main(String args[]) {\n', - b'\t\tSystem.out.println("Hello, World");\n', - b'\n', - b'\t\tDemoClass demo = new DemoClass();\n', - b'\t\tSystem.out.printf("Sum %d\n", demo.getSum(5,6));\n', - b'\t}\n', - b'\n', - b'\tprivate int getSum(int a, int b) {\n', - b'\t\treturn (a + b);\n', - b'\t}\n', - b'}\n']) + temp.writelines( + [ + b"public class DemoClass {\n", + b"\tpublic static void main(String args[]) {\n", + b'\t\tSystem.out.println("Hello, World");\n', + b"\n", + b"\t\tDemoClass demo = new DemoClass();\n", + b'\t\tSystem.out.printf("Sum %d\n", demo.getSum(5,6));\n', + b"\t}\n", + b"\n", + b"\tprivate int getSum(int a, int b) {\n", + b"\t\treturn (a + b);\n", + b"\t}\n", + b"}\n", + ] + ) finally: temp.close() @@ -114,32 +118,29 @@ def remove_tmp_files(self, paths): # find_tags_relative_to def test_find_tags_relative_to__find_tags_in_current_directory(self): - tag_file = 'example_tags' + tag_file = "example_tags" current_path = self.build_python_file() tag_file_ = ctags.build_ctags(path=current_path, tag_file=tag_file) # should find tag file in current directory - self.assertEqual( - cmds.find_tags_relative_to(current_path, tag_file), - tag_file_) + self.assertEqual(cmds.find_tags_relative_to(current_path, tag_file), tag_file_) # cleanup self.remove_tmp_files([current_path, tag_file_]) def test_find_tags_relative_to__find_tags_in_parent_directory(self): - tag_file = 'example_tags' + tag_file = "example_tags" parent_path = self.build_python_file() - parent_tag_file = ctags.build_ctags(path=parent_path, - tag_file=tag_file) + parent_tag_file = ctags.build_ctags(path=parent_path, tag_file=tag_file) child_dir = self.make_tmp_directory() child_path = self.build_python_file(pwd=child_dir) # should find tag file in parent directory self.assertEqual( - cmds.find_tags_relative_to(child_path, tag_file), - parent_tag_file) + cmds.find_tags_relative_to(child_path, tag_file), parent_tag_file + ) # cleanup self.remove_tmp_files([parent_path, parent_tag_file]) @@ -148,9 +149,9 @@ def test_find_tags_relative_to__find_tags_in_parent_directory(self): # get_common_ancestor_folder def test_get_common_ancestor_folder__current_folder_open(self): - parent_dir = '/c/users' + parent_dir = "/c/users" - temp = parent_dir + '/example.py' + temp = parent_dir + "/example.py" path = cmds.get_common_ancestor_folder(temp, [parent_dir]) @@ -159,10 +160,10 @@ def test_get_common_ancestor_folder__current_folder_open(self): self.assertEqual(path, parent_dir) def test_get_common_ancestor_folder__single_ancestor_folder_open(self): - parent_dir = '/c/users' - child_dir = parent_dir + '/child' + parent_dir = "/c/users" + child_dir = parent_dir + "/child" - temp = child_dir + '/example.py' + temp = child_dir + "/example.py" path = cmds.get_common_ancestor_folder(temp, [parent_dir]) @@ -171,11 +172,11 @@ def test_get_common_ancestor_folder__single_ancestor_folder_open(self): self.assertEqual(path, parent_dir) def test_get_common_ancestor_folder__single_sibling_folder_open(self): - parent_dir = '/c/users' - child_a_dir = parent_dir + '/child_a' - child_b_dir = parent_dir + '/child_b' + parent_dir = "/c/users" + child_a_dir = parent_dir + "/child_a" + child_b_dir = parent_dir + "/child_b" - temp = child_b_dir + '/example.py' + temp = child_b_dir + "/example.py" path = cmds.get_common_ancestor_folder(temp, [child_a_dir]) @@ -184,11 +185,11 @@ def test_get_common_ancestor_folder__single_sibling_folder_open(self): self.assertEqual(path, parent_dir) def test_get_common_ancestor_folder__single_child_folder_open(self): - parent_dir = '/c/users' - child_dir = parent_dir + '/child' - grandchild_dir = child_dir + '/grandchild' + parent_dir = "/c/users" + child_dir = parent_dir + "/child" + grandchild_dir = child_dir + "/grandchild" - temp = child_dir + '/example.py' + temp = child_dir + "/example.py" # create temporary folders and files path = cmds.get_common_ancestor_folder(temp, [grandchild_dir]) @@ -199,25 +200,26 @@ def test_get_common_ancestor_folder__single_child_folder_open(self): # get_rel_path_to_source def test_get_rel_path_to_source__source_file_in_sibling_directory(self): - temp = '/c/users/temporary_file' - tag_file = '/c/users/tags' + temp = "/c/users/temporary_file" + tag_file = "/c/users/tags" result = cmds.get_rel_path_to_source(temp, tag_file) - relative_path = 'temporary_file' + relative_path = "temporary_file" self.assertEqual(relative_path, result) def test_get_rel_path_to_source__source_file_in_child_directory(self): - temp = '/c/users/folder/temporary_file' - tag_file = '/c/users/tags' + temp = "/c/users/folder/temporary_file" + tag_file = "/c/users/tags" result = cmds.get_rel_path_to_source(temp, tag_file) # handle [windows, unix] paths - relative_paths = ['folder\\temporary_file', 'folder/temporary_file'] + relative_paths = ["folder\\temporary_file", "folder/temporary_file"] self.assertIn(result, relative_paths) -if __name__ == '__main__': + +if __name__ == "__main__": unittest.main() diff --git a/plugins/utils.py b/plugins/utils.py index 4d4ac37..5d969e4 100644 --- a/plugins/utils.py +++ b/plugins/utils.py @@ -29,6 +29,7 @@ def get_setting(key, default=None): """ return get_settings().get(key, default) + setting = get_setting @@ -38,7 +39,7 @@ def concat_re(reList, escape=False, wrapCapture=False): wrapCapture - if true --> adds () around the result regex --> split will keep the splitters in its output array. """ ret = "|".join((re.escape(spl) if escape else spl) for spl in reList) - if (wrapCapture): + if wrapCapture: ret = "(" + ret + ")" return ret @@ -80,12 +81,14 @@ def merge_two_dicts_deep(a, b, path=None): a[key] = b[key] return a + RE_SPECIAL_CHARS = re.compile( - '(\\\\|\\*|\\+|\\?|\\||\\{|\\}|\\[|\\]|\\(|\\)|\\^|\\$|\\.|\\#|\\ )') + "(\\\\|\\*|\\+|\\?|\\||\\{|\\}|\\[|\\]|\\(|\\)|\\^|\\$|\\.|\\#|\\ )" +) def escape_regex(s): - return RE_SPECIAL_CHARS.sub(lambda m: '\\%s' % m.group(1), s) + return RE_SPECIAL_CHARS.sub(lambda m: "\\%s" % m.group(1), s) def get_source(view): @@ -93,8 +96,9 @@ def get_source(view): return the language used in current caret or selection location """ scope_name = view.scope_name( - view.sel()[0].begin()) # ex: 'source.python meta.function-call.python ' - source = re.split(' ', scope_name)[0] # ex: 'source.python' + view.sel()[0].begin() + ) # ex: 'source.python meta.function-call.python ' + source = re.split(" ", scope_name)[0] # ex: 'source.python' return source @@ -103,9 +107,9 @@ def get_lang_setting(source): given source (ex: 'source.python') --> return its language_syntax settings. A language can inherit its settings from another language, overidding as needed. """ - lang = setting('language_syntax').get(source) + lang = setting("language_syntax").get(source) if lang is not None: - base = setting('language_syntax').get(lang.get('inherit')) + base = setting("language_syntax").get(lang.get("inherit")) lang = dict_extend(lang, base) else: lang = {} @@ -114,8 +118,7 @@ def get_lang_setting(source): def compile_filters(view): filters = [] - for selector, regexes in list(setting('filters', {}).items()): - if view.match_selector(view.sel() and view.sel()[0].begin() or 0, - selector): + for selector, regexes in list(setting("filters", {}).items()): + if view.match_selector(view.sel() and view.sel()[0].begin() or 0, selector): filters.append(regexes) return filters