Package com.dubture.twig.ui.actions

Source Code of com.dubture.twig.ui.actions.TwigToggleLineCommentHandler$ToggleLinesRunnable

/*******************************************************************************
* This file is part of the Twig eclipse plugin.
*
* (c) Robert Gruendler <r.gruendler@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
******************************************************************************/
package com.dubture.twig.ui.actions;

import java.lang.reflect.InvocationTargetException;
import java.util.Collections;

import org.eclipse.core.commands.ExecutionException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.jface.dialogs.ProgressMonitorDialog;
import org.eclipse.jface.operation.IRunnableWithProgress;
import org.eclipse.jface.text.BadLocationException;
import org.eclipse.jface.text.DocumentRewriteSession;
import org.eclipse.jface.text.DocumentRewriteSessionType;
import org.eclipse.jface.text.IDocument;
import org.eclipse.jface.text.IDocumentExtension4;
import org.eclipse.jface.text.IRegion;
import org.eclipse.jface.text.ITextSelection;
import org.eclipse.jface.text.ITypedRegion;
import org.eclipse.jface.text.Position;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Event;
import org.eclipse.ui.ISources;
import org.eclipse.ui.texteditor.ITextEditor;
import org.eclipse.wst.sse.core.StructuredModelManager;
import org.eclipse.wst.sse.core.internal.provisional.IStructuredModel;
import org.eclipse.wst.sse.core.internal.provisional.text.IStructuredDocument;
import org.eclipse.wst.sse.ui.StructuredTextEditor;
import org.eclipse.wst.sse.ui.internal.Logger;
import org.eclipse.wst.sse.ui.internal.SSEUIMessages;
import org.eclipse.wst.sse.ui.internal.StructuredTextViewer;
import org.eclipse.wst.sse.ui.internal.handlers.AbstractCommentHandler;
import org.eclipse.wst.sse.ui.internal.handlers.ToggleLineCommentHandler;

@SuppressWarnings("restriction")
public class TwigToggleLineCommentHandler extends AbstractCommentHandler
{

    static final String OPEN_COMMENT = "{#"; //$NON-NLS-1$
    static final String CLOSE_COMMENT = "#}"; //$NON-NLS-1$

    /** if toggling more then this many lines then use a busy indicator */
    private static final int TOGGLE_LINES_MAX_NO_BUSY_INDICATOR = 20;

    private static final ToggleLineCommentHandler toggleLineCommentHandler = new ToggleLineCommentHandler();

