356 lines
15 KiB
Python
Executable File
356 lines
15 KiB
Python
Executable File
# Pluma snippets plugin
|
|
# Copyright (C) 2005-2006 Jesse van den Kieboom <jesse@icecrew.nl>
|
|
#
|
|
# This program is free software; you can redistribute it and/or modify
|
|
# it under the terms of the GNU General Public License as published by
|
|
# the Free Software Foundation; either version 2 of the License, 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
|
|
# GNU General Public License for more details.
|
|
#
|
|
# You should have received a copy of the GNU General Public License
|
|
# along with this program; if not, write to the Free Software
|
|
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
|
|
|
import os
|
|
import gio
|
|
|
|
from Placeholder import *
|
|
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(filename)
|
|
|
|
return gfile.get_uri()
|
|
|
|
def util_readfile(self, filename):
|
|
stream = gio.File(filename).read()
|
|
|
|
if not stream:
|
|
return ''
|
|
|
|
res = stream.read()
|
|
stream.close()
|
|
|
|
return res
|
|
|
|
def util_filesize(self, filename):
|
|
gfile = gio.File(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:
|