/******************************************************************************* * Copyright (c) 2000, 2007 IBM Corporation 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: * IBM Corporation - initial API and implementation *******************************************************************************/ package org.eclipse.vjet.eclipse.internal.ui.text; import org.eclipse.vjet.eclipse.ui.VjetPreferenceConstants; import org.eclipse.vjet.eclipse.ui.VjetUIPlugin; import org.eclipse.core.runtime.Assert; import org.eclipse.dltk.mod.ui.DLTKUIPlugin; 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.Region; import org.eclipse.jface.text.TextUtilities; import org.eclipse.ui.IEditorPart; import org.eclipse.ui.IWorkbenchPage; import org.eclipse.ui.texteditor.ITextEditorExtension3; /** * * copy from JavaDocAutoIndentStrategy and comment the tag support part. * Auto indent strategy for vjo doc comments. */ public class VjoDocAutoIndentStrategy extends DefaultIndentLineAutoEditStrategy { /** The partitioning that this strategy operates on. */ private final String fPartitioning; /** * Creates a new Javadoc auto indent strategy for the given document * partitioning. * * @param partitioning * the document partitioning */ public VjoDocAutoIndentStrategy(String partitioning) { fPartitioning = partitioning; } /** * Copies the indentation of the previous line and adds a star. If the * javadoc just started on this line add standard method tags and close the * javadoc. * * @param d * the document to work on * @param c * the command to deal with */ private void indentAfterNewLine(IDocument d, DocumentCommand c) { int offset = c.offset; if (offset == -1 || d.getLength() == 0) return; try { int p = (offset == d.getLength() ? offset - 1 : offset); IRegion line = d.getLineInformationOfOffset(p); int lineOffset = line.getOffset(); int firstNonWS = findEndOfWhiteSpace(d, lineOffset, offset); Assert.isTrue(firstNonWS >= lineOffset, "indentation must not be negative"); //$NON-NLS-1$ StringBuffer buf = new StringBuffer(c.text); IRegion prefix = findPrefixRange(d, line); String indentation = d.get(prefix.getOffset(), prefix.getLength()); int lengthToAdd = Math.min(offset - prefix.getOffset(), prefix .getLength()); buf.append(indentation.substring(0, lengthToAdd)); if (firstNonWS < offset) { if (d.getChar(firstNonWS) == '/') { // javadoc started on this line buf.append(" * "); //$NON-NLS-1$ if (isPreferenceTrue(VjetPreferenceConstants.EDITOR_CLOSE_COMMENTS) && isNewComment(d, offset)) { c.shiftsCaret = false; c.caretOffset = c.offset + buf.length(); String lineDelimiter = TextUtilities .getDefaultLineDelimiter(d); String endTag = lineDelimiter + indentation + " */"; //$NON-NLS-1$ // if // (isPreferenceTrue(PreferenceConstants.EDITOR_ADD_JAVADOC_TAGS)) // { // // we need to close the comment before computing // // the correct tags in order to get the method // d.replace(offset, 0, endTag); // // // evaluate method signature // ICompilationUnit unit= getCompilationUnit(); // // if (unit != null) { // try { // JavaModelUtil.reconcile(unit); // String string= createJavaDocTags(d, c, indentation, // lineDelimiter, unit); // // only add tags if they are non-empty - the empty // line has already been added above. // if (string != null && !string.trim().equals("*")) // //$NON-NLS-1$ // buf.append(string); // } catch (CoreException e) { // // ignore // } // } // } else { buf.append(endTag); // } } } } // move the caret behind the prefix, even if we do not have to // insert it. if (lengthToAdd < prefix.getLength()) c.caretOffset = offset + prefix.getLength() - lengthToAdd; c.text = buf.toString(); } catch (BadLocationException excp) { // stop work } } /** * Returns the value of the given boolean-typed preference. * * @param preference * the preference to look up * @return the value of the given preference in the Java plug-in's default * preference store */ private boolean isPreferenceTrue(String preference) { return VjetUIPlugin.getDefault().getPreferenceStore().getBoolean( preference); } /** * Returns the range of the Javadoc prefix on the given line in * <code>document</code>. The prefix greedily matches the following regex * pattern: <code>\w*\*\w*</code>, that is, any number of whitespace * characters, followed by an asterix ('*'), followed by any number of * whitespace characters. * * @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 */ private IRegion findPrefixRange(IDocument document, IRegion line) throws BadLocationException { int lineOffset = line.getOffset(); int lineEnd = lineOffset + line.getLength(); int indentEnd = findEndOfWhiteSpace(document, lineOffset, lineEnd); if (indentEnd < lineEnd && document.getChar(indentEnd) == '*') { indentEnd++; while (indentEnd < lineEnd && document.getChar(indentEnd) == ' ') indentEnd++; } return new Region(lineOffset, indentEnd - lineOffset); } /** * Creates the Javadoc tags for newly inserted comments. * * @param document * the document * @param command * the command * @param indentation * the base indentation to use * @param lineDelimiter * the line delimiter to use * @param unit * the compilation unit shown in the editor * @return the tags to add to the document * @throws CoreException * if accessing the java model fails * @throws BadLocationException * if accessing the document fails */ // private String createJavaDocTags(IDocument document, DocumentCommand // command, String indentation, String lineDelimiter, ICompilationUnit unit) // throws CoreException, BadLocationException // { // IJavaElement element= unit.getElementAt(command.offset); // if (element == null) // return null; // // switch (element.getElementType()) { // case IJavaElement.TYPE: // return createTypeTags(document, command, indentation, lineDelimiter, // (IType) element); // // case IJavaElement.METHOD: // return createMethodTags(document, command, indentation, lineDelimiter, // (IMethod) element); // // default: // return null; // } // } /** * Removes start and end of a comment and corrects indentation and line * delimiters. * * @param comment * the computed comment * @param indentation * the base indentation * @param project * the java project for the formatter settings, or * <code>null</code> for global preferences * @param lineDelimiter * the line delimiter * @return a trimmed version of <code>comment</code> */ // private String prepareTemplateComment(String comment, String indentation, // IJavaProject project, String lineDelimiter) { // // trim comment start and end if any // if (comment.endsWith("*/")) //$NON-NLS-1$ // comment= comment.substring(0, comment.length() - 2); // comment= comment.trim(); // if (comment.startsWith("/*")) { //$NON-NLS-1$ // if (comment.length() > 2 && comment.charAt(2) == '*') { // comment= comment.substring(3); // remove '/**' // } else { // comment= comment.substring(2); // remove '/*' // } // } // // trim leading spaces, but not new lines // int nonSpace= 0; // int len= comment.length(); // while (nonSpace < len && Character.getType(comment.charAt(nonSpace)) == // Character.SPACE_SEPARATOR) // nonSpace++; // comment= comment.substring(nonSpace); // // return Strings.changeIndent(comment, 0, project, indentation, // lineDelimiter); // } // // private String createTypeTags(IDocument document, DocumentCommand // command, String indentation, String lineDelimiter, IType type) // throws CoreException, BadLocationException // { // String[] typeParamNames= // StubUtility.getTypeParameterNames(type.getTypeParameters()); // String comment= CodeGeneration.getTypeComment(type.getCompilationUnit(), // type.getTypeQualifiedName('.'), typeParamNames, lineDelimiter); // if (comment != null) { // boolean javadocComment= comment.startsWith("/**"); //$NON-NLS-1$ // if (!isFirstComment(document, command, type, javadocComment)) // return null; // return prepareTemplateComment(comment.trim(), indentation, // type.getJavaProject(), lineDelimiter); // } // return null; // } // // private String createMethodTags(IDocument document, DocumentCommand // command, String indentation, String lineDelimiter, IMethod method) // throws CoreException, BadLocationException // { // IRegion partition= TextUtilities.getPartition(document, fPartitioning, // command.offset, false); // IMethod inheritedMethod= getInheritedMethod(method); // String comment= CodeGeneration.getMethodComment(method, inheritedMethod, // lineDelimiter); // if (comment != null) { // comment= comment.trim(); // boolean javadocComment= comment.startsWith("/**"); //$NON-NLS-1$ // if (!isFirstComment(document, command, method, javadocComment)) // return null; // boolean isJavaDoc= partition.getLength() >= 3 && // document.get(partition.getOffset(), 3).equals("/**"); //$NON-NLS-1$ // if (javadocComment == isJavaDoc) { // return prepareTemplateComment(comment, indentation, // method.getJavaProject(), lineDelimiter); // } // } // return null; // } /** * Returns <code>true</code> if the comment being inserted at * <code>command.offset</code> is the first comment (the first javadoc * comment if <code>ignoreJavadoc</code> is <code>true</code>) of the * given member. * <p> * see also https://bugs.eclipse.org/bugs/show_bug.cgi?id=55325 (don't add * parameters if the member already has a comment) * </p> */ // private boolean isFirstComment(IDocument document, DocumentCommand // command, IMember member, boolean ignoreNonJavadoc) throws // BadLocationException, JavaModelException { // IRegion partition= TextUtilities.getPartition(document, fPartitioning, // command.offset, false); // ISourceRange sourceRange= member.getSourceRange(); // if (sourceRange == null || sourceRange.getOffset() != // partition.getOffset()) // return false; // int srcOffset= sourceRange.getOffset(); // int srcLength= sourceRange.getLength(); // int nameRelativeOffset= member.getNameRange().getOffset() - srcOffset; // int partitionRelativeOffset= partition.getOffset() - srcOffset; // String token= ignoreNonJavadoc ? "/**" : "/*"; //$NON-NLS-1$ // //$NON-NLS-2$ // return document.get(srcOffset, srcLength).lastIndexOf(token, // nameRelativeOffset) == partitionRelativeOffset; // } /** * Unindents a typed slash ('/') if it forms the end of a comment. * * @param d * the document * @param c * the command */ private void indentAfterCommentEnd(IDocument d, DocumentCommand c) { if (c.offset < 2 || d.getLength() == 0) { return; } try { if ("* ".equals(d.get(c.offset - 2, 2))) { //$NON-NLS-1$ // modify document command c.length++; c.offset--; } } catch (BadLocationException excp) { // stop work } } /** * Guesses if the command operates within a newly created javadoc comment or * not. If in doubt, it will assume that the javadoc 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, fPartitioning, 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()); if (comment.indexOf("/*", 2) != -1) //$NON-NLS-1$ return true; // enclosed another comment -> probably a new // comment return false; } catch (BadLocationException e) { return false; } } private boolean isSmartMode() { IWorkbenchPage page = DLTKUIPlugin.getActivePage(); if (page != null) { IEditorPart part = page.getActiveEditor(); if (part instanceof ITextEditorExtension3) { ITextEditorExtension3 extension = (ITextEditorExtension3) part; return extension.getInsertMode() == ITextEditorExtension3.SMART_INSERT; } } return false; } /* * @see IAutoIndentStrategy#customizeDocumentCommand */ public void customizeDocumentCommand(IDocument document, DocumentCommand command) { if (!isSmartMode()) return; if (command.text != null) { if (command.length == 0) { String[] lineDelimiters = document.getLegalLineDelimiters(); int index = TextUtilities .endsWith(lineDelimiters, command.text); if (index > -1) { // ends with line delimiter if (lineDelimiters[index].equals(command.text)) // just the line delimiter indentAfterNewLine(document, command); return; } } if (command.text.equals("/")) { //$NON-NLS-1$ indentAfterCommentEnd(document, command); return; } } } /** * Returns the method inherited from, <code>null</code> if method is newly * defined. * * @param method * the method being written * @return the ancestor method, or <code>null</code> if none * @throws JavaModelException * if accessing the java model fails */ // private static IMethod getInheritedMethod(IMethod method) throws // JavaModelException { // IType declaringType= method.getDeclaringType(); // MethodOverrideTester tester= // SuperTypeHierarchyCache.getMethodOverrideTester(declaringType); // return tester.findOverriddenMethod(method, true); // } // // /** // * Returns the compilation unit of the CompilationUnitEditor invoking the // AutoIndentStrategy, // * might return <code>null</code> on error. // * @return the compilation unit represented by the document // */ // private static ICompilationUnit getCompilationUnit() { // // IWorkbenchWindow window= // PlatformUI.getWorkbench().getActiveWorkbenchWindow(); // if (window == null) // return null; // // IWorkbenchPage page= window.getActivePage(); // if (page == null) // return null; // // IEditorPart editor= page.getActiveEditor(); // if (editor == null) // return null; // // IWorkingCopyManager manager= // JavaPlugin.getDefault().getWorkingCopyManager(); // ICompilationUnit unit= manager.getWorkingCopy(editor.getEditorInput()); // if (unit == null) // return null; // // return unit; // } }