Beispiel #1
0
    def __init__(self, parent, indent, initial_text, start=None, end=None):
        if start is None:
            start = Position(0, 0)
        if end is None:
            end = Position(0, 0)

        TextObject.__init__(self, parent, start, end, initial_text)

        _TOParser(self, initial_text, indent).parse()

        # Check if we have a zero Tab, if not, add one at the end
        if isinstance(parent, TabStop):
            if not parent.no == 0:
                # We are recursively called, if we have a zero tab, remove it.
                if 0 in self._tabstops:
                    self._tabstops[0].current_text = ""
                    del self._tabstops[0]
        else:
            self.update()
            if 0 not in self._tabstops:
                delta = self._end - self._start
                col = self.end.col
                if delta.line == 0:
                    col -= self.start.col
                start = Position(delta.line, col)
                end = Position(delta.line, col)
                ts = TabStop(0, self, start, end, "")
                self._add_tabstop(0, ts)

                self.update()
Beispiel #2
0
 def calc_end(self, start):
     text = self._lines[:]
     if len(text) == 1:
         new_end = start + Position(0, len(text[0]))
     else:
         new_end = Position(start.line + len(text) - 1, len(text[-1]))
     return new_end
Beispiel #3
0
    def _replace(self, start, end, content, first_line, last_line):
        text = content[:]
        if len(text) == 1:
            arr = [first_line + text[0] + last_line]
            new_end = start + Position(0, len(text[0]))
        else:
            arr = [ first_line + text[0] ] + \
                    text[1:-1] + \
                    [ text[-1] + last_line ]
            new_end = Position(start.line + len(text) - 1, len(text[-1]))

        self[start.line:end.line + 1] = arr

        return new_end
Beispiel #4
0
    def update(self):
        line, col = vim_cursor()
        line -= 1
        abs_pos = Position(line,col)
        if self._abs_pos:
            self._moved = abs_pos - self._abs_pos
        self._abs_pos = abs_pos

        # Update buffer infos
        cols = len(as_unicode(vim.current.buffer[line]))
        if self._cols:
            self._dcols = cols - self._cols
        self._cols = cols

        lines = len(vim.current.buffer)
        if self._lines:
            self._dlines = lines - self._lines
        self._lines = lines

        # Check if the buffer has changed in any ways
        self._text_changed = False
        # does it have more lines?
        if self._dlines:
            self._text_changed = True
        # did we stay in the same line and it has more columns now?
        elif not self.moved.line and self._dcols:
            self._text_changed = True
        # If the length didn't change but we moved a column, check if
        # the char under the cursor has changed (might be one char tab).
        elif self.moved.col == 1:
            self._text_changed = self._cline != as_unicode(vim.current.buffer[line])
        self._lline = self._cline
        self._cline = as_unicode(vim.current.buffer[line])
Beispiel #5
0
 def abs_start(self):
     if self._parent:
         ps = self._parent.abs_start
         if self._start.line == 0:
             return ps + self._start
         else:
             return Position(ps.line + self._start.line, self._start.col)
     return self._start
Beispiel #6
0
    def _do_snippet(self, snippet, before, after):
        """ Expands the given snippet, and handles everything
        that needs to be done with it. 'before' and 'after' should
        come from _get_before_after.
        """
        lineno, col = vim_cursor()
        # Adjust before, maybe the trigger is not the complete word

        text_before = before
        if snippet.matched:
            text_before = before[:-len(snippet.matched)]

        self._unset_offending_vim_options(snippet)

        self._expect_move_wo_change = True
        if self._cs:
            # Determine position
            pos = self._vstate.pos
            p_start = self._ctab.abs_start

            if pos.line == p_start.line:
                end = Position(0, pos.col - p_start.col)
            else:
                end = Position(pos.line - p_start.line, pos.col)
            start = Position(end.line, end.col - len(snippet.matched))

            si = snippet.launch(text_before, self._visual_content, self._ctab, start, end)
            self._visual_content = ""

            self._update_vim_buffer()

            if si.has_tabs:
                self._csnippets.append(si)
                self._jump()
        else:
            self._vb = VimBuffer(text_before, after)

            start = Position(lineno-1, len(text_before))
            self._csnippets.append(snippet.launch(text_before, self._visual_content, None, start))
            self._visual_content = ""

            self._vb.replace_lines(lineno-1, lineno-1,
                       self._cs._current_text)

            self._jump()
