Java Code Examples for org.netbeans.editor.Utilities#getRowEnd()

The following examples show how to use org.netbeans.editor.Utilities#getRowEnd() . You can vote up the ones you like or vote down the ones you don't like, and go to the original project or source file by following the links above each example. You may check out the related API usage on the sidebar.
Example 1
Source File: ToggleCommentAction.java    From netbeans with Apache License 2.0 6 votes vote down vote up
private void commentLine(BaseDocument doc, String mimeType, int offset) throws BadLocationException {
    Feature feature = null;
    try {
        Language language = LanguagesManager.getDefault().getLanguage(mimeType);
        feature = language.getFeatureList ().getFeature (CodeCommentAction.COMMENT_LINE);
    } catch (LanguageDefinitionNotFoundException e) {
    }
    if (feature != null) {
        String prefix = (String) feature.getValue("prefix"); // NOI18N
        if (prefix == null) {
            return;
        }
        String suffix = (String) feature.getValue("suffix"); // NOI18N
        if (suffix != null) {
            int end = Utilities.getRowEnd(doc, offset);
            doc.insertString(end, suffix, null);
        }
        doc.insertString(offset, prefix, null);
    }
}
 
Example 2
Source File: CssTypedBreakInterceptor.java    From netbeans with Apache License 2.0 6 votes vote down vote up
@Override
public void insert(MutableContext context) throws BadLocationException {
    int offset = context.getBreakInsertOffset();
    BaseDocument doc = (BaseDocument) context.getDocument();
    if (offset > 0 && offset < doc.getLength()) { //check corners
        String text = doc.getText(offset - 1, 2); //get char before and after
        if (TWO_CURLY_BRACES_IMAGE.equals(text)) { //NOI18N
            //context.setText("\n\n", 1, 1, 0, 2);
            //reformat workaround -- the preferred 
            //won't work as the reformatter will not reformat the line with the closing tag
            int from = Utilities.getRowStart(doc, offset);
            int to = Utilities.getRowEnd(doc, offset);
            reformat = new Position[]{doc.createPosition(from), doc.createPosition(to)};
            context.setText("\n\n", 1, 1);
        }
    }
}
 
Example 3
Source File: ExtKit.java    From netbeans with Apache License 2.0 6 votes vote down vote up
private boolean allComments(BaseDocument doc, int startOffset, int lineCount) throws BadLocationException {
    for (int offset = startOffset; lineCount > 0; lineCount--) {
        int firstNonWhitePos = Utilities.getRowFirstNonWhite(doc, offset);
        if (firstNonWhitePos == -1) {
            return false;
        }
        
        if (Utilities.getRowEnd(doc, firstNonWhitePos) - firstNonWhitePos < lineCommentStringLen) {
            return false;
        }
        
        CharSequence maybeLineComment = DocumentUtilities.getText(doc, firstNonWhitePos, lineCommentStringLen);
        if (!CharSequenceUtilities.textEquals(maybeLineComment, lineCommentString)) {
            return false;
        }
        
        offset = Utilities.getRowStart(doc, offset, +1);
    }
    return true;
}
 
Example 4
Source File: ToggleBlockCommentAction.java    From netbeans with Apache License 2.0 6 votes vote down vote up
private static int[] getPositions(TokenSequence<TplTopTokenId> ts, boolean commentIt, int caretOffset,
        JTextComponent target, TplMetaData tplMetaData) throws BadLocationException {
    int[] positions = new int[2];
    if (commentIt) {
        if (Utilities.isSelectionShowing(target)) {
            positions[0] = caretOffset;
            positions[1] = target.getSelectionEnd();
        } else {
            positions[0] = Utilities.getRowStart((BaseDocument) target.getDocument(), caretOffset);
            positions[1] = Utilities.getRowEnd((BaseDocument) target.getDocument(), caretOffset);
        }
    } else {
        while (ts.movePrevious() && ts.token().id() != TplTopTokenId.T_SMARTY_OPEN_DELIMITER) {
            //NOOP - just find start
        }
        positions[0] = ts.offset();
        while (ts.moveNext() && ts.token().id() != TplTopTokenId.T_SMARTY_CLOSE_DELIMITER) {
            //NOOP - just find end
        }
        positions[1] = ts.offset() - tplMetaData.getCloseDelimiter().length() - 2;
    }
    return positions;
}
 
