Search in sources :

Example 1 with CharacterPosition

use of com.maddyhome.idea.vim.common.CharacterPosition in project ideavim by JetBrains.

the class SearchGroup method searchAndReplace.

public boolean searchAndReplace(@NotNull Editor editor, @NotNull LineRange range, @NotNull String excmd, String exarg) {
    // Explicitly exit visual mode here, so that visual mode marks don't change when we move the cursor to a match.
    if (CommandState.getInstance(editor).getMode() == CommandState.Mode.VISUAL) {
        VimPlugin.getMotion().exitVisual(editor);
    }
    CharPointer cmd = new CharPointer(new StringBuffer(exarg));
    //sub_nsubs = 0;
    //sub_nlines = 0;
    int which_pat;
    if (excmd.equals("~")) {
        which_pat = RE_LAST;
    /* use last used regexp */
    } else {
        which_pat = RE_SUBST;
    /* use last substitute regexp */
    }
    CharPointer pat;
    CharPointer sub;
    char delimiter;
    /* new pattern and substitution */
    if (excmd.charAt(0) == 's' && !cmd.isNul() && !Character.isWhitespace(cmd.charAt()) && "0123456789cegriIp|\"".indexOf(cmd.charAt()) == -1) {
        /* don't accept alphanumeric for separator */
        if (CharacterClasses.isAlpha(cmd.charAt())) {
            VimPlugin.showMessage(MessageHelper.message(Msg.E146));
            return false;
        }
        /*
      * undocumented vi feature:
      *  "\/sub/" and "\?sub?" use last used search pattern (almost like
      *  //sub/r).  "\&sub&" use last substitute pattern (like //sub/).
      */
        if (cmd.charAt() == '\\') {
            cmd.inc();
            if ("/?&".indexOf(cmd.charAt()) == -1) {
                VimPlugin.showMessage(MessageHelper.message(Msg.e_backslash));
                return false;
            }
            if (cmd.charAt() != '&') {
                which_pat = RE_SEARCH;
            /* use last '/' pattern */
            }
            pat = new CharPointer("");
            /* empty search pattern */
            delimiter = cmd.charAt();
            /* remember delimiter character */
            cmd.inc();
        } else /* find the end of the regexp */
        {
            which_pat = RE_LAST;
            /* use last used regexp */
            delimiter = cmd.charAt();
            /* remember delimiter character */
            cmd.inc();
            pat = cmd.ref(0);
            /* remember start of search pat */
            cmd = RegExp.skip_regexp(cmd, delimiter, true);
            if (cmd.charAt() == delimiter) /* end delimiter found */
            {
                cmd.set('').inc();
            /* replace it with a NUL */
            }
        }
        /*
      * Small incompatibility: vi sees '\n' as end of the command, but in
      * Vim we want to use '\n' to find/substitute a NUL.
      */
        sub = cmd.ref(0);
        while (!cmd.isNul()) {
            if (cmd.charAt() == delimiter) /* end delimiter found */
            {
                cmd.set('').inc();
                /* replace it with a NUL */
                break;
            }
            if (cmd.charAt(0) == '\\' && cmd.charAt(1) != 0) /* skip escaped characters */
            {
                cmd.inc();
            }
            cmd.inc();
        }
    } else /* use previous pattern and substitution */
    {
        if (lastReplace == null) /* there is no previous command */
        {
            VimPlugin.showMessage(MessageHelper.message(Msg.e_nopresub));
            return false;
        }
        pat = null;
        /* search_regcomp() will use previous pattern */
        sub = new CharPointer(lastReplace);
    }
    /*
    * Find trailing options.  When '&' is used, keep old options.
    */
    if (cmd.charAt() == '&') {
        cmd.inc();
    } else {
        do_all = Options.getInstance().isSet("gdefault");
        do_ask = false;
        do_error = true;
        //do_print = false;
        do_ic = 0;
    }
    while (!cmd.isNul()) {
        /*
      * Note that 'g' and 'c' are always inverted, also when p_ed is off.
      * 'r' is never inverted.
      */
        if (cmd.charAt() == 'g') {
            do_all = !do_all;
        } else if (cmd.charAt() == 'c') {
            do_ask = !do_ask;
        } else if (cmd.charAt() == 'e') {
            do_error = !do_error;
        } else if (cmd.charAt() == 'r') /* use last used regexp */
        {
            which_pat = RE_LAST;
        } else if (cmd.charAt() == 'i') /* ignore case */
        {
            do_ic = 'i';
        } else if (cmd.charAt() == 'I') /* don't ignore case */
        {
            do_ic = 'I';
        } else if (cmd.charAt() != 'p') {
            break;
        }
        cmd.inc();
    }
    int line1 = range.getStartLine();
    int line2 = range.getEndLine();
    if (line1 < 0 || line2 < 0) {
        return false;
    }
    /*
    * check for a trailing count
    */
    cmd = CharHelper.skipwhite(cmd);
    if (CharacterClasses.isDigit(cmd.charAt())) {
        int i = CharHelper.getdigits(cmd);
        if (i <= 0 && do_error) {
            VimPlugin.showMessage(MessageHelper.message(Msg.e_zerocount));
            return false;
        }
        line1 = line2;
        line2 = EditorHelper.normalizeLine(editor, line1 + i - 1);
    }
    /*
    * check for trailing command or garbage
    */
    cmd = CharHelper.skipwhite(cmd);
    if (!cmd.isNul() && cmd.charAt() != '"') /* if not end-of-line or comment */
    {
        VimPlugin.showMessage(MessageHelper.message(Msg.e_trailing));
        return false;
    }
    String pattern = "";
    if (pat == null || pat.isNul()) {
        switch(which_pat) {
            case RE_LAST:
                pattern = lastPattern;
                break;
            case RE_SEARCH:
                pattern = lastSearch;
                break;
            case RE_SUBST:
                pattern = lastSubstitute;
                break;
        }
    } else {
        pattern = pat.toString();
    }
    lastSubstitute = pattern;
    lastSearch = pattern;
    if (pattern != null) {
        setLastPattern(editor, pattern);
    }
    //int start = editor.logicalPositionToOffset(new LogicalPosition(line1, 0));
    //int end = editor.logicalPositionToOffset(new LogicalPosition(line2, EditorHelper.getLineLength(editor, line2)));
    int start = editor.getDocument().getLineStartOffset(line1);
    int end = editor.getDocument().getLineEndOffset(line2);
    RegExp sp;
    RegExp.regmmatch_T regmatch = new RegExp.regmmatch_T();
    sp = new RegExp();
    regmatch.regprog = sp.vim_regcomp(pattern, 1);
    if (regmatch.regprog == null) {
        if (do_error) {
            VimPlugin.showMessage(MessageHelper.message(Msg.e_invcmd));
        }
        return false;
    }
    /* the 'i' or 'I' flag overrules 'ignorecase' and 'smartcase' */
    if (do_ic == 'i') {
        regmatch.rmm_ic = true;
    } else if (do_ic == 'I') {
        regmatch.rmm_ic = false;
    }
    /*
    * ~ in the substitute pattern is replaced with the old pattern.
    * We do it here once to avoid it to be replaced over and over again.
    * But don't do it when it starts with "\=", then it's an expression.
    */
    if (!(sub.charAt(0) == '\\' && sub.charAt(1) == '=') && lastReplace != null) {
        StringBuffer tmp = new StringBuffer(sub.toString());
        int pos = 0;
        while ((pos = tmp.indexOf("~", pos)) != -1) {
            if (pos == 0 || tmp.charAt(pos - 1) != '\\') {
                tmp.replace(pos, pos + 1, lastReplace);
                pos += lastReplace.length();
            }
            pos++;
        }
        sub = new CharPointer(tmp);
    }
    lastReplace = sub.toString();
    searchHighlight(false);
    if (logger.isDebugEnabled()) {
        logger.debug("search range=[" + start + "," + end + "]");
        logger.debug("pattern=" + pattern + ", replace=" + sub);
    }
    int lastMatch = -1;
    int lastLine = -1;
    int searchcol = 0;
    boolean firstMatch = true;
    boolean got_quit = false;
    int lcount = EditorHelper.getLineCount(editor);
    for (int lnum = line1; lnum <= line2 && !got_quit; ) {
        CharacterPosition newpos = null;
        int nmatch = sp.vim_regexec_multi(regmatch, editor, lcount, lnum, searchcol);
        if (nmatch > 0) {
            if (firstMatch) {
                VimPlugin.getMark().saveJumpLocation(editor);
                firstMatch = false;
            }
            String match = sp.vim_regsub_multi(regmatch, lnum, sub, 1, false);
            if (match == null) {
                return false;
            }
            //logger.debug("found match[" + spos + "," + epos + "] - replace " + match);
            int line = lnum + regmatch.startpos[0].lnum;
            CharacterPosition startpos = new CharacterPosition(lnum + regmatch.startpos[0].lnum, regmatch.startpos[0].col);
            CharacterPosition endpos = new CharacterPosition(lnum + regmatch.endpos[0].lnum, regmatch.endpos[0].col);
            int startoff = EditorHelper.characterPositionToOffset(editor, startpos);
            int endoff = EditorHelper.characterPositionToOffset(editor, endpos);
            int newend = startoff + match.length();
            if (do_all || line != lastLine) {
                boolean doReplace = true;
                if (do_ask) {
                    RangeHighlighter hl = highlightConfirm(editor, startoff, endoff);
                    MotionGroup.scrollPositionIntoView(editor, editor.offsetToVisualPosition(startoff), true);
                    MotionGroup.moveCaret(editor, startoff);
                    final ReplaceConfirmationChoice choice = confirmChoice(editor, match);
                    editor.getMarkupModel().removeHighlighter(hl);
                    switch(choice) {
                        case SUBSTITUTE_THIS:
                            doReplace = true;
                            break;
                        case SKIP:
                            doReplace = false;
                            break;
                        case SUBSTITUTE_ALL:
                            do_ask = false;
                            break;
                        case QUIT:
                            doReplace = false;
                            got_quit = true;
                            break;
                        case SUBSTITUTE_LAST:
                            do_all = false;
                            line2 = lnum;
                            doReplace = true;
                            break;
                    }
                }
                if (doReplace) {
                    editor.getDocument().replaceString(startoff, endoff, match);
                    lastMatch = startoff;
                    newpos = EditorHelper.offsetToCharacterPosition(editor, newend);
                    lnum += newpos.line - endpos.line;
                    line2 += newpos.line - endpos.line;
                }
            }
            lastLine = line;
            lnum += nmatch - 1;
            if (do_all && startoff != endoff) {
                if (newpos != null) {
                    lnum = newpos.line;
                    searchcol = newpos.column;
                } else {
                    searchcol = endpos.column;
                }
            } else {
                searchcol = 0;
                lnum++;
            }
        } else {
            lnum++;
            searchcol = 0;
        }
    }
    if (lastMatch != -1) {
        MotionGroup.moveCaret(editor, VimPlugin.getMotion().moveCaretToLineStartSkipLeading(editor, editor.offsetToLogicalPosition(lastMatch).line));
    } else {
        VimPlugin.showMessage(MessageHelper.message(Msg.e_patnotf2, pattern));
    }
    return true;
}
Also used : RangeHighlighter(com.intellij.openapi.editor.markup.RangeHighlighter) RegExp(com.maddyhome.idea.vim.regexp.RegExp) CharacterPosition(com.maddyhome.idea.vim.common.CharacterPosition) CharPointer(com.maddyhome.idea.vim.regexp.CharPointer)

