[gedit] Use new completion framework with snippets



commit e22651bf0ab657747f6b8a76eefd59266562c188
Author: Jesse van den Kieboom <jesse icecrew nl>
Date:   Sun Oct 4 14:16:51 2009 +0200

    Use new completion framework with snippets

 plugins/snippets/snippets/Completion.py      |  168 ++++++++++++++
 plugins/snippets/snippets/Document.py        |  170 ++++++---------
 plugins/snippets/snippets/LanguageManager.py |   21 ++
 plugins/snippets/snippets/Makefile.am        |    5 +-
 plugins/snippets/snippets/Manager.py         |   17 +--
 plugins/snippets/snippets/Placeholder.py     |   18 +-
 plugins/snippets/snippets/SnippetComplete.py |  315 --------------------------
 7 files changed, 275 insertions(+), 439 deletions(-)
---
diff --git a/plugins/snippets/snippets/Completion.py b/plugins/snippets/snippets/Completion.py
new file mode 100644
index 0000000..8e45eaf
--- /dev/null
+++ b/plugins/snippets/snippets/Completion.py
@@ -0,0 +1,168 @@
+import gtksourceview2 as gsv
+import gobject
+import gedit
+import gtk
+
+from Library import Library
+from LanguageManager import get_language_manager
+from Snippet import Snippet
+
+class Proposal(gobject.GObject, gsv.CompletionProposal):
+        def __init__(self, snippet):
+                gobject.GObject.__init__(self)
+                self._snippet = Snippet(snippet)
+        
+        def snippet(self):
+                return self._snippet.data
+        
+        # Interface implementation
+        def do_get_markup(self):
+                return self._snippet.display()
+        
+        def do_get_info(self):
+                return self._snippet.data['text']
+
+class Provider(gobject.GObject, gsv.CompletionProvider):
+        def __init__(self, name, language_id, handler):
+                gobject.GObject.__init__(self)
+                
+                self.name = name
+                self.info_widget = None
+                self.proposals = []
+                self.language_id = language_id
+                self.handler = handler
+                self.info_widget = None
+                self.mark = None
+                
+                theme = gtk.icon_theme_get_default()
+                w, h = gtk.icon_size_lookup(gtk.ICON_SIZE_MENU)
+
+                self.icon = theme.load_icon(gtk.STOCK_JUSTIFY_LEFT, w, 0)
+        
+        def __del__(self):
+                if self.mark:
+                        self.mark.get_buffer().delete_mark(self.mark)
+        
+        def set_proposals(self, proposals):
+                self.proposals = proposals
+
+        def mark_position(self, it):
+                if not self.mark:
+                        self.mark = it.get_buffer().create_mark(None, it, True)
+                else:
+                        self.mark.get_buffer().move_mark(self.mark, it)
+        
+        def get_word(self, context):
+                it = context.get_iter()
+                
+                if it.starts_word() or it.starts_line() or not it.ends_word():
+                        return None
+                
+                start = it.copy()
+                
+                if start.backward_word_start():
+                        self.mark_position(start)
+                        return start.get_text(it)
+                else:
+                        return None
+        
+        def do_get_start_iter(self, context, proposal):
+                if not self.mark or self.mark.get_deleted():
+                        return None
+                
+                return self.mark.get_buffer().get_iter_at_mark(self.mark)
+                
+        def do_match(self, context):
+                return self.get_word(context) or context.get_default()
+
+        def get_proposals(self, word):
+                if self.proposals:
+                        proposals = self.proposals
+                else:
+                        proposals = Library().get_snippets(None)
+                        
+                        if self.language_id:
+                                proposals += Library().get_snippets(self.language_id)
+
+                # Filter based on the current word
+                if word:
+                        proposals = filter(lambda x: x['tag'].startswith(word), proposals)
+
+                return map(lambda x: Proposal(x), proposals)
+
+        def do_populate(self, context):
+                word = self.get_word(context)
+                
+                if word or context.get_default():
+                        proposals = self.get_proposals(word)
+                else:
+                        proposals = []
+
+                context.add_proposals(self, proposals, True)
+
+        def do_get_name(self):
+                return self.name
+
+        def do_activate_proposal(self, proposal, piter):
+                return self.handler(proposal, piter)
+        
+        def do_get_info_widget(self, proposal):
+                if not self.info_widget:
+                        view = gedit.View(gedit.Document())
+                        manager = get_language_manager()
+
+                        lang = manager.get_language('snippets')
+                        view.get_buffer().set_language(lang)
+                        
+                        sw = gtk.ScrolledWindow()
+                        sw.add(view)
+                        
+                        self.info_view = view
+                        self.info_widget = sw
+                
+                return self.info_widget
+        
+        def do_update_info(self, proposal, info):
+                buf = self.info_view.get_buffer()
+                
+                buf.set_text(proposal.get_info())
+                buf.move_mark(buf.get_insert(), buf.get_start_iter())
+                buf.move_mark(buf.get_selection_bound(), buf.get_start_iter())
+                self.info_view.scroll_to_iter(buf.get_start_iter(), False)
+
+                info.set_sizing(-1, -1, False, False)
+                info.process_resize()
+        
+        def do_get_icon(self):
+                return self.icon
+
+class Defaults(gobject.GObject, gsv.CompletionProvider):
+        def __init__(self, handler):
+                gobject.GObject.__init__(self)
+
+                self.handler = handler
+                self.proposals = []
+        
+        def set_defaults(self, defaults):
+                self.proposals = []
+                
+                for d in defaults:
+                        self.proposals.append(gsv.CompletionItem(d))
+                
+        def do_get_name(self):
+                return ""
+        
+        def do_activate_proposal(self, proposal, piter):
+                return self.handler(proposal, piter)
+        
+        def do_populate(self, context):
+                context.add_proposals(self, self.proposals, True)
+
+        def do_get_activation(self):
+                return gsv.COMPLETION_ACTIVATION_NONE
+
+gobject.type_register(Proposal)
+gobject.type_register(Provider)
+gobject.type_register(Defaults)
+
+# ex:ts=8:et:
diff --git a/plugins/snippets/snippets/Document.py b/plugins/snippets/snippets/Document.py
index 658f954..fbe3583 100644
--- a/plugins/snippets/snippets/Document.py
+++ b/plugins/snippets/snippets/Document.py
@@ -22,11 +22,13 @@ import gtk
 from gtk import gdk
 import gio
 import gedit
