From 6895034bba7626fb1dab792f56babaf022ca5913 Mon Sep 17 00:00:00 2001 From: Patrick Monnerat Date: Mon, 27 May 2019 18:16:18 +0200 Subject: snippets plugin: change code for Python 2 & 3 compatibility --- plugins/snippets/snippets/Completion.py | 23 +- plugins/snippets/snippets/Document.py | 151 +++--- plugins/snippets/snippets/Exporter.py | 4 +- plugins/snippets/snippets/Helper.py | 32 +- plugins/snippets/snippets/Importer.py | 2 +- plugins/snippets/snippets/LanguageManager.py | 3 +- plugins/snippets/snippets/Library.py | 43 +- plugins/snippets/snippets/Manager.py | 76 +-- plugins/snippets/snippets/Parser.py | 2 +- plugins/snippets/snippets/Placeholder.py | 85 ++-- plugins/snippets/snippets/Snippet.py | 46 +- plugins/snippets/snippets/WindowHelper.py | 4 +- plugins/snippets/snippets/__init__.py | 19 +- plugins/snippets/snippets/snippets.ui | 679 +++++++++++---------------- 14 files changed, 528 insertions(+), 641 deletions(-) (limited to 'plugins/snippets') diff --git a/plugins/snippets/snippets/Completion.py b/plugins/snippets/snippets/Completion.py index a860d210..ad0ad754 100644 --- a/plugins/snippets/snippets/Completion.py +++ b/plugins/snippets/snippets/Completion.py @@ -1,14 +1,14 @@ from gi.repository import GObject, Gtk, GtkSource, Pluma -from Library import Library -from LanguageManager import get_language_manager -from Snippet import Snippet +from .Library import Library +from .LanguageManager import get_language_manager +from .Snippet import Snippet class Proposal(GObject.Object, GtkSource.CompletionProposal): __gtype_name__ = "PlumaSnippetsProposal" def __init__(self, snippet): - GObject.Object.__init__(self) + super(Proposal, self).__init__() self._snippet = Snippet(snippet) def snippet(self): @@ -25,7 +25,7 @@ class Provider(GObject.Object, GtkSource.CompletionProvider): __gtype_name__ = "PlumaSnippetsProvider" def __init__(self, name, language_id, handler): - GObject.Object.__init__(self) + super(Provider, self).__init__() self.name = name self.info_widget = None @@ -38,7 +38,10 @@ class Provider(GObject.Object, GtkSource.CompletionProvider): theme = Gtk.IconTheme.get_default() f, w, h = Gtk.icon_size_lookup(Gtk.IconSize.MENU) - self.icon = theme.load_icon(Gtk.STOCK_JUSTIFY_LEFT, w, 0) + try: + self.icon = theme.load_icon("format-justify-left", w, 0) + except: + self.icon = None def __del__(self): if self.mark: @@ -89,9 +92,9 @@ class Provider(GObject.Object, GtkSource.CompletionProvider): # Filter based on the current word if word: - proposals = filter(lambda x: x['tag'].startswith(word), proposals) + proposals = (x for x in proposals if x['tag'].startswith(word)) - return map(lambda x: Proposal(x), proposals) + return [Proposal(x) for x in proposals] def do_populate(self, context): proposals = self.get_proposals(self.get_word(context)) @@ -113,6 +116,10 @@ class Provider(GObject.Object, GtkSource.CompletionProvider): sw = Gtk.ScrolledWindow() sw.add(view) + sw.show_all() + + # Fixed size + sw.set_size_request(300, 200) self.info_view = view self.info_widget = sw diff --git a/plugins/snippets/snippets/Document.py b/plugins/snippets/snippets/Document.py index f1cc0654..f40186b4 100644 --- a/plugins/snippets/snippets/Document.py +++ b/plugins/snippets/snippets/Document.py @@ -18,12 +18,12 @@ import os import re -from gi.repository import GLib, Gio, Gdk, Gtk, GtkSource, Pluma +from gi.repository import GLib, Gio, Gdk, Gtk, Pluma -from Library import Library -from Snippet import Snippet -from Placeholder import * -import Completion +from .Library import Library +from .Snippet import Snippet +from .Placeholder import * +from . import Completion class DynamicSnippet(dict): def __init__(self, text): @@ -31,9 +31,7 @@ class DynamicSnippet(dict): self.valid = True class Document: - TAB_KEY_VAL = (Gdk.KEY_Tab, \ - Gdk.KEY_ISO_Left_Tab) - SPACE_KEY_VAL = (Gdk.KEY_space,) + TAB_KEY_VAL = ('Tab', 'ISO_Left_Tab') def __init__(self, instance, view): self.view = None @@ -109,7 +107,7 @@ class Document: self.deactivate_snippet(snippet, True) completion = self.view.get_completion() - if completion: + if completion and self.provider in completion.get_providers(): completion.remove_provider(self.provider) self.view = view @@ -182,8 +180,6 @@ class Document: snippets = Library().from_accelerator(accelerator, \ self.language_id) - snippets_debug('Accel!') - if len(snippets) == 0: return False elif len(snippets) == 1: @@ -193,6 +189,7 @@ class Document: provider = Completion.Provider(_('Snippets'), self.language_id, self.on_proposal_activated) provider.set_proposals(snippets) + cm = self.view.get_completion() cm.show([provider], cm.create_context(None)) return True @@ -266,7 +263,6 @@ class Document: # Find the nearest placeholder if nearest(piter, begin, end, found): - foundIndex = index found = placeholder # Find the current placeholder @@ -276,7 +272,7 @@ class Document: currentIndex = index current = placeholder - if current and current != found and \ + if current and found and current != found and \ (current.begin_iter().compare(found.begin_iter()) == 0 or \ current.end_iter().compare(found.begin_iter()) == 0) and \ self.active_placeholder and \ @@ -363,57 +359,60 @@ class Document: def env_get_current_word(self, buf): start, end = buffer_word_boundary(buf) - return buf.get_text(start, end, False) def env_get_current_line(self, buf): start, end = buffer_line_boundary(buf) - return buf.get_text(start, end, False) def env_get_current_line_number(self, buf): start, end = buffer_line_boundary(buf) return str(start.get_line() + 1) - def env_get_document_uri(self, buf): - location = buf.get_location() - + def location_uri_for_env(self, location): if location: return location.get_uri() - else: - return '' - - def env_get_document_name(self, buf): - location = buf.get_location() + return '' + def location_name_for_env(self, location): if location: return location.get_basename() - else: - return '' - - def env_get_document_scheme(self, buf): - location = buf.get_location() + return '' + def location_scheme_for_env(self, location): if location: return location.get_uri_scheme() - else: - return '' - - def env_get_document_path(self, buf): - location = buf.get_location() + return '' + def location_path_for_env(self, location): if location: return location.get_path() - else: - return '' - - def env_get_document_dir(self, buf): - location = buf.get_location() + return '' + def location_dir_for_env(self, location): if location: - return location.get_parent().get_path() or '' - else: - return '' + parent = location.get_parent() + + if parent and parent.has_uri_scheme('file'): + return parent.get_path() or '' + + return '' + + def env_add_for_location(self, environ, location, prefix): + parts = { + 'URI': self.location_uri_for_env, + 'NAME': self.location_name_for_env, + 'SCHEME': self.location_scheme_for_env, + 'PATH': self.location_path_for_env, + 'DIR': self.location_dir_for_env, + } + + for k in parts: + v = parts[k](location) + key = prefix + '_' + k + environ[key] = str(v) + + return environ def env_get_document_type(self, buf): typ = buf.get_mime_type() @@ -451,25 +450,30 @@ class Document: return ' '.join(documents_path) - def update_environment(self): + def get_environment(self): buf = self.view.get_buffer() + environ = {} + + for k in os.environ: + # Get the original environment + v = os.environ[k] + environ[k] = v - variables = {'PLUMA_SELECTED_TEXT': self.env_get_selected_text, + variables = { + 'PLUMA_SELECTED_TEXT': self.env_get_selected_text, 'PLUMA_CURRENT_WORD': self.env_get_current_word, 'PLUMA_CURRENT_LINE': self.env_get_current_line, 'PLUMA_CURRENT_LINE_NUMBER': self.env_get_current_line_number, - 'PLUMA_CURRENT_DOCUMENT_URI': self.env_get_document_uri, - 'PLUMA_CURRENT_DOCUMENT_NAME': self.env_get_document_name, - 'PLUMA_CURRENT_DOCUMENT_SCHEME': self.env_get_document_scheme, - 'PLUMA_CURRENT_DOCUMENT_PATH': self.env_get_document_path, - 'PLUMA_CURRENT_DOCUMENT_DIR': self.env_get_document_dir, 'PLUMA_CURRENT_DOCUMENT_TYPE': self.env_get_document_type, 'PLUMA_DOCUMENTS_URI': self.env_get_documents_uri, 'PLUMA_DOCUMENTS_PATH': self.env_get_documents_path, } for var in variables: - os.environ[var] = variables[var](buf) + environ[var] = variables[var](buf) + + self.env_add_for_location(environ, buf.get_location(), 'PLUMA_CURRENT_DOCUMENT') + return environ def uses_current_word(self, snippet): matches = re.findall('(\\\\*)\\$PLUMA_CURRENT_WORD', snippet['text']) @@ -489,12 +493,19 @@ class Document: return False - def apply_snippet(self, snippet, start = None, end = None): + def apply_snippet(self, snippet, start = None, end = None, environ = {}): if not snippet.valid: return False + # Set environmental variables + env = self.get_environment() + + if environ: + for k in environ: + env[k] = environ[k] + buf = self.view.get_buffer() - s = Snippet(snippet) + s = Snippet(snippet, env) if not start: start = buf.get_iter_at_mark(buf.get_insert()) @@ -513,9 +524,6 @@ class Document: # it will be removed start, end = buffer_line_boundary(buf) - # Set environmental variables - self.update_environment() - # You know, we could be in an end placeholder (current, next) = self.next_placeholder() if current and current.__class__ == PlaceholderEnd: @@ -527,8 +535,6 @@ class Document: buf.delete(start, end) # Insert the snippet - holders = len(self.placeholders) - if len(self.active_snippets) == 0: self.first_snippet_inserted() @@ -536,7 +542,7 @@ class Document: self.active_snippets.append(sn) # Put cursor at first tab placeholder - keys = filter(lambda x: x > 0, sn.placeholders.keys()) + keys = [x for x in sn.placeholders.keys() if x > 0] if len(keys) == 0: if 0 in sn.placeholders: @@ -637,7 +643,6 @@ class Document: return True def deactivate_snippet(self, snippet, force = False): - buf = self.view.get_buffer() remove = [] ordered_remove = [] @@ -792,10 +797,11 @@ class Document: library = Library() state = event.get_state() + keyname = Gdk.keyval_name(event.keyval) if not (state & Gdk.ModifierType.CONTROL_MASK) and \ not (state & Gdk.ModifierType.MOD1_MASK) and \ - event.keyval in self.TAB_KEY_VAL: + keyname in self.TAB_KEY_VAL: if not state & Gdk.ModifierType.SHIFT_MASK: return self.run_snippet() else: @@ -868,20 +874,9 @@ class Document: pathname = '' dirname = '' ruri = '' + environ = {'PLUMA_DROP_DOCUMENT_TYPE': mime} - if Pluma.utils_uri_has_file_scheme(uri): - pathname = gfile.get_path() - dirname = gfile.get_parent().get_path() - - name = os.path.basename(uri) - scheme = gfile.get_uri_scheme() - - os.environ['PLUMA_DROP_DOCUMENT_URI'] = uri - os.environ['PLUMA_DROP_DOCUMENT_NAME'] = name - os.environ['PLUMA_DROP_DOCUMENT_SCHEME'] = scheme - os.environ['PLUMA_DROP_DOCUMENT_PATH'] = pathname - os.environ['PLUMA_DROP_DOCUMENT_DIR'] = dirname - os.environ['PLUMA_DROP_DOCUMENT_TYPE'] = mime + self.env_add_for_location(environ, gfile, 'PLUMA_DROP_DOCUMENT') buf = self.view.get_buffer() location = buf.get_location() @@ -890,7 +885,7 @@ class Document: relpath = self.relative_path(ruri, uri, mime) - os.environ['PLUMA_DROP_DOCUMENT_RELATIVE_PATH'] = relpath + environ['PLUMA_DROP_DOCUMENT_RELATIVE_PATH'] = relpath mark = buf.get_mark('gtk_drag_target') @@ -898,7 +893,7 @@ class Document: mark = buf.get_insert() piter = buf.get_iter_at_mark(mark) - self.apply_snippet(snippet, piter, piter) + self.apply_snippet(snippet, piter, piter, environ) def in_bounds(self, x, y): rect = self.view.get_visible_rect() @@ -907,6 +902,9 @@ class Document: return not (x < rect.x or x > rect.x + rect.width or y < rect.y or y > rect.y + rect.height) def on_drag_data_received(self, view, context, x, y, data, info, timestamp): + if not self.view.get_editable(): + return + uris = drop_get_uris(data) if not uris: return @@ -944,6 +942,9 @@ class Document: return self.view.drag_dest_find_target(context, lst) def on_proposal_activated(self, proposal, piter): + if not self.view.get_editable(): + return False + buf = self.view.get_buffer() bounds = buf.get_selection_bounds() @@ -1048,8 +1049,6 @@ class Document: if isinstance(placeholder, PlaceholderEnd): return - buf = self.view.get_buffer() - col = self.view.get_style_context().get_color(Gtk.StateFlags.INSENSITIVE) col.alpha = 0.5 Gdk.cairo_set_source_rgba(ctx, col) diff --git a/plugins/snippets/snippets/Exporter.py b/plugins/snippets/snippets/Exporter.py index 850c3a4a..713077f5 100644 --- a/plugins/snippets/snippets/Exporter.py +++ b/plugins/snippets/snippets/Exporter.py @@ -3,9 +3,9 @@ import tempfile import sys import shutil -from Library import * +from .Library import * import xml.etree.ElementTree as et -from Helper import * +from .Helper import * class Exporter: def __init__(self, filename, snippets): diff --git a/plugins/snippets/snippets/Helper.py b/plugins/snippets/snippets/Helper.py index 6d440d03..c1f1c35d 100644 --- a/plugins/snippets/snippets/Helper.py +++ b/plugins/snippets/snippets/Helper.py @@ -15,10 +15,10 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -import string from xml.sax import saxutils -from xml.etree.ElementTree import * +import xml.etree.ElementTree as et import re +import codecs from gi.repository import Gtk @@ -93,7 +93,7 @@ def write_xml(node, f, cdata_nodes=()): assert node is not None if not hasattr(f, "write"): - f = open(f, "wb") + f = codecs.open(f, "wb", encoding="utf-8") # Encoding f.write("\n") @@ -107,27 +107,27 @@ def _write_node(node, file, cdata_nodes=(), indent=0): # write XML to file tag = node.tag - if node is Comment: - _write_indent(file, "\n" % saxutils.escape(node.text.encode('utf-8')), indent) - elif node is ProcessingInstruction: - _write_indent(file, "\n" % saxutils.escape(node.text.encode('utf-8')), indent) + if node is et.Comment: + _write_indent(file, "\n" % saxutils.escape(node.text), indent) + elif node is et.ProcessingInstruction: + _write_indent(file, "\n" % saxutils.escape(node.text), indent) else: items = node.items() if items or node.text or len(node): - _write_indent(file, "<" + tag.encode('utf-8'), indent) + _write_indent(file, "<" + tag, indent) if items: items.sort() # lexical order for k, v in items: - file.write(" %s=%s" % (k.encode('utf-8'), saxutils.quoteattr(v.encode('utf-8')))) + file.write(" %s=%s" % (k, saxutils.quoteattr(v))) if node.text or len(node): file.write(">") if node.text and node.text.strip() != "": if tag in cdata_nodes: file.write(_cdata(node.text)) else: - file.write(saxutils.escape(node.text.encode('utf-8'))) + file.write(saxutils.escape(node.text)) else: file.write("\n") @@ -135,19 +135,17 @@ def _write_node(node, file, cdata_nodes=(), indent=0): _write_node(n, file, cdata_nodes, indent + 1) if not len(node): - file.write("\n") + file.write("\n") else: - _write_indent(file, "\n", \ - indent) + _write_indent(file, "\n", indent) else: file.write(" />\n") if node.tail and node.tail.strip() != "": - file.write(saxutils.escape(node.tail.encode('utf-8'))) + file.write(saxutils.escape(node.tail)) -def _cdata(text, replace=string.replace): - text = text.encode('utf-8') - return '', ']]]]>') + ']]>' +def _cdata(text): + return '', ']]]]>') + ']]>' def buffer_word_boundary(buf): iter = buf.get_iter_at_mark(buf.get_insert()) diff --git a/plugins/snippets/snippets/Importer.py b/plugins/snippets/snippets/Importer.py index c1d211e2..83c520c1 100644 --- a/plugins/snippets/snippets/Importer.py +++ b/plugins/snippets/snippets/Importer.py @@ -3,7 +3,7 @@ import tempfile import sys import shutil -from Library import * +from .Library import * class Importer: def __init__(self, filename): diff --git a/plugins/snippets/snippets/LanguageManager.py b/plugins/snippets/snippets/LanguageManager.py index e738333e..d962dcf7 100644 --- a/plugins/snippets/snippets/LanguageManager.py +++ b/plugins/snippets/snippets/LanguageManager.py @@ -1,7 +1,7 @@ import os from gi.repository import GtkSource -from Library import Library +from .Library import Library global manager manager = None @@ -19,4 +19,5 @@ def get_language_manager(): manager.set_search_path(dirs + manager.get_search_path()) return manager + # ex:ts=4:et: diff --git a/plugins/snippets/snippets/Library.py b/plugins/snippets/snippets/Library.py index f1520822..d8ae219e 100644 --- a/plugins/snippets/snippets/Library.py +++ b/plugins/snippets/snippets/Library.py @@ -20,11 +20,12 @@ import weakref import sys import tempfile import re +import codecs from gi.repository import Gdk, Gtk import xml.etree.ElementTree as et -from Helper import * +from .Helper import * class NamespacedId: def __init__(self, namespace, id): @@ -453,28 +454,38 @@ class SnippetsSystemFile: lambda node: elements.append((node, True)), \ lambda node: elements.append((node, False))) - parser = et.XMLTreeBuilder(target=builder) + self.ok = True + parser = et.XMLParser(target=builder) self.insnippet = False try: - f = open(self.path, "r") + f = codecs.open(self.path, "r", encoding='utf-8') + except IOError: + self.ok = False + return - while True: + while self.ok: + try: data = f.read(readsize) + except IOError: + self.ok = False + break - if not data: - break + if not data: + break + try: parser.feed(data) + except Exception: + self.ok = False + break - for element in elements: - yield element + for element in elements: + yield element - del elements[:] + del elements[:] - f.close() - except IOError: - self.ok = False + f.close() def load(self): if not self.ok: @@ -531,6 +542,8 @@ class SnippetsUserFile(SnippetsSystemFile): SnippetsSystemFile.__init__(self, path) self.tainted = False self.need_id = False + self.modifier = False + self.root = None def _set_root(self, element): SnippetsSystemFile._set_root(self, element) @@ -611,7 +624,7 @@ class SnippetsUserFile(SnippetsSystemFile): try: if not os.path.isdir(path): - os.makedirs(path, 0755) + os.makedirs(path, 0o755) except OSError: # TODO: this is bad... sys.stderr.write("Error in making dirs\n") @@ -929,8 +942,8 @@ class Library(Singleton): def valid_accelerator(self, keyval, mod): mod &= Gtk.accelerator_get_default_mod_mask() - return (mod and (Gdk.keyval_to_unicode(keyval) or \ - keyval in range(Gdk.KEY_F1, Gdk.KEY_F12 + 1))) + return mod and (Gdk.keyval_to_unicode(keyval) or \ + re.match('^F(?:1[012]?|[2-9])$', Gdk.keyval_name(keyval))) def valid_tab_trigger(self, trigger): if not trigger: diff --git a/plugins/snippets/snippets/Manager.py b/plugins/snippets/snippets/Manager.py index 9760fa7c..71ada38e 100644 --- a/plugins/snippets/snippets/Manager.py +++ b/plugins/snippets/snippets/Manager.py @@ -21,13 +21,13 @@ import shutil from gi.repository import GObject, Gio, Gdk, Gtk, GtkSource, Pluma -from Snippet import Snippet -from Helper import * -from Library import * -from Importer import * -from Exporter import * -from Document import Document -from LanguageManager import get_language_manager +from .Snippet import Snippet +from .Helper import * +from .Library import * +from .Importer import * +from .Exporter import * +from .Document import Document +from .LanguageManager import get_language_manager class Manager: NAME_COLUMN = 0 @@ -42,7 +42,7 @@ class Manager: dragging = False dnd_target_list = [Gtk.TargetEntry.new('text/uri-list', 0, TARGET_URI)] - def __init__(self, datadir): + def __init__(self, datadir, window=None): self.datadir = datadir self.snippet = None self.dlg = None @@ -52,7 +52,7 @@ class Manager: self.default_size = None self.key_press_id = 0 - self.run() + self.run(window) def get_language_snippets(self, path, name = None): library = Library() @@ -159,9 +159,9 @@ class Manager: snippet = model.get_value(iter, self.SNIPPET_COLUMN) if snippet and not snippet.valid: - cell.set_property('stock-id', Gtk.STOCK_DIALOG_ERROR) + cell.set_property('icon-name', 'dialog-error') else: - cell.set_property('stock-id', None) + cell.set_property('icon-name', None) cell.set_property('xalign', 1.0) @@ -300,9 +300,6 @@ class Manager: self.build_tree_view() self.build_model() - image = self['image_remove'] - image.set_from_stock(Gtk.STOCK_REMOVE, Gtk.IconSize.SMALL_TOOLBAR) - source_view = self['source_view_snippet'] manager = get_language_manager() lang = manager.get_language('snippets') @@ -391,15 +388,15 @@ class Manager: if not (override ^ remove) or system: button_remove.set_sensitive(False) - image_remove.set_from_stock(Gtk.STOCK_DELETE, Gtk.IconSize.BUTTON) + image_remove.set_from_icon_name("edit-delete", Gtk.IconSize.BUTTON) else: button_remove.set_sensitive(True) if override: - image_remove.set_from_stock(Gtk.STOCK_UNDO, Gtk.IconSize.BUTTON) + image_remove.set_from_icon_name("edit-undo", Gtk.IconSize.BUTTON) tooltip = _('Revert selected snippet') else: - image_remove.set_from_stock(Gtk.STOCK_DELETE, Gtk.IconSize.BUTTON) + image_remove.set_from_icon_name("edit-delete", Gtk.IconSize.BUTTON) tooltip = _('Delete selected snippet') button_remove.set_tooltip_text(tooltip) @@ -427,12 +424,14 @@ class Manager: return self.snippet_changed(piter) - def run(self): + def run(self, window=None): if not self.dlg: self.build() + self.dlg.set_transient_for(window) self.dlg.show() else: self.build_model() + self.dlg.set_transient_for(window) self.dlg.present() def snippet_from_iter(self, model, piter): @@ -611,7 +610,7 @@ class Manager: self.default_size = [alloc.width, alloc.height] if resp == Gtk.ResponseType.HELP: - Pluma.help_display(self, 'pluma', 'pluma-snippets-plugin') + Pluma.help_display(self.dlg, 'pluma', 'pluma-snippets-plugin') return self.dlg.destroy() @@ -668,7 +667,7 @@ class Manager: if text and not Library().valid_tab_trigger(text): img = self['image_tab_trigger'] - img.set_from_stock(Gtk.STOCK_DIALOG_ERROR, Gtk.IconSize.BUTTON) + img.set_from_icon_name("dialog-error", Gtk.IconSize.BUTTON) img.show() #self['hbox_tab_trigger'].set_spacing(3) @@ -790,10 +789,11 @@ class Manager: self.import_snippets(f) def on_button_import_snippets_clicked(self, button): - dlg = Gtk.FileChooserDialog(parent=self.dlg, title=_("Import snippets"), - action=Gtk.FileChooserAction.OPEN, - buttons=(Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL, - Gtk.STOCK_OPEN, Gtk.ResponseType.OK)) + dlg = Gtk.FileChooserDialog(title=_("Import snippets"), + parent=self.dlg, + action=Gtk.FileChooserAction.OPEN) + self._add_button(dlg, _('_Cancel'), Gtk.ResponseType.CANCEL, "process-stop") + self._add_button(dlg, _("_Open"), Gtk.ResponseType.OK, "document-open") dlg.add_filter(self.file_filter(_('All supported archives'), ('*.gz','*.bz2','*.tar', '*.xml'))) dlg.add_filter(self.file_filter(_('Gzip compressed archive'), ('*.tar.gz',))) @@ -875,10 +875,11 @@ class Manager: return False if not filename: - dlg = Gtk.FileChooserDialog(parent=self.dlg, title=_('Export snippets'), - action=Gtk.FileChooserAction.SAVE, - buttons=(Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL, - Gtk.STOCK_SAVE, Gtk.ResponseType.OK)) + dlg = Gtk.FileChooserDialog(title=_('Export snippets'), + parent=self.dlg, + action=Gtk.FileChooserAction.SAVE) + self._add_button(dlg, _('_Cancel'), Gtk.ResponseType.CANCEL, "process-stop") + self._add_button(dlg, _("_Save"), Gtk.ResponseType.OK, "document-save") dlg._export_snippets = export_snippets dlg.add_filter(self.file_filter(_('All supported archives'), ('*.gz','*.bz2','*.tar'))) @@ -913,10 +914,11 @@ class Manager: else: systemsnippets.append(snippet) - dlg = Gtk.FileChooserDialog(parent=self.dlg, title=_('Export snippets'), - action=Gtk.FileChooserAction.SAVE, - buttons=(Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL, - Gtk.STOCK_SAVE, Gtk.ResponseType.OK)) + dlg = Gtk.FileChooserDialog(title=_('Export snippets'), + parent=self.dlg, + action=Gtk.FileChooserAction.SAVE) + self._add_button(dlg, _('_Cancel'), Gtk.ResponseType.CANCEL, "process-stop") + self._add_button(dlg, _("_Save"), Gtk.ResponseType.OK, "document-save") dlg._export_snippets = snippets @@ -1145,4 +1147,14 @@ class Manager: context.finish(True, False, timestamp) entry.stop_emission('drag_data_received') + + @staticmethod + def _add_button(dialog, label, response, icon=None): + button = dialog.add_button(label, response) + if icon: + image = Gtk.Image.new_from_icon_name(icon, Gtk.IconSize.BUTTON) + button.set_image(image) + button.set_property("always-show-image", True) + return button + # ex:ts=4:et: diff --git a/plugins/snippets/snippets/Parser.py b/plugins/snippets/snippets/Parser.py index 280ce0c1..f2000438 100644 --- a/plugins/snippets/snippets/Parser.py +++ b/plugins/snippets/snippets/Parser.py @@ -18,7 +18,7 @@ import os import re import sys -from SubstitutionParser import SubstitutionParser +from .SubstitutionParser import SubstitutionParser class Token: def __init__(self, klass, data): diff --git a/plugins/snippets/snippets/Placeholder.py b/plugins/snippets/snippets/Placeholder.py index 9edf099e..050fda67 100644 --- a/plugins/snippets/snippets/Placeholder.py +++ b/plugins/snippets/snippets/Placeholder.py @@ -24,12 +24,12 @@ import locale import subprocess from gi.repository import GObject, GLib, Gtk -from SubstitutionParser import SubstitutionParser -from Helper import * +from .SubstitutionParser import SubstitutionParser +from .Helper import * # These are places in a view where the cursor can go and do things class Placeholder: - def __init__(self, view, tabstop, defaults, begin): + def __init__(self, view, tabstop, environ, defaults, begin): self.ok = True self.done = False self.buf = view.get_buffer() @@ -38,6 +38,7 @@ class Placeholder: self.mirrors = [] self.leave_mirrors = [] self.tabstop = tabstop + self.environ = environ self.set_default(defaults) self.prev_contents = self.default self.set_mark_gravity() @@ -49,6 +50,9 @@ class Placeholder: self.end = None + def get_environ(self): + return self.environ + def __str__(self): return '%s (%s)' % (str(self.__class__), str(self.default)) @@ -81,10 +85,12 @@ class Placeholder: return s def re_environment(self, m): - if m.group(1) or not m.group(2) in os.environ: + env = self.get_environ() + + if m.group(1) or not m.group(2) in env: return '$' + m.group(2) else: - return self.format_environment(os.environ[m.group(2)]) + return self.format_environment(env[m.group(2)]) def expand_environment(self, text): if not text: @@ -214,8 +220,8 @@ class Placeholder: # This is an placeholder which inserts a mirror of another Placeholder class PlaceholderMirror(Placeholder): - def __init__(self, view, tabstop, begin): - Placeholder.__init__(self, view, -1, None, begin) + def __init__(self, view, tabstop, environ, begin): + Placeholder.__init__(self, view, -1, environ, None, begin) self.mirror_stop = tabstop def update(self, mirror): @@ -237,8 +243,8 @@ class PlaceholderMirror(Placeholder): # This placeholder indicates the end of a snippet class PlaceholderEnd(Placeholder): - def __init__(self, view, begin, default): - Placeholder.__init__(self, view, 0, default, begin) + def __init__(self, view, environ, begin, default): + Placeholder.__init__(self, view, 0, environ, default, begin) def run_last(self, placeholders): Placeholder.run_last(self, placeholders) @@ -264,8 +270,8 @@ class PlaceholderEnd(Placeholder): # This placeholder is used to expand a command with embedded mirrors class PlaceholderExpand(Placeholder): - def __init__(self, view, tabstop, begin, s): - Placeholder.__init__(self, view, tabstop, None, begin) + def __init__(self, view, tabstop, environ, begin, s): + Placeholder.__init__(self, view, tabstop, environ, None, begin) self.mirror_text = {0: ''} self.timeout_id = None @@ -359,8 +365,6 @@ class PlaceholderExpand(Placeholder): return ret def update(self, mirror): - text = None - if mirror: self.mirror_text[mirror.tabstop] = mirror.get_text() @@ -379,8 +383,8 @@ class PlaceholderExpand(Placeholder): # The shell placeholder executes commands in a subshell class PlaceholderShell(PlaceholderExpand): - def __init__(self, view, tabstop, begin, s): - PlaceholderExpand.__init__(self, view, tabstop, begin, s) + def __init__(self, view, tabstop, environ, begin, s): + PlaceholderExpand.__init__(self, view, tabstop, environ, begin, s) self.shell = None self.remove_me = False @@ -412,7 +416,7 @@ class PlaceholderShell(PlaceholderExpand): self.close_shell() self.remove_timeout() - self.set_text(str.join('', self.shell_output).rstrip('\n')) + self.set_text(''.join(self.shell_output).rstrip('\n')) if self.default == None: self.default = self.get_text() @@ -423,19 +427,24 @@ class PlaceholderShell(PlaceholderExpand): def process_cb(self, source, condition): if condition & GObject.IO_IN: - line = source.readline() + while True: + line = source.readline() - if len(line) > 0: - try: - line = unicode(line, 'utf-8') - except: - line = unicode(line, locale.getdefaultlocale()[1], - 'replace') + if len(line) <= 0: + break - self.shell_output += line - self.install_timeout() + if isinstance(line, bytes): + try: + line = line.decode('utf-8') + except UnicodeDecodeError: + line = line.decode(locale.getdefaultlocale()[1], + errors='replace') - return True + self.shell_output += line + self.install_timeout() + + if not (condition & GObject.IO_HUP): + return True self.process_close() return False @@ -456,7 +465,7 @@ class PlaceholderShell(PlaceholderExpand): popen_args = { 'cwd' : None, 'shell': True, - 'env' : os.environ, + 'env': self.get_environ(), 'stdout': subprocess.PIPE } @@ -491,8 +500,8 @@ class TimeoutError(Exception): # The python placeholder evaluates commands in python class PlaceholderEval(PlaceholderExpand): - def __init__(self, view, tabstop, refs, begin, s, namespace): - PlaceholderExpand.__init__(self, view, tabstop, begin, s) + def __init__(self, view, tabstop, environ, refs, begin, s, namespace): + PlaceholderExpand.__init__(self, view, tabstop, environ, begin, s) self.fdread = 0 self.remove_me = False @@ -529,7 +538,7 @@ class PlaceholderEval(PlaceholderExpand): return hasattr(signal, 'SIGALRM') def timeout_cb(self, signum = 0, frame = 0): - raise TimeoutError, "Operation timed out (>2 seconds)" + raise TimeoutError("Operation timed out (>2 seconds)") def install_timeout(self): if not self.timeout_supported(): @@ -568,7 +577,7 @@ class PlaceholderEval(PlaceholderExpand): del self.namespace['process_snippet'] try: - exec text in self.namespace + exec(text, self.namespace) except: traceback.print_exc() @@ -593,7 +602,7 @@ class PlaceholderEval(PlaceholderExpand): 'time, execution aborted.') % self.command) return False - except Exception, detail: + except Exception as detail: self.remove_timeout() message_dialog(None, Gtk.MessageType.ERROR, @@ -612,8 +621,8 @@ class PlaceholderEval(PlaceholderExpand): # Regular expression placeholder class PlaceholderRegex(PlaceholderExpand): - def __init__(self, view, tabstop, begin, inp, pattern, substitution, modifiers): - PlaceholderExpand.__init__(self, view, tabstop, begin, '') + def __init__(self, view, tabstop, environ, begin, inp, pattern, substitution, modifiers): + PlaceholderExpand.__init__(self, view, tabstop, environ, begin, '') self.instant_update = True self.inp = inp @@ -652,10 +661,12 @@ class PlaceholderRegex(PlaceholderExpand): return re.escape(s) def get_input(self): + env = self.get_environ() + if isinstance(self.inp, int): return self.mirror_text[self.inp] - elif self.inp in os.environ: - return os.environ[self.inp] + elif self.inp in env: + return env[self.inp] else: return '' @@ -672,7 +683,7 @@ class PlaceholderRegex(PlaceholderExpand): # Try to compile pattern try: regex = re.compile(pattern, self.modifiers) - except re.error, message: + except re.error as message: sys.stderr.write('Could not compile regular expression: %s\n%s\n' % (pattern, message)) return False diff --git a/plugins/snippets/snippets/Snippet.py b/plugins/snippets/snippets/Snippet.py index 192b036a..91e63803 100644 --- a/plugins/snippets/snippets/Snippet.py +++ b/plugins/snippets/snippets/Snippet.py @@ -16,11 +16,12 @@ # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA import os +import six from gi.repository import Gio, Gtk -from Placeholder import * -from Parser import Parser, Token -from Helper import * +from .Placeholder import * +from .Parser import Parser, Token +from .Helper import * class EvalUtilities: def __init__(self, view=None): @@ -87,8 +88,8 @@ class EvalUtilities: for col in range(0, len(items[row]) - 1): item = items[row][col] - result += item + ("\t" * ((maxlen[col] - \ - self._real_len(item, tablen)) / tablen)) + result += item + ("\t" * int(((maxlen[col] - \ + self._real_len(item, tablen)) / tablen))) result += items[row][len(items[row]) - 1] @@ -98,8 +99,9 @@ class EvalUtilities: return result class Snippet: - def __init__(self, data): + def __init__(self, data, environ = {}): self.data = data + self.environ = environ def __getitem__(self, prop): return self.data[prop] @@ -132,7 +134,7 @@ class Snippet: if not detail: return nm else: - return nm + ' (' + markup_escape(str.join(', ', detail)) + \ + return nm + ' (' + markup_escape(", ".join(detail)) + \ ')' def _add_placeholder(self, placeholder): @@ -149,14 +151,17 @@ class Snippet: def _insert_text(self, text): # Insert text keeping indentation in mind - indented = unicode.join('\n' + unicode(self._indent), spaces_instead_of_tabs(self._view, text).split('\n')) + indented = (six.u('\n') + self._indent).join(spaces_instead_of_tabs(self._view, text).split('\n')) self._view.get_buffer().insert(self._insert_iter(), indented) def _insert_iter(self): return self._view.get_buffer().get_iter_at_mark(self._insert_mark) def _create_environment(self, data): - val = ((data in os.environ) and os.environ[data]) or '' + if data in self.environ: + val = self.environ[data] + else: + val = '' # Get all the current indentation all_indent = compute_indentation(self._view, self._insert_iter()) @@ -165,7 +170,7 @@ class Snippet: indent = all_indent[len(self._indent):] # Keep indentation - return unicode.join('\n' + unicode(indent), val.split('\n')) + return (six.u('\n') + indent).join(val.split('\n')) def _create_placeholder(self, data): tabstop = data['tabstop'] @@ -173,25 +178,25 @@ class Snippet: if tabstop == 0: # End placeholder - return PlaceholderEnd(self._view, begin, data['default']) + return PlaceholderEnd(self._view, self.environ, begin, data['default']) elif tabstop in self.placeholders: # Mirror placeholder - return PlaceholderMirror(self._view, tabstop, begin) + return PlaceholderMirror(self._view, tabstop, self.environ, begin) else: # Default placeholder - return Placeholder(self._view, tabstop, data['default'], begin) + return Placeholder(self._view, tabstop, self.environ, data['default'], begin) def _create_shell(self, data): begin = self._insert_iter() - return PlaceholderShell(self._view, data['tabstop'], begin, data['contents']) + return PlaceholderShell(self._view, data['tabstop'], self.environ, begin, data['contents']) def _create_eval(self, data): begin = self._insert_iter() - return PlaceholderEval(self._view, data['tabstop'], data['dependencies'], begin, data['contents'], self._utils.namespace) + return PlaceholderEval(self._view, data['tabstop'], self.environ, data['dependencies'], begin, data['contents'], self._utils.namespace) def _create_regex(self, data): begin = self._insert_iter() - return PlaceholderRegex(self._view, data['tabstop'], begin, data['input'], data['pattern'], data['substitution'], data['modifiers']) + return PlaceholderRegex(self._view, data['tabstop'], self.environ, begin, data['input'], data['pattern'], data['substitution'], data['modifiers']) def _create_text(self, data): return data @@ -239,11 +244,11 @@ class Snippet: 'eval': self._create_eval, 'regex': self._create_regex, 'text': self._create_text}[token.klass](token.data) - except: + except KeyError: sys.stderr.write('Token class not supported: %s\n' % token.klass) continue - if isinstance(val, basestring): + if isinstance(val, six.string_types): # Insert text self._insert_text(val) else: @@ -252,7 +257,7 @@ class Snippet: # Create end placeholder if there isn't one yet if 0 not in self.placeholders: - self.placeholders[0] = PlaceholderEnd(self._view, self.end_iter(), None) + self.placeholders[0] = PlaceholderEnd(self._view, self.environ, self.end_iter(), None) self.plugin_data.ordered_placeholders.append(self.placeholders[0]) # Make sure run_last is ran for all placeholders and remove any @@ -317,8 +322,7 @@ class Snippet: # So now all of the snippet is in the buffer, we have all our # placeholders right here, what's next, put all marks in the # plugin_data.marks - k = self.placeholders.keys() - k.sort(reverse=True) + k = sorted(self.placeholders.keys(), reverse=True) plugin_data.placeholders.insert(last_index, self.placeholders[0]) last_iter = self.placeholders[0].end_iter() diff --git a/plugins/snippets/snippets/WindowHelper.py b/plugins/snippets/snippets/WindowHelper.py index 296ff039..44ac558d 100644 --- a/plugins/snippets/snippets/WindowHelper.py +++ b/plugins/snippets/snippets/WindowHelper.py @@ -21,8 +21,8 @@ import gettext from gi.repository import GObject, Gtk, Pluma -from Document import Document -from Library import Library +from .Document import Document +from .Library import Library class WindowHelper: def __init__(self, plugin): diff --git a/plugins/snippets/snippets/__init__.py b/plugins/snippets/snippets/__init__.py index 86424069..ada586c2 100644 --- a/plugins/snippets/snippets/__init__.py +++ b/plugins/snippets/snippets/__init__.py @@ -18,9 +18,9 @@ import os from gi.repository import GObject, GLib, Gtk, Peas, Pluma -from WindowHelper import WindowHelper -from Library import Library -from Manager import Manager +from .WindowHelper import WindowHelper +from .Library import Library +from .Manager import Manager class SnippetsPlugin(GObject.Object, Peas.Activatable): __gtype_name__ = "SnippetsPlugin" @@ -53,7 +53,7 @@ class SnippetsPlugin(GObject.Object, Peas.Activatable): library = Library() library.add_accelerator_callback(self.accelerator_activated) - snippetsdir = os.path.join(GLib.get_user_config_dir(), '/pluma/snippets') + snippetsdir = os.path.join(GLib.get_user_config_dir(), 'pluma/snippets') library.set_dirs(snippetsdir, self.system_dirs()) self._helper = WindowHelper(self) @@ -72,15 +72,12 @@ class SnippetsPlugin(GObject.Object, Peas.Activatable): self._helper.update() def create_configure_dialog(self): - if not self.dlg: - self.dlg = Manager(self.plugin_info.get_data_dir()) - else: - self.dlg.run() - window = Pluma.App.get_default().get_active_window() - if window: - self.dlg.dlg.set_transient_for(window) + if not self.dlg: + self.dlg = Manager(self.plugin_info.get_data_dir(), window) + else: + self.dlg.run(window) return self.dlg.dlg diff --git a/plugins/snippets/snippets/snippets.ui b/plugins/snippets/snippets/snippets.ui index 6fcaf854..833aa026 100644 --- a/plugins/snippets/snippets/snippets.ui +++ b/plugins/snippets/snippets/snippets.ui @@ -1,8 +1,11 @@ - + + + + @@ -32,303 +35,249 @@ - - True - + False Snippets Manager - GTK_WINDOW_TOPLEVEL - GTK_WIN_POS_NONE - False 750 500 - True True - True + dialog True - False - GDK_WINDOW_TYPE_HINT_DIALOG - GDK_GRAVITY_NORTH_WEST - True - False - - + + + + + - + True - False - 0 + False + vertical - + True - GTK_BUTTONBOX_END + False + True + end - + + gtk-help True - True True - gtk-close + True + False True - GTK_RELIEF_NORMAL - True + + True + True + 0 + - + + gtk-close True - True True - gtk-help + True + False True - GTK_RELIEF_NORMAL - True + + True + True + end + 1 + - 0 False True - GTK_PACK_END + end + 0 - - 6 + True True 275 - - 230 + True - False - 6 - - - True - _Snippets: - True - False - GTK_JUSTIFY_LEFT - False - False - 0 - 0.5 - 0 - 0 - tree_view_snippets - PANGO_ELLIPSIZE_NONE - -1 - False - 0 - - - 0 - False - False - - + False + True + True + vertical + 6 True True - GTK_POLICY_AUTOMATIC - GTK_POLICY_AUTOMATIC - GTK_SHADOW_IN - GTK_CORNER_TOP_LEFT + True + True + in True True False - False - False - True - False - False - False - - + + + + + - 0 - True - True + 0 + 1 - + True - False - 6 + False + start + _Snippets: + True + tree_view_snippets + + + 0 + 0 + + + + + True + False + True + 6 True - Create new snippet - True True - GTK_RELIEF_NORMAL - True - + True + False + Create new snippet + True - gtk-new - 4 - 0.5 - 0.5 - 0 - 0 + False + document-new - 0 - False - False + 0 + 0 True - Import snippets - True True - GTK_RELIEF_NORMAL - True - + True + False + Import snippets + True - gtk-open - 4 - 0.5 - 0.5 - 0 - 0 + False + document-open - 0 - False - False + 1 + 0 True - Export selected snippets - True True - GTK_RELIEF_NORMAL - True - + True + False + Export selected snippets + True - gtk-save - 4 - 0.5 - 0.5 - 0 - 0 + False + document-save - 0 - False - False + 2 + 0 True False - Delete selected snippet - True True - GTK_RELIEF_NORMAL - True - + True + False + Delete selected snippet + end + True + True - gtk-delete - 4 - 0.5 - 0.5 - 0 - 0 + False + edit-delete - 0 - False - False - GTK_PACK_END + 3 + 0 - 0 - False - False + 0 + 2 - False False + True - + True - False - 12 + False + True + True + vertical + 12 - + True - False - 6 - - - True - _Edit: - True - False - GTK_JUSTIFY_LEFT - False - False - 0 - 0.5 - 0 - 0 - PANGO_ELLIPSIZE_NONE - -1 - False - 0 - - - 0 - False - False - - + False + True + True + vertical + 6 True True - GTK_POLICY_AUTOMATIC - GTK_POLICY_AUTOMATIC - GTK_SHADOW_IN - GTK_CORNER_TOP_LEFT + True + True + in source_buffer @@ -346,302 +295,198 @@ - 0 - True - True + 0 + 1 + + + + + True + False + start + True + _Edit: + True + + + 0 + 0 - 0 - True - True + 0 + 0 - + True - False - 6 + False + True + vertical + 6 True + False + start + True Activation - False True - GTK_JUSTIFY_LEFT - False - False - 0 - 0.5 - 0 - 0 - PANGO_ELLIPSIZE_NONE - -1 - False - 0 - + - 0 - False - False + 0 + 0 - + True - False - 0 + False + 12 + True + 6 + 6 - + True - - False - False - GTK_JUSTIFY_LEFT - False - False - 0.5 - 0.5 - 0 - 0 - PANGO_ELLIPSIZE_NONE - -1 - False - 0 + False + True + model1 + True + 0 + + + True + + - 0 - False - False + 1 + 2 + 2 - + True - 3 - 2 - False - 6 - 6 - - - True - _Tab trigger: - True - False - GTK_JUSTIFY_LEFT - False - False - 0 - 0.5 - 0 - 0 - entry_tab_trigger - PANGO_ELLIPSIZE_NONE - -1 - False - 0 - - - 0 - 1 - 0 - 1 - fill - - - - - - True - - - True - False - Single word the snippet is activated with after pressing Tab - True - True - True - 0 - - True - * - False - - - - - True - 0 - - - - - False - - - False - 1 - 3 - - - - - 1 - 2 - 0 - 1 - - - - - - True - False - Shortcut key with which the snippet is activated - True - False - True - 0 - - True - * - False - - - - - - 1 - 2 - 1 - 2 - - - - - - True - S_hortcut key: - True - False - GTK_JUSTIFY_LEFT - False - False - 0 - 0.5 - 0 - 0 - entry_accelerator - PANGO_ELLIPSIZE_NONE - -1 - False - 0 - - - 0 - 1 - 1 - 2 - fill - - - - - - True - _Drop targets: - True - False - GTK_JUSTIFY_LEFT - False - False - 0 - 0.5 - 0 - 0 - entry_accelerator - PANGO_ELLIPSIZE_NONE - -1 - False - 0 - - - 0 - 1 - 2 - 3 - fill - - - - - - True - False - True - True - True - model1 - - - - 0 - - - - - 1 - 2 - 2 - 3 - fill - fill - - + False + start + _Drop targets: + True + + + 0 + 2 + + + + + True + False + True + Shortcut key with which the snippet is activated + True + False + * + + + - 0 - True - True + 1 + 1 + 2 + + + + + True + False + start + S_hortcut key: + True + + + 0 + 1 + + + + + True + False + start + _Tab trigger: + True + + + 0 + 0 + + + + + False + + + 2 + 0 + + + + + True + False + True + Single word the snippet is activated with after pressing Tab + True + * + + + + + 1 + 0 - 0 - True - True + 0 + 1 - 0 - False - False + 0 + 1 - True True + True - 0 True True + 1 - closebutton1 button1 + closebutton1 + + True + -- cgit v1.2.1