Example 2 with CharacterPosition

use of com.maddyhome.idea.vim.common.CharacterPosition in project ideavim by JetBrains.

the class SearchGroup method findIt.

@Nullable
private TextRange findIt(@NotNull Editor editor, int startOffset, int count, int dir, boolean noSmartCase, boolean wrap, boolean showMessages, boolean wholeFile) {
    TextRange res = null;
    if (lastSearch == null || lastSearch.length() == 0) {
        return res;
    }
    /*
    int pflags = RE.REG_MULTILINE;
    if (shouldIgnoreCase(lastSearch, noSmartCase))
    {
        pflags |= RE.REG_ICASE;
    }
    */
    //RE sp;
    RegExp sp;
    RegExp.regmmatch_T regmatch = new RegExp.regmmatch_T();
    regmatch.rmm_ic = shouldIgnoreCase(lastSearch, noSmartCase);
    sp = new RegExp();
    regmatch.regprog = sp.vim_regcomp(lastSearch, 1);
    if (regmatch == null) {
        if (logger.isDebugEnabled())
            logger.debug("bad pattern: " + lastSearch);
        return res;
    }
    /*
    int extra_col = 1;
    int startcol = -1;
    boolean found = false;
    boolean match_ok = true;
    LogicalPosition pos = editor.offsetToLogicalPosition(startOffset);
    LogicalPosition endpos = null;
    //REMatch match = null;
    */
    CharacterPosition lpos = EditorHelper.offsetToCharacterPosition(editor, startOffset);
    RegExp.lpos_T pos = new RegExp.lpos_T();
    pos.lnum = lpos.line;
    pos.col = lpos.column;
    int found;
    int lnum;
    /* no init to shut up Apollo cc */
    //RegExp.regmmatch_T regmatch;
    CharPointer ptr;
    int matchcol;
    int startcol;
    RegExp.lpos_T endpos = new RegExp.lpos_T();
    int loop;
    RegExp.lpos_T start_pos;
    boolean at_first_line;
    int extra_col = 1;
    boolean match_ok;
    long nmatched;
    //int         submatch = 0;
    int first_lnum;
    int lineCount = EditorHelper.getLineCount(editor);
    int startLine = 0;
    int endLine = lineCount;
    do /* loop for count */
    {
        start_pos = new RegExp.lpos_T(pos);
        /* remember start pos for detecting no match */
        found = 0;
        /* default: not found */
        at_first_line = true;
        /* default: start in first line */
        if (pos.lnum == -1) /* correct lnum for when starting in line 0 */
        {
            pos.lnum = 0;
            pos.col = 0;
            at_first_line = false;
        /* not in first line now */
        }
        /*
      * Start searching in current line, unless searching backwards and
      * we're in column 0.
      */
        if (dir == -1 && start_pos.col == 0) {
            lnum = pos.lnum - 1;
            at_first_line = false;
        } else {
            lnum = pos.lnum;
        }
        int lcount = EditorHelper.getLineCount(editor);
        for (loop = 0; loop <= 1; ++loop) /* loop twice if 'wrapscan' set */
        {
            if (!wholeFile) {
                startLine = lnum;
                endLine = lnum + 1;
            }
            for (; lnum >= startLine && lnum < endLine; lnum += dir, at_first_line = false) {
                /*
          * Look for a match somewhere in the line.
          */
                first_lnum = lnum;
                nmatched = sp.vim_regexec_multi(regmatch, editor, lcount, lnum, 0);
                if (nmatched > 0) {
                    /* match may actually be in another line when using \zs */
                    lnum += regmatch.startpos[0].lnum;
                    ptr = new CharPointer(EditorHelper.getLineBuffer(editor, lnum));
                    startcol = regmatch.startpos[0].col;
                    endpos = regmatch.endpos[0];
                    /*
            * Forward search in the first line: match should be after
            * the start position. If not, continue at the end of the
            * match (this is vi compatible) or on the next char.
            */
                    if (dir == 1 && at_first_line) {
                        match_ok = true;
                        /*
              * When match lands on a NUL the cursor will be put
              * one back afterwards, compare with that position,
              * otherwise "/$" will get stuck on end of line.
              */
                        while ((startcol - (startcol == ptr.strlen() ? 1 : 0)) < (start_pos.col + extra_col)) {
                            if (nmatched > 1) {
                                /* end is in next line, thus no match in
               * this line */
                                match_ok = false;
                                break;
                            }
                            matchcol = endpos.col;
                            /* for empty match: advance one char */
                            if (matchcol == startcol && ptr.charAt(matchcol) != '') {
                                ++matchcol;
                            }
                            if (ptr.charAt(matchcol) == '' || (nmatched = sp.vim_regexec_multi(regmatch, editor, lcount, lnum, matchcol)) == 0) {
                                match_ok = false;
                                break;
                            }
                            startcol = regmatch.startpos[0].col;
                            endpos = regmatch.endpos[0];
                            /* Need to get the line pointer again, a
        * multi-line search may have made it invalid. */
                            ptr = new CharPointer(EditorHelper.getLineBuffer(editor, lnum));
                        }
                        if (!match_ok) {
                            continue;
                        }
                    }
                    if (dir == -1) {
                        /*
              * Now, if there are multiple matches on this line,
              * we have to get the last one. Or the last one before
              * the cursor, if we're on that line.
              * When putting the new cursor at the end, compare
              * relative to the end of the match.
              */
                        match_ok = false;
                        for (; ; ) {
                            if (!at_first_line || (regmatch.startpos[0].col + extra_col <= start_pos.col)) {
                                /* Remember this position, we use it if it's
           * the last match in the line. */
                                match_ok = true;
                                startcol = regmatch.startpos[0].col;
                                endpos = regmatch.endpos[0];
                            } else {
                                break;
                            }
                            /*
                * We found a valid match, now check if there is
                * another one after it.
                * If vi-compatible searching, continue at the end
                * of the match, otherwise continue one position
                * forward.
                */
                            if (nmatched > 1) {
                                break;
                            }
                            matchcol = endpos.col;
                            /* for empty match: advance one char */
                            if (matchcol == startcol && ptr.charAt(matchcol) != '') {
                                ++matchcol;
                            }
                            if (ptr.charAt(matchcol) == '' || (nmatched = sp.vim_regexec_multi(regmatch, editor, lcount, lnum, matchcol)) == 0) {
                                break;
                            }
                            /* Need to get the line pointer again, a
        * multi-line search may have made it invalid. */
                            ptr = new CharPointer(EditorHelper.getLineBuffer(editor, lnum));
                        }
                        /*
              * If there is only a match after the cursor, skip
              * this match.
              */
                        if (!match_ok) {
                            continue;
                        }
                    }
                    pos.lnum = lnum;
                    pos.col = startcol;
                    endpos.lnum += first_lnum;
                    found = 1;
                    //search_match_endcol = endpos.col;
                    break;
                }
                if (loop != 0 && lnum == start_pos.lnum) {
                    break;
                /* if second loop, stop where started */
                }
            }
            at_first_line = false;
            /*
        * stop the search if wrapscan isn't set, after an interrupt and
        * after a match
        */
            if (!wrap || found != 0) {
                break;
            }
            /*
        * If 'wrapscan' is set we continue at the other end of the file.
        * If 'shortmess' does not contain 's', we give a message.
        * This message is also remembered in keep_msg for when the screen
        * is redrawn. The keep_msg is cleared whenever another message is
        * written.
        */
            if (dir == -1) /* start second loop at the other end */
            {
                lnum = lineCount - 1;
            //if (!shortmess(SHM_SEARCH) && (options & SEARCH_MSG))
            //    give_warning((char_u *)_(top_bot_msg), TRUE);
            } else {
                lnum = 0;
            //if (!shortmess(SHM_SEARCH) && (options & SEARCH_MSG))
            //    give_warning((char_u *)_(bot_top_msg), TRUE);
            }
        }
    //if (got_int || called_emsg || break_loop)
    //    break;
    } while (--count > 0 && found != 0);
    if (found == 0) /* did not find it */
    {
        //if ((options & SEARCH_MSG) == SEARCH_MSG)
        if (showMessages) {
            if (wrap) {
                VimPlugin.showMessage(MessageHelper.message(Msg.e_patnotf2, lastSearch));
            } else if (lnum <= 0) {
                VimPlugin.showMessage(MessageHelper.message(Msg.E384, lastSearch));
            } else {
                VimPlugin.showMessage(MessageHelper.message(Msg.E385, lastSearch));
            }
        }
        return null;
    }
    //    editor.logicalPositionToOffset(new LogicalPosition(endpos.lnum, 0)) + endpos.col);
    return new TextRange(EditorHelper.characterPositionToOffset(editor, new CharacterPosition(pos.lnum, pos.col)), EditorHelper.characterPositionToOffset(editor, new CharacterPosition(endpos.lnum, endpos.col)));
}
Also used : RegExp(com.maddyhome.idea.vim.regexp.RegExp) CharacterPosition(com.maddyhome.idea.vim.common.CharacterPosition) TextRange(com.maddyhome.idea.vim.common.TextRange) CharPointer(com.maddyhome.idea.vim.regexp.CharPointer) Nullable(org.jetbrains.annotations.Nullable)

