Beispiel #1
0
 def popup_list(self, key):
     """
     This method popups a window with a list of snippets.
     
     @param key: A snippet key.
     @type key: A string.
     """
     buffer = self.view.buffer
     insert_mark = buffer.get_insert()
     insert_iter = buffer.get_iter_at_mark(insert_mark)
     alloc = self.view.get_iter_location(insert_iter)
     cursor_pos = self.view.buffer_to_window_coords(gtk.TEXT_WINDOW_TEXT, 
                                                    alloc.x, alloc.y)
     
     window = self.view.get_window(gtk.TEXT_WINDOW_WIDGET)
     wp_x, wp_y = window.get_origin()
     view_rec = self.view.get_visible_rect()
     position_x = cursor_pos[0] + wp_x + 20
     position_y = cursor_pos[1] + wp_y + 20
     
     if (position_x + 190) > (wp_x + view_rec.width):
         position_x = (wp_x + view_rec.width) - 190
     if (position_y + 190) > (wp_y + view_rec.height):
         position_y = (wp_y + cursor_pos[1]) - 190
         
     self.pw = SnippetPopupWindow()
     self.pw.run(self.snippets[key], (position_x, position_y))
Beispiel #2
0
class SnippetManager(object):
    """
    This class manage all snippets of current language.
    """
    def __init__(self, document):
        """
        Constructor.
        
        @param document: The current Document
        @type document: A Document object.
        """
        self.document = document
        self.view = document.view
        self.snippets = {}
        self.language = None
        self.stack = []
        self.top = None
    
    #################### Public Methods ####################
    
    def load(self, language):
        """
        Loads all snippets of current language syntax highlight.
        
        @param language: The name of a language.
        @type language: A string.
        """
        self.language = language
        try:
            doc = \
            xml.dom.minidom.parse(constants.LANGUAGES_USER_DIR + "/" + self.language + "/" + "snippets.xml")
        except IOError:
            self.snippets = {}
        except xml.parsers.expat.ExpatError:
            self.__show_error_dialog()
            self.snippets = {}
        else:
            key = None
            string = None
            name = None
            
            for i in doc.getElementsByTagName("snippet"):
                for node in i.getElementsByTagName("key"):
                    key = node.childNodes[0].data

                for node in i.getElementsByTagName("body"):
                    string = node.childNodes[0].data
                    
                for node in i.getElementsByTagName("name"):
                    name = node.childNodes[0].data

                if key and string and name:
                    info = SnippetInfo(key, string, name)
                    
                    if self.snippets.has_key(key):
                        self.snippets[key].append(info)
                    else:
                        self.snippets[key] = [info]
                else:
                    self.__show_error_dialog()
                    self.snippets = {}
                    return

    def active(self):
        """
        This method actives a snippet.
        
        @param key: The snippet activation key.
        @type: A string.
        """
        if len(self.snippets) == 0:
            return False
        
        buffer = self.view.buffer
        key = self.__get_key()
        
        if self.snippets.has_key(key):
            if len(self.snippets[key]) > 1:
                self.popup_list(key)
                self.id_key_press = self.view.connect('key-press-event',
                                                      self.key_press)
                self.id_event_after = self.view.connect('event-after',
                                             self.event_after, key, self.snippets[key])
                self.id_button_press = self.pw.treeview.connect('event-after',
                                                                self.item_clicked)
                notebook = self.view.get_parent().get_parent().get_parent()
                self.id_switch_page = notebook.connect('switch-page',
                                                       self.switch_page)
            else:
                # Delete key
                insert_mark = buffer.get_insert()
                insert_iter = buffer.get_iter_at_mark(insert_mark)
                start_iter = insert_iter.copy()

                self.__find_word_start(start_iter)
                        
                start_del_mark = buffer.create_mark(None, start_iter, True)
                
                si = buffer.get_iter_at_mark(start_del_mark)
                ei = buffer.get_iter_at_mark(insert_mark)
                buffer.delete(si, ei)
                
                text = self.snippets[key][0].string
                self.__parser(text)
            
            return True
        else:
            return False

    def popup_list(self, key):
        """
        This method popups a window with a list of snippets.
        
        @param key: A snippet key.
        @type key: A string.
        """
        buffer = self.view.buffer
        insert_mark = buffer.get_insert()
        insert_iter = buffer.get_iter_at_mark(insert_mark)
        alloc = self.view.get_iter_location(insert_iter)
        cursor_pos = self.view.buffer_to_window_coords(gtk.TEXT_WINDOW_TEXT, 
                                                       alloc.x, alloc.y)
        
        window = self.view.get_window(gtk.TEXT_WINDOW_WIDGET)
        wp_x, wp_y = window.get_origin()
        view_rec = self.view.get_visible_rect()
        position_x = cursor_pos[0] + wp_x + 20
        position_y = cursor_pos[1] + wp_y + 20
        
        if (position_x + 190) > (wp_x + view_rec.width):
            position_x = (wp_x + view_rec.width) - 190
        if (position_y + 190) > (wp_y + view_rec.height):
            position_y = (wp_y + cursor_pos[1]) - 190
            
        self.pw = SnippetPopupWindow()
        self.pw.run(self.snippets[key], (position_x, position_y))
            
    
    def pop_snippet(self):
        """
        Pop a snippet.
        """
        self.stack[-1].remove_marks()
        self.stack.pop()
        
    def push_snippet(self, snippet):
        """
        Push a snippet.
        
        @param snippet: A snippet that will be pushed.
        @type: A Snippet Object.
        """
        self.stack.append(snippet)
    
    def next_field(self):
        """
        This method selects the next field of the snippet at the top of the
        stack.
        """
        self.stack[-1].next()
        
    def previous_field(self):
        """
        This method selects the previous field of the snippet at the top of the
        stack.
        """
        self.stack[-1].previous()
    
    def valid_cursor_position(self):
        """
        This method verify if the insert cursor is between the snippet bounds.
        
        @return: True if insert cursor is between the snippet bounds,
        else, return False.
        @rtype: A boolean.
        """
        buffer = self.view.buffer
        insert = buffer.get_insert()
        insert_iter = buffer.get_iter_at_mark(insert)
        begin, end = self.stack[-1].bounds
        begin_iter = buffer.get_iter_at_mark(begin)
        end_iter = buffer.get_iter_at_mark(end)
        
        return insert_iter.in_range(begin_iter, end_iter)


    #################### Signals ####################
    
    def item_clicked(self, widget, event):
        if event.type == gtk.gdk.BUTTON_PRESS:
            if event.button == 1:
                buffer = self.view.buffer
                key = self.__get_key()
                position = self.pw.get_selected()
                
                insert_mark = buffer.get_insert()
                insert_iter = buffer.get_iter_at_mark(insert_mark)
                start_iter = insert_iter.copy()

                self.__find_word_start(start_iter)
                        
                start_del_mark = buffer.create_mark(None, start_iter, True)
                
                si = buffer.get_iter_at_mark(start_del_mark)
                ei = buffer.get_iter_at_mark(insert_mark)
                buffer.delete(si, ei)
                
                text = self.snippets[key][position].string
                self.__parser(text)
                
                self.__destroy()
        else:
            return
    
    def key_press(self, widget, event):
        keyval = event.keyval
        if keyval == gtk.keysyms.Down:
            self.pw.item_down()
            return True
        elif keyval == gtk.keysyms.Up:
            self.pw.item_up()
            return True
        elif keyval == gtk.keysyms.Return:
            return True
        
    def event_after(self, widget, event, key, words):
        if event.type == gtk.gdk.KEY_RELEASE:
            keyval = event.keyval
            
            if keyval == gtk.keysyms.Up:
                return
            elif keyval == gtk.keysyms.Down:
                return
            elif keyval == gtk.keysyms.Return:
                buffer = self.view.buffer
                position = self.pw.get_selected()
                
                insert_mark = buffer.get_insert()
                insert_iter = buffer.get_iter_at_mark(insert_mark)
                start_iter = insert_iter.copy()

                self.__find_word_start(start_iter)
                        
                start_del_mark = buffer.create_mark(None, start_iter, True)
                
                si = buffer.get_iter_at_mark(start_del_mark)
                ei = buffer.get_iter_at_mark(insert_mark)
                buffer.delete(si, ei)
                
                text = self.snippets[key][position].string
                self.__parser(text)
                
                self.__destroy()
            elif keyval == gtk.keysyms.Tab:
                return
            else:
                self.__destroy()
            
            return False
        elif event.type == gtk.gdk.BUTTON_RELEASE:
            self.__destroy()
            return
        else:
            return
            
    def switch_page(self, widget, page, page_num):
        self.__destroy()
    
    #################### Private Methods ####################
    
    def __parser(self, text):
        """
        This method parses the TextFlow snippet definition syntax.
        
        @param text: The text coded using TextFlow snippet definition syntax.
        @type text: A string.
        """
        buffer = self.view.buffer
        
        # Snippet components
        fields = Tree()
        mirrors = []
        stop = None
        
        root_init = fields.add(None, None) #empty root
        root = root_init
        
        # Cursor
        insert = buffer.get_insert()
        insert_iter = buffer.get_iter_at_mark(insert)
        
        # Indentation stuff
        pref_manager = tf.app.preferences_manager
        spaces = pref_manager.get_value("indentation/use_spaces")
        tab_width = self.view.get_tab_width()
        indent = self.document.get_indentation(insert_iter)
        
        # Create a mark at start of snippet
        begin_bound_mark = buffer.create_mark(None, insert_iter, True)
        
        # Parsing text
        i = 0
        stack = []
        while (i<len(text)):
            char = text[i]
            
            # Verifying escape char "\"
            if char == "\\":
                self.view.buffer.insert_at_cursor(text[i+1])
                i += 2
                continue            
            
            # Look for a snippet special component "${}"
            if char == '$' and (i+1) < len(text) and text[i+1] == '{':
                
                if text[i+2] == '0':
                    # STOP
                    stop_iter = buffer.get_iter_at_mark(buffer.get_insert())
                    stop = buffer.create_mark(None, stop_iter, True)

                    i += 3
                elif text[i+2] == "%":
                    # MIRROR
                    mirror_iter = buffer.get_iter_at_mark(buffer.get_insert())
                    begin_mark = buffer.create_mark(None, mirror_iter, True)
                    end_mark = buffer.create_mark(None, mirror_iter, True)
                    
                    #begin_mark.set_visible(True)
                    
                    # Get mirror number
                    j = i+3
                    num = []

                    while char != '}' and char != '/':
                        char = text[j]
                        num.append(char)
                        j += 1

                    mirror_num = int("".join(num[:-1]))
                    i = j-1
                    
                    if char == '/':
                        k = i
                        brace_count = 1
                        
                        while True:
                        
                            if text[k] == '{':
                                brace_count += 1
                            elif text[k] == '}':
                                brace_count -= 1
                                
                            if brace_count == 0:
                                break
                                
                            k += 1
                        
                        regexp = text[i+1:k].split('/')
                        i = k
                        
                        m = SnippetMirror(self.view, mirror_num, 
                                      (begin_mark, end_mark))
                        
                        m.regexp = (regexp[0], regexp[1])
                    
                    else:
                        m = SnippetMirror(self.view, mirror_num, 
                                          (begin_mark, end_mark))
                    mirrors.append(m)
                else:
                    # FIELD
                    j = i+2
                    num = []
                    
                    char = text[j]
                    while char != ':':
                        num.append(char)
                        j+=1
                        char = text[j]

                    num = int("".join(num))
                    
                    field_iter = buffer.get_iter_at_mark(buffer.get_insert())
                    begin_mark = buffer.create_mark(None, field_iter, True)
                    #begin_mark.set_visible(True)
                    
                    f = SnippetField(self.view, num, (begin_mark,))
                    
                    root = fields.add(f, root)
                    stack.append(root)

                    i = j
                    
            elif char == '}':
                if len(stack) > 0:
                    node = stack.pop()
                    
                    if len(stack) == 0:
                        root = root_init
                    
                    bm = node.elem.marks[0]
                    end_iter = buffer.get_iter_at_mark(buffer.get_insert())
                    em = buffer.create_mark(None, end_iter, True)
                    #em.set_visible(True)
                    node.elem.marks = (bm, em)

                elif len(stack) == 0:
                    root = root_init
                    self.view.buffer.insert_at_cursor(char)
                else:
                    root = stack[-1]

            elif char == '\t':
                if spaces:
                    self.view.buffer.insert_at_cursor(" " * tab_width)
                else:
                    self.view.buffer.insert_at_cursor(char)
            elif char == '\n':
                # LINE BREAK
                buffer.insert_at_cursor("\n")
                buffer.insert_at_cursor(indent)
            else:
                self.view.buffer.insert_at_cursor(char)
            
            i+=1
        
        #Not well-formed snippet
        if len(stack) > 0:
            fields.pre_order(self.__disconnect_field_signal)
            return
            
        # Change stop gravity
        if stop != None:
            stop_iter = buffer.get_iter_at_mark(stop)
            buffer.delete_mark(stop)
            stop = buffer.create_mark(None, stop_iter, False)
            #stop.set_visible(True)
            
        # Change mirrors gravity
        for i in range(len(mirrors)):
            m = mirrors[i].marks[1]
            n = mirrors[i].marks[0]
            m_iter = buffer.get_iter_at_mark(m)
            buffer.delete_mark(m)
            new_m = buffer.create_mark(None, m_iter, False)
            #new_m.set_visible(True)
            mirrors[i].marks = (n, new_m)
            
        # Change fields gravity
        fields.pre_order(self.__fields_change_gravity)
        
        # Change begin bound gravity
        m = begin_bound_mark
        m_iter = buffer.get_iter_at_mark(m)
        buffer.delete_mark(m)
        begin_bound_mark = buffer.create_mark(None, m_iter, False)
        #begin_bound_mark.set_visible(True)
        
        # Create end bound mark
        insert_iter = buffer.get_iter_at_mark(insert)
        end_bound_mark = buffer.create_mark(None, insert_iter, False)
        #end_bound_mark.set_visible(True)
        