    @Override
    protected void processAction(final ITextEditor textEditor,
            final IStructuredDocument document, ITextSelection textSelection)
    {

        IStructuredModel model = null;
        DocumentRewriteSession session = null;
        boolean changed = false;

        try {
            // get text selection lines info
            int selectionStartLine = textSelection.getStartLine();
            int selectionEndLine = textSelection.getEndLine();

            int selectionEndLineOffset = document
                    .getLineOffset(selectionEndLine);
            int selectionEndOffset = textSelection.getOffset()
                    + textSelection.getLength();

            // adjust selection end line
            if ((selectionEndLine > selectionStartLine)
                    && (selectionEndLineOffset == selectionEndOffset)) {
                selectionEndLine--;
                selectionEndLineOffset = document.getLineInformation(
                        selectionEndLine).getOffset();
            }

            int selectionStartLineOffset = document
                    .getLineOffset(selectionStartLine);
            ITypedRegion[] lineTypedRegions = document.computePartitioning(
                    selectionStartLineOffset, selectionEndLineOffset
                            - selectionStartLineOffset);

            if (lineTypedRegions != null
                    && lineTypedRegions.length >= 1
                    && (lineTypedRegions[0].getType().equals(
                            "org.eclipse.wst.html.HTML_DEFAULT") || lineTypedRegions[0]
                            .getType().equals("com.dubture.twig.TWIG_DEFAULT"))) {

                // save the selection position since it will be changing
                Position selectionPosition = null;
                selectionPosition = new Position(textSelection.getOffset(),
                        textSelection.getLength());
                document.addPosition(selectionPosition);

                model = StructuredModelManager.getModelManager()
                        .getModelForEdit(document);
                if (model != null) {
                    // makes it so one undo will undo all the edits to the
                    // document
                    model.beginRecording(this,
                            SSEUIMessages.ToggleComment_label,
                            SSEUIMessages.ToggleComment_description);

                    // keeps listeners from doing anything until updates are all
                    // done
                    model.aboutToChangeModel();
                    if (document instanceof IDocumentExtension4) {
                        session = ((IDocumentExtension4) document)
                                .startRewriteSession(DocumentRewriteSessionType.UNRESTRICTED);
                    }
                    changed = true;

                    // get the display for the editor if we can
                    Display display = null;
                    if (textEditor instanceof StructuredTextEditor) {
                        StructuredTextViewer viewer = ((StructuredTextEditor) textEditor)
                                .getTextViewer();
                        if (viewer != null) {
                            display = viewer.getControl().getDisplay();
                        }
                    }

                    // create the toggling operation
                    IRunnableWithProgress toggleCommentsRunnable = new ToggleLinesRunnable(
                            model.getContentTypeIdentifier(), document,
                            selectionStartLine, selectionEndLine, display);

                    // if toggling lots of lines then use progress monitor else
                    // just
                    // run the operation
                    if ((selectionEndLine - selectionStartLine) > TOGGLE_LINES_MAX_NO_BUSY_INDICATOR
                            && display != null) {
                        ProgressMonitorDialog dialog = new ProgressMonitorDialog(
                                display.getActiveShell());
                        dialog.run(false, true, toggleCommentsRunnable);
                    } else {
                        toggleCommentsRunnable.run(new NullProgressMonitor());
                    }
                }
            } else {
                org.eclipse.core.expressions.EvaluationContext evaluationContext = new org.eclipse.core.expressions.EvaluationContext(
                        null, "")
                {
                    @Override
                    public Object getVariable(String name)
                    {
                        if (ISources.ACTIVE_EDITOR_NAME.equals(name)) {
                            return textEditor;
                        }
                        return null;
                    }
                };
                org.eclipse.core.commands.ExecutionEvent executionEvent = new org.eclipse.core.commands.ExecutionEvent(
                        null, Collections.EMPTY_MAP, new Event(),
                        evaluationContext);
                toggleLineCommentHandler.execute(executionEvent);
            }

        } catch (InvocationTargetException e) {
            Logger.logException(
                    "Problem running toggle comment progess dialog.", e); //$NON-NLS-1$
        } catch (InterruptedException e) {
            Logger.logException(
                    "Problem running toggle comment progess dialog.", e); //$NON-NLS-1$
        } catch (BadLocationException e) {
            Logger.logException(
                    "The given selection " + textSelection + " must be invalid", e); //$NON-NLS-1$ //$NON-NLS-2$
        } catch (ExecutionException e) {
        } finally {
            // clean everything up
            if (session != null && document instanceof IDocumentExtension4) {
                ((IDocumentExtension4) document).stopRewriteSession(session);
            }

            if (model != null) {
                model.endRecording(this);
                if (changed) {
                    model.changedModel();
                }
                model.releaseFromEdit();
            }
        }

    }

    private static class ToggleLinesRunnable implements IRunnableWithProgress
    {
        private IStructuredDocument fDocument;
        private int fSelectionStartLine;
        private int fSelectionEndLine;
        private Display fDisplay;

        protected ToggleLinesRunnable(String contentTypeIdentifier,
                IStructuredDocument document, int selectionStartLine,
                int selectionEndLine, Display display)
        {
            this.fDocument = document;
            this.fSelectionStartLine = selectionStartLine;
            this.fSelectionEndLine = selectionEndLine;
            this.fDisplay = display;
        }