Example 5
Source File: YamlScanner.java    From netbeans with Apache License 2.0 6 votes vote down vote up
private void addBlocks(YamlParserResult result, BaseDocument doc, CharSequence text, List<OffsetRange> codeblocks, StructureItem item) throws BadLocationException {
    int docLength = doc == null ? text.length() : doc.getLength();
    int begin = Math.min((int) item.getPosition(), docLength);
    int end = Math.min((int) item.getEndPosition(), docLength);
    int firstRowEnd = doc == null ? GsfUtilities.getRowEnd(text, begin) : Utilities.getRowEnd(doc, begin);
    int lastRowEnd = doc == null ? GsfUtilities.getRowEnd(text, end) : Utilities.getRowEnd(doc, end);
    if (begin < end && firstRowEnd != lastRowEnd) {
        codeblocks.add(new OffsetRange(firstRowEnd, end));
    } else {
        return;
    }

    for (StructureItem child : item.getNestedItems()) {
        int childBegin = (int) child.getPosition();
        int childEnd = (int) child.getEndPosition();
        if (childBegin >= begin && childEnd <= end) {
            addBlocks(result, doc, text, codeblocks, child);
        }
    }
}
 
Example 6
Source File: ToggleBlockCommentAction.java    From netbeans with Apache License 2.0 6 votes vote down vote up
private boolean allComments(BaseDocument doc, int startOffset, int lineCount, String lineCommentString) throws BadLocationException {
    final int lineCommentStringLen = lineCommentString.length();
    for (int offset = startOffset; lineCount > 0; lineCount--) {
        int firstNonWhitePos = Utilities.getRowFirstNonWhite(doc, offset);
        if (firstNonWhitePos == -1) {
            return false;
        }

        if (Utilities.getRowEnd(doc, firstNonWhitePos) - firstNonWhitePos < lineCommentStringLen) {
            return false;
        }

        CharSequence maybeLineComment = DocumentUtilities.getText(doc, firstNonWhitePos, lineCommentStringLen);
        if (!CharSequenceUtilities.textEquals(maybeLineComment, lineCommentString)) {
            return false;
        }

        offset = Utilities.getRowStart(doc, offset, +1);
    }
    return true;
}
 
Example 7
Source File: ExtKit.java    From netbeans with Apache License 2.0 6 votes vote down vote up
private void uncomment(BaseDocument doc, int startOffset, int lineCount) throws BadLocationException {
    for (int offset = startOffset; lineCount > 0; lineCount--) {
        // Get the first non-whitespace char on the current line
        int firstNonWhitePos = Utilities.getRowFirstNonWhite(doc, offset);

        // If there is any, check wheter it's the line-comment-chars and remove them
        if (firstNonWhitePos != -1) {
            if (Utilities.getRowEnd(doc, firstNonWhitePos) - firstNonWhitePos >= lineCommentStringLen) {
                CharSequence maybeLineComment = DocumentUtilities.getText(doc, firstNonWhitePos, lineCommentStringLen);
                if (CharSequenceUtilities.textEquals(maybeLineComment, lineCommentString)) {
                    doc.remove(firstNonWhitePos, lineCommentStringLen);
                }
            }
        }

        offset = Utilities.getRowStart(doc, offset, +1);
    }
}
 
Example 8
Source File: HintUtils.java    From netbeans with Apache License 2.0 6 votes vote down vote up
/**
 * Returns {@link OffsetRange} for the whole line where the {@link GroovyError} occurred.
 * 
 * @param context current hint context
 * @param error error for which we want to find {@link OffsetRange}
 * @return offset range for the whole line
 */
@CheckForNull
public static OffsetRange getLineOffset(
        @NonNull RuleContext context, 
        @NonNull GroovyError error) {
    
    Parameters.notNull("context", context);
    Parameters.notNull("error", error);
    
    // FIXME: for CLASS_NOT_FOUND errors we mark the whole line.
    // This should be replaced with marking the indentifier only.
    try {

        int lineStart = Utilities.getRowStart(context.doc, error.getStartPosition());
        int lineEnd = Utilities.getRowEnd(context.doc, error.getEndPosition());

        return new OffsetRange(lineStart, lineEnd);
        
    } catch (BadLocationException ex) {
        return null;
    }
}
 
