Skip to content

Commit

Permalink
added panel with completion suggestions list, use 4 spaces for indent…
Browse files Browse the repository at this point in the history
…, pep8
  • Loading branch information
Skurihin Aleksandr committed Sep 22, 2011
1 parent 2db3601 commit 614e2b5
Showing 1 changed file with 78 additions and 74 deletions.
152 changes: 78 additions & 74 deletions 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)

0 comments on commit 614e2b5

Please sign in to comment.