/******************************************************************************* * Copyright (c) 2008, 2010 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 * Anton Leherbauer (Wind River Systems) *******************************************************************************/ package org.eclipse.cdt.ui.text.doctools.doxygen; import java.util.LinkedHashSet; import org.eclipse.jface.text.BadLocationException; import org.eclipse.jface.text.IAutoEditStrategy; import org.eclipse.jface.text.IDocument; import org.eclipse.jface.text.IRegion; import org.eclipse.jface.text.ITypedRegion; import org.eclipse.jface.text.TextUtilities; import org.eclipse.cdt.core.dom.ast.ExpansionOverlapsBoundaryException; import org.eclipse.cdt.core.dom.ast.IASTArrayDeclarator; import org.eclipse.cdt.core.dom.ast.IASTCompositeTypeSpecifier; import org.eclipse.cdt.core.dom.ast.IASTDeclSpecifier; import org.eclipse.cdt.core.dom.ast.IASTDeclaration; import org.eclipse.cdt.core.dom.ast.IASTDeclarator; import org.eclipse.cdt.core.dom.ast.IASTEnumerationSpecifier; import org.eclipse.cdt.core.dom.ast.IASTEnumerationSpecifier.IASTEnumerator; import org.eclipse.cdt.core.dom.ast.IASTFunctionDeclarator; import org.eclipse.cdt.core.dom.ast.IASTFunctionDefinition; import org.eclipse.cdt.core.dom.ast.IASTNodeLocation; import org.eclipse.cdt.core.dom.ast.IASTParameterDeclaration; import org.eclipse.cdt.core.dom.ast.IASTSimpleDeclSpecifier; import org.eclipse.cdt.core.dom.ast.IASTSimpleDeclaration; import org.eclipse.cdt.core.dom.ast.IASTStandardFunctionDeclarator; import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTFunctionDeclarator; import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTLinkageSpecification; import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTTemplateDeclaration; import org.eclipse.cdt.core.parser.IToken; import org.eclipse.cdt.ui.text.doctools.DefaultMultilineCommentAutoEditStrategy; /** * {@link IAutoEditStrategy} for adding Doxygen tags for comments. * * @since 5.0 * @noextend This class is not intended to be subclassed by clients. */ public class DoxygenMultilineAutoEditStrategy extends DefaultMultilineCommentAutoEditStrategy { private static final String SINGLELINE_COMMENT_PRECEDING = "//!< "; //$NON-NLS-1$ private static final String PARAM = "@param "; //$NON-NLS-1$ private static final String RETURN = "@return"; //$NON-NLS-1$ protected boolean documentPureVirtuals= true; protected boolean documentDeclarations= true; private String fLineDelimiter; public DoxygenMultilineAutoEditStrategy() { } /** * @param decl the function declarator to document * @param ds the function specifier to document * @return content describing the specified function */ protected StringBuilder documentFunction(IASTFunctionDeclarator decl, IASTDeclSpecifier ds) { StringBuilder result= new StringBuilder(); result.append(documentFunctionParameters(getParameterDecls(decl))); boolean hasReturn= true; if(ds instanceof IASTSimpleDeclSpecifier) { IASTSimpleDeclSpecifier sds= (IASTSimpleDeclSpecifier) ds; if(sds.getType() == IASTSimpleDeclSpecifier.t_void || sds.getType() == IASTSimpleDeclSpecifier.t_unspecified) { hasReturn= false; } } if(hasReturn) { result.append(documentFunctionReturn()); } return result; } /** * Returns the comment content to add to the documentation comment. * @param decls The parameter declarations to describe * @return a buffer containing the comment content to generate to describe the parameters of * the specified {@link IASTParameterDeclaration} objects. */ protected StringBuilder documentFunctionParameters(IASTParameterDeclaration[] decls) { StringBuilder result= new StringBuilder(); for(int i=0; i<decls.length; i++) { if(!isVoidParameter(decls[i])) { result.append(PARAM).append(getParameterName(decls[i])).append(getLineDelimiter()); } } return result; } /** * Get the default line delimiter for the currently customized document * which should be used for new lines. * * @return the default line delimiter */ private String getLineDelimiter() { return fLineDelimiter; } /** * @param decl * @return the name of the parameter */ String getParameterName(IASTParameterDeclaration decl) { IASTDeclarator dtor= decl.getDeclarator(); for(int i=0; i<8 && dtor.getName().getRawSignature().length()==0 && dtor.getNestedDeclarator() != null; i++) { dtor= dtor.getNestedDeclarator(); } return dtor.getName().getRawSignature(); } /** * @param decl * @return true if the specified parameter declaration is of void type */ boolean isVoidParameter(IASTParameterDeclaration decl) { if(decl.getDeclSpecifier() instanceof IASTSimpleDeclSpecifier) { if(((IASTSimpleDeclSpecifier)decl.getDeclSpecifier()).getType() == IASTSimpleDeclSpecifier.t_void) { IASTDeclarator dtor= decl.getDeclarator(); if(dtor.getPointerOperators().length == 0) { if(!(dtor instanceof IASTFunctionDeclarator) && !(dtor instanceof IASTArrayDeclarator)) { return true; } } } } return false; } /** * @return the comment content to describe the return */ protected StringBuilder documentFunctionReturn() { return new StringBuilder(RETURN).append(getLineDelimiter()); } /** * @param decl the function declarator to analyze * @return the parameter declarations for the specified function definition */ protected IASTParameterDeclaration[] getParameterDecls(IASTFunctionDeclarator decl) { IASTParameterDeclaration[] result; if (decl instanceof IASTStandardFunctionDeclarator) { IASTStandardFunctionDeclarator standardFunctionDecl= (IASTStandardFunctionDeclarator)decl; result= standardFunctionDecl.getParameters(); } else /*if (def instanceof ICASTKnRFunctionDeclarator) { ICASTKnRFunctionDeclarator knrDeclarator= (ICASTKnRFunctionDeclarator)decl; result= knrDeclarator.getParameterDeclarations(); } else */{ result= new IASTParameterDeclaration[0]; } return result; } /* * @see org.eclipse.cdt.ui.text.doctools.DefaultMultilineCommentAutoEditStrategy#customizeAfterNewLineForDeclaration(org.eclipse.jface.text.IDocument, org.eclipse.cdt.core.dom.ast.IASTDeclaration, org.eclipse.jface.text.ITypedRegion) */ @Override protected StringBuilder customizeAfterNewLineForDeclaration(IDocument doc, IASTDeclaration dec, ITypedRegion partition) { fLineDelimiter = TextUtilities.getDefaultLineDelimiter(doc); IASTDeclaration declToDocument = dec; if(declToDocument instanceof ICPPASTLinkageSpecification) { ICPPASTLinkageSpecification linkageSpecification = (ICPPASTLinkageSpecification)declToDocument; IASTDeclaration[] declarations = linkageSpecification.getDeclarations(); if(declarations.length == 1) { boolean isCurlyExtern = false; IToken token = null; try { token = declarations[0].getTrailingSyntax(); } catch (UnsupportedOperationException e) { return new StringBuilder(); } catch (ExpansionOverlapsBoundaryException e) { return new StringBuilder(); } if(token != null && token.getType() == IToken.tRBRACE) { isCurlyExtern = true; } if(!isCurlyExtern) { declToDocument = declarations[0]; } } } while(declToDocument instanceof ICPPASTTemplateDeclaration) /* if? */ declToDocument= ((ICPPASTTemplateDeclaration)declToDocument).getDeclaration(); if(declToDocument instanceof IASTFunctionDefinition) { IASTFunctionDefinition fd= (IASTFunctionDefinition) declToDocument; return documentFunction(fd.getDeclarator(), fd.getDeclSpecifier()); } if(declToDocument instanceof IASTSimpleDeclaration) { IASTSimpleDeclaration sdec= (IASTSimpleDeclaration) declToDocument; StringBuilder result= new StringBuilder(); if(sdec.getDeclSpecifier() instanceof IASTCompositeTypeSpecifier) { return result; } else { IASTDeclarator[] dcs= sdec.getDeclarators(); if(dcs.length == 1 && dcs[0] instanceof IASTFunctionDeclarator) { IASTFunctionDeclarator fdecl = (IASTFunctionDeclarator)dcs[0]; boolean shouldDocument= documentDeclarations; if(documentPureVirtuals && dcs[0] instanceof ICPPASTFunctionDeclarator) { ICPPASTFunctionDeclarator cppfdecl= (ICPPASTFunctionDeclarator) dcs[0]; shouldDocument = shouldDocument || cppfdecl.isPureVirtual(); } if(shouldDocument) { return documentFunction(fdecl, sdec.getDeclSpecifier()); } } } } try { alterDoc(doc, declToDocument); } catch(BadLocationException ble) { /*ignore*/ } return new StringBuilder(); } /* * Add post-declaration comments to enumerators, after initializing a doc-comment on an enumeration */ private void alterDoc(IDocument doc, IASTDeclaration dec) throws BadLocationException { if(dec instanceof IASTSimpleDeclaration && ((IASTSimpleDeclaration)dec).getDeclSpecifier() instanceof IASTEnumerationSpecifier) { IASTEnumerationSpecifier spc= (IASTEnumerationSpecifier) ((IASTSimpleDeclaration)dec).getDeclSpecifier(); IASTEnumerator[] enms= spc.getEnumerators(); class Entry { final int offset, length; StringBuilder comment; Entry(int offset, int length, String comment) { this.offset= offset; this.length= length; this.comment= new StringBuilder(comment); } @Override public int hashCode() { return offset; } @Override public boolean equals(Object obj) { if(obj instanceof Entry) { Entry other= (Entry) obj; return offset == other.offset; } return false; } } boolean noCollisions= true; LinkedHashSet<Entry> entries= new LinkedHashSet<Entry>(); for(IASTEnumerator enumerator : enms) { IASTNodeLocation loc= enumerator.getName().getFileLocation(); if(loc != null) { int nodeOffset= loc.getNodeOffset()+loc.getNodeLength(); String cmt= SINGLELINE_COMMENT_PRECEDING+enumerator.getName(); IRegion line= doc.getLineInformationOfOffset(nodeOffset); if(!doc.get(line.getOffset(), line.getLength()).contains("//")) { //$NON-NLS-1$ noCollisions &= entries.add(new Entry(line.getOffset(),line.getLength(), cmt)); } } } /* * Only auto-insert comments if each enumerator is declared on a unique line */ if(noCollisions) { int max= Integer.MIN_VALUE; for(Entry e : entries) { if(e.length > max) max= e.length; } int addedLength=0; for(Entry e : entries) { // pad with whitespace int toAdd= max-e.length; for(int j=0; j<toAdd; j++) { e.comment.insert(0, " "); //$NON-NLS-1$ } doc.replace(e.offset+e.length+addedLength, 0, e.comment.toString()); addedLength+= e.comment.length(); } } } } }