Example 9
Source File: AbstractIndenter.java    From netbeans with Apache License 2.0 5 votes vote down vote up
private boolean doesLineStartWithOurLanguage(BaseDocument doc, int lineIndex, JoinedTokenSequence<T1> joinedTS) throws BadLocationException {
    int rowStartOffset = Utilities.getRowStartFromLineOffset(doc, lineIndex);
    int rowEndOffset = Utilities.getRowEnd(doc, rowStartOffset);
    int firstNonWhite = Utilities.getRowFirstNonWhite(doc, rowStartOffset);
    if (firstNonWhite != -1) {
        // there is something on the line:
        int newRowStartOffset = findLanguageOffset(joinedTS, rowStartOffset, rowEndOffset, true);
        if (newRowStartOffset == -1) {
            // but it is not our langauge
            return false;
        }
    }
    return true;
}
 
Example 10
Source File: HtmlExternalDropHandler.java    From netbeans with Apache License 2.0 5 votes vote down vote up
private int getLineEndOffset(JEditorPane pane, Point location) {
    int offset = pane.getUI().viewToModel(pane, location);
    try {
        return Utilities.getRowEnd((BaseDocument) pane.getDocument(), offset);
    } catch (BadLocationException ex) {
        //highly unlikely to happen
        Exceptions.printStackTrace(ex);
        return offset;
    }
}
 
Example 11
Source File: TagBasedFormatter.java    From netbeans with Apache License 2.0 5 votes vote down vote up
private int getInitialIndentFromNextLine(final BaseDocument doc, final int line) throws BadLocationException {
    
    // get initial indent from the next line
    int initialIndent = 0;
    
    int lineStart = Utilities.getRowStartFromLineOffset(doc, line);
    int lineEnd = Utilities.getRowEnd(doc, lineStart);
    int nextNonWhiteLineStart = Utilities.getFirstNonWhiteFwd(doc, lineEnd);
    
    if (nextNonWhiteLineStart > 0){
        initialIndent = Utilities.getRowIndent(doc, nextNonWhiteLineStart, true);
    }
    
    return initialIndent;
}
 
Example 12
Source File: InsertSemicolonAction.java    From netbeans with Apache License 2.0 5 votes vote down vote up
@Override
public void actionPerformed(ActionEvent evt, final JTextComponent target) {
    if (!target.isEditable() || !target.isEnabled()) {
        target.getToolkit().beep();
        return;
    }
    final BaseDocument doc = (BaseDocument) target.getDocument();
    final Indent indenter = Indent.get(doc);
    final class R implements Runnable {
        public @Override void run() {
            try {
                Caret caret = target.getCaret();
                int dotpos = caret.getDot();
                int eoloffset = Utilities.getRowEnd(target, dotpos);
                doc.insertString(eoloffset, "" + what, null); //NOI18N
                if (withNewline) {
                    //This is code from the editor module, but it is
                    //a pretty strange way to do this:
                    doc.insertString(dotpos, "-", null); //NOI18N
                    doc.remove(dotpos, 1);
                    int eolDot = Utilities.getRowEnd(target, caret.getDot());
                    int newDotPos = indenter.indentNewLine(eolDot);
                    caret.setDot(newDotPos);
                }
            } catch (BadLocationException ex) {
                Exceptions.printStackTrace(ex);
            }
        }
    }
    indenter.lock();
    try {
        doc.runAtomicAsUser(new R());
    } finally {
        indenter.unlock();
    }
}
 
Example 13
Source File: HintsTask.java    From netbeans with Apache License 2.0 5 votes vote down vote up
@Override
public List<ErrorDescription> getErrorDescriptionsAt(CompilationInfo info, Context context, Document doc) throws BadLocationException {
    int rowStart = Utilities.getRowStart((BaseDocument) doc, context.getPosition());
    int rowEnd = Utilities.getRowEnd((BaseDocument) doc, context.getPosition());

    return new HintsInvoker(HintsSettings.getSettingsFor(info.getFileObject()), rowStart, rowEnd, context.getCancel()).computeHints(info);
}
 
Example 14
Source File: ExtFinderFactory.java    From netbeans with Apache License 2.0 5 votes vote down vote up
public int adjustStartPos(BaseDocument doc, int startPos) {
    origStartPos = startPos;
    try {
        return Utilities.getRowEnd(doc, startPos);
    } catch (BadLocationException e) {
        return startPos;
    }
}
 
