Commits

Henning Schröder committed b70f6e1

snippet engine and editor test widget

  • Participants
  • Parent commits 23ce977

Comments (0)

Files changed (2)

+# -*- coding: utf-8 -*-
+import re
+import os
+# for loading snippet definitions:
+from xml.etree import ElementTree 
+
+
+
+digits = "0123456789"
+lowercase_letters = "abcdefghijklmnopqrstuvwxyz"
+uppercase_letters = lowercase_letters.upper()
+letters = uppercase_letters + lowercase_letters
+whitespace = " \t\r\n"
+punct = re.escape(".-,;!?") # XXX: Any more?
+
+
+character_classes = {
+  "alpha": letters,
+  "alnum": letters + digits,
+  "ascii": re.escape("".join([chr(i) for i in range(128)])),
+  "cntrl": re.escape("".join([chr(i) for i in range(32)])),
+  "digit": digits,
+  "graph": letters + digits + punct,
+  "lower": lowercase_letters,
+  "print": letters + digits + punct + whitespace,
+  "punct": punct,
+  "space": whitespace,
+  "upper": uppercase_letters,
+  "word" : letters + digits + "_",
+  "xdigit": "0-9a-fA-F"
+  }
+
+
+
+class PerlRegEx(object):
+    """
+    This class can search and replace with a single string like sed or Perl.
+    Additionally support for Posix character classes was added for compatibily reasons.
+    
+    Full syntax documented at http://perldoc.perl.org/perlre.html
+
+    >>> pre = PerlRegEx(r"/(:alpha:)/'$1'/g")
+    >>> pre.search
+    '([ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz])'
+    
+    >>> print pre.replace
+    '\\1'
+    
+    >>> pre("a+1*b")
+    "'a'+1*'b'"
+    
+    """
+
+    def __init__(self, pattern):
+        self.pattern = pattern
+        self.length = len(pattern)
+        self.pos = -1
+        self.search = []
+        self.replace = []
+        self.modifiers = []
+        self.target = self.search
+        self.next_char()
+        self.parse()
+
+
+    def next_char(self):
+        self.pos += 1
+        if self.pos < self.length:
+            self.char = self.pattern[self.pos]
+        else:
+            self.char = None
+        return self.char
+
+
+    def emit(self, char):
+        self.target.append(char)
+
+
+    def _parse_word(self, allowed=letters):
+        word = ""
+        while self.char:
+            if not self.char in allowed:
+                break
+            word += self.char
+            self.next_char()
+        return word
+
+
+    def parse_character_class(self):
+        # Currently not supported by Python:
+        # http://bugs.python.org/issue433029
+        # Will hopefully be added besides other goodies:
+        # http://bugs.python.org/issue2636
+        self.next_char()
+        if self.char == "^":
+            negate = True
+            self.next_char()
+        else:
+            negate = False
+        if self.char in letters:
+            name = self._parse_word()
+            if self.char == ":":
+                self.next_char()
+                found = character_classes.get(name)
+                if not found:
+                    raise ValueError, ":%s: unknown posix character class" % name
+                self.emit("[")
+                if negate:
+                    self.emit("^")
+                self.emit(found)
+                self.emit("]")
+            else:
+                self.emit(":")
+                self.emit(name)
+        else:
+            self.emit(self.char)
+            if negate:
+                self.emit("^")
+
+
+    def parse(self):
+        while self.char:
+            if self.char == "/":
+                if self.pos == 0:
+                    pass # skip
+                elif self.target == self.search:
+                    self.target = self.replace
+                elif self.target == self.replace:
+                    self.target = self.modifiers
+                elif self.target == self.modifiers:
+                    raise ValueError, "Third unescaped '/' found in regex"
+                self.next_char()
+
+            elif self.char == ":" and self.target == self.search:
+                self.parse_character_class()
+
+            elif self.char == "$" and self.target == self.replace:
+                # Python uses \1 and Perl uses $1
+                # XXX: Perl's \1 would mean chr(1)
+                self.next_char()
+                if self.char in digits:
+                    self.emit("\\")
+                    while self.char:
+                        if not self.char in digits:
+                            break
+                        self.emit(self.char)
+                        self.next_char()
+                else:
+                    self.emit("$")
+
+            elif self.char == "\\":
+                # This makes sure that nexxxt char is not handled 
+                # above by accident
+                self.next_char()
+                self.emit("\\")
+                self.emit(self.char)
+                self.next_char()
+
+            else:
+                self.emit(self.char)
+                self.next_char()
+
+        if self.target == self.replace:
+            self.replace, self.modifiers = self.modifiers, self.replace
+        self.search = "".join(self.search)
+        self.replace = "".join(self.replace)
+        self.global_mode = False
+        self.re_options = 0
+        processed = []
+        for char in self.modifiers:
+            if char in processed:
+                raise ValueError, "More than one occurence of same modifier %r" % m
+            elif char == "g":
+                self.global_mode = True
+            elif char == "i":
+                self.re_options |= re.I
+            elif char == "m":
+                self.re_options |= re.M
+            elif char == "s":
+                self.re_options |= re.S
+            elif char == "x":
+                self.re_optons |= re.X
+            else:
+                raise ValueError, "Unknown modifier %r" % char
+            processed.append(char)
+        self.search_re = re.compile(self.search, self.re_options)
+        self.search_match = self.search_re.match
+
+        
+    def __call__(self, s):
+        if self.replace:
+            if self.global_mode:
+                count = 0
+            else:
+                count = 1
+            return re.sub(self.search_re, self.replace, s, count)
+        else:
+            found = self.search_match(s)
+            if found:
+                return found.groups()
+
+
+
+class Environ(dict):
+
+
+    def __init__(self, filename, selection=None):
+        dict.__init__(self)
+        self.filename = os.path.abspath(filename)
+        self.path, self.name = os.path.split(self.filename)
+        self.selection = selection
+        self.populate()
+
+
+    def populate(self):
+        pass
+
+
+
+class GEditEnviron(Environ):
+
+
+    def populate(self):
+        # http://live.gnome.org/Gedit/Plugins/Snippets
+        v = dict(
+            GEDIT_SELECTED_TEXT=self.selection,
+            GEDIT_FILENAME=self.filename,
+            GEDIT_BASENAME=self.name,
+            )
+        self.update(v)
+
+
+
+
+
+class TextmateEnviron(Environ):
+
+
+    def populate(self):
+        filename = os.path.abspath(filename)
+        v = dict(
+            TM_FILENAME=self.name,
+            TM_FILEPATH=self.filename, 
+            TM_DIRECTORY=self.path,
+    #         TM_CURRENT_WORD 	
+    #         TM_CURRENT_LINE 	
+    #         TM_LINE_NUMBER 	
+    #         TM_LINE_INDEX 	
+    #         TM_COLUMN_NUMBER 	
+    #         TM_TAB_SIZE 	
+    #         TM_SOFT_TABS 	
+    #         TM_SCOPE 	
+    #         TM_MODE 	
+    #         TM_CARET_XPOS 	
+    #         TM_CARET_YPOS 	
+    #         TM_PROJECT_DIRECTORY 	
+    #         TM_SELECTED_FILE 	
+    #         TM_SELECTED_FILES 	
+    #         TM_BUNDLE_PATH 	
+    #         TM_BUNDLE_SUPPORT
+    #         TM_SUPPORT_PATH  
+            )
+        if self.selection:
+            v.update(TM_SELECTED_TEXT=self.selection) 
+        self.update(v)
+
+
+
+class TabStop(object):
+
+
+    def __init__(self, number):
+        self.number = number
+
+
+    def __repr__(self):
+        return "<%s object at 0x%x %r>" % (
+            self.__class__.__name__, id(self), self.__dict__)
+
+
+
+class SimpleTabStop(TabStop):
+    pass
+
+
+class ExtendedTabStop(TabStop):
+
+
+    def __init__(self, number, value=None, transform=None):
+        TabStop.__init__(self, number)
+        self.value = value # initial
+        self.transform = transform
+
+
+    def __call__(self, value):
+        if self.transform:
+            pre = PerlRegEx(self.transform)
+            return pre(value)
+        else:
+            return value
+
+    
+    def __str__(self):
+        value = self.value
+        if self.transform:
+            pass # XXX
+        return value
+
+
+
+class SnippetParser(object):
+    """
+    Simple recursive decent LL(1) parser
+    """
+
+
+    def __init__(self, s):
+        self.parts = []
+        self.s = s
+        self.pos = -1
+        self.length = len(self.s)
+        self.parse()
+
+
+    def next_char(self):
+        self.pos += 1
+        if self.pos < self.length:
+            self.char = self.s[self.pos]
+        else:
+            self.char = None
+        return self.char
+
+
+    def emit(self, item):
+        if isinstance(item, basestring) and \
+                len(self.parts) and \
+                isinstance(self.parts[-1], basestring):
+            self.parts[-1] += item
+            return
+        self.parts.append(item)
+
+
+    def parse_number(self):
+        num = int(self.char)
+        self.next_char()
+        while self.char:
+            if not self.char in digits:
+                break
+            num = 10 * num + int(self.char)
+            self.next_char()
+        return num
+
+    
+    def _parse_escaped(self, escape_chars=None):
+        result = ""
+        while self.char:
+            if not self.char == "\\":
+                break
+            self.next_char()
+            if self.char in escape_chars or not escape_chars:
+                result += self.char
+                self.next_char()
+                continue
+            else:
+                result += "\\"
+                continue
+        return result
+
+
+    def __iter__(self):
+        return iter(self.parts)
+
+
+    def __len__(self):
+        return len(self.parts)
+
+
+
+class TextmateSnippetParser(SnippetParser):
+    
+
+    def parse_simple_tabstop(self):
+        number = self.parse_number()
+        self.parts.append(SimpleTabStop(number))
+
+
+    def parse_tabstop(self):
+        self.next_char()
+        if self.char in digits:
+            number = self.parse_number()
+        else:
+            raise ValueError(
+                "Number expected after '{' at position %s" % self.pos)
+        value = transform = code = ""
+        if self.char ==  ":":
+            self.next_char()
+            while self.char:
+                if self.char == "}":
+                    self.next_char()
+                    break
+                elif self.char == "\\":
+                    value += self._parse_escaped("}")
+                    continue
+                value += self.char
+                self.next_char()
+        if self.char == "/":
+            self.next_char()
+            while self.char:
+                if self.char == "}":
+                    self.next_char()
+                    break
+                elif self.char == "\\":
+                    transform += self._parse_escaped("}")
+                    continue
+                transform += self.char
+                self.next_char()
+        elif self.char == "`":
+            self.next_char()
+            while self.char:
+                if self.char == "}":
+                    self.next_char()
+                    break
+                elif self.char == "\\":
+                    code += self._parse_escaped("`")
+                    continue
+                code += self.char
+                self.next_char()
+        ts = ExtendedTabStop(number, value, transform)
+        return ts
+
+
+    def parse(self):
+        self.next_char()
+        while self.char:
+            if self.char == "$":
+                self.next_char()
+                if self.char in digits:
+                    ts = self.parse_simple_tabstop()
+                    self.emit(ts)
+                elif self.char == "{":
+                    ts = self.parse_tabstop()
+                    self.emit(ts)
+                else:
+                    self.emit("$")
+            elif self.char == "\\":
+                self.emit(self._parse_escaped("{"))
+            else:
+                self.emit(self.char)
+                self.next_char()
+
+
+
+
+class SnippetField(object):
+    
+    def __init__(self, pos, length, tabstop):
+        self.pos = pos
+        self.length = length
+        self.tabstop = tabstop
+
+
+    def __repr__(self):
+        return "<%s object at 0x%x %r>" % (
+            self.__class__.__name__, id(self), self.__dict__)
+
+
+
+class SnippetTemplate(object):
+
+    """
+    >>> example = "if (${1:condition}) { $2 }"
+    >>> t = SnippetTemplate(example)
+    >>> t.template
+    'if (condition) {  }'
+    """
+
+    def __init__(self, s, Parser=TextmateSnippetParser):
+        self.parser = Parser(s)
+        self.reset()
+
+
+    def reset(self):
+        self.values = {}
+        for part in self.parser:
+            if isinstance(part, TabStop):
+                self.values.setdefault(part.number, []).append(part)
+        for number in self.values.keys():
+            value = ""
+            for ts in self.values[number]:
+                if isinstance(ts, ExtendedTabStop):
+                    if ts.value:
+                        value = ts.value
+                        break
+            self.values[number] = value
+        self.render()
+        self.field_count = len(self.values)
+        self.tab_count = len(self.parser)
+        self.tab_index = 1
+
+
+    def render(self):
+        fields = {}
+        template = ""
+        for part in self.parser:
+            pos = len(template)
+            if isinstance(part, basestring):
+                template += part
+            elif isinstance(part, TabStop):
+                if isinstance(part, ExtendedTabStop):
+                    value = part(self.values[part.number])
+                else:
+                    value = self.values[part.number]
+                f = SnippetField(pos, len(value), part)
+                fields.setdefault(part.number, []).append(f)
+                template += value
+        self.length = len(template)
+        if not self.values.get(0):
+            ts = SimpleTabStop(0)
+            f = SnippetField(len(template), 0, ts)
+            fields.setdefault(0, []).append(f)
+        self.fields = fields
+        self.template = template
+
+
+    def __str__(self):
+        return self.template
+
+
+    def set(self, tab_number, value):
+        """
+        >>> example = "if (${1:condition}) { $2 }"
+        >>> t = SnippetTemplate(example)
+        >>> t.set(1, "FOOBAR")
+        >>> t.template
+        'if (FOOBAR) {  }'
+        """
+        self.values[tab_number] = value
+        self.render()
+
+
+    def insert_chars(self, tab_number, pos, chars):
+        """
+        >>> example = "if (${1:condition}) { $2 }"
+        >>> t = SnippetTemplate(example)
+        >>> t.set(1, "FOO")
+        >>> t.insert_chars(1, 3, "BAZ")
+        >>> t.template
+        'if (FOOBAZ) {  }'
+        """
+        value = self.values[tab_number]
+        value = value[:pos] +  chars + value[pos:]
+        self.values[tab_number] = value
+        self.render()
+
+
+    def remove_chars(self, tab_number, pos, count=1):
+        """
+        >>> example = "if (${1:condition}) { $2 }"
+        >>> t = SnippetTemplate(example)
+        >>> t.set(1, "FOOBAR")
+        >>> t.remove_chars(1, 1, 2)
+        >>> t.template
+        'if (FBAR) {  }'
+        """
+        value = self.values[tab_number]
+        value = value[:pos] + value[pos+count:]
+        self.values[tab_number] = value
+        self.render()
+
+
+    def iter_fields(self):
+        return iter(self.fields.get(self.tab_index, []))
+
+
+    def next_tab(self):
+        self.tab_index += 1
+        if self.tab_index > len(self.values):
+            self.tab_index = 1
+        return self.tab_index
+
+
+    def previous_tab(self):
+        self.tab_index -= 1
+        if self.tab_index < 1:
+            self.tab_index = len(self.values)
+        return self.tab_index
+    
+
+    def field_pos(self, cursor_pos):
+        found = None
+        for field in self.fields.get(self.tab_index, []):
+            if cursor_pos >= field.pos and cursor_pos <= field.pos + field.length:
+                found = field
+                break
+        if not found:
+            #raise ValueError, "Field not found. This should not happen!"
+            return -1
+        return cursor_pos - field.pos
+
+
+    def field_value_length(self):
+        return len(self.values.get(self.tab_index, ""))
+
+
+    def find_tab(self, cursor_pos, assign=True):
+        length = self.field_value_length()
+        distances = []
+        for tab_index in range(1, self.tab_count):
+            for field in self.fields.get(tab_index, []):
+                if cursor_pos  >= field.pos and cursor_pos <= field.pos + length:
+                    if tab_index != self.tab_index:
+                        # inside other tabstop
+                        return tab_index
+                    else:
+                        # inside old tabstop
+                        return tab_index
+                else:
+                    # outside tabstop
+                    if tab_index != self.tab_index:
+                        if not getattr(field, "transform", ""):
+                            distances.append((abs(cursor_pos - field.pos), tab_index))
+        if assign and distances:
+            distances.sort(cmp=lambda a, b: cmp(a[0], b[0]))
+            return distances[0][1]
+        return -1
+
+
+
+
+if __name__ == "__main__":
+    import doctest
+    doctest.testmod()

