blob: 55b9e7c8458b4b6c332bcbd6435c89e9522f8f83 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2008, 2016 Symbian Software Systems and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Andrew Ferguson (Symbian) - Initial implementation
* Martin Stumpf - adapted orginal to cope with single line comments
*******************************************************************************/
package org.eclipse.cdt.ui.text.doctools.doxygen;
import org.eclipse.jface.text.BadLocationException;
import org.eclipse.jface.text.DocumentCommand;
import org.eclipse.jface.text.DocumentRewriteSession;
import org.eclipse.jface.text.DocumentRewriteSessionType;
import org.eclipse.jface.text.IAutoEditStrategy;
import org.eclipse.jface.text.IDocument;
import org.eclipse.jface.text.IDocumentExtension4;
import org.eclipse.jface.text.IRegion;
import org.eclipse.jface.text.ITypedRegion;
import org.eclipse.jface.text.Region;
import org.eclipse.jface.text.TextUtilities;
import org.eclipse.cdt.core.dom.ast.IASTDeclaration;
import org.eclipse.cdt.core.dom.ast.IASTNode;
import org.eclipse.cdt.core.dom.ast.IASTNodeSelector;
import org.eclipse.cdt.core.dom.ast.IASTTranslationUnit;
import org.eclipse.cdt.ui.text.ICPartitions;
/**
* {@link IAutoEditStrategy} for adding Doxygen tags for comments.
*
* @since 5.11
* @noextend This class is not intended to be subclassed by clients.
*/
public class DoxygenSingleAutoEditStrategy extends DoxygenMultilineAutoEditStrategy {
private static final String SLASH_COMMENT = "///"; //$NON-NLS-1$
private static final String EXCL_COMMENT = "//!"; //$NON-NLS-1$
private static String fgDefaultLineDelim = "\n"; //$NON-NLS-1$
public DoxygenSingleAutoEditStrategy() {
}
/**
* @see org.eclipse.jface.text.IAutoEditStrategy#customizeDocumentCommand(org.eclipse.jface.text.IDocument, org.eclipse.jface.text.DocumentCommand)
*/
@Override
public void customizeDocumentCommand(IDocument doc, DocumentCommand cmd) {
fgDefaultLineDelim = TextUtilities.getDefaultLineDelimiter(doc);
if(doc instanceof IDocumentExtension4) {
boolean forNewLine= cmd.length == 0 && cmd.text != null && endsWithDelimiter(doc, cmd.text);
if(forNewLine ) {
IDocumentExtension4 ext4= (IDocumentExtension4) doc;
DocumentRewriteSession drs= ext4.startRewriteSession(DocumentRewriteSessionType.UNRESTRICTED_SMALL);
try {
customizeDocumentAfterNewLine(doc, cmd);
} finally {
ext4.stopRewriteSession(drs);
}
}
}
}
@Override
public void customizeDocumentAfterNewLine(IDocument doc, final DocumentCommand c) {
int offset= c.offset;
if (offset == -1 || doc.getLength() == 0)
return;
final StringBuilder buf= new StringBuilder(c.text);
try {
IRegion line= doc.getLineInformationOfOffset(c.offset);
String lineDelimiter = doc.getLineDelimiter(doc.getLineOfOffset(c.offset));
int lineDelimiterLength = lineDelimiter.length();
IRegion prefix= findPrefixRange(doc, line);
String indentationWithPrefix = doc.get(prefix.getOffset(), prefix.getLength());
String commentPrefix = getCommentPrefix(indentationWithPrefix);
String commentContent = doc.get(prefix.getOffset() + prefix.getLength(),
line.getLength() - prefix.getLength());
//String commentContentBeforeCursor = doc.get(prefix.getOffset() + prefix.getLength(),
// c.offset - line.getOffset() - prefix.getLength());
String commentContentBehindCursor = doc.get(c.offset,
line.getLength() - (c.offset - line.getOffset()));
buf.append(indentationWithPrefix);
boolean commentAtStart = prefix.getOffset() + prefix.getLength() <= c.offset;
boolean commentFollows = false;
boolean commentAhead = false;
boolean firstLineContainsText = commentContent.trim().length() > 0;
if (commentAtStart) {
if (line.getOffset() + line.getLength() + lineDelimiterLength < doc.getLength())
{
IRegion nextLine = doc.getLineInformationOfOffset(line.getOffset() + line.getLength() + lineDelimiterLength);
commentFollows = doc.get(nextLine.getOffset(), nextLine.getLength()).trim().startsWith(commentPrefix);
if (line.getOffset() >= 1)
{
IRegion previousLine = doc.getLineInformationOfOffset(line.getOffset() - 1);
commentAhead = doc.get(previousLine.getOffset(), previousLine.getLength()).trim().startsWith(commentPrefix);
}
}
// comment started on this line
buf.append(" "); //$NON-NLS-1$
}
c.shiftsCaret= false;
c.caretOffset= c.offset + buf.length();
if(commentAtStart && !commentFollows && !commentAhead) {
try {
StringBuilder content = getDeclarationLines(doc, offset);
boolean contentAlreadyThere = (firstLineContainsText && content != null && content.toString().contains(commentContentBehindCursor.trim()));
if (content == null || content.toString().trim().length() == 0 || contentAlreadyThere)
{
buf.setLength(0);
buf.append(fgDefaultLineDelim);
buf.append(indentationWithPrefix).append(' ');
c.shiftsCaret= false;
c.caretOffset= c.offset + buf.length();
} else {
if (!firstLineContainsText)
{
c.shiftsCaret= false;
c.caretOffset= c.offset + 1;
buf.setLength(0);
buf.append(' ').append(
indent(content, indentationWithPrefix + " ", //$NON-NLS-1$
fgDefaultLineDelim).substring((indentationWithPrefix + " ").length())); //$NON-NLS-1$
}
else
{
buf.append(fgDefaultLineDelim);
buf.append(indent(content, indentationWithPrefix + " ", fgDefaultLineDelim)); //$NON-NLS-1$
}
buf.setLength(buf.length() - fgDefaultLineDelim.length());
}
} catch(BadLocationException ble) {
ble.printStackTrace();
}
}
c.text= buf.toString();
} catch (BadLocationException excp) {
}
}
private StringBuilder getDeclarationLines(IDocument doc, int offset)
throws BadLocationException {
IASTDeclaration dec= null;
IASTTranslationUnit ast= getAST();
if(ast != null) {
dec= findFollowingDeclaration(ast, offset);
if(dec == null) {
IASTNodeSelector ans= ast.getNodeSelector(ast.getFilePath());
IASTNode node= ans.findEnclosingNode(offset, 0);
if(node instanceof IASTDeclaration) {
dec= (IASTDeclaration) node;
}
}
}
if(dec!=null) {
ITypedRegion partition= TextUtilities.getPartition(doc, ICPartitions.C_PARTITIONING /* this! */, offset, false);
return customizeAfterNewLineForDeclaration(doc, dec, partition);
}
return null;
}
private String getCommentPrefix(String indent) throws BadLocationException {
if (indent.endsWith(SLASH_COMMENT))
{
return SLASH_COMMENT;
}
else
{
return EXCL_COMMENT;
}
}
/**
* Returns the range of the comment prefix on the given line in
* <code>document</code>. The prefix greedily matches the following regex
* pattern: <code>\s*\/\/[\/!]</code>, that is, any number of whitespace
* characters, followed by an comment ('///' or '//!').
*
* @param document the document to which <code>line</code> refers
* @param line the line from which to extract the prefix range
* @return an <code>IRegion</code> describing the range of the prefix on
* the given line
* @throws BadLocationException if accessing the document fails
*/
protected static IRegion findPrefixRange(IDocument document, IRegion line) throws BadLocationException {
int lineOffset= line.getOffset();
int lineEnd= lineOffset + line.getLength();
int indentEnd= findEndOfWhiteSpaceAt(document, lineOffset, lineEnd);
if (indentEnd < lineEnd-2 && document.getChar(indentEnd) == '/' && document.getChar(indentEnd+1) == '/' && (
document.getChar(indentEnd+2) == '/' || document.getChar(indentEnd+2) == '!')) {
indentEnd += 3;
}
return new Region(lineOffset, indentEnd - lineOffset);
}
}