+import gtksourceview2 as gsv
+import gobject
 
 from Library import Library
 from Snippet import Snippet
 from Placeholder import *
-from SnippetComplete import SnippetComplete
+import Completion
 
 class Document:
         TAB_KEY_VAL = (gtk.keysyms.Tab, \
@@ -48,6 +50,9 @@ class Document:
                 self.language_id = 0
                 self.timeout_update_id = 0
                 
+                self.provider = Completion.Provider(_('Snippets'), self.language_id, self.on_proposal_activated)
+                self.defaults_provider = Completion.Defaults(self.on_default_activated)
+                
                 # Always have a reference to the global snippets
                 Library().ref(None)
                 self.set_view(view)
@@ -92,7 +97,8 @@ class Document:
                         # Remove signals
                         signals = {self.view: ('key-press-event', 'destroy', 
                                                'notify::editable', 'drag-data-received'),
-                                   buf:       ('notify::language', 'changed', 'cursor-moved', 'insert-text')}
+                                   buf:       ('notify::language', 'changed', 'cursor-moved', 'insert-text'),
+                                   self.view.get_completion(): ('hide',)}
                         
                         for obj, sig in signals.items():
                                 for s in sig:
@@ -101,6 +107,10 @@ class Document:
                         # Remove all active snippets
                         for snippet in list(self.active_snippets):
                                 self.deactivate_snippet(snippet, True)
+                        
+                        completion = self.view.get_completion()
+                        completion.remove_provider(self.provider)
+                        completion.remove_provider(self.defaults_provider)                        
 
                 self.view = view
                 
@@ -117,10 +127,18 @@ class Document:
                         self.connect_signal(view, 'drag-data-received', self.on_drag_data_received)
 
                         self.update_language()
