/*
* Copyright (c) 2012, the Dart project authors.
*
* Licensed under the Eclipse Public License v1.0 (the "License"); you may not use this file except
* in compliance with the License. You may obtain a copy of the License at
*
* http://www.eclipse.org/legal/epl-v10.html
*
* Unless required by applicable law or agreed to in writing, software distributed under the License
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
* or implied. See the License for the specific language governing permissions and limitations under
* the License.
*/
package com.google.dart.tools.ui.internal.text.dartdoc;
import com.google.dart.tools.ui.text.DartPartitions;
import org.apache.commons.lang3.StringUtils;
import org.eclipse.jface.text.BadLocationException;
import org.eclipse.jface.text.DefaultIndentLineAutoEditStrategy;
import org.eclipse.jface.text.DocumentCommand;
import org.eclipse.jface.text.IDocument;
import org.eclipse.jface.text.IRegion;
import org.eclipse.jface.text.ITypedRegion;
import org.eclipse.jface.text.TextUtilities;
/**
* A simple auto indent strategy for DartDoc comments (as well as regular multi-line comments).
*
* @coverage dart.editor.ui.text.dart
*/
public class DartDocAutoIndentStrategy extends DefaultIndentLineAutoEditStrategy {
private String partitioning;
public DartDocAutoIndentStrategy(String partitioning) {
this.partitioning = partitioning;
}
@Override
public void customizeDocumentCommand(IDocument document, DocumentCommand command) {
if (!isValidPartition(document, command.offset)) {
return;
}
if (command.text != null) {
if (command.length == 0) {
String[] lineDelimiters = document.getLegalLineDelimiters();
int idx = TextUtilities.endsWith(lineDelimiters, command.text);
if (idx > -1) {
if (lineDelimiters[idx].equals(command.text)) {
// auto-indent only if adding a newline and nothing else
autoIndentAfterNewLine(document, command);
}
return;
}
}
}
}
/**
* Copies the indentation of the previous line
* {@link #customizeDocumentCommand(IDocument, DocumentCommand)}.
*
* @see DartDocAutoIndentStrategy
* @param d the document to work on
* @param c the command to deal with
*/
protected void autoIndentAfterNewLine(IDocument d, DocumentCommand c) {
int offset = c.offset;
if (offset == -1 || d.getLength() == 0) {
return;
}
try {
// find start of line
int p = (offset == d.getLength() ? offset - 1 : offset);
IRegion info = d.getLineInformationOfOffset(p);
int start = info.getOffset();
int end = start + info.getLength();
// split line
String strLine = d.get(start, end - start);
int firstNotWS = StringUtils.indexOfAnyBut(strLine, " \t");
if (firstNotWS == -1) {
firstNotWS = 0;
}
String strWS = strLine.substring(0, firstNotWS);
String strAfterWS = strLine.substring(firstNotWS);
String lineDelimiter = TextUtilities.getDefaultLineDelimiter(d);
StringBuffer buf = new StringBuffer();
buf.append(lineDelimiter);
buf.append(strWS);
if (strAfterWS.startsWith("/")) {
buf.append(" ");
} else if (firstNotWS == 0) {
buf.append(" ");
}
buf.append("* ");
int newCaretOffset = offset + buf.length();
if (strLine.endsWith("*/")) {
buf.append(lineDelimiter);
buf.append(strWS);
buf.append(" ");
} else if (isNewComment(d, offset)) {
buf.append(lineDelimiter);
buf.append(strWS);
buf.append(" */");
}
c.shiftsCaret = false;
c.caretOffset = newCaretOffset;
c.text = buf.toString();
} catch (BadLocationException excp) {
// stop work
}
}
protected String getLineStart(IDocument document, int offset, int end, boolean includeNonWs)
throws BadLocationException {
int start = offset;
while (offset < end) {
char c = document.getChar(offset);
if (c != ' ' && c != '\t') {
if (includeNonWs) {
return document.get(start, offset + 1 - start);
} else {
return document.get(start, offset - start);
}
}
offset++;
}
return document.get(start, end - start);
}
/**
* Guesses if the command operates within a newly created DartDoc comment or not. If in doubt, it
* will assume that the DartDoc is new.
*
* @param document the document
* @param commandOffset the command offset
* @return <code>true</code> if the comment should be closed, <code>false</code> if not
*/
private boolean isNewComment(IDocument document, int commandOffset) {
try {
int lineIndex = document.getLineOfOffset(commandOffset) + 1;
if (lineIndex >= document.getNumberOfLines()) {
return true;
}
IRegion line = document.getLineInformation(lineIndex);
ITypedRegion partition = TextUtilities.getPartition(
document,
partitioning,
commandOffset,
false);
int partitionEnd = partition.getOffset() + partition.getLength();
if (line.getOffset() >= partitionEnd) {
return false;
}
if (document.getLength() == partitionEnd) {
return true; // partition goes to end of document - probably a new comment
}
String comment = document.get(partition.getOffset(), partition.getLength());
int openCount = StringUtils.countMatches(comment, "/*");
int closeCount = StringUtils.countMatches(comment, "*/");
return openCount > closeCount;
// if (comment.indexOf("/*", 2) != -1) {
// return true; // enclosed another comment -> probably a new comment
// }
//
// return false;
} catch (BadLocationException e) {
return false;
}
}
private boolean isValidPartition(IDocument document, int offset) {
try {
String contentType = TextUtilities.getContentType(document, partitioning, offset, false);
return DartPartitions.DART_DOC.equals(contentType)
|| DartPartitions.DART_MULTI_LINE_COMMENT.equals(contentType)
|| DartPartitions.DART_SINGLE_LINE_DOC.equals(contentType);
} catch (BadLocationException e) {
}
return false;
}
}