Beispiel #7
0
    def __init__(self):
        self._abs_pos = None
        self._moved = Position(0,0)

        self._lines = None
        self._dlines = None
        self._cols = None
        self._dcols = None
        self._cline = None
        self._lline = None
        self._text_changed = None
Beispiel #8
0
        def _update_childs(childs):
            for idx, c in childs:
                oldend = Position(c.end.line, c.end.col)

                new_end = c.update()

                moved_lines = new_end.line - oldend.line
                moved_cols = new_end.col - oldend.col

                self._current_text.replace_text(c.start, oldend,
                                                c._current_text)

                self._move_textobjects_behind(c.start, oldend, moved_lines,
                                              moved_cols, idx)
Beispiel #9
0
    def update(self):
        for idx, c in enumerate(self._childs):
            oldend = Position(c.end.line, c.end.col)

            new_end = c.update()

            moved_lines = new_end.line - oldend.line
            moved_cols = new_end.col - oldend.col

            self._current_text.replace_text(c.start, oldend, c._current_text)

            self._move_textobjects_behind(c.start, oldend, moved_lines,
                                          moved_cols, idx)

        self._do_update()

        new_end = self._current_text.calc_end(self._start)

        self._end = new_end

        return new_end
Beispiel #10
0
    def list_snippets(self):
        filetypes = self._ensure_snippets_loaded()

        # TODO: this code is duplicated below
        filetypes = vim.eval("&filetype").split(".") + ["all"]
        lineno, col = vim.current.window.cursor

        line = vim.current.line
        before, after = line[:col], line[col:]

        word = ''
        if len(before):
            word = before.split()[-1]

        found_snippets = []
        for ft in filetypes[::-1]:
            found_snippets += self._find_snippets(ft, word, True)

        if len(found_snippets) == 0:
            return True

        display = [
            "%i %s" % (idx + 1, s.description)
            for idx, s in enumerate(found_snippets)
        ]

        # TODO: this code is also mirrored below
        try:
            rv = vim.eval("inputlist(%s)" % display)
            if rv is None or rv == '0':
                return True
            rv = int(rv)
            if rv > len(found_snippets):
                rv = len(found_snippets)
            snippet = found_snippets[rv - 1]
        except:  # vim.error, e:
            if str(e) == 'invalid expression':
                return True
            raise

        # TODO: even more code duplicated below
        # Adjust before, maybe the trigger is not the complete word
        text_before = before.rstrip()[:-len(word)]
        text_before += word[:-len(snippet.trigger)]

        self._expect_move_wo_change = True
        if self._cs:
            # Determine position
            pos = self._vstate.pos
            p_start = self._ctab.abs_start

            if pos.line == p_start.line:
                end = Position(0, pos.col - p_start.col)
            else:
                end = Position(pos.line - p_start.line, pos.col)
            start = Position(end.line, end.col - len(snippet.trigger))

            si = snippet.launch(text_before, self._ctab, start, end)

            self._update_vim_buffer()

            if si.has_tabs:
                self._csnippets.append(si)
                self._jump()
        else:
            self._vb = VimBuffer(text_before, after)

            start = Position(lineno - 1, len(text_before))
            self._csnippets.append(snippet.launch(text_before, None, start))

            self._vb.replace_lines(lineno - 1, lineno - 1,
                                   self._cs._current_text)

            self._jump()

        return True
