class GoPlugin(gedit.Plugin): def __init__(self): gedit.Plugin.__init__(self) self._instances = {} self.views = {} self.icons = {} self._icons_path = self.get_install_dir() + os.sep + self.__module__ + os.sep + "icons" + os.sep self.model = SettingsModel(self) self.model.load() self.update_path() # load completion icons self._load_completion_icons() def activate(self, window): self._instances[window] = GoWindowHelper(self, window) def deactivate(self, window): self._instances[window].deactivate() del self._instances[window] def update_ui(self, window): self._instances[window].update_ui() def update_all_ui(self): for instance in self._instances: self.update_ui(instance) def is_configurable(self): return True def create_configure_dialog(self): return ConfigurationDialog(self).dialog def update_path(self): # make sure $GOBIN is in $PATH if self.model.gobin_path not in os.getenv("PATH", "").split(":"): os.environ["PATH"] += ":" + self.model.gobin_path def _load_completion_icons(self): self.icons['var'] = gtk.gdk.pixbuf_new_from_file(self._icons_path + "var16.png") self.icons['const'] = gtk.gdk.pixbuf_new_from_file(self._icons_path + "const16.png") self.icons['func'] = gtk.gdk.pixbuf_new_from_file(self._icons_path + "func16.png") self.icons['interface'] = gtk.gdk.pixbuf_new_from_file(self._icons_path + "interface16.png") self.icons['package'] = gtk.gdk.pixbuf_new_from_file(self._icons_path + "package16.png") self.icons['struct'] = gtk.gdk.pixbuf_new_from_file(self._icons_path + "struct16.png") self.icons['gopher'] = gtk.gdk.pixbuf_new_from_file(self._icons_path + "gopher16.png")
class ConfigurationDialog(gtk.Dialog): def __init__(self, caller): self._plugin = caller self._model = SettingsModel(self) self._model.load() self.current_source = "" gtk.Dialog.__init__(self, "AutoComplete settings", None, gtk.DIALOG_DESTROY_WITH_PARENT) self.set_resizable(False) # Definitions source_label = gtk.Label("<b>Completion source:</b>") source_label.set_use_markup(True) source_label.set_justify(gtk.JUSTIFY_LEFT) self.mixed_radio = gtk.RadioButton(None, "Mixed") self.all_documents_radio = gtk.RadioButton(self.mixed_radio, "All documents") self.library_radio = gtk.RadioButton(self.mixed_radio, "Library") if self._model.get_source() == "ALL_DOCUMENTS": self.all_documents_radio.set_active(True) if self._model.get_source() == "LIBRARY": self.library_radio.set_active(True) self.mixed_radio.connect("toggled", self.source_change, "MIXED") self.all_documents_radio.connect("toggled", self.source_change, "ALL_DOCUMENTS") self.library_radio.connect("toggled", self.source_change, "LIBRARY") # Positioning option_a_box = gtk.HBox() option_b_box = gtk.HBox() option_c_box = gtk.HBox() option_a_box.pack_start(self.mixed_radio, False, False, 0) option_b_box.pack_start(self.all_documents_radio, False, False, 0) option_c_box.pack_start(self.library_radio, False, False, 0) self.vbox.pack_start(source_label, True, True, 10) self.vbox.pack_start(option_a_box, True, True, 0) self.vbox.pack_start(option_b_box, True, True, 0) self.vbox.pack_start(option_c_box, True, True, 0) # Buttons close_button = self.add_button(gtk.STOCK_CLOSE, gtk.RESPONSE_CLOSE) close_button.grab_default() close_button.connect_object("clicked", gtk.Widget.destroy, self) help_button = self.add_button(gtk.STOCK_HELP, gtk.RESPONSE_HELP) help_button.connect_object("clicked", self.show_help_dialog, None) # Display self.vbox.show_all() self.show() def source_change(self, widget, data=None): if widget.get_active(): self._model.set_source(data) self._model.save() def show_help_dialog(self, gobject): dialog = gtk.Dialog("AutoComplete help") close_button = dialog.add_button(gtk.STOCK_CLOSE, gtk.RESPONSE_CLOSE) close_button.grab_default() close_button.connect_object("clicked", gtk.Widget.destroy, dialog) hbox = gtk.HBox(False, 0) hbox.set_border_width(40) label = gtk.Label("Help still isn't available yet...") hbox.pack_start(label, True, True, 0) dialog.vbox.pack_start(hbox, True, True, 0) dialog.show_all()
class AutoComplete: """Automatically complete words with <Return>""" def __init__(self, plugin, window): self._window = window self._plugin = plugin self._model = SettingsModel(self) self._model.load() self.completion = None self.id_name = 'AutoCompleteID' self.tip = Tip(self._window) self.words = {} self.dictionary_words = [] self.last_typed_line = None self.regex_completion = 0 l_ids = [] for signal in ('tab-added', 'tab-removed'): method = getattr(self, 'on_window_' + signal.replace('-', '_')) l_ids.append(window.connect(signal, method)) window.set_data(self.id_name, l_ids) for view in window.get_views(): self.connect_view(view) for doc in window.get_documents(): self.connect_document(doc) self.scan(doc) def deactivate(self): self.hide_tip() widgets = [self._window] + self._window.get_views() + self._window.get_documents() for widget in widgets: l_ids = widget.get_data(self.id_name) for l_id in l_ids: widget.disconnect(l_id) widget.set_data(self.id_name, None) self.tip = None self.words = {} self._window = None self._plugin = None def update_ui(self): pass def show_tip(self, text, x, y): """Show a completion tip in the main window's coordinates.""" (root_x, root_y) = self._window.get_position() self.tip.move(root_x + x + 48, root_y + y + 48) self.tip.set_text(text) self.tip.show_all() def hide_tip(self): """Hide the completion tip.""" self.tip.hide() self.completion = None def complete(self): """Complete the current word.""" doc = self._window.get_active_document() if self.regex_completion: insert = doc.get_iter_at_mark(doc.get_insert()) start = insert.copy() for i in range(0, self.regex_completion): start.backward_char() doc.delete(start, insert) doc.insert_at_cursor(self.completion) def cancel(self): """Hide the completion tip and return False.""" self.hide_tip() return False def connect_document(self, doc): """Connect to document's signals.""" l_ids = [] for signal in ('end-user-action', 'loaded'): method = getattr(self, 'on_document_' + signal.replace('-', '_')) l_ids.append(doc.connect(signal, method)) doc.set_data(self.id_name, l_ids) def connect_view(self, view): """Connect to view's signals.""" l_ids = [] for signal in ('focus-out-event', 'key-press-event'): method = getattr(self, 'on_view_' + signal.replace('-', '_')) l_ids.append(view.connect(signal, method)) view.set_data(self.id_name, l_ids) def on_document_end_user_action(self, doc): """Scan document for words.""" current_position = doc.get_iter_at_mark(doc.get_insert()) if self.last_typed_line != current_position.get_line(): self.scan(doc) elif current_position.backward_char() and len(doc.get_text(doc.get_start_iter(), doc.get_end_iter(), False)) < 10000: if RE_SIMPLE_WORD_SYNTAX.match(current_position.get_char()): if current_position.backward_char(): if not RE_SIMPLE_WORD_SYNTAX.match(current_position.get_char()): self.scan(doc) self.last_typed_line = current_position.get_line() return def on_document_loaded(self, doc, *args): """Scan document for words.""" self.scan(doc) def on_view_focus_out_event(self, view, event): """Hide the completion tip.""" doc = view.get_buffer() self.scan(doc) self.hide_tip() def len_compare(self, x, y): """This is the comparing function for the alternative words to autocomplete""" x1 = '' for a in x: if a in AUTOCOMPLETE_BREAKS: x1 += str(AUTOCOMPLETE_BREAKS.index(a)) else: x1 += '9' y1 = '' for a in y: if a in AUTOCOMPLETE_BREAKS: y1 += str(AUTOCOMPLETE_BREAKS.index(a)) else: y1 += '9' d = len(y1) - len(x1) if d > 0: for i in range(0, d): x1 += ' ' else: for i in range(0, d): y1 += ' ' if x1 < y1: return -1 elif x1 == y1: return 0 else: return 1 def len_compare___alphaSomething(self, x, y): """This is the comparing function for the alternative words to autocomplete""" x1 = '' for a in x: if a in AUTOCOMPLETE_BREAKS: x1 += chr(ord(' ') - AUTOCOMPLETE_BREAKS.index(a)) else: x1 += a y1 = '' for a in y: if a in AUTOCOMPLETE_BREAKS: y1 += chr(ord(' ') - AUTOCOMPLETE_BREAKS.index(a)) else: y1 += a d = len(y1) - len(x1) if d > 0: for i in range(0, d): x1 += ' ' else: for i in range(0, d): y1 += ' ' if x1 < y1: return -1 elif x1 == y1: return 0 else: return 1 def startswith_filter(self, list_to_filter, reference_item): """Filters the list of words""" list_to_filter_len = len(list_to_filter) if list_to_filter_len <= 1: return [] if len(reference_item) < MIN_CHARACTERS_BEFORE_AUTOCOMPLETE: return [] list_to_filter_start = 0 list_to_filter_end = list_to_filter_len - 1 list_to_filter_search = int(list_to_filter_start + list_to_filter_end) / 2 while True: if (list_to_filter[list_to_filter_search])[0:MIN_CHARACTERS_BEFORE_AUTOCOMPLETE] == reference_item[0:MIN_CHARACTERS_BEFORE_AUTOCOMPLETE]: while list_to_filter_search >= 0 and (list_to_filter[list_to_filter_search])[0:MIN_CHARACTERS_BEFORE_AUTOCOMPLETE] == reference_item[0:MIN_CHARACTERS_BEFORE_AUTOCOMPLETE]: list_to_filter_search -= 1 list_to_filter_search += 1 break elif reference_item[0:MIN_CHARACTERS_BEFORE_AUTOCOMPLETE] > (list_to_filter[list_to_filter_search])[0:MIN_CHARACTERS_BEFORE_AUTOCOMPLETE]: list_to_filter_start = list_to_filter_search list_to_filter_search = int(list_to_filter_start + list_to_filter_end) / 2 if list_to_filter_search == list_to_filter_start: list_to_filter_search += 1 if list_to_filter_search == list_to_filter_len: list_to_filter_search -= 1 break elif list_to_filter_search != list_to_filter_end: list_to_filter_end = list_to_filter_search list_to_filter_search = int(list_to_filter_start + list_to_filter_end) / 2 else: break new_list = list() while list_to_filter_search != list_to_filter_len and reference_item[0:MIN_CHARACTERS_BEFORE_AUTOCOMPLETE] == (list_to_filter[list_to_filter_search])[0:MIN_CHARACTERS_BEFORE_AUTOCOMPLETE]: if list_to_filter[list_to_filter_search].startswith(reference_item) and list_to_filter[list_to_filter_search] != reference_item: new_list.append(list_to_filter[list_to_filter_search]) list_to_filter_search += 1 return new_list def startswith_filter_linear(self, list_to_filter, reference_item): """Filters the list of words""" if not list_to_filter: return [] if len(reference_item) < MIN_CHARACTERS_BEFORE_AUTOCOMPLETE: return [] new_list = list() for item in list_to_filter: if item.startswith(reference_item) and item != reference_item: new_list.append(item) return new_list def simple_contains_filter(self, list_to_filter, reference_item): new_list = list() current_position = doc.get_iter_at_mark(doc.get_insert()) for item in list_to_filter: if not RE_COMPOUND_WORD_SYNTAX.match(item) and reference_item in item: emph_item = item.replace(reference_item, TIP_EMPHASIS[0] + reference_item + TIP_EMPHASIS[1]) new_list.append(emph_item) return new_list def re_contains_filter(self, list_to_filter, reference_item): new_list = list() reference_item_case_insensitive = '' for c in reference_item: if c >= 'a' and c <= 'z' or c >= 'A' and c <= 'Z': reference_item_case_insensitive += '[' + c.lower() + c.upper() + ']' else: reference_item_case_insensitive += c for item in list_to_filter: if not RE_COMPOUND_WORD_SYNTAX.match(item) and re.findall(reference_item_case_insensitive, item): new_list.append(item) return new_list def on_view_key_press_event(self, view, event): """Display a completion or complete the current word.""" if event.state & gtk.gdk.CONTROL_MASK: return self.cancel() if event.state & gtk.gdk.MOD1_MASK and event.string != '/': return self.cancel() key = gtk.gdk.keyval_name(event.keyval) if key == 'Return': if self.completion is None: return self.cancel() self.complete() complete_key_pressed = True else: complete_key_pressed = False if key == 'Down': if self.completion is None: return self.cancel() self.select_alternative('Down') return True if key == 'Up': if self.completion is None: return self.cancel() self.select_alternative('Up') return True doc = view.get_buffer() insert = doc.get_iter_at_mark(doc.get_insert()) if event.keyval >= 128: # non-alphanumeric key if gtk.gdk.keyval_name(event.keyval) == 'BackSpace': doc = view.get_buffer() insert = doc.get_iter_at_mark(doc.get_insert()) insert.backward_char() elif not complete_key_pressed: return self.cancel() selection_iters = view.get_buffer().get_selection_bounds() if selection_iters: regex_word = doc.get_text(selection_iters[0], selection_iters[1], False) else: start = insert.copy() while not start.is_start(): start.backward_char() if re.match("\s", start.get_char()): start.forward_char() break regex_word = doc.get_text(start, insert, False) start = insert.copy() while start.backward_char(): match_list = RE_SIMPLE_WORD_SYNTAX.findall(doc.get_text(start, insert, False)) if len(match_list) == 1 and len(match_list[0]) == len(doc.get_text(start, insert, False)): continue else: start.forward_char() break incomplete_simple_word = doc.get_text(start, insert, False) if not complete_key_pressed: incomplete_simple_word += event.string start_compound = start.copy() match_success = True while match_success == True: match_success = False if start.is_start(): break for i in range(0, MAX_COMPOSITION_TOKEN_SIZE + 1): # +1: need to catch the char before the token start.backward_char() match_list = RE_BEGINNING_OF_COMPOUND_WORD_SYNTAX.findall(doc.get_text(start, start_compound, False)) if len(match_list) == 1 and len(match_list[0]) == len(doc.get_text(start, start_compound, False)): match_success = True break for i in range(0, MAX_COMPOSITION_TOKEN_SIZE + 1): start.forward_char() incomplete_compound_word = doc.get_text(start, insert, False) if not complete_key_pressed: incomplete_compound_word += event.string compound_word_alternatives = self.startswith_filter(self.dictionary_words, incomplete_compound_word) compound_word_alternatives.sort(self.len_compare) compound_word_alternatives = self.aggressive_filter(compound_word_alternatives, incomplete_compound_word) alternatives = compound_word_alternatives incomplete = incomplete_compound_word if not compound_word_alternatives: simple_word_alternatives = self.startswith_filter(self.dictionary_words, incomplete_simple_word) simple_word_alternatives.sort(self.len_compare) simple_word_alternatives = self.aggressive_filter(simple_word_alternatives, incomplete_simple_word) alternatives = simple_word_alternatives incomplete = incomplete_simple_word if event.string == '/' and event.state & gtk.gdk.MOD1_MASK: words_containing_regex = self.re_contains_filter(self.dictionary_words, regex_word) alternatives = words_containing_regex incomplete = '' self.regex_completion = len(regex_word) else: self.regex_completion = 0 self.complete_word = None display_string = '' alternatives_counter = 0 alternatives.sort() for word in alternatives: if not self.complete_word: self.complete_word = word display_string += MARKER else: display_string += SPACES display_string = display_string + word + '\n' alternatives_counter += 1 if alternatives_counter == MAX_SUGGESTIONS: break if gtk.gdk.keyval_name(event.keyval) == 'BackSpace': insert.forward_char() if self.complete_word is None: self.cancel() if complete_key_pressed: return True else: return False self.completion = self.complete_word[len(incomplete):] window = gtk.TEXT_WINDOW_TEXT rect = view.get_iter_location(insert) (x, y) = view.buffer_to_window_coords(window, rect.x, rect.y) (x, y) = view.translate_coordinates(self._window, x, y) self.show_tip(display_string, x, y) if complete_key_pressed: return True else: return False def aggressive_filter(self, initial_alternatives, incomplete): cursor_pos = len(incomplete) if cursor_pos < MIN_CHARACTERS_BEFORE_AUTOCOMPLETE: return [] if len(initial_alternatives) < MAX_SUGGESTIONS_BEFORE_USE_BREAKS: return initial_alternatives filtered_alternatives = [] for item in initial_alternatives: break_pos = len(item) item_end = item[cursor_pos:] for separator in list(AUTOCOMPLETE_BREAKS): if separator in item_end: break_pos = min(break_pos, item_end.index(separator)) if not item[:break_pos + cursor_pos + 1] in filtered_alternatives: filtered_alternatives += [item[:break_pos + cursor_pos + 1]] filtered_alternatives.sort(self.len_compare) return filtered_alternatives def on_window_tab_added(self, window, tab): """Connect the document and view in tab.""" context = tab.get_view().get_pango_context() font_desc = context.get_font_description() self.tip.set_font_description(font_desc) self.connect_document(tab.get_document()) self.connect_view(tab.get_view()) def on_window_tab_removed(self, window, tab): """Remove document's word set.""" doc = tab.get_document() if doc in self.words: self.words.pop(doc) def scan(self, doc, what_to_scan='ALL_WORDS'): """Scan document for new words.""" text = doc.get_text(*doc.get_bounds()) self.words[doc] = frozenset([]) if self._model.get_source() != "LIBRARY": if what_to_scan == 'ALL_WORDS': self.words[doc] = self.words[doc].union(RE_COMPOUND_WORD_SYNTAX.findall(text)) self.words[doc] = self.words[doc].union(RE_SIMPLE_WORD_SYNTAX.findall(text)) elif what_to_scan == 'SIMPLE_WORDS': self.words[doc] = self.words[doc].union(RE_SIMPLE_WORD_SYNTAX.findall(text)) elif what_to_scan == 'COMPOUND_WORDS': self.words[doc] = self.words[doc].union(RE_COMPOUND_WORD_SYNTAX.findall(text)) if self._window.get_active_document() == doc: if self._model.get_source() != "ALL_DOCUMENTS": lang = doc.get_language() if lang != None: lang = lang.get_name().lower() lang_static_words = self._model.get_words(lang) self.words[doc] = self.words[doc].union(lang_static_words) lang_library = self._model.get_language_library(lang) if lang_library: identificators = lang_library["dynamic"]["identificators"] for identificator in identificators: RE_IDENTIFICATOR = re.compile(r'%s'%identificator, re.UNICODE) for m in RE_IDENTIFICATOR.finditer(text): tokens = lang_library["dynamic"]["members"].get(m.group("class")) if tokens: tokens = tokens.split(' ') self.words[doc] = self.words[doc].union(["%s%s%s" % (m.group("instance"), lang_library["dynamic"]["tokenSeparator"], token) for token in tokens]) self.dictionary_words = set([]) for word in self.words.values(): self.dictionary_words.update(word) self.dictionary_words = list(self.dictionary_words) self.dictionary_words.sort() # def refresh_tip_on_complete(self): # """Refresh the alternative word list when <Return> is pressed and a completion is done.""" # # display_string = '' # local_complete_word = self.complete_word # for current_line in (self.tip.get_text() + '\n').splitlines(True): # if current_line.startswith(SPACES + local_complete_word): # if display_string == '': # display_string += current_line.replace(SPACES, MARKER) # self.completion = current_line.strip()[len(local_complete_word):] # self.complete_word = current_line.strip(' \n') # else: # display_string += current_line # if len(display_string) != 0: # self.tip.set_text(display_string.rstrip('\n')) # else: # self.hide_tip() # return True def select_alternative(self, direction=None): """Makes all the necessary modifications when an alternative word is selected from the list.""" display_string = self.tip.get_text() + '\n' previous_line = '' first_line = None marker_moved = False display_lines = display_string.splitlines(True) if len(display_lines) == 1: return True for current_line in display_lines: if first_line is None: first_line = current_line if direction == 'Down': if previous_line == MARKER + self.complete_word + '\n': marker_moved = True display_string = display_string.replace(previous_line, previous_line.replace(MARKER, SPACES)) display_string = display_string.replace(current_line, current_line.replace(SPACES, MARKER)) self.completion = current_line.strip()[len(self.complete_word) - len(self.completion):] self.complete_word = current_line.strip() break if direction == 'Up': if current_line == MARKER + self.complete_word + '\n' and previous_line != '': marker_moved = True display_string = display_string.replace(current_line, current_line.replace(MARKER, SPACES)) display_string = display_string.replace(previous_line, previous_line.replace(SPACES, MARKER)) self.completion = previous_line.strip()[len(self.complete_word) - len(self.completion):] self.complete_word = previous_line.strip() break previous_line = current_line if not marker_moved: if direction == 'Down': display_string = display_string.replace(current_line, current_line.replace(MARKER, SPACES)) display_string = display_string.replace(first_line, first_line.replace(SPACES, MARKER)) self.completion = first_line.strip()[len(self.complete_word) - len(self.completion):] self.complete_word = first_line.strip() if direction == 'Up': display_string = display_string.replace(current_line, current_line.replace(SPACES, MARKER)) display_string = display_string.replace(first_line, first_line.replace(MARKER, SPACES)) self.completion = current_line.strip()[len(self.complete_word) - len(self.completion):] self.complete_word = current_line.strip() self.tip.set_text(display_string.rstrip('\n')) return True
class AutoComplete: """Automatically complete words with <Return>""" def __init__(self, plugin, window): self._window = window self._plugin = plugin self._model = SettingsModel(self) self._model.load() self.completion = None self.id_name = 'AutoCompleteID' self.tip = Tip(self._window) self.words = {} self.dictionary_words = [] self.last_typed_line = None self.regex_completion = 0 l_ids = [] for signal in ('tab-added', 'tab-removed'): method = getattr(self, 'on_window_' + signal.replace('-', '_')) l_ids.append(window.connect(signal, method)) window.set_data(self.id_name, l_ids) for view in window.get_views(): self.connect_view(view) for doc in window.get_documents(): self.connect_document(doc) self.scan(doc) def deactivate(self): self.hide_tip() widgets = [self._window ] + self._window.get_views() + self._window.get_documents() for widget in widgets: l_ids = widget.get_data(self.id_name) for l_id in l_ids: widget.disconnect(l_id) widget.set_data(self.id_name, None) self.tip = None self.words = {} self._window = None self._plugin = None def update_ui(self): pass def show_tip(self, text, x, y): """Show a completion tip in the main window's coordinates.""" (root_x, root_y) = self._window.get_position() self.tip.move(root_x + x + 48, root_y + y + 48) self.tip.set_text(text) self.tip.show_all() def hide_tip(self): """Hide the completion tip.""" self.tip.hide() self.completion = None def complete(self): """Complete the current word.""" doc = self._window.get_active_document() if self.regex_completion: insert = doc.get_iter_at_mark(doc.get_insert()) start = insert.copy() for i in range(0, self.regex_completion): start.backward_char() doc.delete(start, insert) doc.insert_at_cursor(self.completion) def cancel(self): """Hide the completion tip and return False.""" self.hide_tip() return False def connect_document(self, doc): """Connect to document's signals.""" l_ids = [] for signal in ('end-user-action', 'loaded'): method = getattr(self, 'on_document_' + signal.replace('-', '_')) l_ids.append(doc.connect(signal, method)) doc.set_data(self.id_name, l_ids) def connect_view(self, view): """Connect to view's signals.""" l_ids = [] for signal in ('focus-out-event', 'key-press-event'): method = getattr(self, 'on_view_' + signal.replace('-', '_')) l_ids.append(view.connect(signal, method)) view.set_data(self.id_name, l_ids) def on_document_end_user_action(self, doc): """Scan document for words.""" current_position = doc.get_iter_at_mark(doc.get_insert()) if self.last_typed_line != current_position.get_line(): self.scan(doc) elif current_position.backward_char() and len( doc.get_text(doc.get_start_iter(), doc.get_end_iter(), False)) < 10000: if RE_SIMPLE_WORD_SYNTAX.match(current_position.get_char()): if current_position.backward_char(): if not RE_SIMPLE_WORD_SYNTAX.match( current_position.get_char()): self.scan(doc) self.last_typed_line = current_position.get_line() return def on_document_loaded(self, doc, *args): """Scan document for words.""" self.scan(doc) def on_view_focus_out_event(self, view, event): """Hide the completion tip.""" doc = view.get_buffer() self.scan(doc) self.hide_tip() def len_compare(self, x, y): """This is the comparing function for the alternative words to autocomplete""" x1 = '' for a in x: if a in AUTOCOMPLETE_BREAKS: x1 += str(AUTOCOMPLETE_BREAKS.index(a)) else: x1 += '9' y1 = '' for a in y: if a in AUTOCOMPLETE_BREAKS: y1 += str(AUTOCOMPLETE_BREAKS.index(a)) else: y1 += '9' d = len(y1) - len(x1) if d > 0: for i in range(0, d): x1 += ' ' else: for i in range(0, d): y1 += ' ' if x1 < y1: return -1 elif x1 == y1: return 0 else: return 1 def len_compare___alphaSomething(self, x, y): """This is the comparing function for the alternative words to autocomplete""" x1 = '' for a in x: if a in AUTOCOMPLETE_BREAKS: x1 += chr(ord(' ') - AUTOCOMPLETE_BREAKS.index(a)) else: x1 += a y1 = '' for a in y: if a in AUTOCOMPLETE_BREAKS: y1 += chr(ord(' ') - AUTOCOMPLETE_BREAKS.index(a)) else: y1 += a d = len(y1) - len(x1) if d > 0: for i in range(0, d): x1 += ' ' else: for i in range(0, d): y1 += ' ' if x1 < y1: return -1 elif x1 == y1: return 0 else: return 1 def startswith_filter(self, list_to_filter, reference_item): """Filters the list of words""" list_to_filter_len = len(list_to_filter) if list_to_filter_len <= 1: return [] if len(reference_item) < MIN_CHARACTERS_BEFORE_AUTOCOMPLETE: return [] list_to_filter_start = 0 list_to_filter_end = list_to_filter_len - 1 list_to_filter_search = int(list_to_filter_start + list_to_filter_end) / 2 while True: if (list_to_filter[list_to_filter_search] )[0:MIN_CHARACTERS_BEFORE_AUTOCOMPLETE] == reference_item[ 0:MIN_CHARACTERS_BEFORE_AUTOCOMPLETE]: while list_to_filter_search >= 0 and ( list_to_filter[list_to_filter_search] )[0:MIN_CHARACTERS_BEFORE_AUTOCOMPLETE] == reference_item[ 0:MIN_CHARACTERS_BEFORE_AUTOCOMPLETE]: list_to_filter_search -= 1 list_to_filter_search += 1 break elif reference_item[0:MIN_CHARACTERS_BEFORE_AUTOCOMPLETE] > ( list_to_filter[list_to_filter_search] )[0:MIN_CHARACTERS_BEFORE_AUTOCOMPLETE]: list_to_filter_start = list_to_filter_search list_to_filter_search = int(list_to_filter_start + list_to_filter_end) / 2 if list_to_filter_search == list_to_filter_start: list_to_filter_search += 1 if list_to_filter_search == list_to_filter_len: list_to_filter_search -= 1 break elif list_to_filter_search != list_to_filter_end: list_to_filter_end = list_to_filter_search list_to_filter_search = int(list_to_filter_start + list_to_filter_end) / 2 else: break new_list = list() while list_to_filter_search != list_to_filter_len and reference_item[ 0:MIN_CHARACTERS_BEFORE_AUTOCOMPLETE] == ( list_to_filter[list_to_filter_search] )[0:MIN_CHARACTERS_BEFORE_AUTOCOMPLETE]: if list_to_filter[list_to_filter_search].startswith( reference_item ) and list_to_filter[list_to_filter_search] != reference_item: new_list.append(list_to_filter[list_to_filter_search]) list_to_filter_search += 1 return new_list def startswith_filter_linear(self, list_to_filter, reference_item): """Filters the list of words""" if not list_to_filter: return [] if len(reference_item) < MIN_CHARACTERS_BEFORE_AUTOCOMPLETE: return [] new_list = list() for item in list_to_filter: if item.startswith(reference_item) and item != reference_item: new_list.append(item) return new_list def simple_contains_filter(self, list_to_filter, reference_item): new_list = list() current_position = doc.get_iter_at_mark(doc.get_insert()) for item in list_to_filter: if not RE_COMPOUND_WORD_SYNTAX.match( item) and reference_item in item: emph_item = item.replace( reference_item, TIP_EMPHASIS[0] + reference_item + TIP_EMPHASIS[1]) new_list.append(emph_item) return new_list def re_contains_filter(self, list_to_filter, reference_item): new_list = list() reference_item_case_insensitive = '' for c in reference_item: if c >= 'a' and c <= 'z' or c >= 'A' and c <= 'Z': reference_item_case_insensitive += '[' + c.lower() + c.upper( ) + ']' else: reference_item_case_insensitive += c for item in list_to_filter: if not RE_COMPOUND_WORD_SYNTAX.match(item) and re.findall( reference_item_case_insensitive, item): new_list.append(item) return new_list def on_view_key_press_event(self, view, event): """Display a completion or complete the current word.""" if event.state & gtk.gdk.CONTROL_MASK: return self.cancel() if event.state & gtk.gdk.MOD1_MASK and event.string != '/': return self.cancel() key = gtk.gdk.keyval_name(event.keyval) if key == 'Return': if self.completion is None: return self.cancel() self.complete() complete_key_pressed = True else: complete_key_pressed = False if key == 'Down': if self.completion is None: return self.cancel() self.select_alternative('Down') return True if key == 'Up': if self.completion is None: return self.cancel() self.select_alternative('Up') return True doc = view.get_buffer() insert = doc.get_iter_at_mark(doc.get_insert()) if event.keyval >= 128: # non-alphanumeric key if gtk.gdk.keyval_name(event.keyval) == 'BackSpace': doc = view.get_buffer() insert = doc.get_iter_at_mark(doc.get_insert()) insert.backward_char() elif not complete_key_pressed: return self.cancel() selection_iters = view.get_buffer().get_selection_bounds() if selection_iters: regex_word = doc.get_text(selection_iters[0], selection_iters[1], False) else: start = insert.copy() while not start.is_start(): start.backward_char() if re.match("\s", start.get_char()): start.forward_char() break regex_word = doc.get_text(start, insert, False) start = insert.copy() while start.backward_char(): match_list = RE_SIMPLE_WORD_SYNTAX.findall( doc.get_text(start, insert, False)) if len(match_list) == 1 and len(match_list[0]) == len( doc.get_text(start, insert, False)): continue else: start.forward_char() break incomplete_simple_word = doc.get_text(start, insert, False) if not complete_key_pressed: incomplete_simple_word += event.string start_compound = start.copy() match_success = True while match_success == True: match_success = False if start.is_start(): break for i in range(0, MAX_COMPOSITION_TOKEN_SIZE + 1): # +1: need to catch the char before the token start.backward_char() match_list = RE_BEGINNING_OF_COMPOUND_WORD_SYNTAX.findall( doc.get_text(start, start_compound, False)) if len(match_list) == 1 and len(match_list[0]) == len( doc.get_text(start, start_compound, False)): match_success = True break for i in range(0, MAX_COMPOSITION_TOKEN_SIZE + 1): start.forward_char() incomplete_compound_word = doc.get_text(start, insert, False) if not complete_key_pressed: incomplete_compound_word += event.string compound_word_alternatives = self.startswith_filter( self.dictionary_words, incomplete_compound_word) compound_word_alternatives.sort(self.len_compare) compound_word_alternatives = self.aggressive_filter( compound_word_alternatives, incomplete_compound_word) alternatives = compound_word_alternatives incomplete = incomplete_compound_word if not compound_word_alternatives: simple_word_alternatives = self.startswith_filter( self.dictionary_words, incomplete_simple_word) simple_word_alternatives.sort(self.len_compare) simple_word_alternatives = self.aggressive_filter( simple_word_alternatives, incomplete_simple_word) alternatives = simple_word_alternatives incomplete = incomplete_simple_word if event.string == '/' and event.state & gtk.gdk.MOD1_MASK: words_containing_regex = self.re_contains_filter( self.dictionary_words, regex_word) alternatives = words_containing_regex incomplete = '' self.regex_completion = len(regex_word) else: self.regex_completion = 0 self.complete_word = None display_string = '' alternatives_counter = 0 alternatives.sort() for word in alternatives: if not self.complete_word: self.complete_word = word display_string += MARKER else: display_string += SPACES display_string = display_string + word + '\n' alternatives_counter += 1 if alternatives_counter == MAX_SUGGESTIONS: break if gtk.gdk.keyval_name(event.keyval) == 'BackSpace': insert.forward_char() if self.complete_word is None: self.cancel() if complete_key_pressed: return True else: return False self.completion = self.complete_word[len(incomplete):] window = gtk.TEXT_WINDOW_TEXT rect = view.get_iter_location(insert) (x, y) = view.buffer_to_window_coords(window, rect.x, rect.y) (x, y) = view.translate_coordinates(self._window, x, y) self.show_tip(display_string, x, y) if complete_key_pressed: return True else: return False def aggressive_filter(self, initial_alternatives, incomplete): cursor_pos = len(incomplete) if cursor_pos < MIN_CHARACTERS_BEFORE_AUTOCOMPLETE: return [] if len(initial_alternatives) < MAX_SUGGESTIONS_BEFORE_USE_BREAKS: return initial_alternatives filtered_alternatives = [] for item in initial_alternatives: break_pos = len(item) item_end = item[cursor_pos:] for separator in list(AUTOCOMPLETE_BREAKS): if separator in item_end: break_pos = min(break_pos, item_end.index(separator)) if not item[:break_pos + cursor_pos + 1] in filtered_alternatives: filtered_alternatives += [item[:break_pos + cursor_pos + 1]] filtered_alternatives.sort(self.len_compare) return filtered_alternatives def on_window_tab_added(self, window, tab): """Connect the document and view in tab.""" context = tab.get_view().get_pango_context() font_desc = context.get_font_description() self.tip.set_font_description(font_desc) self.connect_document(tab.get_document()) self.connect_view(tab.get_view()) def on_window_tab_removed(self, window, tab): """Remove document's word set.""" doc = tab.get_document() if doc in self.words: self.words.pop(doc) def scan(self, doc, what_to_scan='ALL_WORDS'): """Scan document for new words.""" text = doc.get_text(*doc.get_bounds()) self.words[doc] = frozenset([]) if self._model.get_source() != "LIBRARY": if what_to_scan == 'ALL_WORDS': self.words[doc] = self.words[doc].union( RE_COMPOUND_WORD_SYNTAX.findall(text)) self.words[doc] = self.words[doc].union( RE_SIMPLE_WORD_SYNTAX.findall(text)) elif what_to_scan == 'SIMPLE_WORDS': self.words[doc] = self.words[doc].union( RE_SIMPLE_WORD_SYNTAX.findall(text)) elif what_to_scan == 'COMPOUND_WORDS': self.words[doc] = self.words[doc].union( RE_COMPOUND_WORD_SYNTAX.findall(text)) if self._window.get_active_document() == doc: if self._model.get_source() != "ALL_DOCUMENTS": lang = doc.get_language() if lang != None: lang = lang.get_name().lower() lang_static_words = self._model.get_words(lang) self.words[doc] = self.words[doc].union(lang_static_words) lang_library = self._model.get_language_library(lang) if lang_library: identificators = lang_library["dynamic"][ "identificators"] for identificator in identificators: RE_IDENTIFICATOR = re.compile( r'%s' % identificator, re.UNICODE) for m in RE_IDENTIFICATOR.finditer(text): tokens = lang_library["dynamic"][ "members"].get(m.group("class")) if tokens: tokens = tokens.split(' ') self.words[doc] = self.words[doc].union([ "%s%s%s" % (m.group("instance"), lang_library["dynamic"] ["tokenSeparator"], token) for token in tokens ]) self.dictionary_words = set([]) for word in self.words.values(): self.dictionary_words.update(word) self.dictionary_words = list(self.dictionary_words) self.dictionary_words.sort() # def refresh_tip_on_complete(self): # """Refresh the alternative word list when <Return> is pressed and a completion is done.""" # # display_string = '' # local_complete_word = self.complete_word # for current_line in (self.tip.get_text() + '\n').splitlines(True): # if current_line.startswith(SPACES + local_complete_word): # if display_string == '': # display_string += current_line.replace(SPACES, MARKER) # self.completion = current_line.strip()[len(local_complete_word):] # self.complete_word = current_line.strip(' \n') # else: # display_string += current_line # if len(display_string) != 0: # self.tip.set_text(display_string.rstrip('\n')) # else: # self.hide_tip() # return True def select_alternative(self, direction=None): """Makes all the necessary modifications when an alternative word is selected from the list.""" display_string = self.tip.get_text() + '\n' previous_line = '' first_line = None marker_moved = False display_lines = display_string.splitlines(True) if len(display_lines) == 1: return True for current_line in display_lines: if first_line is None: first_line = current_line if direction == 'Down': if previous_line == MARKER + self.complete_word + '\n': marker_moved = True display_string = display_string.replace( previous_line, previous_line.replace(MARKER, SPACES)) display_string = display_string.replace( current_line, current_line.replace(SPACES, MARKER)) self.completion = current_line.strip( )[len(self.complete_word) - len(self.completion):] self.complete_word = current_line.strip() break if direction == 'Up': if current_line == MARKER + self.complete_word + '\n' and previous_line != '': marker_moved = True display_string = display_string.replace( current_line, current_line.replace(MARKER, SPACES)) display_string = display_string.replace( previous_line, previous_line.replace(SPACES, MARKER)) self.completion = previous_line.strip( )[len(self.complete_word) - len(self.completion):] self.complete_word = previous_line.strip() break previous_line = current_line if not marker_moved: if direction == 'Down': display_string = display_string.replace( current_line, current_line.replace(MARKER, SPACES)) display_string = display_string.replace( first_line, first_line.replace(SPACES, MARKER)) self.completion = first_line.strip()[len(self.complete_word) - len(self.completion):] self.complete_word = first_line.strip() if direction == 'Up': display_string = display_string.replace( current_line, current_line.replace(SPACES, MARKER)) display_string = display_string.replace( first_line, first_line.replace(MARKER, SPACES)) self.completion = current_line.strip( )[len(self.complete_word) - len(self.completion):] self.complete_word = current_line.strip() self.tip.set_text(display_string.rstrip('\n')) return True