        @Override
        public void run(IProgressMonitor monitor)
                throws InvocationTargetException, InterruptedException
        {

            monitor.beginTask(SSEUIMessages.ToggleComment_progress,
                    this.fSelectionEndLine - this.fSelectionStartLine);
            try {
                boolean allLinesCommented = true;
                for (int i = fSelectionStartLine; i <= fSelectionEndLine; i++) {
                    try {
                        if (fDocument.getLineLength(i) > 0) {
                            if (!isCommentLine(fDocument, i)) {
                                allLinesCommented = false;
                                break;
                            }
                        }
                    } catch (BadLocationException e) {
                        Logger.log(Logger.WARNING_DEBUG, e.getMessage(), e);
                    }
                }
                // toggle each line so long as task not canceled
                for (int line = this.fSelectionStartLine; line <= this.fSelectionEndLine
                        && !monitor.isCanceled(); ++line) {

                    // allows the user to be able to click the cancel button
                    readAndDispatch(this.fDisplay);

                    // get the line region
                    IRegion lineRegion = this.fDocument
                            .getLineInformation(line);

                    // don't toggle empty lines
                    String content = this.fDocument.get(lineRegion.getOffset(),
                            lineRegion.getLength());
                    if (content.trim().length() > 0) {
                        // try to get a line comment type

                        // toggle the comment on the line
                        if (allLinesCommented) {
                            remove(this.fDocument, lineRegion.getOffset(),
                                    lineRegion.getLength(), true);
                        } else {
                            apply(this.fDocument, lineRegion.getOffset(),
                                    lineRegion.getLength());
                        }
                    }
                    monitor.worked(1);
                }
            } catch (BadLocationException e) {
                Logger.logException("Bad location while toggling comments.", e); //$NON-NLS-1$
            }
            // done work
            monitor.done();

        }

        /**
         * <p>
         * Assumes that the given offset is at the begining of a line and adds
         * the line comment prefix there
         * </p>
         *
         */
        public void apply(IStructuredDocument document, int offset, int length)
                throws BadLocationException
        {

            document.replace(offset, 0, OPEN_COMMENT + " ");
            document.replace(offset + length + 3, 0, CLOSE_COMMENT);

        }

        /**
         * <p>
         * This method modifies the given document to remove the given comment
         * prefix at the given comment prefix offset and the given comment
         * suffix at the given comment suffix offset. In the case of removing a
         * line comment that does not have a suffix, pass <code>null</code> for
         * the comment suffix and it and its associated offset will be ignored.
         * </p>
         *
         * <p>
         * <b>NOTE:</b> it is a good idea if a model is at hand when calling
         * this to warn the model of an impending update
         * </p>
         *
         * @param document
         *            the document to remove the comment from
         * @param commentPrefixOffset
         *            the offset of the comment prefix
         * @param commentSuffixOffset
         *            the offset of the comment suffix (ignored if
         *            <code>commentSuffix</code> is <code>null</code>)
         * @param commentPrefix
         *            the prefix of the comment to remove from its associated
         *            given offset
         * @param commentSuffix
         *            the suffix of the comment to remove from its associated
         *            given offset, or null if there is not suffix to remove for
         *            this comment
         */
        protected static void uncomment(IDocument document,
                int commentPrefixOffset, String commentPrefix,
                int commentSuffixOffset, String commentSuffix)
        {

            try {
                // determine if there is a space after the comment prefix that
                // should also be removed
                int commentPrefixLength = commentPrefix.length();
                String postCommentPrefixChar = document.get(commentPrefixOffset
                        + commentPrefix.length(), 1);
                if (postCommentPrefixChar.equals(" ")) {
                    commentPrefixLength++;
                }

                // remove the comment prefix
                document.replace(commentPrefixOffset, commentPrefixLength, ""); //$NON-NLS-1$

                if (commentSuffix != null) {
                    commentSuffixOffset -= commentPrefixLength;

                    // determine if there is a space before the comment suffix
                    // that should also be removed
                    int commentSuffixLength = commentSuffix.length();
                    String preCommentSuffixChar = document.get(
                            commentSuffixOffset - 1, 1);
                    if (preCommentSuffixChar.equals(" ")) {
                        commentSuffixLength++;
                        commentSuffixOffset--;
                    }

                    // remove the comment suffix
                    document.replace(commentSuffixOffset, commentSuffixLength,
                            ""); //$NON-NLS-1$
                }
            } catch (BadLocationException e) {
                Logger.log(Logger.WARNING_DEBUG, e.getMessage(), e);
            }
        }

