X-Git-Url: http://git.phpeclipse.com

diff --git a/net.sourceforge.phpeclipse/src/net/sourceforge/phpeclipse/phpeditor/ToggleCommentAction.java b/net.sourceforge.phpeclipse/src/net/sourceforge/phpeclipse/phpeditor/ToggleCommentAction.java
index 0ca710d..ada76ed 100644
--- a/net.sourceforge.phpeclipse/src/net/sourceforge/phpeclipse/phpeditor/ToggleCommentAction.java
+++ b/net.sourceforge.phpeclipse/src/net/sourceforge/phpeclipse/phpeditor/ToggleCommentAction.java
@@ -8,9 +8,10 @@
  * Contributors:
  *     IBM Corporation - initial API and implementation
  *******************************************************************************/
-
 package net.sourceforge.phpeclipse.phpeditor;
 
+import java.util.HashMap;
+import java.util.Map;
 import java.util.ResourceBundle;
 
 import net.sourceforge.phpeclipse.PHPeclipsePlugin;
@@ -18,8 +19,14 @@ import net.sourceforge.phpeclipse.PHPeclipsePlugin;
 import org.eclipse.jface.dialogs.MessageDialog;
 import org.eclipse.jface.text.BadLocationException;
 import org.eclipse.jface.text.IDocument;
+import org.eclipse.jface.text.IRegion;
 import org.eclipse.jface.text.ITextOperationTarget;
 import org.eclipse.jface.text.ITextSelection;
+import org.eclipse.jface.text.ITypedRegion;
+import org.eclipse.jface.text.Region;
+import org.eclipse.jface.text.TextUtilities;
+import org.eclipse.jface.text.source.ISourceViewer;
+import org.eclipse.jface.text.source.SourceViewerConfiguration;
 import org.eclipse.jface.viewers.ISelection;
 import org.eclipse.swt.custom.BusyIndicator;
 import org.eclipse.swt.widgets.Display;
@@ -28,107 +35,261 @@ import org.eclipse.ui.texteditor.ITextEditor;
 import org.eclipse.ui.texteditor.ResourceAction;
 import org.eclipse.ui.texteditor.TextEditorAction;
 