#        print "root: ", fields.root
#        print "root's children: ", fields.root.children
        
        bounds = (begin_bound_mark, end_bound_mark)
        snippet = Snippet(self.document, fields, mirrors, stop, bounds)
        self.push_snippet(snippet)
        
        if len(snippet.fields.root.children) > 0:
            buffer.place_cursor(buffer.get_iter_at_mark(begin_bound_mark))
            self.next_field()
        else:
            self.pop_snippet()
        
    def __fields_change_gravity(self, node):
        """
        This method change gravity of a snippet field's right mark.
        
        @param node: A snippet field.
        @type node: A TreeNode.
        """
        buffer = self.view.buffer
        field = node.elem
        
        if field != None:
            m = field.marks[1]
            n = field.marks[0]
            
            m_iter = buffer.get_iter_at_mark(m)
            buffer.delete_mark(m)
            new_m = buffer.create_mark(None, m_iter, False)
            #new_m.set_visible(True)
            field.marks = (n, new_m)
            
    def __disconnect_field_signal(self, node):
        """
        Disconnect all field's signals.
        
        @param node: A snippet field. 
        @type node:  A TreeNode.
        """
        field = node.elem
        if field != None:
            if field.id != None:
                field.view.disconnect(field.id)
                
    def __show_error_dialog(self):
        """
        Show the snippet error dialog.
        """
        dialog = gtk.MessageDialog(None, 0, gtk.MESSAGE_ERROR,
                                   gtk.BUTTONS_NONE, None)
        dialog.format_secondary_text(constants.MESSAGE_0012)
        dialog.set_markup(constants.MESSAGE_0011)
        dialog.add_button(gtk.STOCK_OK, 1)
        dialog.run()
        dialog.destroy()

    def __get_key(self):
        """
        Get the snippet key from textbuffer according cursor position.
        """
        buffer = self.view.buffer
        insert_mark = buffer.get_insert()
        insert_iter = buffer.get_iter_at_mark(insert_mark)
        start_iter = insert_iter.copy()

        self.__find_word_start(start_iter)
        key = buffer.get_text(start_iter, insert_iter)
        
        return key
    
    def __find_word_start(self, iterator):
        """
        Find the start of a word.
        
        @param iterator: A iterator at end of a word.
        @type iterator: A TextIter.
        """
        pattern = re.compile("[a-z|A-Z|0-9|<|>|/]")
        symbols = ('!', '@', '#', '$', '%', '&', '*',
                   '(', ')', '-' ,'+', '.', ',', '~', '^')
        iterator.backward_char()
        if iterator.get_char() in symbols:
            return
        while True:
            char = iterator.get_char()
            if not(re.match(pattern, char)):
                iterator.forward_char()
                return
            elif iterator.starts_line():
                return
            else:
                iterator.backward_char()

    def __destroy(self):
        """
        Destroy the snippet popup window.
        """
        self.view.disconnect(self.id_event_after)
        self.view.disconnect(self.id_key_press)

        notebook = self.view.get_parent().get_parent().get_parent()
        notebook.disconnect(self.id_switch_page)
        
        self.pw.destroy()
        self.pw = None