xed/plugins/snippets/snippets/Snippet.py

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: