13184 lines
472 KiB
Diff
13184 lines
472 KiB
Diff
From ec7ec9934d6f80f99474ebeca16bce7b52fe78a1 Mon Sep 17 00:00:00 2001
|
|
From: Patrick Monnerat <patrick@monnerat.net>
|
|
Date: Fri, 10 May 2019 16:26:39 +0200
|
|
Subject: [PATCH 1/6] Reindent all Python sources to ts=4. Strip trailing
|
|
spaces.
|
|
|
|
---
|
|
plugins/externaltools/tools/__init__.py | 22 +-
|
|
plugins/externaltools/tools/capture.py | 18 +-
|
|
plugins/externaltools/tools/functions.py | 44 +-
|
|
plugins/externaltools/tools/library.py | 18 +-
|
|
plugins/externaltools/tools/linkparsing.py | 2 +-
|
|
plugins/externaltools/tools/manager.py | 272 +--
|
|
plugins/externaltools/tools/outputpanel.py | 6 +-
|
|
.../pythonconsole/pythonconsole/__init__.py | 2 +-
|
|
plugins/quickopen/quickopen/__init__.py | 36 +-
|
|
plugins/quickopen/quickopen/popup.py | 877 ++++---
|
|
plugins/quickopen/quickopen/virtualdirs.py | 84 +-
|
|
plugins/quickopen/quickopen/windowhelper.py | 218 +-
|
|
plugins/snippets/snippets/Completion.py | 304 +--
|
|
plugins/snippets/snippets/Document.py | 1994 ++++++++-------
|
|
plugins/snippets/snippets/Exporter.py | 176 +-
|
|
plugins/snippets/snippets/Helper.py | 244 +-
|
|
plugins/snippets/snippets/Importer.py | 184 +-
|
|
plugins/snippets/snippets/LanguageManager.py | 25 +-
|
|
plugins/snippets/snippets/Library.py | 1892 +++++++-------
|
|
plugins/snippets/snippets/Manager.py | 2176 ++++++++---------
|
|
plugins/snippets/snippets/Parser.py | 466 ++--
|
|
plugins/snippets/snippets/Placeholder.py | 1278 +++++-----
|
|
plugins/snippets/snippets/Snippet.py | 657 ++---
|
|
.../snippets/snippets/SubstitutionParser.py | 356 +--
|
|
plugins/snippets/snippets/WindowHelper.py | 248 +-
|
|
plugins/snippets/snippets/__init__.py | 94 +-
|
|
28 files changed, 5871 insertions(+), 5856 deletions(-)
|
|
mode change 100755 => 100644 plugins/quickopen/quickopen/__init__.py
|
|
mode change 100755 => 100644 plugins/quickopen/quickopen/popup.py
|
|
mode change 100755 => 100644 plugins/quickopen/quickopen/virtualdirs.py
|
|
mode change 100755 => 100644 plugins/quickopen/quickopen/windowhelper.py
|
|
mode change 100755 => 100644 plugins/snippets/snippets/Completion.py
|
|
mode change 100755 => 100644 plugins/snippets/snippets/Document.py
|
|
mode change 100755 => 100644 plugins/snippets/snippets/Exporter.py
|
|
mode change 100755 => 100644 plugins/snippets/snippets/Helper.py
|
|
mode change 100755 => 100644 plugins/snippets/snippets/Importer.py
|
|
mode change 100755 => 100644 plugins/snippets/snippets/LanguageManager.py
|
|
mode change 100755 => 100644 plugins/snippets/snippets/Library.py
|
|
mode change 100755 => 100644 plugins/snippets/snippets/Manager.py
|
|
mode change 100755 => 100644 plugins/snippets/snippets/Parser.py
|
|
mode change 100755 => 100644 plugins/snippets/snippets/Placeholder.py
|
|
mode change 100755 => 100644 plugins/snippets/snippets/Snippet.py
|
|
mode change 100755 => 100644 plugins/snippets/snippets/SubstitutionParser.py
|
|
mode change 100755 => 100644 plugins/snippets/snippets/WindowHelper.py
|
|
mode change 100755 => 100644 plugins/snippets/snippets/__init__.py
|
|
|
|
diff --git a/plugins/externaltools/tools/__init__.py b/plugins/externaltools/tools/__init__.py
|
|
index 463c8f5..153d6c6 100755
|
|
--- a/plugins/externaltools/tools/__init__.py
|
|
+++ b/plugins/externaltools/tools/__init__.py
|
|
@@ -57,12 +57,12 @@ class ToolMenu(object):
|
|
action._tool_handler = None
|
|
|
|
self._action_group.remove_action(action)
|
|
-
|
|
+
|
|
accelmap = Gtk.AccelMap.get()
|
|
|
|
for s in self._signals:
|
|
accelmap.disconnect(s)
|
|
-
|
|
+
|
|
self._signals = []
|
|
|
|
def _insert_directory(self, directory, path):
|
|
@@ -76,7 +76,7 @@ class ToolMenu(object):
|
|
manager.add_ui(self._merge_id, path,
|
|
action_name, action_name,
|
|
Gtk.UIManagerItemType.MENU, False)
|
|
-
|
|
+
|
|
self._insert_directory(item, path + '/' + action_name)
|
|
|
|
for item in directory.tools:
|
|
@@ -87,16 +87,16 @@ class ToolMenu(object):
|
|
# Attach the item and the handler to the action object
|
|
action._tool_item = item
|
|
action._tool_handler = handler
|
|
-
|
|
+
|
|
# Make sure to replace accel
|
|
accelpath = '<Actions>/ExternalToolsPluginToolActions/%s' % (action_name, )
|
|
-
|
|
+
|
|
if item.shortcut:
|
|
key, mod = Gtk.accelerator_parse(item.shortcut)
|
|
Gtk.AccelMap.change_entry(accelpath, key, mod, True)
|
|
-
|
|
+
|
|
self._signals.append(Gtk.AccelMap.get().connect('changed::%s' % (accelpath,), self.on_accelmap_changed, item))
|
|
-
|
|
+
|
|
self._action_group.add_action_with_accel(action, item.shortcut)
|
|
|
|
manager.add_ui(self._merge_id, path,
|
|
@@ -106,7 +106,7 @@ class ToolMenu(object):
|
|
def on_accelmap_changed(self, accelmap, path, key, mod, tool):
|
|
tool.shortcut = Gtk.accelerator_name(key, mod)
|
|
tool.save()
|
|
-
|
|
+
|
|
self._plugin.update_manager(tool)
|
|
|
|
def update(self):
|
|
@@ -119,10 +119,10 @@ class ToolMenu(object):
|
|
def filter_language(self, language, item):
|
|
if not item.languages:
|
|
return True
|
|
-
|
|
+
|
|
if not language and 'plain' in item.languages:
|
|
return True
|
|
-
|
|
+
|
|
if language and (language.get_id() in item.languages):
|
|
return True
|
|
else:
|
|
@@ -142,7 +142,7 @@ class ToolMenu(object):
|
|
'titled': titled,
|
|
'untitled': not titled,
|
|
}
|
|
-
|
|
+
|
|
language = document.get_language()
|
|
|
|
for action in self._action_group.list_actions():
|
|
diff --git a/plugins/externaltools/tools/capture.py b/plugins/externaltools/tools/capture.py
|
|
index 487c8db..73ce270 100755
|
|
--- a/plugins/externaltools/tools/capture.py
|
|
+++ b/plugins/externaltools/tools/capture.py
|
|
@@ -29,7 +29,7 @@ class Capture(GObject.Object):
|
|
CAPTURE_STDERR = 0x02
|
|
CAPTURE_BOTH = 0x03
|
|
CAPTURE_NEEDS_SHELL = 0x04
|
|
-
|
|
+
|
|
WRITE_BUFFER_SIZE = 0x4000
|
|
|
|
__gsignals__ = {
|
|
@@ -73,7 +73,7 @@ class Capture(GObject.Object):
|
|
'shell': self.flags & self.CAPTURE_NEEDS_SHELL,
|
|
'env' : self.env
|
|
}
|
|
-
|
|
+
|
|
if self.input_text is not None:
|
|
popen_args['stdin'] = subprocess.PIPE
|
|
if self.flags & self.CAPTURE_STDOUT:
|
|
@@ -84,17 +84,17 @@ class Capture(GObject.Object):
|
|
self.tried_killing = False
|
|
self.idle_write_id = 0
|
|
self.read_buffer = ''
|
|
-
|
|
+
|
|
try:
|
|
self.pipe = subprocess.Popen(self.command, **popen_args)
|
|
except OSError, e:
|
|
self.pipe = None
|
|
self.emit('stderr-line', _('Could not execute command: %s') % (e, ))
|
|
return
|
|
-
|
|
+
|
|
# Signal
|
|
self.emit('begin-execute')
|
|
-
|
|
+
|
|
if self.flags & self.CAPTURE_STDOUT:
|
|
# Set non blocking
|
|
flags = fcntl.fcntl(self.pipe.stdout.fileno(), fcntl.F_GETFL) | os.O_NONBLOCK
|
|
@@ -132,13 +132,13 @@ class Capture(GObject.Object):
|
|
try:
|
|
l = len(self.write_buffer)
|
|
m = min(l, self.WRITE_BUFFER_SIZE)
|
|
-
|
|
+
|
|
self.pipe.stdin.write(self.write_buffer[:m])
|
|
-
|
|
+
|
|
if m == l:
|
|
self.write_buffer = ''
|
|
self.pipe.stdin.close()
|
|
-
|
|
+
|
|
self.idle_write_id = 0
|
|
|
|
return False
|
|
@@ -165,7 +165,7 @@ class Capture(GObject.Object):
|
|
|
|
self.read_buffer += line
|
|
lines = self.read_buffer.splitlines(True)
|
|
-
|
|
+
|
|
if not lines[-1].endswith("\n"):
|
|
self.read_buffer = lines[-1]
|
|
lines = lines[0:-1]
|
|
diff --git a/plugins/externaltools/tools/functions.py b/plugins/externaltools/tools/functions.py
|
|
index e126844..dd4f82b 100755
|
|
--- a/plugins/externaltools/tools/functions.py
|
|
+++ b/plugins/externaltools/tools/functions.py
|
|
@@ -30,13 +30,13 @@ def default(val, d):
|
|
def current_word(document):
|
|
piter = document.get_iter_at_mark(document.get_insert())
|
|
start = piter.copy()
|
|
-
|
|
+
|
|
if not piter.starts_word() and (piter.inside_word() or piter.ends_word()):
|
|
start.backward_word_start()
|
|
-
|
|
+
|
|
if not piter.ends_word() and piter.inside_word():
|
|
piter.forward_word_end()
|
|
-
|
|
+
|
|
return (start, piter)
|
|
|
|
# ==== Capture related functions ====
|
|
@@ -56,32 +56,32 @@ def run_external_tool(window, panel, node):
|
|
# Environment vars relative to current document
|
|
document = view.get_buffer()
|
|
uri = document.get_uri()
|
|
-
|
|
+
|
|
# Current line number
|
|
piter = document.get_iter_at_mark(document.get_insert())
|
|
capture.set_env(PLUMA_CURRENT_LINE_NUMBER=str(piter.get_line() + 1))
|
|
-
|
|
+
|
|
# Current line text
|
|
piter.set_line_offset(0)
|
|
end = piter.copy()
|
|
-
|
|
+
|
|
if not end.ends_line():
|
|
end.forward_to_line_end()
|
|
-
|
|
+
|
|
capture.set_env(PLUMA_CURRENT_LINE=piter.get_text(end))
|
|
-
|
|
+
|
|
# Selected text (only if input is not selection)
|
|
if node.input != 'selection' and node.input != 'selection-document':
|
|
bounds = document.get_selection_bounds()
|
|
-
|
|
+
|
|
if bounds:
|
|
capture.set_env(PLUMA_SELECTED_TEXT=bounds[0].get_text(bounds[1]))
|
|
-
|
|
+
|
|
bounds = current_word(document)
|
|
capture.set_env(PLUMA_CURRENT_WORD=bounds[0].get_text(bounds[1]))
|
|
-
|
|
+
|
|
capture.set_env(PLUMA_CURRENT_DOCUMENT_TYPE=document.get_mime_type())
|
|
-
|
|
+
|
|
if uri is not None:
|
|
gfile = Gio.file_new_for_uri(uri)
|
|
scheme = gfile.get_uri_scheme()
|
|
@@ -106,7 +106,7 @@ def run_external_tool(window, panel, node):
|
|
PLUMA_DOCUMENTS_PATH = ' '.join(documents_path))
|
|
|
|
flags = capture.CAPTURE_BOTH
|
|
-
|
|
+
|
|
if not node.has_hash_bang():
|
|
flags |= capture.CAPTURE_NEEDS_SHELL
|
|
|
|
@@ -131,7 +131,7 @@ def run_external_tool(window, panel, node):
|
|
elif input_type == 'selection' or input_type == 'selection-document':
|
|
try:
|
|
start, end = document.get_selection_bounds()
|
|
-
|
|
+
|
|
print start, end
|
|
except ValueError:
|
|
if input_type == 'selection-document':
|
|
@@ -142,7 +142,7 @@ def run_external_tool(window, panel, node):
|
|
else:
|
|
start = document.get_iter_at_mark(document.get_insert())
|
|
end = start.copy()
|
|
-
|
|
+
|
|
elif input_type == 'line':
|
|
start = document.get_iter_at_mark(document.get_insert())
|
|
end = start.copy()
|
|
@@ -196,12 +196,12 @@ def run_external_tool(window, panel, node):
|
|
document.begin_user_action()
|
|
|
|
capture.connect('stderr-line', capture_stderr_line_panel, panel)
|
|
- capture.connect('begin-execute', capture_begin_execute_panel, panel, view, node.name)
|
|
+ capture.connect('begin-execute', capture_begin_execute_panel, panel, view, node.name)
|
|
capture.connect('end-execute', capture_end_execute_panel, panel, view, output_type)
|
|
|
|
# Run the command
|
|
capture.execute()
|
|
-
|
|
+
|
|
if output_type != 'nothing':
|
|
document.end_user_action()
|
|
|
|
@@ -222,7 +222,7 @@ class MultipleDocumentsSaver:
|
|
signals[doc] = doc.connect('saving', self.on_document_saving)
|
|
Pluma.commands_save_document(window, doc)
|
|
doc.disconnect(signals[doc])
|
|
-
|
|
+
|
|
def on_document_saving(self, doc, size, total_size):
|
|
self._counter += 1
|
|
self._signal_ids[doc] = doc.connect('saved', self.on_document_saved)
|
|
@@ -230,12 +230,12 @@ class MultipleDocumentsSaver:
|
|
def on_document_saved(self, doc, error):
|
|
if error:
|
|
self._error = True
|
|
-
|
|
+
|
|
doc.disconnect(self._signal_ids[doc])
|
|
del self._signal_ids[doc]
|
|
-
|
|
+
|
|
self._counter -= 1
|
|
-
|
|
+
|
|
if self._counter == 0 and not self._error:
|
|
run_external_tool(self._window, self._panel, self._node)
|
|
|
|
@@ -275,7 +275,7 @@ def capture_end_execute_panel(capture, exit_code, panel, view, output_type):
|
|
mtype, uncertain = Gio.content_type_guess(None, doc.get_text(start, end, False).encode('utf-8'))
|
|
lmanager = GtkSource.LanguageManager.get_default()
|
|
language = lmanager.guess_language(doc.get_uri(), mtype)
|
|
-
|
|
+
|
|
if language is not None:
|
|
doc.set_language(language)
|
|
|
|
diff --git a/plugins/externaltools/tools/library.py b/plugins/externaltools/tools/library.py
|
|
index b4e6924..186c33f 100755
|
|
--- a/plugins/externaltools/tools/library.py
|
|
+++ b/plugins/externaltools/tools/library.py
|
|
@@ -286,64 +286,80 @@ class Tool(object):
|
|
applicability = self._properties.get('Applicability')
|
|
if applicability: return applicability
|
|
return 'all'
|
|
+
|
|
def set_applicability(self, value):
|
|
self._set_property_if_changed('Applicability', value)
|
|
+
|
|
applicability = property(get_applicability, set_applicability)
|
|
|
|
def get_name(self):
|
|
name = self._properties.get('Name')
|
|
if name: return name
|
|
return os.path.basename(self.filename)
|
|
+
|
|
def set_name(self, value):
|
|
self._set_property_if_changed('Name', value)
|
|
+
|
|
name = property(get_name, set_name)
|
|
|
|
def get_shortcut(self):
|
|
shortcut = self._properties.get('Shortcut')
|
|
if shortcut: return shortcut
|
|
return None
|
|
+
|
|
def set_shortcut(self, value):
|
|
self._set_property_if_changed('Shortcut', value)
|
|
+
|
|
shortcut = property(get_shortcut, set_shortcut)
|
|
|
|
def get_comment(self):
|
|
comment = self._properties.get('Comment')
|
|
if comment: return comment
|
|
return self.filename
|
|
+
|
|
def set_comment(self, value):
|
|
self._set_property_if_changed('Comment', value)
|
|
+
|
|
comment = property(get_comment, set_comment)
|
|
|
|
def get_input(self):
|
|
input = self._properties.get('Input')
|
|
if input: return input
|
|
return 'nothing'
|
|
+
|
|
def set_input(self, value):
|
|
self._set_property_if_changed('Input', value)
|
|
+
|
|
input = property(get_input, set_input)
|
|
|
|
def get_output(self):
|
|
output = self._properties.get('Output')
|
|
if output: return output
|
|
return 'output-panel'
|
|
+
|
|
def set_output(self, value):
|
|
self._set_property_if_changed('Output', value)
|
|
+
|
|
output = property(get_output, set_output)
|
|
|
|
def get_save_files(self):
|
|
save_files = self._properties.get('Save-files')
|
|
if save_files: return save_files
|
|
return 'nothing'
|
|
+
|
|
def set_save_files(self, value):
|
|
self._set_property_if_changed('Save-files', value)
|
|
+
|
|
save_files = property(get_save_files, set_save_files)
|
|
|
|
def get_languages(self):
|
|
languages = self._properties.get('Languages')
|
|
if languages: return languages
|
|
return []
|
|
+
|
|
def set_languages(self, value):
|
|
self._set_property_if_changed('Languages', value)
|
|
+
|
|
languages = property(get_languages, set_languages)
|
|
|
|
def has_hash_bang(self):
|
|
@@ -358,7 +374,6 @@ class Tool(object):
|
|
for line in fp:
|
|
if line.strip() == '':
|
|
continue
|
|
-
|
|
return line.startswith('#!')
|
|
|
|
# There is no property for this one because this function is quite
|
|
@@ -404,7 +419,6 @@ class Tool(object):
|
|
|
|
def save_with_script(self, script):
|
|
filename = self.library.get_full_path(self.filename, 'w')
|
|
-
|
|
fp = open(filename, 'w', 1)
|
|
|
|
# Make sure to first print header (shebang, modeline), then
|
|
diff --git a/plugins/externaltools/tools/linkparsing.py b/plugins/externaltools/tools/linkparsing.py
|
|
index 27b9ba8..33ed614 100755
|
|
--- a/plugins/externaltools/tools/linkparsing.py
|
|
+++ b/plugins/externaltools/tools/linkparsing.py
|
|
@@ -193,7 +193,7 @@ REGEXP_VALAC = r"""
|
|
|
|
#ruby
|
|
#test.rb:5: ...
|
|
-# from test.rb:3:in `each'
|
|
+# from test.rb:3:in `each'
|
|
# fist line parsed by REGEXP_STANDARD
|
|
REGEXP_RUBY = r"""
|
|
^\s+from\s
|
|
diff --git a/plugins/externaltools/tools/manager.py b/plugins/externaltools/tools/manager.py
|
|
index 24d7d71..4da0deb 100755
|
|
--- a/plugins/externaltools/tools/manager.py
|
|
+++ b/plugins/externaltools/tools/manager.py
|
|
@@ -34,7 +34,7 @@ class LanguagesPopup(Gtk.Window):
|
|
|
|
def __init__(self, languages):
|
|
Gtk.Window.__init__(self, type=Gtk.WindowType.POPUP)
|
|
-
|
|
+
|
|
self.set_default_size(200, 200)
|
|
self.props.can_focus = True
|
|
|
|
@@ -42,9 +42,9 @@ class LanguagesPopup(Gtk.Window):
|
|
self.init_languages(languages)
|
|
|
|
self.show()
|
|
-
|
|
+
|
|
self.grab_add()
|
|
-
|
|
+
|
|
self.keyboard = None
|
|
device_manager = Gdk.Display.get_device_manager(self.get_window().get_display())
|
|
for device in device_manager.list_devices(Gdk.DeviceType.MASTER):
|
|
@@ -76,40 +76,40 @@ class LanguagesPopup(Gtk.Window):
|
|
|
|
def build(self):
|
|
self.model = Gtk.ListStore(str, str, bool)
|
|
-
|
|
+
|
|
self.sw = Gtk.ScrolledWindow()
|
|
self.sw.show()
|
|
-
|
|
+
|
|
self.sw.set_policy(Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.AUTOMATIC)
|
|
self.sw.set_shadow_type(Gtk.ShadowType.ETCHED_IN)
|
|
-
|
|
+
|
|
self.view = Gtk.TreeView(self.model)
|
|
self.view.show()
|
|
-
|
|
+
|
|
self.view.set_headers_visible(False)
|
|
-
|
|
+
|
|
column = Gtk.TreeViewColumn()
|
|
-
|
|
+
|
|
renderer = Gtk.CellRendererToggle()
|
|
column.pack_start(renderer, False)
|
|
column.set_attributes(renderer, active=self.COLUMN_ENABLED)
|
|
-
|
|
+
|
|
renderer.connect('toggled', self.on_language_toggled)
|
|
-
|
|
+
|
|
renderer = Gtk.CellRendererText()
|
|
column.pack_start(renderer, True)
|
|
column.set_attributes(renderer, text=self.COLUMN_NAME)
|
|
-
|
|
+
|
|
self.view.append_column(column)
|
|
self.view.set_row_separator_func(self.on_separator)
|
|
-
|
|
+
|
|
self.sw.add(self.view)
|
|
-
|
|
+
|
|
self.add(self.sw)
|
|
-
|
|
+
|
|
def enabled_languages(self, model, path, piter, ret):
|
|
enabled = model.get_value(piter, self.COLUMN_ENABLED)
|
|
-
|
|
+
|
|
if path.get_indices()[0] == 0 and enabled:
|
|
return True
|
|
|
|
@@ -117,42 +117,42 @@ class LanguagesPopup(Gtk.Window):
|
|
ret.append(model.get_value(piter, self.COLUMN_ID))
|
|
|
|
return False
|
|
-
|
|
+
|
|
def languages(self):
|
|
ret = []
|
|
-
|
|
+
|
|
self.model.foreach(self.enabled_languages, ret)
|
|
return ret
|
|
-
|
|
+
|
|
def on_separator(self, model, piter):
|
|
val = model.get_value(piter, self.COLUMN_NAME)
|
|
return val == '-'
|
|
-
|
|
+
|
|
def init_languages(self, languages):
|
|
manager = GtkSource.LanguageManager()
|
|
langs = [manager.get_language(x) for x in manager.get_language_ids()]
|
|
langs.sort(key=lambda x: x.get_name())
|
|
-
|
|
+
|
|
self.model.append([_('All languages'), None, not languages])
|
|
self.model.append(['-', None, False])
|
|
self.model.append([_('Plain Text'), 'plain', 'plain' in languages])
|
|
self.model.append(['-', None, False])
|
|
-
|
|
+
|
|
for lang in langs:
|
|
self.model.append([lang.get_name(), lang.get_id(), lang.get_id() in languages])
|
|
|
|
def correct_all(self, model, path, piter, enabled):
|
|
if path == (0,):
|
|
return False
|
|
-
|
|
+
|
|
model.set_value(piter, self.COLUMN_ENABLED, enabled)
|
|
|
|
def on_language_toggled(self, renderer, path):
|
|
piter = self.model.get_iter(path)
|
|
-
|
|
+
|
|
enabled = self.model.get_value(piter, self.COLUMN_ENABLED)
|
|
self.model.set_value(piter, self.COLUMN_ENABLED, not enabled)
|
|
-
|
|
+
|
|
if path == '0':
|
|
self.model.foreach(self.correct_all, False)
|
|
else:
|
|
@@ -165,55 +165,55 @@ class LanguagesPopup(Gtk.Window):
|
|
else:
|
|
event.window = self.view.get_bin_window()
|
|
return self.view.event(event)
|
|
-
|
|
+
|
|
def do_key_release_event(self, event):
|
|
event.window = self.view.get_bin_window()
|
|
return self.view.event(event)
|
|
-
|
|
+
|
|
def in_window(self, event, window=None):
|
|
if not window:
|
|
window = self.get_window()
|
|
|
|
geometry = window.get_geometry()
|
|
origin = window.get_origin()
|
|
-
|
|
+
|
|
return event.x_root >= origin[1] and \
|
|
event.x_root <= origin[1] + geometry[2] and \
|
|
event.y_root >= origin[2] and \
|
|
event.y_root <= origin[2] + geometry[3]
|
|
-
|
|
+
|
|
def do_destroy(self):
|
|
if self.keyboard:
|
|
self.keyboard.ungrab(Gdk.CURRENT_TIME)
|
|
self.pointer.ungrab(Gdk.CURRENT_TIME)
|
|
-
|
|
+
|
|
return Gtk.Window.do_destroy(self)
|
|
-
|
|
+
|
|
def setup_event(self, event, window):
|
|
fr = event.window.get_origin()
|
|
to = window.get_origin()
|
|
-
|
|
+
|
|
event.window = window
|
|
event.x += fr[1] - to[1]
|
|
event.y += fr[2] - to[2]
|
|
-
|
|
+
|
|
def resolve_widgets(self, root):
|
|
res = [root]
|
|
-
|
|
+
|
|
if isinstance(root, Gtk.Container):
|
|
root.forall(lambda x, y: res.extend(self.resolve_widgets(x)), None)
|
|
-
|
|
+
|
|
return res
|
|
-
|
|
+
|
|
def resolve_windows(self, window):
|
|
if not window:
|
|
return []
|
|
|
|
res = [window]
|
|
res.extend(window.get_children())
|
|
-
|
|
+
|
|
return res
|
|
-
|
|
+
|
|
def propagate_mouse_event(self, event, reverse=True):
|
|
allwidgets = self.resolve_widgets(self.get_child())
|
|
|
|
@@ -223,19 +223,19 @@ class LanguagesPopup(Gtk.Window):
|
|
for widget in allwidgets:
|
|
windows = self.resolve_windows(widget.get_window())
|
|
windows.reverse()
|
|
-
|
|
+
|
|
for window in windows:
|
|
if not (window.get_events() & event.type):
|
|
continue
|
|
|
|
- if self.in_window(event, window):
|
|
+ if self.in_window(event, window):
|
|
self.setup_event(event, window)
|
|
|
|
if widget.event(event):
|
|
return True
|
|
-
|
|
+
|
|
return False
|
|
-
|
|
+
|
|
def do_button_press_event(self, event):
|
|
if not self.in_window(event):
|
|
self.destroy()
|
|
@@ -250,19 +250,19 @@ class LanguagesPopup(Gtk.Window):
|
|
|
|
def do_scroll_event(self, event):
|
|
return self.propagate_mouse_event(event, False)
|
|
-
|
|
+
|
|
def do_motion_notify_event(self, event):
|
|
return self.propagate_mouse_event(event)
|
|
-
|
|
+
|
|
def do_enter_notify_event(self, event):
|
|
return self.propagate_mouse_event(event)
|
|
|
|
def do_leave_notify_event(self, event):
|
|
return self.propagate_mouse_event(event)
|
|
-
|
|
+
|
|
def do_proximity_in_event(self, event):
|
|
return self.propagate_mouse_event(event)
|
|
-
|
|
+
|
|
def do_proximity_out_event(self, event):
|
|
return self.propagate_mouse_event(event)
|
|
|
|
@@ -281,12 +281,12 @@ class Manager(GObject.Object):
|
|
self._size = (0, 0)
|
|
self._languages = {}
|
|
self._tool_rows = {}
|
|
-
|
|
+
|
|
self.build()
|
|
|
|
def get_final_size(self):
|
|
return self._size
|
|
-
|
|
+
|
|
def build(self):
|
|
callbacks = {
|
|
'on_new_tool_button_clicked' : self.on_new_tool_button_clicked,
|
|
@@ -305,42 +305,42 @@ class Manager(GObject.Object):
|
|
self.ui.add_from_file(os.path.join(self.datadir, 'ui', 'tools.ui'))
|
|
self.ui.connect_signals(callbacks)
|
|
self.dialog = self.ui.get_object('tool-manager-dialog')
|
|
-
|
|
+
|
|
self.view = self.ui.get_object('view')
|
|
-
|
|
+
|
|
self.__init_tools_model()
|
|
self.__init_tools_view()
|
|
|
|
for name in ['input', 'output', 'applicability', 'save-files']:
|
|
self.__init_combobox(name)
|
|
-
|
|
+
|
|
self.do_update()
|
|
|
|
def expand_from_doc(self, doc):
|
|
row = None
|
|
-
|
|
+
|
|
if doc:
|
|
if doc.get_language():
|
|
lid = doc.get_language().get_id()
|
|
-
|
|
+
|
|
if lid in self._languages:
|
|
row = self._languages[lid]
|
|
elif 'plain' in self._languages:
|
|
row = self._languages['plain']
|
|
-
|
|
+
|
|
if not row and None in self._languages:
|
|
row = self._languages[None]
|
|
-
|
|
+
|
|
if not row:
|
|
return
|
|
-
|
|
+
|
|
self.view.expand_row(row.get_path(), False)
|
|
self.view.get_selection().select_path(row.get_path())
|
|
-
|
|
+
|
|
def run(self, window):
|
|
if self.dialog == None:
|
|
self.build()
|
|
-
|
|
+
|
|
# Open up language
|
|
self.expand_from_doc(window.get_active_document())
|
|
|
|
@@ -351,7 +351,7 @@ class Manager(GObject.Object):
|
|
def add_accelerator(self, item):
|
|
if not item.shortcut:
|
|
return
|
|
-
|
|
+
|
|
if item.shortcut in self.accelerators:
|
|
if not item in self.accelerators[item.shortcut]:
|
|
self.accelerators[item.shortcut].append(item)
|
|
@@ -375,42 +375,42 @@ class Manager(GObject.Object):
|
|
lid = language.get_id()
|
|
else:
|
|
lid = language
|
|
-
|
|
+
|
|
if not lid in self._languages:
|
|
piter = self.model.append(None, [language])
|
|
-
|
|
+
|
|
parent = Gtk.TreeRowReference.new(self.model, self.model.get_path(piter))
|
|
self._languages[lid] = parent
|
|
else:
|
|
parent = self._languages[lid]
|
|
-
|
|
+
|
|
piter = self.model.get_iter(parent.get_path())
|
|
child = self.model.append(piter, [tool])
|
|
-
|
|
+
|
|
if not tool in self._tool_rows:
|
|
self._tool_rows[tool] = []
|
|
-
|
|
+
|
|
self._tool_rows[tool].append(Gtk.TreeRowReference.new(self.model, self.model.get_path(child)))
|
|
return child
|
|
|
|
def add_tool(self, tool):
|
|
manager = GtkSource.LanguageManager()
|
|
ret = None
|
|
-
|
|
+
|
|
for lang in tool.languages:
|
|
l = manager.get_language(lang)
|
|
-
|
|
+
|
|
if l:
|
|
ret = self.add_tool_to_language(tool, l)
|
|
elif lang == 'plain':
|
|
ret = self.add_tool_to_language(tool, 'plain')
|
|
-
|
|
+
|
|
if not ret:
|
|
ret = self.add_tool_to_language(tool, None)
|
|
|
|
self.add_accelerator(tool)
|
|
return ret
|
|
-
|
|
+
|
|
def __init_tools_model(self):
|
|
self.tools = ToolLibrary()
|
|
self.current_node = None
|
|
@@ -430,26 +430,26 @@ class Manager(GObject.Object):
|
|
# For languages, sort All before everything else, otherwise alphabetical
|
|
t1 = model.get_value(iter1, self.TOOL_COLUMN)
|
|
t2 = model.get_value(iter2, self.TOOL_COLUMN)
|
|
-
|
|
+
|
|
if model.iter_parent(iter1) == None:
|
|
if t1 == None:
|
|
return -1
|
|
-
|
|
+
|
|
if t2 == None:
|
|
return 1
|
|
-
|
|
+
|
|
def lang_name(lang):
|
|
if isinstance(lang, GtkSource.Language):
|
|
return lang.get_name()
|
|
else:
|
|
return _('Plain Text')
|
|
-
|
|
+
|
|
n1 = lang_name(t1)
|
|
n2 = lang_name(t2)
|
|
else:
|
|
n1 = t1.name
|
|
n2 = t2.name
|
|
-
|
|
+
|
|
return cmp(n1.lower(), n2.lower())
|
|
|
|
def __init_tools_view(self):
|
|
@@ -459,12 +459,12 @@ class Manager(GObject.Object):
|
|
column.pack_start(renderer, False)
|
|
renderer.set_property('editable', True)
|
|
self.view.append_column(column)
|
|
-
|
|
+
|
|
column.set_cell_data_func(renderer, self.get_cell_data_cb, None)
|
|
|
|
renderer.connect('edited', self.on_view_label_cell_edited)
|
|
renderer.connect('editing-started', self.on_view_label_cell_editing_started)
|
|
-
|
|
+
|
|
self.selection_changed_id = self.view.get_selection().connect('changed', self.on_view_selection_changed, None)
|
|
|
|
def __init_combobox(self, name):
|
|
@@ -491,10 +491,10 @@ class Manager(GObject.Object):
|
|
|
|
if piter is not None:
|
|
tool = model.get_value(piter, self.TOOL_COLUMN)
|
|
-
|
|
+
|
|
if not isinstance(tool, Tool):
|
|
tool = None
|
|
-
|
|
+
|
|
return piter, tool
|
|
else:
|
|
return None, None
|
|
@@ -541,27 +541,27 @@ class Manager(GObject.Object):
|
|
|
|
for nm in ('input', 'output', 'applicability', 'save-files'):
|
|
self[nm].set_active(0)
|
|
-
|
|
+
|
|
self['languages_label'].set_text(_('All Languages'))
|
|
-
|
|
+
|
|
def fill_languages_button(self):
|
|
if not self.current_node or not self.current_node.languages:
|
|
self['languages_label'].set_text(_('All Languages'))
|
|
else:
|
|
manager = GtkSource.LanguageManager()
|
|
langs = []
|
|
-
|
|
+
|
|
for lang in self.current_node.languages:
|
|
if lang == 'plain':
|
|
langs.append(_('Plain Text'))
|
|
else:
|
|
l = manager.get_language(lang)
|
|
-
|
|
+
|
|
if l:
|
|
langs.append(l.get_name())
|
|
-
|
|
+
|
|
self['languages_label'].set_text(', '.join(langs))
|
|
-
|
|
+
|
|
def fill_fields(self):
|
|
node = self.current_node
|
|
self['accelerator'].set_text(default(node.shortcut, ''))
|
|
@@ -587,7 +587,7 @@ class Manager(GObject.Object):
|
|
for nm in ('input', 'output', 'applicability', 'save-files'):
|
|
model = self[nm].get_model()
|
|
piter = model.get_iter_first()
|
|
-
|
|
+
|
|
self.set_active_by_name(nm,
|
|
default(node.__getattribute__(nm.replace('-', '_')),
|
|
model.get_value(piter, self.NAME_COLUMN)))
|
|
@@ -620,34 +620,34 @@ class Manager(GObject.Object):
|
|
self['tool-table'].set_sensitive(True)
|
|
else:
|
|
self.clear_fields()
|
|
- self['tool-table'].set_sensitive(False)
|
|
+ self['tool-table'].set_sensitive(False)
|
|
|
|
def language_id_from_iter(self, piter):
|
|
if not piter:
|
|
return None
|
|
|
|
tool = self.model.get_value(piter, self.TOOL_COLUMN)
|
|
-
|
|
+
|
|
if isinstance(tool, Tool):
|
|
piter = self.model.iter_parent(piter)
|
|
tool = self.model.get_value(piter, self.TOOL_COLUMN)
|
|
-
|
|
+
|
|
if isinstance(tool, GtkSource.Language):
|
|
return tool.get_id()
|
|
elif tool:
|
|
return 'plain'
|
|
-
|
|
+
|
|
return None
|
|
|
|
def selected_language_id(self):
|
|
# Find current language if there is any
|
|
model, piter = self.view.get_selection().get_selected()
|
|
-
|
|
+
|
|
return self.language_id_from_iter(piter)
|
|
|
|
def on_new_tool_button_clicked(self, button):
|
|
self.save_current_tool()
|
|
-
|
|
+
|
|
# block handlers while inserting a new item
|
|
self.view.get_selection().handler_block(self.selection_changed_id)
|
|
|
|
@@ -656,10 +656,10 @@ class Manager(GObject.Object):
|
|
self.tools.tree.tools.append(self.current_node)
|
|
|
|
lang = self.selected_language_id()
|
|
-
|
|
+
|
|
if lang:
|
|
self.current_node.languages = [lang]
|
|
-
|
|
+
|
|
piter = self.add_tool(self.current_node)
|
|
|
|
self.view.set_cursor(self.model.get_path(piter), self.view.get_column(self.TOOL_COLUMN), True)
|
|
@@ -671,7 +671,7 @@ class Manager(GObject.Object):
|
|
def tool_changed(self, tool, refresh=False):
|
|
for row in self._tool_rows[tool]:
|
|
self.model.row_changed(row.get_path(), self.model.get_iter(row.get_path()))
|
|
-
|
|
+
|
|
if refresh and tool == self.current_node:
|
|
self.fill_fields()
|
|
|
|
@@ -685,29 +685,29 @@ class Manager(GObject.Object):
|
|
|
|
if node.is_global():
|
|
shortcut = node.shortcut
|
|
-
|
|
+
|
|
if node.parent.revert_tool(node):
|
|
self.remove_accelerator(node, shortcut)
|
|
self.add_accelerator(node)
|
|
|
|
self['revert-tool-button'].set_sensitive(False)
|
|
self.fill_fields()
|
|
-
|
|
+
|
|
self.tool_changed(node)
|
|
else:
|
|
parent = self.model.iter_parent(piter)
|
|
language = self.language_id_from_iter(parent)
|
|
-
|
|
+
|
|
self.model.remove(piter)
|
|
-
|
|
+
|
|
if language in node.languages:
|
|
node.languages.remove(language)
|
|
|
|
self._tool_rows[node] = filter(lambda x: x.valid(), self._tool_rows[node])
|
|
-
|
|
+
|
|
if not self._tool_rows[node]:
|
|
del self._tool_rows[node]
|
|
-
|
|
+
|
|
if node.parent.delete_tool(node):
|
|
self.remove_accelerator(node)
|
|
self.current_node = None
|
|
@@ -717,10 +717,10 @@ class Manager(GObject.Object):
|
|
self.view.set_cursor(self.model.get_path(piter), self.view.get_column(self.TOOL_COLUMN), False)
|
|
|
|
self.view.grab_focus()
|
|
-
|
|
+
|
|
path = self._languages[language].get_path()
|
|
parent = self.model.get_iter(path)
|
|
-
|
|
+
|
|
if not self.model.iter_has_child(parent):
|
|
self.model.remove(parent)
|
|
del self._languages[language]
|
|
@@ -729,9 +729,9 @@ class Manager(GObject.Object):
|
|
if new_text != '':
|
|
piter = self.model.get_iter(path)
|
|
tool = self.model.get_value(piter, self.TOOL_COLUMN)
|
|
-
|
|
+
|
|
tool.name = new_text
|
|
-
|
|
+
|
|
self.save_current_tool()
|
|
self.tool_changed(tool)
|
|
|
|
@@ -742,7 +742,7 @@ class Manager(GObject.Object):
|
|
if isinstance(editable, Gtk.Entry):
|
|
editable.set_text(tool.name)
|
|
editable.grab_focus()
|
|
-
|
|
+
|
|
def on_view_selection_changed(self, selection, userdata):
|
|
self.save_current_tool()
|
|
self.do_update()
|
|
@@ -750,19 +750,19 @@ class Manager(GObject.Object):
|
|
def accelerator_collision(self, name, node):
|
|
if not name in self.accelerators:
|
|
return []
|
|
-
|
|
+
|
|
ret = []
|
|
-
|
|
+
|
|
for other in self.accelerators[name]:
|
|
if not other.languages or not node.languages:
|
|
ret.append(other)
|
|
continue
|
|
-
|
|
+
|
|
for lang in other.languages:
|
|
if lang in node.languages:
|
|
ret.append(other)
|
|
continue
|
|
-
|
|
+
|
|
return ret
|
|
|
|
def set_accelerator(self, keyval, mod):
|
|
@@ -775,9 +775,9 @@ class Manager(GObject.Object):
|
|
self.current_node.shorcut = None
|
|
self.save_current_tool()
|
|
return True
|
|
-
|
|
+
|
|
col = self.accelerator_collision(name, self.current_node)
|
|
-
|
|
+
|
|
if col:
|
|
dialog = Gtk.MessageDialog(self.dialog,
|
|
Gtk.DialogFlags.MODAL,
|
|
@@ -787,7 +787,7 @@ class Manager(GObject.Object):
|
|
|
|
dialog.run()
|
|
dialog.destroy()
|
|
-
|
|
+
|
|
self.add_accelerator(self.current_node)
|
|
return False
|
|
|
|
@@ -816,7 +816,7 @@ class Manager(GObject.Object):
|
|
if self.set_accelerator(event.keyval, mask):
|
|
entry.set_text(default(self.current_node.shortcut, ''))
|
|
self['commands'].grab_focus()
|
|
-
|
|
+
|
|
# Capture all `normal characters`
|
|
return True
|
|
elif Gdk.keyval_to_unicode(event.keyval):
|
|
@@ -849,7 +849,7 @@ class Manager(GObject.Object):
|
|
return
|
|
|
|
self.on_tool_manager_dialog_focus_out(dialog, None)
|
|
-
|
|
+
|
|
self.dialog.destroy()
|
|
self.dialog = None
|
|
self.tools = None
|
|
@@ -873,7 +873,7 @@ class Manager(GObject.Object):
|
|
label = _('Plain Text')
|
|
else:
|
|
label = tool.get_name()
|
|
-
|
|
+
|
|
markup = saxutils.escape(label)
|
|
editable = False
|
|
else:
|
|
@@ -885,7 +885,7 @@ class Manager(GObject.Object):
|
|
markup = escaped
|
|
|
|
editable = True
|
|
-
|
|
+
|
|
cell.set_properties(markup=markup, editable=editable)
|
|
|
|
def tool_in_language(self, tool, lang):
|
|
@@ -894,84 +894,84 @@ class Manager(GObject.Object):
|
|
|
|
ref = self._languages[lang]
|
|
parent = ref.get_path()
|
|
-
|
|
+
|
|
for row in self._tool_rows[tool]:
|
|
path = row.get_path()
|
|
-
|
|
+
|
|
if path.get_indices()[0] == parent.get_indices()[0]:
|
|
return True
|
|
-
|
|
+
|
|
return False
|
|
|
|
def update_languages(self, popup):
|
|
self.current_node.languages = popup.languages()
|
|
self.fill_languages_button()
|
|
-
|
|
+
|
|
piter, node = self.get_selected_tool()
|
|
ret = None
|
|
-
|
|
+
|
|
if node:
|
|
ref = Gtk.TreeRowReference(self.model, self.model.get_path(piter))
|
|
-
|
|
+
|
|
# Update languages, make sure to inhibit selection change stuff
|
|
self.view.get_selection().handler_block(self.selection_changed_id)
|
|
-
|
|
+
|
|
# Remove all rows that are no longer
|
|
for row in list(self._tool_rows[self.current_node]):
|
|
piter = self.model.get_iter(row.get_path())
|
|
language = self.language_id_from_iter(piter)
|
|
-
|
|
+
|
|
if (not language and not self.current_node.languages) or \
|
|
(language in self.current_node.languages):
|
|
continue
|
|
-
|
|
+
|
|
# Remove from language
|
|
self.model.remove(piter)
|
|
self._tool_rows[self.current_node].remove(row)
|
|
-
|
|
+
|
|
# If language is empty, remove it
|
|
parent = self.model.get_iter(self._languages[language].get_path())
|
|
-
|
|
+
|
|
if not self.model.iter_has_child(parent):
|
|
self.model.remove(parent)
|
|
del self._languages[language]
|
|
-
|
|
+
|
|
# Now, add for any that are new
|
|
manager = GtkSource.LanguageManager()
|
|
-
|
|
+
|
|
for lang in self.current_node.languages:
|
|
if not self.tool_in_language(self.current_node, lang):
|
|
l = manager.get_language(lang)
|
|
-
|
|
+
|
|
if not l:
|
|
l = 'plain'
|
|
-
|
|
+
|
|
self.add_tool_to_language(self.current_node, l)
|
|
-
|
|
+
|
|
if not self.current_node.languages and not self.tool_in_language(self.current_node, None):
|
|
self.add_tool_to_language(self.current_node, None)
|
|
-
|
|
+
|
|
# Check if we can still keep the current
|
|
if not ref or not ref.valid():
|
|
# Change selection to first language
|
|
path = self._tool_rows[self.current_node][0].get_path()
|
|
piter = self.model.get_iter(path)
|
|
parent = self.model.iter_parent(piter)
|
|
-
|
|
+
|
|
# Expand parent, select child and scroll to it
|
|
self.view.expand_row(self.model.get_path(parent), False)
|
|
self.view.get_selection().select_path(path)
|
|
self.view.set_cursor(path, self.view.get_column(self.TOOL_COLUMN), False)
|
|
-
|
|
+
|
|
self.view.get_selection().handler_unblock(self.selection_changed_id)
|
|
|
|
def on_languages_button_clicked(self, button):
|
|
popup = LanguagesPopup(self.current_node.languages)
|
|
popup.set_transient_for(self.dialog)
|
|
-
|
|
+
|
|
origin = button.get_window().get_origin()
|
|
popup.move(origin[1], origin[2] - popup.get_allocation().height)
|
|
-
|
|
+
|
|
popup.connect('destroy', self.update_languages)
|
|
|
|
# ex:et:ts=4:
|
|
diff --git a/plugins/externaltools/tools/outputpanel.py b/plugins/externaltools/tools/outputpanel.py
|
|
index 9613d78..e063eb2 100755
|
|
--- a/plugins/externaltools/tools/outputpanel.py
|
|
+++ b/plugins/externaltools/tools/outputpanel.py
|
|
@@ -129,11 +129,11 @@ class OutputPanel(UniqueById):
|
|
# find all links and apply the appropriate tag for them
|
|
links = self.link_parser.parse(text)
|
|
for lnk in links:
|
|
-
|
|
+
|
|
insert_iter = buffer.get_iter_at_mark(insert)
|
|
lnk.start = insert_iter.get_offset() + lnk.start
|
|
lnk.end = insert_iter.get_offset() + lnk.end
|
|
-
|
|
+
|
|
start_iter = buffer.get_iter_at_offset(lnk.start)
|
|
end_iter = buffer.get_iter_at_offset(lnk.end)
|
|
|
|
@@ -156,7 +156,7 @@ class OutputPanel(UniqueById):
|
|
panel.show()
|
|
panel.activate_item(self.panel)
|
|
|
|
- def update_cursor_style(self, view, x, y):
|
|
+ def update_cursor_style(self, view, x, y):
|
|
if self.get_link_at_location(view, x, y) is not None:
|
|
cursor = self.link_cursor
|
|
else:
|
|
diff --git a/plugins/pythonconsole/pythonconsole/__init__.py b/plugins/pythonconsole/pythonconsole/__init__.py
|
|
index 59ac413..07d13c7 100755
|
|
--- a/plugins/pythonconsole/pythonconsole/__init__.py
|
|
+++ b/plugins/pythonconsole/pythonconsole/__init__.py
|
|
@@ -8,7 +8,7 @@
|
|
# it under the terms of the GNU General Public License as published by
|
|
# the Free Software Foundation; either version 2, or (at your option)
|
|
# any later version.
|
|
-#
|
|
+#
|
|
# This program is distributed in the hope that it will be useful,
|
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
diff --git a/plugins/quickopen/quickopen/__init__.py b/plugins/quickopen/quickopen/__init__.py
|
|
old mode 100755
|
|
new mode 100644
|
|
index 54e9598..3ae72a4
|
|
--- a/plugins/quickopen/quickopen/__init__.py
|
|
+++ b/plugins/quickopen/quickopen/__init__.py
|
|
@@ -21,30 +21,30 @@ from gi.repository import GObject, Peas
|
|
from windowhelper import WindowHelper
|
|
|
|
class QuickOpenPlugin(GObject.Object, Peas.Activatable):
|
|
- __gtype_name__ = "QuickOpenPlugin"
|
|
+ __gtype_name__ = "QuickOpenPlugin"
|
|
|
|
- object = GObject.Property(type=GObject.Object)
|
|
+ object = GObject.Property(type=GObject.Object)
|
|
|
|
- def __init__(self):
|
|
- GObject.Object.__init__(self)
|
|
+ def __init__(self):
|
|
+ GObject.Object.__init__(self)
|
|
|
|
- self._popup_size = (450, 300)
|
|
+ self._popup_size = (450, 300)
|
|
|
|
- def do_activate(self):
|
|
- window = self.object
|
|
- self._helper = WindowHelper(window, self)
|
|
+ def do_activate(self):
|
|
+ window = self.object
|
|
+ self._helper = WindowHelper(window, self)
|
|
|
|
- def do_deactivate(self):
|
|
- self._helper.deactivate()
|
|
- self._helper = None
|
|
+ def do_deactivate(self):
|
|
+ self._helper.deactivate()
|
|
+ self._helper = None
|
|
|
|
- def do_update_state(self):
|
|
- self._helper.update_ui()
|
|
+ def do_update_state(self):
|
|
+ self._helper.update_ui()
|
|
|
|
- def get_popup_size(self):
|
|
- return self._popup_size
|
|
+ def get_popup_size(self):
|
|
+ return self._popup_size
|
|
|
|
- def set_popup_size(self, size):
|
|
- self._popup_size = size
|
|
+ def set_popup_size(self, size):
|
|
+ self._popup_size = size
|
|
|
|
-# ex:ts=8:et:
|
|
+# ex:ts=4:et:
|
|
diff --git a/plugins/quickopen/quickopen/popup.py b/plugins/quickopen/quickopen/popup.py
|
|
old mode 100755
|
|
new mode 100644
|
|
index b571b68..c6cc801
|
|
--- a/plugins/quickopen/quickopen/popup.py
|
|
+++ b/plugins/quickopen/quickopen/popup.py
|
|
@@ -24,533 +24,532 @@ from gi.repository import GObject, Gio, GLib, Gdk, Gtk, Pango, Pluma
|
|
from virtualdirs import VirtualDirectory
|
|
|
|
class Popup(Gtk.Dialog):
|
|
- __gtype_name__ = "QuickOpenPopup"
|
|
+ __gtype_name__ = "QuickOpenPopup"
|
|
|
|
- def __init__(self, window, paths, handler):
|
|
- Gtk.Dialog.__init__(self,
|
|
- title=_('Quick Open'),
|
|
- parent=window,
|
|
- flags=Gtk.DialogFlags.DESTROY_WITH_PARENT | Gtk.DialogFlags.MODAL,
|
|
- buttons=(Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL))
|
|
+ def __init__(self, window, paths, handler):
|
|
+ Gtk.Dialog.__init__(self,
|
|
+ title=_('Quick Open'),
|
|
+ parent=window,
|
|
+ flags=Gtk.DialogFlags.DESTROY_WITH_PARENT | Gtk.DialogFlags.MODAL,
|
|
+ buttons=(Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL))
|
|
|
|
- self._open_button = self.add_button(Gtk.STOCK_OPEN, Gtk.ResponseType.ACCEPT)
|
|
+ self._open_button = self.add_button(Gtk.STOCK_OPEN, Gtk.ResponseType.ACCEPT)
|
|
|
|
- self._handler = handler
|
|
- self._build_ui()
|
|
+ self._handler = handler
|
|
+ self._build_ui()
|
|
|
|
- self._size = (0, 0)
|
|
- self._dirs = []
|
|
- self._cache = {}
|
|
- self._theme = None
|
|
- self._cursor = None
|
|
- self._shift_start = None
|
|
+ self._size = (0, 0)
|
|
+ self._dirs = []
|
|
+ self._cache = {}
|
|
+ self._theme = None
|
|
+ self._cursor = None
|
|
+ self._shift_start = None
|
|
|
|
- self._busy_cursor = Gdk.Cursor(Gdk.CursorType.WATCH)
|
|
+ self._busy_cursor = Gdk.Cursor(Gdk.CursorType.WATCH)
|
|
|
|
- accel_group = Gtk.AccelGroup()
|
|
- accel_group.connect(Gdk.KEY_l, Gdk.ModifierType.CONTROL_MASK, 0, self.on_focus_entry)
|
|
+ accel_group = Gtk.AccelGroup()
|
|
+ accel_group.connect(Gdk.KEY_l, Gdk.ModifierType.CONTROL_MASK, 0, self.on_focus_entry)
|
|
|
|
- self.add_accel_group(accel_group)
|
|
+ self.add_accel_group(accel_group)
|
|
|
|
- unique = []
|
|
+ unique = []
|
|
|
|
- for path in paths:
|
|
- if not path.get_uri() in unique:
|
|
- self._dirs.append(path)
|
|
- unique.append(path.get_uri())
|
|
+ for path in paths:
|
|
+ if not path.get_uri() in unique:
|
|
+ self._dirs.append(path)
|
|
+ unique.append(path.get_uri())
|
|
|
|
- def get_final_size(self):
|
|
- return self._size
|
|
+ def get_final_size(self):
|
|
+ return self._size
|
|
|
|
- def _build_ui(self):
|
|
- vbox = self.get_content_area()
|
|
- vbox.set_spacing(3)
|
|
+ def _build_ui(self):
|
|
+ vbox = self.get_content_area()
|
|
+ vbox.set_spacing(3)
|
|
|
|
- self._entry = Gtk.Entry()
|
|
+ self._entry = Gtk.Entry()
|
|
|
|
- self._entry.connect('changed', self.on_changed)
|
|
- self._entry.connect('key-press-event', self.on_key_press_event)
|
|
+ self._entry.connect('changed', self.on_changed)
|
|
+ self._entry.connect('key-press-event', self.on_key_press_event)
|
|
|
|
- sw = Gtk.ScrolledWindow()
|
|
- sw.set_policy(Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.AUTOMATIC)
|
|
- sw.set_shadow_type(Gtk.ShadowType.OUT)
|
|
+ sw = Gtk.ScrolledWindow()
|
|
+ sw.set_policy(Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.AUTOMATIC)
|
|
+ sw.set_shadow_type(Gtk.ShadowType.OUT)
|
|
|
|
- tv = Gtk.TreeView()
|
|
- tv.set_headers_visible(False)
|
|
+ tv = Gtk.TreeView()
|
|
+ tv.set_headers_visible(False)
|
|
|
|
- self._store = Gtk.ListStore(Gio.Icon, str, GObject.Object, Gio.FileType)
|
|
- tv.set_model(self._store)
|
|
+ self._store = Gtk.ListStore(Gio.Icon, str, GObject.Object, Gio.FileType)
|
|
+ tv.set_model(self._store)
|
|
|
|
- self._treeview = tv
|
|
- tv.connect('row-activated', self.on_row_activated)
|
|
+ self._treeview = tv
|
|
+ tv.connect('row-activated', self.on_row_activated)
|
|
|
|
- renderer = Gtk.CellRendererPixbuf()
|
|
- column = Gtk.TreeViewColumn()
|
|
- column.pack_start(renderer, False)
|
|
- column.add_attribute(renderer, "gicon", 0)
|
|
+ renderer = Gtk.CellRendererPixbuf()
|
|
+ column = Gtk.TreeViewColumn()
|
|
+ column.pack_start(renderer, False)
|
|
+ column.add_attribute(renderer, "gicon", 0)
|
|
|
|
- renderer = Gtk.CellRendererText()
|
|
- column.pack_start(renderer, True)
|
|
- column.add_attribute(renderer, "markup", 1)
|
|
+ renderer = Gtk.CellRendererText()
|
|
+ column.pack_start(renderer, True)
|
|
+ column.add_attribute(renderer, "markup", 1)
|
|
|
|
- column.set_cell_data_func(renderer, self.on_cell_data_cb, None)
|
|
+ column.set_cell_data_func(renderer, self.on_cell_data_cb, None)
|
|
|
|
- tv.append_column(column)
|
|
- sw.add(tv)
|
|
-
|
|
- selection = tv.get_selection()
|
|
- selection.connect('changed', self.on_selection_changed)
|
|
- selection.set_mode(Gtk.SelectionMode.MULTIPLE)
|
|
+ tv.append_column(column)
|
|
+ sw.add(tv)
|
|
|
|
- vbox.pack_start(self._entry, False, False, 0)
|
|
- vbox.pack_start(sw, True, True, 0)
|
|
+ selection = tv.get_selection()
|
|
+ selection.connect('changed', self.on_selection_changed)
|
|
+ selection.set_mode(Gtk.SelectionMode.MULTIPLE)
|
|
|
|
- lbl = Gtk.Label()
|
|
- lbl.set_alignment(0, 0.5)
|
|
- lbl.set_ellipsize(Pango.EllipsizeMode.MIDDLE)
|
|
- self._info_label = lbl
|
|
+ vbox.pack_start(self._entry, False, False, 0)
|
|
+ vbox.pack_start(sw, True, True, 0)
|
|
|
|
- vbox.pack_start(lbl, False, False, 0)
|
|
+ lbl = Gtk.Label()
|
|
+ lbl.set_alignment(0, 0.5)
|
|
+ lbl.set_ellipsize(Pango.EllipsizeMode.MIDDLE)
|
|
+ self._info_label = lbl
|
|
|
|
- # Initial selection
|
|
- self.on_selection_changed(tv.get_selection())
|
|
- vbox.show_all()
|
|
+ vbox.pack_start(lbl, False, False, 0)
|
|
|
|
- def on_cell_data_cb(self, column, cell, model, piter, user_data):
|
|
- path = model.get_path(piter)
|
|
-
|
|
- if self._cursor and path == self._cursor.get_path():
|
|
- style = self._treeview.get_style()
|
|
- bg = style.bg[Gtk.StateType.PRELIGHT]
|
|
-
|
|
- cell.set_property('cell-background-gdk', bg)
|
|
- cell.set_property('style', Pango.Style.ITALIC)
|
|
- else:
|
|
- cell.set_property('cell-background-set', False)
|
|
- cell.set_property('style-set', False)
|
|
+ # Initial selection
|
|
+ self.on_selection_changed(tv.get_selection())
|
|
+ vbox.show_all()
|
|
|
|
- def _icon_from_stock(self, stock):
|
|
- theme = Gtk.icon_theme_get_default()
|
|
- size = Gtk.icon_size_lookup(Gtk.IconSize.MENU)
|
|
- pixbuf = theme.load_icon(stock, size[0], Gtk.IconLookupFlags.USE_BUILTIN)
|
|
+ def on_cell_data_cb(self, column, cell, model, piter, user_data):
|
|
+ path = model.get_path(piter)
|
|
|
|
- return pixbuf
|
|
+ if self._cursor and path == self._cursor.get_path():
|
|
+ style = self._treeview.get_style()
|
|
+ bg = style.bg[Gtk.StateType.PRELIGHT]
|
|
|
|
- def _list_dir(self, gfile):
|
|
- entries = []
|
|
+ cell.set_property('cell-background-gdk', bg)
|
|
+ cell.set_property('style', Pango.Style.ITALIC)
|
|
+ else:
|
|
+ cell.set_property('cell-background-set', False)
|
|
+ cell.set_property('style-set', False)
|
|
|
|
- try:
|
|
- ret = gfile.enumerate_children("standard::*", Gio.FileQueryInfoFlags.NONE, None)
|
|
- except GLib.GError:
|
|
- pass
|
|
+ def _icon_from_stock(self, stock):
|
|
+ theme = Gtk.icon_theme_get_default()
|
|
+ size = Gtk.icon_size_lookup(Gtk.IconSize.MENU)
|
|
+ pixbuf = theme.load_icon(stock, size[0], Gtk.IconLookupFlags.USE_BUILTIN)
|
|
|
|
- if isinstance(ret, Gio.FileEnumerator):
|
|
- while True:
|
|
- entry = ret.next_file(None)
|
|
+ return pixbuf
|
|
|
|
- if not entry:
|
|
- break
|
|
+ def _list_dir(self, gfile):
|
|
+ entries = []
|
|
|
|
- entries.append((gfile.get_child(entry.get_name()), entry))
|
|
- else:
|
|
- entries = ret
|
|
+ try:
|
|
+ ret = gfile.enumerate_children("standard::*", Gio.FileQueryInfoFlags.NONE, None)
|
|
+ except GLib.GError:
|
|
+ pass
|
|
|
|
- children = []
|
|
+ if isinstance(ret, Gio.FileEnumerator):
|
|
+ while True:
|
|
+ entry = ret.next_file(None)
|
|
|
|
- for entry in entries:
|
|
- children.append((entry[0], entry[1].get_name(), entry[1].get_file_type(), entry[1].get_icon()))
|
|
+ if not entry:
|
|
+ break
|
|
|
|
- return children
|
|
+ entries.append((gfile.get_child(entry.get_name()), entry))
|
|
+ else:
|
|
+ entries = ret
|
|
|
|
- def _compare_entries(self, a, b, lpart):
|
|
- if lpart in a:
|
|
- if lpart in b:
|
|
- return cmp(a.index(lpart), b.index(lpart))
|
|
- else:
|
|
- return -1
|
|
- elif lpart in b:
|
|
- return 1
|
|
- else:
|
|
- return 0
|
|
+ children = []
|
|
|
|
- def _match_glob(self, s, glob):
|
|
- if glob:
|
|
- glob += '*'
|
|
+ for entry in entries:
|
|
+ children.append((entry[0], entry[1].get_name(), entry[1].get_file_type(), entry[1].get_icon()))
|
|
|
|
- return fnmatch.fnmatch(s, glob)
|
|
+ return children
|
|
|
|
- def do_search_dir(self, parts, d):
|
|
- if not parts or not d:
|
|
- return []
|
|
+ def _compare_entries(self, a, b, lpart):
|
|
+ if lpart in a:
|
|
+ if lpart in b:
|
|
+ return cmp(a.index(lpart), b.index(lpart))
|
|
+ else:
|
|
+ return -1
|
|
+ elif lpart in b:
|
|
+ return 1
|
|
+ else:
|
|
+ return 0
|
|
|
|
- if not d in self._cache:
|
|
- entries = self._list_dir(d)
|
|
- entries.sort(lambda x, y: cmp(x[1].lower(), y[1].lower()))
|
|
+ def _match_glob(self, s, glob):
|
|
+ if glob:
|
|
+ glob += '*'
|
|
|
|
- self._cache[d] = entries
|
|
- else:
|
|
- entries = self._cache[d]
|
|
+ return fnmatch.fnmatch(s, glob)
|
|
|
|
- found = []
|
|
- newdirs = []
|
|
+ def do_search_dir(self, parts, d):
|
|
+ if not parts or not d:
|
|
+ return []
|
|
|
|
- lpart = parts[0].lower()
|
|
+ if not d in self._cache:
|
|
+ entries = self._list_dir(d)
|
|
+ entries.sort(lambda x, y: cmp(x[1].lower(), y[1].lower()))
|
|
|
|
- for entry in entries:
|
|
- if not entry:
|
|
- continue
|
|
+ self._cache[d] = entries
|
|
+ else:
|
|
+ entries = self._cache[d]
|
|
|
|
- lentry = entry[1].lower()
|
|
+ found = []
|
|
+ newdirs = []
|
|
|
|
- if not lpart or lpart in lentry or self._match_glob(lentry, lpart):
|
|
- if entry[2] == Gio.FileType.DIRECTORY:
|
|
- if len(parts) > 1:
|
|
- newdirs.append(entry[0])
|
|
- else:
|
|
- found.append(entry)
|
|
- elif entry[2] == Gio.FileType.REGULAR and \
|
|
- (not lpart or len(parts) == 1):
|
|
- found.append(entry)
|
|
+ lpart = parts[0].lower()
|
|
|
|
- found.sort(lambda a, b: self._compare_entries(a[1].lower(), b[1].lower(), lpart))
|
|
+ for entry in entries:
|
|
+ if not entry:
|
|
+ continue
|
|
|
|
- if lpart == '..':
|
|
- newdirs.append(d.get_parent())
|
|
+ lentry = entry[1].lower()
|
|
|
|
- for dd in newdirs:
|
|
- found.extend(self.do_search_dir(parts[1:], dd))
|
|
+ if not lpart or lpart in lentry or self._match_glob(lentry, lpart):
|
|
+ if entry[2] == Gio.FileType.DIRECTORY:
|
|
+ if len(parts) > 1:
|
|
+ newdirs.append(entry[0])
|
|
+ else:
|
|
+ found.append(entry)
|
|
+ elif entry[2] == Gio.FileType.REGULAR and \
|
|
+ (not lpart or len(parts) == 1):
|
|
+ found.append(entry)
|
|
|
|
- return found
|
|
+ found.sort(lambda a, b: self._compare_entries(a[1].lower(), b[1].lower(), lpart))
|
|
|
|
- def _replace_insensitive(self, s, find, rep):
|
|
- out = ''
|
|
- l = s.lower()
|
|
- find = find.lower()
|
|
- last = 0
|
|
+ if lpart == '..':
|
|
+ newdirs.append(d.get_parent())
|
|
|
|
- if len(find) == 0:
|
|
- return xml.sax.saxutils.escape(s)
|
|
+ for dd in newdirs:
|
|
+ found.extend(self.do_search_dir(parts[1:], dd))
|
|
|
|
- while True:
|
|
- m = l.find(find, last)
|
|
+ return found
|
|
|
|
- if m == -1:
|
|
- break
|
|
- else:
|
|
- out += xml.sax.saxutils.escape(s[last:m]) + rep % (xml.sax.saxutils.escape(s[m:m + len(find)]),)
|
|
- last = m + len(find)
|
|
+ def _replace_insensitive(self, s, find, rep):
|
|
+ out = ''
|
|
+ l = s.lower()
|
|
+ find = find.lower()
|
|
+ last = 0
|
|
|
|
- return out + xml.sax.saxutils.escape(s[last:])
|
|
+ if len(find) == 0:
|
|
+ return xml.sax.saxutils.escape(s)
|
|
|
|
+ while True:
|
|
+ m = l.find(find, last)
|
|
|
|
- def make_markup(self, parts, path):
|
|
- out = []
|
|
+ if m == -1:
|
|
+ break
|
|
+ else:
|
|
+ out += xml.sax.saxutils.escape(s[last:m]) + rep % (xml.sax.saxutils.escape(s[m:m + len(find)]),)
|
|
+ last = m + len(find)
|
|
|
|
- for i in range(0, len(parts)):
|
|
- out.append(self._replace_insensitive(path[i], parts[i], "<b>%s</b>"))
|
|
+ return out + xml.sax.saxutils.escape(s[last:])
|
|
|
|
- return os.sep.join(out)
|
|
+ def make_markup(self, parts, path):
|
|
+ out = []
|
|
|
|
- def _get_icon(self, f):
|
|
- query = f.query_info(Gio.FILE_ATTRIBUTE_STANDARD_ICON, Gio.FileQueryInfoFlags.NONE, None)
|
|
+ for i in range(0, len(parts)):
|
|
+ out.append(self._replace_insensitive(path[i], parts[i], "<b>%s</b>"))
|
|
|
|
- if not query:
|
|
- return None
|
|
- else:
|
|
- return query.get_icon()
|
|
+ return os.sep.join(out)
|
|
|
|
- def _make_parts(self, parent, child, pp):
|
|
- parts = []
|
|
+ def _get_icon(self, f):
|
|
+ query = f.query_info(Gio.FILE_ATTRIBUTE_STANDARD_ICON, Gio.FileQueryInfoFlags.NONE, None)
|
|
|
|
- # We went from parent, to child, using pp
|
|
- idx = len(pp) - 1
|
|
+ if not query:
|
|
+ return None
|
|
+ else:
|
|
+ return query.get_icon()
|
|
|
|
- while idx >= 0:
|
|
- if pp[idx] == '..':
|
|
- parts.insert(0, '..')
|
|
- else:
|
|
- parts.insert(0, child.get_basename())
|
|
- child = child.get_parent()
|
|
+ def _make_parts(self, parent, child, pp):
|
|
+ parts = []
|
|
|
|
- idx -= 1
|
|
+ # We went from parent, to child, using pp
|
|
+ idx = len(pp) - 1
|
|
|
|
- return parts
|
|
+ while idx >= 0:
|
|
+ if pp[idx] == '..':
|
|
+ parts.insert(0, '..')
|
|
+ else:
|
|
+ parts.insert(0, child.get_basename())
|
|
+ child = child.get_parent()
|
|
|
|
- def normalize_relative(self, parts):
|
|
- if not parts:
|
|
- return []
|
|
+ idx -= 1
|
|
|
|
- out = self.normalize_relative(parts[:-1])
|
|
+ return parts
|
|
|
|
- if parts[-1] == '..':
|
|
- if not out or (out[-1] == '..') or len(out) == 1:
|
|
- out.append('..')
|
|
- else:
|
|
- del out[-1]
|
|
- else:
|
|
- out.append(parts[-1])
|
|
+ def normalize_relative(self, parts):
|
|
+ if not parts:
|
|
+ return []
|
|
|
|
- return out
|
|
+ out = self.normalize_relative(parts[:-1])
|
|
|
|
- def _append_to_store(self, item):
|
|
- if not item in self._stored_items:
|
|
- self._store.append(item)
|
|
- self._stored_items[item] = True
|
|
+ if parts[-1] == '..':
|
|
+ if not out or (out[-1] == '..') or len(out) == 1:
|
|
+ out.append('..')
|
|
+ else:
|
|
+ del out[-1]
|
|
+ else:
|
|
+ out.append(parts[-1])
|
|
|
|
- def _clear_store(self):
|
|
- self._store.clear()
|
|
- self._stored_items = {}
|
|
+ return out
|
|
|
|
- def _show_virtuals(self):
|
|
- for d in self._dirs:
|
|
- if isinstance(d, VirtualDirectory):
|
|
- for entry in d.enumerate_children("standard::*", 0, None):
|
|
- self._append_to_store((entry[1].get_icon(), xml.sax.saxutils.escape(entry[1].get_name()), entry[0], entry[1].get_file_type()))
|
|
+ def _append_to_store(self, item):
|
|
+ if not item in self._stored_items:
|
|
+ self._store.append(item)
|
|
+ self._stored_items[item] = True
|
|
|
|
- def _set_busy(self, busy):
|
|
- if busy:
|
|
- self.get_window().set_cursor(self._busy_cursor)
|
|
- else:
|
|
- self.get_window().set_cursor(None)
|
|
+ def _clear_store(self):
|
|
+ self._store.clear()
|
|
+ self._stored_items = {}
|
|
|
|
- Gdk.flush()
|
|
+ def _show_virtuals(self):
|
|
+ for d in self._dirs:
|
|
+ if isinstance(d, VirtualDirectory):
|
|
+ for entry in d.enumerate_children("standard::*", 0, None):
|
|
+ self._append_to_store((entry[1].get_icon(), xml.sax.saxutils.escape(entry[1].get_name()), entry[0], entry[1].get_file_type()))
|
|
|
|
- def _remove_cursor(self):
|
|
- if self._cursor:
|
|
- path = self._cursor.get_path()
|
|
- self._cursor = None
|
|
+ def _set_busy(self, busy):
|
|
+ if busy:
|
|
+ self.get_window().set_cursor(self._busy_cursor)
|
|
+ else:
|
|
+ self.get_window().set_cursor(None)
|
|
|
|
- self._store.row_changed(path, self._store.get_iter(path))
|
|
+ Gdk.flush()
|
|
|
|
- def do_search(self):
|
|
- self._set_busy(True)
|
|
- self._remove_cursor()
|
|
+ def _remove_cursor(self):
|
|
+ if self._cursor:
|
|
+ path = self._cursor.get_path()
|
|
+ self._cursor = None
|
|
|
|
- text = self._entry.get_text().strip()
|
|
- self._clear_store()
|
|
+ self._store.row_changed(path, self._store.get_iter(path))
|
|
|
|
- if text == '':
|
|
- self._show_virtuals()
|
|
- else:
|
|
- parts = self.normalize_relative(text.split(os.sep))
|
|
- files = []
|
|
+ def do_search(self):
|
|
+ self._set_busy(True)
|
|
+ self._remove_cursor()
|
|
|
|
- for d in self._dirs:
|
|
- for entry in self.do_search_dir(parts, d):
|
|
- pathparts = self._make_parts(d, entry[0], parts)
|
|
- self._append_to_store((entry[3], self.make_markup(parts, pathparts), entry[0], entry[2]))
|
|
+ text = self._entry.get_text().strip()
|
|
+ self._clear_store()
|
|
|
|
- piter = self._store.get_iter_first()
|
|
+ if text == '':
|
|
+ self._show_virtuals()
|
|
+ else:
|
|
+ parts = self.normalize_relative(text.split(os.sep))
|
|
+ files = []
|
|
|
|
- if piter:
|
|
- self._treeview.get_selection().select_path(self._store.get_path(piter))
|
|
+ for d in self._dirs:
|
|
+ for entry in self.do_search_dir(parts, d):
|
|
+ pathparts = self._make_parts(d, entry[0], parts)
|
|
+ self._append_to_store((entry[3], self.make_markup(parts, pathparts), entry[0], entry[2]))
|
|
|
|
- self.get_window().set_cursor(None)
|
|
- self._set_busy(False)
|
|
+ piter = self._store.get_iter_first()
|
|
|
|
- def do_show(self):
|
|
- Gtk.Window.do_show(self)
|
|
+ if piter:
|
|
+ self._treeview.get_selection().select_path(self._store.get_path(piter))
|
|
|
|
- self._entry.grab_focus()
|
|
- self._entry.set_text("")
|
|
-
|
|
- self.do_search()
|
|
-
|
|
- def on_changed(self, editable):
|
|
- self.do_search()
|
|
- self.on_selection_changed(self._treeview.get_selection())
|
|
-
|
|
- def _shift_extend(self, towhere):
|
|
- selection = self._treeview.get_selection()
|
|
-
|
|
- if not self._shift_start:
|
|
- model, rows = selection.get_selected_rows()
|
|
- start = rows[0]
|
|
-
|
|
- self._shift_start = Gtk.TreeRowReference(self._store, start)
|
|
- else:
|
|
- start = self._shift_start.get_path()
|
|
-
|
|
- selection.unselect_all()
|
|
- selection.select_range(start, towhere)
|
|
-
|
|
- def _select_index(self, idx, hasctrl, hasshift):
|
|
- path = (idx,)
|
|
-
|
|
- if not (hasctrl or hasshift):
|
|
- self._treeview.get_selection().unselect_all()
|
|
-
|
|
- if hasshift:
|
|
- self._shift_extend(path)
|
|
- else:
|
|
- self._shift_start = None
|
|
-
|
|
- if not hasctrl:
|
|
- self._treeview.get_selection().select_path(path)
|
|
-
|
|
- self._treeview.scroll_to_cell(path, None, True, 0.5, 0)
|
|
- self._remove_cursor()
|
|
-
|
|
- if hasctrl or hasshift:
|
|
- self._cursor = Gtk.TreeRowReference(self._store, path)
|
|
-
|
|
- piter = self._store.get_iter(path)
|
|
- self._store.row_changed(path, piter)
|
|
-
|
|
- def _move_selection(self, howmany, hasctrl, hasshift):
|
|
- num = self._store.iter_n_children(None)
|
|
-
|
|
- if num == 0:
|
|
- return True
|
|
-
|
|
- # Test for cursor
|
|
- path = None
|
|
-
|
|
- if self._cursor:
|
|
- path = self._cursor.get_path()
|
|
- else:
|
|
- model, rows = self._treeview.get_selection().get_selected_rows()
|
|
-
|
|
- if len(rows) == 1:
|
|
- path = rows[0]
|
|
-
|
|
- if not path:
|
|
- if howmany > 0:
|
|
- self._select_index(0, hasctrl, hasshift)
|
|
- else:
|
|
- self._select_index(num - 1, hasctrl, hasshift)
|
|
- else:
|
|
- idx = path[0]
|
|
-
|
|
- if idx + howmany < 0:
|
|
- self._select_index(0, hasctrl, hasshift)
|
|
- elif idx + howmany >= num:
|
|
- self._select_index(num - 1, hasctrl, hasshift)
|
|
- else:
|
|
- self._select_index(idx + howmany, hasctrl, hasshift)
|
|
+ self.get_window().set_cursor(None)
|
|
+ self._set_busy(False)
|
|
|
|
- return True
|
|
+ def do_show(self):
|
|
+ Gtk.Window.do_show(self)
|
|
+
|
|
+ self._entry.grab_focus()
|
|
+ self._entry.set_text("")
|
|
+
|
|
+ self.do_search()
|
|
+
|
|
+ def on_changed(self, editable):
|
|
+ self.do_search()
|
|
+ self.on_selection_changed(self._treeview.get_selection())
|
|
+
|
|
+ def _shift_extend(self, towhere):
|
|
+ selection = self._treeview.get_selection()
|
|
+
|
|
+ if not self._shift_start:
|
|
+ model, rows = selection.get_selected_rows()
|
|
+ start = rows[0]
|
|
+
|
|
+ self._shift_start = Gtk.TreeRowReference(self._store, start)
|
|
+ else:
|
|
+ start = self._shift_start.get_path()
|
|
+
|
|
+ selection.unselect_all()
|
|
+ selection.select_range(start, towhere)
|
|
+
|
|
+ def _select_index(self, idx, hasctrl, hasshift):
|
|
+ path = (idx,)
|
|
+
|
|
+ if not (hasctrl or hasshift):
|
|
+ self._treeview.get_selection().unselect_all()
|
|
+
|
|
+ if hasshift:
|
|
+ self._shift_extend(path)
|
|
+ else:
|
|
+ self._shift_start = None
|
|
+
|
|
+ if not hasctrl:
|
|
+ self._treeview.get_selection().select_path(path)
|
|
+
|
|
+ self._treeview.scroll_to_cell(path, None, True, 0.5, 0)
|
|
+ self._remove_cursor()
|
|
+
|
|
+ if hasctrl or hasshift:
|
|
+ self._cursor = Gtk.TreeRowReference(self._store, path)
|
|
+
|
|
+ piter = self._store.get_iter(path)
|
|
+ self._store.row_changed(path, piter)
|
|
+
|
|
+ def _move_selection(self, howmany, hasctrl, hasshift):
|
|
+ num = self._store.iter_n_children(None)
|
|
|
|
- def _direct_file(self):
|
|
- uri = self._entry.get_text()
|
|
- gfile = None
|
|
-
|
|
- if Pluma.utils_is_valid_uri(uri):
|
|
- gfile = Gio.file_new_for_uri(uri)
|
|
- elif os.path.isabs(uri):
|
|
- f = Gio.file_new_for_uri(uri)
|
|
-
|
|
- if f.query_exists():
|
|
- gfile = f
|
|
-
|
|
- return gfile
|
|
-
|
|
- def _activate(self):
|
|
- model, rows = self._treeview.get_selection().get_selected_rows()
|
|
- ret = True
|
|
-
|
|
- for row in rows:
|
|
- s = model.get_iter(row)
|
|
- info = model.get(s, 2, 3)
|
|
-
|
|
- if info[1] != Gio.FileType.DIRECTORY:
|
|
- ret = ret and self._handler(info[0])
|
|
- else:
|
|
- text = self._entry.get_text()
|
|
-
|
|
- for i in range(len(text) - 1, -1, -1):
|
|
- if text[i] == os.sep:
|
|
- break
|
|
-
|
|
- self._entry.set_text(os.path.join(text[:i], os.path.basename(info[0].get_uri())) + os.sep)
|
|
- self._entry.set_position(-1)
|
|
- self._entry.grab_focus()
|
|
- return True
|
|
-
|
|
- if rows and ret:
|
|
- self.destroy()
|
|
-
|
|
- if not rows:
|
|
- gfile = self._direct_file()
|
|
-
|
|
- if gfile and self._handler(gfile):
|
|
- self.destroy()
|
|
- else:
|
|
- ret = False
|
|
- else:
|
|
- ret = False
|
|
-
|
|
- return ret
|
|
-
|
|
- def toggle_cursor(self):
|
|
- if not self._cursor:
|
|
- return
|
|
-
|
|
- path = self._cursor.get_path()
|
|
- selection = self._treeview.get_selection()
|
|
-
|
|
- if selection.path_is_selected(path):
|
|
- selection.unselect_path(path)
|
|
- else:
|
|
- selection.select_path(path)
|
|
-
|
|
- def on_key_press_event(self, widget, event):
|
|
- move_mapping = {
|
|
- Gdk.KEY_Down: 1,
|
|
- Gdk.KEY_Up: -1,
|
|
- Gdk.KEY_Page_Down: 5,
|
|
- Gdk.KEY_Page_Up: -5
|
|
- }
|
|
-
|
|
- if event.keyval == Gdk.KEY_Escape:
|
|
- self.destroy()
|
|
- return True
|
|
- elif event.keyval in move_mapping:
|
|
- return self._move_selection(move_mapping[event.keyval], event.state & Gdk.ModifierType.CONTROL_MASK, event.state & Gdk.ModifierType.SHIFT_MASK)
|
|
- elif event.keyval in [Gdk.KEY_Return, Gdk.KEY_KP_Enter, Gdk.KEY_Tab, Gdk.KEY_ISO_Left_Tab]:
|
|
- return self._activate()
|
|
- elif event.keyval == Gdk.KEY_space and event.state & Gdk.ModifierType.CONTROL_MASK:
|
|
- self.toggle_cursor()
|
|
-
|
|
- return False
|
|
-
|
|
- def on_row_activated(self, view, path, column):
|
|
- self._activate()
|
|
-
|
|
- def do_response(self, response):
|
|
- if response != Gtk.ResponseType.ACCEPT or not self._activate():
|
|
- self.destroy()
|
|
-
|
|
- def do_configure_event(self, event):
|
|
- if self.get_realized():
|
|
- alloc = self.get_allocation()
|
|
- self._size = (alloc.width, alloc.height)
|
|
-
|
|
- return Gtk.Dialog.do_configure_event(self, event)
|
|
-
|
|
- def on_selection_changed(self, selection):
|
|
- model, rows = selection.get_selected_rows()
|
|
-
|
|
- gfile = None
|
|
- fname = None
|
|
-
|
|
- if not rows:
|
|
- gfile = self._direct_file()
|
|
- elif len(rows) == 1:
|
|
- gfile = model.get(model.get_iter(rows[0]), 2)[0]
|
|
- else:
|
|
- fname = ''
|
|
-
|
|
- if gfile:
|
|
- if gfile.is_native():
|
|
- fname = xml.sax.saxutils.escape(gfile.get_path())
|
|
- else:
|
|
- fname = xml.sax.saxutils.escape(gfile.get_uri())
|
|
-
|
|
- self._open_button.set_sensitive(fname != None)
|
|
- self._info_label.set_markup(fname or '')
|
|
-
|
|
- def on_focus_entry(self, group, accel, keyval, modifier):
|
|
+ if num == 0:
|
|
+ return True
|
|
+
|
|
+ # Test for cursor
|
|
+ path = None
|
|
+
|
|
+ if self._cursor:
|
|
+ path = self._cursor.get_path()
|
|
+ else:
|
|
+ model, rows = self._treeview.get_selection().get_selected_rows()
|
|
+
|
|
+ if len(rows) == 1:
|
|
+ path = rows[0]
|
|
+
|
|
+ if not path:
|
|
+ if howmany > 0:
|
|
+ self._select_index(0, hasctrl, hasshift)
|
|
+ else:
|
|
+ self._select_index(num - 1, hasctrl, hasshift)
|
|
+ else:
|
|
+ idx = path[0]
|
|
+
|
|
+ if idx + howmany < 0:
|
|
+ self._select_index(0, hasctrl, hasshift)
|
|
+ elif idx + howmany >= num:
|
|
+ self._select_index(num - 1, hasctrl, hasshift)
|
|
+ else:
|
|
+ self._select_index(idx + howmany, hasctrl, hasshift)
|
|
+
|
|
+ return True
|
|
+
|
|
+ def _direct_file(self):
|
|
+ uri = self._entry.get_text()
|
|
+ gfile = None
|
|
+
|
|
+ if Pluma.utils_is_valid_uri(uri):
|
|
+ gfile = Gio.file_new_for_uri(uri)
|
|
+ elif os.path.isabs(uri):
|
|
+ f = Gio.file_new_for_uri(uri)
|
|
+
|
|
+ if f.query_exists():
|
|
+ gfile = f
|
|
+
|
|
+ return gfile
|
|
+
|
|
+ def _activate(self):
|
|
+ model, rows = self._treeview.get_selection().get_selected_rows()
|
|
+ ret = True
|
|
+
|
|
+ for row in rows:
|
|
+ s = model.get_iter(row)
|
|
+ info = model.get(s, 2, 3)
|
|
+
|
|
+ if info[1] != Gio.FileType.DIRECTORY:
|
|
+ ret = ret and self._handler(info[0])
|
|
+ else:
|
|
+ text = self._entry.get_text()
|
|
+
|
|
+ for i in range(len(text) - 1, -1, -1):
|
|
+ if text[i] == os.sep:
|
|
+ break
|
|
+
|
|
+ self._entry.set_text(os.path.join(text[:i], os.path.basename(info[0].get_uri())) + os.sep)
|
|
+ self._entry.set_position(-1)
|
|
self._entry.grab_focus()
|
|
+ return True
|
|
|
|
-# ex:ts=8:et:
|
|
+ if rows and ret:
|
|
+ self.destroy()
|
|
+
|
|
+ if not rows:
|
|
+ gfile = self._direct_file()
|
|
+
|
|
+ if gfile and self._handler(gfile):
|
|
+ self.destroy()
|
|
+ else:
|
|
+ ret = False
|
|
+ else:
|
|
+ ret = False
|
|
+
|
|
+ return ret
|
|
+
|
|
+ def toggle_cursor(self):
|
|
+ if not self._cursor:
|
|
+ return
|
|
+
|
|
+ path = self._cursor.get_path()
|
|
+ selection = self._treeview.get_selection()
|
|
+
|
|
+ if selection.path_is_selected(path):
|
|
+ selection.unselect_path(path)
|
|
+ else:
|
|
+ selection.select_path(path)
|
|
+
|
|
+ def on_key_press_event(self, widget, event):
|
|
+ move_mapping = {
|
|
+ Gdk.KEY_Down: 1,
|
|
+ Gdk.KEY_Up: -1,
|
|
+ Gdk.KEY_Page_Down: 5,
|
|
+ Gdk.KEY_Page_Up: -5
|
|
+ }
|
|
+
|
|
+ if event.keyval == Gdk.KEY_Escape:
|
|
+ self.destroy()
|
|
+ return True
|
|
+ elif event.keyval in move_mapping:
|
|
+ return self._move_selection(move_mapping[event.keyval], event.state & Gdk.ModifierType.CONTROL_MASK, event.state & Gdk.ModifierType.SHIFT_MASK)
|
|
+ elif event.keyval in [Gdk.KEY_Return, Gdk.KEY_KP_Enter, Gdk.KEY_Tab, Gdk.KEY_ISO_Left_Tab]:
|
|
+ return self._activate()
|
|
+ elif event.keyval == Gdk.KEY_space and event.state & Gdk.ModifierType.CONTROL_MASK:
|
|
+ self.toggle_cursor()
|
|
+
|
|
+ return False
|
|
+
|
|
+ def on_row_activated(self, view, path, column):
|
|
+ self._activate()
|
|
+
|
|
+ def do_response(self, response):
|
|
+ if response != Gtk.ResponseType.ACCEPT or not self._activate():
|
|
+ self.destroy()
|
|
+
|
|
+ def do_configure_event(self, event):
|
|
+ if self.get_realized():
|
|
+ alloc = self.get_allocation()
|
|
+ self._size = (alloc.width, alloc.height)
|
|
+
|
|
+ return Gtk.Dialog.do_configure_event(self, event)
|
|
+
|
|
+ def on_selection_changed(self, selection):
|
|
+ model, rows = selection.get_selected_rows()
|
|
+
|
|
+ gfile = None
|
|
+ fname = None
|
|
+
|
|
+ if not rows:
|
|
+ gfile = self._direct_file()
|
|
+ elif len(rows) == 1:
|
|
+ gfile = model.get(model.get_iter(rows[0]), 2)[0]
|
|
+ else:
|
|
+ fname = ''
|
|
+
|
|
+ if gfile:
|
|
+ if gfile.is_native():
|
|
+ fname = xml.sax.saxutils.escape(gfile.get_path())
|
|
+ else:
|
|
+ fname = xml.sax.saxutils.escape(gfile.get_uri())
|
|
+
|
|
+ self._open_button.set_sensitive(fname != None)
|
|
+ self._info_label.set_markup(fname or '')
|
|
+
|
|
+ def on_focus_entry(self, group, accel, keyval, modifier):
|
|
+ self._entry.grab_focus()
|
|
+
|
|
+# ex:ts=4:et:
|
|
diff --git a/plugins/quickopen/quickopen/virtualdirs.py b/plugins/quickopen/quickopen/virtualdirs.py
|
|
old mode 100755
|
|
new mode 100644
|
|
index a2d6985..53d716a
|
|
--- a/plugins/quickopen/quickopen/virtualdirs.py
|
|
+++ b/plugins/quickopen/quickopen/virtualdirs.py
|
|
@@ -20,64 +20,64 @@
|
|
from gi.repository import Gio, Gtk
|
|
|
|
class VirtualDirectory(object):
|
|
- def __init__(self, name):
|
|
- self._name = name
|
|
- self._children = []
|
|
+ def __init__(self, name):
|
|
+ self._name = name
|
|
+ self._children = []
|
|
|
|
- def get_uri(self):
|
|
- return 'virtual://' + self._name
|
|
+ def get_uri(self):
|
|
+ return 'virtual://' + self._name
|
|
|
|
- def get_parent(self):
|
|
- return None
|
|
+ def get_parent(self):
|
|
+ return None
|
|
|
|
- def enumerate_children(self, attr, flags, callback):
|
|
- return self._children
|
|
+ def enumerate_children(self, attr, flags, callback):
|
|
+ return self._children
|
|
|
|
- def append(self, child):
|
|
- if not child.is_native():
|
|
- return
|
|
+ def append(self, child):
|
|
+ if not child.is_native():
|
|
+ return
|
|
|
|
- try:
|
|
- info = child.query_info("standard::*", Gio.FileQueryInfoFlags.NONE, None)
|
|
+ try:
|
|
+ info = child.query_info("standard::*", Gio.FileQueryInfoFlags.NONE, None)
|
|
|
|
- if info:
|
|
- self._children.append((child, info))
|
|
- except:
|
|
- pass
|
|
+ if info:
|
|
+ self._children.append((child, info))
|
|
+ except:
|
|
+ pass
|
|
|
|
class RecentDocumentsDirectory(VirtualDirectory):
|
|
- def __init__(self, maxitems=10):
|
|
- VirtualDirectory.__init__(self, 'recent')
|
|
+ def __init__(self, maxitems=10):
|
|
+ VirtualDirectory.__init__(self, 'recent')
|
|
|
|
- self._maxitems = maxitems
|
|
- self.fill()
|
|
+ self._maxitems = maxitems
|
|
+ self.fill()
|
|
|
|
- def fill(self):
|
|
- manager = Gtk.RecentManager.get_default()
|
|
+ def fill(self):
|
|
+ manager = Gtk.RecentManager.get_default()
|
|
|
|
- items = manager.get_items()
|
|
- items.sort(lambda a, b: cmp(b.get_visited(), a.get_visited()))
|
|
+ items = manager.get_items()
|
|
+ items.sort(lambda a, b: cmp(b.get_visited(), a.get_visited()))
|
|
|
|
- added = 0
|
|
+ added = 0
|
|
|
|
- for item in items:
|
|
- if item.has_group('pluma'):
|
|
- self.append(Gio.file_new_for_uri(item.get_uri()))
|
|
- added += 1
|
|
+ for item in items:
|
|
+ if item.has_group('pluma'):
|
|
+ self.append(Gio.file_new_for_uri(item.get_uri()))
|
|
+ added += 1
|
|
|
|
- if added >= self._maxitems:
|
|
- break
|
|
+ if added >= self._maxitems:
|
|
+ break
|
|
|
|
class CurrentDocumentsDirectory(VirtualDirectory):
|
|
- def __init__(self, window):
|
|
- VirtualDirectory.__init__(self, 'documents')
|
|
+ def __init__(self, window):
|
|
+ VirtualDirectory.__init__(self, 'documents')
|
|
|
|
- self.fill(window)
|
|
+ self.fill(window)
|
|
|
|
- def fill(self, window):
|
|
- for doc in window.get_documents():
|
|
- location = doc.get_location()
|
|
- if location:
|
|
- self.append(location)
|
|
+ def fill(self, window):
|
|
+ for doc in window.get_documents():
|
|
+ location = doc.get_location()
|
|
+ if location:
|
|
+ self.append(location)
|
|
|
|
-# ex:ts=8:et:
|
|
+# ex:ts=4:et:
|
|
diff --git a/plugins/quickopen/quickopen/windowhelper.py b/plugins/quickopen/quickopen/windowhelper.py
|
|
old mode 100755
|
|
new mode 100644
|
|
index c23629c..19e44cb
|
|
--- a/plugins/quickopen/quickopen/windowhelper.py
|
|
+++ b/plugins/quickopen/quickopen/windowhelper.py
|
|
@@ -27,7 +27,7 @@ ui_str = """<ui>
|
|
<menubar name="MenuBar">
|
|
<menu name="FileMenu" action="File">
|
|
<placeholder name="FileOps_2">
|
|
- <menuitem name="QuickOpen" action="QuickOpen"/>
|
|
+ <menuitem name="QuickOpen" action="QuickOpen"/>
|
|
</placeholder>
|
|
</menu>
|
|
</menubar>
|
|
@@ -35,157 +35,157 @@ ui_str = """<ui>
|
|
"""
|
|
|
|
class WindowHelper:
|
|
- def __init__(self, window, plugin):
|
|
- self._window = window
|
|
- self._plugin = plugin
|
|
+ def __init__(self, window, plugin):
|
|
+ self._window = window
|
|
+ self._plugin = plugin
|
|
|
|
- self._popup = None
|
|
- self._install_menu()
|
|
+ self._popup = None
|
|
+ self._install_menu()
|
|
|
|
- def deactivate(self):
|
|
- self._uninstall_menu()
|
|
- self._window = None
|
|
- self._plugin = None
|
|
+ def deactivate(self):
|
|
+ self._uninstall_menu()
|
|
+ self._window = None
|
|
+ self._plugin = None
|
|
|
|
- def update_ui(self):
|
|
- pass
|
|
+ def update_ui(self):
|
|
+ pass
|
|
|
|
- def _uninstall_menu(self):
|
|
- manager = self._window.get_ui_manager()
|
|
+ def _uninstall_menu(self):
|
|
+ manager = self._window.get_ui_manager()
|
|
|
|
- manager.remove_ui(self._ui_id)
|
|
- manager.remove_action_group(self._action_group)
|
|
+ manager.remove_ui(self._ui_id)
|
|
+ manager.remove_action_group(self._action_group)
|
|
|
|
- manager.ensure_update()
|
|
+ manager.ensure_update()
|
|
|
|
- def _install_menu(self):
|
|
- manager = self._window.get_ui_manager()
|
|
- self._action_group = Gtk.ActionGroup("PlumaQuickOpenPluginActions")
|
|
- self._action_group.add_actions([
|
|
- ("QuickOpen", Gtk.STOCK_OPEN, _("Quick open"),
|
|
- '<Ctrl><Alt>O', _("Quickly open documents"),
|
|
- self.on_quick_open_activate)
|
|
- ])
|
|
+ def _install_menu(self):
|
|
+ manager = self._window.get_ui_manager()
|
|
+ self._action_group = Gtk.ActionGroup("PlumaQuickOpenPluginActions")
|
|
+ self._action_group.add_actions([
|
|
+ ("QuickOpen", Gtk.STOCK_OPEN, _("Quick open"),
|
|
+ '<Ctrl><Alt>O', _("Quickly open documents"),
|
|
+ self.on_quick_open_activate)
|
|
+ ])
|
|
|
|
- manager.insert_action_group(self._action_group, -1)
|
|
- self._ui_id = manager.add_ui_from_string(ui_str)
|
|
+ manager.insert_action_group(self._action_group, -1)
|
|
+ self._ui_id = manager.add_ui_from_string(ui_str)
|
|
|
|
- def _create_popup(self):
|
|
- paths = []
|
|
+ def _create_popup(self):
|
|
+ paths = []
|
|
|
|
- # Open documents
|
|
- paths.append(CurrentDocumentsDirectory(self._window))
|
|
+ # Open documents
|
|
+ paths.append(CurrentDocumentsDirectory(self._window))
|
|
|
|
- doc = self._window.get_active_document()
|
|
+ doc = self._window.get_active_document()
|
|
|
|
- # Current document directory
|
|
- if doc and doc.is_local():
|
|
- gfile = doc.get_location()
|
|
- paths.append(gfile.get_parent())
|
|
+ # Current document directory
|
|
+ if doc and doc.is_local():
|
|
+ gfile = doc.get_location()
|
|
+ paths.append(gfile.get_parent())
|
|
|
|
- # File browser root directory
|
|
- bus = self._window.get_message_bus()
|
|
+ # File browser root directory
|
|
+ bus = self._window.get_message_bus()
|
|
|
|
- try:
|
|
- msg = bus.send_sync('/plugins/filebrowser', 'get_root')
|
|
+ try:
|
|
+ msg = bus.send_sync('/plugins/filebrowser', 'get_root')
|
|
|
|
- if msg:
|
|
- uri = msg.get_value('uri')
|
|
+ if msg:
|
|
+ uri = msg.get_value('uri')
|
|
|
|
- if uri:
|
|
- gfile = Gio.file_new_for_uri(uri)
|
|
+ if uri:
|
|
+ gfile = Gio.file_new_for_uri(uri)
|
|
|
|
- if gfile.is_native():
|
|
- paths.append(gfile)
|
|
+ if gfile.is_native():
|
|
+ paths.append(gfile)
|
|
|
|
- except StandardError:
|
|
- pass
|
|
+ except StandardError:
|
|
+ pass
|
|
|
|
- # Recent documents
|
|
- paths.append(RecentDocumentsDirectory())
|
|
+ # Recent documents
|
|
+ paths.append(RecentDocumentsDirectory())
|
|
|
|
- # Local bookmarks
|
|
- for path in self._local_bookmarks():
|
|
- paths.append(path)
|
|
+ # Local bookmarks
|
|
+ for path in self._local_bookmarks():
|
|
+ paths.append(path)
|
|
|
|
- # Desktop directory
|
|
- desktopdir = self._desktop_dir()
|
|
+ # Desktop directory
|
|
+ desktopdir = self._desktop_dir()
|
|
|
|
- if desktopdir:
|
|
- paths.append(Gio.file_new_for_path(desktopdir))
|
|
+ if desktopdir:
|
|
+ paths.append(Gio.file_new_for_path(desktopdir))
|
|
|
|
- # Home directory
|
|
- paths.append(Gio.file_new_for_path(os.path.expanduser('~')))
|
|
+ # Home directory
|
|
+ paths.append(Gio.file_new_for_path(os.path.expanduser('~')))
|
|
|
|
- self._popup = Popup(self._window, paths, self.on_activated)
|
|
+ self._popup = Popup(self._window, paths, self.on_activated)
|
|
|
|
- self._popup.set_default_size(*self._plugin.get_popup_size())
|
|
- self._popup.set_transient_for(self._window)
|
|
- self._popup.set_position(Gtk.WindowPosition.CENTER_ON_PARENT)
|
|
+ self._popup.set_default_size(*self._plugin.get_popup_size())
|
|
+ self._popup.set_transient_for(self._window)
|
|
+ self._popup.set_position(Gtk.WindowPosition.CENTER_ON_PARENT)
|
|
|
|
- self._window.get_group().add_window(self._popup)
|
|
+ self._window.get_group().add_window(self._popup)
|
|
|
|
- self._popup.connect('destroy', self.on_popup_destroy)
|
|
+ self._popup.connect('destroy', self.on_popup_destroy)
|
|
|
|
- def _local_bookmarks(self):
|
|
- filename = os.path.expanduser('~/.gtk-bookmarks')
|
|
+ def _local_bookmarks(self):
|
|
+ filename = os.path.expanduser('~/.gtk-bookmarks')
|
|
|
|
- if not os.path.isfile(filename):
|
|
- return []
|
|
+ if not os.path.isfile(filename):
|
|
+ return []
|
|
|
|
- paths = []
|
|
+ paths = []
|
|
|
|
- for line in file(filename, 'r').xreadlines():
|
|
- uri = line.strip().split(" ")[0]
|
|
- f = Gio.file_new_for_uri(uri)
|
|
+ for line in file(filename, 'r').xreadlines():
|
|
+ uri = line.strip().split(" ")[0]
|
|
+ f = Gio.file_new_for_uri(uri)
|
|
|
|
- if f.is_native():
|
|
- try:
|
|
- info = f.query_info(Gio.FILE_ATTRIBUTE_STANDARD_TYPE, Gio.FileQueryInfoFlags.NONE, None)
|
|
+ if f.is_native():
|
|
+ try:
|
|
+ info = f.query_info(Gio.FILE_ATTRIBUTE_STANDARD_TYPE, Gio.FileQueryInfoFlags.NONE, None)
|
|
|
|
- if info and info.get_file_type() == Gio.FileType.DIRECTORY:
|
|
- paths.append(f)
|
|
- except GLib.GError:
|
|
- pass
|
|
+ if info and info.get_file_type() == Gio.FileType.DIRECTORY:
|
|
+ paths.append(f)
|
|
+ except GLib.GError:
|
|
+ pass
|
|
|
|
- return paths
|
|
+ return paths
|
|
|
|
- def _desktop_dir(self):
|
|
- config = os.getenv('XDG_CONFIG_HOME')
|
|
+ def _desktop_dir(self):
|
|
+ config = os.getenv('XDG_CONFIG_HOME')
|
|
|
|
- if not config:
|
|
- config = os.path.expanduser('~/.config')
|
|
+ if not config:
|
|
+ config = os.path.expanduser('~/.config')
|
|
|
|
- config = os.path.join(config, 'user-dirs.dirs')
|
|
- desktopdir = None
|
|
+ config = os.path.join(config, 'user-dirs.dirs')
|
|
+ desktopdir = None
|
|
|
|
- if os.path.isfile(config):
|
|
- for line in file(config, 'r').xreadlines():
|
|
- line = line.strip()
|
|
+ if os.path.isfile(config):
|
|
+ for line in file(config, 'r').xreadlines():
|
|
+ line = line.strip()
|
|
|
|
- if line.startswith('XDG_DESKTOP_DIR'):
|
|
- parts = line.split('=', 1)
|
|
- desktopdir = os.path.expandvars(parts[1].strip('"').strip("'"))
|
|
- break
|
|
+ if line.startswith('XDG_DESKTOP_DIR'):
|
|
+ parts = line.split('=', 1)
|
|
+ desktopdir = os.path.expandvars(parts[1].strip('"').strip("'"))
|
|
+ break
|
|
|
|
- if not desktopdir:
|
|
- desktopdir = os.path.expanduser('~/Desktop')
|
|
+ if not desktopdir:
|
|
+ desktopdir = os.path.expanduser('~/Desktop')
|
|
|
|
- return desktopdir
|
|
+ return desktopdir
|
|
|
|
- # Callbacks
|
|
- def on_quick_open_activate(self, action):
|
|
- if not self._popup:
|
|
- self._create_popup()
|
|
+ # Callbacks
|
|
+ def on_quick_open_activate(self, action):
|
|
+ if not self._popup:
|
|
+ self._create_popup()
|
|
|
|
- self._popup.show()
|
|
+ self._popup.show()
|
|
|
|
- def on_popup_destroy(self, popup):
|
|
- self._plugin.set_popup_size(popup.get_final_size())
|
|
- self._popup = None
|
|
+ def on_popup_destroy(self, popup):
|
|
+ self._plugin.set_popup_size(popup.get_final_size())
|
|
+ self._popup = None
|
|
|
|
- def on_activated(self, gfile):
|
|
- Pluma.commands_load_uri(self._window, gfile.get_uri(), None, -1)
|
|
- return True
|
|
+ def on_activated(self, gfile):
|
|
+ Pluma.commands_load_uri(self._window, gfile.get_uri(), None, -1)
|
|
+ return True
|
|
|
|
-# ex:ts=8:et:
|
|
+# ex:ts=4:et:
|
|
diff --git a/plugins/snippets/snippets/Completion.py b/plugins/snippets/snippets/Completion.py
|
|
old mode 100755
|
|
new mode 100644
|
|
index 1788ecd..a860d21
|
|
--- a/plugins/snippets/snippets/Completion.py
|
|
+++ b/plugins/snippets/snippets/Completion.py
|
|
@@ -5,159 +5,159 @@ 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)
|
|
- 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']
|
|
+ __gtype_name__ = "PlumaSnippetsProposal"
|
|
+
|
|
+ def __init__(self, snippet):
|
|
+ GObject.Object.__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.Object, GtkSource.CompletionProvider):
|
|
- __gtype_name__ = "PlumaSnippetsProvider"
|
|
-
|
|
- def __init__(self, name, language_id, handler):
|
|
- GObject.Object.__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.IconTheme.get_default()
|
|
- f, w, h = Gtk.icon_size_lookup(Gtk.IconSize.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):
|
|
- (valid_context, it) = context.get_iter()
|
|
- if not valid_context:
|
|
- return None
|
|
-
|
|
- 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 (False, None)
|
|
-
|
|
- return (True, self.mark.get_buffer().get_iter_at_mark(self.mark))
|
|
-
|
|
- def do_match(self, context):
|
|
- return True
|
|
-
|
|
- 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):
|
|
- proposals = self.get_proposals(self.get_word(context))
|
|
- 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 = Pluma.View.new_with_buffer(Pluma.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(), 0.0, False, 0.5, 0.5)
|
|
-
|
|
- def do_get_icon(self):
|
|
- return self.icon
|
|
-
|
|
- def do_get_activation(self):
|
|
- return GtkSource.CompletionActivation.USER_REQUESTED
|
|
+ __gtype_name__ = "PlumaSnippetsProvider"
|
|
+
|
|
+ def __init__(self, name, language_id, handler):
|
|
+ GObject.Object.__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.IconTheme.get_default()
|
|
+ f, w, h = Gtk.icon_size_lookup(Gtk.IconSize.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):
|
|
+ (valid_context, it) = context.get_iter()
|
|
+ if not valid_context:
|
|
+ return None
|
|
+
|
|
+ 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 (False, None)
|
|
+
|
|
+ return (True, self.mark.get_buffer().get_iter_at_mark(self.mark))
|
|
+
|
|
+ def do_match(self, context):
|
|
+ return True
|
|
+
|
|
+ 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):
|
|
+ proposals = self.get_proposals(self.get_word(context))
|
|
+ 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 = Pluma.View.new_with_buffer(Pluma.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(), 0.0, False, 0.5, 0.5)
|
|
+
|
|
+ def do_get_icon(self):
|
|
+ return self.icon
|
|
+
|
|
+ def do_get_activation(self):
|
|
+ return GtkSource.CompletionActivation.USER_REQUESTED
|
|
|
|
class Defaults(GObject.Object, GtkSource.CompletionProvider):
|
|
- __gtype_name__ = "PlumaSnippetsDefaultsProvider"
|
|
-
|
|
- def __init__(self, handler):
|
|
- GObject.Object.__init__(self)
|
|
-
|
|
- self.handler = handler
|
|
- self.proposals = []
|
|
-
|
|
- def set_defaults(self, defaults):
|
|
- self.proposals = []
|
|
-
|
|
- for d in defaults:
|
|
- self.proposals.append(GtkSource.CompletionItem.new(d, d, None, None))
|
|
-
|
|
- 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 GtkSource.CompletionActivation.USER_REQUESTED
|
|
-
|
|
-# ex:ts=8:et:
|
|
+ __gtype_name__ = "PlumaSnippetsDefaultsProvider"
|
|
+
|
|
+ def __init__(self, handler):
|
|
+ GObject.Object.__init__(self)
|
|
+
|
|
+ self.handler = handler
|
|
+ self.proposals = []
|
|
+
|
|
+ def set_defaults(self, defaults):
|
|
+ self.proposals = []
|
|
+
|
|
+ for d in defaults:
|
|
+ self.proposals.append(GtkSource.CompletionItem.new(d, d, None, None))
|
|
+
|
|
+ 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 GtkSource.CompletionActivation.USER_REQUESTED
|
|
+
|
|
+# ex:ts=4:et:
|
|
diff --git a/plugins/snippets/snippets/Document.py b/plugins/snippets/snippets/Document.py
|
|
old mode 100755
|
|
new mode 100644
|
|
index 07faf2a..f1cc065
|
|
--- a/plugins/snippets/snippets/Document.py
|
|
+++ b/plugins/snippets/snippets/Document.py
|
|
@@ -26,1070 +26,1068 @@ from Placeholder import *
|
|
import Completion
|
|
|
|
class DynamicSnippet(dict):
|
|
- def __init__(self, text):
|
|
- self['text'] = text
|
|
- self.valid = True
|
|
+ def __init__(self, text):
|
|
+ self['text'] = text
|
|
+ self.valid = True
|
|
|
|
class Document:
|
|
- TAB_KEY_VAL = (Gdk.KEY_Tab, \
|
|
- Gdk.KEY_ISO_Left_Tab)
|
|
- SPACE_KEY_VAL = (Gdk.KEY_space,)
|
|
-
|
|
- def __init__(self, instance, view):
|
|
- self.view = None
|
|
- self.instance = instance
|
|
-
|
|
- self.placeholders = []
|
|
- self.active_snippets = []
|
|
- self.active_placeholder = None
|
|
- self.signal_ids = {}
|
|
-
|
|
- self.ordered_placeholders = []
|
|
- self.update_placeholders = []
|
|
- self.jump_placeholders = []
|
|
- self.language_id = 0
|
|
- self.timeout_update_id = 0
|
|
-
|
|
- self.provider = Completion.Provider(_('Snippets'), self.language_id, self.on_proposal_activated)
|
|
-
|
|
- # Always have a reference to the global snippets
|
|
- Library().ref(None)
|
|
- self.set_view(view)
|
|
-
|
|
- # Stop controlling the view. Remove all active snippets, remove references
|
|
- # to the view and the plugin instance, disconnect all signal handlers
|
|
- def stop(self):
|
|
- if self.timeout_update_id != 0:
|
|
- GLib.source_remove(self.timeout_update_id)
|
|
- self.timeout_update_id = 0
|
|
- del self.update_placeholders[:]
|
|
- del self.jump_placeholders[:]
|
|
-
|
|
- # Always release the reference to the global snippets
|
|
- Library().unref(None)
|
|
- self.set_view(None)
|
|
- self.instance = None
|
|
- self.active_placeholder = None
|
|
+ TAB_KEY_VAL = (Gdk.KEY_Tab, \
|
|
+ Gdk.KEY_ISO_Left_Tab)
|
|
+ SPACE_KEY_VAL = (Gdk.KEY_space,)
|
|
+
|
|
+ def __init__(self, instance, view):
|
|
+ self.view = None
|
|
+ self.instance = instance
|
|
+
|
|
+ self.placeholders = []
|
|
+ self.active_snippets = []
|
|
+ self.active_placeholder = None
|
|
+ self.signal_ids = {}
|
|
+
|
|
+ self.ordered_placeholders = []
|
|
+ self.update_placeholders = []
|
|
+ self.jump_placeholders = []
|
|
+ self.language_id = 0
|
|
+ self.timeout_update_id = 0
|
|
+
|
|
+ self.provider = Completion.Provider(_('Snippets'), self.language_id, self.on_proposal_activated)
|
|
+
|
|
+ # Always have a reference to the global snippets
|
|
+ Library().ref(None)
|
|
+ self.set_view(view)
|
|
+
|
|
+ # Stop controlling the view. Remove all active snippets, remove references
|
|
+ # to the view and the plugin instance, disconnect all signal handlers
|
|
+ def stop(self):
|
|
+ if self.timeout_update_id != 0:
|
|
+ GLib.source_remove(self.timeout_update_id)
|
|
+ self.timeout_update_id = 0
|
|
+ del self.update_placeholders[:]
|
|
+ del self.jump_placeholders[:]
|
|
+
|
|
+ # Always release the reference to the global snippets
|
|
+ Library().unref(None)
|
|
+ self.set_view(None)
|
|
+ self.instance = None
|
|
+ self.active_placeholder = None
|
|
+
|
|
+ def disconnect_signal(self, obj, signal):
|
|
+ if (obj, signal) in self.signal_ids:
|
|
+ obj.disconnect(self.signal_ids[(obj, signal)])
|
|
+ del self.signal_ids[(obj, signal)]
|
|
+
|
|
+ def connect_signal(self, obj, signal, cb):
|
|
+ self.disconnect_signal(obj, signal)
|
|
+ self.signal_ids[(obj, signal)] = obj.connect(signal, cb)
|
|
+
|
|
+ def connect_signal_after(self, obj, signal, cb):
|
|
+ self.disconnect_signal(obj, signal)
|
|
+ self.signal_ids[(obj, signal)] = obj.connect_after(signal, cb)
|
|
|
|
- def disconnect_signal(self, obj, signal):
|
|
- if (obj, signal) in self.signal_ids:
|
|
- obj.disconnect(self.signal_ids[(obj, signal)])
|
|
- del self.signal_ids[(obj, signal)]
|
|
-
|
|
- def connect_signal(self, obj, signal, cb):
|
|
- self.disconnect_signal(obj, signal)
|
|
- self.signal_ids[(obj, signal)] = obj.connect(signal, cb)
|
|
-
|
|
- def connect_signal_after(self, obj, signal, cb):
|
|
- self.disconnect_signal(obj, signal)
|
|
- self.signal_ids[(obj, signal)] = obj.connect_after(signal, cb)
|
|
-
|
|
- # Set the view to be controlled. Installs signal handlers and sets current
|
|
- # language. If there is already a view set this function will first remove
|
|
- # all currently active snippets and disconnect all current signals. So
|
|
- # self.set_view(None) will effectively remove all the control from the
|
|
- # current view
|
|
- def _set_view(self, view):
|
|
- if self.view:
|
|
- buf = self.view.get_buffer()
|
|
-
|
|
- # Remove signals
|
|
- signals = {self.view: ('key-press-event', 'destroy',
|
|
- 'notify::editable', 'drag-data-received', 'expose-event'),
|
|
- buf: ('notify::language', 'changed', 'cursor-moved', 'insert-text'),
|
|
- self.view.get_completion(): ('hide',)}
|
|
-
|
|
- for obj, sig in signals.items():
|
|
- if obj:
|
|
- for s in sig:
|
|
- self.disconnect_signal(obj, s)
|
|
-
|
|
- # Remove all active snippets
|
|
- for snippet in list(self.active_snippets):
|
|
- self.deactivate_snippet(snippet, True)
|
|
-
|
|
- completion = self.view.get_completion()
|
|
- if completion:
|
|
- completion.remove_provider(self.provider)
|
|
-
|
|
- self.view = view
|
|
-
|
|
- if view != None:
|
|
- buf = view.get_buffer()
|
|
-
|
|
- self.connect_signal(view, 'destroy', self.on_view_destroy)
|
|
-
|
|
- if view.get_editable():
|
|
- self.connect_signal(view, 'key-press-event', self.on_view_key_press)
|
|
-
|
|
- self.connect_signal(buf, 'notify::language', self.on_notify_language)
|
|
- self.connect_signal(view, 'notify::editable', self.on_notify_editable)
|
|
- self.connect_signal(view, 'drag-data-received', self.on_drag_data_received)
|
|
- self.connect_signal_after(view, 'draw', self.on_draw)
|
|
-
|
|
- self.update_language()
|
|
-
|
|
- completion = view.get_completion()
|
|
- completion.add_provider(self.provider)
|
|
- 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)
|
|
-
|
|
- Library().unref(langid)
|
|
-
|
|
- def set_view(self, view):
|
|
- if view == self.view:
|
|
- return
|
|
-
|
|
- self._set_view(view)
|
|
-
|
|
- # Call this whenever the language in the view changes. This makes sure that
|
|
- # the correct language is used when finding snippets
|
|
- def update_language(self):
|
|
- lang = self.view.get_buffer().get_language()
|
|
-
|
|
- if lang == None and self.language_id == None:
|
|
- return
|
|
- elif lang and lang.get_id() == self.language_id:
|
|
- return
|
|
-
|
|
- langid = self.language_id
|
|
-
|
|
- if lang:
|
|
- self.language_id = lang.get_id()
|
|
- else:
|
|
- self.language_id = None
|
|
+ # Set the view to be controlled. Installs signal handlers and sets current
|
|
+ # language. If there is already a view set this function will first remove
|
|
+ # all currently active snippets and disconnect all current signals. So
|
|
+ # self.set_view(None) will effectively remove all the control from the
|
|
+ # current view
|
|
+ def _set_view(self, view):
|
|
+ if self.view:
|
|
+ buf = self.view.get_buffer()
|
|
+
|
|
+ # Remove signals
|
|
+ signals = {self.view: ('key-press-event', 'destroy',
|
|
+ 'notify::editable', 'drag-data-received', 'expose-event'),
|
|
+ buf: ('notify::language', 'changed', 'cursor-moved', 'insert-text'),
|
|
+ self.view.get_completion(): ('hide',)}
|
|
+
|
|
+ for obj, sig in signals.items():
|
|
+ if obj:
|
|
+ for s in sig:
|
|
+ self.disconnect_signal(obj, s)
|
|
|
|
- if self.instance:
|
|
- self.instance.language_changed(self)
|
|
+ # Remove all active snippets
|
|
+ for snippet in list(self.active_snippets):
|
|
+ self.deactivate_snippet(snippet, True)
|
|
+
|
|
+ completion = self.view.get_completion()
|
|
+ if completion:
|
|
+ completion.remove_provider(self.provider)
|
|
+
|
|
+ self.view = view
|
|
|
|
- if langid != 0:
|
|
- Library().unref(langid)
|
|
+ if view != None:
|
|
+ buf = view.get_buffer()
|
|
|
|
- Library().ref(self.language_id)
|
|
- self.provider.language_id = self.language_id
|
|
+ self.connect_signal(view, 'destroy', self.on_view_destroy)
|
|
+
|
|
+ if view.get_editable():
|
|
+ self.connect_signal(view, 'key-press-event', self.on_view_key_press)
|
|
|
|
- def accelerator_activate(self, keyval, mod):
|
|
- if not self.view or not self.view.get_editable():
|
|
- return False
|
|
+ self.connect_signal(buf, 'notify::language', self.on_notify_language)
|
|
+ self.connect_signal(view, 'notify::editable', self.on_notify_editable)
|
|
+ self.connect_signal(view, 'drag-data-received', self.on_drag_data_received)
|
|
+ self.connect_signal_after(view, 'draw', self.on_draw)
|
|
+
|
|
+ self.update_language()
|
|
+
|
|
+ completion = view.get_completion()
|
|
+ completion.add_provider(self.provider)
|
|
+ elif self.language_id != 0:
|
|
+ langid = self.language_id
|
|
|
|
- accelerator = Gtk.accelerator_name(keyval, mod)
|
|
- snippets = Library().from_accelerator(accelerator, \
|
|
- self.language_id)
|
|
+ self.language_id = None;
|
|
+ self.provider.language_id = self.language_id
|
|
+
|
|
+ if self.instance:
|
|
+ self.instance.language_changed(self)
|
|
|
|
- snippets_debug('Accel!')
|
|
+ Library().unref(langid)
|
|
|
|
- if len(snippets) == 0:
|
|
- return False
|
|
- elif len(snippets) == 1:
|
|
- self.apply_snippet(snippets[0])
|
|
- else:
|
|
- # Do the fancy completion dialog
|
|
- provider = Completion.Provider(_('Snippets'), self.language_id, self.on_proposal_activated)
|
|
- provider.set_proposals(snippets)
|
|
+ def set_view(self, view):
|
|
+ if view == self.view:
|
|
+ return
|
|
|
|
- cm.show([provider], cm.create_context(None))
|
|
+ self._set_view(view)
|
|
|
|
- return True
|
|
+ # Call this whenever the language in the view changes. This makes sure that
|
|
+ # the correct language is used when finding snippets
|
|
+ def update_language(self):
|
|
+ lang = self.view.get_buffer().get_language()
|
|
+
|
|
+ if lang == None and self.language_id == None:
|
|
+ return
|
|
+ elif lang and lang.get_id() == self.language_id:
|
|
+ return
|
|
+
|
|
+ langid = self.language_id
|
|
+
|
|
+ if lang:
|
|
+ self.language_id = lang.get_id()
|
|
+ else:
|
|
+ self.language_id = None
|
|
|
|
- def first_snippet_inserted(self):
|
|
- buf = self.view.get_buffer()
|
|
-
|
|
- self.connect_signal(buf, 'changed', self.on_buffer_changed)
|
|
- self.connect_signal(buf, 'cursor-moved', self.on_buffer_cursor_moved)
|
|
- self.connect_signal_after(buf, 'insert-text', self.on_buffer_insert_text)
|
|
-
|
|
- def last_snippet_removed(self):
|
|
- buf = self.view.get_buffer()
|
|
- self.disconnect_signal(buf, 'changed')
|
|
- self.disconnect_signal(buf, 'cursor-moved')
|
|
- self.disconnect_signal(buf, 'insert-text')
|
|
-
|
|
- def current_placeholder(self):
|
|
- buf = self.view.get_buffer()
|
|
-
|
|
- piter = buf.get_iter_at_mark(buf.get_insert())
|
|
- found = []
|
|
-
|
|
- for placeholder in self.placeholders:
|
|
- begin = placeholder.begin_iter()
|
|
- end = placeholder.end_iter()
|
|
-
|
|
- if piter.compare(begin) >= 0 and piter.compare(end) <= 0:
|
|
- found.append(placeholder)
|
|
-
|
|
- if self.active_placeholder in found:
|
|
- return self.active_placeholder
|
|
- elif len(found) > 0:
|
|
- return found[0]
|
|
- else:
|
|
- return None
|
|
-
|
|
- def advance_placeholder(self, direction):
|
|
- # Returns (CurrentPlaceholder, NextPlaceholder), depending on direction
|
|
- buf = self.view.get_buffer()
|
|
-
|
|
- piter = buf.get_iter_at_mark(buf.get_insert())
|
|
- found = current = next = None
|
|
- length = len(self.placeholders)
|
|
-
|
|
- placeholders = list(self.placeholders)
|
|
-
|
|
- if self.active_placeholder:
|
|
- begin = self.active_placeholder.begin_iter()
|
|
- end = self.active_placeholder.end_iter()
|
|
-
|
|
- if piter.compare(begin) >= 0 and piter.compare(end) <= 0:
|
|
- current = self.active_placeholder
|
|
- currentIndex = placeholders.index(self.active_placeholder)
|
|
-
|
|
- if direction == 1:
|
|
- # w = piter, x = begin, y = end, z = found
|
|
- nearest = lambda w, x, y, z: (w.compare(x) <= 0 and (not z or \
|
|
- x.compare(z.begin_iter()) < 0))
|
|
- indexer = lambda x: x < length - 1
|
|
- else:
|
|
- # w = piter, x = begin, y = end, z = prev
|
|
- nearest = lambda w, x, y, z: (w.compare(x) >= 0 and (not z or \
|
|
- x.compare(z.begin_iter()) >= 0))
|
|
- indexer = lambda x: x > 0
|
|
-
|
|
- for index in range(0, length):
|
|
- placeholder = placeholders[index]
|
|
- begin = placeholder.begin_iter()
|
|
- end = placeholder.end_iter()
|
|
-
|
|
- # Find the nearest placeholder
|
|
- if nearest(piter, begin, end, found):
|
|
- foundIndex = index
|
|
- found = placeholder
|
|
-
|
|
- # Find the current placeholder
|
|
- if piter.compare(begin) >= 0 and \
|
|
- piter.compare(end) <= 0 and \
|
|
- current == None:
|
|
- currentIndex = index
|
|
- current = placeholder
|
|
-
|
|
- if current 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 \
|
|
- current.begin_iter().compare(self.active_placeholder.begin_iter()) == 0:
|
|
- # if current and found are at the same place, then
|
|
- # resolve the 'hugging' problem
|
|
- current = self.active_placeholder
|
|
- currentIndex = placeholders.index(current)
|
|
-
|
|
- if current:
|
|
- if indexer(currentIndex):
|
|
- next = placeholders[currentIndex + direction]
|
|
- elif found:
|
|
- next = found
|
|
- elif length > 0:
|
|
- next = self.placeholders[0]
|
|
-
|
|
- return current, next
|
|
-
|
|
- def next_placeholder(self):
|
|
- return self.advance_placeholder(1)
|
|
-
|
|
- def previous_placeholder(self):
|
|
- return self.advance_placeholder(-1)
|
|
-
|
|
- def cursor_on_screen(self):
|
|
- buf = self.view.get_buffer()
|
|
- self.view.scroll_mark_onscreen(buf.get_insert())
|
|
-
|
|
- def set_active_placeholder(self, placeholder):
|
|
- self.active_placeholder = placeholder
|
|
-
|
|
- def goto_placeholder(self, current, next):
|
|
- last = None
|
|
-
|
|
- if current:
|
|
- # Signal this placeholder to end action
|
|
- self.view.get_completion().hide()
|
|
- current.leave()
|
|
-
|
|
- if current.__class__ == PlaceholderEnd:
|
|
- last = current
|
|
-
|
|
- self.set_active_placeholder(next)
|
|
-
|
|
- if next:
|
|
- next.enter()
|
|
-
|
|
- if next.__class__ == PlaceholderEnd:
|
|
- last = next
|
|
- elif len(next.defaults) > 1 and next.get_text() == next.default:
|
|
- provider = Completion.Defaults(self.on_default_activated)
|
|
- provider.set_defaults(next.defaults)
|
|
-
|
|
- cm = self.view.get_completion()
|
|
- cm.show([provider], cm.create_context(None))
|
|
-
|
|
- if last:
|
|
- # This is the end of the placeholder, remove the snippet etc
|
|
- for snippet in list(self.active_snippets):
|
|
- if snippet.placeholders[0] == last:
|
|
- self.deactivate_snippet(snippet)
|
|
- break
|
|
-
|
|
- self.cursor_on_screen()
|
|
-
|
|
- return next != None
|
|
-
|
|
- def skip_to_next_placeholder(self):
|
|
- (current, next) = self.next_placeholder()
|
|
- return self.goto_placeholder(current, next)
|
|
-
|
|
- def skip_to_previous_placeholder(self):
|
|
- (current, prev) = self.previous_placeholder()
|
|
- return self.goto_placeholder(current, prev)
|
|
-
|
|
- def env_get_selected_text(self, buf):
|
|
- bounds = buf.get_selection_bounds()
|
|
-
|
|
- if bounds:
|
|
- return buf.get_text(bounds[0], bounds[1], False)
|
|
- else:
|
|
- return ''
|
|
-
|
|
- 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()
|
|
-
|
|
- if location:
|
|
- return location.get_uri()
|
|
- else:
|
|
- return ''
|
|
-
|
|
- def env_get_document_name(self, buf):
|
|
- location = buf.get_location()
|
|
-
|
|
- if location:
|
|
- return location.get_basename()
|
|
- else:
|
|
- return ''
|
|
+ if self.instance:
|
|
+ self.instance.language_changed(self)
|
|
+
|
|
+ if langid != 0:
|
|
+ Library().unref(langid)
|
|
|
|
- def env_get_document_scheme(self, buf):
|
|
- location = buf.get_location()
|
|
-
|
|
- if location:
|
|
- return location.get_uri_scheme()
|
|
- else:
|
|
- return ''
|
|
+ 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():
|
|
+ return False
|
|
+
|
|
+ accelerator = Gtk.accelerator_name(keyval, mod)
|
|
+ snippets = Library().from_accelerator(accelerator, \
|
|
+ self.language_id)
|
|
+
|
|
+ snippets_debug('Accel!')
|
|
+
|
|
+ if len(snippets) == 0:
|
|
+ return False
|
|
+ elif len(snippets) == 1:
|
|
+ self.apply_snippet(snippets[0])
|
|
+ else:
|
|
+ # Do the fancy completion dialog
|
|
+ provider = Completion.Provider(_('Snippets'), self.language_id, self.on_proposal_activated)
|
|
+ provider.set_proposals(snippets)
|
|
+
|
|
+ cm.show([provider], cm.create_context(None))
|
|
+
|
|
+ return True
|
|
+
|
|
+ def first_snippet_inserted(self):
|
|
+ buf = self.view.get_buffer()
|
|
+
|
|
+ self.connect_signal(buf, 'changed', self.on_buffer_changed)
|
|
+ self.connect_signal(buf, 'cursor-moved', self.on_buffer_cursor_moved)
|
|
+ self.connect_signal_after(buf, 'insert-text', self.on_buffer_insert_text)
|
|
|
|
- def env_get_document_path(self, buf):
|
|
- location = buf.get_location()
|
|
-
|
|
- if location:
|
|
- return location.get_path()
|
|
- else:
|
|
- return ''
|
|
+ def last_snippet_removed(self):
|
|
+ buf = self.view.get_buffer()
|
|
+ self.disconnect_signal(buf, 'changed')
|
|
+ self.disconnect_signal(buf, 'cursor-moved')
|
|
+ self.disconnect_signal(buf, 'insert-text')
|
|
|
|
- def env_get_document_dir(self, buf):
|
|
- location = buf.get_location()
|
|
+ def current_placeholder(self):
|
|
+ buf = self.view.get_buffer()
|
|
|
|
- if location:
|
|
- return location.get_parent().get_path() or ''
|
|
- else:
|
|
- return ''
|
|
+ piter = buf.get_iter_at_mark(buf.get_insert())
|
|
+ found = []
|
|
+
|
|
+ for placeholder in self.placeholders:
|
|
+ begin = placeholder.begin_iter()
|
|
+ end = placeholder.end_iter()
|
|
+
|
|
+ if piter.compare(begin) >= 0 and piter.compare(end) <= 0:
|
|
+ found.append(placeholder)
|
|
+
|
|
+ if self.active_placeholder in found:
|
|
+ return self.active_placeholder
|
|
+ elif len(found) > 0:
|
|
+ return found[0]
|
|
+ else:
|
|
+ return None
|
|
+
|
|
+ def advance_placeholder(self, direction):
|
|
+ # Returns (CurrentPlaceholder, NextPlaceholder), depending on direction
|
|
+ buf = self.view.get_buffer()
|
|
|
|
- def env_get_document_type(self, buf):
|
|
- typ = buf.get_mime_type()
|
|
-
|
|
- if typ:
|
|
- return typ
|
|
- else:
|
|
- return ''
|
|
-
|
|
- def env_get_documents_uri(self, buf):
|
|
- toplevel = self.view.get_toplevel()
|
|
-
|
|
- if isinstance(toplevel, Pluma.Window):
|
|
- documents_uri = [doc.get_location().get_uri()
|
|
- for doc in toplevel.get_documents()
|
|
- if doc.get_location() is not None]
|
|
- else:
|
|
- documents_uri = []
|
|
-
|
|
- return ' '.join(documents_uri)
|
|
-
|
|
- def env_get_documents_path(self, buf):
|
|
- toplevel = self.view.get_toplevel()
|
|
-
|
|
- if isinstance(toplevel, Pluma.Window):
|
|
- documents_location = [doc.get_location()
|
|
- for doc in toplevel.get_documents()
|
|
- if doc.get_location() is not None]
|
|
-
|
|
- documents_path = [location.get_path()
|
|
- for location in documents_location
|
|
- if Pluma.utils_uri_has_file_scheme(location.get_uri())]
|
|
- else:
|
|
- documents_path = []
|
|
-
|
|
- return ' '.join(documents_path)
|
|
-
|
|
- def update_environment(self):
|
|
- buf = self.view.get_buffer()
|
|
-
|
|
- 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)
|
|
-
|
|
- def uses_current_word(self, snippet):
|
|
- matches = re.findall('(\\\\*)\\$PLUMA_CURRENT_WORD', snippet['text'])
|
|
-
|
|
- for match in matches:
|
|
- if len(match) % 2 == 0:
|
|
- return True
|
|
-
|
|
- return False
|
|
-
|
|
- def uses_current_line(self, snippet):
|
|
- matches = re.findall('(\\\\*)\\$PLUMA_CURRENT_LINE', snippet['text'])
|
|
-
|
|
- for match in matches:
|
|
- if len(match) % 2 == 0:
|
|
- return True
|
|
-
|
|
- return False
|
|
-
|
|
- def apply_snippet(self, snippet, start = None, end = None):
|
|
- if not snippet.valid:
|
|
- return False
|
|
-
|
|
- buf = self.view.get_buffer()
|
|
- s = Snippet(snippet)
|
|
-
|
|
- if not start:
|
|
- start = buf.get_iter_at_mark(buf.get_insert())
|
|
-
|
|
- if not end:
|
|
- end = buf.get_iter_at_mark(buf.get_selection_bound())
|
|
-
|
|
- if start.equal(end) and self.uses_current_word(s):
|
|
- # There is no tab trigger and no selection and the snippet uses
|
|
- # the current word. Set start and end to the word boundary so that
|
|
- # it will be removed
|
|
- start, end = buffer_word_boundary(buf)
|
|
- elif start.equal(end) and self.uses_current_line(s):
|
|
- # There is no tab trigger and no selection and the snippet uses
|
|
- # the current line. Set start and end to the line boundary so that
|
|
- # 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:
|
|
- self.goto_placeholder(current, None)
|
|
-
|
|
- buf.begin_user_action()
|
|
-
|
|
- # Remove the tag, selection or current word
|
|
- buf.delete(start, end)
|
|
-
|
|
- # Insert the snippet
|
|
- holders = len(self.placeholders)
|
|
-
|
|
- if len(self.active_snippets) == 0:
|
|
- self.first_snippet_inserted()
|
|
-
|
|
- sn = s.insert_into(self, start)
|
|
- self.active_snippets.append(sn)
|
|
-
|
|
- # Put cursor at first tab placeholder
|
|
- keys = filter(lambda x: x > 0, sn.placeholders.keys())
|
|
-
|
|
- if len(keys) == 0:
|
|
- if 0 in sn.placeholders:
|
|
- self.goto_placeholder(self.active_placeholder, sn.placeholders[0])
|
|
- else:
|
|
- buf.place_cursor(sn.begin_iter())
|
|
- else:
|
|
- self.goto_placeholder(self.active_placeholder, sn.placeholders[keys[0]])
|
|
+ piter = buf.get_iter_at_mark(buf.get_insert())
|
|
+ found = current = next = None
|
|
+ length = len(self.placeholders)
|
|
+
|
|
+ placeholders = list(self.placeholders)
|
|
+
|
|
+ if self.active_placeholder:
|
|
+ begin = self.active_placeholder.begin_iter()
|
|
+ end = self.active_placeholder.end_iter()
|
|
+
|
|
+ if piter.compare(begin) >= 0 and piter.compare(end) <= 0:
|
|
+ current = self.active_placeholder
|
|
+ currentIndex = placeholders.index(self.active_placeholder)
|
|
+
|
|
+ if direction == 1:
|
|
+ # w = piter, x = begin, y = end, z = found
|
|
+ nearest = lambda w, x, y, z: (w.compare(x) <= 0 and (not z or \
|
|
+ x.compare(z.begin_iter()) < 0))
|
|
+ indexer = lambda x: x < length - 1
|
|
+ else:
|
|
+ # w = piter, x = begin, y = end, z = prev
|
|
+ nearest = lambda w, x, y, z: (w.compare(x) >= 0 and (not z or \
|
|
+ x.compare(z.begin_iter()) >= 0))
|
|
+ indexer = lambda x: x > 0
|
|
+
|
|
+ for index in range(0, length):
|
|
+ placeholder = placeholders[index]
|
|
+ begin = placeholder.begin_iter()
|
|
+ end = placeholder.end_iter()
|
|
+
|
|
+ # Find the nearest placeholder
|
|
+ if nearest(piter, begin, end, found):
|
|
+ foundIndex = index
|
|
+ found = placeholder
|
|
+
|
|
+ # Find the current placeholder
|
|
+ if piter.compare(begin) >= 0 and \
|
|
+ piter.compare(end) <= 0 and \
|
|
+ current == None:
|
|
+ currentIndex = index
|
|
+ current = placeholder
|
|
+
|
|
+ if current 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 \
|
|
+ current.begin_iter().compare(self.active_placeholder.begin_iter()) == 0:
|
|
+ # if current and found are at the same place, then
|
|
+ # resolve the 'hugging' problem
|
|
+ current = self.active_placeholder
|
|
+ currentIndex = placeholders.index(current)
|
|
+
|
|
+ if current:
|
|
+ if indexer(currentIndex):
|
|
+ next = placeholders[currentIndex + direction]
|
|
+ elif found:
|
|
+ next = found
|
|
+ elif length > 0:
|
|
+ next = self.placeholders[0]
|
|
+
|
|
+ return current, next
|
|
+
|
|
+ def next_placeholder(self):
|
|
+ return self.advance_placeholder(1)
|
|
+
|
|
+ def previous_placeholder(self):
|
|
+ return self.advance_placeholder(-1)
|
|
+
|
|
+ def cursor_on_screen(self):
|
|
+ buf = self.view.get_buffer()
|
|
+ self.view.scroll_mark_onscreen(buf.get_insert())
|
|
+
|
|
+ def set_active_placeholder(self, placeholder):
|
|
+ self.active_placeholder = placeholder
|
|
+
|
|
+ def goto_placeholder(self, current, next):
|
|
+ last = None
|
|
+
|
|
+ if current:
|
|
+ # Signal this placeholder to end action
|
|
+ self.view.get_completion().hide()
|
|
+ current.leave()
|
|
+
|
|
+ if current.__class__ == PlaceholderEnd:
|
|
+ last = current
|
|
+
|
|
+ self.set_active_placeholder(next)
|
|
+
|
|
+ if next:
|
|
+ next.enter()
|
|
+
|
|
+ if next.__class__ == PlaceholderEnd:
|
|
+ last = next
|
|
+ elif len(next.defaults) > 1 and next.get_text() == next.default:
|
|
+ provider = Completion.Defaults(self.on_default_activated)
|
|
+ provider.set_defaults(next.defaults)
|
|
+
|
|
+ cm = self.view.get_completion()
|
|
+ cm.show([provider], cm.create_context(None))
|
|
+
|
|
+ if last:
|
|
+ # This is the end of the placeholder, remove the snippet etc
|
|
+ for snippet in list(self.active_snippets):
|
|
+ if snippet.placeholders[0] == last:
|
|
+ self.deactivate_snippet(snippet)
|
|
+ break
|
|
+
|
|
+ self.cursor_on_screen()
|
|
+
|
|
+ return next != None
|
|
+
|
|
+ def skip_to_next_placeholder(self):
|
|
+ (current, next) = self.next_placeholder()
|
|
+ return self.goto_placeholder(current, next)
|
|
+
|
|
+ def skip_to_previous_placeholder(self):
|
|
+ (current, prev) = self.previous_placeholder()
|
|
+ return self.goto_placeholder(current, prev)
|
|
+
|
|
+ def env_get_selected_text(self, buf):
|
|
+ bounds = buf.get_selection_bounds()
|
|
+
|
|
+ if bounds:
|
|
+ return buf.get_text(bounds[0], bounds[1], False)
|
|
+ else:
|
|
+ return ''
|
|
+
|
|
+ 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()
|
|
+
|
|
+ if location:
|
|
+ return location.get_uri()
|
|
+ else:
|
|
+ return ''
|
|
+
|
|
+ def env_get_document_name(self, buf):
|
|
+ location = buf.get_location()
|
|
+
|
|
+ if location:
|
|
+ return location.get_basename()
|
|
+ else:
|
|
+ return ''
|
|
+
|
|
+ def env_get_document_scheme(self, buf):
|
|
+ location = buf.get_location()
|
|
+
|
|
+ if location:
|
|
+ return location.get_uri_scheme()
|
|
+ else:
|
|
+ return ''
|
|
+
|
|
+ def env_get_document_path(self, buf):
|
|
+ location = buf.get_location()
|
|
+
|
|
+ if location:
|
|
+ return location.get_path()
|
|
+ else:
|
|
+ return ''
|
|
+
|
|
+ def env_get_document_dir(self, buf):
|
|
+ location = buf.get_location()
|
|
+
|
|
+ if location:
|
|
+ return location.get_parent().get_path() or ''
|
|
+ else:
|
|
+ return ''
|
|
+
|
|
+ def env_get_document_type(self, buf):
|
|
+ typ = buf.get_mime_type()
|
|
+
|
|
+ if typ:
|
|
+ return typ
|
|
+ else:
|
|
+ return ''
|
|
+
|
|
+ def env_get_documents_uri(self, buf):
|
|
+ toplevel = self.view.get_toplevel()
|
|
+
|
|
+ if isinstance(toplevel, Pluma.Window):
|
|
+ documents_uri = [doc.get_location().get_uri()
|
|
+ for doc in toplevel.get_documents()
|
|
+ if doc.get_location() is not None]
|
|
+ else:
|
|
+ documents_uri = []
|
|
+
|
|
+ return ' '.join(documents_uri)
|
|
+
|
|
+ def env_get_documents_path(self, buf):
|
|
+ toplevel = self.view.get_toplevel()
|
|
|
|
- if sn in self.active_snippets:
|
|
- # Check if we can get end_iter in view without moving the
|
|
- # current cursor position out of view
|
|
- cur = buf.get_iter_at_mark(buf.get_insert())
|
|
- last = sn.end_iter()
|
|
+ if isinstance(toplevel, Pluma.Window):
|
|
+ documents_location = [doc.get_location()
|
|
+ for doc in toplevel.get_documents()
|
|
+ if doc.get_location() is not None]
|
|
|
|
- curloc = self.view.get_iter_location(cur)
|
|
- lastloc = self.view.get_iter_location(last)
|
|
+ documents_path = [location.get_path()
|
|
+ for location in documents_location
|
|
+ if Pluma.utils_uri_has_file_scheme(location.get_uri())]
|
|
+ else:
|
|
+ documents_path = []
|
|
|
|
- if (lastloc.y + lastloc.height) - curloc.y <= \
|
|
- self.view.get_visible_rect().height:
|
|
- self.view.scroll_mark_onscreen(sn.end_mark)
|
|
+ return ' '.join(documents_path)
|
|
|
|
- buf.end_user_action()
|
|
- self.view.grab_focus()
|
|
+ def update_environment(self):
|
|
+ buf = self.view.get_buffer()
|
|
|
|
+ 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)
|
|
+
|
|
+ def uses_current_word(self, snippet):
|
|
+ matches = re.findall('(\\\\*)\\$PLUMA_CURRENT_WORD', snippet['text'])
|
|
+
|
|
+ for match in matches:
|
|
+ if len(match) % 2 == 0:
|
|
return True
|
|
|
|
- def get_tab_tag(self, buf, end = None):
|
|
- if not end:
|
|
- end = buf.get_iter_at_mark(buf.get_insert())
|
|
+ return False
|
|
|
|
- start = end.copy()
|
|
-
|
|
- word = None
|
|
-
|
|
- if start.backward_word_start():
|
|
- # Check if we were at a word start ourselves
|
|
- tmp = start.copy()
|
|
- tmp.forward_word_end()
|
|
-
|
|
- if tmp.equal(end):
|
|
- word = buf.get_text(start, end, False)
|
|
- else:
|
|
- start = end.copy()
|
|
- else:
|
|
- start = end.copy()
|
|
-
|
|
- if not word or word == '':
|
|
- if start.backward_char():
|
|
- word = start.get_char()
|
|
+ def uses_current_line(self, snippet):
|
|
+ matches = re.findall('(\\\\*)\\$PLUMA_CURRENT_LINE', snippet['text'])
|
|
|
|
- if word.isalnum() or word.isspace():
|
|
- return (None, None, None)
|
|
- else:
|
|
- return (None, None, None)
|
|
+ for match in matches:
|
|
+ if len(match) % 2 == 0:
|
|
+ return True
|
|
|
|
- return (word, start, end)
|
|
+ return False
|
|
|
|
- def parse_and_run_snippet(self, data, iter):
|
|
- self.apply_snippet(DynamicSnippet(data), iter, iter)
|
|
+ def apply_snippet(self, snippet, start = None, end = None):
|
|
+ if not snippet.valid:
|
|
+ return False
|
|
|
|
- def run_snippet_trigger(self, trigger, bounds):
|
|
- if not self.view:
|
|
- return False
|
|
+ buf = self.view.get_buffer()
|
|
+ s = Snippet(snippet)
|
|
|
|
- snippets = Library().from_tag(trigger, self.language_id)
|
|
- buf = self.view.get_buffer()
|
|
+ if not start:
|
|
+ start = buf.get_iter_at_mark(buf.get_insert())
|
|
|
|
- if snippets:
|
|
- if len(snippets) == 1:
|
|
- return self.apply_snippet(snippets[0], bounds[0], bounds[1])
|
|
- else:
|
|
- # Do the fancy completion dialog
|
|
- provider = Completion.Provider(_('Snippets'), self.language_id, self.on_proposal_activated)
|
|
- provider.set_proposals(snippets)
|
|
+ if not end:
|
|
+ end = buf.get_iter_at_mark(buf.get_selection_bound())
|
|
|
|
- cm = self.view.get_completion()
|
|
- cm.show([provider], cm.create_context(None))
|
|
+ if start.equal(end) and self.uses_current_word(s):
|
|
+ # There is no tab trigger and no selection and the snippet uses
|
|
+ # the current word. Set start and end to the word boundary so that
|
|
+ # it will be removed
|
|
+ start, end = buffer_word_boundary(buf)
|
|
+ elif start.equal(end) and self.uses_current_line(s):
|
|
+ # There is no tab trigger and no selection and the snippet uses
|
|
+ # the current line. Set start and end to the line boundary so that
|
|
+ # it will be removed
|
|
+ start, end = buffer_line_boundary(buf)
|
|
|
|
- return True
|
|
+ # Set environmental variables
|
|
+ self.update_environment()
|
|
|
|
- return False
|
|
-
|
|
- def run_snippet(self):
|
|
- if not self.view:
|
|
- return False
|
|
+ # You know, we could be in an end placeholder
|
|
+ (current, next) = self.next_placeholder()
|
|
+ if current and current.__class__ == PlaceholderEnd:
|
|
+ self.goto_placeholder(current, None)
|
|
|
|
- buf = self.view.get_buffer()
|
|
+ buf.begin_user_action()
|
|
|
|
- # get the word preceding the current insertion position
|
|
- (word, start, end) = self.get_tab_tag(buf)
|
|
+ # Remove the tag, selection or current word
|
|
+ buf.delete(start, end)
|
|
|
|
- if not word:
|
|
- return self.skip_to_next_placeholder()
|
|
+ # Insert the snippet
|
|
+ holders = len(self.placeholders)
|
|
|
|
- if not self.run_snippet_trigger(word, (start, end)):
|
|
- return self.skip_to_next_placeholder()
|
|
- else:
|
|
- return True
|
|
+ if len(self.active_snippets) == 0:
|
|
+ self.first_snippet_inserted()
|
|
|
|
- def deactivate_snippet(self, snippet, force = False):
|
|
- buf = self.view.get_buffer()
|
|
- remove = []
|
|
- ordered_remove = []
|
|
+ sn = s.insert_into(self, start)
|
|
+ self.active_snippets.append(sn)
|
|
|
|
- for tabstop in snippet.placeholders:
|
|
- if tabstop == -1:
|
|
- placeholders = snippet.placeholders[-1]
|
|
- else:
|
|
- placeholders = [snippet.placeholders[tabstop]]
|
|
-
|
|
- for placeholder in placeholders:
|
|
- if placeholder in self.placeholders:
|
|
- if placeholder in self.update_placeholders:
|
|
- placeholder.update_contents()
|
|
-
|
|
- self.update_placeholders.remove(placeholder)
|
|
- elif placeholder in self.jump_placeholders:
|
|
- placeholder[0].leave()
|
|
+ # Put cursor at first tab placeholder
|
|
+ keys = filter(lambda x: x > 0, sn.placeholders.keys())
|
|
|
|
- remove.append(placeholder)
|
|
- elif placeholder in self.ordered_placeholders:
|
|
- ordered_remove.append(placeholder)
|
|
+ if len(keys) == 0:
|
|
+ if 0 in sn.placeholders:
|
|
+ self.goto_placeholder(self.active_placeholder, sn.placeholders[0])
|
|
+ else:
|
|
+ buf.place_cursor(sn.begin_iter())
|
|
+ else:
|
|
+ self.goto_placeholder(self.active_placeholder, sn.placeholders[keys[0]])
|
|
|
|
- for placeholder in remove:
|
|
- if placeholder == self.active_placeholder:
|
|
- self.active_placeholder = None
|
|
+ if sn in self.active_snippets:
|
|
+ # Check if we can get end_iter in view without moving the
|
|
+ # current cursor position out of view
|
|
+ cur = buf.get_iter_at_mark(buf.get_insert())
|
|
+ last = sn.end_iter()
|
|
|
|
- self.placeholders.remove(placeholder)
|
|
- self.ordered_placeholders.remove(placeholder)
|
|
+ curloc = self.view.get_iter_location(cur)
|
|
+ lastloc = self.view.get_iter_location(last)
|
|
|
|
- placeholder.remove(force)
|
|
+ if (lastloc.y + lastloc.height) - curloc.y <= \
|
|
+ self.view.get_visible_rect().height:
|
|
+ self.view.scroll_mark_onscreen(sn.end_mark)
|
|
|
|
- for placeholder in ordered_remove:
|
|
- self.ordered_placeholders.remove(placeholder)
|
|
- placeholder.remove(force)
|
|
+ buf.end_user_action()
|
|
+ self.view.grab_focus()
|
|
|
|
- snippet.deactivate()
|
|
- self.active_snippets.remove(snippet)
|
|
+ return True
|
|
|
|
- if len(self.active_snippets) == 0:
|
|
- self.last_snippet_removed()
|
|
+ def get_tab_tag(self, buf, end = None):
|
|
+ if not end:
|
|
+ end = buf.get_iter_at_mark(buf.get_insert())
|
|
|
|
- self.view.queue_draw()
|
|
+ start = end.copy()
|
|
+ word = None
|
|
|
|
- def update_snippet_contents(self):
|
|
- self.timeout_update_id = 0
|
|
+ if start.backward_word_start():
|
|
+ # Check if we were at a word start ourselves
|
|
+ tmp = start.copy()
|
|
+ tmp.forward_word_end()
|
|
|
|
- for placeholder in self.update_placeholders:
|
|
- placeholder.update_contents()
|
|
+ if tmp.equal(end):
|
|
+ word = buf.get_text(start, end, False)
|
|
+ else:
|
|
+ start = end.copy()
|
|
+ else:
|
|
+ start = end.copy()
|
|
|
|
- for placeholder in self.jump_placeholders:
|
|
- self.goto_placeholder(placeholder[0], placeholder[1])
|
|
-
|
|
- del self.update_placeholders[:]
|
|
- del self.jump_placeholders[:]
|
|
-
|
|
- return False
|
|
-
|
|
- # Callbacks
|
|
- def on_view_destroy(self, view):
|
|
- self.stop()
|
|
- return
|
|
-
|
|
- def on_buffer_cursor_moved(self, buf):
|
|
- piter = buf.get_iter_at_mark(buf.get_insert())
|
|
-
|
|
- # Check for all snippets if the cursor is outside its scope
|
|
- for snippet in list(self.active_snippets):
|
|
- if snippet.begin_mark.get_deleted() or snippet.end_mark.get_deleted():
|
|
- self.deactivate(snippet)
|
|
- else:
|
|
- begin = snippet.begin_iter()
|
|
- end = snippet.end_iter()
|
|
-
|
|
- if piter.compare(begin) < 0 or piter.compare(end) > 0:
|
|
- # Oh no! Remove the snippet this instant!!
|
|
- self.deactivate_snippet(snippet)
|
|
-
|
|
- current = self.current_placeholder()
|
|
-
|
|
- if current != self.active_placeholder:
|
|
- self.jump_placeholders.append((self.active_placeholder, current))
|
|
-
|
|
- if self.timeout_update_id == 0:
|
|
- self.timeout_update_id = GLib.timeout_add(0,
|
|
- self.update_snippet_contents)
|
|
-
|
|
- def on_buffer_changed(self, buf):
|
|
- for snippet in list(self.active_snippets):
|
|
- begin = snippet.begin_iter()
|
|
- end = snippet.end_iter()
|
|
-
|
|
- if begin.compare(end) >= 0:
|
|
- # Begin collapsed on end, just remove it
|
|
- self.deactivate_snippet(snippet)
|
|
-
|
|
- current = self.current_placeholder()
|
|
-
|
|
- if current:
|
|
- if not current in self.update_placeholders:
|
|
- self.update_placeholders.append(current)
|
|
-
|
|
- if self.timeout_update_id == 0:
|
|
- self.timeout_update_id = GLib.timeout_add(0, \
|
|
- self.update_snippet_contents)
|
|
-
|
|
- def on_buffer_insert_text(self, buf, piter, text, length):
|
|
- ctx = get_buffer_context(buf)
|
|
-
|
|
- # do nothing special if there is no context and no active
|
|
- # placeholder
|
|
- if (not ctx) and (not self.active_placeholder):
|
|
- return
|
|
-
|
|
- if not ctx:
|
|
- ctx = self.active_placeholder
|
|
-
|
|
- if not ctx in self.ordered_placeholders:
|
|
- return
|
|
-
|
|
- # move any marks that were incorrectly moved by this insertion
|
|
- # back to where they belong
|
|
- begin = ctx.begin_iter()
|
|
- end = ctx.end_iter()
|
|
- idx = self.ordered_placeholders.index(ctx)
|
|
-
|
|
- for placeholder in self.ordered_placeholders:
|
|
- if placeholder == ctx:
|
|
- continue
|
|
-
|
|
- ob = placeholder.begin_iter()
|
|
- oe = placeholder.end_iter()
|
|
-
|
|
- if ob.compare(begin) == 0 and ((not oe) or oe.compare(end) == 0):
|
|
- oidx = self.ordered_placeholders.index(placeholder)
|
|
-
|
|
- if oidx > idx and ob:
|
|
- buf.move_mark(placeholder.begin, end)
|
|
- elif oidx < idx and oe:
|
|
- buf.move_mark(placeholder.end, begin)
|
|
- elif ob.compare(begin) >= 0 and ob.compare(end) < 0 and (oe and oe.compare(end) >= 0):
|
|
- buf.move_mark(placeholder.begin, end)
|
|
- elif (oe and oe.compare(begin) > 0) and ob.compare(begin) <= 0:
|
|
- buf.move_mark(placeholder.end, begin)
|
|
-
|
|
- def on_notify_language(self, buf, spec):
|
|
- self.update_language()
|
|
-
|
|
- def on_notify_editable(self, view, spec):
|
|
- self._set_view(view)
|
|
-
|
|
- def on_view_key_press(self, view, event):
|
|
- library = Library()
|
|
-
|
|
- state = event.get_state()
|
|
-
|
|
- if not (state & Gdk.ModifierType.CONTROL_MASK) and \
|
|
- not (state & Gdk.ModifierType.MOD1_MASK) and \
|
|
- event.keyval in self.TAB_KEY_VAL:
|
|
- if not state & Gdk.ModifierType.SHIFT_MASK:
|
|
- return self.run_snippet()
|
|
- else:
|
|
- return self.skip_to_previous_placeholder()
|
|
- elif not library.loaded and \
|
|
- library.valid_accelerator(event.keyval, state):
|
|
- library.ensure_files()
|
|
- library.ensure(self.language_id)
|
|
- self.accelerator_activate(event.keyval, \
|
|
- state & Gtk.accelerator_get_default_mod_mask())
|
|
-
|
|
- return False
|
|
-
|
|
- def path_split(self, path, components=[]):
|
|
- head, tail = os.path.split(path)
|
|
-
|
|
- if not tail and head:
|
|
- return [head] + components
|
|
- elif tail:
|
|
- return self.path_split(head, [tail] + components)
|
|
- else:
|
|
- return components
|
|
-
|
|
- def relative_path(self, first, second, mime):
|
|
- prot1 = re.match('(^[a-z]+:\/\/|\/)(.*)', first)
|
|
- prot2 = re.match('(^[a-z]+:\/\/|\/)(.*)', second)
|
|
-
|
|
- if not prot1 or not prot2:
|
|
- return second
|
|
-
|
|
- # Different protocols
|
|
- if prot1.group(1) != prot2.group(1):
|
|
- return second
|
|
-
|
|
- # Split on backslash
|
|
- path1 = self.path_split(prot1.group(2))
|
|
- path2 = self.path_split(prot2.group(2))
|
|
-
|
|
- # Remove as long as common
|
|
- while path1 and path2 and path1[0] == path2[0]:
|
|
- path1.pop(0)
|
|
- path2.pop(0)
|
|
-
|
|
- # If we need to ../ more than 3 times, then just return
|
|
- # the absolute path
|
|
- if len(path1) - 1 > 3:
|
|
- return second
|
|
-
|
|
- if mime.startswith('x-directory'):
|
|
- # directory, special case
|
|
- if not path2:
|
|
- result = './'
|
|
- else:
|
|
- result = '../' * (len(path1) - 1)
|
|
- else:
|
|
- # Insert ../
|
|
- result = '../' * (len(path1) - 1)
|
|
-
|
|
- if not path2:
|
|
- result = os.path.basename(second)
|
|
-
|
|
- if path2:
|
|
- result += os.path.join(*path2)
|
|
-
|
|
- return result
|
|
-
|
|
- def apply_uri_snippet(self, snippet, mime, uri):
|
|
- # Remove file scheme
|
|
- gfile = Gio.file_new_for_uri(uri)
|
|
- pathname = ''
|
|
- dirname = ''
|
|
- ruri = ''
|
|
-
|
|
- 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
|
|
-
|
|
- buf = self.view.get_buffer()
|
|
- location = buf.get_location()
|
|
- if location:
|
|
- ruri = location.get_uri()
|
|
-
|
|
- relpath = self.relative_path(ruri, uri, mime)
|
|
-
|
|
- os.environ['PLUMA_DROP_DOCUMENT_RELATIVE_PATH'] = relpath
|
|
-
|
|
- mark = buf.get_mark('gtk_drag_target')
|
|
-
|
|
- if not mark:
|
|
- mark = buf.get_insert()
|
|
-
|
|
- piter = buf.get_iter_at_mark(mark)
|
|
- self.apply_snippet(snippet, piter, piter)
|
|
-
|
|
- def in_bounds(self, x, y):
|
|
- rect = self.view.get_visible_rect()
|
|
- rect.x, rect.y = self.view.buffer_to_window_coords(Gtk.TextWindowType.WIDGET, rect.x, rect.y)
|
|
-
|
|
- 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):
|
|
- uris = drop_get_uris(data)
|
|
- if not uris:
|
|
- return
|
|
-
|
|
- if not self.in_bounds(x, y):
|
|
- return
|
|
-
|
|
- uris.reverse()
|
|
- stop = False
|
|
-
|
|
- for uri in uris:
|
|
- try:
|
|
- mime = Gio.content_type_guess(uri)
|
|
- except:
|
|
- mime = None
|
|
-
|
|
- if not mime:
|
|
- continue
|
|
-
|
|
- snippets = Library().from_drop_target(mime, self.language_id)
|
|
-
|
|
- if snippets:
|
|
- stop = True
|
|
- self.apply_uri_snippet(snippets[0], mime, uri)
|
|
-
|
|
- if stop:
|
|
- context.finish(True, False, timestamp)
|
|
- view.stop_emission('drag-data-received')
|
|
- view.get_toplevel().present()
|
|
- view.grab_focus()
|
|
-
|
|
- def find_uri_target(self, context):
|
|
- lst = Gtk.target_list_add_uri_targets((), 0)
|
|
-
|
|
- return self.view.drag_dest_find_target(context, lst)
|
|
-
|
|
- 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)
|
|
+ if not word or word == '':
|
|
+ if start.backward_char():
|
|
+ word = start.get_char()
|
|
+
|
|
+ if word.isalnum() or word.isspace():
|
|
+ return (None, None, None)
|
|
+ else:
|
|
+ return (None, None, None)
|
|
+
|
|
+ return (word, start, end)
|
|
+
|
|
+ def parse_and_run_snippet(self, data, iter):
|
|
+ self.apply_snippet(DynamicSnippet(data), iter, iter)
|
|
+
|
|
+ def run_snippet_trigger(self, trigger, bounds):
|
|
+ if not self.view:
|
|
+ return False
|
|
+
|
|
+ snippets = Library().from_tag(trigger, self.language_id)
|
|
+ buf = self.view.get_buffer()
|
|
+
|
|
+ if snippets:
|
|
+ if len(snippets) == 1:
|
|
+ return self.apply_snippet(snippets[0], bounds[0], bounds[1])
|
|
+ else:
|
|
+ # Do the fancy completion dialog
|
|
+ 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
|
|
-
|
|
- 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
|
|
+
|
|
+ return False
|
|
+
|
|
+ def run_snippet(self):
|
|
+ if not self.view:
|
|
+ return False
|
|
+
|
|
+ buf = self.view.get_buffer()
|
|
+
|
|
+ # get the word preceding the current insertion position
|
|
+ (word, start, end) = self.get_tab_tag(buf)
|
|
+
|
|
+ if not word:
|
|
+ return self.skip_to_next_placeholder()
|
|
+
|
|
+ if not self.run_snippet_trigger(word, (start, end)):
|
|
+ return self.skip_to_next_placeholder()
|
|
+ else:
|
|
+ return True
|
|
+
|
|
+ def deactivate_snippet(self, snippet, force = False):
|
|
+ buf = self.view.get_buffer()
|
|
+ remove = []
|
|
+ ordered_remove = []
|
|
+
|
|
+ for tabstop in snippet.placeholders:
|
|
+ if tabstop == -1:
|
|
+ placeholders = snippet.placeholders[-1]
|
|
+ else:
|
|
+ placeholders = [snippet.placeholders[tabstop]]
|
|
+
|
|
+ for placeholder in placeholders:
|
|
+ if placeholder in self.placeholders:
|
|
+ if placeholder in self.update_placeholders:
|
|
+ placeholder.update_contents()
|
|
+
|
|
+ self.update_placeholders.remove(placeholder)
|
|
+ elif placeholder in self.jump_placeholders:
|
|
+ placeholder[0].leave()
|
|
+
|
|
+ remove.append(placeholder)
|
|
+ elif placeholder in self.ordered_placeholders:
|
|
+ ordered_remove.append(placeholder)
|
|
+
|
|
+ for placeholder in remove:
|
|
+ if placeholder == self.active_placeholder:
|
|
+ self.active_placeholder = None
|
|
+
|
|
+ self.placeholders.remove(placeholder)
|
|
+ self.ordered_placeholders.remove(placeholder)
|
|
+
|
|
+ placeholder.remove(force)
|
|
+
|
|
+ for placeholder in ordered_remove:
|
|
+ self.ordered_placeholders.remove(placeholder)
|
|
+ placeholder.remove(force)
|
|
+
|
|
+ snippet.deactivate()
|
|
+ self.active_snippets.remove(snippet)
|
|
+
|
|
+ if len(self.active_snippets) == 0:
|
|
+ self.last_snippet_removed()
|
|
+
|
|
+ self.view.queue_draw()
|
|
+
|
|
+ def update_snippet_contents(self):
|
|
+ self.timeout_update_id = 0
|
|
+
|
|
+ for placeholder in self.update_placeholders:
|
|
+ placeholder.update_contents()
|
|
+
|
|
+ for placeholder in self.jump_placeholders:
|
|
+ self.goto_placeholder(placeholder[0], placeholder[1])
|
|
+
|
|
+ del self.update_placeholders[:]
|
|
+ del self.jump_placeholders[:]
|
|
+
|
|
+ return False
|
|
+
|
|
+ # Callbacks
|
|
+ def on_view_destroy(self, view):
|
|
+ self.stop()
|
|
+ return
|
|
+
|
|
+ def on_buffer_cursor_moved(self, buf):
|
|
+ piter = buf.get_iter_at_mark(buf.get_insert())
|
|
+
|
|
+ # Check for all snippets if the cursor is outside its scope
|
|
+ for snippet in list(self.active_snippets):
|
|
+ if snippet.begin_mark.get_deleted() or snippet.end_mark.get_deleted():
|
|
+ self.deactivate(snippet)
|
|
+ else:
|
|
+ begin = snippet.begin_iter()
|
|
+ end = snippet.end_iter()
|
|
+
|
|
+ if piter.compare(begin) < 0 or piter.compare(end) > 0:
|
|
+ # Oh no! Remove the snippet this instant!!
|
|
+ self.deactivate_snippet(snippet)
|
|
+
|
|
+ current = self.current_placeholder()
|
|
+
|
|
+ if current != self.active_placeholder:
|
|
+ self.jump_placeholders.append((self.active_placeholder, current))
|
|
+
|
|
+ if self.timeout_update_id == 0:
|
|
+ self.timeout_update_id = GLib.timeout_add(0,
|
|
+ self.update_snippet_contents)
|
|
+
|
|
+ def on_buffer_changed(self, buf):
|
|
+ for snippet in list(self.active_snippets):
|
|
+ begin = snippet.begin_iter()
|
|
+ end = snippet.end_iter()
|
|
+
|
|
+ if begin.compare(end) >= 0:
|
|
+ # Begin collapsed on end, just remove it
|
|
+ self.deactivate_snippet(snippet)
|
|
+
|
|
+ current = self.current_placeholder()
|
|
+
|
|
+ if current:
|
|
+ if not current in self.update_placeholders:
|
|
+ self.update_placeholders.append(current)
|
|
+
|
|
+ if self.timeout_update_id == 0:
|
|
+ self.timeout_update_id = GLib.timeout_add(0, \
|
|
+ self.update_snippet_contents)
|
|
+
|
|
+ def on_buffer_insert_text(self, buf, piter, text, length):
|
|
+ ctx = get_buffer_context(buf)
|
|
+
|
|
+ # do nothing special if there is no context and no active
|
|
+ # placeholder
|
|
+ if (not ctx) and (not self.active_placeholder):
|
|
+ return
|
|
+
|
|
+ if not ctx:
|
|
+ ctx = self.active_placeholder
|
|
+
|
|
+ if not ctx in self.ordered_placeholders:
|
|
+ return
|
|
+
|
|
+ # move any marks that were incorrectly moved by this insertion
|
|
+ # back to where they belong
|
|
+ begin = ctx.begin_iter()
|
|
+ end = ctx.end_iter()
|
|
+ idx = self.ordered_placeholders.index(ctx)
|
|
+
|
|
+ for placeholder in self.ordered_placeholders:
|
|
+ if placeholder == ctx:
|
|
+ continue
|
|
+
|
|
+ ob = placeholder.begin_iter()
|
|
+ oe = placeholder.end_iter()
|
|
+
|
|
+ if ob.compare(begin) == 0 and ((not oe) or oe.compare(end) == 0):
|
|
+ oidx = self.ordered_placeholders.index(placeholder)
|
|
+
|
|
+ if oidx > idx and ob:
|
|
+ buf.move_mark(placeholder.begin, end)
|
|
+ elif oidx < idx and oe:
|
|
+ buf.move_mark(placeholder.end, begin)
|
|
+ elif ob.compare(begin) >= 0 and ob.compare(end) < 0 and (oe and oe.compare(end) >= 0):
|
|
+ buf.move_mark(placeholder.begin, end)
|
|
+ elif (oe and oe.compare(begin) > 0) and ob.compare(begin) <= 0:
|
|
+ buf.move_mark(placeholder.end, begin)
|
|
+
|
|
+ def on_notify_language(self, buf, spec):
|
|
+ self.update_language()
|
|
+
|
|
+ def on_notify_editable(self, view, spec):
|
|
+ self._set_view(view)
|
|
+
|
|
+ def on_view_key_press(self, view, event):
|
|
+ library = Library()
|
|
+
|
|
+ state = event.get_state()
|
|
+
|
|
+ if not (state & Gdk.ModifierType.CONTROL_MASK) and \
|
|
+ not (state & Gdk.ModifierType.MOD1_MASK) and \
|
|
+ event.keyval in self.TAB_KEY_VAL:
|
|
+ if not state & Gdk.ModifierType.SHIFT_MASK:
|
|
+ return self.run_snippet()
|
|
+ else:
|
|
+ return self.skip_to_previous_placeholder()
|
|
+ elif not library.loaded and \
|
|
+ library.valid_accelerator(event.keyval, state):
|
|
+ library.ensure_files()
|
|
+ library.ensure(self.language_id)
|
|
+ self.accelerator_activate(event.keyval, \
|
|
+ state & Gtk.accelerator_get_default_mod_mask())
|
|
+
|
|
+ return False
|
|
+
|
|
+ def path_split(self, path, components=[]):
|
|
+ head, tail = os.path.split(path)
|
|
+
|
|
+ if not tail and head:
|
|
+ return [head] + components
|
|
+ elif tail:
|
|
+ return self.path_split(head, [tail] + components)
|
|
+ else:
|
|
+ return components
|
|
+
|
|
+ def relative_path(self, first, second, mime):
|
|
+ prot1 = re.match('(^[a-z]+:\/\/|\/)(.*)', first)
|
|
+ prot2 = re.match('(^[a-z]+:\/\/|\/)(.*)', second)
|
|
+
|
|
+ if not prot1 or not prot2:
|
|
+ return second
|
|
+
|
|
+ # Different protocols
|
|
+ if prot1.group(1) != prot2.group(1):
|
|
+ return second
|
|
+
|
|
+ # Split on backslash
|
|
+ path1 = self.path_split(prot1.group(2))
|
|
+ path2 = self.path_split(prot2.group(2))
|
|
+
|
|
+ # Remove as long as common
|
|
+ while path1 and path2 and path1[0] == path2[0]:
|
|
+ path1.pop(0)
|
|
+ path2.pop(0)
|
|
+
|
|
+ # If we need to ../ more than 3 times, then just return
|
|
+ # the absolute path
|
|
+ if len(path1) - 1 > 3:
|
|
+ return second
|
|
+
|
|
+ if mime.startswith('x-directory'):
|
|
+ # directory, special case
|
|
+ if not path2:
|
|
+ result = './'
|
|
+ else:
|
|
+ result = '../' * (len(path1) - 1)
|
|
+ else:
|
|
+ # Insert ../
|
|
+ result = '../' * (len(path1) - 1)
|
|
+
|
|
+ if not path2:
|
|
+ result = os.path.basename(second)
|
|
+
|
|
+ if path2:
|
|
+ result += os.path.join(*path2)
|
|
+
|
|
+ return result
|
|
+
|
|
+ def apply_uri_snippet(self, snippet, mime, uri):
|
|
+ # Remove file scheme
|
|
+ gfile = Gio.file_new_for_uri(uri)
|
|
+ pathname = ''
|
|
+ dirname = ''
|
|
+ ruri = ''
|
|
+
|
|
+ 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
|
|
+
|
|
+ buf = self.view.get_buffer()
|
|
+ location = buf.get_location()
|
|
+ if location:
|
|
+ ruri = location.get_uri()
|
|
+
|
|
+ relpath = self.relative_path(ruri, uri, mime)
|
|
+
|
|
+ os.environ['PLUMA_DROP_DOCUMENT_RELATIVE_PATH'] = relpath
|
|
+
|
|
+ mark = buf.get_mark('gtk_drag_target')
|
|
+
|
|
+ if not mark:
|
|
+ mark = buf.get_insert()
|
|
+
|
|
+ piter = buf.get_iter_at_mark(mark)
|
|
+ self.apply_snippet(snippet, piter, piter)
|
|
+
|
|
+ def in_bounds(self, x, y):
|
|
+ rect = self.view.get_visible_rect()
|
|
+ rect.x, rect.y = self.view.buffer_to_window_coords(Gtk.TextWindowType.WIDGET, rect.x, rect.y)
|
|
+
|
|
+ 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):
|
|
+ uris = drop_get_uris(data)
|
|
+ if not uris:
|
|
+ return
|
|
+
|
|
+ if not self.in_bounds(x, y):
|
|
+ return
|
|
+
|
|
+ uris.reverse()
|
|
+ stop = False
|
|
+
|
|
+ for uri in uris:
|
|
+ try:
|
|
+ mime = Gio.content_type_guess(uri)
|
|
+ except:
|
|
+ mime = None
|
|
+
|
|
+ if not mime:
|
|
+ continue
|
|
+
|
|
+ snippets = Library().from_drop_target(mime, self.language_id)
|
|
+
|
|
+ if snippets:
|
|
+ stop = True
|
|
+ self.apply_uri_snippet(snippets[0], mime, uri)
|
|
+
|
|
+ if stop:
|
|
+ context.finish(True, False, timestamp)
|
|
+ view.stop_emission('drag-data-received')
|
|
+ view.get_toplevel().present()
|
|
+ view.grab_focus()
|
|
+
|
|
+ def find_uri_target(self, context):
|
|
+ lst = Gtk.target_list_add_uri_targets((), 0)
|
|
+
|
|
+ return self.view.drag_dest_find_target(context, lst)
|
|
+
|
|
+ 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
|
|
+
|
|
+ def iter_coords(self, piter):
|
|
+ rect = self.view.get_iter_location(piter)
|
|
+ rect.x, rect.y = self.view.buffer_to_window_coords(Gtk.TextWindowType.TEXT, rect.x, rect.y)
|
|
+
|
|
+ return rect
|
|
+
|
|
+ def placeholder_in_area(self, placeholder, area):
|
|
+ start = placeholder.begin_iter()
|
|
+ end = placeholder.end_iter()
|
|
+
|
|
+ if not start or not end:
|
|
+ return False
|
|
+
|
|
+ # Test if start is before bottom, and end is after top
|
|
+ start_rect = self.iter_coords(start)
|
|
+ end_rect = self.iter_coords(end)
|
|
+
|
|
+ return start_rect.y <= area.y + area.height and \
|
|
+ end_rect.y + end_rect.height >= area.y
|
|
+
|
|
+ def draw_placeholder_rect(self, ctx, placeholder):
|
|
+ start = placeholder.begin_iter()
|
|
+ start_rect = self.iter_coords(start)
|
|
+ start_line = start.get_line()
|
|
+
|
|
+ end = placeholder.end_iter()
|
|
+ end_rect = self.iter_coords(end)
|
|
+ end_line = end.get_line()
|
|
+
|
|
+ line = start.copy()
|
|
+ line.set_line_offset(0)
|
|
+ geom = self.view.get_window(Gtk.TextWindowType.TEXT).get_geometry()
|
|
+
|
|
+ ctx.translate(0.5, 0.5)
|
|
+
|
|
+ while line.get_line() <= end_line:
|
|
+ ypos, height = self.view.get_line_yrange(line)
|
|
+ x_, ypos = self.view.window_to_buffer_coords(Gtk.TextWindowType.TEXT, 0, ypos)
|
|
+
|
|
+ if line.get_line() == start_line and line.get_line() == end_line:
|
|
+ # Simply draw a box, both are on the same line
|
|
+ ctx.rectangle(start_rect.x, start_rect.y, end_rect.x - start_rect.x, start_rect.height - 1)
|
|
+ ctx.stroke()
|
|
+ elif line.get_line() == start_line or line.get_line() == end_line:
|
|
+ if line.get_line() == start_line:
|
|
+ rect = start_rect
|
|
else:
|
|
- return False
|
|
-
|
|
- def iter_coords(self, piter):
|
|
- rect = self.view.get_iter_location(piter)
|
|
- rect.x, rect.y = self.view.buffer_to_window_coords(Gtk.TextWindowType.TEXT, rect.x, rect.y)
|
|
-
|
|
- return rect
|
|
-
|
|
- def placeholder_in_area(self, placeholder, area):
|
|
- start = placeholder.begin_iter()
|
|
- end = placeholder.end_iter()
|
|
-
|
|
- if not start or not end:
|
|
- return False
|
|
-
|
|
- # Test if start is before bottom, and end is after top
|
|
- start_rect = self.iter_coords(start)
|
|
- end_rect = self.iter_coords(end)
|
|
-
|
|
- return start_rect.y <= area.y + area.height and \
|
|
- end_rect.y + end_rect.height >= area.y
|
|
-
|
|
- def draw_placeholder_rect(self, ctx, placeholder):
|
|
- start = placeholder.begin_iter()
|
|
- start_rect = self.iter_coords(start)
|
|
- start_line = start.get_line()
|
|
-
|
|
- end = placeholder.end_iter()
|
|
- end_rect = self.iter_coords(end)
|
|
- end_line = end.get_line()
|
|
-
|
|
- line = start.copy()
|
|
- line.set_line_offset(0)
|
|
- geom = self.view.get_window(Gtk.TextWindowType.TEXT).get_geometry()
|
|
-
|
|
- ctx.translate(0.5, 0.5)
|
|
-
|
|
- while line.get_line() <= end_line:
|
|
- ypos, height = self.view.get_line_yrange(line)
|
|
- x_, ypos = self.view.window_to_buffer_coords(Gtk.TextWindowType.TEXT, 0, ypos)
|
|
-
|
|
- if line.get_line() == start_line and line.get_line() == end_line:
|
|
- # Simply draw a box, both are on the same line
|
|
- ctx.rectangle(start_rect.x, start_rect.y, end_rect.x - start_rect.x, start_rect.height - 1)
|
|
- ctx.stroke()
|
|
- elif line.get_line() == start_line or line.get_line() == end_line:
|
|
- if line.get_line() == start_line:
|
|
- rect = start_rect
|
|
- else:
|
|
- rect = end_rect
|
|
-
|
|
- ctx.move_to(0, rect.y + rect.height - 1)
|
|
- ctx.rel_line_to(rect.x, 0)
|
|
- ctx.rel_line_to(0, -rect.height + 1)
|
|
- ctx.rel_line_to(geom[2], 0)
|
|
- ctx.stroke()
|
|
-
|
|
- if not line.forward_line():
|
|
- break
|
|
-
|
|
- def draw_placeholder_bar(self, ctx, placeholder):
|
|
- start = placeholder.begin_iter()
|
|
- start_rect = self.iter_coords(start)
|
|
-
|
|
- ctx.translate(0.5, 0.5)
|
|
- extend_width = 2.5
|
|
-
|
|
- ctx.move_to(start_rect.x - extend_width, start_rect.y)
|
|
- ctx.rel_line_to(extend_width * 2, 0)
|
|
-
|
|
- ctx.move_to(start_rect.x, start_rect.y)
|
|
- ctx.rel_line_to(0, start_rect.height - 1)
|
|
-
|
|
- ctx.rel_move_to(-extend_width, 0)
|
|
- ctx.rel_line_to(extend_width * 2, 0)
|
|
+ rect = end_rect
|
|
+
|
|
+ ctx.move_to(0, rect.y + rect.height - 1)
|
|
+ ctx.rel_line_to(rect.x, 0)
|
|
+ ctx.rel_line_to(0, -rect.height + 1)
|
|
+ ctx.rel_line_to(geom[2], 0)
|
|
ctx.stroke()
|
|
|
|
- def draw_placeholder(self, ctx, placeholder):
|
|
- if isinstance(placeholder, PlaceholderEnd):
|
|
- return
|
|
+ if not line.forward_line():
|
|
+ break
|
|
|
|
- buf = self.view.get_buffer()
|
|
+ def draw_placeholder_bar(self, ctx, placeholder):
|
|
+ start = placeholder.begin_iter()
|
|
+ start_rect = self.iter_coords(start)
|
|
|
|
- col = self.view.get_style_context().get_color(Gtk.StateFlags.INSENSITIVE)
|
|
- col.alpha = 0.5
|
|
- Gdk.cairo_set_source_rgba(ctx, col)
|
|
-
|
|
- if placeholder.tabstop > 0:
|
|
- ctx.set_dash([], 0)
|
|
- else:
|
|
- ctx.set_dash([2], 0)
|
|
+ ctx.translate(0.5, 0.5)
|
|
+ extend_width = 2.5
|
|
|
|
- start = placeholder.begin_iter()
|
|
- end = placeholder.end_iter()
|
|
+ ctx.move_to(start_rect.x - extend_width, start_rect.y)
|
|
+ ctx.rel_line_to(extend_width * 2, 0)
|
|
|
|
- if start.equal(end):
|
|
- self.draw_placeholder_bar(ctx, placeholder)
|
|
- else:
|
|
- self.draw_placeholder_rect(ctx, placeholder)
|
|
+ ctx.move_to(start_rect.x, start_rect.y)
|
|
+ ctx.rel_line_to(0, start_rect.height - 1)
|
|
+
|
|
+ ctx.rel_move_to(-extend_width, 0)
|
|
+ ctx.rel_line_to(extend_width * 2, 0)
|
|
+ ctx.stroke()
|
|
+
|
|
+ def draw_placeholder(self, ctx, placeholder):
|
|
+ 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)
|
|
+
|
|
+ if placeholder.tabstop > 0:
|
|
+ ctx.set_dash([], 0)
|
|
+ else:
|
|
+ ctx.set_dash([2], 0)
|
|
+
|
|
+ start = placeholder.begin_iter()
|
|
+ end = placeholder.end_iter()
|
|
+
|
|
+ if start.equal(end):
|
|
+ self.draw_placeholder_bar(ctx, placeholder)
|
|
+ else:
|
|
+ self.draw_placeholder_rect(ctx, placeholder)
|
|
|
|
- def on_draw(self, view, ctx):
|
|
- window = view.get_window(Gtk.TextWindowType.TEXT)
|
|
+ def on_draw(self, view, ctx):
|
|
+ window = view.get_window(Gtk.TextWindowType.TEXT)
|
|
|
|
- if not Gtk.cairo_should_draw_window(ctx, window):
|
|
- return False
|
|
+ if not Gtk.cairo_should_draw_window(ctx, window):
|
|
+ return False
|
|
|
|
- ctx.set_line_width(1.0)
|
|
- Gtk.cairo_transform_to_window(ctx, view, window)
|
|
- clipped, clip = Gdk.cairo_get_clip_rectangle(ctx)
|
|
+ ctx.set_line_width(1.0)
|
|
+ Gtk.cairo_transform_to_window(ctx, view, window)
|
|
+ clipped, clip = Gdk.cairo_get_clip_rectangle(ctx)
|
|
|
|
- if not clipped:
|
|
- return False
|
|
+ if not clipped:
|
|
+ return False
|
|
|
|
- for placeholder in self.ordered_placeholders:
|
|
- if not self.placeholder_in_area(placeholder, clip):
|
|
- continue
|
|
+ for placeholder in self.ordered_placeholders:
|
|
+ if not self.placeholder_in_area(placeholder, clip):
|
|
+ continue
|
|
|
|
- ctx.save()
|
|
- self.draw_placeholder(ctx, placeholder)
|
|
- ctx.restore()
|
|
+ ctx.save()
|
|
+ self.draw_placeholder(ctx, placeholder)
|
|
+ ctx.restore()
|
|
|
|
- return False
|
|
+ return False
|
|
|
|
-# ex:ts=8:et:
|
|
+# ex:ts=4:et:
|
|
diff --git a/plugins/snippets/snippets/Exporter.py b/plugins/snippets/snippets/Exporter.py
|
|
old mode 100755
|
|
new mode 100644
|
|
index 18369a6..850c3a4
|
|
--- a/plugins/snippets/snippets/Exporter.py
|
|
+++ b/plugins/snippets/snippets/Exporter.py
|
|
@@ -8,91 +8,91 @@ import xml.etree.ElementTree as et
|
|
from Helper import *
|
|
|
|
class Exporter:
|
|
- def __init__(self, filename, snippets):
|
|
- self.filename = filename
|
|
- self.set_snippets(snippets)
|
|
-
|
|
- def set_snippets(self, snippets):
|
|
- self.snippets = {}
|
|
-
|
|
- for snippet in snippets:
|
|
- lang = snippet.language()
|
|
-
|
|
- if lang in self.snippets:
|
|
- self.snippets[lang].append(snippet)
|
|
- else:
|
|
- self.snippets[lang] = [snippet]
|
|
-
|
|
- def export_xml(self, dirname, language, snippets):
|
|
- # Create the root snippets node
|
|
- root = et.Element('snippets')
|
|
-
|
|
- # Create filename based on language
|
|
- if language:
|
|
- filename = os.path.join(dirname, language + '.xml')
|
|
-
|
|
- # Set the language attribute
|
|
- root.attrib['language'] = language
|
|
- else:
|
|
- filename = os.path.join(dirname, 'global.xml')
|
|
-
|
|
- # Add all snippets to the root node
|
|
- for snippet in snippets:
|
|
- root.append(snippet.to_xml())
|
|
-
|
|
- # Write xml
|
|
- write_xml(root, filename, ('text', 'accelerator'))
|
|
-
|
|
- def export_archive(self, cmd):
|
|
- dirname = tempfile.mkdtemp()
|
|
-
|
|
- # Save current working directory and change to temporary directory
|
|
- curdir = os.getcwd()
|
|
-
|
|
- try:
|
|
- os.chdir(dirname)
|
|
-
|
|
- # Write snippet xml files
|
|
- for language, snippets in self.snippets.items():
|
|
- self.export_xml(dirname, language , snippets)
|
|
-
|
|
- # Archive files
|
|
- status = os.system('%s "%s" *.xml' % (cmd, self.filename))
|
|
- finally:
|
|
- os.chdir(curdir)
|
|
-
|
|
- if status != 0:
|
|
- return _('The archive "%s" could not be created' % self.filename)
|
|
-
|
|
- # Remove the temporary directory
|
|
- shutil.rmtree(dirname)
|
|
-
|
|
- def export_targz(self):
|
|
- self.export_archive('tar -c --gzip -f')
|
|
-
|
|
- def export_tarbz2(self):
|
|
- self.export_archive('tar -c --bzip2 -f')
|
|
-
|
|
- def export_tar(self):
|
|
- self.export_archive('tar -cf')
|
|
-
|
|
- def run(self):
|
|
- dirname = os.path.dirname(self.filename)
|
|
- if not os.path.exists(dirname):
|
|
- return _('Target directory "%s" does not exist') % dirname
|
|
-
|
|
- if not os.path.isdir(dirname):
|
|
- return _('Target directory "%s" is not a valid directory') % dirname
|
|
-
|
|
- (root, ext) = os.path.splitext(self.filename)
|
|
-
|
|
- actions = {'.tar.gz': self.export_targz,
|
|
- '.tar.bz2': self.export_tarbz2,
|
|
- '.tar': self.export_tar}
|
|
-
|
|
- for k, v in actions.items():
|
|
- if self.filename.endswith(k):
|
|
- return v()
|
|
-
|
|
- return self.export_targz()
|
|
-# ex:ts=8:et:
|
|
+ def __init__(self, filename, snippets):
|
|
+ self.filename = filename
|
|
+ self.set_snippets(snippets)
|
|
+
|
|
+ def set_snippets(self, snippets):
|
|
+ self.snippets = {}
|
|
+
|
|
+ for snippet in snippets:
|
|
+ lang = snippet.language()
|
|
+
|
|
+ if lang in self.snippets:
|
|
+ self.snippets[lang].append(snippet)
|
|
+ else:
|
|
+ self.snippets[lang] = [snippet]
|
|
+
|
|
+ def export_xml(self, dirname, language, snippets):
|
|
+ # Create the root snippets node
|
|
+ root = et.Element('snippets')
|
|
+
|
|
+ # Create filename based on language
|
|
+ if language:
|
|
+ filename = os.path.join(dirname, language + '.xml')
|
|
+
|
|
+ # Set the language attribute
|
|
+ root.attrib['language'] = language
|
|
+ else:
|
|
+ filename = os.path.join(dirname, 'global.xml')
|
|
+
|
|
+ # Add all snippets to the root node
|
|
+ for snippet in snippets:
|
|
+ root.append(snippet.to_xml())
|
|
+
|
|
+ # Write xml
|
|
+ write_xml(root, filename, ('text', 'accelerator'))
|
|
+
|
|
+ def export_archive(self, cmd):
|
|
+ dirname = tempfile.mkdtemp()
|
|
+
|
|
+ # Save current working directory and change to temporary directory
|
|
+ curdir = os.getcwd()
|
|
+
|
|
+ try:
|
|
+ os.chdir(dirname)
|
|
+
|
|
+ # Write snippet xml files
|
|
+ for language, snippets in self.snippets.items():
|
|
+ self.export_xml(dirname, language , snippets)
|
|
+
|
|
+ # Archive files
|
|
+ status = os.system('%s "%s" *.xml' % (cmd, self.filename))
|
|
+ finally:
|
|
+ os.chdir(curdir)
|
|
+
|
|
+ if status != 0:
|
|
+ return _('The archive "%s" could not be created' % self.filename)
|
|
+
|
|
+ # Remove the temporary directory
|
|
+ shutil.rmtree(dirname)
|
|
+
|
|
+ def export_targz(self):
|
|
+ self.export_archive('tar -c --gzip -f')
|
|
+
|
|
+ def export_tarbz2(self):
|
|
+ self.export_archive('tar -c --bzip2 -f')
|
|
+
|
|
+ def export_tar(self):
|
|
+ self.export_archive('tar -cf')
|
|
+
|
|
+ def run(self):
|
|
+ dirname = os.path.dirname(self.filename)
|
|
+ if not os.path.exists(dirname):
|
|
+ return _('Target directory "%s" does not exist') % dirname
|
|
+
|
|
+ if not os.path.isdir(dirname):
|
|
+ return _('Target directory "%s" is not a valid directory') % dirname
|
|
+
|
|
+ (root, ext) = os.path.splitext(self.filename)
|
|
+
|
|
+ actions = {'.tar.gz': self.export_targz,
|
|
+ '.tar.bz2': self.export_tarbz2,
|
|
+ '.tar': self.export_tar}
|
|
+
|
|
+ for k, v in actions.items():
|
|
+ if self.filename.endswith(k):
|
|
+ return v()
|
|
+
|
|
+ return self.export_targz()
|
|
+# ex:ts=4:et:
|
|
diff --git a/plugins/snippets/snippets/Helper.py b/plugins/snippets/snippets/Helper.py
|
|
old mode 100755
|
|
new mode 100644
|
|
index d8a1967..6d440d0
|
|
--- a/plugins/snippets/snippets/Helper.py
|
|
+++ b/plugins/snippets/snippets/Helper.py
|
|
@@ -23,164 +23,164 @@ import re
|
|
from gi.repository import Gtk
|
|
|
|
def message_dialog(par, typ, msg):
|
|
- d = Gtk.MessageDialog(par, Gtk.DialogFlags.MODAL, typ, Gtk.ButtonsType.OK, msg)
|
|
- d.set_property('use-markup', True)
|
|
+ d = Gtk.MessageDialog(par, Gtk.DialogFlags.MODAL, typ, Gtk.ButtonsType.OK, msg)
|
|
+ d.set_property('use-markup', True)
|
|
|
|
- d.run()
|
|
- d.destroy()
|
|
+ d.run()
|
|
+ d.destroy()
|
|
|
|
def compute_indentation(view, piter):
|
|
- line = piter.get_line()
|
|
- start = view.get_buffer().get_iter_at_line(line)
|
|
- end = start.copy()
|
|
-
|
|
+ line = piter.get_line()
|
|
+ start = view.get_buffer().get_iter_at_line(line)
|
|
+ end = start.copy()
|
|
+
|
|
+ ch = end.get_char()
|
|
+
|
|
+ while (ch.isspace() and ch != '\r' and ch != '\n' and \
|
|
+ end.compare(piter) < 0):
|
|
+ if not end.forward_char():
|
|
+ break;
|
|
+
|
|
ch = end.get_char()
|
|
-
|
|
- while (ch.isspace() and ch != '\r' and ch != '\n' and \
|
|
- end.compare(piter) < 0):
|
|
- if not end.forward_char():
|
|
- break;
|
|
-
|
|
- ch = end.get_char()
|
|
-
|
|
- if start.equal(end):
|
|
- return ''
|
|
-
|
|
- return start.get_slice(end)
|
|
+
|
|
+ if start.equal(end):
|
|
+ return ''
|
|
+
|
|
+ return start.get_slice(end)
|
|
|
|
def markup_escape(text):
|
|
- return saxutils.escape(text)
|
|
+ return saxutils.escape(text)
|
|
|
|
def spaces_instead_of_tabs(view, text):
|
|
- if not view.get_insert_spaces_instead_of_tabs():
|
|
- return text
|
|
+ if not view.get_insert_spaces_instead_of_tabs():
|
|
+ return text
|
|
|
|
- return text.replace("\t", view.get_tab_width() * ' ')
|
|
+ return text.replace("\t", view.get_tab_width() * ' ')
|
|
|
|
def insert_with_indent(view, piter, text, indentfirst = True, context = None):
|
|
- text = spaces_instead_of_tabs(view, text)
|
|
- lines = text.split('\n')
|
|
- buf = view.get_buffer()
|
|
+ text = spaces_instead_of_tabs(view, text)
|
|
+ lines = text.split('\n')
|
|
+ buf = view.get_buffer()
|
|
+
|
|
+ buf._snippets_context = context
|
|
|
|
- buf._snippets_context = context
|
|
+ if len(lines) == 1:
|
|
+ view.get_buffer().insert(piter, text)
|
|
+ else:
|
|
+ # Compute indentation
|
|
+ indent = compute_indentation(view, piter)
|
|
+ text = ''
|
|
|
|
- if len(lines) == 1:
|
|
- view.get_buffer().insert(piter, text)
|
|
- else:
|
|
- # Compute indentation
|
|
- indent = compute_indentation(view, piter)
|
|
- text = ''
|
|
+ for i in range(0, len(lines)):
|
|
+ if indentfirst or i > 0:
|
|
+ text += indent + lines[i] + '\n'
|
|
+ else:
|
|
+ text += lines[i] + '\n'
|
|
|
|
- for i in range(0, len(lines)):
|
|
- if indentfirst or i > 0:
|
|
- text += indent + lines[i] + '\n'
|
|
- else:
|
|
- text += lines[i] + '\n'
|
|
-
|
|
- buf.insert(piter, text[:-1])
|
|
+ buf.insert(piter, text[:-1])
|
|
|
|
- buf._snippets_context = None
|
|
+ buf._snippets_context = None
|
|
|
|
def get_buffer_context(buf):
|
|
- if hasattr(buf, "_snippets_context"):
|
|
- return buf._snippets_context
|
|
- return None
|
|
+ if hasattr(buf, "_snippets_context"):
|
|
+ return buf._snippets_context
|
|
+ return None
|
|
|
|
def snippets_debug(*s):
|
|
- return
|
|
+ return
|
|
|
|
def write_xml(node, f, cdata_nodes=()):
|
|
- assert node is not None
|
|
+ assert node is not None
|
|
|
|
- if not hasattr(f, "write"):
|
|
- f = open(f, "wb")
|
|
+ if not hasattr(f, "write"):
|
|
+ f = open(f, "wb")
|
|
|
|
- # Encoding
|
|
- f.write("<?xml version='1.0' encoding='utf-8'?>\n")
|
|
+ # Encoding
|
|
+ f.write("<?xml version='1.0' encoding='utf-8'?>\n")
|
|
|
|
- _write_node(node, f, cdata_nodes)
|
|
+ _write_node(node, f, cdata_nodes)
|
|
|
|
def _write_indent(file, text, indent):
|
|
- file.write(' ' * indent + text)
|
|
+ file.write(' ' * indent + text)
|
|
|
|
def _write_node(node, file, cdata_nodes=(), indent=0):
|
|
- # write XML to file
|
|
- tag = node.tag
|
|
-
|
|
- if node is Comment:
|
|
- _write_indent(file, "<!-- %s -->\n" % saxutils.escape(node.text.encode('utf-8')), indent)
|
|
- elif node is ProcessingInstruction:
|
|
- _write_indent(file, "<?%s?>\n" % saxutils.escape(node.text.encode('utf-8')), indent)
|
|
- else:
|
|
- items = node.items()
|
|
-
|
|
- if items or node.text or len(node):
|
|
- _write_indent(file, "<" + tag.encode('utf-8'), 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'))))
|
|
- 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')))
|
|
- else:
|
|
- file.write("\n")
|
|
-
|
|
- for n in node:
|
|
- _write_node(n, file, cdata_nodes, indent + 1)
|
|
-
|
|
- if not len(node):
|
|
- file.write("</" + tag.encode('utf-8') + ">\n")
|
|
- else:
|
|
- _write_indent(file, "</" + tag.encode('utf-8') + ">\n", \
|
|
- indent)
|
|
- else:
|
|
- file.write(" />\n")
|
|
-
|
|
- if node.tail and node.tail.strip() != "":
|
|
- file.write(saxutils.escape(node.tail.encode('utf-8')))
|
|
+ # write XML to file
|
|
+ tag = node.tag
|
|
+
|
|
+ if node is Comment:
|
|
+ _write_indent(file, "<!-- %s -->\n" % saxutils.escape(node.text.encode('utf-8')), indent)
|
|
+ elif node is ProcessingInstruction:
|
|
+ _write_indent(file, "<?%s?>\n" % saxutils.escape(node.text.encode('utf-8')), indent)
|
|
+ else:
|
|
+ items = node.items()
|
|
+
|
|
+ if items or node.text or len(node):
|
|
+ _write_indent(file, "<" + tag.encode('utf-8'), 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'))))
|
|
+ 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')))
|
|
+ else:
|
|
+ file.write("\n")
|
|
+
|
|
+ for n in node:
|
|
+ _write_node(n, file, cdata_nodes, indent + 1)
|
|
+
|
|
+ if not len(node):
|
|
+ file.write("</" + tag.encode('utf-8') + ">\n")
|
|
+ else:
|
|
+ _write_indent(file, "</" + tag.encode('utf-8') + ">\n", \
|
|
+ indent)
|
|
+ else:
|
|
+ file.write(" />\n")
|
|
+
|
|
+ if node.tail and node.tail.strip() != "":
|
|
+ file.write(saxutils.escape(node.tail.encode('utf-8')))
|
|
|
|
def _cdata(text, replace=string.replace):
|
|
- text = text.encode('utf-8')
|
|
- return '<![CDATA[' + replace(text, ']]>', ']]]]><![CDATA[>') + ']]>'
|
|
+ text = text.encode('utf-8')
|
|
+ return '<![CDATA[' + replace(text, ']]>', ']]]]><![CDATA[>') + ']]>'
|
|
|
|
def buffer_word_boundary(buf):
|
|
- iter = buf.get_iter_at_mark(buf.get_insert())
|
|
- start = iter.copy()
|
|
-
|
|
- if not iter.starts_word() and (iter.inside_word() or iter.ends_word()):
|
|
- start.backward_word_start()
|
|
-
|
|
- if not iter.ends_word() and iter.inside_word():
|
|
- iter.forward_word_end()
|
|
-
|
|
- return (start, iter)
|
|
+ iter = buf.get_iter_at_mark(buf.get_insert())
|
|
+ start = iter.copy()
|
|
+
|
|
+ if not iter.starts_word() and (iter.inside_word() or iter.ends_word()):
|
|
+ start.backward_word_start()
|
|
+
|
|
+ if not iter.ends_word() and iter.inside_word():
|
|
+ iter.forward_word_end()
|
|
+
|
|
+ return (start, iter)
|
|
|
|
def buffer_line_boundary(buf):
|
|
- iter = buf.get_iter_at_mark(buf.get_insert())
|
|
- start = iter.copy()
|
|
- start.set_line_offset(0)
|
|
-
|
|
- if not iter.ends_line():
|
|
- iter.forward_to_line_end()
|
|
-
|
|
- return (start, iter)
|
|
+ iter = buf.get_iter_at_mark(buf.get_insert())
|
|
+ start = iter.copy()
|
|
+ start.set_line_offset(0)
|
|
+
|
|
+ if not iter.ends_line():
|
|
+ iter.forward_to_line_end()
|
|
+
|
|
+ return (start, iter)
|
|
|
|
def drop_get_uris(selection):
|
|
- uris = []
|
|
- if selection.targets_include_uri():
|
|
- data = selection.get_data()
|
|
- lines = re.split('\\s*[\\n\\r]+\\s*', data.strip())
|
|
+ uris = []
|
|
+ if selection.targets_include_uri():
|
|
+ data = selection.get_data()
|
|
+ lines = re.split('\\s*[\\n\\r]+\\s*', data.strip())
|
|
|
|
- for line in lines:
|
|
- if not line.startswith('#'):
|
|
- uris.append(line)
|
|
+ for line in lines:
|
|
+ if not line.startswith('#'):
|
|
+ uris.append(line)
|
|
|
|
- return uris
|
|
+ return uris
|
|
|
|
-# ex:ts=8:et:
|
|
+# ex:ts=4:et:
|
|
diff --git a/plugins/snippets/snippets/Importer.py b/plugins/snippets/snippets/Importer.py
|
|
old mode 100755
|
|
new mode 100644
|
|
index b2e8723..c1d211e
|
|
--- a/plugins/snippets/snippets/Importer.py
|
|
+++ b/plugins/snippets/snippets/Importer.py
|
|
@@ -6,95 +6,95 @@ import shutil
|
|
from Library import *
|
|
|
|
class Importer:
|
|
- def __init__(self, filename):
|
|
- self.filename = filename
|
|
-
|
|
- def import_destination(self, filename):
|
|
- userdir = Library().userdir
|
|
-
|
|
- filename = os.path.basename(filename)
|
|
- (root, ext) = os.path.splitext(filename)
|
|
-
|
|
- filename = os.path.join(userdir, root + ext)
|
|
- i = 1
|
|
-
|
|
- while os.path.exists(filename):
|
|
- filename = os.path.join(userdir, root + '_' + str(i) + ext)
|
|
- i += 1
|
|
-
|
|
- return filename
|
|
-
|
|
- def import_file(self, filename):
|
|
- if not os.path.exists(filename):
|
|
- return _('File "%s" does not exist') % filename
|
|
-
|
|
- if not os.path.isfile(filename):
|
|
- return _('File "%s" is not a valid snippets file') % filename
|
|
-
|
|
- # Find destination for file to copy to
|
|
- dest = self.import_destination(filename)
|
|
-
|
|
- # Copy file
|
|
- shutil.copy(filename, dest)
|
|
-
|
|
- # Add library
|
|
- if not Library().add_user_library(dest):
|
|
- return _('Imported file "%s" is not a valid snippets file') % os.path.basename(dest)
|
|
-
|
|
- def import_xml(self):
|
|
- return self.import_file(self.filename)
|
|
-
|
|
- def import_archive(self, cmd):
|
|
- dirname = tempfile.mkdtemp()
|
|
- status = os.system('cd %s; %s "%s"' % (dirname, cmd, self.filename))
|
|
-
|
|
- if status != 0:
|
|
- return _('The archive "%s" could not be extracted' % self.filename)
|
|
-
|
|
- errors = []
|
|
-
|
|
- # Now import all the files from the archive
|
|
- for f in os.listdir(dirname):
|
|
- f = os.path.join(dirname, f)
|
|
-
|
|
- if os.path.isfile(f):
|
|
- if self.import_file(f):
|
|
- errors.append(os.path.basename(f))
|
|
- else:
|
|
- sys.stderr.write('Skipping %s, not a valid snippets file' % os.path.basename(f))
|
|
-
|
|
- # Remove the temporary directory
|
|
- shutil.rmtree(dirname)
|
|
-
|
|
- if len(errors) > 0:
|
|
- return _('The following files could not be imported: %s') % ', '.join(errors)
|
|
-
|
|
- def import_targz(self):
|
|
- self.import_archive('tar -x --gzip -f')
|
|
-
|
|
- def import_tarbz2(self):
|
|
- self.import_archive('tar -x --bzip2 -f')
|
|
-
|
|
- def import_tar(self):
|
|
- self.import_archive('tar -xf')
|
|
-
|
|
- def run(self):
|
|
- if not os.path.exists(self.filename):
|
|
- return _('File "%s" does not exist') % self.filename
|
|
-
|
|
- if not os.path.isfile(self.filename):
|
|
- return _('File "%s" is not a valid snippets archive') % self.filename
|
|
-
|
|
- (root, ext) = os.path.splitext(self.filename)
|
|
-
|
|
- actions = {'.tar.gz': self.import_targz,
|
|
- '.tar.bz2': self.import_tarbz2,
|
|
- '.xml': self.import_xml,
|
|
- '.tar': self.import_tar}
|
|
-
|
|
- for k, v in actions.items():
|
|
- if self.filename.endswith(k):
|
|
- return v()
|
|
-
|
|
- return _('File "%s" is not a valid snippets archive') % self.filename
|
|
-# ex:ts=8:et:
|
|
+ def __init__(self, filename):
|
|
+ self.filename = filename
|
|
+
|
|
+ def import_destination(self, filename):
|
|
+ userdir = Library().userdir
|
|
+
|
|
+ filename = os.path.basename(filename)
|
|
+ (root, ext) = os.path.splitext(filename)
|
|
+
|
|
+ filename = os.path.join(userdir, root + ext)
|
|
+ i = 1
|
|
+
|
|
+ while os.path.exists(filename):
|
|
+ filename = os.path.join(userdir, root + '_' + str(i) + ext)
|
|
+ i += 1
|
|
+
|
|
+ return filename
|
|
+
|
|
+ def import_file(self, filename):
|
|
+ if not os.path.exists(filename):
|
|
+ return _('File "%s" does not exist') % filename
|
|
+
|
|
+ if not os.path.isfile(filename):
|
|
+ return _('File "%s" is not a valid snippets file') % filename
|
|
+
|
|
+ # Find destination for file to copy to
|
|
+ dest = self.import_destination(filename)
|
|
+
|
|
+ # Copy file
|
|
+ shutil.copy(filename, dest)
|
|
+
|
|
+ # Add library
|
|
+ if not Library().add_user_library(dest):
|
|
+ return _('Imported file "%s" is not a valid snippets file') % os.path.basename(dest)
|
|
+
|
|
+ def import_xml(self):
|
|
+ return self.import_file(self.filename)
|
|
+
|
|
+ def import_archive(self, cmd):
|
|
+ dirname = tempfile.mkdtemp()
|
|
+ status = os.system('cd %s; %s "%s"' % (dirname, cmd, self.filename))
|
|
+
|
|
+ if status != 0:
|
|
+ return _('The archive "%s" could not be extracted' % self.filename)
|
|
+
|
|
+ errors = []
|
|
+
|
|
+ # Now import all the files from the archive
|
|
+ for f in os.listdir(dirname):
|
|
+ f = os.path.join(dirname, f)
|
|
+
|
|
+ if os.path.isfile(f):
|
|
+ if self.import_file(f):
|
|
+ errors.append(os.path.basename(f))
|
|
+ else:
|
|
+ sys.stderr.write('Skipping %s, not a valid snippets file' % os.path.basename(f))
|
|
+
|
|
+ # Remove the temporary directory
|
|
+ shutil.rmtree(dirname)
|
|
+
|
|
+ if len(errors) > 0:
|
|
+ return _('The following files could not be imported: %s') % ', '.join(errors)
|
|
+
|
|
+ def import_targz(self):
|
|
+ self.import_archive('tar -x --gzip -f')
|
|
+
|
|
+ def import_tarbz2(self):
|
|
+ self.import_archive('tar -x --bzip2 -f')
|
|
+
|
|
+ def import_tar(self):
|
|
+ self.import_archive('tar -xf')
|
|
+
|
|
+ def run(self):
|
|
+ if not os.path.exists(self.filename):
|
|
+ return _('File "%s" does not exist') % self.filename
|
|
+
|
|
+ if not os.path.isfile(self.filename):
|
|
+ return _('File "%s" is not a valid snippets archive') % self.filename
|
|
+
|
|
+ (root, ext) = os.path.splitext(self.filename)
|
|
+
|
|
+ actions = {'.tar.gz': self.import_targz,
|
|
+ '.tar.bz2': self.import_tarbz2,
|
|
+ '.xml': self.import_xml,
|
|
+ '.tar': self.import_tar}
|
|
+
|
|
+ for k, v in actions.items():
|
|
+ if self.filename.endswith(k):
|
|
+ return v()
|
|
+
|
|
+ return _('File "%s" is not a valid snippets archive') % self.filename
|
|
+# ex:ts=4:et:
|
|
diff --git a/plugins/snippets/snippets/LanguageManager.py b/plugins/snippets/snippets/LanguageManager.py
|
|
old mode 100755
|
|
new mode 100644
|
|
index 1fb4347..e738333
|
|
--- a/plugins/snippets/snippets/LanguageManager.py
|
|
+++ b/plugins/snippets/snippets/LanguageManager.py
|
|
@@ -7,15 +7,16 @@ 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 = GtkSource.LanguageManager()
|
|
- manager.set_search_path(dirs + manager.get_search_path())
|
|
-
|
|
- return manager
|
|
+ global manager
|
|
+
|
|
+ if not manager:
|
|
+ dirs = []
|
|
+
|
|
+ for d in Library().systemdirs:
|
|
+ dirs.append(os.path.join(d, 'lang'))
|
|
+
|
|
+ manager = GtkSource.LanguageManager()
|
|
+ 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
|
|
old mode 100755
|
|
new mode 100644
|
|
index 5b3773a..f152082
|
|
--- a/plugins/snippets/snippets/Library.py
|
|
+++ b/plugins/snippets/snippets/Library.py
|
|
@@ -27,973 +27,973 @@ import xml.etree.ElementTree as et
|
|
from Helper import *
|
|
|
|
class NamespacedId:
|
|
- def __init__(self, namespace, id):
|
|
- if not id:
|
|
- self.id = None
|
|
- else:
|
|
- if namespace:
|
|
- self.id = namespace + '-'
|
|
- else:
|
|
- self.id = 'global-'
|
|
-
|
|
- self.id += id
|
|
+ def __init__(self, namespace, id):
|
|
+ if not id:
|
|
+ self.id = None
|
|
+ else:
|
|
+ if namespace:
|
|
+ self.id = namespace + '-'
|
|
+ else:
|
|
+ self.id = 'global-'
|
|
+
|
|
+ self.id += id
|
|
|
|
class SnippetData:
|
|
- PROPS = {'tag': '', 'text': '', 'description': 'New snippet',
|
|
- 'accelerator': '', 'drop-targets': ''}
|
|
+ PROPS = {'tag': '', 'text': '', 'description': 'New snippet',
|
|
+ 'accelerator': '', 'drop-targets': ''}
|
|
|
|
- def __init__(self, node, library):
|
|
- self.priv_id = node.attrib.get('id')
|
|
+ def __init__(self, node, library):
|
|
+ self.priv_id = node.attrib.get('id')
|
|
|
|
- self.set_library(library)
|
|
- self.valid = False
|
|
- self.set_node(node)
|
|
+ self.set_library(library)
|
|
+ self.valid = False
|
|
+ self.set_node(node)
|
|
|
|
- def can_modify(self):
|
|
- return (self.library and (isinstance(self.library(), SnippetsUserFile)))
|
|
+ def can_modify(self):
|
|
+ return (self.library and (isinstance(self.library(), SnippetsUserFile)))
|
|
|
|
- def set_library(self, library):
|
|
- if library:
|
|
- self.library = weakref.ref(library)
|
|
- else:
|
|
- self.library = None
|
|
+ def set_library(self, library):
|
|
+ if library:
|
|
+ self.library = weakref.ref(library)
|
|
+ else:
|
|
+ self.library = None
|
|
+
|
|
+ self.id = NamespacedId(self.language(), self.priv_id).id
|
|
+
|
|
+ def set_node(self, node):
|
|
+ if self.can_modify():
|
|
+ self.node = node
|
|
+ else:
|
|
+ self.node = None
|
|
+
|
|
+ self.init_snippet_data(node)
|
|
+
|
|
+ def init_snippet_data(self, node):
|
|
+ if node == None:
|
|
+ return
|
|
+
|
|
+ self.override = node.attrib.get('override')
|
|
|
|
- self.id = NamespacedId(self.language(), self.priv_id).id
|
|
+ self.properties = {}
|
|
+ props = SnippetData.PROPS.copy()
|
|
+
|
|
+ # Store all properties present
|
|
+ for child in node:
|
|
+ if child.tag in props:
|
|
+ del props[child.tag]
|
|
+
|
|
+ # Normalize accelerator
|
|
+ if child.tag == 'accelerator' and child.text != None:
|
|
+ keyval, mod = Gtk.accelerator_parse(child.text)
|
|
+
|
|
+ if Gtk.accelerator_valid(keyval, mod):
|
|
+ child.text = Gtk.accelerator_name(keyval, mod)
|
|
+ else:
|
|
+ child.text = ''
|
|
|
|
- def set_node(self, node):
|
|
if self.can_modify():
|
|
- self.node = node
|
|
- else:
|
|
- self.node = None
|
|
-
|
|
- self.init_snippet_data(node)
|
|
-
|
|
- def init_snippet_data(self, node):
|
|
- if node == None:
|
|
- return
|
|
-
|
|
- self.override = node.attrib.get('override')
|
|
-
|
|
- self.properties = {}
|
|
- props = SnippetData.PROPS.copy()
|
|
-
|
|
- # Store all properties present
|
|
- for child in node:
|
|
- if child.tag in props:
|
|
- del props[child.tag]
|
|
-
|
|
- # Normalize accelerator
|
|
- if child.tag == 'accelerator' and child.text != None:
|
|
- keyval, mod = Gtk.accelerator_parse(child.text)
|
|
-
|
|
- if Gtk.accelerator_valid(keyval, mod):
|
|
- child.text = Gtk.accelerator_name(keyval, mod)
|
|
- else:
|
|
- child.text = ''
|
|
-
|
|
- if self.can_modify():
|
|
- self.properties[child.tag] = child
|
|
- else:
|
|
- self.properties[child.tag] = child.text or ''
|
|
-
|
|
- # Create all the props that were not found so we stay consistent
|
|
- for prop in props:
|
|
- if self.can_modify():
|
|
- child = et.SubElement(node, prop)
|
|
-
|
|
- child.text = props[prop]
|
|
- self.properties[prop] = child
|
|
- else:
|
|
- self.properties[prop] = props[prop]
|
|
-
|
|
- self.check_validation()
|
|
-
|
|
- def check_validation(self):
|
|
- if not self['tag'] and not self['accelerator'] and not self['drop-targets']:
|
|
- return False
|
|
-
|
|
- library = Library()
|
|
- keyval, mod = Gtk.accelerator_parse(self['accelerator'])
|
|
-
|
|
- self.valid = library.valid_tab_trigger(self['tag']) and \
|
|
- (not self['accelerator'] or library.valid_accelerator(keyval, mod))
|
|
-
|
|
- def _format_prop(self, prop, value):
|
|
- if prop == 'drop-targets' and value != '':
|
|
- return re.split('\\s*[,;]\\s*', value)
|
|
+ self.properties[child.tag] = child
|
|
else:
|
|
- return value
|
|
-
|
|
- def __getitem__(self, prop):
|
|
- if prop in self.properties:
|
|
- if self.can_modify():
|
|
- return self._format_prop(prop, self.properties[prop].text or '')
|
|
- else:
|
|
- return self._format_prop(prop, self.properties[prop] or '')
|
|
-
|
|
- return self._format_prop(prop, '')
|
|
-
|
|
- def __setitem__(self, prop, value):
|
|
- if not prop in self.properties:
|
|
- return
|
|
-
|
|
- if isinstance(value, list):
|
|
- value = ','.join(value)
|
|
-
|
|
- if not self.can_modify() and self.properties[prop] != value:
|
|
- # ohoh, this is not can_modify, but it needs to be changed...
|
|
- # make sure it is transfered to the changes file and set all the
|
|
- # fields.
|
|
- # This snippet data container will effectively become the container
|
|
- # for the newly created node, but transparently to whoever uses
|
|
- # it
|
|
- self._override()
|
|
-
|
|
- if self.can_modify() and self.properties[prop].text != value:
|
|
- if self.library():
|
|
- self.library().tainted = True
|
|
-
|
|
- oldvalue = self.properties[prop].text
|
|
- self.properties[prop].text = value
|
|
-
|
|
- if prop == 'tag' or prop == 'accelerator' or prop == 'drop-targets':
|
|
- container = Library().container(self.language())
|
|
- container.prop_changed(self, prop, oldvalue)
|
|
-
|
|
- self.check_validation()
|
|
-
|
|
- def language(self):
|
|
- if self.library and self.library():
|
|
- return self.library().language
|
|
- else:
|
|
- return None
|
|
-
|
|
- def is_override(self):
|
|
- return self.override and Library().overridden[self.override]
|
|
-
|
|
- def to_xml(self):
|
|
- return self._create_xml()
|
|
-
|
|
- def _create_xml(self, parent=None, update=False, attrib={}):
|
|
- # Create a new node
|
|
- if parent != None:
|
|
- element = et.SubElement(parent, 'snippet', attrib)
|
|
- else:
|
|
- element = et.Element('snippet')
|
|
-
|
|
- # Create all the properties
|
|
- for p in self.properties:
|
|
- prop = et.SubElement(element, p)
|
|
- prop.text = self[p]
|
|
-
|
|
- if update:
|
|
- self.properties[p] = prop
|
|
-
|
|
- return element
|
|
-
|
|
- def _override(self):
|
|
- # Find the user file
|
|
- target = Library().get_user_library(self.language())
|
|
-
|
|
- # Create a new node there with override
|
|
- element = self._create_xml(target.root, True, {'override': self.id})
|
|
-
|
|
- # Create an override snippet data, feed it element so that it stores
|
|
- # all the values and then set the node to None so that it only contains
|
|
- # the values in .properties
|
|
- override = SnippetData(element, self.library())
|
|
- override.set_node(None)
|
|
- override.id = self.id
|
|
-
|
|
- # Set our node to the new element
|
|
- self.node = element
|
|
-
|
|
- # Set the override to our id
|
|
- self.override = self.id
|
|
- self.id = None
|
|
-
|
|
- # Set the new library
|
|
- self.set_library(target)
|
|
-
|
|
- # The library is tainted because we added this snippet
|
|
- target.tainted = True
|
|
-
|
|
- # Add the override
|
|
- Library().overridden[self.override] = override
|
|
-
|
|
- def revert(self, snippet):
|
|
- userlib = self.library()
|
|
- self.set_library(snippet.library())
|
|
-
|
|
- userlib.remove(self.node)
|
|
-
|
|
- self.set_node(None)
|
|
-
|
|
- # Copy the properties
|
|
- self.properties = snippet.properties
|
|
-
|
|
- # Set the id
|
|
- self.id = snippet.id
|
|
-
|
|
- # Reset the override flag
|
|
- self.override = None
|
|
+ self.properties[child.tag] = child.text or ''
|
|
+
|
|
+ # Create all the props that were not found so we stay consistent
|
|
+ for prop in props:
|
|
+ if self.can_modify():
|
|
+ child = et.SubElement(node, prop)
|
|
+
|
|
+ child.text = props[prop]
|
|
+ self.properties[prop] = child
|
|
+ else:
|
|
+ self.properties[prop] = props[prop]
|
|
+
|
|
+ self.check_validation()
|
|
+
|
|
+ def check_validation(self):
|
|
+ if not self['tag'] and not self['accelerator'] and not self['drop-targets']:
|
|
+ return False
|
|
+
|
|
+ library = Library()
|
|
+ keyval, mod = Gtk.accelerator_parse(self['accelerator'])
|
|
+
|
|
+ self.valid = library.valid_tab_trigger(self['tag']) and \
|
|
+ (not self['accelerator'] or library.valid_accelerator(keyval, mod))
|
|
+
|
|
+ def _format_prop(self, prop, value):
|
|
+ if prop == 'drop-targets' and value != '':
|
|
+ return re.split('\\s*[,;]\\s*', value)
|
|
+ else:
|
|
+ return value
|
|
+
|
|
+ def __getitem__(self, prop):
|
|
+ if prop in self.properties:
|
|
+ if self.can_modify():
|
|
+ return self._format_prop(prop, self.properties[prop].text or '')
|
|
+ else:
|
|
+ return self._format_prop(prop, self.properties[prop] or '')
|
|
+
|
|
+ return self._format_prop(prop, '')
|
|
+
|
|
+ def __setitem__(self, prop, value):
|
|
+ if not prop in self.properties:
|
|
+ return
|
|
+
|
|
+ if isinstance(value, list):
|
|
+ value = ','.join(value)
|
|
+
|
|
+ if not self.can_modify() and self.properties[prop] != value:
|
|
+ # ohoh, this is not can_modify, but it needs to be changed...
|
|
+ # make sure it is transfered to the changes file and set all the
|
|
+ # fields.
|
|
+ # This snippet data container will effectively become the container
|
|
+ # for the newly created node, but transparently to whoever uses
|
|
+ # it
|
|
+ self._override()
|
|
+
|
|
+ if self.can_modify() and self.properties[prop].text != value:
|
|
+ if self.library():
|
|
+ self.library().tainted = True
|
|
+
|
|
+ oldvalue = self.properties[prop].text
|
|
+ self.properties[prop].text = value
|
|
+
|
|
+ if prop == 'tag' or prop == 'accelerator' or prop == 'drop-targets':
|
|
+ container = Library().container(self.language())
|
|
+ container.prop_changed(self, prop, oldvalue)
|
|
+
|
|
+ self.check_validation()
|
|
+
|
|
+ def language(self):
|
|
+ if self.library and self.library():
|
|
+ return self.library().language
|
|
+ else:
|
|
+ return None
|
|
+
|
|
+ def is_override(self):
|
|
+ return self.override and Library().overridden[self.override]
|
|
+
|
|
+ def to_xml(self):
|
|
+ return self._create_xml()
|
|
+
|
|
+ def _create_xml(self, parent=None, update=False, attrib={}):
|
|
+ # Create a new node
|
|
+ if parent != None:
|
|
+ element = et.SubElement(parent, 'snippet', attrib)
|
|
+ else:
|
|
+ element = et.Element('snippet')
|
|
+
|
|
+ # Create all the properties
|
|
+ for p in self.properties:
|
|
+ prop = et.SubElement(element, p)
|
|
+ prop.text = self[p]
|
|
+
|
|
+ if update:
|
|
+ self.properties[p] = prop
|
|
+
|
|
+ return element
|
|
+
|
|
+ def _override(self):
|
|
+ # Find the user file
|
|
+ target = Library().get_user_library(self.language())
|
|
+
|
|
+ # Create a new node there with override
|
|
+ element = self._create_xml(target.root, True, {'override': self.id})
|
|
+
|
|
+ # Create an override snippet data, feed it element so that it stores
|
|
+ # all the values and then set the node to None so that it only contains
|
|
+ # the values in .properties
|
|
+ override = SnippetData(element, self.library())
|
|
+ override.set_node(None)
|
|
+ override.id = self.id
|
|
+
|
|
+ # Set our node to the new element
|
|
+ self.node = element
|
|
+
|
|
+ # Set the override to our id
|
|
+ self.override = self.id
|
|
+ self.id = None
|
|
+
|
|
+ # Set the new library
|
|
+ self.set_library(target)
|
|
+
|
|
+ # The library is tainted because we added this snippet
|
|
+ target.tainted = True
|
|
+
|
|
+ # Add the override
|
|
+ Library().overridden[self.override] = override
|
|
+
|
|
+ def revert(self, snippet):
|
|
+ userlib = self.library()
|
|
+ self.set_library(snippet.library())
|
|
+
|
|
+ userlib.remove(self.node)
|
|
+
|
|
+ self.set_node(None)
|
|
+
|
|
+ # Copy the properties
|
|
+ self.properties = snippet.properties
|
|
+
|
|
+ # Set the id
|
|
+ self.id = snippet.id
|
|
+
|
|
+ # Reset the override flag
|
|
+ self.override = None
|
|
|
|
class SnippetsTreeBuilder(et.TreeBuilder):
|
|
- def __init__(self, start=None, end=None):
|
|
- et.TreeBuilder.__init__(self)
|
|
- self.set_start(start)
|
|
- self.set_end(end)
|
|
-
|
|
- def set_start(self, start):
|
|
- self._start_cb = start
|
|
-
|
|
- def set_end(self, end):
|
|
- self._end_cb = end
|
|
-
|
|
- def start(self, tag, attrs):
|
|
- result = et.TreeBuilder.start(self, tag, attrs)
|
|
-
|
|
- if self._start_cb:
|
|
- self._start_cb(result)
|
|
-
|
|
- return result
|
|
-
|
|
- def end(self, tag):
|
|
- result = et.TreeBuilder.end(self, tag)
|
|
-
|
|
- if self._end_cb:
|
|
- self._end_cb(result)
|
|
-
|
|
- return result
|
|
+ def __init__(self, start=None, end=None):
|
|
+ et.TreeBuilder.__init__(self)
|
|
+ self.set_start(start)
|
|
+ self.set_end(end)
|
|
+
|
|
+ def set_start(self, start):
|
|
+ self._start_cb = start
|
|
+
|
|
+ def set_end(self, end):
|
|
+ self._end_cb = end
|
|
+
|
|
+ def start(self, tag, attrs):
|
|
+ result = et.TreeBuilder.start(self, tag, attrs)
|
|
+
|
|
+ if self._start_cb:
|
|
+ self._start_cb(result)
|
|
+
|
|
+ return result
|
|
+
|
|
+ def end(self, tag):
|
|
+ result = et.TreeBuilder.end(self, tag)
|
|
+
|
|
+ if self._end_cb:
|
|
+ self._end_cb(result)
|
|
+
|
|
+ return result
|
|
|
|
class LanguageContainer:
|
|
- def __init__(self, language):
|
|
- self.language = language
|
|
- self.snippets = []
|
|
- self.snippets_by_prop = {'tag': {}, 'accelerator': {}, 'drop-targets': {}}
|
|
- self.accel_group = Gtk.AccelGroup()
|
|
- self._refs = 0
|
|
-
|
|
- def _add_prop(self, snippet, prop, value=0):
|
|
- if value == 0:
|
|
- value = snippet[prop]
|
|
-
|
|
- if not value or value == '':
|
|
- return
|
|
-
|
|
- snippets_debug('Added ', prop ,' ', value, ' to ', str(self.language))
|
|
-
|
|
- if prop == 'accelerator':
|
|
- keyval, mod = Gtk.accelerator_parse(value)
|
|
- self.accel_group.connect(keyval, mod, 0, \
|
|
- Library().accelerator_activated)
|
|
-
|
|
- snippets = self.snippets_by_prop[prop]
|
|
-
|
|
- if not isinstance(value, list):
|
|
- value = [value]
|
|
-
|
|
- for val in value:
|
|
- if val in snippets:
|
|
- snippets[val].append(snippet)
|
|
- else:
|
|
- snippets[val] = [snippet]
|
|
-
|
|
- def _remove_prop(self, snippet, prop, value=0):
|
|
- if value == 0:
|
|
- value = snippet[prop]
|
|
-
|
|
- if not value or value == '':
|
|
- return
|
|
-
|
|
- snippets_debug('Removed ', prop, ' ', value, ' from ', str(self.language))
|
|
-
|
|
- if prop == 'accelerator':
|
|
- keyval, mod = Gtk.accelerator_parse(value)
|
|
- self.accel_group.disconnect_key(keyval, mod)
|
|
-
|
|
- snippets = self.snippets_by_prop[prop]
|
|
-
|
|
- if not isinstance(value, list):
|
|
- value = [value]
|
|
-
|
|
- for val in value:
|
|
- try:
|
|
- snippets[val].remove(snippet)
|
|
- except:
|
|
- True
|
|
-
|
|
- def append(self, snippet):
|
|
- tag = snippet['tag']
|
|
- accelerator = snippet['accelerator']
|
|
-
|
|
- self.snippets.append(snippet)
|
|
-
|
|
- self._add_prop(snippet, 'tag')
|
|
- self._add_prop(snippet, 'accelerator')
|
|
- self._add_prop(snippet, 'drop-targets')
|
|
-
|
|
- return snippet
|
|
-
|
|
- def remove(self, snippet):
|
|
- try:
|
|
- self.snippets.remove(snippet)
|
|
- except:
|
|
- True
|
|
-
|
|
- self._remove_prop(snippet, 'tag')
|
|
- self._remove_prop(snippet, 'accelerator')
|
|
- self._remove_prop(snippet, 'drop-targets')
|
|
-
|
|
- def prop_changed(self, snippet, prop, oldvalue):
|
|
- snippets_debug('PROP CHANGED (', prop, ')', oldvalue)
|
|
-
|
|
- self._remove_prop(snippet, prop, oldvalue)
|
|
- self._add_prop(snippet, prop)
|
|
-
|
|
- def from_prop(self, prop, value):
|
|
- snippets = self.snippets_by_prop[prop]
|
|
-
|
|
- if prop == 'drop-targets':
|
|
- s = []
|
|
-
|
|
- # FIXME: change this to use
|
|
- # matevfs.mime_type_get_equivalence when it comes
|
|
- # available
|
|
- for key, val in snippets.items():
|
|
- if not value.startswith(key):
|
|
- continue
|
|
-
|
|
- for snippet in snippets[key]:
|
|
- if not snippet in s:
|
|
- s.append(snippet)
|
|
-
|
|
- return s
|
|
- else:
|
|
- if value in snippets:
|
|
- return snippets[value]
|
|
- else:
|
|
- return []
|
|
-
|
|
- def ref(self):
|
|
- self._refs += 1
|
|
-
|
|
- return True
|
|
-
|
|
- def unref(self):
|
|
- if self._refs > 0:
|
|
- self._refs -= 1
|
|
-
|
|
- return self._refs != 0
|
|
+ def __init__(self, language):
|
|
+ self.language = language
|
|
+ self.snippets = []
|
|
+ self.snippets_by_prop = {'tag': {}, 'accelerator': {}, 'drop-targets': {}}
|
|
+ self.accel_group = Gtk.AccelGroup()
|
|
+ self._refs = 0
|
|
+
|
|
+ def _add_prop(self, snippet, prop, value=0):
|
|
+ if value == 0:
|
|
+ value = snippet[prop]
|
|
+
|
|
+ if not value or value == '':
|
|
+ return
|
|
+
|
|
+ snippets_debug('Added ', prop ,' ', value, ' to ', str(self.language))
|
|
+
|
|
+ if prop == 'accelerator':
|
|
+ keyval, mod = Gtk.accelerator_parse(value)
|
|
+ self.accel_group.connect(keyval, mod, 0, \
|
|
+ Library().accelerator_activated)
|
|
+
|
|
+ snippets = self.snippets_by_prop[prop]
|
|
+
|
|
+ if not isinstance(value, list):
|
|
+ value = [value]
|
|
+
|
|
+ for val in value:
|
|
+ if val in snippets:
|
|
+ snippets[val].append(snippet)
|
|
+ else:
|
|
+ snippets[val] = [snippet]
|
|
+
|
|
+ def _remove_prop(self, snippet, prop, value=0):
|
|
+ if value == 0:
|
|
+ value = snippet[prop]
|
|
+
|
|
+ if not value or value == '':
|
|
+ return
|
|
+
|
|
+ snippets_debug('Removed ', prop, ' ', value, ' from ', str(self.language))
|
|
+
|
|
+ if prop == 'accelerator':
|
|
+ keyval, mod = Gtk.accelerator_parse(value)
|
|
+ self.accel_group.disconnect_key(keyval, mod)
|
|
+
|
|
+ snippets = self.snippets_by_prop[prop]
|
|
+
|
|
+ if not isinstance(value, list):
|
|
+ value = [value]
|
|
+
|
|
+ for val in value:
|
|
+ try:
|
|
+ snippets[val].remove(snippet)
|
|
+ except:
|
|
+ True
|
|
+
|
|
+ def append(self, snippet):
|
|
+ tag = snippet['tag']
|
|
+ accelerator = snippet['accelerator']
|
|
+
|
|
+ self.snippets.append(snippet)
|
|
+
|
|
+ self._add_prop(snippet, 'tag')
|
|
+ self._add_prop(snippet, 'accelerator')
|
|
+ self._add_prop(snippet, 'drop-targets')
|
|
+
|
|
+ return snippet
|
|
+
|
|
+ def remove(self, snippet):
|
|
+ try:
|
|
+ self.snippets.remove(snippet)
|
|
+ except:
|
|
+ True
|
|
+
|
|
+ self._remove_prop(snippet, 'tag')
|
|
+ self._remove_prop(snippet, 'accelerator')
|
|
+ self._remove_prop(snippet, 'drop-targets')
|
|
+
|
|
+ def prop_changed(self, snippet, prop, oldvalue):
|
|
+ snippets_debug('PROP CHANGED (', prop, ')', oldvalue)
|
|
+
|
|
+ self._remove_prop(snippet, prop, oldvalue)
|
|
+ self._add_prop(snippet, prop)
|
|
+
|
|
+ def from_prop(self, prop, value):
|
|
+ snippets = self.snippets_by_prop[prop]
|
|
+
|
|
+ if prop == 'drop-targets':
|
|
+ s = []
|
|
+
|
|
+ # FIXME: change this to use
|
|
+ # matevfs.mime_type_get_equivalence when it comes
|
|
+ # available
|
|
+ for key, val in snippets.items():
|
|
+ if not value.startswith(key):
|
|
+ continue
|
|
+
|
|
+ for snippet in snippets[key]:
|
|
+ if not snippet in s:
|
|
+ s.append(snippet)
|
|
+
|
|
+ return s
|
|
+ else:
|
|
+ if value in snippets:
|
|
+ return snippets[value]
|
|
+ else:
|
|
+ return []
|
|
+
|
|
+ def ref(self):
|
|
+ self._refs += 1
|
|
+
|
|
+ return True
|
|
+
|
|
+ def unref(self):
|
|
+ if self._refs > 0:
|
|
+ self._refs -= 1
|
|
+
|
|
+ return self._refs != 0
|
|
|
|
class SnippetsSystemFile:
|
|
- def __init__(self, path=None):
|
|
- self.path = path
|
|
- self.loaded = False
|
|
- self.language = None
|
|
- self.ok = True
|
|
- self.need_id = True
|
|
-
|
|
- def load_error(self, message):
|
|
- sys.stderr.write("An error occurred loading " + self.path + ":\n")
|
|
- sys.stderr.write(message + "\nSnippets in this file will not be " \
|
|
- "available, please correct or remove the file.\n")
|
|
-
|
|
- def _add_snippet(self, element):
|
|
- if not self.need_id or element.attrib.get('id'):
|
|
- self.loading_elements.append(element)
|
|
-
|
|
- def set_language(self, element):
|
|
- self.language = element.attrib.get('language')
|
|
-
|
|
- if self.language:
|
|
- self.language = self.language.lower()
|
|
-
|
|
- def _set_root(self, element):
|
|
- self.set_language(element)
|
|
-
|
|
- def _preprocess_element(self, element):
|
|
- if not self.loaded:
|
|
- if not element.tag == "snippets":
|
|
- self.load_error("Root element should be `snippets' instead " \
|
|
- "of `%s'" % element.tag)
|
|
- return False
|
|
- else:
|
|
- self._set_root(element)
|
|
- self.loaded = True
|
|
- elif element.tag != 'snippet' and not self.insnippet:
|
|
- self.load_error("Element should be `snippet' instead of `%s'" \
|
|
- % element.tag)
|
|
- return False
|
|
- else:
|
|
- self.insnippet = True
|
|
-
|
|
- return True
|
|
-
|
|
- def _process_element(self, element):
|
|
- if element.tag == 'snippet':
|
|
- self._add_snippet(element)
|
|
- self.insnippet = False
|
|
-
|
|
- return True
|
|
-
|
|
- def ensure(self):
|
|
- if not self.ok or self.loaded:
|
|
- return
|
|
-
|
|
- self.load()
|
|
-
|
|
- def parse_xml(self, readsize=16384):
|
|
- if not self.path:
|
|
- return
|
|
-
|
|
- elements = []
|
|
-
|
|
- builder = SnippetsTreeBuilder( \
|
|
- lambda node: elements.append((node, True)), \
|
|
- lambda node: elements.append((node, False)))
|
|
-
|
|
- parser = et.XMLTreeBuilder(target=builder)
|
|
- self.insnippet = False
|
|
-
|
|
- try:
|
|
- f = open(self.path, "r")
|
|
-
|
|
- while True:
|
|
- data = f.read(readsize)
|
|
-
|
|
- if not data:
|
|
- break
|
|
-
|
|
- parser.feed(data)
|
|
-
|
|
- for element in elements:
|
|
- yield element
|
|
-
|
|
- del elements[:]
|
|
-
|
|
- f.close()
|
|
- except IOError:
|
|
- self.ok = False
|
|
-
|
|
- def load(self):
|
|
- if not self.ok:
|
|
- return
|
|
-
|
|
- snippets_debug("Loading library (" + str(self.language) + "): " + \
|
|
- self.path)
|
|
-
|
|
- self.loaded = False
|
|
- self.ok = False
|
|
- self.loading_elements = []
|
|
-
|
|
- for element in self.parse_xml():
|
|
- if element[1]:
|
|
- if not self._preprocess_element(element[0]):
|
|
- del self.loading_elements[:]
|
|
- return
|
|
- else:
|
|
- if not self._process_element(element[0]):
|
|
- del self.loading_elements[:]
|
|
- return
|
|
-
|
|
- for element in self.loading_elements:
|
|
- snippet = Library().add_snippet(self, element)
|
|
-
|
|
- del self.loading_elements[:]
|
|
- self.ok = True
|
|
-
|
|
- # This function will get the language for a file by just inspecting the
|
|
- # root element of the file. This is provided so that a cache can be built
|
|
- # for which file contains which language.
|
|
- # It returns the name of the language
|
|
- def ensure_language(self):
|
|
- if not self.loaded:
|
|
- self.ok = False
|
|
-
|
|
- for element in self.parse_xml(256):
|
|
- if element[1]:
|
|
- if element[0].tag == 'snippets':
|
|
- self.set_language(element[0])
|
|
- self.ok = True
|
|
-
|
|
- break
|
|
-
|
|
- def unload(self):
|
|
- snippets_debug("Unloading library (" + str(self.language) + "): " + \
|
|
- self.path)
|
|
- self.language = None
|
|
- self.loaded = False
|
|
- self.ok = True
|
|
+ def __init__(self, path=None):
|
|
+ self.path = path
|
|
+ self.loaded = False
|
|
+ self.language = None
|
|
+ self.ok = True
|
|
+ self.need_id = True
|
|
+
|
|
+ def load_error(self, message):
|
|
+ sys.stderr.write("An error occurred loading " + self.path + ":\n")
|
|
+ sys.stderr.write(message + "\nSnippets in this file will not be " \
|
|
+ "available, please correct or remove the file.\n")
|
|
+
|
|
+ def _add_snippet(self, element):
|
|
+ if not self.need_id or element.attrib.get('id'):
|
|
+ self.loading_elements.append(element)
|
|
+
|
|
+ def set_language(self, element):
|
|
+ self.language = element.attrib.get('language')
|
|
+
|
|
+ if self.language:
|
|
+ self.language = self.language.lower()
|
|
+
|
|
+ def _set_root(self, element):
|
|
+ self.set_language(element)
|
|
+
|
|
+ def _preprocess_element(self, element):
|
|
+ if not self.loaded:
|
|
+ if not element.tag == "snippets":
|
|
+ self.load_error("Root element should be `snippets' instead " \
|
|
+ "of `%s'" % element.tag)
|
|
+ return False
|
|
+ else:
|
|
+ self._set_root(element)
|
|
+ self.loaded = True
|
|
+ elif element.tag != 'snippet' and not self.insnippet:
|
|
+ self.load_error("Element should be `snippet' instead of `%s'" \
|
|
+ % element.tag)
|
|
+ return False
|
|
+ else:
|
|
+ self.insnippet = True
|
|
+
|
|
+ return True
|
|
+
|
|
+ def _process_element(self, element):
|
|
+ if element.tag == 'snippet':
|
|
+ self._add_snippet(element)
|
|
+ self.insnippet = False
|
|
+
|
|
+ return True
|
|
+
|
|
+ def ensure(self):
|
|
+ if not self.ok or self.loaded:
|
|
+ return
|
|
+
|
|
+ self.load()
|
|
+
|
|
+ def parse_xml(self, readsize=16384):
|
|
+ if not self.path:
|
|
+ return
|
|
+
|
|
+ elements = []
|
|
+
|
|
+ builder = SnippetsTreeBuilder( \
|
|
+ lambda node: elements.append((node, True)), \
|
|
+ lambda node: elements.append((node, False)))
|
|
+
|
|
+ parser = et.XMLTreeBuilder(target=builder)
|
|
+ self.insnippet = False
|
|
+
|
|
+ try:
|
|
+ f = open(self.path, "r")
|
|
+
|
|
+ while True:
|
|
+ data = f.read(readsize)
|
|
+
|
|
+ if not data:
|
|
+ break
|
|
+
|
|
+ parser.feed(data)
|
|
+
|
|
+ for element in elements:
|
|
+ yield element
|
|
+
|
|
+ del elements[:]
|
|
+
|
|
+ f.close()
|
|
+ except IOError:
|
|
+ self.ok = False
|
|
+
|
|
+ def load(self):
|
|
+ if not self.ok:
|
|
+ return
|
|
+
|
|
+ snippets_debug("Loading library (" + str(self.language) + "): " + \
|
|
+ self.path)
|
|
+
|
|
+ self.loaded = False
|
|
+ self.ok = False
|
|
+ self.loading_elements = []
|
|
+
|
|
+ for element in self.parse_xml():
|
|
+ if element[1]:
|
|
+ if not self._preprocess_element(element[0]):
|
|
+ del self.loading_elements[:]
|
|
+ return
|
|
+ else:
|
|
+ if not self._process_element(element[0]):
|
|
+ del self.loading_elements[:]
|
|
+ return
|
|
+
|
|
+ for element in self.loading_elements:
|
|
+ snippet = Library().add_snippet(self, element)
|
|
+
|
|
+ del self.loading_elements[:]
|
|
+ self.ok = True
|
|
+
|
|
+ # This function will get the language for a file by just inspecting the
|
|
+ # root element of the file. This is provided so that a cache can be built
|
|
+ # for which file contains which language.
|
|
+ # It returns the name of the language
|
|
+ def ensure_language(self):
|
|
+ if not self.loaded:
|
|
+ self.ok = False
|
|
+
|
|
+ for element in self.parse_xml(256):
|
|
+ if element[1]:
|
|
+ if element[0].tag == 'snippets':
|
|
+ self.set_language(element[0])
|
|
+ self.ok = True
|
|
+
|
|
+ break
|
|
+
|
|
+ def unload(self):
|
|
+ snippets_debug("Unloading library (" + str(self.language) + "): " + \
|
|
+ self.path)
|
|
+ self.language = None
|
|
+ self.loaded = False
|
|
+ self.ok = True
|
|
|
|
class SnippetsUserFile(SnippetsSystemFile):
|
|
- def __init__(self, path=None):
|
|
- SnippetsSystemFile.__init__(self, path)
|
|
- self.tainted = False
|
|
- self.need_id = False
|
|
-
|
|
- def _set_root(self, element):
|
|
- SnippetsSystemFile._set_root(self, element)
|
|
- self.root = element
|
|
-
|
|
- def add_prop(self, node, tag, data):
|
|
- if data[tag]:
|
|
- prop = et.SubElement(node, tag)
|
|
- prop.text = data[tag]
|
|
-
|
|
- return prop
|
|
- else:
|
|
- return None
|
|
-
|
|
- def new_snippet(self, properties=None):
|
|
- if (not self.ok) or self.root == None:
|
|
- return None
|
|
-
|
|
- element = et.SubElement(self.root, 'snippet')
|
|
-
|
|
- if properties:
|
|
- for prop in properties:
|
|
- sub = et.SubElement(element, prop)
|
|
- sub.text = properties[prop]
|
|
-
|
|
- self.tainted = True
|
|
-
|
|
- return Library().add_snippet(self, element)
|
|
-
|
|
- def set_language(self, element):
|
|
- SnippetsSystemFile.set_language(self, element)
|
|
-
|
|
- filename = os.path.basename(self.path).lower()
|
|
-
|
|
- if not self.language and filename == "global.xml":
|
|
- self.modifier = True
|
|
- elif self.language and filename == self.language + ".xml":
|
|
- self.modifier = True
|
|
- else:
|
|
- self.modifier = False
|
|
-
|
|
- def create_root(self, language):
|
|
- if self.loaded:
|
|
- snippets_debug('Not creating root, already loaded')
|
|
- return
|
|
-
|
|
- if language:
|
|
- root = et.Element('snippets', {'language': language})
|
|
- self.path = os.path.join(Library().userdir, language.lower() + '.xml')
|
|
- else:
|
|
- root = et.Element('snippets')
|
|
- self.path = os.path.join(Library().userdir, 'global.xml')
|
|
-
|
|
- self._set_root(root)
|
|
- self.loaded = True
|
|
- self.ok = True
|
|
- self.tainted = True
|
|
- self.save()
|
|
-
|
|
- def remove(self, element):
|
|
- try:
|
|
- self.root.remove(element)
|
|
- self.tainted = True
|
|
- except:
|
|
- return
|
|
-
|
|
- try:
|
|
- first = self.root[0]
|
|
- except:
|
|
- # No more elements, this library is useless now
|
|
- Library().remove_library(self)
|
|
-
|
|
- def save(self):
|
|
- if not self.ok or self.root == None or not self.tainted:
|
|
- return
|
|
-
|
|
- path = os.path.dirname(self.path)
|
|
-
|
|
- try:
|
|
- if not os.path.isdir(path):
|
|
- os.makedirs(path, 0755)
|
|
- except OSError:
|
|
- # TODO: this is bad...
|
|
- sys.stderr.write("Error in making dirs\n")
|
|
-
|
|
- try:
|
|
- write_xml(self.root, self.path, ('text', 'accelerator'))
|
|
- self.tainted = False
|
|
- except IOError:
|
|
- # Couldn't save, what to do
|
|
- sys.stderr.write("Could not save user snippets file to " + \
|
|
- self.path + "\n")
|
|
-
|
|
- def unload(self):
|
|
- SnippetsSystemFile.unload(self)
|
|
- self.root = None
|
|
+ def __init__(self, path=None):
|
|
+ SnippetsSystemFile.__init__(self, path)
|
|
+ self.tainted = False
|
|
+ self.need_id = False
|
|
+
|
|
+ def _set_root(self, element):
|
|
+ SnippetsSystemFile._set_root(self, element)
|
|
+ self.root = element
|
|
+
|
|
+ def add_prop(self, node, tag, data):
|
|
+ if data[tag]:
|
|
+ prop = et.SubElement(node, tag)
|
|
+ prop.text = data[tag]
|
|
+
|
|
+ return prop
|
|
+ else:
|
|
+ return None
|
|
+
|
|
+ def new_snippet(self, properties=None):
|
|
+ if (not self.ok) or self.root == None:
|
|
+ return None
|
|
+
|
|
+ element = et.SubElement(self.root, 'snippet')
|
|
+
|
|
+ if properties:
|
|
+ for prop in properties:
|
|
+ sub = et.SubElement(element, prop)
|
|
+ sub.text = properties[prop]
|
|
+
|
|
+ self.tainted = True
|
|
+
|
|
+ return Library().add_snippet(self, element)
|
|
+
|
|
+ def set_language(self, element):
|
|
+ SnippetsSystemFile.set_language(self, element)
|
|
+
|
|
+ filename = os.path.basename(self.path).lower()
|
|
+
|
|
+ if not self.language and filename == "global.xml":
|
|
+ self.modifier = True
|
|
+ elif self.language and filename == self.language + ".xml":
|
|
+ self.modifier = True
|
|
+ else:
|
|
+ self.modifier = False
|
|
+
|
|
+ def create_root(self, language):
|
|
+ if self.loaded:
|
|
+ snippets_debug('Not creating root, already loaded')
|
|
+ return
|
|
+
|
|
+ if language:
|
|
+ root = et.Element('snippets', {'language': language})
|
|
+ self.path = os.path.join(Library().userdir, language.lower() + '.xml')
|
|
+ else:
|
|
+ root = et.Element('snippets')
|
|
+ self.path = os.path.join(Library().userdir, 'global.xml')
|
|
+
|
|
+ self._set_root(root)
|
|
+ self.loaded = True
|
|
+ self.ok = True
|
|
+ self.tainted = True
|
|
+ self.save()
|
|
+
|
|
+ def remove(self, element):
|
|
+ try:
|
|
+ self.root.remove(element)
|
|
+ self.tainted = True
|
|
+ except:
|
|
+ return
|
|
+
|
|
+ try:
|
|
+ first = self.root[0]
|
|
+ except:
|
|
+ # No more elements, this library is useless now
|
|
+ Library().remove_library(self)
|
|
+
|
|
+ def save(self):
|
|
+ if not self.ok or self.root == None or not self.tainted:
|
|
+ return
|
|
+
|
|
+ path = os.path.dirname(self.path)
|
|
+
|
|
+ try:
|
|
+ if not os.path.isdir(path):
|
|
+ os.makedirs(path, 0755)
|
|
+ except OSError:
|
|
+ # TODO: this is bad...
|
|
+ sys.stderr.write("Error in making dirs\n")
|
|
+
|
|
+ try:
|
|
+ write_xml(self.root, self.path, ('text', 'accelerator'))
|
|
+ self.tainted = False
|
|
+ except IOError:
|
|
+ # Couldn't save, what to do
|
|
+ sys.stderr.write("Could not save user snippets file to " + \
|
|
+ self.path + "\n")
|
|
+
|
|
+ def unload(self):
|
|
+ SnippetsSystemFile.unload(self)
|
|
+ self.root = None
|
|
|
|
class Singleton(object):
|
|
- _instance = None
|
|
-
|
|
- def __new__(cls, *args, **kwargs):
|
|
- if not cls._instance:
|
|
- cls._instance = super(Singleton, cls).__new__(
|
|
- cls, *args, **kwargs)
|
|
- cls._instance.__init_once__()
|
|
-
|
|
- return cls._instance
|
|
-
|
|
-class Library(Singleton):
|
|
- def __init_once__(self):
|
|
- self._accelerator_activated_cb = []
|
|
- self.loaded = False
|
|
- self.check_buffer = Gtk.TextBuffer()
|
|
-
|
|
- def set_dirs(self, userdir, systemdirs):
|
|
- self.userdir = userdir
|
|
- self.systemdirs = systemdirs
|
|
-
|
|
- self.libraries = {}
|
|
- self.containers = {}
|
|
- self.overridden = {}
|
|
- self.loaded_ids = []
|
|
-
|
|
- self.loaded = False
|
|
-
|
|
- def add_accelerator_callback(self, cb):
|
|
- self._accelerator_activated_cb.append(cb)
|
|
-
|
|
- def remove_accelerator_callback(self, cb):
|
|
- self._accelerator_activated_cb.remove(cb)
|
|
-
|
|
- def accelerator_activated(self, group, obj, keyval, mod):
|
|
- ret = False
|
|
-
|
|
- for cb in self._accelerator_activated_cb:
|
|
- ret = cb(group, obj, keyval, mod)
|
|
-
|
|
- if ret:
|
|
- break
|
|
-
|
|
- return ret
|
|
-
|
|
- def add_snippet(self, library, element):
|
|
- container = self.container(library.language)
|
|
- overrided = self.overrided(library, element)
|
|
-
|
|
- if overrided:
|
|
- overrided.set_library(library)
|
|
- snippets_debug('Snippet is overriden: ' + overrided['description'])
|
|
- return None
|
|
-
|
|
- snippet = SnippetData(element, library)
|
|
-
|
|
- if snippet.id in self.loaded_ids:
|
|
- snippets_debug('Not added snippet ' + str(library.language) + \
|
|
- '::' + snippet['description'] + ' (duplicate)')
|
|
- return None
|
|
-
|
|
- snippet = container.append(snippet)
|
|
- snippets_debug('Added snippet ' + str(library.language) + '::' + \
|
|
- snippet['description'])
|
|
-
|
|
- if snippet and snippet.override:
|
|
- self.add_override(snippet)
|
|
-
|
|
- if snippet.id:
|
|
- self.loaded_ids.append(snippet.id)
|
|
-
|
|
- return snippet
|
|
-
|
|
- def container(self, language):
|
|
- language = self.normalize_language(language)
|
|
-
|
|
- if not language in self.containers:
|
|
- self.containers[language] = LanguageContainer(language)
|
|
-
|
|
- return self.containers[language]
|
|
-
|
|
- def get_user_library(self, language):
|
|
- target = None
|
|
-
|
|
- if language in self.libraries:
|
|
- for library in self.libraries[language]:
|
|
- if isinstance(library, SnippetsUserFile) and library.modifier:
|
|
- target = library
|
|
- elif not isinstance(library, SnippetsUserFile):
|
|
- break
|
|
-
|
|
- if not target:
|
|
- # Create a new user file then
|
|
- snippets_debug('Creating a new user file for language ' + \
|
|
- str(language))
|
|
- target = SnippetsUserFile()
|
|
- target.create_root(language)
|
|
- self.add_library(target)
|
|
-
|
|
- return target
|
|
-
|
|
- def new_snippet(self, language, properties=None):
|
|
- language = self.normalize_language(language)
|
|
- library = self.get_user_library(language)
|
|
-
|
|
- return library.new_snippet(properties)
|
|
-
|
|
- def revert_snippet(self, snippet):
|
|
- # This will revert the snippet to the one it overrides
|
|
- if not snippet.can_modify() or not snippet.override in self.overridden:
|
|
- # It can't be reverted, shouldn't happen, but oh..
|
|
- return
|
|
-
|
|
- # The snippet in self.overriden only contains the property contents and
|
|
- # the library it belongs to
|
|
- revertto = self.overridden[snippet.override]
|
|
- del self.overridden[snippet.override]
|
|
-
|
|
- if revertto:
|
|
- snippet.revert(revertto)
|
|
-
|
|
- if revertto.id:
|
|
- self.loaded_ids.append(revertto.id)
|
|
-
|
|
- def remove_snippet(self, snippet):
|
|
- if not snippet.can_modify() or snippet.is_override():
|
|
- return
|
|
-
|
|
- # Remove from the library
|
|
- userlib = snippet.library()
|
|
- userlib.remove(snippet.node)
|
|
-
|
|
- # Remove from the container
|
|
- container = self.containers[userlib.language]
|
|
+ _instance = None
|
|
+
|
|
+ def __new__(cls, *args, **kwargs):
|
|
+ if not cls._instance:
|
|
+ cls._instance = super(Singleton, cls).__new__(
|
|
+ cls, *args, **kwargs)
|
|
+ cls._instance.__init_once__()
|
|
+
|
|
+ return cls._instance
|
|
+
|
|
+class Library(Singleton):
|
|
+ def __init_once__(self):
|
|
+ self._accelerator_activated_cb = []
|
|
+ self.loaded = False
|
|
+ self.check_buffer = Gtk.TextBuffer()
|
|
+
|
|
+ def set_dirs(self, userdir, systemdirs):
|
|
+ self.userdir = userdir
|
|
+ self.systemdirs = systemdirs
|
|
+
|
|
+ self.libraries = {}
|
|
+ self.containers = {}
|
|
+ self.overridden = {}
|
|
+ self.loaded_ids = []
|
|
+
|
|
+ self.loaded = False
|
|
+
|
|
+ def add_accelerator_callback(self, cb):
|
|
+ self._accelerator_activated_cb.append(cb)
|
|
+
|
|
+ def remove_accelerator_callback(self, cb):
|
|
+ self._accelerator_activated_cb.remove(cb)
|
|
+
|
|
+ def accelerator_activated(self, group, obj, keyval, mod):
|
|
+ ret = False
|
|
+
|
|
+ for cb in self._accelerator_activated_cb:
|
|
+ ret = cb(group, obj, keyval, mod)
|
|
+
|
|
+ if ret:
|
|
+ break
|
|
+
|
|
+ return ret
|
|
+
|
|
+ def add_snippet(self, library, element):
|
|
+ container = self.container(library.language)
|
|
+ overrided = self.overrided(library, element)
|
|
+
|
|
+ if overrided:
|
|
+ overrided.set_library(library)
|
|
+ snippets_debug('Snippet is overriden: ' + overrided['description'])
|
|
+ return None
|
|
+
|
|
+ snippet = SnippetData(element, library)
|
|
+
|
|
+ if snippet.id in self.loaded_ids:
|
|
+ snippets_debug('Not added snippet ' + str(library.language) + \
|
|
+ '::' + snippet['description'] + ' (duplicate)')
|
|
+ return None
|
|
+
|
|
+ snippet = container.append(snippet)
|
|
+ snippets_debug('Added snippet ' + str(library.language) + '::' + \
|
|
+ snippet['description'])
|
|
+
|
|
+ if snippet and snippet.override:
|
|
+ self.add_override(snippet)
|
|
+
|
|
+ if snippet.id:
|
|
+ self.loaded_ids.append(snippet.id)
|
|
+
|
|
+ return snippet
|
|
+
|
|
+ def container(self, language):
|
|
+ language = self.normalize_language(language)
|
|
+
|
|
+ if not language in self.containers:
|
|
+ self.containers[language] = LanguageContainer(language)
|
|
+
|
|
+ return self.containers[language]
|
|
+
|
|
+ def get_user_library(self, language):
|
|
+ target = None
|
|
+
|
|
+ if language in self.libraries:
|
|
+ for library in self.libraries[language]:
|
|
+ if isinstance(library, SnippetsUserFile) and library.modifier:
|
|
+ target = library
|
|
+ elif not isinstance(library, SnippetsUserFile):
|
|
+ break
|
|
+
|
|
+ if not target:
|
|
+ # Create a new user file then
|
|
+ snippets_debug('Creating a new user file for language ' + \
|
|
+ str(language))
|
|
+ target = SnippetsUserFile()
|
|
+ target.create_root(language)
|
|
+ self.add_library(target)
|
|
+
|
|
+ return target
|
|
+
|
|
+ def new_snippet(self, language, properties=None):
|
|
+ language = self.normalize_language(language)
|
|
+ library = self.get_user_library(language)
|
|
+
|
|
+ return library.new_snippet(properties)
|
|
+
|
|
+ def revert_snippet(self, snippet):
|
|
+ # This will revert the snippet to the one it overrides
|
|
+ if not snippet.can_modify() or not snippet.override in self.overridden:
|
|
+ # It can't be reverted, shouldn't happen, but oh..
|
|
+ return
|
|
+
|
|
+ # The snippet in self.overriden only contains the property contents and
|
|
+ # the library it belongs to
|
|
+ revertto = self.overridden[snippet.override]
|
|
+ del self.overridden[snippet.override]
|
|
+
|
|
+ if revertto:
|
|
+ snippet.revert(revertto)
|
|
+
|
|
+ if revertto.id:
|
|
+ self.loaded_ids.append(revertto.id)
|
|
+
|
|
+ def remove_snippet(self, snippet):
|
|
+ if not snippet.can_modify() or snippet.is_override():
|
|
+ return
|
|
+
|
|
+ # Remove from the library
|
|
+ userlib = snippet.library()
|
|
+ userlib.remove(snippet.node)
|
|
+
|
|
+ # Remove from the container
|
|
+ container = self.containers[userlib.language]
|
|
+ container.remove(snippet)
|
|
+
|
|
+ def overrided(self, library, element):
|
|
+ id = NamespacedId(library.language, element.attrib.get('id')).id
|
|
+
|
|
+ if id in self.overridden:
|
|
+ snippet = SnippetData(element, None)
|
|
+ snippet.set_node(None)
|
|
+
|
|
+ self.overridden[id] = snippet
|
|
+ return snippet
|
|
+ else:
|
|
+ return None
|
|
+
|
|
+ def add_override(self, snippet):
|
|
+ snippets_debug('Add override:', snippet.override)
|
|
+ if not snippet.override in self.overridden:
|
|
+ self.overridden[snippet.override] = None
|
|
+
|
|
+ def add_library(self, library):
|
|
+ library.ensure_language()
|
|
+
|
|
+ if not library.ok:
|
|
+ snippets_debug('Library in wrong format, ignoring')
|
|
+ return False
|
|
+
|
|
+ snippets_debug('Adding library (' + str(library.language) + '): ' + \
|
|
+ library.path)
|
|
+
|
|
+ if library.language in self.libraries:
|
|
+ # Make sure all the user files are before the system files
|
|
+ if isinstance(library, SnippetsUserFile):
|
|
+ self.libraries[library.language].insert(0, library)
|
|
+ else:
|
|
+ self.libraries[library.language].append(library)
|
|
+ else:
|
|
+ self.libraries[library.language] = [library]
|
|
+
|
|
+ return True
|
|
+
|
|
+ def remove_library(self, library):
|
|
+ if not library.ok:
|
|
+ return
|
|
+
|
|
+ if library.path and os.path.isfile(library.path):
|
|
+ os.unlink(library.path)
|
|
+
|
|
+ try:
|
|
+ self.libraries[library.language].remove(library)
|
|
+ except KeyError:
|
|
+ True
|
|
+
|
|
+ container = self.containers[library.language]
|
|
+
|
|
+ for snippet in list(container.snippets):
|
|
+ if snippet.library() == library:
|
|
container.remove(snippet)
|
|
-
|
|
- def overrided(self, library, element):
|
|
- id = NamespacedId(library.language, element.attrib.get('id')).id
|
|
-
|
|
- if id in self.overridden:
|
|
- snippet = SnippetData(element, None)
|
|
- snippet.set_node(None)
|
|
-
|
|
- self.overridden[id] = snippet
|
|
- return snippet
|
|
- else:
|
|
- return None
|
|
-
|
|
- def add_override(self, snippet):
|
|
- snippets_debug('Add override:', snippet.override)
|
|
- if not snippet.override in self.overridden:
|
|
- self.overridden[snippet.override] = None
|
|
-
|
|
- def add_library(self, library):
|
|
- library.ensure_language()
|
|
-
|
|
- if not library.ok:
|
|
- snippets_debug('Library in wrong format, ignoring')
|
|
- return False
|
|
-
|
|
- snippets_debug('Adding library (' + str(library.language) + '): ' + \
|
|
- library.path)
|
|
-
|
|
- if library.language in self.libraries:
|
|
- # Make sure all the user files are before the system files
|
|
- if isinstance(library, SnippetsUserFile):
|
|
- self.libraries[library.language].insert(0, library)
|
|
- else:
|
|
- self.libraries[library.language].append(library)
|
|
+
|
|
+ def add_user_library(self, path):
|
|
+ library = SnippetsUserFile(path)
|
|
+ return self.add_library(library)
|
|
+
|
|
+ def add_system_library(self, path):
|
|
+ library = SnippetsSystemFile(path)
|
|
+ return self.add_library(library)
|
|
+
|
|
+ def find_libraries(self, path, searched, addcb):
|
|
+ snippets_debug("Finding in: " + path)
|
|
+
|
|
+ if not os.path.isdir(path):
|
|
+ return searched
|
|
+
|
|
+ files = os.listdir(path)
|
|
+ searched.append(path)
|
|
+
|
|
+ for f in files:
|
|
+ f = os.path.realpath(os.path.join(path, f))
|
|
+
|
|
+ # Determine what language this file provides snippets for
|
|
+ if os.path.isfile(f):
|
|
+ addcb(f)
|
|
+
|
|
+ return searched
|
|
+
|
|
+ def normalize_language(self, language):
|
|
+ if language:
|
|
+ return language.lower()
|
|
+
|
|
+ return language
|
|
+
|
|
+ def remove_container(self, language):
|
|
+ for snippet in self.containers[language].snippets:
|
|
+ if snippet.id in self.loaded_ids:
|
|
+ self.loaded_ids.remove(snippet.id)
|
|
+
|
|
+ if snippet.override in self.overridden:
|
|
+ del self.overridden[snippet.override]
|
|
+
|
|
+ del self.containers[language]
|
|
+
|
|
+ def get_accel_group(self, language):
|
|
+ language = self.normalize_language(language)
|
|
+ container = self.container(language)
|
|
+
|
|
+ self.ensure(language)
|
|
+ return container.accel_group
|
|
+
|
|
+ def save(self, language):
|
|
+ language = self.normalize_language(language)
|
|
+
|
|
+ if language in self.libraries:
|
|
+ for library in self.libraries[language]:
|
|
+ if isinstance(library, SnippetsUserFile):
|
|
+ library.save()
|
|
else:
|
|
- self.libraries[library.language] = [library]
|
|
-
|
|
- return True
|
|
-
|
|
- def remove_library(self, library):
|
|
- if not library.ok:
|
|
- return
|
|
-
|
|
- if library.path and os.path.isfile(library.path):
|
|
- os.unlink(library.path)
|
|
-
|
|
- try:
|
|
- self.libraries[library.language].remove(library)
|
|
- except KeyError:
|
|
- True
|
|
-
|
|
- container = self.containers[library.language]
|
|
-
|
|
- for snippet in list(container.snippets):
|
|
- if snippet.library() == library:
|
|
- container.remove(snippet)
|
|
-
|
|
- def add_user_library(self, path):
|
|
- library = SnippetsUserFile(path)
|
|
- return self.add_library(library)
|
|
-
|
|
- def add_system_library(self, path):
|
|
- library = SnippetsSystemFile(path)
|
|
- return self.add_library(library)
|
|
-
|
|
- def find_libraries(self, path, searched, addcb):
|
|
- snippets_debug("Finding in: " + path)
|
|
-
|
|
- if not os.path.isdir(path):
|
|
- return searched
|
|
-
|
|
- files = os.listdir(path)
|
|
- searched.append(path)
|
|
-
|
|
- for f in files:
|
|
- f = os.path.realpath(os.path.join(path, f))
|
|
-
|
|
- # Determine what language this file provides snippets for
|
|
- if os.path.isfile(f):
|
|
- addcb(f)
|
|
-
|
|
- return searched
|
|
-
|
|
- def normalize_language(self, language):
|
|
- if language:
|
|
- return language.lower()
|
|
-
|
|
- return language
|
|
-
|
|
- def remove_container(self, language):
|
|
- for snippet in self.containers[language].snippets:
|
|
- if snippet.id in self.loaded_ids:
|
|
- self.loaded_ids.remove(snippet.id)
|
|
-
|
|
- if snippet.override in self.overridden:
|
|
- del self.overridden[snippet.override]
|
|
-
|
|
- del self.containers[language]
|
|
-
|
|
- def get_accel_group(self, language):
|
|
- language = self.normalize_language(language)
|
|
- container = self.container(language)
|
|
-
|
|
- self.ensure(language)
|
|
- return container.accel_group
|
|
-
|
|
- def save(self, language):
|
|
- language = self.normalize_language(language)
|
|
-
|
|
- if language in self.libraries:
|
|
- for library in self.libraries[language]:
|
|
- if isinstance(library, SnippetsUserFile):
|
|
- library.save()
|
|
- else:
|
|
- break
|
|
-
|
|
- def ref(self, language):
|
|
- language = self.normalize_language(language)
|
|
-
|
|
- snippets_debug('Ref:', language)
|
|
- self.container(language).ref()
|
|
-
|
|
- def unref(self, language):
|
|
- language = self.normalize_language(language)
|
|
-
|
|
- snippets_debug('Unref:', language)
|
|
-
|
|
- if language in self.containers:
|
|
- if not self.containers[language].unref() and \
|
|
- language in self.libraries:
|
|
-
|
|
- for library in self.libraries[language]:
|
|
- library.unload()
|
|
-
|
|
- self.remove_container(language)
|
|
-
|
|
- def ensure(self, language):
|
|
- self.ensure_files()
|
|
- language = self.normalize_language(language)
|
|
-
|
|
- # Ensure language as well as the global snippets (None)
|
|
- for lang in (None, language):
|
|
- if lang in self.libraries:
|
|
- # Ensure the container exists
|
|
- self.container(lang)
|
|
-
|
|
- for library in self.libraries[lang]:
|
|
- library.ensure()
|
|
-
|
|
- def ensure_files(self):
|
|
- if self.loaded:
|
|
- return
|
|
-
|
|
- searched = []
|
|
- searched = self.find_libraries(self.userdir, searched, \
|
|
- self.add_user_library)
|
|
-
|
|
- for d in self.systemdirs:
|
|
- searched = self.find_libraries(d, searched, \
|
|
- self.add_system_library)
|
|
+ break
|
|
|
|
- self.loaded = True
|
|
+ def ref(self, language):
|
|
+ language = self.normalize_language(language)
|
|
+
|
|
+ snippets_debug('Ref:', language)
|
|
+ self.container(language).ref()
|
|
+
|
|
+ def unref(self, language):
|
|
+ language = self.normalize_language(language)
|
|
+
|
|
+ snippets_debug('Unref:', language)
|
|
+
|
|
+ if language in self.containers:
|
|
+ if not self.containers[language].unref() and \
|
|
+ language in self.libraries:
|
|
+
|
|
+ for library in self.libraries[language]:
|
|
+ library.unload()
|
|
+
|
|
+ self.remove_container(language)
|
|
+
|
|
+ def ensure(self, language):
|
|
+ self.ensure_files()
|
|
+ language = self.normalize_language(language)
|
|
+
|
|
+ # Ensure language as well as the global snippets (None)
|
|
+ for lang in (None, language):
|
|
+ if lang in self.libraries:
|
|
+ # Ensure the container exists
|
|
+ self.container(lang)
|
|
+
|
|
+ for library in self.libraries[lang]:
|
|
+ library.ensure()
|
|
+
|
|
+ def ensure_files(self):
|
|
+ if self.loaded:
|
|
+ return
|
|
+
|
|
+ searched = []
|
|
+ searched = self.find_libraries(self.userdir, searched, \
|
|
+ self.add_user_library)
|
|
+
|
|
+ for d in self.systemdirs:
|
|
+ searched = self.find_libraries(d, searched, \
|
|
+ self.add_system_library)
|
|
+
|
|
+ self.loaded = True
|
|
+
|
|
+ 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)))
|
|
+
|
|
+ def valid_tab_trigger(self, trigger):
|
|
+ if not trigger:
|
|
+ return True
|
|
+
|
|
+ if trigger.isdigit():
|
|
+ return False
|
|
+
|
|
+ self.check_buffer.set_text(trigger)
|
|
+
|
|
+ start, end = self.check_buffer.get_bounds()
|
|
+ text = self.check_buffer.get_text(start, end, False)
|
|
+
|
|
+ s = start.copy()
|
|
+ e = end.copy()
|
|
+
|
|
+ end.backward_word_start()
|
|
+ start.forward_word_end()
|
|
+
|
|
+ return (s.equal(end) and e.equal(start)) or (len(text) == 1 and not (text.isalnum() or text.isspace()))
|
|
+
|
|
+ # Snippet getters
|
|
+ # ===============
|
|
+ def _from_prop(self, prop, value, language=None):
|
|
+ self.ensure_files()
|
|
+
|
|
+ result = []
|
|
+ language = self.normalize_language(language)
|
|
+
|
|
+ if not language in self.containers:
|
|
+ return []
|
|
+
|
|
+ self.ensure(language)
|
|
+ result = self.containers[language].from_prop(prop, value)
|
|
+
|
|
+ if len(result) == 0 and language and None in self.containers:
|
|
+ result = self.containers[None].from_prop(prop, value)
|
|
+
|
|
+ return result
|
|
+
|
|
+ # Get snippets for a given language
|
|
+ def get_snippets(self, language=None):
|
|
+ self.ensure_files()
|
|
+ language = self.normalize_language(language)
|
|
+
|
|
+ if not language in self.libraries:
|
|
+ return []
|
|
+
|
|
+ snippets = []
|
|
+ self.ensure(language)
|
|
+
|
|
+ return list(self.containers[language].snippets)
|
|
+
|
|
+ # Get snippets for a given accelerator
|
|
+ def from_accelerator(self, accelerator, language=None):
|
|
+ return self._from_prop('accelerator', accelerator, language)
|
|
+
|
|
+ # Get snippets for a given tag
|
|
+ def from_tag(self, tag, language=None):
|
|
+ return self._from_prop('tag', tag, language)
|
|
+
|
|
+ # Get snippets for a given drop target
|
|
+ def from_drop_target(self, drop_target, language=None):
|
|
+ return self._from_prop('drop-targets', drop_target, language)
|
|
|
|
- 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)))
|
|
-
|
|
- def valid_tab_trigger(self, trigger):
|
|
- if not trigger:
|
|
- return True
|
|
-
|
|
- if trigger.isdigit():
|
|
- return False
|
|
-
|
|
- self.check_buffer.set_text(trigger)
|
|
-
|
|
- start, end = self.check_buffer.get_bounds()
|
|
- text = self.check_buffer.get_text(start, end, False)
|
|
-
|
|
- s = start.copy()
|
|
- e = end.copy()
|
|
-
|
|
- end.backward_word_start()
|
|
- start.forward_word_end()
|
|
-
|
|
- return (s.equal(end) and e.equal(start)) or (len(text) == 1 and not (text.isalnum() or text.isspace()))
|
|
-
|
|
- # Snippet getters
|
|
- # ===============
|
|
- def _from_prop(self, prop, value, language=None):
|
|
- self.ensure_files()
|
|
-
|
|
- result = []
|
|
- language = self.normalize_language(language)
|
|
-
|
|
- if not language in self.containers:
|
|
- return []
|
|
-
|
|
- self.ensure(language)
|
|
- result = self.containers[language].from_prop(prop, value)
|
|
-
|
|
- if len(result) == 0 and language and None in self.containers:
|
|
- result = self.containers[None].from_prop(prop, value)
|
|
-
|
|
- return result
|
|
-
|
|
- # Get snippets for a given language
|
|
- def get_snippets(self, language=None):
|
|
- self.ensure_files()
|
|
- language = self.normalize_language(language)
|
|
-
|
|
- if not language in self.libraries:
|
|
- return []
|
|
-
|
|
- snippets = []
|
|
- self.ensure(language)
|
|
-
|
|
- return list(self.containers[language].snippets)
|
|
-
|
|
- # Get snippets for a given accelerator
|
|
- def from_accelerator(self, accelerator, language=None):
|
|
- return self._from_prop('accelerator', accelerator, language)
|
|
-
|
|
- # Get snippets for a given tag
|
|
- def from_tag(self, tag, language=None):
|
|
- return self._from_prop('tag', tag, language)
|
|
-
|
|
- # Get snippets for a given drop target
|
|
- def from_drop_target(self, drop_target, language=None):
|
|
- return self._from_prop('drop-targets', drop_target, language)
|
|
-
|
|
-# ex:ts=8:et:
|
|
+# ex:ts=4:et:
|
|
diff --git a/plugins/snippets/snippets/Manager.py b/plugins/snippets/snippets/Manager.py
|
|
old mode 100755
|
|
new mode 100644
|
|
index f496b1c..9760fa7
|
|
--- a/plugins/snippets/snippets/Manager.py
|
|
+++ b/plugins/snippets/snippets/Manager.py
|
|
@@ -30,1119 +30,1119 @@ from Document import Document
|
|
from LanguageManager import get_language_manager
|
|
|
|
class Manager:
|
|
- NAME_COLUMN = 0
|
|
- SORT_COLUMN = 1
|
|
- LANG_COLUMN = 2
|
|
- SNIPPET_COLUMN = 3
|
|
- TARGET_URI = 105
|
|
-
|
|
- model = None
|
|
- drag_icons = ('mate-mime-application-x-tarz', 'mate-package', 'package')
|
|
- default_export_name = _('Snippets archive') + '.tar.gz'
|
|
- dragging = False
|
|
- dnd_target_list = [Gtk.TargetEntry.new('text/uri-list', 0, TARGET_URI)]
|
|
-
|
|
- def __init__(self, datadir):
|
|
- self.datadir = datadir
|
|
- self.snippet = None
|
|
- self.dlg = None
|
|
- self._temp_export = None
|
|
- self.snippets_doc = None
|
|
- self.manager = None
|
|
- self.default_size = None
|
|
-
|
|
- self.key_press_id = 0
|
|
- self.run()
|
|
-
|
|
- def get_language_snippets(self, path, name = None):
|
|
- library = Library()
|
|
-
|
|
- name = self.get_language(path)
|
|
- nodes = library.get_snippets(name)
|
|
-
|
|
- return nodes
|
|
-
|
|
- def add_new_snippet_node(self, parent):
|
|
- return self.model.append(parent, ('<i>' + _('Add a new snippet...') + \
|
|
- '</i>', '', None, None))
|
|
-
|
|
- def fill_language(self, piter, expand=True):
|
|
- # Remove all children
|
|
- child = self.model.iter_children(piter)
|
|
-
|
|
- while child and self.model.remove(child):
|
|
- True
|
|
-
|
|
- path = self.model.get_path(piter)
|
|
- nodes = self.get_language_snippets(path)
|
|
- language = self.get_language(path)
|
|
-
|
|
- Library().ref(language)
|
|
-
|
|
- if nodes:
|
|
- for node in nodes:
|
|
- self.add_snippet(piter, node)
|
|
- else:
|
|
- # Add node that tells there are no snippets currently
|
|
- self.add_new_snippet_node(piter)
|
|
+ NAME_COLUMN = 0
|
|
+ SORT_COLUMN = 1
|
|
+ LANG_COLUMN = 2
|
|
+ SNIPPET_COLUMN = 3
|
|
+ TARGET_URI = 105
|
|
+
|
|
+ model = None
|
|
+ drag_icons = ('mate-mime-application-x-tarz', 'mate-package', 'package')
|
|
+ default_export_name = _('Snippets archive') + '.tar.gz'
|
|
+ dragging = False
|
|
+ dnd_target_list = [Gtk.TargetEntry.new('text/uri-list', 0, TARGET_URI)]
|
|
+
|
|
+ def __init__(self, datadir):
|
|
+ self.datadir = datadir
|
|
+ self.snippet = None
|
|
+ self.dlg = None
|
|
+ self._temp_export = None
|
|
+ self.snippets_doc = None
|
|
+ self.manager = None
|
|
+ self.default_size = None
|
|
+
|
|
+ self.key_press_id = 0
|
|
+ self.run()
|
|
+
|
|
+ def get_language_snippets(self, path, name = None):
|
|
+ library = Library()
|
|
+
|
|
+ name = self.get_language(path)
|
|
+ nodes = library.get_snippets(name)
|
|
+
|
|
+ return nodes
|
|
+
|
|
+ def add_new_snippet_node(self, parent):
|
|
+ return self.model.append(parent, ('<i>' + _('Add a new snippet...') + \
|
|
+ '</i>', '', None, None))
|
|
+
|
|
+ def fill_language(self, piter, expand=True):
|
|
+ # Remove all children
|
|
+ child = self.model.iter_children(piter)
|
|
+
|
|
+ while child and self.model.remove(child):
|
|
+ True
|
|
+
|
|
+ path = self.model.get_path(piter)
|
|
+ nodes = self.get_language_snippets(path)
|
|
+ language = self.get_language(path)
|
|
+
|
|
+ Library().ref(language)
|
|
+
|
|
+ if nodes:
|
|
+ for node in nodes:
|
|
+ self.add_snippet(piter, node)
|
|
+ else:
|
|
+ # Add node that tells there are no snippets currently
|
|
+ self.add_new_snippet_node(piter)
|
|
+
|
|
+ if expand:
|
|
+ self.tree_view.expand_row(path, False)
|
|
+
|
|
+ def build_model(self, force_reload = False):
|
|
+ window = Pluma.App.get_default().get_active_window()
|
|
+
|
|
+ if window:
|
|
+ view = window.get_active_view()
|
|
+
|
|
+ if not view:
|
|
+ current_lang = None
|
|
+ else:
|
|
+ current_lang = view.get_buffer().get_language()
|
|
+ source_view = self['source_view_snippet']
|
|
|
|
- if expand:
|
|
- self.tree_view.expand_row(path, False)
|
|
+ else:
|
|
+ current_lang = None
|
|
|
|
- def build_model(self, force_reload = False):
|
|
- window = Pluma.App.get_default().get_active_window()
|
|
-
|
|
- if window:
|
|
- view = window.get_active_view()
|
|
+ tree_view = self['tree_view_snippets']
|
|
+ expand = None
|
|
|
|
- if not view:
|
|
- current_lang = None
|
|
- else:
|
|
- current_lang = view.get_buffer().get_language()
|
|
- source_view = self['source_view_snippet']
|
|
+ if not self.model or force_reload:
|
|
+ self.model = Gtk.TreeStore(str, str, GObject.Object, object)
|
|
+ self.model.set_sort_column_id(self.SORT_COLUMN, Gtk.SortType.ASCENDING)
|
|
+ manager = get_language_manager()
|
|
+ langs = [manager.get_language(x) for x in manager.get_language_ids()]
|
|
+ langs.sort(key=lambda x: x.get_name())
|
|
|
|
- else:
|
|
- current_lang = None
|
|
-
|
|
- tree_view = self['tree_view_snippets']
|
|
- expand = None
|
|
-
|
|
- if not self.model or force_reload:
|
|
- self.model = Gtk.TreeStore(str, str, GObject.Object, object)
|
|
- self.model.set_sort_column_id(self.SORT_COLUMN, Gtk.SortType.ASCENDING)
|
|
- manager = get_language_manager()
|
|
- langs = [manager.get_language(x) for x in manager.get_language_ids()]
|
|
- langs.sort(key=lambda x: x.get_name())
|
|
-
|
|
- piter = self.model.append(None, (_('Global'), '', None, None))
|
|
- # Add dummy node
|
|
- self.model.append(piter, ('', '', None, None))
|
|
-
|
|
- nm = None
|
|
-
|
|
- if current_lang:
|
|
- nm = current_lang.get_name()
|
|
-
|
|
- for lang in langs:
|
|
- name = lang.get_name()
|
|
- parent = self.model.append(None, (name, name, lang, None))
|
|
-
|
|
- # Add dummy node
|
|
- self.model.append(parent, ('', '', None, None))
|
|
-
|
|
- if (nm == name):
|
|
- expand = parent
|
|
- else:
|
|
- if current_lang:
|
|
- piter = self.model.get_iter_first()
|
|
- nm = current_lang.get_name()
|
|
-
|
|
- while piter:
|
|
- lang = self.model.get_value(piter, \
|
|
- self.SORT_COLUMN)
|
|
-
|
|
- if lang == nm:
|
|
- expand = piter
|
|
- break;
|
|
-
|
|
- piter = self.model.iter_next(piter)
|
|
-
|
|
- tree_view.set_model(self.model)
|
|
-
|
|
- if not expand:
|
|
- expand = self.model.get_iter_first()
|
|
-
|
|
- tree_view.expand_row(self.model.get_path(expand), False)
|
|
- self.select_iter(expand)
|
|
-
|
|
- def get_cell_data_pixbuf_cb(self, column, cell, model, iter, data):
|
|
- snippet = model.get_value(iter, self.SNIPPET_COLUMN)
|
|
-
|
|
- if snippet and not snippet.valid:
|
|
- cell.set_property('stock-id', Gtk.STOCK_DIALOG_ERROR)
|
|
- else:
|
|
- cell.set_property('stock-id', None)
|
|
-
|
|
- cell.set_property('xalign', 1.0)
|
|
-
|
|
- def get_cell_data_cb(self, column, cell, model, iter, data):
|
|
- snippet = model.get_value(iter, self.SNIPPET_COLUMN)
|
|
-
|
|
- cell.set_property('editable', snippet != None)
|
|
- cell.set_property('markup', model.get_value(iter, self.NAME_COLUMN))
|
|
-
|
|
- def on_tree_view_drag_data_get(self, widget, context, selection_data, info, time):
|
|
- gfile = Gio.file_new_for_path(self._temp_export)
|
|
- selection_data.set_uris([gfile.get_uri()])
|
|
-
|
|
- def on_tree_view_drag_begin(self, widget, context):
|
|
- self.dragging = True
|
|
-
|
|
- if self._temp_export:
|
|
- shutil.rmtree(os.path.dirname(self._temp_export))
|
|
- self._temp_export = None
|
|
-
|
|
- if self.dnd_name:
|
|
- Gtk.drag_set_icon_name(context, self.dnd_name, 0, 0)
|
|
-
|
|
- dirname = tempfile.mkdtemp()
|
|
- filename = os.path.join(dirname, self.default_export_name)
|
|
-
|
|
- # Generate temporary file name
|
|
- self.export_snippets(filename, False)
|
|
- self._temp_export = filename
|
|
-
|
|
- def on_tree_view_drag_end(self, widget, context):
|
|
- self.dragging = False
|
|
-
|
|
- def on_tree_view_drag_data_received(self, widget, context, x, y, selection, info, timestamp):
|
|
- uris = selection.get_uris()
|
|
-
|
|
- self.import_snippets(uris)
|
|
-
|
|
- def on_tree_view_drag_motion(self, widget, context, x, y, timestamp):
|
|
- # Return False if we are dragging
|
|
- if self.dragging:
|
|
- return False
|
|
-
|
|
- # Check uri target
|
|
- if not Gtk.targets_include_uri(context.targets):
|
|
- return False
|
|
-
|
|
- # Check action
|
|
- action = None
|
|
- if context.suggested_action == Gdk.DragAction.COPY:
|
|
- action = Gdk.DragAction.COPY
|
|
- else:
|
|
- for act in context.actions:
|
|
- if act == Gdk.DragAction.COPY:
|
|
- action = Gdk.DragAction.COPY
|
|
- break
|
|
-
|
|
- if action == Gdk.DragAction.COPY:
|
|
- context.drag_status(Gdk.DragAction.COPY, timestamp)
|
|
- return True
|
|
- else:
|
|
- return False
|
|
-
|
|
- def build_dnd(self):
|
|
- tv = self.tree_view
|
|
-
|
|
- # Set it as a drag source for exporting snippets
|
|
- tv.drag_source_set(Gdk.ModifierType.BUTTON1_MASK, self.dnd_target_list, Gdk.DragAction.DEFAULT | Gdk.DragAction.COPY)
|
|
-
|
|
- # Set it as a drag destination for importing snippets
|
|
- tv.drag_dest_set(Gtk.DestDefaults.HIGHLIGHT | Gtk.DestDefaults.DROP,
|
|
- self.dnd_target_list, Gdk.DragAction.DEFAULT | Gdk.DragAction.COPY)
|
|
-
|
|
- tv.connect('drag_data_get', self.on_tree_view_drag_data_get)
|
|
- tv.connect('drag_begin', self.on_tree_view_drag_begin)
|
|
- tv.connect('drag_end', self.on_tree_view_drag_end)
|
|
- tv.connect('drag_data_received', self.on_tree_view_drag_data_received)
|
|
- tv.connect('drag_motion', self.on_tree_view_drag_motion)
|
|
-
|
|
- theme = Gtk.IconTheme.get_for_screen(tv.get_screen())
|
|
-
|
|
- self.dnd_name = None
|
|
- for name in self.drag_icons:
|
|
- icon = theme.lookup_icon(name, Gtk.IconSize.DND, 0)
|
|
-
|
|
- if icon:
|
|
- self.dnd_name = name
|
|
- break
|
|
-
|
|
- def build_tree_view(self):
|
|
- self.tree_view = self['tree_view_snippets']
|
|
-
|
|
- self.column = Gtk.TreeViewColumn(None)
|
|
-
|
|
- self.renderer = Gtk.CellRendererText()
|
|
- self.column.pack_start(self.renderer, False)
|
|
- self.column.set_cell_data_func(self.renderer, self.get_cell_data_cb, None)
|
|
-
|
|
- renderer = Gtk.CellRendererPixbuf()
|
|
- self.column.pack_start(renderer, True)
|
|
- self.column.set_cell_data_func(renderer, self.get_cell_data_pixbuf_cb, None)
|
|
-
|
|
- self.tree_view.append_column(self.column)
|
|
-
|
|
- self.renderer.connect('edited', self.on_cell_edited)
|
|
- self.renderer.connect('editing-started', self.on_cell_editing_started)
|
|
-
|
|
- selection = self.tree_view.get_selection()
|
|
- selection.set_mode(Gtk.SelectionMode.MULTIPLE)
|
|
- selection.connect('changed', self.on_tree_view_selection_changed)
|
|
-
|
|
- self.build_dnd()
|
|
-
|
|
- def build(self):
|
|
- self.builder = Gtk.Builder()
|
|
- self.builder.add_from_file(os.path.join(self.datadir, 'ui', 'snippets.ui'))
|
|
-
|
|
- handlers_dic = {
|
|
- 'on_dialog_snippets_response': self.on_dialog_snippets_response,
|
|
- 'on_dialog_snippets_destroy': self.on_dialog_snippets_destroy,
|
|
- 'on_button_new_snippet_clicked': self.on_button_new_snippet_clicked,
|
|
- 'on_button_import_snippets_clicked': self.on_button_import_snippets_clicked,
|
|
- 'on_button_export_snippets_clicked': self.on_button_export_snippets_clicked,
|
|
- 'on_button_remove_snippet_clicked': self.on_button_remove_snippet_clicked,
|
|
- 'on_entry_tab_trigger_focus_out': self.on_entry_tab_trigger_focus_out,
|
|
- 'on_entry_tab_trigger_changed': self.on_entry_tab_trigger_changed,
|
|
- 'on_entry_accelerator_focus_out': self.on_entry_accelerator_focus_out,
|
|
- 'on_entry_accelerator_focus_in': self.on_entry_accelerator_focus_in,
|
|
- 'on_entry_accelerator_key_press': self.on_entry_accelerator_key_press,
|
|
- 'on_source_view_snippet_focus_out': self.on_source_view_snippet_focus_out,
|
|
- 'on_tree_view_snippets_row_expanded': self.on_tree_view_snippets_row_expanded,
|
|
- 'on_tree_view_snippets_key_press': self.on_tree_view_snippets_key_press}
|
|
-
|
|
- self.builder.connect_signals(handlers_dic)
|
|
-
|
|
- self.build_tree_view()
|
|
- self.build_model()
|
|
-
|
|
- image = self['image_remove']
|
|
- image.set_from_stock(Gtk.STOCK_REMOVE, Gtk.IconSize.SMALL_TOOLBAR)
|
|
+ piter = self.model.append(None, (_('Global'), '', None, None))
|
|
+ # Add dummy node
|
|
+ self.model.append(piter, ('', '', None, None))
|
|
|
|
- source_view = self['source_view_snippet']
|
|
- manager = get_language_manager()
|
|
- lang = manager.get_language('snippets')
|
|
-
|
|
- if lang:
|
|
- source_view.get_buffer().set_highlight_syntax(True)
|
|
- source_view.get_buffer().set_language(lang)
|
|
- self.snippets_doc = Document(None, source_view)
|
|
-
|
|
- combo = self['combo_drop_targets']
|
|
-
|
|
- entry = combo.get_child()
|
|
- entry.connect('focus-out-event', self.on_entry_drop_targets_focus_out)
|
|
- entry.connect('drag-data-received', self.on_entry_drop_targets_drag_data_received)
|
|
-
|
|
- lst = entry.drag_dest_get_target_list()
|
|
- lst.add_uri_targets(self.TARGET_URI)
|
|
-
|
|
- self.dlg = self['dialog_snippets']
|
|
-
|
|
- if self.default_size:
|
|
- self.dlg.set_default_size(*self.default_size)
|
|
-
|
|
- def __getitem__(self, key):
|
|
- return self.builder.get_object(key)
|
|
-
|
|
- def is_filled(self, piter):
|
|
- if not self.model.iter_has_child(piter):
|
|
- return True
|
|
-
|
|
- child = self.model.iter_children(piter)
|
|
- nm = self.model.get_value(child, self.NAME_COLUMN)
|
|
- lang = self.model.get_value(child, self.LANG_COLUMN)
|
|
- snippet = self.model.get_value(child, self.SNIPPET_COLUMN)
|
|
-
|
|
- return (lang or snippet or nm)
|
|
-
|
|
- def fill_if_needed(self, piter, expand=True):
|
|
- if not self.is_filled(piter):
|
|
- self.fill_language(piter, expand)
|
|
-
|
|
- def find_iter(self, parent, snippet):
|
|
- self.fill_if_needed(parent)
|
|
- piter = self.model.iter_children(parent)
|
|
-
|
|
- while (piter):
|
|
- node = self.model.get_value(piter, self.SNIPPET_COLUMN)
|
|
-
|
|
- if node == snippet.data:
|
|
- return piter
|
|
-
|
|
- piter = self.model.iter_next(piter)
|
|
-
|
|
- return None
|
|
-
|
|
- def selected_snippets_state(self):
|
|
- snippets = self.selected_snippets(False)
|
|
- override = False
|
|
- remove = False
|
|
- system = False
|
|
-
|
|
- for snippet in snippets:
|
|
- if not snippet:
|
|
- continue
|
|
-
|
|
- if snippet.is_override():
|
|
- override = True
|
|
- elif snippet.can_modify():
|
|
- remove = True
|
|
- else:
|
|
- system = True
|
|
-
|
|
- # No need to continue if both are found
|
|
- if override and remove:
|
|
- break
|
|
-
|
|
- return (override, remove, system)
|
|
-
|
|
- def update_buttons(self):
|
|
- button_remove = self['button_remove_snippet']
|
|
- button_new = self['button_new_snippet']
|
|
- image_remove = self['image_remove']
|
|
-
|
|
- button_new.set_sensitive(self.language_path != None)
|
|
- override, remove, system = self.selected_snippets_state()
|
|
-
|
|
- if not (override ^ remove) or system:
|
|
- button_remove.set_sensitive(False)
|
|
- image_remove.set_from_stock(Gtk.STOCK_DELETE, Gtk.IconSize.BUTTON)
|
|
- else:
|
|
- button_remove.set_sensitive(True)
|
|
-
|
|
- if override:
|
|
- image_remove.set_from_stock(Gtk.STOCK_UNDO, Gtk.IconSize.BUTTON)
|
|
- tooltip = _('Revert selected snippet')
|
|
- else:
|
|
- image_remove.set_from_stock(Gtk.STOCK_DELETE, Gtk.IconSize.BUTTON)
|
|
- tooltip = _('Delete selected snippet')
|
|
-
|
|
- button_remove.set_tooltip_text(tooltip)
|
|
-
|
|
- def snippet_changed(self, piter = None):
|
|
- if piter:
|
|
- node = self.model.get_value(piter, self.SNIPPET_COLUMN)
|
|
- s = Snippet(node)
|
|
- else:
|
|
- s = self.snippet
|
|
- piter = self.find_iter(self.model.get_iter(self.language_path), s)
|
|
+ nm = None
|
|
+
|
|
+ if current_lang:
|
|
+ nm = current_lang.get_name()
|
|
+
|
|
+ for lang in langs:
|
|
+ name = lang.get_name()
|
|
+ parent = self.model.append(None, (name, name, lang, None))
|
|
+
|
|
+ # Add dummy node
|
|
+ self.model.append(parent, ('', '', None, None))
|
|
+
|
|
+ if (nm == name):
|
|
+ expand = parent
|
|
+ else:
|
|
+ if current_lang:
|
|
+ piter = self.model.get_iter_first()
|
|
+ nm = current_lang.get_name()
|
|
+
|
|
+ while piter:
|
|
+ lang = self.model.get_value(piter, \
|
|
+ self.SORT_COLUMN)
|
|
+
|
|
+ if lang == nm:
|
|
+ expand = piter
|
|
+ break;
|
|
+
|
|
+ piter = self.model.iter_next(piter)
|
|
+
|
|
+ tree_view.set_model(self.model)
|
|
+
|
|
+ if not expand:
|
|
+ expand = self.model.get_iter_first()
|
|
+
|
|
+ tree_view.expand_row(self.model.get_path(expand), False)
|
|
+ self.select_iter(expand)
|
|
+
|
|
+ def get_cell_data_pixbuf_cb(self, column, cell, model, iter, data):
|
|
+ snippet = model.get_value(iter, self.SNIPPET_COLUMN)
|
|
+
|
|
+ if snippet and not snippet.valid:
|
|
+ cell.set_property('stock-id', Gtk.STOCK_DIALOG_ERROR)
|
|
+ else:
|
|
+ cell.set_property('stock-id', None)
|
|
+
|
|
+ cell.set_property('xalign', 1.0)
|
|
+
|
|
+ def get_cell_data_cb(self, column, cell, model, iter, data):
|
|
+ snippet = model.get_value(iter, self.SNIPPET_COLUMN)
|
|
+
|
|
+ cell.set_property('editable', snippet != None)
|
|
+ cell.set_property('markup', model.get_value(iter, self.NAME_COLUMN))
|
|
+
|
|
+ def on_tree_view_drag_data_get(self, widget, context, selection_data, info, time):
|
|
+ gfile = Gio.file_new_for_path(self._temp_export)
|
|
+ selection_data.set_uris([gfile.get_uri()])
|
|
+
|
|
+ def on_tree_view_drag_begin(self, widget, context):
|
|
+ self.dragging = True
|
|
+
|
|
+ if self._temp_export:
|
|
+ shutil.rmtree(os.path.dirname(self._temp_export))
|
|
+ self._temp_export = None
|
|
+
|
|
+ if self.dnd_name:
|
|
+ Gtk.drag_set_icon_name(context, self.dnd_name, 0, 0)
|
|
+
|
|
+ dirname = tempfile.mkdtemp()
|
|
+ filename = os.path.join(dirname, self.default_export_name)
|
|
+
|
|
+ # Generate temporary file name
|
|
+ self.export_snippets(filename, False)
|
|
+ self._temp_export = filename
|
|
+
|
|
+ def on_tree_view_drag_end(self, widget, context):
|
|
+ self.dragging = False
|
|
+
|
|
+ def on_tree_view_drag_data_received(self, widget, context, x, y, selection, info, timestamp):
|
|
+ uris = selection.get_uris()
|
|
+
|
|
+ self.import_snippets(uris)
|
|
+
|
|
+ def on_tree_view_drag_motion(self, widget, context, x, y, timestamp):
|
|
+ # Return False if we are dragging
|
|
+ if self.dragging:
|
|
+ return False
|
|
+
|
|
+ # Check uri target
|
|
+ if not Gtk.targets_include_uri(context.targets):
|
|
+ return False
|
|
+
|
|
+ # Check action
|
|
+ action = None
|
|
+ if context.suggested_action == Gdk.DragAction.COPY:
|
|
+ action = Gdk.DragAction.COPY
|
|
+ else:
|
|
+ for act in context.actions:
|
|
+ if act == Gdk.DragAction.COPY:
|
|
+ action = Gdk.DragAction.COPY
|
|
+ break
|
|
+
|
|
+ if action == Gdk.DragAction.COPY:
|
|
+ context.drag_status(Gdk.DragAction.COPY, timestamp)
|
|
+ return True
|
|
+ else:
|
|
+ return False
|
|
+
|
|
+ def build_dnd(self):
|
|
+ tv = self.tree_view
|
|
+
|
|
+ # Set it as a drag source for exporting snippets
|
|
+ tv.drag_source_set(Gdk.ModifierType.BUTTON1_MASK, self.dnd_target_list, Gdk.DragAction.DEFAULT | Gdk.DragAction.COPY)
|
|
+
|
|
+ # Set it as a drag destination for importing snippets
|
|
+ tv.drag_dest_set(Gtk.DestDefaults.HIGHLIGHT | Gtk.DestDefaults.DROP,
|
|
+ self.dnd_target_list, Gdk.DragAction.DEFAULT | Gdk.DragAction.COPY)
|
|
+
|
|
+ tv.connect('drag_data_get', self.on_tree_view_drag_data_get)
|
|
+ tv.connect('drag_begin', self.on_tree_view_drag_begin)
|
|
+ tv.connect('drag_end', self.on_tree_view_drag_end)
|
|
+ tv.connect('drag_data_received', self.on_tree_view_drag_data_received)
|
|
+ tv.connect('drag_motion', self.on_tree_view_drag_motion)
|
|
+
|
|
+ theme = Gtk.IconTheme.get_for_screen(tv.get_screen())
|
|
+
|
|
+ self.dnd_name = None
|
|
+ for name in self.drag_icons:
|
|
+ icon = theme.lookup_icon(name, Gtk.IconSize.DND, 0)
|
|
+
|
|
+ if icon:
|
|
+ self.dnd_name = name
|
|
+ break
|
|
+
|
|
+ def build_tree_view(self):
|
|
+ self.tree_view = self['tree_view_snippets']
|
|
+
|
|
+ self.column = Gtk.TreeViewColumn(None)
|
|
+
|
|
+ self.renderer = Gtk.CellRendererText()
|
|
+ self.column.pack_start(self.renderer, False)
|
|
+ self.column.set_cell_data_func(self.renderer, self.get_cell_data_cb, None)
|
|
+
|
|
+ renderer = Gtk.CellRendererPixbuf()
|
|
+ self.column.pack_start(renderer, True)
|
|
+ self.column.set_cell_data_func(renderer, self.get_cell_data_pixbuf_cb, None)
|
|
+
|
|
+ self.tree_view.append_column(self.column)
|
|
+
|
|
+ self.renderer.connect('edited', self.on_cell_edited)
|
|
+ self.renderer.connect('editing-started', self.on_cell_editing_started)
|
|
+
|
|
+ selection = self.tree_view.get_selection()
|
|
+ selection.set_mode(Gtk.SelectionMode.MULTIPLE)
|
|
+ selection.connect('changed', self.on_tree_view_selection_changed)
|
|
+
|
|
+ self.build_dnd()
|
|
+
|
|
+ def build(self):
|
|
+ self.builder = Gtk.Builder()
|
|
+ self.builder.add_from_file(os.path.join(self.datadir, 'ui', 'snippets.ui'))
|
|
+
|
|
+ handlers_dic = {
|
|
+ 'on_dialog_snippets_response': self.on_dialog_snippets_response,
|
|
+ 'on_dialog_snippets_destroy': self.on_dialog_snippets_destroy,
|
|
+ 'on_button_new_snippet_clicked': self.on_button_new_snippet_clicked,
|
|
+ 'on_button_import_snippets_clicked': self.on_button_import_snippets_clicked,
|
|
+ 'on_button_export_snippets_clicked': self.on_button_export_snippets_clicked,
|
|
+ 'on_button_remove_snippet_clicked': self.on_button_remove_snippet_clicked,
|
|
+ 'on_entry_tab_trigger_focus_out': self.on_entry_tab_trigger_focus_out,
|
|
+ 'on_entry_tab_trigger_changed': self.on_entry_tab_trigger_changed,
|
|
+ 'on_entry_accelerator_focus_out': self.on_entry_accelerator_focus_out,
|
|
+ 'on_entry_accelerator_focus_in': self.on_entry_accelerator_focus_in,
|
|
+ 'on_entry_accelerator_key_press': self.on_entry_accelerator_key_press,
|
|
+ 'on_source_view_snippet_focus_out': self.on_source_view_snippet_focus_out,
|
|
+ 'on_tree_view_snippets_row_expanded': self.on_tree_view_snippets_row_expanded,
|
|
+ 'on_tree_view_snippets_key_press': self.on_tree_view_snippets_key_press}
|
|
|
|
- if piter:
|
|
- nm = s.display()
|
|
-
|
|
- self.model.set_value(piter, self.NAME_COLUMN, nm)
|
|
- self.model.set_value(piter, self.SORT_COLUMN, nm)
|
|
- self.update_buttons()
|
|
- self.entry_tab_trigger_update_valid()
|
|
+ self.builder.connect_signals(handlers_dic)
|
|
|
|
+ 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')
|
|
+
|
|
+ if lang:
|
|
+ source_view.get_buffer().set_highlight_syntax(True)
|
|
+ source_view.get_buffer().set_language(lang)
|
|
+ self.snippets_doc = Document(None, source_view)
|
|
+
|
|
+ combo = self['combo_drop_targets']
|
|
+
|
|
+ entry = combo.get_child()
|
|
+ entry.connect('focus-out-event', self.on_entry_drop_targets_focus_out)
|
|
+ entry.connect('drag-data-received', self.on_entry_drop_targets_drag_data_received)
|
|
+
|
|
+ lst = entry.drag_dest_get_target_list()
|
|
+ lst.add_uri_targets(self.TARGET_URI)
|
|
+
|
|
+ self.dlg = self['dialog_snippets']
|
|
+
|
|
+ if self.default_size:
|
|
+ self.dlg.set_default_size(*self.default_size)
|
|
+
|
|
+ def __getitem__(self, key):
|
|
+ return self.builder.get_object(key)
|
|
+
|
|
+ def is_filled(self, piter):
|
|
+ if not self.model.iter_has_child(piter):
|
|
+ return True
|
|
+
|
|
+ child = self.model.iter_children(piter)
|
|
+ nm = self.model.get_value(child, self.NAME_COLUMN)
|
|
+ lang = self.model.get_value(child, self.LANG_COLUMN)
|
|
+ snippet = self.model.get_value(child, self.SNIPPET_COLUMN)
|
|
+
|
|
+ return (lang or snippet or nm)
|
|
+
|
|
+ def fill_if_needed(self, piter, expand=True):
|
|
+ if not self.is_filled(piter):
|
|
+ self.fill_language(piter, expand)
|
|
+
|
|
+ def find_iter(self, parent, snippet):
|
|
+ self.fill_if_needed(parent)
|
|
+ piter = self.model.iter_children(parent)
|
|
+
|
|
+ while (piter):
|
|
+ node = self.model.get_value(piter, self.SNIPPET_COLUMN)
|
|
+
|
|
+ if node == snippet.data:
|
|
return piter
|
|
|
|
- def add_snippet(self, parent, snippet):
|
|
- piter = self.model.append(parent, ('', '', None, snippet))
|
|
-
|
|
- return self.snippet_changed(piter)
|
|
+ piter = self.model.iter_next(piter)
|
|
|
|
- def run(self):
|
|
- if not self.dlg:
|
|
- self.build()
|
|
- self.dlg.show()
|
|
+ return None
|
|
+
|
|
+ def selected_snippets_state(self):
|
|
+ snippets = self.selected_snippets(False)
|
|
+ override = False
|
|
+ remove = False
|
|
+ system = False
|
|
+
|
|
+ for snippet in snippets:
|
|
+ if not snippet:
|
|
+ continue
|
|
+
|
|
+ if snippet.is_override():
|
|
+ override = True
|
|
+ elif snippet.can_modify():
|
|
+ remove = True
|
|
+ else:
|
|
+ system = True
|
|
+
|
|
+ # No need to continue if both are found
|
|
+ if override and remove:
|
|
+ break
|
|
+
|
|
+ return (override, remove, system)
|
|
+
|
|
+ def update_buttons(self):
|
|
+ button_remove = self['button_remove_snippet']
|
|
+ button_new = self['button_new_snippet']
|
|
+ image_remove = self['image_remove']
|
|
+
|
|
+ button_new.set_sensitive(self.language_path != None)
|
|
+ override, remove, system = self.selected_snippets_state()
|
|
+
|
|
+ if not (override ^ remove) or system:
|
|
+ button_remove.set_sensitive(False)
|
|
+ image_remove.set_from_stock(Gtk.STOCK_DELETE, Gtk.IconSize.BUTTON)
|
|
+ else:
|
|
+ button_remove.set_sensitive(True)
|
|
+
|
|
+ if override:
|
|
+ image_remove.set_from_stock(Gtk.STOCK_UNDO, Gtk.IconSize.BUTTON)
|
|
+ tooltip = _('Revert selected snippet')
|
|
+ else:
|
|
+ image_remove.set_from_stock(Gtk.STOCK_DELETE, Gtk.IconSize.BUTTON)
|
|
+ tooltip = _('Delete selected snippet')
|
|
+
|
|
+ button_remove.set_tooltip_text(tooltip)
|
|
+
|
|
+ def snippet_changed(self, piter = None):
|
|
+ if piter:
|
|
+ node = self.model.get_value(piter, self.SNIPPET_COLUMN)
|
|
+ s = Snippet(node)
|
|
+ else:
|
|
+ s = self.snippet
|
|
+ piter = self.find_iter(self.model.get_iter(self.language_path), s)
|
|
+
|
|
+ if piter:
|
|
+ nm = s.display()
|
|
+
|
|
+ self.model.set_value(piter, self.NAME_COLUMN, nm)
|
|
+ self.model.set_value(piter, self.SORT_COLUMN, nm)
|
|
+ self.update_buttons()
|
|
+ self.entry_tab_trigger_update_valid()
|
|
+
|
|
+ return piter
|
|
+
|
|
+ def add_snippet(self, parent, snippet):
|
|
+ piter = self.model.append(parent, ('', '', None, snippet))
|
|
+
|
|
+ return self.snippet_changed(piter)
|
|
+
|
|
+ def run(self):
|
|
+ if not self.dlg:
|
|
+ self.build()
|
|
+ self.dlg.show()
|
|
+ else:
|
|
+ self.build_model()
|
|
+ self.dlg.present()
|
|
+
|
|
+ def snippet_from_iter(self, model, piter):
|
|
+ parent = model.iter_parent(piter)
|
|
+
|
|
+ if parent:
|
|
+ return model.get_value(piter, self.SNIPPET_COLUMN)
|
|
+ else:
|
|
+ return None
|
|
+
|
|
+ def language_snippets(self, model, parent, as_path=False):
|
|
+ self.fill_if_needed(parent, False)
|
|
+ piter = model.iter_children(parent)
|
|
+ snippets = []
|
|
+
|
|
+ if not piter:
|
|
+ return snippets
|
|
+
|
|
+ while piter:
|
|
+ snippet = self.snippet_from_iter(model, piter)
|
|
+
|
|
+ if snippet:
|
|
+ if as_path:
|
|
+ snippets.append(model.get_path(piter))
|
|
else:
|
|
- self.build_model()
|
|
- self.dlg.present()
|
|
+ snippets.append(snippet)
|
|
+
|
|
+ piter = model.iter_next(piter)
|
|
+
|
|
+ return snippets
|
|
|
|
- def snippet_from_iter(self, model, piter):
|
|
+ def selected_snippets(self, include_languages=True, as_path=False):
|
|
+ selection = self.tree_view.get_selection()
|
|
+ (model, paths) = selection.get_selected_rows()
|
|
+ snippets = []
|
|
+
|
|
+ if paths and len(paths) != 0:
|
|
+ for p in paths:
|
|
+ piter = model.get_iter(p)
|
|
parent = model.iter_parent(piter)
|
|
-
|
|
- if parent:
|
|
- return model.get_value(piter, self.SNIPPET_COLUMN)
|
|
- else:
|
|
- return None
|
|
-
|
|
- def language_snippets(self, model, parent, as_path=False):
|
|
- self.fill_if_needed(parent, False)
|
|
- piter = model.iter_children(parent)
|
|
- snippets = []
|
|
-
|
|
+
|
|
if not piter:
|
|
- return snippets
|
|
-
|
|
- while piter:
|
|
- snippet = self.snippet_from_iter(model, piter)
|
|
-
|
|
- if snippet:
|
|
- if as_path:
|
|
- snippets.append(model.get_path(piter))
|
|
- else:
|
|
- snippets.append(snippet)
|
|
-
|
|
- piter = model.iter_next(piter)
|
|
-
|
|
- return snippets
|
|
-
|
|
- def selected_snippets(self, include_languages=True, as_path=False):
|
|
- selection = self.tree_view.get_selection()
|
|
- (model, paths) = selection.get_selected_rows()
|
|
- snippets = []
|
|
-
|
|
- if paths and len(paths) != 0:
|
|
- for p in paths:
|
|
- piter = model.get_iter(p)
|
|
- parent = model.iter_parent(piter)
|
|
-
|
|
- if not piter:
|
|
- continue
|
|
-
|
|
- if parent:
|
|
- snippet = self.snippet_from_iter(model, piter)
|
|
-
|
|
- if not snippet:
|
|
- continue
|
|
-
|
|
- if as_path:
|
|
- snippets.append(p)
|
|
- else:
|
|
- snippets.append(snippet)
|
|
- elif include_languages:
|
|
- snippets += self.language_snippets(model, piter, as_path)
|
|
-
|
|
- return snippets
|
|
-
|
|
- def selected_snippet(self):
|
|
- selection = self.tree_view.get_selection()
|
|
- (model, paths) = selection.get_selected_rows()
|
|
-
|
|
- if len(paths) == 1:
|
|
- piter = model.get_iter(paths[0])
|
|
- parent = model.iter_parent(piter)
|
|
- snippet = self.snippet_from_iter(model, piter)
|
|
-
|
|
- return parent, piter, snippet
|
|
- else:
|
|
- return None, None, None
|
|
+ continue
|
|
|
|
- def selection_changed(self):
|
|
- if not self.snippet:
|
|
- sens = False
|
|
+ if parent:
|
|
+ snippet = self.snippet_from_iter(model, piter)
|
|
|
|
- self['entry_tab_trigger'].set_text('')
|
|
- self['entry_accelerator'].set_text('')
|
|
- buf = self['source_view_snippet'].get_buffer()
|
|
- buf.begin_not_undoable_action()
|
|
- buf.set_text('')
|
|
- buf.end_not_undoable_action()
|
|
- self['combo_drop_targets'].get_child().set_text('')
|
|
+ if not snippet:
|
|
+ continue
|
|
|
|
+ if as_path:
|
|
+ snippets.append(p)
|
|
+ else:
|
|
+ snippets.append(snippet)
|
|
+ elif include_languages:
|
|
+ snippets += self.language_snippets(model, piter, as_path)
|
|
+
|
|
+ return snippets
|
|
+
|
|
+ def selected_snippet(self):
|
|
+ selection = self.tree_view.get_selection()
|
|
+ (model, paths) = selection.get_selected_rows()
|
|
+
|
|
+ if len(paths) == 1:
|
|
+ piter = model.get_iter(paths[0])
|
|
+ parent = model.iter_parent(piter)
|
|
+ snippet = self.snippet_from_iter(model, piter)
|
|
+
|
|
+ return parent, piter, snippet
|
|
+ else:
|
|
+ return None, None, None
|
|
+
|
|
+ def selection_changed(self):
|
|
+ if not self.snippet:
|
|
+ sens = False
|
|
+
|
|
+ self['entry_tab_trigger'].set_text('')
|
|
+ self['entry_accelerator'].set_text('')
|
|
+ buf = self['source_view_snippet'].get_buffer()
|
|
+ buf.begin_not_undoable_action()
|
|
+ buf.set_text('')
|
|
+ buf.end_not_undoable_action()
|
|
+ self['combo_drop_targets'].get_child().set_text('')
|
|
+
|
|
+ else:
|
|
+ sens = True
|
|
+
|
|
+ self['entry_tab_trigger'].set_text(self.snippet['tag'])
|
|
+ self['entry_accelerator'].set_text( \
|
|
+ self.snippet.accelerator_display())
|
|
+ self['combo_drop_targets'].get_child().set_text(', '.join(self.snippet['drop-targets']))
|
|
+
|
|
+ buf = self['source_view_snippet'].get_buffer()
|
|
+ buf.begin_not_undoable_action()
|
|
+ buf.set_text(self.snippet['text'])
|
|
+ buf.end_not_undoable_action()
|
|
+
|
|
+
|
|
+ for name in ['source_view_snippet', 'label_tab_trigger',
|
|
+ 'entry_tab_trigger', 'label_accelerator',
|
|
+ 'entry_accelerator', 'label_drop_targets',
|
|
+ 'combo_drop_targets']:
|
|
+ self[name].set_sensitive(sens)
|
|
+
|
|
+ self.update_buttons()
|
|
+
|
|
+ def select_iter(self, piter, unselect=True):
|
|
+ selection = self.tree_view.get_selection()
|
|
+
|
|
+ if unselect:
|
|
+ selection.unselect_all()
|
|
+
|
|
+ selection.select_iter(piter)
|
|
+
|
|
+ self.tree_view.scroll_to_cell(self.model.get_path(piter), None, \
|
|
+ True, 0.5, 0.5)
|
|
+
|
|
+ def get_language(self, path):
|
|
+ if path.get_indices()[0] == 0:
|
|
+ return None
|
|
+ else:
|
|
+ return self.model.get_value(self.model.get_iter(path), \
|
|
+ self.LANG_COLUMN).get_id()
|
|
+
|
|
+ def new_snippet(self, properties=None):
|
|
+ if not self.language_path:
|
|
+ return None
|
|
+
|
|
+ snippet = Library().new_snippet(self.get_language(self.language_path), properties)
|
|
+
|
|
+ return Snippet(snippet)
|
|
+
|
|
+ def get_dummy(self, parent):
|
|
+ if not self.model.iter_n_children(parent) == 1:
|
|
+ return None
|
|
+
|
|
+ dummy = self.model.iter_children(parent)
|
|
+
|
|
+ if not self.model.get_value(dummy, self.SNIPPET_COLUMN):
|
|
+ return dummy
|
|
+
|
|
+ return None
|
|
+
|
|
+ def unref_languages(self):
|
|
+ piter = self.model.get_iter_first()
|
|
+ library = Library()
|
|
+
|
|
+ while piter:
|
|
+ if self.is_filled(piter):
|
|
+ language = self.get_language(self.model.get_path(piter))
|
|
+ library.save(language)
|
|
+
|
|
+ library.unref(language)
|
|
+
|
|
+ piter = self.model.iter_next(piter)
|
|
+
|
|
+ # Callbacks
|
|
+ def on_dialog_snippets_destroy(self, dlg):
|
|
+ # Remove temporary drag export
|
|
+ if self._temp_export:
|
|
+ shutil.rmtree(os.path.dirname(self._temp_export))
|
|
+ self._temp_export = None
|
|
+
|
|
+ if self.snippets_doc:
|
|
+ self.snippets_doc.stop()
|
|
+
|
|
+ self.manager = None
|
|
+ self.unref_languages()
|
|
+ self.snippet = None
|
|
+ self.model = None
|
|
+ self.dlg = None
|
|
+
|
|
+ def on_dialog_snippets_response(self, dlg, resp):
|
|
+
|
|
+ alloc = dlg.get_allocation()
|
|
+ self.default_size = [alloc.width, alloc.height]
|
|
+
|
|
+ if resp == Gtk.ResponseType.HELP:
|
|
+ Pluma.help_display(self, 'pluma', 'pluma-snippets-plugin')
|
|
+ return
|
|
+
|
|
+ self.dlg.destroy()
|
|
+
|
|
+ def on_cell_editing_started(self, renderer, editable, path):
|
|
+ piter = self.model.get_iter(path)
|
|
+
|
|
+ if not self.model.iter_parent(piter):
|
|
+ renderer.stop_editing(True)
|
|
+ editable.remove_widget()
|
|
+ elif isinstance(editable, Gtk.Entry):
|
|
+ if self.snippet:
|
|
+ editable.set_text(self.snippet['description'])
|
|
+ else:
|
|
+ # This is the `Add a new snippet...` item
|
|
+ editable.set_text('')
|
|
+
|
|
+ editable.grab_focus()
|
|
+
|
|
+ def on_cell_edited(self, cell, path, new_text):
|
|
+ if new_text != '':
|
|
+ piter = self.model.get_iter(path)
|
|
+ node = self.model.get_value(piter, self.SNIPPET_COLUMN)
|
|
+
|
|
+ if node:
|
|
+ if node == self.snippet.data:
|
|
+ s = self.snippet
|
|
else:
|
|
- sens = True
|
|
-
|
|
- self['entry_tab_trigger'].set_text(self.snippet['tag'])
|
|
- self['entry_accelerator'].set_text( \
|
|
- self.snippet.accelerator_display())
|
|
- self['combo_drop_targets'].get_child().set_text(', '.join(self.snippet['drop-targets']))
|
|
-
|
|
- buf = self['source_view_snippet'].get_buffer()
|
|
- buf.begin_not_undoable_action()
|
|
- buf.set_text(self.snippet['text'])
|
|
- buf.end_not_undoable_action()
|
|
-
|
|
-
|
|
- for name in ['source_view_snippet', 'label_tab_trigger',
|
|
- 'entry_tab_trigger', 'label_accelerator',
|
|
- 'entry_accelerator', 'label_drop_targets',
|
|
- 'combo_drop_targets']:
|
|
- self[name].set_sensitive(sens)
|
|
-
|
|
- self.update_buttons()
|
|
-
|
|
- def select_iter(self, piter, unselect=True):
|
|
- selection = self.tree_view.get_selection()
|
|
-
|
|
- if unselect:
|
|
- selection.unselect_all()
|
|
-
|
|
- selection.select_iter(piter)
|
|
-
|
|
- self.tree_view.scroll_to_cell(self.model.get_path(piter), None, \
|
|
- True, 0.5, 0.5)
|
|
-
|
|
- def get_language(self, path):
|
|
- if path.get_indices()[0] == 0:
|
|
- return None
|
|
- else:
|
|
- return self.model.get_value(self.model.get_iter(path), \
|
|
- self.LANG_COLUMN).get_id()
|
|
-
|
|
- def new_snippet(self, properties=None):
|
|
- if not self.language_path:
|
|
- return None
|
|
-
|
|
- snippet = Library().new_snippet(self.get_language(self.language_path), properties)
|
|
-
|
|
- return Snippet(snippet)
|
|
-
|
|
- def get_dummy(self, parent):
|
|
- if not self.model.iter_n_children(parent) == 1:
|
|
- return None
|
|
-
|
|
- dummy = self.model.iter_children(parent)
|
|
-
|
|
- if not self.model.get_value(dummy, self.SNIPPET_COLUMN):
|
|
- return dummy
|
|
-
|
|
- return None
|
|
-
|
|
- def unref_languages(self):
|
|
- piter = self.model.get_iter_first()
|
|
- library = Library()
|
|
-
|
|
- while piter:
|
|
- if self.is_filled(piter):
|
|
- language = self.get_language(self.model.get_path(piter))
|
|
- library.save(language)
|
|
-
|
|
- library.unref(language)
|
|
-
|
|
- piter = self.model.iter_next(piter)
|
|
-
|
|
- # Callbacks
|
|
- def on_dialog_snippets_destroy(self, dlg):
|
|
- # Remove temporary drag export
|
|
- if self._temp_export:
|
|
- shutil.rmtree(os.path.dirname(self._temp_export))
|
|
- self._temp_export = None
|
|
-
|
|
- if self.snippets_doc:
|
|
- self.snippets_doc.stop()
|
|
-
|
|
- self.manager = None
|
|
- self.unref_languages()
|
|
- self.snippet = None
|
|
- self.model = None
|
|
- self.dlg = None
|
|
-
|
|
- def on_dialog_snippets_response(self, dlg, resp):
|
|
-
|
|
- alloc = dlg.get_allocation()
|
|
- self.default_size = [alloc.width, alloc.height]
|
|
-
|
|
- if resp == Gtk.ResponseType.HELP:
|
|
- Pluma.help_display(self, 'pluma', 'pluma-snippets-plugin')
|
|
- return
|
|
-
|
|
- self.dlg.destroy()
|
|
-
|
|
- def on_cell_editing_started(self, renderer, editable, path):
|
|
- piter = self.model.get_iter(path)
|
|
-
|
|
- if not self.model.iter_parent(piter):
|
|
- renderer.stop_editing(True)
|
|
- editable.remove_widget()
|
|
- elif isinstance(editable, Gtk.Entry):
|
|
- if self.snippet:
|
|
- editable.set_text(self.snippet['description'])
|
|
- else:
|
|
- # This is the `Add a new snippet...` item
|
|
- editable.set_text('')
|
|
-
|
|
- editable.grab_focus()
|
|
-
|
|
- def on_cell_edited(self, cell, path, new_text):
|
|
- if new_text != '':
|
|
- piter = self.model.get_iter(path)
|
|
- node = self.model.get_value(piter, self.SNIPPET_COLUMN)
|
|
-
|
|
- if node:
|
|
- if node == self.snippet.data:
|
|
- s = self.snippet
|
|
- else:
|
|
- s = Snippet(node)
|
|
-
|
|
- s['description'] = new_text
|
|
- self.snippet_changed(piter)
|
|
- self.select_iter(piter)
|
|
- else:
|
|
- # This is the `Add a new snippet...` item
|
|
- # We create a new snippet
|
|
- snippet = self.new_snippet({'description': new_text})
|
|
-
|
|
- if snippet:
|
|
- self.model.set_value(piter, self.SNIPPET_COLUMN, snippet.data)
|
|
- self.snippet_changed(piter)
|
|
- self.snippet = snippet
|
|
- self.selection_changed()
|
|
-
|
|
- def on_entry_accelerator_focus_out(self, entry, event):
|
|
- if not self.snippet:
|
|
- return
|
|
-
|
|
- entry.set_text(self.snippet.accelerator_display())
|
|
-
|
|
- def entry_tab_trigger_update_valid(self):
|
|
- entry = self['entry_tab_trigger']
|
|
- text = entry.get_text()
|
|
-
|
|
- 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.show()
|
|
-
|
|
- #self['hbox_tab_trigger'].set_spacing(3)
|
|
- tip = _('This is not a valid Tab trigger. Triggers can either contain letters or a single (non-alphanumeric) character like: {, [, etc.')
|
|
-
|
|
- entry.set_tooltip_text(tip)
|
|
- img.set_tooltip_text(tip)
|
|
- else:
|
|
- self['image_tab_trigger'].hide()
|
|
- #self['hbox_tab_trigger'].set_spacing(0)
|
|
- entry.set_tooltip_text(_('Single word the snippet is activated with after pressing Tab'))
|
|
-
|
|
- return False
|
|
+ s = Snippet(node)
|
|
|
|
- def on_entry_tab_trigger_focus_out(self, entry, event):
|
|
- if not self.snippet:
|
|
- return
|
|
-
|
|
- text = entry.get_text()
|
|
-
|
|
- # save tag
|
|
- self.snippet['tag'] = text
|
|
- self.snippet_changed()
|
|
-
|
|
- def on_entry_drop_targets_focus_out(self, entry, event):
|
|
- if not self.snippet:
|
|
- return
|
|
-
|
|
- text = entry.get_text()
|
|
-
|
|
- # save drop targets
|
|
- self.snippet['drop-targets'] = text
|
|
- self.snippet_changed()
|
|
-
|
|
- def on_entry_tab_trigger_changed(self, entry):
|
|
- self.entry_tab_trigger_update_valid()
|
|
-
|
|
- def on_source_view_snippet_focus_out(self, source_view, event):
|
|
- if not self.snippet:
|
|
- return
|
|
-
|
|
- buf = source_view.get_buffer()
|
|
- text = buf.get_text(buf.get_start_iter(), \
|
|
- buf.get_end_iter(), False)
|
|
-
|
|
- self.snippet['text'] = text
|
|
- self.snippet_changed()
|
|
-
|
|
- def on_button_new_snippet_clicked(self, button):
|
|
- snippet = self.new_snippet()
|
|
-
|
|
- if not snippet:
|
|
- return
|
|
-
|
|
- parent = self.model.get_iter(self.language_path)
|
|
- path = self.model.get_path(parent)
|
|
-
|
|
- dummy = self.get_dummy(parent)
|
|
-
|
|
- if dummy:
|
|
- # Remove the dummy
|
|
- self.model.remove(dummy)
|
|
-
|
|
- # Add the snippet
|
|
- piter = self.add_snippet(parent, snippet.data)
|
|
+ s['description'] = new_text
|
|
+ self.snippet_changed(piter)
|
|
self.select_iter(piter)
|
|
+ else:
|
|
+ # This is the `Add a new snippet...` item
|
|
+ # We create a new snippet
|
|
+ snippet = self.new_snippet({'description': new_text})
|
|
|
|
- if not self.tree_view.row_expanded(path):
|
|
- self.tree_view.expand_row(path, False)
|
|
- self.select_iter(piter)
|
|
-
|
|
- self.tree_view.grab_focus()
|
|
-
|
|
- path = self.model.get_path(piter)
|
|
- self.tree_view.set_cursor(path, self.column, True)
|
|
-
|
|
- def file_filter(self, name, pattern):
|
|
- fil = Gtk.FileFilter()
|
|
- fil.set_name(name)
|
|
-
|
|
- for p in pattern:
|
|
- fil.add_pattern(p)
|
|
-
|
|
- return fil
|
|
-
|
|
- def import_snippets(self, filenames):
|
|
- success = True
|
|
-
|
|
- for filename in filenames:
|
|
- if not Pluma.utils_uri_has_file_scheme(filename):
|
|
- continue
|
|
-
|
|
- # Remove file://
|
|
- gfile = Gio.file_new_for_uri(filename)
|
|
- filename = gfile.get_path()
|
|
-
|
|
- importer = Importer(filename)
|
|
- error = importer.run()
|
|
-
|
|
- if error:
|
|
- message = _('The following error occurred while importing: %s') % error
|
|
- success = False
|
|
- message_dialog(self.dlg, Gtk.MessageType.ERROR, message)
|
|
-
|
|
- self.build_model(True)
|
|
-
|
|
- if success:
|
|
- message = _('Import successfully completed')
|
|
- message_dialog(self.dlg, Gtk.MessageType.INFO, message)
|
|
-
|
|
- def on_import_response(self, dialog, response):
|
|
- if response == Gtk.ResponseType.CANCEL or response == Gtk.ResponseType.CLOSE:
|
|
- dialog.destroy()
|
|
- return
|
|
-
|
|
- f = dialog.get_uris()
|
|
- dialog.destroy()
|
|
-
|
|
- 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.add_filter(self.file_filter(_('All supported archives'), ('*.gz','*.bz2','*.tar', '*.xml')))
|
|
- dlg.add_filter(self.file_filter(_('Gzip compressed archive'), ('*.tar.gz',)))
|
|
- dlg.add_filter(self.file_filter(_('Bzip2 compressed archive'), ('*.tar.bz2',)))
|
|
- dlg.add_filter(self.file_filter(_('Single snippets file'), ('*.xml',)))
|
|
- dlg.add_filter(self.file_filter(_('All files'), '*'))
|
|
-
|
|
- dlg.connect('response', self.on_import_response)
|
|
- dlg.set_local_only(True)
|
|
-
|
|
- dlg.show()
|
|
-
|
|
- def export_snippets_real(self, filename, snippets, show_dialogs=True):
|
|
- export = Exporter(filename, snippets)
|
|
- error = export.run()
|
|
-
|
|
- if error:
|
|
- message = _('The following error occurred while exporting: %s') % error
|
|
- msgtype = Gtk.MessageType.ERROR
|
|
- retval = False
|
|
- else:
|
|
- message = _('Export successfully completed')
|
|
- msgtype = Gtk.MessageType.INFO
|
|
- retval = True
|
|
-
|
|
- if show_dialogs:
|
|
- message_dialog(self.dlg, msgtype, message)
|
|
-
|
|
- return retval
|
|
-
|
|
- def on_export_response(self, dialog, response):
|
|
- filename = dialog.get_filename()
|
|
- snippets = dialog._export_snippets
|
|
-
|
|
- dialog.destroy()
|
|
-
|
|
- if response != Gtk.ResponseType.OK:
|
|
- return
|
|
-
|
|
- self.export_snippets_real(filename, snippets);
|
|
-
|
|
- def export_snippets(self, filename=None, show_dialogs=True):
|
|
- snippets = self.selected_snippets()
|
|
-
|
|
- if not snippets or len(snippets) == 0:
|
|
- return False
|
|
-
|
|
- usersnippets = []
|
|
- systemsnippets = []
|
|
-
|
|
- # Iterate through snippets and look for system snippets
|
|
- for snippet in snippets:
|
|
- if snippet.can_modify():
|
|
- usersnippets.append(snippet)
|
|
- else:
|
|
- systemsnippets.append(snippet)
|
|
-
|
|
- export_snippets = snippets
|
|
-
|
|
- if len(systemsnippets) != 0 and show_dialogs:
|
|
- # Ask if system snippets should also be exported
|
|
- message = _('Do you want to include selected <b>system</b> snippets in your export?')
|
|
- mes = Gtk.MessageDialog(flags=Gtk.DialogFlags.MODAL,
|
|
- type=Gtk.MessageType.QUESTION,
|
|
- buttons=Gtk.ButtonsType.YES_NO,
|
|
- message_format=message)
|
|
- mes.set_property('use-markup', True)
|
|
- resp = mes.run()
|
|
- mes.destroy()
|
|
-
|
|
- if resp == Gtk.ResponseType.NO:
|
|
- export_snippets = usersnippets
|
|
- elif resp != Gtk.ResponseType.YES:
|
|
- return False
|
|
-
|
|
- if len(export_snippets) == 0 and show_dialogs:
|
|
- message = _('There are no snippets selected to be exported')
|
|
- message_dialog(self.dlg, Gtk.MessageType.INFO, message)
|
|
- 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._export_snippets = export_snippets
|
|
- dlg.add_filter(self.file_filter(_('All supported archives'), ('*.gz','*.bz2','*.tar')))
|
|
- dlg.add_filter(self.file_filter(_('Gzip compressed archive'), ('*.tar.gz',)))
|
|
- dlg.add_filter(self.file_filter(_('Bzip2 compressed archive'), ('*.tar.bz2',)))
|
|
-
|
|
- dlg.add_filter(self.file_filter(_('All files'), '*'))
|
|
- dlg.set_do_overwrite_confirmation(True)
|
|
- dlg.set_current_name(self.default_export_name)
|
|
-
|
|
- dlg.connect('response', self.on_export_response)
|
|
- dlg.set_local_only(True)
|
|
-
|
|
- dlg.show()
|
|
- return True
|
|
- else:
|
|
- return self.export_snippets_real(filename, export_snippets, show_dialogs)
|
|
-
|
|
- def on_button_export_snippets_clicked(self, button):
|
|
- snippets = self.selected_snippets()
|
|
-
|
|
- if not snippets or len(snippets) == 0:
|
|
- return
|
|
-
|
|
- usersnippets = []
|
|
- systemsnippets = []
|
|
-
|
|
- # Iterate through snippets and look for system snippets
|
|
- for snippet in snippets:
|
|
- if snippet.can_modify():
|
|
- usersnippets.append(snippet)
|
|
- 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._export_snippets = snippets
|
|
-
|
|
- if len(systemsnippets) != 0:
|
|
- # Ask if system snippets should also be exported
|
|
- message = _('Do you want to include selected <b>system</b> snippets in your export?')
|
|
- mes = Gtk.MessageDialog(flags=Gtk.DialogFlags.MODAL,
|
|
- type=Gtk.MessageType.QUESTION,
|
|
- buttons=Gtk.ButtonsType.YES_NO,
|
|
- message_format=message)
|
|
- mes.set_property('use-markup', True)
|
|
- resp = mes.run()
|
|
- mes.destroy()
|
|
-
|
|
- if resp == Gtk.ResponseType.NO:
|
|
- dlg._export_snippets = usersnippets
|
|
- elif resp != Gtk.ResponseType.YES:
|
|
- dlg.destroy()
|
|
- return
|
|
-
|
|
- if len(dlg._export_snippets) == 0:
|
|
- dlg.destroy()
|
|
-
|
|
- message = _('There are no snippets selected to be exported')
|
|
- message_dialog(self.dlg, Gtk.MessageType.INFO, message)
|
|
- return
|
|
-
|
|
- dlg.add_filter(self.file_filter(_('All supported archives'), ('*.gz','*.bz2','*.tar')))
|
|
- dlg.add_filter(self.file_filter(_('Gzip compressed archive'), ('*.tar.gz',)))
|
|
- dlg.add_filter(self.file_filter(_('Bzip2 compressed archive'), ('*.tar.bz2',)))
|
|
-
|
|
- dlg.add_filter(self.file_filter(_('All files'), '*'))
|
|
- dlg.set_do_overwrite_confirmation(True)
|
|
- dlg.set_current_name(self.default_export_name)
|
|
-
|
|
- dlg.connect('response', self.on_export_response)
|
|
- dlg.set_local_only(True)
|
|
-
|
|
- dlg.show()
|
|
-
|
|
- def remove_snippet_revert(self, path, piter):
|
|
- node = self.snippet_from_iter(self.model, piter)
|
|
- Library().revert_snippet(node)
|
|
-
|
|
- return piter
|
|
-
|
|
- def remove_snippet_delete(self, path, piter):
|
|
- node = self.snippet_from_iter(self.model, piter)
|
|
- parent = self.model.iter_parent(piter)
|
|
-
|
|
- Library().remove_snippet(node)
|
|
- idx = path.get_indices()
|
|
-
|
|
- if self.model.remove(piter):
|
|
- return piter
|
|
- elif idx[-1] != 0:
|
|
- self.select_iter(self.model.get_iter((idx[0], idx[1] - 1)))
|
|
- else:
|
|
- dummy = self.add_new_snippet_node(parent)
|
|
- self.tree_view.expand_row(self.model.get_path(parent), False)
|
|
- return dummy
|
|
-
|
|
- def on_button_remove_snippet_clicked(self, button):
|
|
- override, remove, system = self.selected_snippets_state()
|
|
-
|
|
- if not (override ^ remove) or system:
|
|
- return
|
|
-
|
|
- paths = self.selected_snippets(include_languages=False, as_path=True)
|
|
-
|
|
- if override:
|
|
- action = self.remove_snippet_revert
|
|
- else:
|
|
- action = self.remove_snippet_delete
|
|
-
|
|
- # Remove selection
|
|
- self.tree_view.get_selection().unselect_all()
|
|
-
|
|
- # Create tree row references
|
|
- references = []
|
|
- for path in paths:
|
|
- references.append(Gtk.TreeRowReference(self.model, path))
|
|
-
|
|
- # Remove/revert snippets
|
|
- select = None
|
|
- for reference in references:
|
|
- path = reference.get_path()
|
|
- piter = self.model.get_iter(path)
|
|
-
|
|
- res = action(path, piter)
|
|
-
|
|
- if res:
|
|
- select = res
|
|
-
|
|
- if select:
|
|
- self.select_iter(select)
|
|
-
|
|
- self.selection_changed()
|
|
-
|
|
- def set_accelerator(self, keyval, mod):
|
|
- accelerator = Gtk.accelerator_name(keyval, mod)
|
|
- self.snippet['accelerator'] = accelerator
|
|
-
|
|
- return True
|
|
-
|
|
- def on_entry_accelerator_key_press(self, entry, event):
|
|
- source_view = self['source_view_snippet']
|
|
+ if snippet:
|
|
+ self.model.set_value(piter, self.SNIPPET_COLUMN, snippet.data)
|
|
+ self.snippet_changed(piter)
|
|
+ self.snippet = snippet
|
|
+ self.selection_changed()
|
|
|
|
- if event.keyval == Gdk.keyval_from_name('Escape'):
|
|
- # Reset
|
|
- entry.set_text(self.snippet.accelerator_display())
|
|
- self.tree_view.grab_focus()
|
|
-
|
|
- return True
|
|
- elif event.keyval == Gdk.keyval_from_name('Delete') or \
|
|
- event.keyval == Gdk.keyval_from_name('BackSpace'):
|
|
- # Remove the accelerator
|
|
- entry.set_text('')
|
|
- self.snippet['accelerator'] = ''
|
|
- self.tree_view.grab_focus()
|
|
-
|
|
- self.snippet_changed()
|
|
- return True
|
|
- elif Library().valid_accelerator(event.keyval, event.state):
|
|
- # New accelerator
|
|
- self.set_accelerator(event.keyval, \
|
|
- event.state & Gtk.accelerator_get_default_mod_mask())
|
|
- entry.set_text(self.snippet.accelerator_display())
|
|
- self.snippet_changed()
|
|
- self.tree_view.grab_focus()
|
|
+ def on_entry_accelerator_focus_out(self, entry, event):
|
|
+ if not self.snippet:
|
|
+ return
|
|
|
|
- else:
|
|
- return True
|
|
-
|
|
- def on_entry_accelerator_focus_in(self, entry, event):
|
|
- if self.snippet['accelerator']:
|
|
- entry.set_text(_('Type a new shortcut, or press Backspace to clear'))
|
|
- else:
|
|
- entry.set_text(_('Type a new shortcut'))
|
|
-
|
|
- def update_language_path(self):
|
|
- model, paths = self.tree_view.get_selection().get_selected_rows()
|
|
-
|
|
- # Check if all have the same language parent
|
|
- current_parent = None
|
|
+ entry.set_text(self.snippet.accelerator_display())
|
|
|
|
- for path in paths:
|
|
- piter = model.get_iter(path)
|
|
- parent = model.iter_parent(piter)
|
|
-
|
|
- if parent:
|
|
- path = model.get_path(parent)
|
|
-
|
|
- if current_parent != None and current_parent != path:
|
|
- current_parent = None
|
|
- break
|
|
- else:
|
|
- current_parent = path
|
|
-
|
|
- self.language_path = current_parent
|
|
-
|
|
- def on_tree_view_selection_changed(self, selection):
|
|
- parent, piter, node = self.selected_snippet()
|
|
-
|
|
- if self.snippet:
|
|
- self.on_entry_tab_trigger_focus_out(self['entry_tab_trigger'],
|
|
- None)
|
|
- self.on_source_view_snippet_focus_out(self['source_view_snippet'],
|
|
- None)
|
|
- self.on_entry_drop_targets_focus_out(self['combo_drop_targets'].get_child(),
|
|
- None)
|
|
-
|
|
- self.update_language_path()
|
|
-
|
|
- if node:
|
|
- self.snippet = Snippet(node)
|
|
- else:
|
|
- self.snippet = None
|
|
+ def entry_tab_trigger_update_valid(self):
|
|
+ entry = self['entry_tab_trigger']
|
|
+ text = entry.get_text()
|
|
+
|
|
+ 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.show()
|
|
+
|
|
+ #self['hbox_tab_trigger'].set_spacing(3)
|
|
+ tip = _('This is not a valid Tab trigger. Triggers can either contain letters or a single (non-alphanumeric) character like: {, [, etc.')
|
|
+
|
|
+ entry.set_tooltip_text(tip)
|
|
+ img.set_tooltip_text(tip)
|
|
+ else:
|
|
+ self['image_tab_trigger'].hide()
|
|
+ #self['hbox_tab_trigger'].set_spacing(0)
|
|
+ entry.set_tooltip_text(_('Single word the snippet is activated with after pressing Tab'))
|
|
+
|
|
+ return False
|
|
+
|
|
+ def on_entry_tab_trigger_focus_out(self, entry, event):
|
|
+ if not self.snippet:
|
|
+ return
|
|
+
|
|
+ text = entry.get_text()
|
|
+
|
|
+ # save tag
|
|
+ self.snippet['tag'] = text
|
|
+ self.snippet_changed()
|
|
+
|
|
+ def on_entry_drop_targets_focus_out(self, entry, event):
|
|
+ if not self.snippet:
|
|
+ return
|
|
+
|
|
+ text = entry.get_text()
|
|
+
|
|
+ # save drop targets
|
|
+ self.snippet['drop-targets'] = text
|
|
+ self.snippet_changed()
|
|
+
|
|
+ def on_entry_tab_trigger_changed(self, entry):
|
|
+ self.entry_tab_trigger_update_valid()
|
|
+
|
|
+ def on_source_view_snippet_focus_out(self, source_view, event):
|
|
+ if not self.snippet:
|
|
+ return
|
|
+
|
|
+ buf = source_view.get_buffer()
|
|
+ text = buf.get_text(buf.get_start_iter(), \
|
|
+ buf.get_end_iter(), False)
|
|
+
|
|
+ self.snippet['text'] = text
|
|
+ self.snippet_changed()
|
|
+
|
|
+ def on_button_new_snippet_clicked(self, button):
|
|
+ snippet = self.new_snippet()
|
|
+
|
|
+ if not snippet:
|
|
+ return
|
|
+
|
|
+ parent = self.model.get_iter(self.language_path)
|
|
+ path = self.model.get_path(parent)
|
|
+
|
|
+ dummy = self.get_dummy(parent)
|
|
+
|
|
+ if dummy:
|
|
+ # Remove the dummy
|
|
+ self.model.remove(dummy)
|
|
+
|
|
+ # Add the snippet
|
|
+ piter = self.add_snippet(parent, snippet.data)
|
|
+ self.select_iter(piter)
|
|
+
|
|
+ if not self.tree_view.row_expanded(path):
|
|
+ self.tree_view.expand_row(path, False)
|
|
+ self.select_iter(piter)
|
|
+
|
|
+ self.tree_view.grab_focus()
|
|
+
|
|
+ path = self.model.get_path(piter)
|
|
+ self.tree_view.set_cursor(path, self.column, True)
|
|
+
|
|
+ def file_filter(self, name, pattern):
|
|
+ fil = Gtk.FileFilter()
|
|
+ fil.set_name(name)
|
|
+
|
|
+ for p in pattern:
|
|
+ fil.add_pattern(p)
|
|
+
|
|
+ return fil
|
|
+
|
|
+ def import_snippets(self, filenames):
|
|
+ success = True
|
|
+
|
|
+ for filename in filenames:
|
|
+ if not Pluma.utils_uri_has_file_scheme(filename):
|
|
+ continue
|
|
+
|
|
+ # Remove file://
|
|
+ gfile = Gio.file_new_for_uri(filename)
|
|
+ filename = gfile.get_path()
|
|
+
|
|
+ importer = Importer(filename)
|
|
+ error = importer.run()
|
|
+
|
|
+ if error:
|
|
+ message = _('The following error occurred while importing: %s') % error
|
|
+ success = False
|
|
+ message_dialog(self.dlg, Gtk.MessageType.ERROR, message)
|
|
+
|
|
+ self.build_model(True)
|
|
+
|
|
+ if success:
|
|
+ message = _('Import successfully completed')
|
|
+ message_dialog(self.dlg, Gtk.MessageType.INFO, message)
|
|
|
|
- self.selection_changed()
|
|
+ def on_import_response(self, dialog, response):
|
|
+ if response == Gtk.ResponseType.CANCEL or response == Gtk.ResponseType.CLOSE:
|
|
+ dialog.destroy()
|
|
+ return
|
|
|
|
- def iter_after(self, target, after):
|
|
- if not after:
|
|
- return True
|
|
+ f = dialog.get_uris()
|
|
+ dialog.destroy()
|
|
|
|
- tp = self.model.get_path(target)
|
|
- ap = self.model.get_path(after)
|
|
-
|
|
- if tp[0] > ap[0] or (tp[0] == ap[0] and (len(ap) == 1 or tp[1] > ap[1])):
|
|
- return True
|
|
-
|
|
+ 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.add_filter(self.file_filter(_('All supported archives'), ('*.gz','*.bz2','*.tar', '*.xml')))
|
|
+ dlg.add_filter(self.file_filter(_('Gzip compressed archive'), ('*.tar.gz',)))
|
|
+ dlg.add_filter(self.file_filter(_('Bzip2 compressed archive'), ('*.tar.bz2',)))
|
|
+ dlg.add_filter(self.file_filter(_('Single snippets file'), ('*.xml',)))
|
|
+ dlg.add_filter(self.file_filter(_('All files'), '*'))
|
|
+
|
|
+ dlg.connect('response', self.on_import_response)
|
|
+ dlg.set_local_only(True)
|
|
+
|
|
+ dlg.show()
|
|
+
|
|
+ def export_snippets_real(self, filename, snippets, show_dialogs=True):
|
|
+ export = Exporter(filename, snippets)
|
|
+ error = export.run()
|
|
+
|
|
+ if error:
|
|
+ message = _('The following error occurred while exporting: %s') % error
|
|
+ msgtype = Gtk.MessageType.ERROR
|
|
+ retval = False
|
|
+ else:
|
|
+ message = _('Export successfully completed')
|
|
+ msgtype = Gtk.MessageType.INFO
|
|
+ retval = True
|
|
+
|
|
+ if show_dialogs:
|
|
+ message_dialog(self.dlg, msgtype, message)
|
|
+
|
|
+ return retval
|
|
+
|
|
+ def on_export_response(self, dialog, response):
|
|
+ filename = dialog.get_filename()
|
|
+ snippets = dialog._export_snippets
|
|
+
|
|
+ dialog.destroy()
|
|
+
|
|
+ if response != Gtk.ResponseType.OK:
|
|
+ return
|
|
+
|
|
+ self.export_snippets_real(filename, snippets);
|
|
+
|
|
+ def export_snippets(self, filename=None, show_dialogs=True):
|
|
+ snippets = self.selected_snippets()
|
|
+
|
|
+ if not snippets or len(snippets) == 0:
|
|
+ return False
|
|
+
|
|
+ usersnippets = []
|
|
+ systemsnippets = []
|
|
+
|
|
+ # Iterate through snippets and look for system snippets
|
|
+ for snippet in snippets:
|
|
+ if snippet.can_modify():
|
|
+ usersnippets.append(snippet)
|
|
+ else:
|
|
+ systemsnippets.append(snippet)
|
|
+
|
|
+ export_snippets = snippets
|
|
+
|
|
+ if len(systemsnippets) != 0 and show_dialogs:
|
|
+ # Ask if system snippets should also be exported
|
|
+ message = _('Do you want to include selected <b>system</b> snippets in your export?')
|
|
+ mes = Gtk.MessageDialog(flags=Gtk.DialogFlags.MODAL,
|
|
+ type=Gtk.MessageType.QUESTION,
|
|
+ buttons=Gtk.ButtonsType.YES_NO,
|
|
+ message_format=message)
|
|
+ mes.set_property('use-markup', True)
|
|
+ resp = mes.run()
|
|
+ mes.destroy()
|
|
+
|
|
+ if resp == Gtk.ResponseType.NO:
|
|
+ export_snippets = usersnippets
|
|
+ elif resp != Gtk.ResponseType.YES:
|
|
return False
|
|
-
|
|
- def on_tree_view_snippets_key_press(self, treeview, event):
|
|
- if event.keyval == Gdk.keyval_from_name('Delete'):
|
|
- self.on_button_remove_snippet_clicked(None)
|
|
- return True
|
|
-
|
|
- def on_tree_view_snippets_row_expanded(self, treeview, piter, path):
|
|
- # Check if it is already filled
|
|
- self.fill_if_needed(piter)
|
|
- self.select_iter(piter)
|
|
-
|
|
- def on_entry_drop_targets_drag_data_received(self, entry, context, x, y, selection_data, info, timestamp):
|
|
- uris = drop_get_uris(selection_data)
|
|
-
|
|
- if not uris:
|
|
- return
|
|
-
|
|
- if entry.get_text():
|
|
- mimes = [entry.get_text()]
|
|
- else:
|
|
- mimes = []
|
|
-
|
|
- for uri in uris:
|
|
- try:
|
|
- mime = Gio.content_type_guess(uri)
|
|
- except:
|
|
- mime = None
|
|
-
|
|
- if mime:
|
|
- mimes.append(mime)
|
|
-
|
|
- entry.set_text(', '.join(mimes))
|
|
- self.on_entry_drop_targets_focus_out(entry, None)
|
|
- context.finish(True, False, timestamp)
|
|
-
|
|
- entry.stop_emission('drag_data_received')
|
|
-# ex:ts=8:et:
|
|
+
|
|
+ if len(export_snippets) == 0 and show_dialogs:
|
|
+ message = _('There are no snippets selected to be exported')
|
|
+ message_dialog(self.dlg, Gtk.MessageType.INFO, message)
|
|
+ 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._export_snippets = export_snippets
|
|
+ dlg.add_filter(self.file_filter(_('All supported archives'), ('*.gz','*.bz2','*.tar')))
|
|
+ dlg.add_filter(self.file_filter(_('Gzip compressed archive'), ('*.tar.gz',)))
|
|
+ dlg.add_filter(self.file_filter(_('Bzip2 compressed archive'), ('*.tar.bz2',)))
|
|
+
|
|
+ dlg.add_filter(self.file_filter(_('All files'), '*'))
|
|
+ dlg.set_do_overwrite_confirmation(True)
|
|
+ dlg.set_current_name(self.default_export_name)
|
|
+
|
|
+ dlg.connect('response', self.on_export_response)
|
|
+ dlg.set_local_only(True)
|
|
+
|
|
+ dlg.show()
|
|
+ return True
|
|
+ else:
|
|
+ return self.export_snippets_real(filename, export_snippets, show_dialogs)
|
|
+
|
|
+ def on_button_export_snippets_clicked(self, button):
|
|
+ snippets = self.selected_snippets()
|
|
+
|
|
+ if not snippets or len(snippets) == 0:
|
|
+ return
|
|
+
|
|
+ usersnippets = []
|
|
+ systemsnippets = []
|
|
+
|
|
+ # Iterate through snippets and look for system snippets
|
|
+ for snippet in snippets:
|
|
+ if snippet.can_modify():
|
|
+ usersnippets.append(snippet)
|
|
+ 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._export_snippets = snippets
|
|
+
|
|
+ if len(systemsnippets) != 0:
|
|
+ # Ask if system snippets should also be exported
|
|
+ message = _('Do you want to include selected <b>system</b> snippets in your export?')
|
|
+ mes = Gtk.MessageDialog(flags=Gtk.DialogFlags.MODAL,
|
|
+ type=Gtk.MessageType.QUESTION,
|
|
+ buttons=Gtk.ButtonsType.YES_NO,
|
|
+ message_format=message)
|
|
+ mes.set_property('use-markup', True)
|
|
+ resp = mes.run()
|
|
+ mes.destroy()
|
|
+
|
|
+ if resp == Gtk.ResponseType.NO:
|
|
+ dlg._export_snippets = usersnippets
|
|
+ elif resp != Gtk.ResponseType.YES:
|
|
+ dlg.destroy()
|
|
+ return
|
|
+
|
|
+ if len(dlg._export_snippets) == 0:
|
|
+ dlg.destroy()
|
|
+
|
|
+ message = _('There are no snippets selected to be exported')
|
|
+ message_dialog(self.dlg, Gtk.MessageType.INFO, message)
|
|
+ return
|
|
+
|
|
+ dlg.add_filter(self.file_filter(_('All supported archives'), ('*.gz','*.bz2','*.tar')))
|
|
+ dlg.add_filter(self.file_filter(_('Gzip compressed archive'), ('*.tar.gz',)))
|
|
+ dlg.add_filter(self.file_filter(_('Bzip2 compressed archive'), ('*.tar.bz2',)))
|
|
+
|
|
+ dlg.add_filter(self.file_filter(_('All files'), '*'))
|
|
+ dlg.set_do_overwrite_confirmation(True)
|
|
+ dlg.set_current_name(self.default_export_name)
|
|
+
|
|
+ dlg.connect('response', self.on_export_response)
|
|
+ dlg.set_local_only(True)
|
|
+
|
|
+ dlg.show()
|
|
+
|
|
+ def remove_snippet_revert(self, path, piter):
|
|
+ node = self.snippet_from_iter(self.model, piter)
|
|
+ Library().revert_snippet(node)
|
|
+
|
|
+ return piter
|
|
+
|
|
+ def remove_snippet_delete(self, path, piter):
|
|
+ node = self.snippet_from_iter(self.model, piter)
|
|
+ parent = self.model.iter_parent(piter)
|
|
+
|
|
+ Library().remove_snippet(node)
|
|
+ idx = path.get_indices()
|
|
+
|
|
+ if self.model.remove(piter):
|
|
+ return piter
|
|
+ elif idx[-1] != 0:
|
|
+ self.select_iter(self.model.get_iter((idx[0], idx[1] - 1)))
|
|
+ else:
|
|
+ dummy = self.add_new_snippet_node(parent)
|
|
+ self.tree_view.expand_row(self.model.get_path(parent), False)
|
|
+ return dummy
|
|
+
|
|
+ def on_button_remove_snippet_clicked(self, button):
|
|
+ override, remove, system = self.selected_snippets_state()
|
|
+
|
|
+ if not (override ^ remove) or system:
|
|
+ return
|
|
+
|
|
+ paths = self.selected_snippets(include_languages=False, as_path=True)
|
|
+
|
|
+ if override:
|
|
+ action = self.remove_snippet_revert
|
|
+ else:
|
|
+ action = self.remove_snippet_delete
|
|
+
|
|
+ # Remove selection
|
|
+ self.tree_view.get_selection().unselect_all()
|
|
+
|
|
+ # Create tree row references
|
|
+ references = []
|
|
+ for path in paths:
|
|
+ references.append(Gtk.TreeRowReference(self.model, path))
|
|
+
|
|
+ # Remove/revert snippets
|
|
+ select = None
|
|
+ for reference in references:
|
|
+ path = reference.get_path()
|
|
+ piter = self.model.get_iter(path)
|
|
+
|
|
+ res = action(path, piter)
|
|
+
|
|
+ if res:
|
|
+ select = res
|
|
+
|
|
+ if select:
|
|
+ self.select_iter(select)
|
|
+
|
|
+ self.selection_changed()
|
|
+
|
|
+ def set_accelerator(self, keyval, mod):
|
|
+ accelerator = Gtk.accelerator_name(keyval, mod)
|
|
+ self.snippet['accelerator'] = accelerator
|
|
+
|
|
+ return True
|
|
+
|
|
+ def on_entry_accelerator_key_press(self, entry, event):
|
|
+ source_view = self['source_view_snippet']
|
|
+
|
|
+ if event.keyval == Gdk.keyval_from_name('Escape'):
|
|
+ # Reset
|
|
+ entry.set_text(self.snippet.accelerator_display())
|
|
+ self.tree_view.grab_focus()
|
|
+
|
|
+ return True
|
|
+ elif event.keyval == Gdk.keyval_from_name('Delete') or \
|
|
+ event.keyval == Gdk.keyval_from_name('BackSpace'):
|
|
+ # Remove the accelerator
|
|
+ entry.set_text('')
|
|
+ self.snippet['accelerator'] = ''
|
|
+ self.tree_view.grab_focus()
|
|
+
|
|
+ self.snippet_changed()
|
|
+ return True
|
|
+ elif Library().valid_accelerator(event.keyval, event.state):
|
|
+ # New accelerator
|
|
+ self.set_accelerator(event.keyval, \
|
|
+ event.state & Gtk.accelerator_get_default_mod_mask())
|
|
+ entry.set_text(self.snippet.accelerator_display())
|
|
+ self.snippet_changed()
|
|
+ self.tree_view.grab_focus()
|
|
+
|
|
+ else:
|
|
+ return True
|
|
+
|
|
+ def on_entry_accelerator_focus_in(self, entry, event):
|
|
+ if self.snippet['accelerator']:
|
|
+ entry.set_text(_('Type a new shortcut, or press Backspace to clear'))
|
|
+ else:
|
|
+ entry.set_text(_('Type a new shortcut'))
|
|
+
|
|
+ def update_language_path(self):
|
|
+ model, paths = self.tree_view.get_selection().get_selected_rows()
|
|
+
|
|
+ # Check if all have the same language parent
|
|
+ current_parent = None
|
|
+
|
|
+ for path in paths:
|
|
+ piter = model.get_iter(path)
|
|
+ parent = model.iter_parent(piter)
|
|
+
|
|
+ if parent:
|
|
+ path = model.get_path(parent)
|
|
+
|
|
+ if current_parent != None and current_parent != path:
|
|
+ current_parent = None
|
|
+ break
|
|
+ else:
|
|
+ current_parent = path
|
|
+
|
|
+ self.language_path = current_parent
|
|
+
|
|
+ def on_tree_view_selection_changed(self, selection):
|
|
+ parent, piter, node = self.selected_snippet()
|
|
+
|
|
+ if self.snippet:
|
|
+ self.on_entry_tab_trigger_focus_out(self['entry_tab_trigger'],
|
|
+ None)
|
|
+ self.on_source_view_snippet_focus_out(self['source_view_snippet'],
|
|
+ None)
|
|
+ self.on_entry_drop_targets_focus_out(self['combo_drop_targets'].get_child(),
|
|
+ None)
|
|
+
|
|
+ self.update_language_path()
|
|
+
|
|
+ if node:
|
|
+ self.snippet = Snippet(node)
|
|
+ else:
|
|
+ self.snippet = None
|
|
+
|
|
+ self.selection_changed()
|
|
+
|
|
+ def iter_after(self, target, after):
|
|
+ if not after:
|
|
+ return True
|
|
+
|
|
+ tp = self.model.get_path(target)
|
|
+ ap = self.model.get_path(after)
|
|
+
|
|
+ if tp[0] > ap[0] or (tp[0] == ap[0] and (len(ap) == 1 or tp[1] > ap[1])):
|
|
+ return True
|
|
+
|
|
+ return False
|
|
+
|
|
+ def on_tree_view_snippets_key_press(self, treeview, event):
|
|
+ if event.keyval == Gdk.keyval_from_name('Delete'):
|
|
+ self.on_button_remove_snippet_clicked(None)
|
|
+ return True
|
|
+
|
|
+ def on_tree_view_snippets_row_expanded(self, treeview, piter, path):
|
|
+ # Check if it is already filled
|
|
+ self.fill_if_needed(piter)
|
|
+ self.select_iter(piter)
|
|
+
|
|
+ def on_entry_drop_targets_drag_data_received(self, entry, context, x, y, selection_data, info, timestamp):
|
|
+ uris = drop_get_uris(selection_data)
|
|
+
|
|
+ if not uris:
|
|
+ return
|
|
+
|
|
+ if entry.get_text():
|
|
+ mimes = [entry.get_text()]
|
|
+ else:
|
|
+ mimes = []
|
|
+
|
|
+ for uri in uris:
|
|
+ try:
|
|
+ mime = Gio.content_type_guess(uri)
|
|
+ except:
|
|
+ mime = None
|
|
+
|
|
+ if mime:
|
|
+ mimes.append(mime)
|
|
+
|
|
+ entry.set_text(', '.join(mimes))
|
|
+ self.on_entry_drop_targets_focus_out(entry, None)
|
|
+ context.finish(True, False, timestamp)
|
|
+
|
|
+ entry.stop_emission('drag_data_received')
|
|
+# ex:ts=4:et:
|
|
diff --git a/plugins/snippets/snippets/Parser.py b/plugins/snippets/snippets/Parser.py
|
|
old mode 100755
|
|
new mode 100644
|
|
index 0c638df..280ce0c
|
|
--- a/plugins/snippets/snippets/Parser.py
|
|
+++ b/plugins/snippets/snippets/Parser.py
|
|
@@ -21,239 +21,239 @@ import sys
|
|
from SubstitutionParser import SubstitutionParser
|
|
|
|
class Token:
|
|
- def __init__(self, klass, data):
|
|
- self.klass = klass
|
|
- self.data = data
|
|
-
|
|
- def __str__(self):
|
|
- return '%s: [%s]' % (self.klass, self.data)
|
|
-
|
|
- def __eq__(self, other):
|
|
- return self.klass == other.klass and self.data == other.data
|
|
-
|
|
- def __ne__(self, other):
|
|
- return not self.__eq__(other)
|
|
+ def __init__(self, klass, data):
|
|
+ self.klass = klass
|
|
+ self.data = data
|
|
+
|
|
+ def __str__(self):
|
|
+ return '%s: [%s]' % (self.klass, self.data)
|
|
+
|
|
+ def __eq__(self, other):
|
|
+ return self.klass == other.klass and self.data == other.data
|
|
+
|
|
+ def __ne__(self, other):
|
|
+ return not self.__eq__(other)
|
|
|
|
class Parser:
|
|
- SREG_ENV = '[A-Z_]+'
|
|
- SREG_ID = '[0-9]+'
|
|
-
|
|
- REG_ESCAPE = re.compile('(\\$(%s|\\(|\\{|<|%s)|`|\\\\)' % (SREG_ENV, SREG_ID))
|
|
-
|
|
- def __init__(self, **kwargs):
|
|
- for k, v in kwargs.items():
|
|
- setattr(self, k, v)
|
|
-
|
|
- self.position = 0
|
|
- self.data_length = len(self.data)
|
|
-
|
|
- self.RULES = (self._match_env, self._match_regex, self._match_placeholder, self._match_shell, self._match_eval, self._text)
|
|
-
|
|
- def remains(self):
|
|
- return self.data[self.position:]
|
|
-
|
|
- def next_char(self):
|
|
- if self.position + 1 >= self.data_length:
|
|
- return ''
|
|
- else:
|
|
- return self.data[self.position + 1]
|
|
-
|
|
- def char(self):
|
|
- if self.position >= self.data_length:
|
|
- return ''
|
|
- else:
|
|
- return self.data[self.position]
|
|
-
|
|
- def token(self):
|
|
- self.tktext = ''
|
|
-
|
|
- while self.position < self.data_length:
|
|
- try:
|
|
- # Get first character
|
|
- func = {'$': self._rule,
|
|
- '`': self._try_match_shell}[self.char()]
|
|
- except:
|
|
- func = self._text
|
|
-
|
|
- # Detect end of text token
|
|
- if func != self._text and self.tktext != '':
|
|
- return Token('text', self.tktext)
|
|
-
|
|
- tk = func()
|
|
-
|
|
- if tk:
|
|
- return tk
|
|
-
|
|
- if self.tktext != '':
|
|
- return Token('text', self.tktext)
|
|
-
|
|
- def _need_escape(self):
|
|
- text = self.remains()[1:]
|
|
-
|
|
- if text == '':
|
|
- return False
|
|
-
|
|
- return self.REG_ESCAPE.match(text)
|
|
-
|
|
- def _escape(self):
|
|
- if not self._need_escape():
|
|
- return
|
|
-
|
|
- # Increase position with 1
|
|
- self.position += 1
|
|
-
|
|
- def _text(self):
|
|
- if self.char() == '\\':
|
|
- self._escape()
|
|
-
|
|
- self.tktext += self.char()
|
|
- self.position += 1
|
|
-
|
|
- def _rule(self):
|
|
- for rule in self.RULES:
|
|
- res = rule()
|
|
-
|
|
- if res:
|
|
- return res
|
|
-
|
|
- def _match_env(self):
|
|
- text = self.remains()
|
|
- match = re.match('\\$(%s)' % self.SREG_ENV, text) or re.match('\\${(%s)}' % self.SREG_ENV, text)
|
|
-
|
|
- if match:
|
|
- self.position += len(match.group(0))
|
|
- return Token('environment', match.group(1))
|
|
-
|
|
- def _parse_list(self, lst):
|
|
- pos = 0
|
|
- length = len(lst)
|
|
- items = []
|
|
- last = None
|
|
-
|
|
- while pos < length:
|
|
- char = lst[pos]
|
|
- next = pos < length - 1 and lst[pos + 1]
|
|
-
|
|
- if char == '\\' and (next == ',' or next == ']'):
|
|
- char = next
|
|
- pos += 1
|
|
- elif char == ',':
|
|
- if last != None:
|
|
- items.append(last)
|
|
-
|
|
- last = None
|
|
- pos += 1
|
|
- continue
|
|
-
|
|
- last = (last != None and last + char) or char
|
|
- pos += 1
|
|
-
|
|
+ SREG_ENV = '[A-Z_]+'
|
|
+ SREG_ID = '[0-9]+'
|
|
+
|
|
+ REG_ESCAPE = re.compile('(\\$(%s|\\(|\\{|<|%s)|`|\\\\)' % (SREG_ENV, SREG_ID))
|
|
+
|
|
+ def __init__(self, **kwargs):
|
|
+ for k, v in kwargs.items():
|
|
+ setattr(self, k, v)
|
|
+
|
|
+ self.position = 0
|
|
+ self.data_length = len(self.data)
|
|
+
|
|
+ self.RULES = (self._match_env, self._match_regex, self._match_placeholder, self._match_shell, self._match_eval, self._text)
|
|
+
|
|
+ def remains(self):
|
|
+ return self.data[self.position:]
|
|
+
|
|
+ def next_char(self):
|
|
+ if self.position + 1 >= self.data_length:
|
|
+ return ''
|
|
+ else:
|
|
+ return self.data[self.position + 1]
|
|
+
|
|
+ def char(self):
|
|
+ if self.position >= self.data_length:
|
|
+ return ''
|
|
+ else:
|
|
+ return self.data[self.position]
|
|
+
|
|
+ def token(self):
|
|
+ self.tktext = ''
|
|
+
|
|
+ while self.position < self.data_length:
|
|
+ try:
|
|
+ # Get first character
|
|
+ func = {'$': self._rule,
|
|
+ '`': self._try_match_shell}[self.char()]
|
|
+ except:
|
|
+ func = self._text
|
|
+
|
|
+ # Detect end of text token
|
|
+ if func != self._text and self.tktext != '':
|
|
+ return Token('text', self.tktext)
|
|
+
|
|
+ tk = func()
|
|
+
|
|
+ if tk:
|
|
+ return tk
|
|
+
|
|
+ if self.tktext != '':
|
|
+ return Token('text', self.tktext)
|
|
+
|
|
+ def _need_escape(self):
|
|
+ text = self.remains()[1:]
|
|
+
|
|
+ if text == '':
|
|
+ return False
|
|
+
|
|
+ return self.REG_ESCAPE.match(text)
|
|
+
|
|
+ def _escape(self):
|
|
+ if not self._need_escape():
|
|
+ return
|
|
+
|
|
+ # Increase position with 1
|
|
+ self.position += 1
|
|
+
|
|
+ def _text(self):
|
|
+ if self.char() == '\\':
|
|
+ self._escape()
|
|
+
|
|
+ self.tktext += self.char()
|
|
+ self.position += 1
|
|
+
|
|
+ def _rule(self):
|
|
+ for rule in self.RULES:
|
|
+ res = rule()
|
|
+
|
|
+ if res:
|
|
+ return res
|
|
+
|
|
+ def _match_env(self):
|
|
+ text = self.remains()
|
|
+ match = re.match('\\$(%s)' % self.SREG_ENV, text) or re.match('\\${(%s)}' % self.SREG_ENV, text)
|
|
+
|
|
+ if match:
|
|
+ self.position += len(match.group(0))
|
|
+ return Token('environment', match.group(1))
|
|
+
|
|
+ def _parse_list(self, lst):
|
|
+ pos = 0
|
|
+ length = len(lst)
|
|
+ items = []
|
|
+ last = None
|
|
+
|
|
+ while pos < length:
|
|
+ char = lst[pos]
|
|
+ next = pos < length - 1 and lst[pos + 1]
|
|
+
|
|
+ if char == '\\' and (next == ',' or next == ']'):
|
|
+ char = next
|
|
+ pos += 1
|
|
+ elif char == ',':
|
|
if last != None:
|
|
- items.append(last)
|
|
-
|
|
- return items
|
|
-
|
|
- def _parse_default(self, default):
|
|
- match = re.match('^\\s*(\\\\)?(\\[((\\\\]|[^\\]])+)\\]\\s*)$', default)
|
|
-
|
|
- if not match:
|
|
- return [default]
|
|
-
|
|
- groups = match.groups()
|
|
-
|
|
- if groups[0]:
|
|
- return [groups[1]]
|
|
-
|
|
- return self._parse_list(groups[2])
|
|
-
|
|
- def _match_placeholder(self):
|
|
- text = self.remains()
|
|
-
|
|
- match = re.match('\\${(%s)(:((\\\\\\}|[^}])+))?}' % self.SREG_ID, text) or re.match('\\$(%s)' % self.SREG_ID, text)
|
|
-
|
|
- if not match:
|
|
- return None
|
|
-
|
|
- groups = match.groups()
|
|
- default = ''
|
|
- tabstop = int(groups[0])
|
|
- self.position += len(match.group(0))
|
|
-
|
|
- if len(groups) > 1 and groups[2]:
|
|
- default = self._parse_default(groups[2].replace('\\}', '}'))
|
|
-
|
|
- return Token('placeholder', {'tabstop': tabstop, 'default': default})
|
|
-
|
|
- def _match_shell(self):
|
|
- text = self.remains()
|
|
- match = re.match('`((%s):)?((\\\\`|[^`])+?)`' % self.SREG_ID, text) or re.match('\\$\\(((%s):)?((\\\\\\)|[^\\)])+?)\\)' % self.SREG_ID, text)
|
|
-
|
|
- if not match:
|
|
- return None
|
|
-
|
|
- groups = match.groups()
|
|
- tabstop = (groups[1] and int(groups[1])) or -1
|
|
- self.position += len(match.group(0))
|
|
-
|
|
- if text[0] == '`':
|
|
- contents = groups[2].replace('\\`', '`')
|
|
- else:
|
|
- contents = groups[2].replace('\\)', ')')
|
|
-
|
|
- return Token('shell', {'tabstop': tabstop, 'contents': contents})
|
|
-
|
|
- def _try_match_shell(self):
|
|
- return self._match_shell() or self._text()
|
|
-
|
|
- def _eval_options(self, options):
|
|
- reg = re.compile(self.SREG_ID)
|
|
- tabstop = -1
|
|
- depend = []
|
|
-
|
|
- options = options.split(':')
|
|
-
|
|
- for opt in options:
|
|
- if reg.match(opt):
|
|
- tabstop = int(opt)
|
|
- else:
|
|
- depend += self._parse_list(opt[1:-1])
|
|
-
|
|
- return (tabstop, depend)
|
|
-
|
|
- def _match_eval(self):
|
|
- text = self.remains()
|
|
-
|
|
- options = '((%s)|\\[([0-9, ]+)\\])' % self.SREG_ID
|
|
- match = re.match('\\$<((%s:)*)((\\\\>|[^>])+?)>' % options, text)
|
|
-
|
|
- if not match:
|
|
- return None
|
|
-
|
|
- groups = match.groups()
|
|
- (tabstop, depend) = (groups[0] and self._eval_options(groups[0][:-1])) or (-1, [])
|
|
- self.position += len(match.group(0))
|
|
-
|
|
- return Token('eval', {'tabstop': tabstop, 'dependencies': depend, 'contents': groups[5].replace('\\>', '>')})
|
|
-
|
|
- def _match_regex(self):
|
|
- text = self.remains()
|
|
-
|
|
- content = '((?:\\\\[/]|\\\\}|[^/}])+)'
|
|
- match = re.match('\\${(?:(%s):)?\\s*(%s|\\$([A-Z_]+))?[/]%s[/]%s(?:[/]([a-zA-Z]*))?}' % (self.SREG_ID, self.SREG_ID, content, content), text)
|
|
-
|
|
- if not match:
|
|
- return None
|
|
-
|
|
- groups = match.groups()
|
|
- tabstop = (groups[0] and int(groups[0])) or -1
|
|
- inp = (groups[2] or (groups[1] and int(groups[1]))) or ''
|
|
-
|
|
- pattern = re.sub('\\\\([/}])', '\\1', groups[3])
|
|
- substitution = re.sub('\\\\([/}])', '\\1', groups[4])
|
|
- modifiers = groups[5] or ''
|
|
-
|
|
- self.position += len(match.group(0))
|
|
-
|
|
- return Token('regex', {'tabstop': tabstop, 'input': inp, 'pattern': pattern, 'substitution': substitution, 'modifiers': modifiers})
|
|
-
|
|
-# ex:ts=8:et:
|
|
+ items.append(last)
|
|
+
|
|
+ last = None
|
|
+ pos += 1
|
|
+ continue
|
|
+
|
|
+ last = (last != None and last + char) or char
|
|
+ pos += 1
|
|
+
|
|
+ if last != None:
|
|
+ items.append(last)
|
|
+
|
|
+ return items
|
|
+
|
|
+ def _parse_default(self, default):
|
|
+ match = re.match('^\\s*(\\\\)?(\\[((\\\\]|[^\\]])+)\\]\\s*)$', default)
|
|
+
|
|
+ if not match:
|
|
+ return [default]
|
|
+
|
|
+ groups = match.groups()
|
|
+
|
|
+ if groups[0]:
|
|
+ return [groups[1]]
|
|
+
|
|
+ return self._parse_list(groups[2])
|
|
+
|
|
+ def _match_placeholder(self):
|
|
+ text = self.remains()
|
|
+
|
|
+ match = re.match('\\${(%s)(:((\\\\\\}|[^}])+))?}' % self.SREG_ID, text) or re.match('\\$(%s)' % self.SREG_ID, text)
|
|
+
|
|
+ if not match:
|
|
+ return None
|
|
+
|
|
+ groups = match.groups()
|
|
+ default = ''
|
|
+ tabstop = int(groups[0])
|
|
+ self.position += len(match.group(0))
|
|
+
|
|
+ if len(groups) > 1 and groups[2]:
|
|
+ default = self._parse_default(groups[2].replace('\\}', '}'))
|
|
+
|
|
+ return Token('placeholder', {'tabstop': tabstop, 'default': default})
|
|
+
|
|
+ def _match_shell(self):
|
|
+ text = self.remains()
|
|
+ match = re.match('`((%s):)?((\\\\`|[^`])+?)`' % self.SREG_ID, text) or re.match('\\$\\(((%s):)?((\\\\\\)|[^\\)])+?)\\)' % self.SREG_ID, text)
|
|
+
|
|
+ if not match:
|
|
+ return None
|
|
+
|
|
+ groups = match.groups()
|
|
+ tabstop = (groups[1] and int(groups[1])) or -1
|
|
+ self.position += len(match.group(0))
|
|
+
|
|
+ if text[0] == '`':
|
|
+ contents = groups[2].replace('\\`', '`')
|
|
+ else:
|
|
+ contents = groups[2].replace('\\)', ')')
|
|
+
|
|
+ return Token('shell', {'tabstop': tabstop, 'contents': contents})
|
|
+
|
|
+ def _try_match_shell(self):
|
|
+ return self._match_shell() or self._text()
|
|
+
|
|
+ def _eval_options(self, options):
|
|
+ reg = re.compile(self.SREG_ID)
|
|
+ tabstop = -1
|
|
+ depend = []
|
|
+
|
|
+ options = options.split(':')
|
|
+
|
|
+ for opt in options:
|
|
+ if reg.match(opt):
|
|
+ tabstop = int(opt)
|
|
+ else:
|
|
+ depend += self._parse_list(opt[1:-1])
|
|
+
|
|
+ return (tabstop, depend)
|
|
+
|
|
+ def _match_eval(self):
|
|
+ text = self.remains()
|
|
+
|
|
+ options = '((%s)|\\[([0-9, ]+)\\])' % self.SREG_ID
|
|
+ match = re.match('\\$<((%s:)*)((\\\\>|[^>])+?)>' % options, text)
|
|
+
|
|
+ if not match:
|
|
+ return None
|
|
+
|
|
+ groups = match.groups()
|
|
+ (tabstop, depend) = (groups[0] and self._eval_options(groups[0][:-1])) or (-1, [])
|
|
+ self.position += len(match.group(0))
|
|
+
|
|
+ return Token('eval', {'tabstop': tabstop, 'dependencies': depend, 'contents': groups[5].replace('\\>', '>')})
|
|
+
|
|
+ def _match_regex(self):
|
|
+ text = self.remains()
|
|
+
|
|
+ content = '((?:\\\\[/]|\\\\}|[^/}])+)'
|
|
+ match = re.match('\\${(?:(%s):)?\\s*(%s|\\$([A-Z_]+))?[/]%s[/]%s(?:[/]([a-zA-Z]*))?}' % (self.SREG_ID, self.SREG_ID, content, content), text)
|
|
+
|
|
+ if not match:
|
|
+ return None
|
|
+
|
|
+ groups = match.groups()
|
|
+ tabstop = (groups[0] and int(groups[0])) or -1
|
|
+ inp = (groups[2] or (groups[1] and int(groups[1]))) or ''
|
|
+
|
|
+ pattern = re.sub('\\\\([/}])', '\\1', groups[3])
|
|
+ substitution = re.sub('\\\\([/}])', '\\1', groups[4])
|
|
+ modifiers = groups[5] or ''
|
|
+
|
|
+ self.position += len(match.group(0))
|
|
+
|
|
+ return Token('regex', {'tabstop': tabstop, 'input': inp, 'pattern': pattern, 'substitution': substitution, 'modifiers': modifiers})
|
|
+
|
|
+# ex:ts=4:et:
|
|
diff --git a/plugins/snippets/snippets/Placeholder.py b/plugins/snippets/snippets/Placeholder.py
|
|
old mode 100755
|
|
new mode 100644
|
|
index 5fa6e55..9edf099
|
|
--- a/plugins/snippets/snippets/Placeholder.py
|
|
+++ b/plugins/snippets/snippets/Placeholder.py
|
|
@@ -29,671 +29,671 @@ 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):
|
|
- self.ok = True
|
|
- self.done = False
|
|
- self.buf = view.get_buffer()
|
|
- self.view = view
|
|
- self.has_references = False
|
|
- self.mirrors = []
|
|
- self.leave_mirrors = []
|
|
- self.tabstop = tabstop
|
|
- self.set_default(defaults)
|
|
- self.prev_contents = self.default
|
|
- self.set_mark_gravity()
|
|
-
|
|
- if begin:
|
|
- self.begin = self.buf.create_mark(None, begin, self.mark_gravity[0])
|
|
- else:
|
|
- self.begin = None
|
|
-
|
|
- self.end = None
|
|
-
|
|
- def __str__(self):
|
|
- return '%s (%s)' % (str(self.__class__), str(self.default))
|
|
-
|
|
- def set_mark_gravity(self):
|
|
- self.mark_gravity = [True, False]
|
|
-
|
|
- def set_default(self, defaults):
|
|
- self.default = None
|
|
- self.defaults = []
|
|
-
|
|
- if not defaults:
|
|
- return
|
|
-
|
|
- for d in defaults:
|
|
- dm = self.expand_environment(d)
|
|
-
|
|
- if dm:
|
|
- self.defaults.append(dm)
|
|
-
|
|
- if not self.default:
|
|
- self.default = dm
|
|
-
|
|
- if dm != d:
|
|
- break
|
|
-
|
|
-
|
|
- def literal(self, s):
|
|
- return repr(s)
|
|
-
|
|
- def format_environment(self, s):
|
|
- return s
|
|
-
|
|
- def re_environment(self, m):
|
|
- if m.group(1) or not m.group(2) in os.environ:
|
|
- return '$' + m.group(2)
|
|
- else:
|
|
- return self.format_environment(os.environ[m.group(2)])
|
|
-
|
|
- def expand_environment(self, text):
|
|
- if not text:
|
|
- return text
|
|
-
|
|
- return re.sub('(\\\\)?\\$([A-Z_]+)', self.re_environment, text)
|
|
-
|
|
- def get_iter(self, mark):
|
|
- if mark and not mark.get_deleted():
|
|
- return self.buf.get_iter_at_mark(mark)
|
|
- else:
|
|
- return None
|
|
-
|
|
- def begin_iter(self):
|
|
- return self.get_iter(self.begin)
|
|
-
|
|
- def end_iter(self):
|
|
- return self.get_iter(self.end)
|
|
-
|
|
- def run_last(self, placeholders):
|
|
- begin = self.begin_iter()
|
|
- self.end = self.buf.create_mark(None, begin, self.mark_gravity[1])
|
|
-
|
|
- if self.default:
|
|
- insert_with_indent(self.view, begin, self.default, False, self)
|
|
-
|
|
- def remove(self, force = False):
|
|
- if self.begin and not self.begin.get_deleted():
|
|
- self.buf.delete_mark(self.begin)
|
|
-
|
|
- if self.end and not self.end.get_deleted():
|
|
- self.buf.delete_mark(self.end)
|
|
-
|
|
- # Do something on beginning this placeholder
|
|
- def enter(self):
|
|
- if not self.begin or self.begin.get_deleted():
|
|
- return
|
|
-
|
|
- self.buf.move_mark(self.buf.get_insert(), self.begin_iter())
|
|
-
|
|
- if self.end:
|
|
- self.buf.move_mark(self.buf.get_selection_bound(), self.end_iter())
|
|
- else:
|
|
- self.buf.move_mark(self.buf.get_selection_bound(), self.begin_iter())
|
|
-
|
|
- def get_text(self):
|
|
- if self.begin and self.end:
|
|
- biter = self.begin_iter()
|
|
- eiter = self.end_iter()
|
|
-
|
|
- if biter and eiter:
|
|
- return self.buf.get_text(self.begin_iter(), self.end_iter(), False)
|
|
- else:
|
|
- return ''
|
|
- else:
|
|
- return ''
|
|
-
|
|
- def add_mirror(self, mirror, onleave = False):
|
|
- mirror.has_references = True
|
|
-
|
|
- if onleave:
|
|
- self.leave_mirrors.append(mirror)
|
|
- else:
|
|
- self.mirrors.append(mirror)
|
|
-
|
|
- def set_text(self, text):
|
|
- if self.begin.get_deleted() or self.end.get_deleted():
|
|
- return
|
|
-
|
|
- # Set from self.begin to self.end to text!
|
|
- self.buf.begin_user_action()
|
|
- # Remove everything between self.begin and self.end
|
|
- begin = self.begin_iter()
|
|
- self.buf.delete(begin, self.end_iter())
|
|
-
|
|
- # Insert the text from the mirror
|
|
- insert_with_indent(self.view, begin, text, True, self)
|
|
- self.buf.end_user_action()
|
|
-
|
|
- self.update_contents()
|
|
-
|
|
- def update_contents(self):
|
|
- prev = self.prev_contents
|
|
- self.prev_contents = self.get_text()
|
|
-
|
|
- if prev != self.get_text():
|
|
- for mirror in self.mirrors:
|
|
- if not mirror.update(self):
|
|
- return
|
|
-
|
|
- def update_leave_mirrors(self):
|
|
- # Notify mirrors
|
|
- for mirror in self.leave_mirrors:
|
|
- if not mirror.update(self):
|
|
- return
|
|
-
|
|
- # Do something on ending this placeholder
|
|
- def leave(self):
|
|
- self.update_leave_mirrors()
|
|
-
|
|
- def find_mirrors(self, text, placeholders):
|
|
- mirrors = []
|
|
-
|
|
- while (True):
|
|
- m = re.search('(\\\\)?\\$(?:{([0-9]+)}|([0-9]+))', text)
|
|
-
|
|
- if not m:
|
|
- break
|
|
-
|
|
- # Skip escaped mirrors
|
|
- if m.group(1):
|
|
- text = text[m.end():]
|
|
- continue
|
|
-
|
|
- tabstop = int(m.group(2) or m.group(3))
|
|
-
|
|
- if tabstop in placeholders:
|
|
- if not tabstop in mirrors:
|
|
- mirrors.append(tabstop)
|
|
-
|
|
- text = text[m.end():]
|
|
- else:
|
|
- self.ok = False
|
|
- return None
|
|
-
|
|
- return mirrors
|
|
-
|
|
-# This is an placeholder which inserts a mirror of another Placeholder
|
|
+ def __init__(self, view, tabstop, defaults, begin):
|
|
+ self.ok = True
|
|
+ self.done = False
|
|
+ self.buf = view.get_buffer()
|
|
+ self.view = view
|
|
+ self.has_references = False
|
|
+ self.mirrors = []
|
|
+ self.leave_mirrors = []
|
|
+ self.tabstop = tabstop
|
|
+ self.set_default(defaults)
|
|
+ self.prev_contents = self.default
|
|
+ self.set_mark_gravity()
|
|
+
|
|
+ if begin:
|
|
+ self.begin = self.buf.create_mark(None, begin, self.mark_gravity[0])
|
|
+ else:
|
|
+ self.begin = None
|
|
+
|
|
+ self.end = None
|
|
+
|
|
+ def __str__(self):
|
|
+ return '%s (%s)' % (str(self.__class__), str(self.default))
|
|
+
|
|
+ def set_mark_gravity(self):
|
|
+ self.mark_gravity = [True, False]
|
|
+
|
|
+ def set_default(self, defaults):
|
|
+ self.default = None
|
|
+ self.defaults = []
|
|
+
|
|
+ if not defaults:
|
|
+ return
|
|
+
|
|
+ for d in defaults:
|
|
+ dm = self.expand_environment(d)
|
|
+
|
|
+ if dm:
|
|
+ self.defaults.append(dm)
|
|
+
|
|
+ if not self.default:
|
|
+ self.default = dm
|
|
+
|
|
+ if dm != d:
|
|
+ break
|
|
+
|
|
+ def literal(self, s):
|
|
+ return repr(s)
|
|
+
|
|
+ def format_environment(self, s):
|
|
+ return s
|
|
+
|
|
+ def re_environment(self, m):
|
|
+ if m.group(1) or not m.group(2) in os.environ:
|
|
+ return '$' + m.group(2)
|
|
+ else:
|
|
+ return self.format_environment(os.environ[m.group(2)])
|
|
+
|
|
+ def expand_environment(self, text):
|
|
+ if not text:
|
|
+ return text
|
|
+
|
|
+ return re.sub('(\\\\)?\\$([A-Z_]+)', self.re_environment, text)
|
|
+
|
|
+ def get_iter(self, mark):
|
|
+ if mark and not mark.get_deleted():
|
|
+ return self.buf.get_iter_at_mark(mark)
|
|
+ else:
|
|
+ return None
|
|
+
|
|
+ def begin_iter(self):
|
|
+ return self.get_iter(self.begin)
|
|
+
|
|
+ def end_iter(self):
|
|
+ return self.get_iter(self.end)
|
|
+
|
|
+ def run_last(self, placeholders):
|
|
+ begin = self.begin_iter()
|
|
+ self.end = self.buf.create_mark(None, begin, self.mark_gravity[1])
|
|
+
|
|
+ if self.default:
|
|
+ insert_with_indent(self.view, begin, self.default, False, self)
|
|
+
|
|
+ def remove(self, force = False):
|
|
+ if self.begin and not self.begin.get_deleted():
|
|
+ self.buf.delete_mark(self.begin)
|
|
+
|
|
+ if self.end and not self.end.get_deleted():
|
|
+ self.buf.delete_mark(self.end)
|
|
+
|
|
+ # Do something on beginning this placeholder
|
|
+ def enter(self):
|
|
+ if not self.begin or self.begin.get_deleted():
|
|
+ return
|
|
+
|
|
+ self.buf.move_mark(self.buf.get_insert(), self.begin_iter())
|
|
+
|
|
+ if self.end:
|
|
+ self.buf.move_mark(self.buf.get_selection_bound(), self.end_iter())
|
|
+ else:
|
|
+ self.buf.move_mark(self.buf.get_selection_bound(), self.begin_iter())
|
|
+
|
|
+ def get_text(self):
|
|
+ if self.begin and self.end:
|
|
+ biter = self.begin_iter()
|
|
+ eiter = self.end_iter()
|
|
+
|
|
+ if biter and eiter:
|
|
+ return self.buf.get_text(self.begin_iter(), self.end_iter(), False)
|
|
+ else:
|
|
+ return ''
|
|
+ else:
|
|
+ return ''
|
|
+
|
|
+ def add_mirror(self, mirror, onleave = False):
|
|
+ mirror.has_references = True
|
|
+
|
|
+ if onleave:
|
|
+ self.leave_mirrors.append(mirror)
|
|
+ else:
|
|
+ self.mirrors.append(mirror)
|
|
+
|
|
+ def set_text(self, text):
|
|
+ if self.begin.get_deleted() or self.end.get_deleted():
|
|
+ return
|
|
+
|
|
+ # Set from self.begin to self.end to text!
|
|
+ self.buf.begin_user_action()
|
|
+ # Remove everything between self.begin and self.end
|
|
+ begin = self.begin_iter()
|
|
+ self.buf.delete(begin, self.end_iter())
|
|
+
|
|
+ # Insert the text from the mirror
|
|
+ insert_with_indent(self.view, begin, text, True, self)
|
|
+ self.buf.end_user_action()
|
|
+
|
|
+ self.update_contents()
|
|
+
|
|
+ def update_contents(self):
|
|
+ prev = self.prev_contents
|
|
+ self.prev_contents = self.get_text()
|
|
+
|
|
+ if prev != self.get_text():
|
|
+ for mirror in self.mirrors:
|
|
+ if not mirror.update(self):
|
|
+ return
|
|
+
|
|
+ def update_leave_mirrors(self):
|
|
+ # Notify mirrors
|
|
+ for mirror in self.leave_mirrors:
|
|
+ if not mirror.update(self):
|
|
+ return
|
|
+
|
|
+ # Do something on ending this placeholder
|
|
+ def leave(self):
|
|
+ self.update_leave_mirrors()
|
|
+
|
|
+ def find_mirrors(self, text, placeholders):
|
|
+ mirrors = []
|
|
+
|
|
+ while (True):
|
|
+ m = re.search('(\\\\)?\\$(?:{([0-9]+)}|([0-9]+))', text)
|
|
+
|
|
+ if not m:
|
|
+ break
|
|
+
|
|
+ # Skip escaped mirrors
|
|
+ if m.group(1):
|
|
+ text = text[m.end():]
|
|
+ continue
|
|
+
|
|
+ tabstop = int(m.group(2) or m.group(3))
|
|
+
|
|
+ if tabstop in placeholders:
|
|
+ if not tabstop in mirrors:
|
|
+ mirrors.append(tabstop)
|
|
+
|
|
+ text = text[m.end():]
|
|
+ else:
|
|
+ self.ok = False
|
|
+ return None
|
|
+
|
|
+ return mirrors
|
|
+
|
|
+# 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)
|
|
- self.mirror_stop = tabstop
|
|
-
|
|
- def update(self, mirror):
|
|
- self.set_text(mirror.get_text())
|
|
- return True
|
|
-
|
|
- def run_last(self, placeholders):
|
|
- Placeholder.run_last(self, placeholders)
|
|
-
|
|
- if self.mirror_stop in placeholders:
|
|
- mirror = placeholders[self.mirror_stop]
|
|
-
|
|
- mirror.add_mirror(self)
|
|
-
|
|
- if mirror.default:
|
|
- self.set_text(mirror.default)
|
|
- else:
|
|
- self.ok = False
|
|
+ def __init__(self, view, tabstop, begin):
|
|
+ Placeholder.__init__(self, view, -1, None, begin)
|
|
+ self.mirror_stop = tabstop
|
|
+
|
|
+ def update(self, mirror):
|
|
+ self.set_text(mirror.get_text())
|
|
+ return True
|
|
+
|
|
+ def run_last(self, placeholders):
|
|
+ Placeholder.run_last(self, placeholders)
|
|
+
|
|
+ if self.mirror_stop in placeholders:
|
|
+ mirror = placeholders[self.mirror_stop]
|
|
+
|
|
+ mirror.add_mirror(self)
|
|
+
|
|
+ if mirror.default:
|
|
+ self.set_text(mirror.default)
|
|
+ else:
|
|
+ self.ok = False
|
|
|
|
# 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 run_last(self, placeholders):
|
|
- Placeholder.run_last(self, placeholders)
|
|
-
|
|
- # Remove the begin mark and set the begin mark
|
|
- # to the end mark, this is needed so the end placeholder won't contain
|
|
- # any text
|
|
-
|
|
- if not self.default:
|
|
- self.mark_gravity[0] = False
|
|
- self.buf.delete_mark(self.begin)
|
|
- self.begin = self.buf.create_mark(None, self.end_iter(), self.mark_gravity[0])
|
|
-
|
|
- def enter(self):
|
|
- if self.begin and not self.begin.get_deleted():
|
|
- self.buf.move_mark(self.buf.get_insert(), self.begin_iter())
|
|
-
|
|
- if self.end and not self.end.get_deleted():
|
|
- self.buf.move_mark(self.buf.get_selection_bound(), self.end_iter())
|
|
-
|
|
- def leave(self):
|
|
- self.enter()
|
|
-
|
|
-# This placeholder is used to expand a command with embedded mirrors
|
|
+ def __init__(self, view, begin, default):
|
|
+ Placeholder.__init__(self, view, 0, default, begin)
|
|
+
|
|
+ def run_last(self, placeholders):
|
|
+ Placeholder.run_last(self, placeholders)
|
|
+
|
|
+ # Remove the begin mark and set the begin mark
|
|
+ # to the end mark, this is needed so the end placeholder won't contain
|
|
+ # any text
|
|
+
|
|
+ if not self.default:
|
|
+ self.mark_gravity[0] = False
|
|
+ self.buf.delete_mark(self.begin)
|
|
+ self.begin = self.buf.create_mark(None, self.end_iter(), self.mark_gravity[0])
|
|
+
|
|
+ def enter(self):
|
|
+ if self.begin and not self.begin.get_deleted():
|
|
+ self.buf.move_mark(self.buf.get_insert(), self.begin_iter())
|
|
+
|
|
+ if self.end and not self.end.get_deleted():
|
|
+ self.buf.move_mark(self.buf.get_selection_bound(), self.end_iter())
|
|
+
|
|
+ def leave(self):
|
|
+ self.enter()
|
|
+
|
|
+# 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)
|
|
-
|
|
- self.mirror_text = {0: ''}
|
|
- self.timeout_id = None
|
|
- self.cmd = s
|
|
- self.instant_update = False
|
|
-
|
|
- def __str__(self):
|
|
- s = Placeholder.__str__(self)
|
|
-
|
|
- return s + ' ' + self.cmd
|
|
-
|
|
- def get_mirrors(self, placeholders):
|
|
- return self.find_mirrors(self.cmd, placeholders)
|
|
-
|
|
- # Check if all substitution placeholders are accounted for
|
|
- def run_last(self, placeholders):
|
|
- Placeholder.run_last(self, placeholders)
|
|
-
|
|
- self.ok = True
|
|
- mirrors = self.get_mirrors(placeholders)
|
|
-
|
|
- if mirrors:
|
|
- allDefault = True
|
|
-
|
|
- for mirror in mirrors:
|
|
- p = placeholders[mirror]
|
|
- p.add_mirror(self, not self.instant_update)
|
|
- self.mirror_text[p.tabstop] = p.default
|
|
-
|
|
- if not p.default and not isinstance(p, PlaceholderExpand):
|
|
- allDefault = False
|
|
-
|
|
- if allDefault:
|
|
- self.update(None)
|
|
- self.default = self.get_text() or None
|
|
- else:
|
|
- self.update(None)
|
|
- self.default = self.get_text() or None
|
|
-
|
|
- if self.tabstop == -1:
|
|
- self.done = True
|
|
-
|
|
- def re_placeholder(self, m, formatter):
|
|
- if m.group(1):
|
|
- return '"$' + m.group(2) + '"'
|
|
- else:
|
|
- if m.group(3):
|
|
- index = int(m.group(3))
|
|
- else:
|
|
- index = int(m.group(4))
|
|
-
|
|
- return formatter(self.mirror_text[index])
|
|
-
|
|
- def remove_timeout(self):
|
|
- if self.timeout_id != None:
|
|
- GLib.source_remove(self.timeout_id)
|
|
- self.timeout_id = None
|
|
-
|
|
- def install_timeout(self):
|
|
- self.remove_timeout()
|
|
- self.timeout_id = GLib.timeout_add(1000, self.timeout_cb)
|
|
+ def __init__(self, view, tabstop, begin, s):
|
|
+ Placeholder.__init__(self, view, tabstop, None, begin)
|
|
|
|
- def timeout_cb(self):
|
|
- self.timeout_id = None
|
|
-
|
|
- return False
|
|
-
|
|
- def format_environment(self, text):
|
|
- return self.literal(text)
|
|
-
|
|
- def substitute(self, text, formatter = None):
|
|
- formatter = formatter or self.literal
|
|
-
|
|
- # substitute all mirrors, but also environmental variables
|
|
- text = re.sub('(\\\\)?\\$({([0-9]+)}|([0-9]+))', lambda m: self.re_placeholder(m, formatter),
|
|
- text)
|
|
-
|
|
- return self.expand_environment(text)
|
|
-
|
|
- def run_update(self):
|
|
- text = self.substitute(self.cmd)
|
|
-
|
|
- if text:
|
|
- ret = self.expand(text)
|
|
-
|
|
- if ret:
|
|
- self.update_leave_mirrors()
|
|
- else:
|
|
- ret = True
|
|
-
|
|
- return ret
|
|
-
|
|
- def update(self, mirror):
|
|
- text = None
|
|
-
|
|
- if mirror:
|
|
- self.mirror_text[mirror.tabstop] = mirror.get_text()
|
|
-
|
|
- # Check if all substitutions have been made
|
|
- for tabstop in self.mirror_text:
|
|
- if tabstop == 0:
|
|
- continue
|
|
-
|
|
- if self.mirror_text[tabstop] == None:
|
|
- return False
|
|
-
|
|
- return self.run_update()
|
|
-
|
|
- def expand(self, text):
|
|
- return True
|
|
+ self.mirror_text = {0: ''}
|
|
+ self.timeout_id = None
|
|
+ self.cmd = s
|
|
+ self.instant_update = False
|
|
+
|
|
+ def __str__(self):
|
|
+ s = Placeholder.__str__(self)
|
|
+
|
|
+ return s + ' ' + self.cmd
|
|
+
|
|
+ def get_mirrors(self, placeholders):
|
|
+ return self.find_mirrors(self.cmd, placeholders)
|
|
+
|
|
+ # Check if all substitution placeholders are accounted for
|
|
+ def run_last(self, placeholders):
|
|
+ Placeholder.run_last(self, placeholders)
|
|
+
|
|
+ self.ok = True
|
|
+ mirrors = self.get_mirrors(placeholders)
|
|
+
|
|
+ if mirrors:
|
|
+ allDefault = True
|
|
+
|
|
+ for mirror in mirrors:
|
|
+ p = placeholders[mirror]
|
|
+ p.add_mirror(self, not self.instant_update)
|
|
+ self.mirror_text[p.tabstop] = p.default
|
|
+
|
|
+ if not p.default and not isinstance(p, PlaceholderExpand):
|
|
+ allDefault = False
|
|
+
|
|
+ if allDefault:
|
|
+ self.update(None)
|
|
+ self.default = self.get_text() or None
|
|
+ else:
|
|
+ self.update(None)
|
|
+ self.default = self.get_text() or None
|
|
+
|
|
+ if self.tabstop == -1:
|
|
+ self.done = True
|
|
+
|
|
+ def re_placeholder(self, m, formatter):
|
|
+ if m.group(1):
|
|
+ return '"$' + m.group(2) + '"'
|
|
+ else:
|
|
+ if m.group(3):
|
|
+ index = int(m.group(3))
|
|
+ else:
|
|
+ index = int(m.group(4))
|
|
+
|
|
+ return formatter(self.mirror_text[index])
|
|
+
|
|
+ def remove_timeout(self):
|
|
+ if self.timeout_id != None:
|
|
+ GLib.source_remove(self.timeout_id)
|
|
+ self.timeout_id = None
|
|
+
|
|
+ def install_timeout(self):
|
|
+ self.remove_timeout()
|
|
+ self.timeout_id = GLib.timeout_add(1000, self.timeout_cb)
|
|
+
|
|
+ def timeout_cb(self):
|
|
+ self.timeout_id = None
|
|
+
|
|
+ return False
|
|
+
|
|
+ def format_environment(self, text):
|
|
+ return self.literal(text)
|
|
+
|
|
+ def substitute(self, text, formatter = None):
|
|
+ formatter = formatter or self.literal
|
|
+
|
|
+ # substitute all mirrors, but also environmental variables
|
|
+ text = re.sub('(\\\\)?\\$({([0-9]+)}|([0-9]+))', lambda m: self.re_placeholder(m, formatter),
|
|
+ text)
|
|
+
|
|
+ return self.expand_environment(text)
|
|
+
|
|
+ def run_update(self):
|
|
+ text = self.substitute(self.cmd)
|
|
+
|
|
+ if text:
|
|
+ ret = self.expand(text)
|
|
+
|
|
+ if ret:
|
|
+ self.update_leave_mirrors()
|
|
+ else:
|
|
+ ret = True
|
|
+
|
|
+ return ret
|
|
+
|
|
+ def update(self, mirror):
|
|
+ text = None
|
|
+
|
|
+ if mirror:
|
|
+ self.mirror_text[mirror.tabstop] = mirror.get_text()
|
|
+
|
|
+ # Check if all substitutions have been made
|
|
+ for tabstop in self.mirror_text:
|
|
+ if tabstop == 0:
|
|
+ continue
|
|
+
|
|
+ if self.mirror_text[tabstop] == None:
|
|
+ return False
|
|
+
|
|
+ return self.run_update()
|
|
+
|
|
+ def expand(self, text):
|
|
+ return True
|
|
|
|
# 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)
|
|
-
|
|
- self.shell = None
|
|
- self.remove_me = False
|
|
-
|
|
- def close_shell(self):
|
|
- self.shell.stdout.close()
|
|
- self.shell = None
|
|
-
|
|
- def timeout_cb(self):
|
|
- PlaceholderExpand.timeout_cb(self)
|
|
- self.remove_timeout()
|
|
-
|
|
- if not self.shell:
|
|
- return False
|
|
+ def __init__(self, view, tabstop, begin, s):
|
|
+ PlaceholderExpand.__init__(self, view, tabstop, begin, s)
|
|
|
|
- GLib.source_remove(self.watch_id)
|
|
- self.close_shell()
|
|
+ self.shell = None
|
|
+ self.remove_me = False
|
|
|
|
- if self.remove_me:
|
|
- PlaceholderExpand.remove(self)
|
|
+ def close_shell(self):
|
|
+ self.shell.stdout.close()
|
|
+ self.shell = None
|
|
|
|
- message_dialog(None, Gtk.MessageType.ERROR, 'Execution of the shell ' \
|
|
- 'command (%s) exceeded the maximum time; ' \
|
|
- 'execution aborted.' % self.command)
|
|
-
|
|
- return False
|
|
-
|
|
- def process_close(self):
|
|
- self.close_shell()
|
|
- self.remove_timeout()
|
|
+ def timeout_cb(self):
|
|
+ PlaceholderExpand.timeout_cb(self)
|
|
+ self.remove_timeout()
|
|
|
|
- self.set_text(str.join('', self.shell_output).rstrip('\n'))
|
|
-
|
|
- if self.default == None:
|
|
- self.default = self.get_text()
|
|
- self.leave()
|
|
-
|
|
- if self.remove_me:
|
|
- PlaceholderExpand.remove(self, True)
|
|
-
|
|
- def process_cb(self, source, condition):
|
|
- if condition & GObject.IO_IN:
|
|
- line = source.readline()
|
|
-
|
|
- if len(line) > 0:
|
|
- try:
|
|
- line = unicode(line, 'utf-8')
|
|
- except:
|
|
- line = unicode(line, locale.getdefaultlocale()[1],
|
|
- 'replace')
|
|
-
|
|
- self.shell_output += line
|
|
- self.install_timeout()
|
|
-
|
|
- return True
|
|
-
|
|
- self.process_close()
|
|
- return False
|
|
-
|
|
- def literal_replace(self, match):
|
|
- return "\\%s" % (match.group(0))
|
|
-
|
|
- def literal(self, text):
|
|
- return '"' + re.sub('([\\\\"])', self.literal_replace, text) + '"'
|
|
-
|
|
- def expand(self, text):
|
|
+ if not self.shell:
|
|
+ return False
|
|
+
|
|
+ GLib.source_remove(self.watch_id)
|
|
+ self.close_shell()
|
|
+
|
|
+ if self.remove_me:
|
|
+ PlaceholderExpand.remove(self)
|
|
+
|
|
+ message_dialog(None, Gtk.MessageType.ERROR, 'Execution of the shell ' \
|
|
+ 'command (%s) exceeded the maximum time; ' \
|
|
+ 'execution aborted.' % self.command)
|
|
+
|
|
+ return False
|
|
+
|
|
+ def process_close(self):
|
|
+ self.close_shell()
|
|
+ self.remove_timeout()
|
|
+
|
|
+ self.set_text(str.join('', self.shell_output).rstrip('\n'))
|
|
+
|
|
+ if self.default == None:
|
|
+ self.default = self.get_text()
|
|
+ self.leave()
|
|
+
|
|
+ if self.remove_me:
|
|
+ PlaceholderExpand.remove(self, True)
|
|
+
|
|
+ def process_cb(self, source, condition):
|
|
+ if condition & GObject.IO_IN:
|
|
+ line = source.readline()
|
|
+
|
|
+ if len(line) > 0:
|
|
+ try:
|
|
+ line = unicode(line, 'utf-8')
|
|
+ except:
|
|
+ line = unicode(line, locale.getdefaultlocale()[1],
|
|
+ 'replace')
|
|
+
|
|
+ self.shell_output += line
|
|
+ self.install_timeout()
|
|
+
|
|
+ return True
|
|
+
|
|
+ self.process_close()
|
|
+ return False
|
|
+
|
|
+ def literal_replace(self, match):
|
|
+ return "\\%s" % (match.group(0))
|
|
+
|
|
+ def literal(self, text):
|
|
+ return '"' + re.sub('([\\\\"])', self.literal_replace, text) + '"'
|
|
+
|
|
+ def expand(self, text):
|
|
+ self.remove_timeout()
|
|
+
|
|
+ if self.shell:
|
|
+ GLib.source_remove(self.watch_id)
|
|
+ self.close_shell()
|
|
+
|
|
+ popen_args = {
|
|
+ 'cwd' : None,
|
|
+ 'shell': True,
|
|
+ 'env' : os.environ,
|
|
+ 'stdout': subprocess.PIPE
|
|
+ }
|
|
+
|
|
+ self.command = text
|
|
+ self.shell = subprocess.Popen(text, **popen_args)
|
|
+ self.shell_output = ''
|
|
+ self.watch_id = GLib.io_add_watch(self.shell.stdout, GObject.IO_IN | \
|
|
+ GObject.IO_HUP, self.process_cb)
|
|
+ self.install_timeout()
|
|
+
|
|
+ return True
|
|
+
|
|
+ def remove(self, force = False):
|
|
+ if not force and self.shell:
|
|
+ # Still executing shell command
|
|
+ self.remove_me = True
|
|
+ else:
|
|
+ if force:
|
|
self.remove_timeout()
|
|
|
|
if self.shell:
|
|
- GLib.source_remove(self.watch_id)
|
|
- self.close_shell()
|
|
-
|
|
- popen_args = {
|
|
- 'cwd' : None,
|
|
- 'shell': True,
|
|
- 'env' : os.environ,
|
|
- 'stdout': subprocess.PIPE
|
|
- }
|
|
-
|
|
- self.command = text
|
|
- self.shell = subprocess.Popen(text, **popen_args)
|
|
- self.shell_output = ''
|
|
- self.watch_id = GLib.io_add_watch(self.shell.stdout, GObject.IO_IN | \
|
|
- GObject.IO_HUP, self.process_cb)
|
|
- self.install_timeout()
|
|
-
|
|
- return True
|
|
-
|
|
- def remove(self, force = False):
|
|
- if not force and self.shell:
|
|
- # Still executing shell command
|
|
- self.remove_me = True
|
|
- else:
|
|
- if force:
|
|
- self.remove_timeout()
|
|
-
|
|
- if self.shell:
|
|
- self.close_shell()
|
|
-
|
|
- PlaceholderExpand.remove(self, force)
|
|
+ self.close_shell()
|
|
+
|
|
+ PlaceholderExpand.remove(self, force)
|
|
|
|
class TimeoutError(Exception):
|
|
- def __init__(self, value):
|
|
- self.value = value
|
|
-
|
|
- def __str__(self):
|
|
- return repr(self.value)
|
|
+ def __init__(self, value):
|
|
+ self.value = value
|
|
+
|
|
+ def __str__(self):
|
|
+ return repr(self.value)
|
|
|
|
# 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)
|
|
-
|
|
- self.fdread = 0
|
|
- self.remove_me = False
|
|
- self.namespace = namespace
|
|
-
|
|
- self.refs = []
|
|
-
|
|
- if refs:
|
|
- for ref in refs:
|
|
- self.refs.append(int(ref.strip()))
|
|
-
|
|
- def get_mirrors(self, placeholders):
|
|
- mirrors = PlaceholderExpand.get_mirrors(self, placeholders)
|
|
-
|
|
- if not self.ok:
|
|
- return None
|
|
-
|
|
- for ref in self.refs:
|
|
- if ref in placeholders:
|
|
- if ref not in mirrors:
|
|
- mirrors.append(ref)
|
|
- else:
|
|
- self.ok = False
|
|
- return None
|
|
-
|
|
- return mirrors
|
|
-
|
|
- # SIGALRM is not supported on all platforms (e.g. windows). Timeout
|
|
- # with SIGALRM will not be used on those platforms. This will
|
|
- # potentially block pluma if you have a placeholder which gets stuck,
|
|
- # but it's better than not supporting them at all. At some point we
|
|
- # might have proper thread support and we can fix this in a better way
|
|
- def timeout_supported(self):
|
|
- return hasattr(signal, 'SIGALRM')
|
|
-
|
|
- def timeout_cb(self, signum = 0, frame = 0):
|
|
- raise TimeoutError, "Operation timed out (>2 seconds)"
|
|
-
|
|
- def install_timeout(self):
|
|
- if not self.timeout_supported():
|
|
- return
|
|
-
|
|
- if self.timeout_id != None:
|
|
- self.remove_timeout()
|
|
-
|
|
- self.timeout_id = signal.signal(signal.SIGALRM, self.timeout_cb)
|
|
- signal.alarm(2)
|
|
-
|
|
- def remove_timeout(self):
|
|
- if not self.timeout_supported():
|
|
- return
|
|
-
|
|
- if self.timeout_id != None:
|
|
- signal.alarm(0)
|
|
-
|
|
- signal.signal(signal.SIGALRM, self.timeout_id)
|
|
-
|
|
- self.timeout_id = None
|
|
-
|
|
- def expand(self, text):
|
|
+ def __init__(self, view, tabstop, refs, begin, s, namespace):
|
|
+ PlaceholderExpand.__init__(self, view, tabstop, begin, s)
|
|
+
|
|
+ self.fdread = 0
|
|
+ self.remove_me = False
|
|
+ self.namespace = namespace
|
|
+
|
|
+ self.refs = []
|
|
+
|
|
+ if refs:
|
|
+ for ref in refs:
|
|
+ self.refs.append(int(ref.strip()))
|
|
+
|
|
+ def get_mirrors(self, placeholders):
|
|
+ mirrors = PlaceholderExpand.get_mirrors(self, placeholders)
|
|
+
|
|
+ if not self.ok:
|
|
+ return None
|
|
+
|
|
+ for ref in self.refs:
|
|
+ if ref in placeholders:
|
|
+ if ref not in mirrors:
|
|
+ mirrors.append(ref)
|
|
+ else:
|
|
+ self.ok = False
|
|
+ return None
|
|
+
|
|
+ return mirrors
|
|
+
|
|
+ # SIGALRM is not supported on all platforms (e.g. windows). Timeout
|
|
+ # with SIGALRM will not be used on those platforms. This will
|
|
+ # potentially block pluma if you have a placeholder which gets stuck,
|
|
+ # but it's better than not supporting them at all. At some point we
|
|
+ # might have proper thread support and we can fix this in a better way
|
|
+ def timeout_supported(self):
|
|
+ return hasattr(signal, 'SIGALRM')
|
|
+
|
|
+ def timeout_cb(self, signum = 0, frame = 0):
|
|
+ raise TimeoutError, "Operation timed out (>2 seconds)"
|
|
+
|
|
+ def install_timeout(self):
|
|
+ if not self.timeout_supported():
|
|
+ return
|
|
+
|
|
+ if self.timeout_id != None:
|
|
+ self.remove_timeout()
|
|
+
|
|
+ self.timeout_id = signal.signal(signal.SIGALRM, self.timeout_cb)
|
|
+ signal.alarm(2)
|
|
+
|
|
+ def remove_timeout(self):
|
|
+ if not self.timeout_supported():
|
|
+ return
|
|
+
|
|
+ if self.timeout_id != None:
|
|
+ signal.alarm(0)
|
|
+
|
|
+ signal.signal(signal.SIGALRM, self.timeout_id)
|
|
+
|
|
+ self.timeout_id = None
|
|
+
|
|
+ def expand(self, text):
|
|
+ self.remove_timeout()
|
|
+
|
|
+ text = text.strip()
|
|
+ self.command = text
|
|
+
|
|
+ if not self.command or self.command == '':
|
|
+ self.set_text('')
|
|
+ return
|
|
+
|
|
+ text = "def process_snippet():\n\t" + "\n\t".join(text.split("\n"))
|
|
+
|
|
+ if 'process_snippet' in self.namespace:
|
|
+ del self.namespace['process_snippet']
|
|
+
|
|
+ try:
|
|
+ exec text in self.namespace
|
|
+ except:
|
|
+ traceback.print_exc()
|
|
+
|
|
+ if 'process_snippet' in self.namespace:
|
|
+ try:
|
|
+ # Install a sigalarm signal. This is a HACK to make sure
|
|
+ # pluma doesn't get freezed by someone creating a python
|
|
+ # placeholder which for instance loops indefinately. Since
|
|
+ # the code is executed synchronously it will hang pluma. With
|
|
+ # the alarm signal we raise an exception and catch this
|
|
+ # (see below). We show an error message and return False.
|
|
+ # ___this is a HACK___ and should be fixed properly (I just
|
|
+ # don't know how)
|
|
+ self.install_timeout()
|
|
+ result = self.namespace['process_snippet']()
|
|
+ self.remove_timeout()
|
|
+ except TimeoutError:
|
|
self.remove_timeout()
|
|
|
|
- text = text.strip()
|
|
- self.command = text
|
|
+ message_dialog(None, Gtk.MessageType.ERROR, \
|
|
+ _('Execution of the Python command (%s) exceeds the maximum ' \
|
|
+ 'time, execution aborted.') % self.command)
|
|
|
|
- if not self.command or self.command == '':
|
|
- self.set_text('')
|
|
- return
|
|
+ return False
|
|
+ except Exception, detail:
|
|
+ self.remove_timeout()
|
|
|
|
- text = "def process_snippet():\n\t" + "\n\t".join(text.split("\n"))
|
|
-
|
|
- if 'process_snippet' in self.namespace:
|
|
- del self.namespace['process_snippet']
|
|
+ message_dialog(None, Gtk.MessageType.ERROR,
|
|
+ _('Execution of the Python command (%s) failed: %s') %
|
|
+ (self.command, detail))
|
|
|
|
- try:
|
|
- exec text in self.namespace
|
|
- except:
|
|
- traceback.print_exc()
|
|
-
|
|
- if 'process_snippet' in self.namespace:
|
|
- try:
|
|
- # Install a sigalarm signal. This is a HACK to make sure
|
|
- # pluma doesn't get freezed by someone creating a python
|
|
- # placeholder which for instance loops indefinately. Since
|
|
- # the code is executed synchronously it will hang pluma. With
|
|
- # the alarm signal we raise an exception and catch this
|
|
- # (see below). We show an error message and return False.
|
|
- # ___this is a HACK___ and should be fixed properly (I just
|
|
- # don't know how)
|
|
- self.install_timeout()
|
|
- result = self.namespace['process_snippet']()
|
|
- self.remove_timeout()
|
|
- except TimeoutError:
|
|
- self.remove_timeout()
|
|
-
|
|
- message_dialog(None, Gtk.MessageType.ERROR, \
|
|
- _('Execution of the Python command (%s) exceeds the maximum ' \
|
|
- 'time, execution aborted.') % self.command)
|
|
-
|
|
- return False
|
|
- except Exception, detail:
|
|
- self.remove_timeout()
|
|
-
|
|
- message_dialog(None, Gtk.MessageType.ERROR,
|
|
- _('Execution of the Python command (%s) failed: %s') %
|
|
- (self.command, detail))
|
|
-
|
|
- return False
|
|
-
|
|
- if result == None:
|
|
- # sys.stderr.write("%s:\n>> %s\n" % (_('The following python code, run in a snippet, does not return a value'), "\n>> ".join(self.command.split("\n"))))
|
|
- result = ''
|
|
-
|
|
- self.set_text(str(result))
|
|
-
|
|
- return True
|
|
+ return False
|
|
+
|
|
+ if result == None:
|
|
+ # sys.stderr.write("%s:\n>> %s\n" % (_('The following python code, run in a snippet, does not return a value'), "\n>> ".join(self.command.split("\n"))))
|
|
+ result = ''
|
|
+
|
|
+ self.set_text(str(result))
|
|
+
|
|
+ return True
|
|
|
|
# Regular expression placeholder
|
|
class PlaceholderRegex(PlaceholderExpand):
|
|
- def __init__(self, view, tabstop, begin, inp, pattern, substitution, modifiers):
|
|
- PlaceholderExpand.__init__(self, view, tabstop, begin, '')
|
|
-
|
|
- self.instant_update = True
|
|
- self.inp = inp
|
|
- self.pattern = pattern
|
|
- self.substitution = substitution
|
|
-
|
|
- self.init_modifiers(modifiers)
|
|
-
|
|
- def init_modifiers(self, modifiers):
|
|
- mods = {'I': re.I,
|
|
- 'L': re.L,
|
|
- 'M': re.M,
|
|
- 'S': re.S,
|
|
- 'U': re.U,
|
|
- 'X': re.X}
|
|
-
|
|
- self.modifiers = 0
|
|
-
|
|
- for modifier in modifiers:
|
|
- if modifier in mods:
|
|
- self.modifiers |= mods[modifier]
|
|
-
|
|
- def get_mirrors(self, placeholders):
|
|
- mirrors = self.find_mirrors(self.pattern, placeholders) + self.find_mirrors(self.substitution, placeholders)
|
|
-
|
|
- if isinstance(self.inp, int):
|
|
- if self.inp not in placeholders:
|
|
- self.ok = False
|
|
- return None
|
|
- elif self.inp not in mirrors:
|
|
- mirrors.append(self.inp)
|
|
-
|
|
- return mirrors
|
|
-
|
|
- def literal(self, s):
|
|
- return re.escape(s)
|
|
-
|
|
- def get_input(self):
|
|
- if isinstance(self.inp, int):
|
|
- return self.mirror_text[self.inp]
|
|
- elif self.inp in os.environ:
|
|
- return os.environ[self.inp]
|
|
- else:
|
|
- return ''
|
|
-
|
|
- def run_update(self):
|
|
- pattern = self.substitute(self.pattern)
|
|
- substitution = self.substitute(self.substitution, SubstitutionParser.escape_substitution)
|
|
-
|
|
- if pattern:
|
|
- return self.expand(pattern, substitution)
|
|
-
|
|
- return True
|
|
-
|
|
- def expand(self, pattern, substitution):
|
|
- # Try to compile pattern
|
|
- try:
|
|
- regex = re.compile(pattern, self.modifiers)
|
|
- except re.error, message:
|
|
- sys.stderr.write('Could not compile regular expression: %s\n%s\n' % (pattern, message))
|
|
- return False
|
|
-
|
|
- inp = self.get_input()
|
|
- match = regex.search(inp)
|
|
-
|
|
- if not match:
|
|
- self.set_text(inp)
|
|
- else:
|
|
- groups = match.groupdict()
|
|
-
|
|
- idx = 0
|
|
- for group in match.groups():
|
|
- groups[str(idx + 1)] = group
|
|
- idx += 1
|
|
-
|
|
- groups['0'] = match.group(0)
|
|
-
|
|
- parser = SubstitutionParser(substitution, groups)
|
|
- self.set_text(parser.parse())
|
|
-
|
|
- return True
|
|
-# ex:ts=8:et:
|
|
+ def __init__(self, view, tabstop, begin, inp, pattern, substitution, modifiers):
|
|
+ PlaceholderExpand.__init__(self, view, tabstop, begin, '')
|
|
+
|
|
+ self.instant_update = True
|
|
+ self.inp = inp
|
|
+ self.pattern = pattern
|
|
+ self.substitution = substitution
|
|
+
|
|
+ self.init_modifiers(modifiers)
|
|
+
|
|
+ def init_modifiers(self, modifiers):
|
|
+ mods = {'I': re.I,
|
|
+ 'L': re.L,
|
|
+ 'M': re.M,
|
|
+ 'S': re.S,
|
|
+ 'U': re.U,
|
|
+ 'X': re.X}
|
|
+
|
|
+ self.modifiers = 0
|
|
+
|
|
+ for modifier in modifiers:
|
|
+ if modifier in mods:
|
|
+ self.modifiers |= mods[modifier]
|
|
+
|
|
+ def get_mirrors(self, placeholders):
|
|
+ mirrors = self.find_mirrors(self.pattern, placeholders) + self.find_mirrors(self.substitution, placeholders)
|
|
+
|
|
+ if isinstance(self.inp, int):
|
|
+ if self.inp not in placeholders:
|
|
+ self.ok = False
|
|
+ return None
|
|
+ elif self.inp not in mirrors:
|
|
+ mirrors.append(self.inp)
|
|
+
|
|
+ return mirrors
|
|
+
|
|
+ def literal(self, s):
|
|
+ return re.escape(s)
|
|
+
|
|
+ def get_input(self):
|
|
+ if isinstance(self.inp, int):
|
|
+ return self.mirror_text[self.inp]
|
|
+ elif self.inp in os.environ:
|
|
+ return os.environ[self.inp]
|
|
+ else:
|
|
+ return ''
|
|
+
|
|
+ def run_update(self):
|
|
+ pattern = self.substitute(self.pattern)
|
|
+ substitution = self.substitute(self.substitution, SubstitutionParser.escape_substitution)
|
|
+
|
|
+ if pattern:
|
|
+ return self.expand(pattern, substitution)
|
|
+
|
|
+ return True
|
|
+
|
|
+ def expand(self, pattern, substitution):
|
|
+ # Try to compile pattern
|
|
+ try:
|
|
+ regex = re.compile(pattern, self.modifiers)
|
|
+ except re.error, message:
|
|
+ sys.stderr.write('Could not compile regular expression: %s\n%s\n' % (pattern, message))
|
|
+ return False
|
|
+
|
|
+ inp = self.get_input()
|
|
+ match = regex.search(inp)
|
|
+
|
|
+ if not match:
|
|
+ self.set_text(inp)
|
|
+ else:
|
|
+ groups = match.groupdict()
|
|
+
|
|
+ idx = 0
|
|
+ for group in match.groups():
|
|
+ groups[str(idx + 1)] = group
|
|
+ idx += 1
|
|
+
|
|
+ groups['0'] = match.group(0)
|
|
+
|
|
+ parser = SubstitutionParser(substitution, groups)
|
|
+ self.set_text(parser.parse())
|
|
+
|
|
+ return True
|
|
+
|
|
+# ex:ts=4:et:
|
|
diff --git a/plugins/snippets/snippets/Snippet.py b/plugins/snippets/snippets/Snippet.py
|
|
old mode 100755
|
|
new mode 100644
|
|
index 2d7f67d..192b036
|
|
--- a/plugins/snippets/snippets/Snippet.py
|
|
+++ b/plugins/snippets/snippets/Snippet.py
|
|
@@ -23,333 +23,334 @@ from Parser import Parser, Token
|
|
from Helper import *
|
|
|
|
class EvalUtilities:
|
|
- def __init__(self, view=None):
|
|
- self.view = view
|
|
- self._init_namespace()
|
|
-
|
|
- def _init_namespace(self):
|
|
- self.namespace = {
|
|
- '__builtins__': __builtins__,
|
|
- 'align': self.util_align,
|
|
- 'readfile': self.util_readfile,
|
|
- 'filesize': self.util_filesize
|
|
- }
|
|
-
|
|
- def _real_len(self, s, tablen = 0):
|
|
- if tablen == 0:
|
|
- tablen = self.view.get_tab_width()
|
|
-
|
|
- return len(s.expandtabs(tablen))
|
|
-
|
|
- def _filename_to_uri(self, filename):
|
|
- gfile = Gio.file_new_for_path(filename)
|
|
-
|
|
- return gfile.get_uri()
|
|
-
|
|
- def util_readfile(self, filename):
|
|
- stream = Gio.file_new_for_path(filename).read()
|
|
-
|
|
- if not stream:
|
|
- return ''
|
|
-
|
|
- res = stream.read()
|
|
- stream.close()
|
|
-
|
|
- return res
|
|
-
|
|
- def util_filesize(self, filename):
|
|
- gfile = Gio.file_new_for_path(filename)
|
|
- info = gfile.query_info(Gio.FILE_ATTRIBUTE_STANDARD_SIZE)
|
|
-
|
|
- if not info:
|
|
- return 0
|
|
-
|
|
- return info.get_size()
|
|
-
|
|
- def util_align(self, items):
|
|
- maxlen = []
|
|
- tablen = self.view.get_tab_width()
|
|
-
|
|
- for row in range(0, len(items)):
|
|
- for col in range(0, len(items[row]) - 1):
|
|
- if row == 0:
|
|
- maxlen.append(0)
|
|
-
|
|
- items[row][col] += "\t"
|
|
- rl = self._real_len(items[row][col], tablen)
|
|
-
|
|
- if (rl > maxlen[col]):
|
|
- maxlen[col] = rl
|
|
-
|
|
- result = ''
|
|
-
|
|
- for row in range(0, len(items)):
|
|
- 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 += items[row][len(items[row]) - 1]
|
|
-
|
|
- if row != len(items) - 1:
|
|
- result += "\n"
|
|
-
|
|
- return result
|
|
+ def __init__(self, view=None):
|
|
+ self.view = view
|
|
+ self._init_namespace()
|
|
+
|
|
+ def _init_namespace(self):
|
|
+ self.namespace = {
|
|
+ '__builtins__': __builtins__,
|
|
+ 'align': self.util_align,
|
|
+ 'readfile': self.util_readfile,
|
|
+ 'filesize': self.util_filesize
|
|
+ }
|
|
+
|
|
+ def _real_len(self, s, tablen = 0):
|
|
+ if tablen == 0:
|
|
+ tablen = self.view.get_tab_width()
|
|
+
|
|
+ return len(s.expandtabs(tablen))
|
|
+
|
|
+ def _filename_to_uri(self, filename):
|
|
+ gfile = Gio.file_new_for_path(filename)
|
|
+
|
|
+ return gfile.get_uri()
|
|
+
|
|
+ def util_readfile(self, filename):
|
|
+ stream = Gio.file_new_for_path(filename).read()
|
|
+
|
|
+ if not stream:
|
|
+ return ''
|
|
+
|
|
+ res = stream.read()
|
|
+ stream.close()
|
|
+
|
|
+ return res
|
|
+
|
|
+ def util_filesize(self, filename):
|
|
+ gfile = Gio.file_new_for_path(filename)
|
|
+ info = gfile.query_info(Gio.FILE_ATTRIBUTE_STANDARD_SIZE)
|
|
+
|
|
+ if not info:
|
|
+ return 0
|
|
+
|
|
+ return info.get_size()
|
|
+
|
|
+ def util_align(self, items):
|
|
+ maxlen = []
|
|
+ tablen = self.view.get_tab_width()
|
|
+
|
|
+ for row in range(0, len(items)):
|
|
+ for col in range(0, len(items[row]) - 1):
|
|
+ if row == 0:
|
|
+ maxlen.append(0)
|
|
+
|
|
+ items[row][col] += "\t"
|
|
+ rl = self._real_len(items[row][col], tablen)
|
|
+
|
|
+ if (rl > maxlen[col]):
|
|
+ maxlen[col] = rl
|
|
+
|
|
+ result = ''
|
|
+
|
|
+ for row in range(0, len(items)):
|
|
+ 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 += items[row][len(items[row]) - 1]
|
|
+
|
|
+ if row != len(items) - 1:
|
|
+ result += "\n"
|
|
+
|
|
+ return result
|
|
|
|
class Snippet:
|
|
- def __init__(self, data):
|
|
- self.data = data
|
|
-
|
|
- def __getitem__(self, prop):
|
|
- return self.data[prop]
|
|
-
|
|
- def __setitem__(self, prop, value):
|
|
- self.data[prop] = value
|
|
-
|
|
- def accelerator_display(self):
|
|
- accel = self['accelerator']
|
|
-
|
|
- if accel:
|
|
- keyval, mod = Gtk.accelerator_parse(accel)
|
|
- accel = Gtk.accelerator_get_label(keyval, mod)
|
|
-
|
|
- return accel or ''
|
|
-
|
|
- def display(self):
|
|
- nm = markup_escape(self['description'])
|
|
-
|
|
- tag = self['tag']
|
|
- accel = self.accelerator_display()
|
|
- detail = []
|
|
-
|
|
- if tag and tag != '':
|
|
- detail.append(tag)
|
|
-
|
|
- if accel and accel != '':
|
|
- detail.append(accel)
|
|
-
|
|
- if not detail:
|
|
- return nm
|
|
- else:
|
|
- return nm + ' (<b>' + markup_escape(str.join(', ', detail)) + \
|
|
- '</b>)'
|
|
-
|
|
- def _add_placeholder(self, placeholder):
|
|
- if placeholder.tabstop in self.placeholders:
|
|
- if placeholder.tabstop == -1:
|
|
- self.placeholders[-1].append(placeholder)
|
|
- self.plugin_data.ordered_placeholders.append(placeholder)
|
|
- elif placeholder.tabstop == -1:
|
|
- self.placeholders[-1] = [placeholder]
|
|
- self.plugin_data.ordered_placeholders.append(placeholder)
|
|
- else:
|
|
- self.placeholders[placeholder.tabstop] = placeholder
|
|
- self.plugin_data.ordered_placeholders.append(placeholder)
|
|
-
|
|
- 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'))
|
|
- 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 ''
|
|
-
|
|
- # Get all the current indentation
|
|
- all_indent = compute_indentation(self._view, self._insert_iter())
|
|
-
|
|
- # Substract initial indentation to get the snippet indentation
|
|
- indent = all_indent[len(self._indent):]
|
|
-
|
|
- # Keep indentation
|
|
- return unicode.join('\n' + unicode(indent), val.split('\n'))
|
|
-
|
|
- def _create_placeholder(self, data):
|
|
- tabstop = data['tabstop']
|
|
- begin = self._insert_iter()
|
|
-
|
|
- if tabstop == 0:
|
|
- # End placeholder
|
|
- return PlaceholderEnd(self._view, begin, data['default'])
|
|
- elif tabstop in self.placeholders:
|
|
- # Mirror placeholder
|
|
- return PlaceholderMirror(self._view, tabstop, begin)
|
|
- else:
|
|
- # Default placeholder
|
|
- return Placeholder(self._view, tabstop, data['default'], begin)
|
|
-
|
|
- def _create_shell(self, data):
|
|
- begin = self._insert_iter()
|
|
- return PlaceholderShell(self._view, data['tabstop'], 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)
|
|
-
|
|
- def _create_regex(self, data):
|
|
- begin = self._insert_iter()
|
|
- return PlaceholderRegex(self._view, data['tabstop'], begin, data['input'], data['pattern'], data['substitution'], data['modifiers'])
|
|
-
|
|
- def _create_text(self, data):
|
|
- return data
|
|
-
|
|
- def _invalid_placeholder(self, placeholder, remove):
|
|
- buf = self._view.get_buffer()
|
|
-
|
|
- # Remove the text because this placeholder is invalid
|
|
- if placeholder.default and remove:
|
|
- buf.delete(placeholder.begin_iter(), placeholder.end_iter())
|
|
-
|
|
- placeholder.remove()
|
|
-
|
|
- if placeholder.tabstop == -1:
|
|
- index = self.placeholders[-1].index(placeholder)
|
|
- del self.placeholders[-1][index]
|
|
- else:
|
|
- del self.placeholders[placeholder.tabstop]
|
|
-
|
|
- self.plugin_data.ordered_placeholders.remove(placeholder)
|
|
-
|
|
- def _parse(self, plugin_data):
|
|
- # Initialize current variables
|
|
- self._view = plugin_data.view
|
|
- self._indent = compute_indentation(self._view, self._view.get_buffer().get_iter_at_mark(self.begin_mark))
|
|
- self._utils = EvalUtilities(self._view)
|
|
- self.placeholders = {}
|
|
- self._insert_mark = self.end_mark
|
|
- self.plugin_data = plugin_data
|
|
-
|
|
- # Create parser
|
|
- parser = Parser(data=self['text'])
|
|
-
|
|
- # Parse tokens
|
|
- while (True):
|
|
- token = parser.token()
|
|
-
|
|
- if not token:
|
|
- break
|
|
-
|
|
- try:
|
|
- val = {'environment': self._create_environment,
|
|
- 'placeholder': self._create_placeholder,
|
|
- 'shell': self._create_shell,
|
|
- 'eval': self._create_eval,
|
|
- 'regex': self._create_regex,
|
|
- 'text': self._create_text}[token.klass](token.data)
|
|
- except:
|
|
- sys.stderr.write('Token class not supported: %s\n' % token.klass)
|
|
- continue
|
|
-
|
|
- if isinstance(val, basestring):
|
|
- # Insert text
|
|
- self._insert_text(val)
|
|
- else:
|
|
- # Insert placeholder
|
|
- self._add_placeholder(val)
|
|
-
|
|
- # 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.plugin_data.ordered_placeholders.append(self.placeholders[0])
|
|
-
|
|
- # Make sure run_last is ran for all placeholders and remove any
|
|
- # non `ok` placeholders
|
|
- for tabstop in self.placeholders.copy():
|
|
- ph = (tabstop == -1 and list(self.placeholders[-1])) or [self.placeholders[tabstop]]
|
|
-
|
|
- for placeholder in ph:
|
|
- placeholder.run_last(self.placeholders)
|
|
-
|
|
- if not placeholder.ok or placeholder.done:
|
|
- self._invalid_placeholder(placeholder, not placeholder.ok)
|
|
-
|
|
- # Remove all the Expand placeholders which have a tabstop because
|
|
- # they can be used to mirror, but they shouldn't be real tabstops
|
|
- # (if they have mirrors installed). This is problably a bit of
|
|
- # a dirty hack :)
|
|
- if -1 not in self.placeholders:
|
|
- self.placeholders[-1] = []
|
|
-
|
|
- for tabstop in self.placeholders.copy():
|
|
- placeholder = self.placeholders[tabstop]
|
|
-
|
|
- if tabstop != -1:
|
|
- if isinstance(placeholder, PlaceholderExpand) and \
|
|
- placeholder.has_references:
|
|
- # Add to anonymous placeholders
|
|
- self.placeholders[-1].append(placeholder)
|
|
-
|
|
- # Remove placeholder
|
|
- del self.placeholders[tabstop]
|
|
-
|
|
- self.plugin_data = None
|
|
-
|
|
- def insert_into(self, plugin_data, insert):
|
|
- buf = plugin_data.view.get_buffer()
|
|
- last_index = 0
|
|
-
|
|
- # Find closest mark at current insertion, so that we may insert
|
|
- # our marks in the correct order
|
|
- (current, next) = plugin_data.next_placeholder()
|
|
-
|
|
- if current:
|
|
- # Insert AFTER current
|
|
- last_index = plugin_data.placeholders.index(current) + 1
|
|
- elif next:
|
|
- # Insert BEFORE next
|
|
- last_index = plugin_data.placeholders.index(next)
|
|
- else:
|
|
- # Insert at first position
|
|
- last_index = 0
|
|
-
|
|
- # lastIndex now contains the position of the last mark
|
|
- # Create snippet bounding marks
|
|
- self.begin_mark = buf.create_mark(None, insert, True)
|
|
- self.end_mark = buf.create_mark(None, insert, False)
|
|
-
|
|
- # Now parse the contents of this snippet, create Placeholders
|
|
- # and insert the placholder marks in the marks array of plugin_data
|
|
- self._parse(plugin_data)
|
|
-
|
|
- # 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)
|
|
-
|
|
- plugin_data.placeholders.insert(last_index, self.placeholders[0])
|
|
- last_iter = self.placeholders[0].end_iter()
|
|
-
|
|
- for tabstop in k:
|
|
- if tabstop != -1 and tabstop != 0:
|
|
- placeholder = self.placeholders[tabstop]
|
|
- end_iter = placeholder.end_iter()
|
|
-
|
|
- if last_iter.compare(end_iter) < 0:
|
|
- last_iter = end_iter
|
|
-
|
|
- # Inserting placeholder
|
|
- plugin_data.placeholders.insert(last_index, placeholder)
|
|
-
|
|
- # Move end mark to last placeholder
|
|
- buf.move_mark(self.end_mark, last_iter)
|
|
-
|
|
- return self
|
|
-
|
|
- def deactivate(self):
|
|
- buf = self.begin_mark.get_buffer()
|
|
-
|
|
- buf.delete_mark(self.begin_mark)
|
|
- buf.delete_mark(self.end_mark)
|
|
-
|
|
- self.placeholders = {}
|
|
-
|
|
- def begin_iter(self):
|
|
- return self.begin_mark.get_buffer().get_iter_at_mark(self.begin_mark)
|
|
-
|
|
- def end_iter(self):
|
|
- return self.end_mark.get_buffer().get_iter_at_mark(self.end_mark)
|
|
-# ex:ts=8:et:
|
|
+ def __init__(self, data):
|
|
+ self.data = data
|
|
+
|
|
+ def __getitem__(self, prop):
|
|
+ return self.data[prop]
|
|
+
|
|
+ def __setitem__(self, prop, value):
|
|
+ self.data[prop] = value
|
|
+
|
|
+ def accelerator_display(self):
|
|
+ accel = self['accelerator']
|
|
+
|
|
+ if accel:
|
|
+ keyval, mod = Gtk.accelerator_parse(accel)
|
|
+ accel = Gtk.accelerator_get_label(keyval, mod)
|
|
+
|
|
+ return accel or ''
|
|
+
|
|
+ def display(self):
|
|
+ nm = markup_escape(self['description'])
|
|
+
|
|
+ tag = self['tag']
|
|
+ accel = self.accelerator_display()
|
|
+ detail = []
|
|
+
|
|
+ if tag and tag != '':
|
|
+ detail.append(tag)
|
|
+
|
|
+ if accel and accel != '':
|
|
+ detail.append(accel)
|
|
+
|
|
+ if not detail:
|
|
+ return nm
|
|
+ else:
|
|
+ return nm + ' (<b>' + markup_escape(str.join(', ', detail)) + \
|
|
+ '</b>)'
|
|
+
|
|
+ def _add_placeholder(self, placeholder):
|
|
+ if placeholder.tabstop in self.placeholders:
|
|
+ if placeholder.tabstop == -1:
|
|
+ self.placeholders[-1].append(placeholder)
|
|
+ self.plugin_data.ordered_placeholders.append(placeholder)
|
|
+ elif placeholder.tabstop == -1:
|
|
+ self.placeholders[-1] = [placeholder]
|
|
+ self.plugin_data.ordered_placeholders.append(placeholder)
|
|
+ else:
|
|
+ self.placeholders[placeholder.tabstop] = placeholder
|
|
+ self.plugin_data.ordered_placeholders.append(placeholder)
|
|
+
|
|
+ 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'))
|
|
+ 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 ''
|
|
+
|
|
+ # Get all the current indentation
|
|
+ all_indent = compute_indentation(self._view, self._insert_iter())
|
|
+
|
|
+ # Substract initial indentation to get the snippet indentation
|
|
+ indent = all_indent[len(self._indent):]
|
|
+
|
|
+ # Keep indentation
|
|
+ return unicode.join('\n' + unicode(indent), val.split('\n'))
|
|
+
|
|
+ def _create_placeholder(self, data):
|
|
+ tabstop = data['tabstop']
|
|
+ begin = self._insert_iter()
|
|
+
|
|
+ if tabstop == 0:
|
|
+ # End placeholder
|
|
+ return PlaceholderEnd(self._view, begin, data['default'])
|
|
+ elif tabstop in self.placeholders:
|
|
+ # Mirror placeholder
|
|
+ return PlaceholderMirror(self._view, tabstop, begin)
|
|
+ else:
|
|
+ # Default placeholder
|
|
+ return Placeholder(self._view, tabstop, data['default'], begin)
|
|
+
|
|
+ def _create_shell(self, data):
|
|
+ begin = self._insert_iter()
|
|
+ return PlaceholderShell(self._view, data['tabstop'], 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)
|
|
+
|
|
+ def _create_regex(self, data):
|
|
+ begin = self._insert_iter()
|
|
+ return PlaceholderRegex(self._view, data['tabstop'], begin, data['input'], data['pattern'], data['substitution'], data['modifiers'])
|
|
+
|
|
+ def _create_text(self, data):
|
|
+ return data
|
|
+
|
|
+ def _invalid_placeholder(self, placeholder, remove):
|
|
+ buf = self._view.get_buffer()
|
|
+
|
|
+ # Remove the text because this placeholder is invalid
|
|
+ if placeholder.default and remove:
|
|
+ buf.delete(placeholder.begin_iter(), placeholder.end_iter())
|
|
+
|
|
+ placeholder.remove()
|
|
+
|
|
+ if placeholder.tabstop == -1:
|
|
+ index = self.placeholders[-1].index(placeholder)
|
|
+ del self.placeholders[-1][index]
|
|
+ else:
|
|
+ del self.placeholders[placeholder.tabstop]
|
|
+
|
|
+ self.plugin_data.ordered_placeholders.remove(placeholder)
|
|
+
|
|
+ def _parse(self, plugin_data):
|
|
+ # Initialize current variables
|
|
+ self._view = plugin_data.view
|
|
+ self._indent = compute_indentation(self._view, self._view.get_buffer().get_iter_at_mark(self.begin_mark))
|
|
+ self._utils = EvalUtilities(self._view)
|
|
+ self.placeholders = {}
|
|
+ self._insert_mark = self.end_mark
|
|
+ self.plugin_data = plugin_data
|
|
+
|
|
+ # Create parser
|
|
+ parser = Parser(data=self['text'])
|
|
+
|
|
+ # Parse tokens
|
|
+ while (True):
|
|
+ token = parser.token()
|
|
+
|
|
+ if not token:
|
|
+ break
|
|
+
|
|
+ try:
|
|
+ val = {'environment': self._create_environment,
|
|
+ 'placeholder': self._create_placeholder,
|
|
+ 'shell': self._create_shell,
|
|
+ 'eval': self._create_eval,
|
|
+ 'regex': self._create_regex,
|
|
+ 'text': self._create_text}[token.klass](token.data)
|
|
+ except:
|
|
+ sys.stderr.write('Token class not supported: %s\n' % token.klass)
|
|
+ continue
|
|
+
|
|
+ if isinstance(val, basestring):
|
|
+ # Insert text
|
|
+ self._insert_text(val)
|
|
+ else:
|
|
+ # Insert placeholder
|
|
+ self._add_placeholder(val)
|
|
+
|
|
+ # 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.plugin_data.ordered_placeholders.append(self.placeholders[0])
|
|
+
|
|
+ # Make sure run_last is ran for all placeholders and remove any
|
|
+ # non `ok` placeholders
|
|
+ for tabstop in self.placeholders.copy():
|
|
+ ph = (tabstop == -1 and list(self.placeholders[-1])) or [self.placeholders[tabstop]]
|
|
+
|
|
+ for placeholder in ph:
|
|
+ placeholder.run_last(self.placeholders)
|
|
+
|
|
+ if not placeholder.ok or placeholder.done:
|
|
+ self._invalid_placeholder(placeholder, not placeholder.ok)
|
|
+
|
|
+ # Remove all the Expand placeholders which have a tabstop because
|
|
+ # they can be used to mirror, but they shouldn't be real tabstops
|
|
+ # (if they have mirrors installed). This is problably a bit of
|
|
+ # a dirty hack :)
|
|
+ if -1 not in self.placeholders:
|
|
+ self.placeholders[-1] = []
|
|
+
|
|
+ for tabstop in self.placeholders.copy():
|
|
+ placeholder = self.placeholders[tabstop]
|
|
+
|
|
+ if tabstop != -1:
|
|
+ if isinstance(placeholder, PlaceholderExpand) and \
|
|
+ placeholder.has_references:
|
|
+ # Add to anonymous placeholders
|
|
+ self.placeholders[-1].append(placeholder)
|
|
+
|
|
+ # Remove placeholder
|
|
+ del self.placeholders[tabstop]
|
|
+
|
|
+ self.plugin_data = None
|
|
+
|
|
+ def insert_into(self, plugin_data, insert):
|
|
+ buf = plugin_data.view.get_buffer()
|
|
+ last_index = 0
|
|
+
|
|
+ # Find closest mark at current insertion, so that we may insert
|
|
+ # our marks in the correct order
|
|
+ (current, next) = plugin_data.next_placeholder()
|
|
+
|
|
+ if current:
|
|
+ # Insert AFTER current
|
|
+ last_index = plugin_data.placeholders.index(current) + 1
|
|
+ elif next:
|
|
+ # Insert BEFORE next
|
|
+ last_index = plugin_data.placeholders.index(next)
|
|
+ else:
|
|
+ # Insert at first position
|
|
+ last_index = 0
|
|
+
|
|
+ # lastIndex now contains the position of the last mark
|
|
+ # Create snippet bounding marks
|
|
+ self.begin_mark = buf.create_mark(None, insert, True)
|
|
+ self.end_mark = buf.create_mark(None, insert, False)
|
|
+
|
|
+ # Now parse the contents of this snippet, create Placeholders
|
|
+ # and insert the placholder marks in the marks array of plugin_data
|
|
+ self._parse(plugin_data)
|
|
+
|
|
+ # 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)
|
|
+
|
|
+ plugin_data.placeholders.insert(last_index, self.placeholders[0])
|
|
+ last_iter = self.placeholders[0].end_iter()
|
|
+
|
|
+ for tabstop in k:
|
|
+ if tabstop != -1 and tabstop != 0:
|
|
+ placeholder = self.placeholders[tabstop]
|
|
+ end_iter = placeholder.end_iter()
|
|
+
|
|
+ if last_iter.compare(end_iter) < 0:
|
|
+ last_iter = end_iter
|
|
+
|
|
+ # Inserting placeholder
|
|
+ plugin_data.placeholders.insert(last_index, placeholder)
|
|
+
|
|
+ # Move end mark to last placeholder
|
|
+ buf.move_mark(self.end_mark, last_iter)
|
|
+
|
|
+ return self
|
|
+
|
|
+ def deactivate(self):
|
|
+ buf = self.begin_mark.get_buffer()
|
|
+
|
|
+ buf.delete_mark(self.begin_mark)
|
|
+ buf.delete_mark(self.end_mark)
|
|
+
|
|
+ self.placeholders = {}
|
|
+
|
|
+ def begin_iter(self):
|
|
+ return self.begin_mark.get_buffer().get_iter_at_mark(self.begin_mark)
|
|
+
|
|
+ def end_iter(self):
|
|
+ return self.end_mark.get_buffer().get_iter_at_mark(self.end_mark)
|
|
+
|
|
+# ex:ts=4:et:
|
|
diff --git a/plugins/snippets/snippets/SubstitutionParser.py b/plugins/snippets/snippets/SubstitutionParser.py
|
|
old mode 100755
|
|
new mode 100644
|
|
index a41f5a6..246f4da
|
|
--- a/plugins/snippets/snippets/SubstitutionParser.py
|
|
+++ b/plugins/snippets/snippets/SubstitutionParser.py
|
|
@@ -18,185 +18,185 @@
|
|
import re
|
|
|
|
class ParseError(Exception):
|
|
- def __str__(self):
|
|
- return 'Parse error, resume next'
|
|
+ def __str__(self):
|
|
+ return 'Parse error, resume next'
|
|
|
|
class Modifiers:
|
|
- def _first_char(s):
|
|
- first = (s != '' and s[0]) or ''
|
|
- rest = (len(s) > 1 and s[1:]) or ''
|
|
-
|
|
- return first, rest
|
|
-
|
|
- def upper_first(s):
|
|
- first, rest = Modifiers._first_char(s)
|
|
-
|
|
- return '%s%s' % (first.upper(), rest)
|
|
-
|
|
- def upper(s):
|
|
- return s.upper()
|
|
-
|
|
- def lower_first(s):
|
|
- first, rest = Modifiers._first_char(s)
|
|
-
|
|
- return '%s%s' % (first.lower(), rest)
|
|
-
|
|
- def lower(s):
|
|
- return s.lower()
|
|
-
|
|
- def title(s):
|
|
- return s.title()
|
|
-
|
|
- upper_first = staticmethod(upper_first)
|
|
- upper = staticmethod(upper)
|
|
- lower_first = staticmethod(lower_first)
|
|
- lower = staticmethod(lower)
|
|
- title = staticmethod(title)
|
|
- _first_char = staticmethod(_first_char)
|
|
+ def _first_char(s):
|
|
+ first = (s != '' and s[0]) or ''
|
|
+ rest = (len(s) > 1 and s[1:]) or ''
|
|
+
|
|
+ return first, rest
|
|
+
|
|
+ def upper_first(s):
|
|
+ first, rest = Modifiers._first_char(s)
|
|
+
|
|
+ return '%s%s' % (first.upper(), rest)
|
|
+
|
|
+ def upper(s):
|
|
+ return s.upper()
|
|
+
|
|
+ def lower_first(s):
|
|
+ first, rest = Modifiers._first_char(s)
|
|
+
|
|
+ return '%s%s' % (first.lower(), rest)
|
|
+
|
|
+ def lower(s):
|
|
+ return s.lower()
|
|
+
|
|
+ def title(s):
|
|
+ return s.title()
|
|
+
|
|
+ upper_first = staticmethod(upper_first)
|
|
+ upper = staticmethod(upper)
|
|
+ lower_first = staticmethod(lower_first)
|
|
+ lower = staticmethod(lower)
|
|
+ title = staticmethod(title)
|
|
+ _first_char = staticmethod(_first_char)
|
|
|
|
class SubstitutionParser:
|
|
- REG_ID = '[0-9]+'
|
|
- REG_NAME = '[a-zA-Z_]+'
|
|
- REG_MOD = '[a-zA-Z]+'
|
|
- REG_ESCAPE = '\\\\|\\(\\?|,|\\)'
|
|
-
|
|
- def __init__(self, pattern, groups = {}, modifiers = {}):
|
|
- self.pattern = pattern
|
|
- self.groups = groups
|
|
-
|
|
- self.REG_GROUP = '(?:(%s)|<(%s|%s)(?:,(%s))?>)' % (self.REG_ID, self.REG_ID, self.REG_NAME, self.REG_MOD)
|
|
- self.modifiers = {'u': Modifiers.upper_first,
|
|
- 'U': Modifiers.upper,
|
|
- 'l': Modifiers.lower_first,
|
|
- 'L': Modifiers.lower,
|
|
- 't': Modifiers.title}
|
|
-
|
|
- for k, v in modifiers.items():
|
|
- self.modifiers[k] = v
|
|
-
|
|
- def parse(self):
|
|
- result, tokens = self._parse(self.pattern, None)
|
|
-
|
|
- return result
|
|
-
|
|
- def _parse(self, tokens, terminator):
|
|
- result = ''
|
|
-
|
|
- while tokens != '':
|
|
- if self._peek(tokens) == '' or self._peek(tokens) == terminator:
|
|
- tokens = self._remains(tokens)
|
|
- break
|
|
-
|
|
- try:
|
|
- res, tokens = self._expr(tokens, terminator)
|
|
- except ParseError:
|
|
- res, tokens = self._text(tokens)
|
|
-
|
|
- result += res
|
|
-
|
|
- return result, tokens
|
|
-
|
|
- def _peek(self, tokens, num = 0):
|
|
- return (num < len(tokens) and tokens[num])
|
|
-
|
|
- def _token(self, tokens):
|
|
- if tokens == '':
|
|
- return '', '';
|
|
-
|
|
- return tokens[0], (len(tokens) > 1 and tokens[1:]) or ''
|
|
-
|
|
- def _remains(self, tokens, num = 1):
|
|
- return (num < len(tokens) and tokens[num:]) or ''
|
|
-
|
|
- def _expr(self, tokens, terminator):
|
|
- if tokens == '':
|
|
- return ''
|
|
-
|
|
- try:
|
|
- return {'\\': self._escape,
|
|
- '(': self._condition}[self._peek(tokens)](tokens, terminator)
|
|
- except KeyError:
|
|
- raise ParseError
|
|
-
|
|
- def _text(self, tokens):
|
|
- return self._token(tokens)
|
|
-
|
|
- def _substitute(self, group, modifiers = ''):
|
|
- result = (self.groups.has_key(group) and self.groups[group]) or ''
|
|
-
|
|
- for modifier in modifiers:
|
|
- if self.modifiers.has_key(modifier):
|
|
- result = self.modifiers[modifier](result)
|
|
-
|
|
- return result
|
|
-
|
|
- def _match_group(self, tokens):
|
|
- match = re.match('\\\\%s' % self.REG_GROUP, tokens)
|
|
-
|
|
- if not match:
|
|
- return None, tokens
|
|
-
|
|
- return self._substitute(match.group(1) or match.group(2), match.group(3) or ''), tokens[match.end():]
|
|
-
|
|
- def _escape(self, tokens, terminator):
|
|
- # Try to match a group
|
|
- result, tokens = self._match_group(tokens)
|
|
-
|
|
- if result != None:
|
|
- return result, tokens
|
|
-
|
|
- s = self.REG_GROUP
|
|
-
|
|
- if terminator:
|
|
- s += '|%s' % re.escape(terminator)
|
|
-
|
|
- match = re.match('\\\\(\\\\%s|%s)' % (s, self.REG_ESCAPE), tokens)
|
|
-
|
|
- if not match:
|
|
- raise ParseError
|
|
-
|
|
- return match.group(1), tokens[match.end():]
|
|
-
|
|
- def _condition_value(self, tokens):
|
|
- match = re.match('\\\\?%s\s*' % self.REG_GROUP, tokens)
|
|
-
|
|
- if not match:
|
|
- return None, tokens
|
|
-
|
|
- groups = match.groups()
|
|
- name = groups[0] or groups[1]
|
|
-
|
|
- return self.groups.has_key(name) and self.groups[name] != None, tokens[match.end():]
|
|
-
|
|
- def _condition(self, tokens, terminator):
|
|
- # Match ? after (
|
|
- if self._peek(tokens, 1) != '?':
|
|
- raise ParseError
|
|
-
|
|
- # Remove initial (? token
|
|
- tokens = self._remains(tokens, 2)
|
|
- condition, tokens = self._condition_value(tokens)
|
|
-
|
|
- if condition == None or self._peek(tokens) != ',':
|
|
- raise ParseError
|
|
-
|
|
- truepart, tokens = self._parse(self._remains(tokens), ',')
|
|
-
|
|
- if truepart == None:
|
|
- raise ParseError
|
|
-
|
|
- falsepart, tokens = self._parse(tokens, ')')
|
|
-
|
|
- if falsepart == None:
|
|
- raise ParseError
|
|
-
|
|
- if condition:
|
|
- return truepart, tokens
|
|
- else:
|
|
- return falsepart, tokens
|
|
-
|
|
- def escape_substitution(substitution):
|
|
- return re.sub('(%s|%s)' % (self.REG_GROUP, self.REG_ESCAPE), '\\\\\\1', substitution)
|
|
-
|
|
- escapesubstitution = staticmethod(escape_substitution)
|
|
-# ex:ts=8:et:
|
|
+ REG_ID = '[0-9]+'
|
|
+ REG_NAME = '[a-zA-Z_]+'
|
|
+ REG_MOD = '[a-zA-Z]+'
|
|
+ REG_ESCAPE = '\\\\|\\(\\?|,|\\)'
|
|
+
|
|
+ def __init__(self, pattern, groups = {}, modifiers = {}):
|
|
+ self.pattern = pattern
|
|
+ self.groups = groups
|
|
+
|
|
+ self.REG_GROUP = '(?:(%s)|<(%s|%s)(?:,(%s))?>)' % (self.REG_ID, self.REG_ID, self.REG_NAME, self.REG_MOD)
|
|
+ self.modifiers = {'u': Modifiers.upper_first,
|
|
+ 'U': Modifiers.upper,
|
|
+ 'l': Modifiers.lower_first,
|
|
+ 'L': Modifiers.lower,
|
|
+ 't': Modifiers.title}
|
|
+
|
|
+ for k, v in modifiers.items():
|
|
+ self.modifiers[k] = v
|
|
+
|
|
+ def parse(self):
|
|
+ result, tokens = self._parse(self.pattern, None)
|
|
+
|
|
+ return result
|
|
+
|
|
+ def _parse(self, tokens, terminator):
|
|
+ result = ''
|
|
+
|
|
+ while tokens != '':
|
|
+ if self._peek(tokens) == '' or self._peek(tokens) == terminator:
|
|
+ tokens = self._remains(tokens)
|
|
+ break
|
|
+
|
|
+ try:
|
|
+ res, tokens = self._expr(tokens, terminator)
|
|
+ except ParseError:
|
|
+ res, tokens = self._text(tokens)
|
|
+
|
|
+ result += res
|
|
+
|
|
+ return result, tokens
|
|
+
|
|
+ def _peek(self, tokens, num = 0):
|
|
+ return (num < len(tokens) and tokens[num])
|
|
+
|
|
+ def _token(self, tokens):
|
|
+ if tokens == '':
|
|
+ return '', '';
|
|
+
|
|
+ return tokens[0], (len(tokens) > 1 and tokens[1:]) or ''
|
|
+
|
|
+ def _remains(self, tokens, num = 1):
|
|
+ return (num < len(tokens) and tokens[num:]) or ''
|
|
+
|
|
+ def _expr(self, tokens, terminator):
|
|
+ if tokens == '':
|
|
+ return ''
|
|
+
|
|
+ try:
|
|
+ return {'\\': self._escape,
|
|
+ '(': self._condition}[self._peek(tokens)](tokens, terminator)
|
|
+ except KeyError:
|
|
+ raise ParseError
|
|
+
|
|
+ def _text(self, tokens):
|
|
+ return self._token(tokens)
|
|
+
|
|
+ def _substitute(self, group, modifiers = ''):
|
|
+ result = (self.groups.has_key(group) and self.groups[group]) or ''
|
|
+
|
|
+ for modifier in modifiers:
|
|
+ if self.modifiers.has_key(modifier):
|
|
+ result = self.modifiers[modifier](result)
|
|
+
|
|
+ return result
|
|
+
|
|
+ def _match_group(self, tokens):
|
|
+ match = re.match('\\\\%s' % self.REG_GROUP, tokens)
|
|
+
|
|
+ if not match:
|
|
+ return None, tokens
|
|
+
|
|
+ return self._substitute(match.group(1) or match.group(2), match.group(3) or ''), tokens[match.end():]
|
|
+
|
|
+ def _escape(self, tokens, terminator):
|
|
+ # Try to match a group
|
|
+ result, tokens = self._match_group(tokens)
|
|
+
|
|
+ if result != None:
|
|
+ return result, tokens
|
|
+
|
|
+ s = self.REG_GROUP
|
|
+
|
|
+ if terminator:
|
|
+ s += '|%s' % re.escape(terminator)
|
|
+
|
|
+ match = re.match('\\\\(\\\\%s|%s)' % (s, self.REG_ESCAPE), tokens)
|
|
+
|
|
+ if not match:
|
|
+ raise ParseError
|
|
+
|
|
+ return match.group(1), tokens[match.end():]
|
|
+
|
|
+ def _condition_value(self, tokens):
|
|
+ match = re.match('\\\\?%s\s*' % self.REG_GROUP, tokens)
|
|
+
|
|
+ if not match:
|
|
+ return None, tokens
|
|
+
|
|
+ groups = match.groups()
|
|
+ name = groups[0] or groups[1]
|
|
+
|
|
+ return self.groups.has_key(name) and self.groups[name] != None, tokens[match.end():]
|
|
+
|
|
+ def _condition(self, tokens, terminator):
|
|
+ # Match ? after (
|
|
+ if self._peek(tokens, 1) != '?':
|
|
+ raise ParseError
|
|
+
|
|
+ # Remove initial (? token
|
|
+ tokens = self._remains(tokens, 2)
|
|
+ condition, tokens = self._condition_value(tokens)
|
|
+
|
|
+ if condition == None or self._peek(tokens) != ',':
|
|
+ raise ParseError
|
|
+
|
|
+ truepart, tokens = self._parse(self._remains(tokens), ',')
|
|
+
|
|
+ if truepart == None:
|
|
+ raise ParseError
|
|
+
|
|
+ falsepart, tokens = self._parse(tokens, ')')
|
|
+
|
|
+ if falsepart == None:
|
|
+ raise ParseError
|
|
+
|
|
+ if condition:
|
|
+ return truepart, tokens
|
|
+ else:
|
|
+ return falsepart, tokens
|
|
+
|
|
+ def escape_substitution(substitution):
|
|
+ return re.sub('(%s|%s)' % (self.REG_GROUP, self.REG_ESCAPE), '\\\\\\1', substitution)
|
|
+
|
|
+ escapesubstitution = staticmethod(escape_substitution)
|
|
+# ex:ts=4:et:
|
|
diff --git a/plugins/snippets/snippets/WindowHelper.py b/plugins/snippets/snippets/WindowHelper.py
|
|
old mode 100755
|
|
new mode 100644
|
|
index 3b08d12..296ff03
|
|
--- a/plugins/snippets/snippets/WindowHelper.py
|
|
+++ b/plugins/snippets/snippets/WindowHelper.py
|
|
@@ -25,127 +25,127 @@ from Document import Document
|
|
from Library import Library
|
|
|
|
class WindowHelper:
|
|
- def __init__(self, plugin):
|
|
- self.plugin = plugin
|
|
- self.current_controller = None
|
|
- self.current_language = None
|
|
- self.signal_ids = {}
|
|
-
|
|
- def run(self, window):
|
|
- self.window = window
|
|
-
|
|
- self.insert_menu()
|
|
-
|
|
- self.accel_group = Library().get_accel_group(None)
|
|
-
|
|
- window.add_accel_group(self.accel_group)
|
|
- window.connect('tab-added', self.on_tab_added)
|
|
-
|
|
- # Add controllers to all the current views
|
|
- for view in self.window.get_views():
|
|
- if isinstance(view, Pluma.View) and not self.has_controller(view):
|
|
- view._snippet_controller = Document(self, view)
|
|
-
|
|
- self.update()
|
|
-
|
|
- def stop(self):
|
|
- self.window.remove_accel_group(self.accel_group)
|
|
- self.accel_group = None
|
|
-
|
|
- self.remove_menu()
|
|
-
|
|
- # Iterate over all the tabs and remove every controller
|
|
- for view in self.window.get_views():
|
|
- if isinstance(view, Pluma.View) and self.has_controller(view):
|
|
- view._snippet_controller.stop()
|
|
- view._snippet_controller = None
|
|
-
|
|
- self.window = None
|
|
- self.plugin = None
|
|
-
|
|
- def insert_menu(self):
|
|
- manager = self.window.get_ui_manager()
|
|
-
|
|
- self.action_group = Gtk.ActionGroup("PlumaSnippetPluginActions")
|
|
- self.action_group.set_translation_domain('pluma')
|
|
- self.action_group.add_actions([('ManageSnippets', None,
|
|
- _('Manage _Snippets...'), \
|
|
- None, _('Manage snippets'), \
|
|
- self.on_action_snippets_activate)])
|
|
-
|
|
- self.merge_id = manager.new_merge_id()
|
|
- manager.insert_action_group(self.action_group, -1)
|
|
- manager.add_ui(self.merge_id, '/MenuBar/ToolsMenu/ToolsOps_5', \
|
|
- 'ManageSnippets', 'ManageSnippets', Gtk.UIManagerItemType.MENUITEM, False)
|
|
-
|
|
- def remove_menu(self):
|
|
- manager = self.window.get_ui_manager()
|
|
- manager.remove_ui(self.merge_id)
|
|
- manager.remove_action_group(self.action_group)
|
|
- self.action_group = None
|
|
-
|
|
- def find_snippet(self, snippets, tag):
|
|
- result = []
|
|
-
|
|
- for snippet in snippets:
|
|
- if Snippet(snippet)['tag'] == tag:
|
|
- result.append(snippet)
|
|
-
|
|
- return result
|
|
-
|
|
- def has_controller(self, view):
|
|
- return hasattr(view, '_snippet_controller') and view._snippet_controller
|
|
-
|
|
- def update_language(self):
|
|
- if not self.window:
|
|
- return
|
|
-
|
|
- if self.current_language:
|
|
- accel_group = Library().get_accel_group( \
|
|
- self.current_language)
|
|
- self.window.remove_accel_group(accel_group)
|
|
-
|
|
- if self.current_controller:
|
|
- self.current_language = self.current_controller.language_id
|
|
-
|
|
- if self.current_language != None:
|
|
- accel_group = Library().get_accel_group( \
|
|
- self.current_language)
|
|
- self.window.add_accel_group(accel_group)
|
|
- else:
|
|
- self.current_language = None
|
|
-
|
|
- def language_changed(self, controller):
|
|
- if controller == self.current_controller:
|
|
- self.update_language()
|
|
-
|
|
- def update(self):
|
|
- view = self.window.get_active_view()
|
|
-
|
|
- if not view or not self.has_controller(view):
|
|
- return
|
|
-
|
|
- controller = view._snippet_controller
|
|
-
|
|
- if controller != self.current_controller:
|
|
- self.current_controller = controller
|
|
- self.update_language()
|
|
-
|
|
- # Callbacks
|
|
-
|
|
- def on_tab_added(self, window, tab):
|
|
- # Create a new controller for this tab if it has a standard pluma view
|
|
- view = tab.get_view()
|
|
-
|
|
- if isinstance(view, Pluma.View) and not self.has_controller(view):
|
|
- view._snippet_controller = Document(self, view)
|
|
-
|
|
- self.update()
|
|
-
|
|
- def on_action_snippets_activate(self, item):
|
|
- self.plugin.create_configure_dialog()
|
|
-
|
|
- def accelerator_activated(self, keyval, mod):
|
|
- return self.current_controller.accelerator_activate(keyval, mod)
|
|
-
|
|
-# ex:ts=8:et:
|
|
+ def __init__(self, plugin):
|
|
+ self.plugin = plugin
|
|
+ self.current_controller = None
|
|
+ self.current_language = None
|
|
+ self.signal_ids = {}
|
|
+
|
|
+ def run(self, window):
|
|
+ self.window = window
|
|
+
|
|
+ self.insert_menu()
|
|
+
|
|
+ self.accel_group = Library().get_accel_group(None)
|
|
+
|
|
+ window.add_accel_group(self.accel_group)
|
|
+ window.connect('tab-added', self.on_tab_added)
|
|
+
|
|
+ # Add controllers to all the current views
|
|
+ for view in self.window.get_views():
|
|
+ if isinstance(view, Pluma.View) and not self.has_controller(view):
|
|
+ view._snippet_controller = Document(self, view)
|
|
+
|
|
+ self.update()
|
|
+
|
|
+ def stop(self):
|
|
+ self.window.remove_accel_group(self.accel_group)
|
|
+ self.accel_group = None
|
|
+
|
|
+ self.remove_menu()
|
|
+
|
|
+ # Iterate over all the tabs and remove every controller
|
|
+ for view in self.window.get_views():
|
|
+ if isinstance(view, Pluma.View) and self.has_controller(view):
|
|
+ view._snippet_controller.stop()
|
|
+ view._snippet_controller = None
|
|
+
|
|
+ self.window = None
|
|
+ self.plugin = None
|
|
+
|
|
+ def insert_menu(self):
|
|
+ manager = self.window.get_ui_manager()
|
|
+
|
|
+ self.action_group = Gtk.ActionGroup("PlumaSnippetPluginActions")
|
|
+ self.action_group.set_translation_domain('pluma')
|
|
+ self.action_group.add_actions([('ManageSnippets', None,
|
|
+ _('Manage _Snippets...'), \
|
|
+ None, _('Manage snippets'), \
|
|
+ self.on_action_snippets_activate)])
|
|
+
|
|
+ self.merge_id = manager.new_merge_id()
|
|
+ manager.insert_action_group(self.action_group, -1)
|
|
+ manager.add_ui(self.merge_id, '/MenuBar/ToolsMenu/ToolsOps_5', \
|
|
+ 'ManageSnippets', 'ManageSnippets', Gtk.UIManagerItemType.MENUITEM, False)
|
|
+
|
|
+ def remove_menu(self):
|
|
+ manager = self.window.get_ui_manager()
|
|
+ manager.remove_ui(self.merge_id)
|
|
+ manager.remove_action_group(self.action_group)
|
|
+ self.action_group = None
|
|
+
|
|
+ def find_snippet(self, snippets, tag):
|
|
+ result = []
|
|
+
|
|
+ for snippet in snippets:
|
|
+ if Snippet(snippet)['tag'] == tag:
|
|
+ result.append(snippet)
|
|
+
|
|
+ return result
|
|
+
|
|
+ def has_controller(self, view):
|
|
+ return hasattr(view, '_snippet_controller') and view._snippet_controller
|
|
+
|
|
+ def update_language(self):
|
|
+ if not self.window:
|
|
+ return
|
|
+
|
|
+ if self.current_language:
|
|
+ accel_group = Library().get_accel_group( \
|
|
+ self.current_language)
|
|
+ self.window.remove_accel_group(accel_group)
|
|
+
|
|
+ if self.current_controller:
|
|
+ self.current_language = self.current_controller.language_id
|
|
+
|
|
+ if self.current_language != None:
|
|
+ accel_group = Library().get_accel_group( \
|
|
+ self.current_language)
|
|
+ self.window.add_accel_group(accel_group)
|
|
+ else:
|
|
+ self.current_language = None
|
|
+
|
|
+ def language_changed(self, controller):
|
|
+ if controller == self.current_controller:
|
|
+ self.update_language()
|
|
+
|
|
+ def update(self):
|
|
+ view = self.window.get_active_view()
|
|
+
|
|
+ if not view or not self.has_controller(view):
|
|
+ return
|
|
+
|
|
+ controller = view._snippet_controller
|
|
+
|
|
+ if controller != self.current_controller:
|
|
+ self.current_controller = controller
|
|
+ self.update_language()
|
|
+
|
|
+ # Callbacks
|
|
+
|
|
+ def on_tab_added(self, window, tab):
|
|
+ # Create a new controller for this tab if it has a standard pluma view
|
|
+ view = tab.get_view()
|
|
+
|
|
+ if isinstance(view, Pluma.View) and not self.has_controller(view):
|
|
+ view._snippet_controller = Document(self, view)
|
|
+
|
|
+ self.update()
|
|
+
|
|
+ def on_action_snippets_activate(self, item):
|
|
+ self.plugin.create_configure_dialog()
|
|
+
|
|
+ def accelerator_activated(self, keyval, mod):
|
|
+ return self.current_controller.accelerator_activate(keyval, mod)
|
|
+
|
|
+# ex:ts=4:et:
|
|
diff --git a/plugins/snippets/snippets/__init__.py b/plugins/snippets/snippets/__init__.py
|
|
old mode 100755
|
|
new mode 100644
|
|
index d005e89..8642406
|
|
--- a/plugins/snippets/snippets/__init__.py
|
|
+++ b/plugins/snippets/snippets/__init__.py
|
|
@@ -23,71 +23,73 @@ from Library import Library
|
|
from Manager import Manager
|
|
|
|
class SnippetsPlugin(GObject.Object, Peas.Activatable):
|
|
- __gtype_name__ = "SnippetsPlugin"
|
|
+ __gtype_name__ = "SnippetsPlugin"
|
|
|
|
- object = GObject.Property(type=GObject.Object)
|
|
+ object = GObject.Property(type=GObject.Object)
|
|
|
|
- def __init__(self):
|
|
- GObject.Object.__init__(self)
|
|
+ def __init__(self):
|
|
+ GObject.Object.__init__(self)
|
|
|
|
- self.dlg = None
|
|
+ self.dlg = None
|
|
|
|
- def system_dirs(self):
|
|
- if 'XDG_DATA_DIRS' in os.environ:
|
|
- datadirs = os.environ['XDG_DATA_DIRS']
|
|
- else:
|
|
- datadirs = '/usr/local/share' + os.pathsep + '/usr/share'
|
|
+ def system_dirs(self):
|
|
+ if 'XDG_DATA_DIRS' in os.environ:
|
|
+ datadirs = os.environ['XDG_DATA_DIRS']
|
|
+ else:
|
|
+ datadirs = '/usr/local/share' + os.pathsep + '/usr/share'
|
|
|
|
- dirs = []
|
|
+ dirs = []
|
|
|
|
- for d in datadirs.split(os.pathsep):
|
|
- d = os.path.join(d, 'pluma', 'plugins', 'snippets')
|
|
+ for d in datadirs.split(os.pathsep):
|
|
+ d = os.path.join(d, 'pluma', 'plugins', 'snippets')
|
|
|
|
- if os.path.isdir(d):
|
|
- dirs.append(d)
|
|
+ if os.path.isdir(d):
|
|
+ dirs.append(d)
|
|
|
|
- dirs.append(self.plugin_info.get_data_dir())
|
|
- return dirs
|
|
+ dirs.append(self.plugin_info.get_data_dir())
|
|
+ return dirs
|
|
|
|
- def do_activate(self):
|
|
- library = Library()
|
|
- library.add_accelerator_callback(self.accelerator_activated)
|
|
+ def do_activate(self):
|
|
+ library = Library()
|
|
+ library.add_accelerator_callback(self.accelerator_activated)
|
|
|
|
- snippetsdir = os.path.join(GLib.get_user_config_dir(), '/pluma/snippets')
|
|
- library.set_dirs(snippetsdir, self.system_dirs())
|
|
+ snippetsdir = os.path.join(GLib.get_user_config_dir(), '/pluma/snippets')
|
|
+ library.set_dirs(snippetsdir, self.system_dirs())
|
|
|
|
- self._helper = WindowHelper(self)
|
|
+ self._helper = WindowHelper(self)
|
|
|
|
- window = self.object
|
|
- self._helper.run(window)
|
|
+ window = self.object
|
|
+ self._helper.run(window)
|
|
|
|
- def do_deactivate(self):
|
|
- library = Library()
|
|
- library.remove_accelerator_callback(self.accelerator_activated)
|
|
+ def do_deactivate(self):
|
|
+ library = Library()
|
|
+ library.remove_accelerator_callback(self.accelerator_activated)
|
|
|
|
- self._helper.stop()
|
|
- self._helper = None
|
|
+ self._helper.stop()
|
|
+ self._helper = None
|
|
|
|
- def do_update_state(self):
|
|
- self._helper.update()
|
|
+ def do_update_state(self):
|
|
+ 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()
|
|
+ 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()
|
|
+ window = Pluma.App.get_default().get_active_window()
|
|
|
|
- if window:
|
|
- self.dlg.dlg.set_transient_for(window)
|
|
+ if window:
|
|
+ self.dlg.dlg.set_transient_for(window)
|
|
|
|
- return self.dlg.dlg
|
|
+ return self.dlg.dlg
|
|
|
|
- def accelerator_activated(self, group, obj, keyval, mod):
|
|
- ret = False
|
|
+ def accelerator_activated(self, group, obj, keyval, mod):
|
|
+ ret = False
|
|
|
|
- if self._helper:
|
|
- ret = self._helper.accelerator_activated(keyval, mod)
|
|
+ if self._helper:
|
|
+ ret = self._helper.accelerator_activated(keyval, mod)
|
|
|
|
- return ret
|
|
+ return ret
|
|
+
|
|
+# ex:ts=4:et:
|
|
--
|
|
2.21.0
|
|
|