Example 15
Source File: JsFormatter.java    From netbeans with Apache License 2.0 4 votes vote down vote up
private int isEndIndent(IndentContext context, int offset) throws BadLocationException {
    BaseDocument doc = context.getDocument();
    int lineBegin = Utilities.getRowFirstNonWhite(doc, offset);

    if (lineBegin != -1) {
        Token<?extends JsTokenId> token = getFirstToken(context, offset);

        if (token == null) {
            return 0;
        }

        TokenId id = token.id();

        // If the line starts with an end-marker, such as "end", "}", "]", etc.,
        // find the corresponding opening marker, and indent the line to the same
        // offset as the beginning of that line.
        if (id == JsTokenId.BRACKET_RIGHT_CURLY || id == JsTokenId.BRACKET_RIGHT_BRACKET
                /*|| id == JsTokenId.BRACKET_RIGHT_PAREN*/) {
            int indents = 1;

            // Check if there are multiple end markers here... if so increase indent level.
            // This should really do an iteration... for now just handling the most common
            // scenario in JavaScript where we have }) in object literals
            int lineEnd = Utilities.getRowEnd(doc, offset);
            int newOffset = offset;
            while (newOffset < lineEnd && token != null) {
                newOffset = newOffset + token.length();
                if (newOffset < doc.getLength()) {
                    token = LexUtilities.getToken(doc, newOffset, language);
                    if (token != null) {
                        id = token.id();
                        if (id == JsTokenId.WHITESPACE) {
                            continue;
                        } else {
                            break;
                        }
                    }
                }
            }

            return indents;
        }
    }

    return 0;
}
 
Example 16
Source File: WhereUsedElement.java    From netbeans with Apache License 2.0 4 votes vote down vote up
public static WhereUsedElement create(FileObject fileObject, Entry entry, ElementKind kind, boolean related) {
    Icon icon = UiUtils.getElementIcon(kind, Collections.<Modifier>emptyList());
    String name = entry.getName();
    OffsetRange range = entry.getDocumentRange();

    int start = range.getStart();
    int end = range.getEnd();
    
    int sta = start;
    int en = start; // ! Same line as start
    String content = null;
    
    BaseDocument bdoc = GsfUtilities.getDocument(fileObject, true);
    try {
        bdoc.readLock();

        // I should be able to just call tree.getInfo().getText() to get cached
        // copy - but since I'm playing fast and loose with compilationinfos
        // for for example find subclasses (using a singly dummy FileInfo) I need
        // to read it here instead
        content = bdoc.getText(0, bdoc.getLength());
        sta = Utilities.getRowFirstNonWhite(bdoc, start);

        if (sta == -1) {
            sta = Utilities.getRowStart(bdoc, start);
        }

        en = Utilities.getRowLastNonWhite(bdoc, start);

        if (en == -1) {
            en = Utilities.getRowEnd(bdoc, start);
        } else {
            // Last nonwhite - left side of the last char, not inclusive
            en++;
        }

        // Sometimes the node we get from the AST is for the whole block
        // (e.g. such as the whole class), not the argument node. This happens
        // for example in Find Subclasses out of the index. In this case
        if (end > en) {
            end = start + name.length();

            if (end > bdoc.getLength()) {
                end = bdoc.getLength();
            }
        }
    } catch (Exception ex) {
        Exceptions.printStackTrace(ex);
    } finally {
        bdoc.readUnlock();
    }

    StringBuilder sb = new StringBuilder();
    if (end < sta) {
        // XXX Shouldn't happen, but I still have AST offset errors
        sta = end;
    }
    if (start < sta) {
        // XXX Shouldn't happen, but I still have AST offset errors
        start = sta;
    }
    if (en < end) {
        // XXX Shouldn't happen, but I still have AST offset errors
        en = end;
    }
    sb.append(encodeCharRefs(content.subSequence(sta, start).toString()));
    sb.append("<b>"); // NOI18N
    sb.append(content.subSequence(start, end));
    sb.append("</b>"); // NOI18N
    sb.append(encodeCharRefs(content.subSequence(end, en).toString()));
    if(!related) {
        sb.append(NbBundle.getMessage(WhereUsedElement.class, "MSG_Unrelated_Where_Used_Occurance")); //NOI18N
    }


    CloneableEditorSupport ces = GsfUtilities.findCloneableEditorSupport(fileObject);
    PositionRef ref1 = ces.createPositionRef(start, Bias.Forward);
    PositionRef ref2 = ces.createPositionRef(end, Bias.Forward);
    PositionBounds bounds = new PositionBounds(ref1, ref2);

    return new WhereUsedElement(bounds, sb.toString().trim(), 
            fileObject, name, new OffsetRange(start, end), icon);
}
 