+
+                        completion = view.get_completion()
+                        completion.add_provider(self.provider)
+                        
+                        completion.add_provider(self.defaults_provider)
+                        
+                        self.connect_signal(completion, 'hide', self.on_completion_hide)
                 elif self.language_id != 0:
                         langid = self.language_id
                         
                         self.language_id = None;
+                        self.provider.language_id = self.language_id
                         
                         if self.instance:
                                 self.instance.language_changed(self)
@@ -157,6 +175,7 @@ class Document:
                         Library().unref(langid)
 
                 Library().ref(self.language_id)
+                self.provider.language_id = self.language_id
 
         def accelerator_activate(self, keyval, mod):
                 if not self.view or not self.view.get_editable():
@@ -174,7 +193,8 @@ class Document:
                         self.apply_snippet(snippets[0])
                 else:
                         # Do the fancy completion dialog
-                        return self.show_completion(snippets)
+                        self.provider.set_proposals(snippets)
+                        self.view.show_completion((self,))
 
                 return True
 
@@ -295,6 +315,7 @@ class Document:
 
                 if current:
                         # Signal this placeholder to end action
+                        self.view.get_completion().hide()
                         current.leave()
                         
                         if current.__class__ == PlaceholderEnd:
@@ -307,6 +328,11 @@ class Document:
                         
                         if next.__class__ == PlaceholderEnd:
                                 last = next
+                        elif len(next.defaults) > 1 and next.get_text() == next.default:
+                                self.defaults_provider.set_defaults(next.defaults)
+                                
+                                cm = self.view.get_completion()
+                                cm.show([self.defaults_provider], cm.create_context())
 
                 if last:
                         # This is the end of the placeholder, remove the snippet etc
@@ -515,8 +541,10 @@ class Document:
 
                 return True
 
-        def get_tab_tag(self, buf):
-                end = buf.get_iter_at_mark(buf.get_insert())
+        def get_tab_tag(self, buf, end = None):
+                if not end:
+                        end = buf.get_iter_at_mark(buf.get_insert())
+
                 start = end.copy()
                 
                 word = None
@@ -563,7 +591,11 @@ class Document:
                                 return self.apply_snippet(snippets[0], start, end)
                         else:
                                 # Do the fancy completion dialog
-                                return self.show_completion(snippets)
+                                self.provider.set_proposals(snippets)
+                                cm = self.view.get_completion()
+                                
+                                cm.show([self.provider], cm.create_context())
+                                return True
 
                 return self.skip_to_next_placeholder()
         
@@ -603,87 +635,6 @@ class Document:
                 if len(self.active_snippets) == 0:
                         self.last_snippet_removed()
 