snippet_editor.py

+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+import sys
+from PyQt4.QtCore import *
+from PyQt4.QtGui import *
+
+from snippet import SnippetTemplate
+
+INDENTION = " " * 4
+
+snippets = {
+    "class": """class ${1:ClassName}(${2:object}):\n\tdef __init__(self, ${3:arg}):\n\t\tsuper($1, self).__init__(self)\n\t\t""".replace("\t", INDENTION),
+    }
+
+
+
+class EditorCursor(QTextCursor):
+
+
+    def current_word(self):
+        self.select(QTextCursor.WordUnderCursor)
+        return unicode(self.selectedText())
+
+
+    def current_line(self):
+        return unicode(self.block().text())
+
+
+    def endswith_word(self):
+        word = self.current_word()
+        pos = self.position()
+        line = self.current_line()
+        if line[:pos].endswith(word):
+            return word
+
+
+    def select_range(self, start_pos, end_pos):
+        self.setPosition(start_pos)
+        self.setPosition(end_pos, QTextCursor.KeepAnchor)
+
+
+    def remove_range(self, start_pos, end_pos):
+        self.select_range(start_pos, end_pos)
+        self.removeSelectedText()
+
+
+
+
+class SnippetEditor(QPlainTextEdit):
+
+    show_tooltip = True
+
+
+    def __init__(self):
+        QPlainTextEdit.__init__(self, None)
+        self.resize(640, 480)
+        pal = QPalette()
+        self.higlight = pal.highlight() # for extra selections
+        self.snippets = {}
+        self.template = None
+        self.snippet_state = False
+        self.snippet_length = 0
+        for name, value in snippets.items():
+            self.snippets[name] = SnippetTemplate(value)
+        self.connect(self, SIGNAL("cursorPositionChanged()"), self.on_cursor_position_changed)
+
+
+    def editCursor(self):
+        return EditorCursor(self.textCursor())
+
+
+    def trigger(self):
+        cursor = self.editCursor()
+        word = cursor.endswith_word()
+        if word:
+            snippet = self.snippets.get(word)
+            if snippet:
+                if SnippetEditor.show_tooltip:
+                    SnippetEditor.show_tooltip = False
+                    rect = self.cursorRect(cursor)
+                    QToolTip.showText(
+                        self.mapToGlobal(rect.topLeft()),
+                        "Fill the selected fields and confirm with <Enter>.\n" +
+                        "Use <Tab> and <Shift>-<Tab> to navigate.\n" +
+                        "Exit snippet mode with <Escape>.", self)
+                cursor.removeSelectedText()
+                self.template_pos = cursor.position()
+                cursor.setPosition(self.template_pos)
+                self.setTextCursor(cursor)
+                self.template = snippet
+                self.template.reset()
+                self.render()
+                self.snippet_state = True
+                self.set_tab_selections()
+                return True
+
+
+    def render(self):
+        cursor = self.editCursor()
+        old_pos = cursor.position()
+        if self.snippet_length:
+            start_pos = self.template_pos
+            end_pos = self.template_pos + self.snippet_length - 1
+            cursor.remove_range(start_pos, end_pos)
+        cursor.setPosition(self.template_pos)
+        cursor.insertText(str(self.template))
+        self.snippet_length = self.template.length
+        cursor.setPosition(old_pos)
+        self.setTextCursor(cursor)
+
+
+    def on_text(self, text):
+        cursor = EditorCursor(self.textCursor())
+        cursor_pos = cursor.position()
+        if cursor.hasSelection():
+            #cursor.removeSelectedText()
+            cursor.clearSelection()
+            cursor.setPosition(cursor_pos - 1)
+            self.setTextCursor(cursor)
+            self.template.set(self.template.tab_index, text)
+        else:
+            pos = self.template.field_pos(cursor_pos - self.template_pos)
+            self.template.insert_chars(self.template.tab_index, pos, text)
+        self.render()
+        cursor = self.editCursor()
+        cursor.setPosition(cursor.position() + len(text))
+        self.setTextCursor(cursor)
+        return True
+
+
+    def on_delete(self):
+        cursor = self.editCursor()
+        pos = self.template.field_pos(cursor.position() - self.template_pos)
+        self.template.remove_chars(self.template.tab_index, pos)
+        self.render()
+        return True
+
+
+    def on_backspace(self):
+        cursor = self.editCursor()
+        cursor_pos = cursor.position()
+        pos = self.template.field_pos(cursor_pos - self.template_pos - 1)
+        self.template.remove_chars(self.template.tab_index, pos)
+        cursor.setPosition(cursor_pos - 1)
+        self.setTextCursor(cursor)
+        self.render()
+        return True
+
+
+    def goto_next(self):
+        self.template.next_tab()
+        return self.set_tab_selections()
+
+
+    def goto_previous(self):
+        self.template.previous_tab()
+        return self.set_tab_selections()
+        
+
+    def set_tab_selections(self):
+        cursor = self.editCursor()
+        cursor_pos = cursor.position()
+
+        self.selections = []
+        distances = []
+        first = -1
+        length = self.template.field_value_length()
+        for field in self.template.iter_fields():
+            sel = QTextEdit.ExtraSelection()
+            pos = self.template_pos + field.pos
+            if first == -1:
+                first = pos
+            else:
+                first = min(pos, first)
+            distances.append((abs(pos - cursor_pos), pos))
+            sel.cursor = QTextCursor(cursor)
+            sel.cursor.setPosition(pos)
+            sel.cursor.setPosition(pos + length, QTextCursor.KeepAnchor)
+            sel.format.setBackground(self.higlight)
+            self.selections.append(sel)
+        self.setExtraSelections(self.selections)
+
+        if self.template.tab_index == 0:
+            cursor_pos_start = first
+        else:
+            if not distances:
+                cursor_pos_start = first
+            else:
+                distances.sort(cmp=lambda a, b: cmp(a[0], b[0]))
+                cursor_pos_start = distances[0][1]
+
+        cursor.select_range(cursor_pos_start + length, cursor_pos_start)
+        self.setTextCursor(cursor)
+        return True
+
+
+    def on_cursor_position_changed(self):
+        """
+        Make sure the cursor is never outside a field
+        """
+        if not self.snippet_state:
+            return
+        if self.template.tab_index == 0:
+            return
+        cursor = self.editCursor()
+        cursor_pos = cursor.position() - self.template_pos
+        tab_index = self.template.find_tab(cursor_pos, assign=True)
+        if tab_index == -1:
+            raise "tab_index == -1"
+            return
+        elif tab_index != self.template.tab_index:
+            self.set_tab_selections()
+            return
+        else:
+            # inside old tab :-)
+            return
+
+
+    def exit(self):
+        self.snippet_state = False
+        self.snippet_length = 0
+        self.template = None
+        self.selections = []
+        self.setExtraSelections(self.selections)
+        return True
+
+
+    def keyPressEvent(self, event):
+        key = event.key()
+        modifiers = event.modifiers()
+        text = unicode(event.text())
+        
+        if key == Qt.Key_Tab:
+            if self.snippet_state:
+                if self.goto_next():
+                    event.ignore()
+                    return
+            else:
+                if self.trigger():
+                    event.ignore()
+                    return
+
+        elif key == Qt.Key_Backtab:
+            if self.snippet_state:
+                if self.goto_previous():
+                    event.ignore()
+                    return
+
+        elif key == Qt.Key_Return or Qt == Qt.Key_Enter:
+            if self.snippet_state:
+                if self.template.tab_index == len(self.template.values):
+                    self.template.tab_index = 0
+                    self.set_tab_selections()
+                    self.exit()
+                else:
+                    self.goto_next()
+                event.ignore()
+                return
+
+        elif key == Qt.Key_Escape:
+            if self.snippet_state:
+                if self.exit():
+                    event.ignore()
+                    return
+
+        elif key == Qt.Key_Delete:
+            if self.snippet_state:
+                if self.on_delete():
+                    event.ignore()
+                    return
+
+        elif key == Qt.Key_Backspace:
+            if self.snippet_state:
+                if self.on_backspace():
+                    event.ignore()
+                    return
+
+        elif text:
+            if self.snippet_state:
+                if self.on_text(text):
+                    event.ignore()
+                    return
+
+        elif key in (Qt.Key_Up, Qt.Key_Down, Qt.Key_Left, Qt.Key_Right):
+            pass
+
+        else:
+            if self.snippet_state:
+                # unknown key -> ignore
+                event.ignore()
+                return
+
+        return QPlainTextEdit.keyPressEvent(self, event)
+
+
+
+
+
+
+if __name__ == "__main__":
+    app = QApplication(sys.argv)
+    edit = SnippetEditor()
+    edit.setPlainText(open("/home/henning/snippet.py").read())
+    edit.show()
+    app.exec_()