Example 17
Source File: GroovyFormatter.java    From netbeans with Apache License 2.0 4 votes vote down vote up
private void computeIndents(BaseDocument doc, int initialIndent, int startOffset, int endOffset, ParserResult info,
        List<Integer> offsets,
        List<Integer> indents,
        boolean indentEmptyLines, boolean includeEnd, boolean indentOnly
    ) {
    // PENDING:
    // The reformatting APIs in NetBeans should be lexer based. They are still
    // based on the old TokenID apis. Once we get a lexer version, convert this over.
    // I just need -something- in place until that is provided.

    try {
        // Algorithm:
        // Iterate over the range.
        // Accumulate a token balance ( {,(,[, and keywords like class, case, etc. increases the balance,
        //      },),] and "end" decreases it
        // If the line starts with an end marker, indent the line to the level AFTER the token
        // else indent the line to the level BEFORE the token (the level being the balance * indentationSize)
        // Compute the initial balance and indentation level and use that as a "base".
        // If the previous line is not "done" (ends with a comma or a binary operator like "+" etc.
        // add a "hanging indent" modifier.
        // At the end of the day, we're recording a set of line offsets and indents.
        // This can be used either to reformat the buffer, or indent a new line.

        // State:
        int offset = Utilities.getRowStart(doc, startOffset); // The line's offset
        int end = endOffset;

        int indentSize = IndentUtils.indentLevelSize(doc);
        int hangingIndentSize = hangingIndentSize();

        // Pending - apply comment formatting too?


        // Build up a set of offsets and indents for lines where I know I need
        // to adjust the offset. I will then go back over the document and adjust
        // lines that are different from the intended indent. By doing piecemeal
        // replacements in the document rather than replacing the whole thing,
        // a lot of things will work better: breakpoints and other line annotations
        // will be left in place, semantic coloring info will not be temporarily
        // damaged, and the caret will stay roughly where it belongs.

        // The token balance at the offset
        int balance = 0;
        // The bracket balance at the offset ( parens, bracket, brace )
        int bracketBalance = 0;
        boolean continued = false;

        while ((!includeEnd && offset < end) || (includeEnd && offset <= end)) {
            int indent; // The indentation to be used for the current line
            int hangingIndent = continued ? (hangingIndentSize) : 0;

            if (isInLiteral(doc, offset)) {
                // Skip this line - leave formatting as it is prior to reformatting
                indent = GsfUtilities.getLineIndent(doc, offset);

            } else if (isEndIndent(doc, offset)) {
                indent = (balance - 1) * indentSize + hangingIndent + initialIndent;
            } else {
                indent = balance * indentSize + hangingIndent + initialIndent;
            }

            int endOfLine = Utilities.getRowEnd(doc, offset) + 1;

            if (isJavaDocComment(doc, offset, endOfLine)) {
                indent++;
            }

            if (indent < 0) {
                indent = 0;
            }

            int lineBegin = Utilities.getRowFirstNonWhite(doc, offset);

            // Insert whitespace on empty lines too -- needed for abbreviations expansion
            if (lineBegin != -1 || indentEmptyLines) {
                // Don't do a hanging indent if we're already indenting beyond the parent level?

                indents.add(Integer.valueOf(indent));
                offsets.add(Integer.valueOf(offset));
            }



            if (lineBegin != -1) {
                balance += getTokenBalance(doc, lineBegin, endOfLine, true);
                bracketBalance += getTokenBalance(doc, lineBegin, endOfLine, false);
                continued = isLineContinued(doc, offset, bracketBalance);
            }

            offset = endOfLine;
        }
    } catch (BadLocationException ble) {
        Exceptions.printStackTrace(ble);
    }
}
 
