[gedit] Use new completion framework with snippets
- From: Jesse van den Kieboom <jessevdk src gnome org>
- To: svn-commits-list gnome org
- Cc:
- Subject: [gedit] Use new completion framework with snippets
- Date: Sun, 4 Oct 2009 12:18:15 +0000 (UTC)
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]