/******************************************************************************* * Copyright (c) 2000, 2008 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 * Anton Leherbauer (Wind River Systems) * Andrew Gvozdev - http://bugs.eclipse.org/236160 *******************************************************************************/ package org.eclipse.cdt.internal.ui.actions; import java.util.LinkedList; import java.util.List; import java.util.ResourceBundle; import org.eclipse.jface.text.BadLocationException; import org.eclipse.jface.text.BadPartitioningException; import org.eclipse.jface.text.IDocument; import org.eclipse.jface.text.IDocumentExtension3; import org.eclipse.jface.text.IRegion; import org.eclipse.jface.text.ITextSelection; import org.eclipse.jface.text.ITypedRegion; import org.eclipse.jface.text.Region; import org.eclipse.ui.texteditor.ITextEditor; import org.eclipse.cdt.ui.text.ICPartitions; /** * Action that encloses the editor's current selection with C block comment terminators * (<code>/*</code> and <code>*/</code>). * * @since 3.0 */ public class AddBlockCommentAction extends BlockCommentAction { /** * Creates a new instance. * * @param bundle the resource bundle * @param prefix a prefix to be prepended to the various resource keys * (described in <code>ResourceAction</code> constructor), or * <code>null</code> if none * @param editor the text editor */ public AddBlockCommentAction(ResourceBundle bundle, String prefix, ITextEditor editor) { super(bundle, prefix, editor); } @Override protected void runInternal(ITextSelection selection, IDocumentExtension3 docExtension, Edit.EditFactory factory) throws BadLocationException, BadPartitioningException { if ( !(docExtension instanceof IDocument) ) return; List<Edit> edits= new LinkedList<Edit>(); ITypedRegion firstPartition = docExtension.getPartition(ICPartitions.C_PARTITIONING, selection.getOffset(), false); ITypedRegion lastPartition = docExtension.getPartition(ICPartitions.C_PARTITIONING, selection.getOffset() + selection.getLength() - 1, false); int commentAreaStart = selection.getOffset(); int commentAreaEnd = selection.getOffset()+selection.getLength(); // Include special partitions fully in the comment area if (isSpecialPartition(firstPartition.getType())) { commentAreaStart = firstPartition.getOffset(); } if (isSpecialPartition(lastPartition.getType())) { commentAreaEnd = lastPartition.getOffset() + lastPartition.getLength(); } Region estimatedCommentArea = new Region(commentAreaStart,commentAreaEnd-commentAreaStart); Region commentArea = handleEnclosingPartitions(estimatedCommentArea, lastPartition, (IDocument)docExtension, factory, edits); handleInteriorPartition(commentArea, firstPartition, docExtension, factory, edits); executeEdits(edits); } /** * Add enclosing comment tags for the whole area to be commented * * @param commentArea initial comment area which can be adjusted * @param lastPartition last partition * @param doc document * @param factory Edit factory * @param edits List of edits to update the document * @return new possibly adjusted comment area * @throws BadLocationException */ private Region handleEnclosingPartitions(Region commentArea, ITypedRegion lastPartition, IDocument doc, Edit.EditFactory factory, List<Edit> edits) throws BadLocationException { int commentAreaStart = commentArea.getOffset(); int commentAreaEnd = commentArea.getOffset() + commentArea.getLength(); String commentStartTag = getCommentStart(); // "/*" String commentEndTag = getCommentEnd(); // "*/" String startLineEOL = doc.getLineDelimiter(doc.getLineOfOffset(commentAreaStart)); if (startLineEOL==null) startLineEOL=""; //$NON-NLS-1$ String endLineEOL = doc.getLineDelimiter(doc.getLineOfOffset(commentAreaEnd-1)); if (endLineEOL==null) endLineEOL=""; //$NON-NLS-1$ boolean isLeftEol = commentAreaStart<startLineEOL.length() || doc.get(commentAreaStart-startLineEOL.length(),startLineEOL.length()).equals(startLineEOL); boolean isRightEol = doc.get(commentAreaEnd-endLineEOL.length(),endLineEOL.length()).equals(endLineEOL); if (isLeftEol && isRightEol) { // Block of full lines found int areaStartLine = doc.getLineOfOffset(commentAreaStart+startLineEOL.length()); int areaEndLine = doc.getLineOfOffset(commentAreaEnd-endLineEOL.length()); if (areaStartLine!=areaEndLine) { // If multiple full lines arrange inserting comment tags on their own lines commentStartTag = getCommentStart()+startLineEOL; commentEndTag = getCommentEnd()+endLineEOL; } else { // If one full line insert end comment tag on the same line (before the EOL) commentAreaEnd = commentAreaEnd-endLineEOL.length(); } } else { if (lastPartition.getType() == ICPartitions.C_SINGLE_LINE_COMMENT || lastPartition.getType() == ICPartitions.C_SINGLE_LINE_DOC_COMMENT) { // C++ comments "//" partition ends with EOL, insert end comment tag before it // on the same line, so we get something like /*// text*/ commentAreaEnd = commentAreaEnd-endLineEOL.length(); } } edits.add(factory.createEdit(commentAreaStart, 0, commentStartTag)); edits.add(factory.createEdit(commentAreaEnd, 0, commentEndTag)); return new Region(commentAreaStart,commentAreaEnd-commentAreaStart); } /** * Make all inside partitions join in one comment, in particular remove * all enclosing comment tokens of the inside partitions. * * @param commentArea comment area region * @param partition first partition * @param docExtension document * @param factory EditFactory * @param List of edits to update the document * @throws BadLocationException * @throws BadPartitioningException */ private void handleInteriorPartition(IRegion commentArea, ITypedRegion partition, IDocumentExtension3 docExtension, Edit.EditFactory factory, List<Edit> edits) throws BadLocationException, BadPartitioningException { int commentAreaEnd = commentArea.getOffset() + commentArea.getLength(); int prevPartitionEnd = -1; int partitionEnd = partition.getOffset()+partition.getLength(); final int startCommentTokenLength = getCommentStart().length(); final int endCommentTokenLength = getCommentEnd().length(); while (partitionEnd<=commentAreaEnd) { if (partition.getType() == ICPartitions.C_MULTI_LINE_COMMENT || partition.getType() == ICPartitions.C_MULTI_LINE_DOC_COMMENT) { // already in a comment - remove start/end tokens edits.add(factory.createEdit(partition.getOffset(), startCommentTokenLength, "")); //$NON-NLS-1$ edits.add(factory.createEdit(partitionEnd - endCommentTokenLength, endCommentTokenLength, "")); //$NON-NLS-1$ } // advance to next partition prevPartitionEnd = partitionEnd; partition= docExtension.getPartition(ICPartitions.C_PARTITIONING, partitionEnd, false); partitionEnd = partition.getOffset() + partition.getLength(); // break the loop if we get stuck and no advance was made if (partitionEnd<=prevPartitionEnd) break; } } /** * Returns whether <code>partType</code> is special, i.e. a <code>String</code>, * <code>Character</code>, or <code>Comment</code> partition. * * @param partType the partition type to check * @return <code>true</code> if <code>partType</code> is special, <code>false</code> otherwise */ private boolean isSpecialPartition(String partType) { return partType != IDocument.DEFAULT_CONTENT_TYPE; } @Override protected boolean isValidSelection(ITextSelection selection) { return selection != null && !selection.isEmpty() && selection.getLength() > 0; } }