-
-
 /**
- * An action which toggles the single line comment prefixes on the selected lines.
+ * An action which toggles comment prefixes on the selected lines.
  * 
  * @since 3.0
  */
 public final class ToggleCommentAction extends TextEditorAction {
-	
+
 	/** The text operation target */
 	private ITextOperationTarget fOperationTarget;
+
+	/** The document partitioning */
+	private String fDocumentPartitioning;
+
 	/** The comment prefixes */
-	private String[] fCommentPrefixes;
-	
+	private Map fPrefixesMap;
+
 	/**
 	 * Creates and initializes the action for the given text editor. The action
 	 * configures its visual representation from the given resource bundle.
-	 *
-	 * @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
-	 * @see ResourceAction#ResourceAction
+	 * 
+	 * @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
+	 * @see ResourceAction#ResourceAction(ResourceBundle, String, int)
 	 */
-	public ToggleCommentAction(ResourceBundle bundle, String prefix, ITextEditor editor, String[] commentPrefixes) {
+	public ToggleCommentAction(ResourceBundle bundle, String prefix,
+			ITextEditor editor) {
 		super(bundle, prefix, editor);
-		fCommentPrefixes= commentPrefixes;
 	}
-	
+
 	/**
-	 * Implementation of the <code>IAction</code> prototype. Checks if the selected
-	 * lines are all commented or not and uncomment/comments them respectively.
+	 * Implementation of the <code>IAction</code> prototype. Checks if the
+	 * selected lines are all commented or not and uncomments/comments them
+	 * respectively.
 	 */
 	public void run() {
-		if (fOperationTarget == null)
+		if (fOperationTarget == null || fDocumentPartitioning == null
+				|| fPrefixesMap == null)
 			return;
-			
-		ITextEditor editor= getTextEditor();
-		if (!(editor instanceof PHPEditor))
+
+		ITextEditor editor = getTextEditor();
+		if (editor == null)
 			return;
 
 		if (!validateEditorInputState())
 			return;
-		
+
 		final int operationCode;
 		if (isSelectionCommented(editor.getSelectionProvider().getSelection()))
-			operationCode= ITextOperationTarget.STRIP_PREFIX;
+			operationCode = ITextOperationTarget.STRIP_PREFIX;
 		else
-			operationCode= ITextOperationTarget.PREFIX;
-		
-		Shell shell= editor.getSite().getShell();
+			operationCode = ITextOperationTarget.PREFIX;
+
+		Shell shell = editor.getSite().getShell();
 		if (!fOperationTarget.canDoOperation(operationCode)) {
 			if (shell != null)
-				MessageDialog.openError(shell, PHPEditorMessages.getString("ToggleComment.error.title"), PHPEditorMessages.getString("ToggleComment.error.message")); //$NON-NLS-1$ //$NON-NLS-2$
+				MessageDialog
+						.openError(
+								shell,
+								PHPEditorMessages
+										.getString("ToggleComment.error.title"), PHPEditorMessages.getString("ToggleComment.error.message")); //$NON-NLS-1$ //$NON-NLS-2$
 			return;
 		}
-		
-		Display display= null;
-		if (shell != null && !shell.isDisposed()) 
-			display= shell.getDisplay();
-	
+
+		Display display = null;
+		if (shell != null && !shell.isDisposed())
+			display = shell.getDisplay();
+
 		BusyIndicator.showWhile(display, new Runnable() {
 			public void run() {
 				fOperationTarget.doOperation(operationCode);
 			}
 		});
 	}
-	
+
 	/**
 	 * Is the given selection single-line commented?
-	 *
-	 * @param selection Selection to check
-	 * @return <code>true</code> iff all selected lines are single-line commented
+	 * 
+	 * @param selection
+	 *            Selection to check
+	 * @return <code>true</code> iff all selected lines are commented
 	 */
 	private boolean isSelectionCommented(ISelection selection) {
 		if (!(selection instanceof ITextSelection))
 			return false;
-			
-		ITextSelection ts= (ITextSelection) selection;
-		if (ts.getStartLine() < 0 || ts.getEndLine() < 0)
+
+		ITextSelection textSelection = (ITextSelection) selection;
+		if (textSelection.getStartLine() < 0 || textSelection.getEndLine() < 0)
 			return false;
-		
-		IDocument document= getTextEditor().getDocumentProvider().getDocument(getTextEditor().getEditorInput());
-		OUTER: for (int i= ts.getStartLine(); i <= ts.getEndLine(); i++) {
-			for (int j= 0; j < fCommentPrefixes.length; j++) {
-				try {
-					if (fCommentPrefixes[j].length() == 0)
-						continue;
-					String s= document.get(document.getLineOffset(i), document.getLineLength(i));
-					int index= s.indexOf(fCommentPrefixes[j]);
-					if (index >= 0 && s.substring(0, index).trim().length() == 0)
-						continue OUTER;
-				} catch (BadLocationException e) {
-					// should not happen
-					PHPeclipsePlugin.log(e);
-				}
+
+		IDocument document = getTextEditor().getDocumentProvider().getDocument(
+				getTextEditor().getEditorInput());
+
+		try {
+
+			IRegion block = getTextBlockFromSelection(textSelection, document);
+			ITypedRegion[] regions = TextUtilities.computePartitioning(
+					document, fDocumentPartitioning, block.getOffset(), block
+							.getLength(), false);
+
+			int lineCount = 0;
+			int[] lines = new int[regions.length * 2]; // [startline, endline,
+														// startline, endline,
+														// ...]
+			for (int i = 0, j = 0; i < regions.length; i++, j += 2) {
+				// start line of region
+				lines[j] = getFirstCompleteLineOfRegion(regions[i], document);
+				// end line of region
+				int length = regions[i].getLength();
+				int offset = regions[i].getOffset() + length;
+				if (length > 0)
+					offset--;
+				lines[j + 1] = (lines[j] == -1 ? -1 : document
+						.getLineOfOffset(offset));
+				lineCount += lines[j + 1] - lines[j] + 1;
 			}
-			return false;
+
+			// Perform the check
+			for (int i = 0, j = 0; i < regions.length; i++, j += 2) {
+				String[] prefixes = (String[]) fPrefixesMap.get(regions[i]
+						.getType());
+				if (prefixes != null && prefixes.length > 0 && lines[j] >= 0
+						&& lines[j + 1] >= 0)
+					if (!isBlockCommented(lines[j], lines[j + 1], prefixes,
+							document))
+						return false;
+			}
+
+			return true;
+
+		} catch (BadLocationException x) {
+			// should not happen
+			PHPeclipsePlugin.log(x);
+		}
+
+		return false;
+	}
+
+	/**
+	 * Creates a region describing the text block (something that starts at the
+	 * beginning of a line) completely containing the current selection.
+	 * 
+	 * @param selection
+	 *            The selection to use
+	 * @param document
+	 *            The document
+	 * @return the region describing the text block comprising the given
+	 *         selection
+	 */
+	private IRegion getTextBlockFromSelection(ITextSelection selection,
+			IDocument document) {
+
+		try {
+			IRegion line = document.getLineInformationOfOffset(selection
+					.getOffset());
+			int length = selection.getLength() == 0 ? line.getLength()
+					: selection.getLength()
+							+ (selection.getOffset() - line.getOffset());
+			return new Region(line.getOffset(), length);
+
+		} catch (BadLocationException x) {
+			// should not happen
+			PHPeclipsePlugin.log(x);
 		}
-		return true;
+
+		return null;
+	}
+
+	/**
+	 * Returns the index of the first line whose start offset is in the given
+	 * text range.
+	 * 
+	 * @param region
+	 *            the text range in characters where to find the line
+	 * @param document
+	 *            The document
+	 * @return the first line whose start index is in the given range, -1 if
+	 *         there is no such line
+	 */
+	private int getFirstCompleteLineOfRegion(IRegion region, IDocument document) {
+
+		try {
+
+			int startLine = document.getLineOfOffset(region.getOffset());
+
+			int offset = document.getLineOffset(startLine);
+			if (offset >= region.getOffset())
+				return startLine;
+
+			offset = document.getLineOffset(startLine + 1);
+			return (offset > region.getOffset() + region.getLength() ? -1
+					: startLine + 1);
+
+		} catch (BadLocationException x) {
+			// should not happen
+			PHPeclipsePlugin.log(x);
+		}
+
+		return -1;
+	}
+
+	/**
+	 * Determines whether each line is prefixed by one of the prefixes.
+	 * 
+	 * @param startLine
+	 *            Start line in document
+	 * @param endLine
+	 *            End line in document
+	 * @param prefixes
+	 *            Possible comment prefixes
+	 * @param document
+	 *            The document
+	 * @return <code>true</code> iff each line from <code>startLine</code>
+	 *         to and including <code>endLine</code> is prepended by one of
+	 *         the <code>prefixes</code>, ignoring whitespace at the begin of
+	 *         line
+	 */
+	private boolean isBlockCommented(int startLine, int endLine,
+			String[] prefixes, IDocument document) {
+
+		try {
+
+			// check for occurrences of prefixes in the given lines
+			for (int i = startLine; i <= endLine; i++) {
+
+				IRegion line = document.getLineInformation(i);
+				String text = document.get(line.getOffset(), line.getLength());
+
+				int[] found = TextUtilities.indexOf(prefixes, text, 0);
+
+				if (found[0] == -1)
+					// found a line which is not commented
+					return false;
+
+				String s = document.get(line.getOffset(), found[0]);
+				s = s.trim();
+				if (s.length() != 0)
+					// found a line which is not commented
+					return false;
+
+			}
+
+			return true;
+
+		} catch (BadLocationException x) {
+			// should not happen
+			PHPeclipsePlugin.log(x);
+		}
+
+		return false;
 	}
 
 	/**
@@ -139,25 +300,65 @@ public final class ToggleCommentAction extends TextEditorAction {
 	 */
 	public void update() {
 		super.update();
-		
+
 		if (!canModifyEditor()) {
 			setEnabled(false);
 			return;
 		}
-		
-		ITextEditor editor= getTextEditor();
+
+		ITextEditor editor = getTextEditor();
 		if (fOperationTarget == null && editor != null)
-			fOperationTarget= (ITextOperationTarget) editor.getAdapter(ITextOperationTarget.class);
-			
-		boolean isEnabled= (fOperationTarget != null && fOperationTarget.canDoOperation(ITextOperationTarget.PREFIX) && fOperationTarget.canDoOperation(ITextOperationTarget.STRIP_PREFIX));
+			fOperationTarget = (ITextOperationTarget) editor
+					.getAdapter(ITextOperationTarget.class);
+
+		boolean isEnabled = (fOperationTarget != null
+				&& fOperationTarget.canDoOperation(ITextOperationTarget.PREFIX) && fOperationTarget
+				.canDoOperation(ITextOperationTarget.STRIP_PREFIX));
 		setEnabled(isEnabled);
 	}
-	
+
 	/*
 	 * @see TextEditorAction#setEditor(ITextEditor)
 	 */
 	public void setEditor(ITextEditor editor) {
 		super.setEditor(editor);
-		fOperationTarget= null;
+		fOperationTarget = null;
+	}
+
+	public void configure(ISourceViewer sourceViewer,
+			SourceViewerConfiguration configuration) {
+		fPrefixesMap = null;
+
+		String[] types = configuration.getConfiguredContentTypes(sourceViewer);
+		Map prefixesMap = new HashMap(types.length);
+		for (int i = 0; i < types.length; i++) {
+			String type = types[i];
+			String[] prefixes = configuration.getDefaultPrefixes(sourceViewer,
+					type);
+			if (prefixes != null && prefixes.length > 0) {
+				int emptyPrefixes = 0;
+				for (int j = 0; j < prefixes.length; j++)
+					if (prefixes[j].length() == 0)
+						emptyPrefixes++;
+
+				if (emptyPrefixes > 0) {
+					String[] nonemptyPrefixes = new String[prefixes.length
+							- emptyPrefixes];
+					for (int j = 0, k = 0; j < prefixes.length; j++) {
+						String prefix = prefixes[j];
+						if (prefix.length() != 0) {
+							nonemptyPrefixes[k] = prefix;
+							k++;
+						}
+					}
+					prefixes = nonemptyPrefixes;
+				}
+
+				prefixesMap.put(type, prefixes);
+			}
+		}
+		fDocumentPartitioning = configuration
+				.getConfiguredDocumentPartitioning(sourceViewer);
+		fPrefixesMap = prefixesMap;
 	}
 }