Example 18
Source File: LineBreakHook.java    From netbeans with Apache License 2.0 4 votes vote down vote up
@Override
public void insert(MutableContext context) throws BadLocationException {
    if (!(context.getDocument() instanceof BaseDocument)) {
        return;
    }
    BaseDocument doc = (BaseDocument)context.getDocument();

    int insertPos = context.getCaretOffset();
    int caretPos = context.getComponent().getCaretPosition();
    int lineStartPos = Utilities.getRowStart(doc, insertPos);

    TokenHierarchy h = TokenHierarchy.get(doc);
    TokenSequence seq = h.tokenSequence();
    // check the actual tokens
    seq.move(context.getCaretOffset());
    int openOffset = followsOpeningTag(seq);
    
    int nonWhiteBefore = Utilities.getFirstNonWhiteBwd(doc, insertPos, lineStartPos);

    int lineEndPos = Utilities.getRowEnd(doc, caretPos);
    int nonWhiteAfter = Utilities.getFirstNonWhiteFwd(doc, caretPos, lineEndPos);

    // there is a opening tag preceding on the line && something following the insertion point
    if (nonWhiteBefore != -1 && nonWhiteAfter != -1 && openOffset >= 0) {
        // check that the following token (after whitespace(s)) is a 
        // opening tag
        seq.move(nonWhiteAfter);
        // now we need to position the caret at the END of the line immediately 
        // preceding the closing tag. Assuming it's already indented
        if (precedesClosingTag(seq)) {
            int startClosingLine = Utilities.getRowStart(doc, nonWhiteAfter);
            int nextLineStart = Utilities.getRowStart(doc, insertPos, 1);
            if (nextLineStart >= startClosingLine - 1) {
                insertBlankBetweenTabs(context, openOffset);
            }
            return;
        }
    }
    // if the rest of the line is blank, we must insert newline + indent, so the cursor
    // appears at the correct place
    if (nonWhiteAfter != -1) {
        // will be handled by the formatter automatically
        return;
    }

    int desiredIndent;

    if (openOffset != Integer.MIN_VALUE) {
        desiredIndent = IndentUtils.lineIndent(doc, Utilities.getRowStart(doc, Math.abs(openOffset)));
        if (openOffset >= 0) {
            desiredIndent += IndentUtils.indentLevelSize(doc);
        }
    } else {
        // align with the current line
        desiredIndent = IndentUtils.lineIndent(doc, lineStartPos);
    }
    String blankLine = "\n" + 
        IndentUtils.createIndentString(doc, desiredIndent);
    context.setText(blankLine, -1, blankLine.length(), 1, blankLine.length());
}
 
Example 19
Source File: ExtFormatter.java    From netbeans with Apache License 2.0 4 votes vote down vote up
/** Returns offset of EOL for the white line */
protected int getEOLOffset(BaseDocument bdoc, int offset) throws BadLocationException{
    return Utilities.getRowEnd(bdoc, offset);
}
 
Example 20
Source File: YamlKeystrokeHandler.java    From netbeans with Apache License 2.0 4 votes vote down vote up
@Override
public int beforeBreak(Document document, int offset, JTextComponent target) throws BadLocationException {

    Caret caret = target.getCaret();
    BaseDocument doc = (BaseDocument) document;

    // Very simple algorithm for now..
    // Basically, use the same indent as the current line, unless the caret is immediately preceeded by a ":" (possibly with whitespace
    // in between)

    int lineBegin = Utilities.getRowStart(doc, offset);
    int lineEnd = Utilities.getRowEnd(doc, offset);

    if (lineBegin == offset && lineEnd == offset) {
        // Pressed return on a blank newline - do nothing
        return -1;
    }

    int indent = getLineIndent(doc, offset);
    String linePrefix = doc.getText(lineBegin, offset - lineBegin);
    String lineSuffix = doc.getText(offset, lineEnd + 1 - offset);
    if (linePrefix.trim().endsWith(":") && lineSuffix.trim().length() == 0) {
        // Yes, new key: increase indent
        indent += IndentUtils.getIndentSize(doc);
    } else {
        // No, just use same indent as parent
    }

    // Also remove the whitespace from the caret up to the first nonspace character on the current line
    int remove = 0;
    String line = doc.getText(lineBegin, lineEnd + 1 - lineBegin);
    for (int n = line.length(), i = offset - lineBegin; i < n; i++) {
        char c = line.charAt(i);
        if (c == ' ' || c == '\t') {
            remove++;
        } else {
            break;
        }
    }
    if (remove > 0) {
        doc.remove(offset, remove);
    }
    String str = IndentUtils.getIndentString(indent);
    int newPos = offset + str.length();
    doc.insertString(offset, str, null);
    caret.setDot(offset);
    return newPos + 1;
}