-        # Moves the completion window to a suitable place honoring the hint given
-        # by x and y. It tries to position the window so it's always visible on the
-        # screen.
-        def move_completion_window(self, complete, x, y):
-                MARGIN = 15
-                screen = self.view.get_screen()
-                
-                width = screen.get_width()
-                height = screen.get_height()
-                
-                cw, ch = complete.get_size()
-                
-                if x + cw > width:
-                        x = width - cw - MARGIN
-                elif x < MARGIN:
-                        x = MARGIN
-                
-                if y + ch > height:
-                        y = height - ch - MARGIN
-                elif y < MARGIN:
-                        y = MARGIN
-
-                complete.move(x, y)
-
-        # Show completion, shows a completion dialog in the view.
-        # If preset is not None then a completion dialog is shown with the snippets
-        # in the preset list. Otherwise it will try to find the word preceding the
-        # current cursor position. If such a word is found, it is taken as a 
-        # tab trigger prefix so that only snippets with a tab trigger prefixed with
-        # the word are in the list. If no such word can be found than all snippets
-        # are shown.
-        def show_completion(self, preset = None):
-                buf = self.view.get_buffer()
-                bounds = buf.get_selection_bounds()
-                prefix = None
-                
-                if not bounds and not preset:
-                        # When there is no text selected and no preset present, find the
-                        # prefix
-                        (prefix, start, end) = self.get_tab_tag(buf)
-                
-                if not prefix:
-                        # If there is no prefix, than take the insertion point as the end
-                        end = buf.get_iter_at_mark(buf.get_insert())
-                
-                if not preset or len(preset) == 0:
-                        # There is no preset, find all the global snippets and the language
-                        # specific snippets
-                        
-                        nodes = Library().get_snippets(None)
-                        
-                        if self.language_id:
-                                nodes += Library().get_snippets(self.language_id)
-                        
-                        if prefix and len(prefix) == 1 and not prefix.isalnum():
-                                hasnodes = False
-                                
-                                for node in nodes:
-                                        if node['tag'] and node['tag'].startswith(prefix):
-                                                hasnodes = True
-                                                break
-                                
-                                if not hasnodes:
-                                        prefix = None
-                        
-                        complete = SnippetComplete(nodes, prefix, False)        
-                else:
-                        # There is a preset, so show that preset
-                        complete = SnippetComplete(preset, None, True)
-                
-                complete.connect('snippet-activated', self.on_complete_row_activated)
-                
-                rect = self.view.get_iter_location(end)
-                win = self.view.get_window(gtk.TEXT_WINDOW_TEXT)
-                (x, y) = self.view.buffer_to_window_coords( \
-                                gtk.TEXT_WINDOW_TEXT, rect.x + rect.width, rect.y)
-                (xor, yor) = win.get_origin()
-                
-                self.move_completion_window(complete, x + xor, y + yor)                
-                return complete.run()
-
         def update_snippet_contents(self):
                 self.timeout_update_id = 0
                 
@@ -703,16 +654,6 @@ class Document:
                 self.stop()
                 return
 
-        def on_complete_row_activated(self, complete, snippet):
-                buf = self.view.get_buffer()
-                bounds = buf.get_selection_bounds()
-                
-                if bounds:
-                        self.apply_snippet(snippet.data, None, None)
-                else:
-                        (word, start, end) = self.get_tab_tag(buf)
-                        self.apply_snippet(snippet.data, start, end)
-
         def on_buffer_cursor_moved(self, buf):
                 piter = buf.get_iter_at_mark(buf.get_insert())
 
@@ -806,11 +747,6 @@ class Document:
                                 return self.run_snippet()
                         else:
                                 return self.skip_to_previous_placeholder()
-                elif (event.state & gdk.CONTROL_MASK) and \
-                                not (event.state & gdk.MOD1_MASK) and \
-                                not (event.state & gdk.SHIFT_MASK) and \
-                                event.keyval in self.SPACE_KEY_VAL:
-                        return self.show_completion()
                 elif not library.loaded and \
                                 library.valid_accelerator(event.keyval, event.state):
                         library.ensure_files()
@@ -934,4 +870,34 @@ class Document:
                 lst = gtk.target_list_add_uri_targets((), 0)
                 
                 return self.view.drag_dest_find_target(context, lst)
+        
+        def on_completion_hide(self, completion):
+                self.provider.set_proposals(None)
+
+        def on_proposal_activated(self, proposal, piter):
+                buf = self.view.get_buffer()
+                bounds = buf.get_selection_bounds()
+                
+                if bounds:
+                        self.apply_snippet(proposal.snippet(), None, None)
+                else:
+                        (word, start, end) = self.get_tab_tag(buf, piter)
+                        self.apply_snippet(proposal.snippet(), start, end)
+
+                return True
+        
+        def on_default_activated(self, proposal, piter):
+                buf = self.view.get_buffer()
+                bounds = buf.get_selection_bounds()
+
+                if bounds:
+                        buf.begin_user_action()
+                        buf.delete(bounds[0], bounds[1])
+                        buf.insert(bounds[0], proposal.props.label)
+                        buf.end_user_action()
+
+                        return True
+                else:
+                        return False
+
 # ex:ts=8:et:
