From 614e2b557cce495e3cc91d160cd004d899f372f7 Mon Sep 17 00:00:00 2001 From: Skurihin Aleksandr Date: Thu, 22 Sep 2011 04:29:53 +0400 Subject: [PATCH] added panel with completion suggestions list, use 4 spaces for indent, pep8 --- alternative_autocomplete.py | 152 ++++++++++++++++++------------------ 1 file changed, 78 insertions(+), 74 deletions(-) diff --git a/alternative_autocomplete.py b/alternative_autocomplete.py index 01a5203..5da5900 100644 --- a/alternative_autocomplete.py +++ b/alternative_autocomplete.py @@ -1,89 +1,93 @@ import sublime import sublime_plugin import re +from contextlib import contextmanager + +#TODO: add caching mechanizm +#Add unite all completion functionality +#last modified: 22.9.2011 by ask + + +@contextmanager +def edit(view): + """Context manager start and finish undo stage""" + edit_sequence = view.begin_edit() + yield edit_sequence + view.end_edit(edit_sequence) + def uniq(list): - seen = set() - return [value for value in list if value not in seen and not seen.add(value)] + seen = set() + return [value for value in list if value not in seen and not seen.add(value)] + def fuzzy_match(query, word): - query, word = query.lower(), word.lower() - qi, wi = 0, 0 - while qi < len(query): - wi = word.find(query[qi], wi) - if wi == -1: - return False - qi += 1 - return True + query, word = query.lower(), word.lower() + qi, wi = 0, 0 + while qi < len(query): + wi = word.find(query[qi], wi) + if wi == -1: + return False + qi += 1 + return True + class Candidate: - def __init__(self, distance, text): - self.distance = distance - self.text = text + def __init__(self, distance, text): + self.distance = distance + self.text = text - def __hash__(self): - return hash(self.text) + def __hash__(self): + return hash(self.text) + + def __cmp__(self, other): + return cmp(self.text, other.text) - def __cmp__(self, other): - return cmp(self.text, other.text) class AlternativeAutocompleteCommand(sublime_plugin.TextCommand): - candidates = [] - previous_completion = None - - def run(self, edit, cycle = 'next', default = ''): - self.edit = edit - self.insert_completion(self.view.sel()[0].b, - self.view.substr(sublime.Region(0, self.view.size())), cycle, default) - - def insert_completion(self, position, text, cycle, default): - prefix_match = re.search(r'([\w\d_]+)\Z', text[0:position], re.M | re.U) - if prefix_match: - current_word_match = re.search(r'^([\w\d_]+)', text[prefix_match.start(1):], re.M | re.U) - if current_word_match: - current_word = current_word_match.group(1) - else: - current_word = None - - prefix = prefix_match.group(1) - if self.previous_completion is None or prefix != self.previous_completion: - self.previous_completion = None - self.candidates = self.find_candidates(prefix, position, text) - if current_word in self.candidates: - self.candidates.remove(current_word) - if self.candidates: - edit = self.view.begin_edit() - self.view.erase(edit, sublime.Region(prefix_match.start(1), prefix_match.end(1))) - if self.previous_completion is None: - completion = self.candidates[0] - else: - if cycle == 'previous': - direction = -1 - else: - direction = 1 - completion = self.candidates[(self.candidates.index(self.previous_completion) + direction) % len(self.candidates)] - self.view.insert(edit, prefix_match.start(1), completion) - self.view.end_edit(edit) - self.previous_completion = completion - else: - if default and default != '': - self.view.insert(self.edit, position, default) - - def find_candidates(self, prefix, position, text): candidates = [] - regex = re.compile(r'[^\w\d](' + re.escape(prefix) + r'[\w\d]+)', re.M | re.U) - for match in regex.finditer(text): - candidates.append(Candidate(abs(match.start(1) - position), match.group(1))) - if len(candidates) > 100: - break - if candidates: - candidates.sort(lambda a, b: cmp(a.distance, b.distance)) - candidates = [candidate.text for candidate in candidates] - else: - word_regex = re.compile(r'\b' + re.escape(prefix[0:1]) + r'[\w\d]+', re.M | re.U | re.I) - words = word_regex.findall(text) - candidates = [word for word in words if word != prefix and fuzzy_match(prefix, word)] - if candidates: - candidates.append(prefix) - return uniq(candidates) + previous_completion = None + + def run(self, edit, cycle='next', default=''): + + self.edit = edit + self.show_panel(self.on_choise_insert) + + def suggestions(self, position, text): + prefix_match = re.search(r'([\w\d_]+)\Z', text[0:position], re.M | re.U) + if prefix_match: + prefix = prefix_match.group(1) + self.p = prefix_match + candidates = self.find_candidates(prefix, position, text) + return candidates + + def show_panel(self, callback): + self.suggestions_list = self.suggestions(self.view.sel()[0].b, + self.view.substr(sublime.Region(0, self.view.size()))) + self.view.window().show_quick_panel(self.suggestions_list, callback) + + def on_choise_insert(self, choise): + if choise == -1: + return + with edit(self.view) as e: + choise = self.suggestions_list[choise] + self.view.replace(e, sublime.Region(self.p.start(1), self.p.end(1)), choise) + + def find_candidates(self, prefix, position, text): + candidates = [] + regex = re.compile(r'[^\w\d](' + re.escape(prefix) + r'[\w\d]+)', re.M | re.U) + for match in regex.finditer(text): + candidates.append(Candidate(abs(match.start(1) - position), match.group(1))) + if len(candidates) > 100: + break + if candidates: + candidates.sort(lambda a, b: cmp(a.distance, b.distance)) + candidates = [candidate.text for candidate in candidates] + else: + word_regex = re.compile(r'\b' + re.escape(prefix[0:1]) + r'[\w\d]+', re.M | re.U | re.I) + words = word_regex.findall(text) + candidates = [word for word in words if word != prefix and fuzzy_match(prefix, word)] + if candidates: + candidates.append(prefix) + return uniq(candidates)