        /**
         * <p>
         * Assumes that the given offset is at the beginning of a line that is
         * commented and removes the comment prefix from the beginning of the
         * line, leading whitespace on the line will not prevent this method
         * from finishing correctly
         * </p>
         *
         */
        public void remove(IStructuredDocument document, int offset,
                int length, boolean removeEnclosing)
                throws BadLocationException
        {

            String content = document.get(offset, length);

            int origOffset = offset;

            int innerOffset = content.indexOf(OPEN_COMMENT);
            if (innerOffset > 0) {
                offset += innerOffset;
            }

            // remove the opening tag
            uncomment(document, offset, OPEN_COMMENT, -1, null);

            innerOffset = content.indexOf(CLOSE_COMMENT);
            if (innerOffset > 0) {
                origOffset += innerOffset;
            }

            // take into account the removed opening comment tag
            origOffset -= 3;

            // remove the closing tag
            uncomment(document, origOffset, CLOSE_COMMENT, -1, null);

        }

        /**
         * <p>
         * When calling {@link Display#readAndDispatch()} the game is off as to
         * whose code you maybe calling into because of event
         * handling/listeners/etc. The only important thing is that the UI has
         * been given a chance to react to user clicks. Thus the logging of most
         * {@link Exception}s and {@link Error}s as caused by
         * {@link Display#readAndDispatch()} because they are not caused by this
         * code and do not effect it.
         * </p>
         *
         * @param display
         *            the {@link Display} to call <code>readAndDispatch</code>
         *            on with exception/error handling.
         */
        private void readAndDispatch(Display display)
        {
            try {
                display.readAndDispatch();
            } catch (Exception e) {
                Logger.log(
                        Logger.WARNING,
                        "Exception caused by readAndDispatch, not caused by or fatal to caller",
                        e);
            } catch (LinkageError e) {
                Logger.log(
                        Logger.WARNING,
                        "LinkageError caused by readAndDispatch, not caused by or fatal to caller",
                        e);
            } catch (VirtualMachineError e) {
                // re-throw these
                throw e;
            } catch (ThreadDeath e) {
                // re-throw these
                throw e;
            } catch (Error e) {
                // catch every error, except for a few that we don't want to
                // handle
                Logger.log(
                        Logger.WARNING,
                        "Error caused by readAndDispatch, not caused by or fatal to caller",
                        e);
            }
        }

    }

    public static boolean isCommentLine(IDocument document, int line)
    {
        boolean isComment = false;

        try {
            IRegion region = document.getLineInformation(line);
            String string = document
                    .get(region.getOffset(), region.getLength()).trim();

            // empty line
            if (string.trim().length() == 0)
                return true;

            isComment = (string.length() >= OPEN_COMMENT.length() && string
                    .startsWith(OPEN_COMMENT))
                    || (string.length() >= OPEN_COMMENT.length() && string
                            .startsWith(OPEN_COMMENT));

        } catch (BadLocationException e) {
            Logger.log(Logger.WARNING_DEBUG, e.getMessage(), e);
        }
        return isComment;
    }
}
TOP

Related Classes of com.dubture.twig.ui.actions.TwigToggleLineCommentHandler$ToggleLinesRunnable

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.