/** * Returns the indentation of the line <code>line</code> in <code>document</code>. The returned * string may contain pairs of leading slashes that are considered part of the indentation. The * space before the asterisk in a javadoc-like comment is not considered part of the indentation. * * @param document the document * @param line the line * @return the indentation of <code>line</code> in <code>document</code> * @throws BadLocationException if the document is changed concurrently */ private static String getCurrentIndent(Document document, int line) throws BadLocationException { IRegion region = document.getLineInformation(line); int from = region.getOffset(); int endOffset = region.getOffset() + region.getLength(); // go behind line comments int to = from; while (to < endOffset - 2 && document.get(to, 2).equals(LINE_COMMENT)) to += 2; while (to < endOffset) { char ch = document.getChar(to); if (!Character.isWhitespace(ch)) break; to++; } // don't count the space before javadoc like, asterisk-style comment lines if (to > from && to < endOffset - 1 && document.get(to - 1, 2).equals(" *")) { // $NON-NLS-1$ String type = TextUtilities.getContentType(document, IJavaPartitions.JAVA_PARTITIONING, to, true); if (type.equals(IJavaPartitions.JAVA_DOC) || type.equals(IJavaPartitions.JAVA_MULTI_LINE_COMMENT)) to--; } return document.get(from, to - from); }
/** * Indents line <code>line</code> in <code>document</code> with <code>indent</code>. Leaves * leading comment signs alone. * * @param document the document * @param line the line * @param indent the indentation to insert * @param tabLength the length of a tab * @throws BadLocationException on concurrent document modification */ private void addIndent(Document document, int line, CharSequence indent, int tabLength) throws BadLocationException { IRegion region = document.getLineInformation(line); int insert = region.getOffset(); int endOffset = region.getOffset() + region.getLength(); // Compute insert after all leading line comment markers int newInsert = insert; while (newInsert < endOffset - 2 && document.get(newInsert, 2).equals(LINE_COMMENT)) newInsert += 2; // Heuristic to check whether it is commented code or just a comment if (newInsert > insert) { int whitespaceCount = 0; int i = newInsert; while (i < endOffset - 1) { char ch = document.get(i, 1).charAt(0); if (!Character.isWhitespace(ch)) break; whitespaceCount = whitespaceCount + computeVisualLength(ch, tabLength); i++; } if (whitespaceCount != 0 && whitespaceCount >= CodeFormatterUtil.getIndentWidth(fProject)) insert = newInsert; } // Insert indent document.replace(insert, 0, indent.toString()); }
/** * Cuts the visual equivalent of <code>toDelete</code> characters out of the indentation of line * <code>line</code> in <code>document</code>. Leaves leading comment signs alone. * * @param document the document * @param line the line * @param toDelete the number of space equivalents to delete * @param tabLength the length of a tab * @throws BadLocationException on concurrent document modification */ private void cutIndent(Document document, int line, int toDelete, int tabLength) throws BadLocationException { IRegion region = document.getLineInformation(line); int from = region.getOffset(); int endOffset = region.getOffset() + region.getLength(); // go behind line comments while (from < endOffset - 2 && document.get(from, 2).equals(LINE_COMMENT)) from += 2; int to = from; while (toDelete > 0 && to < endOffset) { char ch = document.getChar(to); if (!Character.isWhitespace(ch)) break; toDelete -= computeVisualLength(ch, tabLength); if (toDelete >= 0) to++; else break; } document.replace(from, to - from, ""); // $NON-NLS-1$ }
private void smartPaste(IDocument document, DocumentCommand command) { int newOffset = command.offset; int newLength = command.length; String newText = command.text; try { JavaHeuristicScanner scanner = new JavaHeuristicScanner(document); JavaIndenter indenter = new JavaIndenter(document, scanner, fProject); int offset = newOffset; // reference position to get the indent from int refOffset = indenter.findReferencePosition(offset); if (refOffset == JavaHeuristicScanner.NOT_FOUND) return; int peerOffset = getPeerPosition(document, command); peerOffset = indenter.findReferencePosition(peerOffset); if (peerOffset != JavaHeuristicScanner.NOT_FOUND) refOffset = Math.min(refOffset, peerOffset); // eat any WS before the insertion to the beginning of the line int firstLine = 1; // don't format the first line per default, as it has other content before it IRegion line = document.getLineInformationOfOffset(offset); String notSelected = document.get(line.getOffset(), offset - line.getOffset()); if (notSelected.trim().length() == 0) { newLength += notSelected.length(); newOffset = line.getOffset(); firstLine = 0; } // prefix: the part we need for formatting but won't paste IRegion refLine = document.getLineInformationOfOffset(refOffset); String prefix = document.get(refLine.getOffset(), newOffset - refLine.getOffset()); // handle the indentation computation inside a temporary document Document temp = new Document(prefix + newText); DocumentRewriteSession session = temp.startRewriteSession(DocumentRewriteSessionType.STRICTLY_SEQUENTIAL); scanner = new JavaHeuristicScanner(temp); indenter = new JavaIndenter(temp, scanner, fProject); installJavaStuff(temp); // indent the first and second line // compute the relative indentation difference from the second line // (as the first might be partially selected) and use the value to // indent all other lines. boolean isIndentDetected = false; StringBuffer addition = new StringBuffer(); int insertLength = 0; int firstLineInsertLength = 0; int firstLineIndent = 0; int first = document.computeNumberOfLines(prefix) + firstLine; // don't format first line int lines = temp.getNumberOfLines(); int tabLength = getVisualTabLengthPreference(); boolean changed = false; for (int l = first; l < lines; l++) { // we don't change the number of lines while adding indents IRegion r = temp.getLineInformation(l); int lineOffset = r.getOffset(); int lineLength = r.getLength(); if (lineLength == 0) // don't modify empty lines continue; if (!isIndentDetected) { // indent the first pasted line String current = getCurrentIndent(temp, l); StringBuffer correct = indenter.computeIndentation(lineOffset); if (correct == null) return; // bail out insertLength = subtractIndent(correct, current, addition, tabLength); if (l == first) { firstLineInsertLength = insertLength; firstLineIndent = current.length(); } if (l != first && temp.get(lineOffset, lineLength).trim().length() != 0) { isIndentDetected = true; if (firstLineIndent >= current.length()) insertLength = firstLineInsertLength; if (insertLength == 0) { // no adjustment needed, bail out if (firstLine == 0) { // but we still need to adjust the first line command.offset = newOffset; command.length = newLength; if (changed) break; // still need to get the leading indent of the first line } return; } } else { changed = insertLength != 0; } } // relatively indent all pasted lines if (insertLength > 0) addIndent(temp, l, addition, tabLength); else if (insertLength < 0) cutIndent(temp, l, -insertLength, tabLength); } removeJavaStuff(temp); temp.stopRewriteSession(session); newText = temp.get(prefix.length(), temp.getLength() - prefix.length()); command.offset = newOffset; command.length = newLength; command.text = newText; } catch (BadLocationException e) { JavaPlugin.log(e); } }