diff --git a/plugins/snippets/snippets/LanguageManager.py b/plugins/snippets/snippets/LanguageManager.py
new file mode 100644
index 0000000..9646ef1
--- /dev/null
+++ b/plugins/snippets/snippets/LanguageManager.py
@@ -0,0 +1,21 @@
+import gtksourceview2 as gsv
+import os
+
+from Library import Library
+
+global manager
+manager = None
+
+def get_language_manager():
+        global manager
+        
+        if not manager:
+                dirs = []
+        
+                for d in Library().systemdirs:
+                        dirs.append(os.path.join(d, 'lang'))
+        
+                manager = gsv.LanguageManager()
+                manager.set_search_path(dirs + manager.get_search_path())
+        
+        return manager
diff --git a/plugins/snippets/snippets/Makefile.am b/plugins/snippets/snippets/Makefile.am
index ee5ec20..8f218cb 100644
--- a/plugins/snippets/snippets/Makefile.am
+++ b/plugins/snippets/snippets/Makefile.am
@@ -11,11 +11,12 @@ plugin_PYTHON = \
 	Parser.py \
 	Placeholder.py \
 	Manager.py \
-	SnippetComplete.py \
 	Helper.py \
 	SubstitutionParser.py \
 	Importer.py \
-	Exporter.py
+	Exporter.py \
+	LanguageManager.py \
+	Completion.py
 
 uidir = $(GEDIT_PLUGINS_DATA_DIR)/snippets/ui
 ui_DATA = snippets.ui
diff --git a/plugins/snippets/snippets/Manager.py b/plugins/snippets/snippets/Manager.py
index 6f1fbec..1daa279 100644
--- a/plugins/snippets/snippets/Manager.py
+++ b/plugins/snippets/snippets/Manager.py
@@ -33,6 +33,7 @@ from Library import *
 from Importer import *
 from Exporter import *
 from Document import Document
+from LanguageManager import get_language_manager
 
 class Manager:
         NAME_COLUMN = 0
@@ -114,7 +115,7 @@ class Manager:
                 if not self.model or force_reload:
                         self.model = gtk.TreeStore(str, str, object)
                         self.model.set_sort_column_id(self.SORT_COLUMN, gtk.SORT_ASCENDING)
-                        manager = self.get_language_manager()
+                        manager = get_language_manager()
                         langs = gedit.language_manager_list_languages_sorted(manager, True)
                         
                         piter = self.model.append(None, (_('Global'), '', None))
@@ -282,18 +283,6 @@ class Manager:
                 
                 self.build_dnd()
         
-        def get_language_manager(self):
-                if not self.manager:
-                        dirs = []
-                
-                        for d in Library().systemdirs:
-                                dirs.append(os.path.join(d, 'lang'))
-                
-                        self.manager = gsv.LanguageManager()
-                        self.manager.set_search_path(dirs + self.manager.get_search_path())
-                
-                return self.manager
-                
         def build(self):
                 self.builder = gtk.Builder()
                 self.builder.add_from_file(os.path.join(self.datadir, 'ui', 'snippets.ui'))
@@ -323,7 +312,7 @@ class Manager:
                 image.set_from_stock(gtk.STOCK_REMOVE, gtk.ICON_SIZE_SMALL_TOOLBAR)
 
                 source_view = self['source_view_snippet']
-                manager = self.get_language_manager()
+                manager = get_language_manager()
                 lang = manager.get_language('snippets')
 
                 if lang:
diff --git a/plugins/snippets/snippets/Placeholder.py b/plugins/snippets/snippets/Placeholder.py
index a294982..7b1656e 100644
--- a/plugins/snippets/snippets/Placeholder.py
+++ b/plugins/snippets/snippets/Placeholder.py
@@ -57,18 +57,24 @@ class Placeholder:
                 self.mark_gravity = [True, False]
 
         def set_default(self, defaults):
+                self.default = None
+                self.defaults = []
+
                 if not defaults:
-                        self.default = None
                         return
 
                 for d in defaults:
-                        d = self.expand_environment(d)
+                        dm = self.expand_environment(d)
                         
-                        if d != '':
-                                self.default = d
-                                return
+                        if dm:
+                                self.defaults.append(dm)
+
+                                if not self.default:
+                                        self.default = dm
+                                
+                                if dm != d:
+                                        break
 
-                self.default = None
         
         def literal(self, s):
                 return repr(s)



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]