Example 3 with CharacterPosition

use of com.maddyhome.idea.vim.common.CharacterPosition in project ideavim by JetBrains.

the class SearchGroup method findAll.

@NotNull
private static List<TextRange> findAll(@NotNull Editor editor, @NotNull String pattern, int startLine, int endLine, boolean ignoreCase) {
    final List<TextRange> results = Lists.newArrayList();
    final int lineCount = EditorHelper.getLineCount(editor);
    final int actualEndLine = endLine == -1 ? lineCount : endLine;
    final RegExp.regmmatch_T regMatch = new RegExp.regmmatch_T();
    final RegExp regExp = new RegExp();
    regMatch.regprog = regExp.vim_regcomp(pattern, 1);
    if (regMatch.regprog == null) {
        return results;
    }
    regMatch.rmm_ic = ignoreCase;
    int col = 0;
    for (int line = startLine; line <= actualEndLine; ) {
        int matchedLines = regExp.vim_regexec_multi(regMatch, editor, lineCount, line, col);
        if (matchedLines > 0) {
            final CharacterPosition startPos = new CharacterPosition(line + regMatch.startpos[0].lnum, regMatch.startpos[0].col);
            final CharacterPosition endPos = new CharacterPosition(line + regMatch.endpos[0].lnum, regMatch.endpos[0].col);
            int start = EditorHelper.characterPositionToOffset(editor, startPos);
            int end = EditorHelper.characterPositionToOffset(editor, endPos);
            results.add(new TextRange(start, end));
            if (start != end) {
                line += matchedLines - 1;
                col = endPos.column;
            } else {
                line += matchedLines;
                col = 0;
            }
        } else {
            line++;
            col = 0;
        }
    }
    return results;
}
Also used : RegExp(com.maddyhome.idea.vim.regexp.RegExp) CharacterPosition(com.maddyhome.idea.vim.common.CharacterPosition) TextRange(com.maddyhome.idea.vim.common.TextRange) NotNull(org.jetbrains.annotations.NotNull)

Aggregations

CharacterPosition (com.maddyhome.idea.vim.common.CharacterPosition)3 RegExp (com.maddyhome.idea.vim.regexp.RegExp)3 TextRange (com.maddyhome.idea.vim.common.TextRange)2 CharPointer (com.maddyhome.idea.vim.regexp.CharPointer)2 RangeHighlighter (com.intellij.openapi.editor.markup.RangeHighlighter)1 NotNull (org.jetbrains.annotations.NotNull)1 Nullable (org.jetbrains.annotations.Nullable)1