Beispiel #11
0
class SnippetManager(object):
    def __init__(self):
        self._vstate = VimState()
        self._supertab_keys = None

        self.reset()

    def reset(self, test_error=False):
        self._test_error = test_error
        self._snippets = {}
        self._csnippets = []
        self._reinit()

    def jump_forwards(self):
        if not self._jump():
            return self._handle_failure(self.forward_trigger)

    def jump_backwards(self):
        if not self._jump(True):
            return self._handle_failure(self.backward_trigger)

    def expand(self):
        if not self._try_expand():
            self._handle_failure(self.expand_trigger)

    def list_snippets(self):
        filetypes = self._ensure_snippets_loaded()

        # TODO: this code is duplicated below
        filetypes = vim.eval("&filetype").split(".") + ["all"]
        lineno, col = vim.current.window.cursor

        line = vim.current.line
        before, after = line[:col], line[col:]

        word = ''
        if len(before):
            word = before.split()[-1]

        found_snippets = []
        for ft in filetypes[::-1]:
            found_snippets += self._find_snippets(ft, word, True)

        if len(found_snippets) == 0:
            return True

        display = [
            "%i %s" % (idx + 1, s.description)
            for idx, s in enumerate(found_snippets)
        ]

        # TODO: this code is also mirrored below
        try:
            rv = vim.eval("inputlist(%s)" % display)
            if rv is None or rv == '0':
                return True
            rv = int(rv)
            if rv > len(found_snippets):
                rv = len(found_snippets)
            snippet = found_snippets[rv - 1]
        except:  # vim.error, e:
            if str(e) == 'invalid expression':
                return True
            raise

        # TODO: even more code duplicated below
        # Adjust before, maybe the trigger is not the complete word
        text_before = before.rstrip()[:-len(word)]
        text_before += word[:-len(snippet.trigger)]

        self._expect_move_wo_change = True
        if self._cs:
            # Determine position
            pos = self._vstate.pos
            p_start = self._ctab.abs_start

            if pos.line == p_start.line:
                end = Position(0, pos.col - p_start.col)
            else:
                end = Position(pos.line - p_start.line, pos.col)
            start = Position(end.line, end.col - len(snippet.trigger))

            si = snippet.launch(text_before, self._ctab, start, end)

            self._update_vim_buffer()

            if si.has_tabs:
                self._csnippets.append(si)
                self._jump()
        else:
            self._vb = VimBuffer(text_before, after)

            start = Position(lineno - 1, len(text_before))
            self._csnippets.append(snippet.launch(text_before, None, start))

            self._vb.replace_lines(lineno - 1, lineno - 1,
                                   self._cs._current_text)

            self._jump()

        return True

    def expand_or_jump(self):
        """
        This function is used for people who wants to have the same trigger for
        expansion and forward jumping. It first tries to expand a snippet, if
        this fails, it tries to jump forward.
        """
        rv = self._try_expand()
        if not rv:
            rv = self._jump()
        if not rv:
            self._handle_failure(self.expand_trigger)

    def add_snippet(self, trigger, value, descr, options, ft="all"):
        if ft not in self._snippets:
            self._snippets[ft] = _SnippetDictionary()
        l = self._snippets[ft].add_snippet(
            Snippet(trigger, value, descr, options))

    def clear_snippets(self, triggers=[], ft="all"):
        if ft in self._snippets:
            self._snippets[ft].clear_snippets(triggers)

    def add_extending_info(self, ft, parents):
        if ft not in self._snippets:
            self._snippets[ft] = _SnippetDictionary()
        sd = self._snippets[ft]
        for p in parents:
            if p in sd.extends:
                continue

            sd.extends.append(p)

    def backspace_while_selected(self):
        """
        This is called when backspace was used while a placeholder was selected.
        """
        # BS was called in select mode

        if self._cs and (self._span_selected is not None):
            # This only happens when a default value is delted using backspace
            vim.command(r'call feedkeys("i")')
            self._chars_entered('')
        else:
            vim.command(r'call feedkeys("\<BS>")')

    def cursor_moved(self):
        self._vstate.update()

        if not self._vstate.buf_changed and not self._expect_move_wo_change:
            self._check_if_still_inside_snippet()

        if not self._ctab:
            return

        if self._vstate.buf_changed and self._ctab:
            # Detect a carriage return
            if self._vstate.moved.col <= 0 and self._vstate.moved.line == 1:
                # Multiple things might have happened: either the user entered
                # a newline character or pasted some text which means we have
                # to copy everything he entered on the last line and keep the
                # indent vim chose for this line.
                lline = vim.current.buffer[self._vstate.ppos.line]

                # Another thing that might have happened is that a word
                # wrapped, in this case the last line is shortened and we must
                # delete what Vim deleted there
                line_was_shortened = len(self._vstate.last_line) > len(lline)

                # Another thing that might have happened is that vim has
                # adjusted the indent of the last line and therefore the line
                # effectivly got longer. This means a newline was entered and
                # we quite definitivly do not want the indent that vim added
                line_was_lengthened = len(lline) > len(self._vstate.last_line)

                user_didnt_enter_newline = len(lline) != self._vstate.ppos.col
                cline = vim.current.buffer[self._vstate.pos.line]
                if line_was_lengthened:
                    this_entered = vim.current.line[:self._vstate.pos.col]
                    self._chars_entered('\n' + cline + this_entered, 1)
                if line_was_shortened and user_didnt_enter_newline:
                    self._backspace(len(self._vstate.last_line) - len(lline))
                    self._chars_entered('\n' + cline, 1)
                else:
                    pentered = lline[self._vstate.ppos.col:]
                    this_entered = vim.current.line[:self._vstate.pos.col]

                    self._chars_entered(pentered + '\n' + this_entered)
            elif self._vstate.moved.line == 0 and self._vstate.moved.col < 0:
                # Some deleting was going on
                self._backspace(-self._vstate.moved.col)
            elif self._vstate.moved.line < 0:
                # Backspace over line end
                self._backspace(1)
            else:
                line = vim.current.line

                chars = line[self._vstate.pos.col -
                             self._vstate.moved.col:self._vstate.pos.col]
                self._chars_entered(chars)

        self._expect_move_wo_change = False

    def entered_insert_mode(self):
        self._vstate.update()
        if self._cs and self._vstate.has_moved:
            self._reinit()
            self._csnippets = []

    ###################################
    # Private/Protect Functions Below #
    ###################################
    def _error(self, msg):
        msg = _vim_quote("UltiSnips: " + msg)
        if self._test_error:
            msg = msg.replace('"', r'\"')
            msg = msg.replace('|', r'\|')
            vim.command("let saved_pos=getpos('.')")
            vim.command("$:put =%s" % msg)
            vim.command("call setpos('.', saved_pos)")
        elif False:
            vim.command("echohl WarningMsg")
            vim.command("echomsg %s" % msg)
            vim.command("echohl None")
        else:
            vim.command("echoerr %s" % msg)

    def _reinit(self):
        self._ctab = None
        self._span_selected = None
        self._expect_move_wo_change = False

    def _check_if_still_inside_snippet(self):
        # Cursor moved without input.
        self._ctab = None

        # Did we leave the snippet with this movement?
        if self._cs and not (self._vstate.pos in self._cs.abs_span):
            self._csnippets.pop()

            self._reinit()

            self._check_if_still_inside_snippet()

    def _jump(self, backwards=False):
        jumped = False
        if self._cs:
            self._expect_move_wo_change = True
            self._ctab = self._cs.select_next_tab(backwards)
            if self._ctab:
                self._vstate.select_span(self._ctab.abs_span)
                self._span_selected = self._ctab.abs_span
                jumped = True
                if self._ctab.no == 0:
                    self._ctab = None
                    self._csnippets.pop()
                self._vstate.update()
            else:
                # This really shouldn't happen, because a snippet should
                # have been popped when its final tabstop was used.
                # Cleanup by removing current snippet and recursing.
                self._csnippets.pop()
                jumped = self._jump(backwards)
        return jumped

    def _handle_failure(self, trigger):
        """
        Mainly make sure that we play well with SuperTab
        """
        if trigger.lower() == "<tab>":
            feedkey = "\\" + trigger
        else:
            feedkey = None
        mode = "n"
        if not self._supertab_keys:
            if vim.eval("exists('g:SuperTabMappingForward')") != "0":
                self._supertab_keys = (
                    vim.eval("g:SuperTabMappingForward"),
                    vim.eval("g:SuperTabMappingBackward"),
                )
            else:
                self._supertab_keys = ['', '']

        for idx, sttrig in enumerate(self._supertab_keys):
            if trigger.lower() == sttrig.lower():
                if idx == 0:
                    feedkey = r"\<c-n>"
                elif idx == 1:
                    feedkey = r"\<c-p>"
                # Use remap mode so SuperTab mappings will be invoked.
                mode = "m"
                break

        if feedkey:
            vim.command(r'call feedkeys("%s", "%s")' % (feedkey, mode))

    def _ensure_snippets_loaded(self):
        filetypes = vim.eval("&filetype").split(".") + ["all"]
        for ft in filetypes[::-1]:
            if len(ft) and ft not in self._snippets:
                self._load_snippets_for(ft)

        return filetypes

    def _try_expand(self):
        filetypes = self._ensure_snippets_loaded()

        self._expect_move_wo_change = False

        lineno, col = vim.current.window.cursor
        if col == 0:
            return False

        line = vim.current.line

        if col > 0 and line[col - 1] in string.whitespace:
            return False

        # Get the word to the left of the current edit position
        before, after = line[:col], line[col:]

        word = before.split()[-1]
        found_snippets = []
        for ft in filetypes[::-1]:
            found_snippets += self._find_snippets(ft, word)

        # Search if any of the snippets overwrites the previous
        snippets = []
        for s in found_snippets:
            if s.overwrites_previous:
                snippets = []
            snippets.append(s)

        # Check if there are any only whitespace in front snippets
        text_before = before.rstrip()[:-len(word)]
        if text_before.strip(" \t") != '':
            snippets = [s for s in snippets if not s.needs_ws_in_front]

        if not len(snippets):
            # No snippet found
            return False
        elif len(snippets) == 1:
            snippet, = snippets
        else:
            display = repr([
                "%i: %s" % (i + 1, s.description)
                for i, s in enumerate(snippets)
            ])

            try:
                rv = vim.eval("inputlist(%s)" % display)
                if rv is None or rv == '0':
                    return True
                rv = int(rv)
                if rv > len(snippets):
                    rv = len(snippets)
                snippet = snippets[rv - 1]
            except vim.error, e:
                if str(e) == 'invalid expression':
                    return True
                raise

        # Adjust before, maybe the trigger is not the complete word
        text_before += word[:-len(snippet.trigger)]

        self._expect_move_wo_change = True
        if self._cs:
            # Determine position
            pos = self._vstate.pos
            p_start = self._ctab.abs_start

            if pos.line == p_start.line:
                end = Position(0, pos.col - p_start.col)
            else:
                end = Position(pos.line - p_start.line, pos.col)
            start = Position(end.line, end.col - len(snippet.trigger))

            si = snippet.launch(text_before, self._ctab, start, end)

            self._update_vim_buffer()

            if si.has_tabs:
                self._csnippets.append(si)
                self._jump()
        else:
            self._vb = VimBuffer(text_before, after)

            start = Position(lineno - 1, len(text_before))
            self._csnippets.append(snippet.launch(text_before, None, start))

            self._vb.replace_lines(lineno - 1, lineno - 1,
                                   self._cs._current_text)

            self._jump()

        return True
Beispiel #12
0
 def replace_lines(self, fline, eline, content):
     start = Position(fline, 0)
     end = Position(eline, 100000)
     return self._replace(start, end, content, self._bf, self._af)
Beispiel #13
0
 def __init__(self, start):
     end = Position(start.line, start.col)
     TextObject.__init__(self, None, start, end)
Beispiel #14
0
 def _get_pos(s, pos):
     line_idx = s[:pos].count('\n')
     line_start = s[:pos].rfind('\n') + 1
     start_in_line = pos - line_start
     return Position(line_idx, start_in_line)
Beispiel #15
0
 def pos(self):
     return Position(self._line, self._col)