| /******************************************************************************* |
| * 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); |
| } |
| |
| } |