Package com.salesforce.ide.ui.editors.apex.assistance

Source Code of com.salesforce.ide.ui.editors.apex.assistance.ApexAutoIndentStrategy

/*******************************************************************************
* Copyright (c) 2014 Salesforce.com, inc..
* 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:
*     Salesforce.com, inc. - initial API and implementation
******************************************************************************/
package com.salesforce.ide.ui.editors.apex.assistance;

import org.apache.log4j.Logger;
import org.eclipse.core.runtime.Platform;
import org.eclipse.core.runtime.preferences.IPreferencesService;
import org.eclipse.jface.preference.IPreferenceStore;
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.TextUtilities;
import org.eclipse.ui.editors.text.EditorsUI;
import org.eclipse.ui.texteditor.AbstractDecoratedTextEditorPreferenceConstants;

import com.salesforce.ide.core.internal.utils.Utils;
import com.salesforce.ide.ui.editors.ForceIdeEditorsPlugin;
import com.salesforce.ide.ui.editors.apex.preferences.PreferenceConstants;
import com.salesforce.ide.ui.editors.internal.utils.EditorMessages;

/**
* Auto indent line strategy sensitive to brackets.
*/
public class ApexAutoIndentStrategy extends DefaultIndentLineAutoEditStrategy {

    private static final Logger logger = Logger.getLogger(ApexAutoIndentStrategy.class);
   
    private String indent;

    public ApexAutoIndentStrategy() {
       
        indent = indentStringFromEditorsUIPreferences();
    }
   
    /**
     * Returns the String to use for indenting based on the General/Editors/Text Editors preferences
     * that are respected by the underlying platform editing code.
     *
     * @return the String to use for indenting
     */
    private String indentStringFromEditorsUIPreferences() {
       
        IPreferencesService ps = Platform.getPreferencesService();
        boolean spacesForTabs = ps.getBoolean(
                EditorsUI.PLUGIN_ID,
                AbstractDecoratedTextEditorPreferenceConstants.EDITOR_SPACES_FOR_TABS,
                false,
                null
                );
        if (spacesForTabs) {
            int tabWidth = ps.getInt(
                    EditorsUI.PLUGIN_ID,
                    AbstractDecoratedTextEditorPreferenceConstants.EDITOR_TAB_WIDTH,
                    4,
                    null
                    );
            StringBuilder sb = new StringBuilder(tabWidth);
            for (int i = 0; i < tabWidth; i++) {
                sb.append(" ");
            }
            return sb.toString();
        } else {
            return "\t";
        }
    }

    /*
     * (non-Javadoc) Method declared on IAutoIndentStrategy
     */
    @Override
    public void customizeDocumentCommand(IDocument d, DocumentCommand c) {
        if ((c.length == 0) && (c.text != null) && endsWithDelimiter(d, c.text)) {
            smartIndentAfterNewLine(d, c);
        } else if ("}".equals(c.text)) { //$NON-NLS-1$
            smartInsertAfterBracket(d, c);
        }
    }

    /**
     * Returns whether or not the given text ends with one of the documents legal line delimiters.
     *
     * @param d
     *            the document
     * @param txt
     *            the text
     * @return <code>true</code> if <code>txt</code> ends with one of the document's line delimiters,
     *         <code>false</code> otherwise
     */
    private boolean endsWithDelimiter(IDocument d, String txt) {
        String[] delimiters = d.getLegalLineDelimiters();
        if (delimiters != null) {
            return TextUtilities.endsWith(delimiters, txt) > -1;
        }
        return false;
    }

    /**
     * Returns the line number of the next bracket after end.
     *
     * @param document -
     *            the document being parsed
     * @param line -
     *            the line to start searching back from
     * @param end -
     *            the end position to search back from
     * @param closingBracketIncrease -
     *            the number of brackets to skip
     * @return the line number of the next matching bracket after end
     * @throws BadLocationException
     *             in case the line numbers are invalid in the document
     */
    protected int findMatchingOpenBracket(IDocument document, int line, int end, int closingBracketIncrease)
            throws BadLocationException {

        int start = document.getLineOffset(line);
        int brackcount = getBracketCount(document, start, end, false) - closingBracketIncrease;

        // sum up the brackets counts of each line (closing brackets count
        // negative,
        // opening positive) until we find a line the brings the count to zero
        while (brackcount < 0) {
            line--;
            if (line < 0) {
                return -1;
            }
            start = document.getLineOffset(line);
            end = start + document.getLineLength(line) - 1;
            brackcount += getBracketCount(document, start, end, false);
        }
        return line;
    }

    /**
     * Returns the bracket value of a section of text. Closing brackets have a value of -1 and open brackets have a
     * value of 1.
     *
     * The braces in the commented region (// or /* .. are ignored ( not counted )
     *
     * @param document -
     *            the document being parsed
     * @param start -
     *            the start position for the search
     * @param end -
     *            the end position for the search
     * @param ignoreCloseBrackets -
     *            whether or not to ignore closing brackets in the count
     * @return the bracket value of a section of text
     * @throws BadLocationException
     *             in case the positions are invalid in the document
     */
    private int getBracketCount(IDocument document, int start, int end, boolean ignoreCloseBrackets)
            throws BadLocationException {

        int begin = start;
        int bracketcount = 0;
        while (begin < end) {
            char curr = document.getChar(begin);
            begin++;
            switch (curr) {
            case '/':
                if (begin < end) {
                    char next = document.getChar(begin);
                    if (next == '*') {
                        // a comment starts, advance to the comment end
                        begin = getCommentEnd(document, begin + 1, end);
                    } else if (next == '/') {
                        // '//'-comment: nothing to do anymore on this line
                        //  change the begin index to the start of the next line if it exists
                        //  else change the begin index to end.
                        int lineNumber = document.getLineOfOffset(begin);
                        if(lineNumber + 1 >= document.getNumberOfLines()){
                            begin = end;
                        }else{
                            begin = document.getLineOffset(lineNumber+1);
                        }
                       
                    }
                }
                break;
            case '*':
                if (begin < end) {
                    char next = document.getChar(begin);
                    if (next == '/') {
                        // we have been in a comment: forget what we read before
                        bracketcount = 0;
                        begin++;
                    }
                }
                break;
            case '{':
                bracketcount++;
                ignoreCloseBrackets = false;
                break;
            case '}':
                if (!ignoreCloseBrackets) {
                    bracketcount--;
                }
                break;
            case '"':
            case '\'':
                begin = getStringEnd(document, begin, end, curr);
                break;
            default:
            }
        }
        return bracketcount;
    }

    /**
     * Returns the end position of a comment starting at the given <code>position</code>.
     *
     * @param document -
     *            the document being parsed
     * @param position -
     *            the start position for the search
     * @param end -
     *            the end position for the search
     * @return the end position of a comment starting at the given <code>position</code>
     * @throws BadLocationException
     *             in case <code>position</code> and <code>end</code> are invalid in the document
     */
    private int getCommentEnd(IDocument document, int position, int end) throws BadLocationException {
        int currentPosition = position;
        while (currentPosition < end) {
            char curr = document.getChar(currentPosition);
            currentPosition++;
            if (curr == '*') {
                if ((currentPosition < end) && (document.getChar(currentPosition) == '/')) {
                    return currentPosition + 1;
                }
            }
        }
        return end;
    }

    /**
     * Returns the content of the given line without the leading whitespace.
     *
     * @param document -
     *            the document being parsed
     * @param line -
     *            the line being searched
     * @return the content of the given line without the leading whitespace
     * @throws BadLocationException
     *             in case <code>line</code> is invalid in the document
     */
    protected String getIndentOfLine(IDocument document, int line) throws BadLocationException {
        if (line > -1) {
            int start = document.getLineOffset(line);
            int end = start + document.getLineLength(line) - 1;
            int whiteend = findEndOfWhiteSpace(document, start, end);
            return document.get(start, whiteend - start);
        }
        return ""; //$NON-NLS-1$
    }

    /**
     * Returns the position of the <code>character</code> in the <code>document</code> after <code>position</code>.
     *
     * @param document -
     *            the document being parsed
     * @param position -
     *            the position to start searching from
     * @param end -
     *            the end of the document
     * @param character -
     *            the character you are trying to match
     * @return the next location of <code>character</code>
     * @throws BadLocationException
     *             in case <code>position</code> is invalid in the document
     */
    private int getStringEnd(IDocument document, int position, int end, char character) throws BadLocationException {
        int currentPosition = position;
        while (currentPosition < end) {
            char currentCharacter = document.getChar(currentPosition);
            currentPosition++;
            if (currentCharacter == '\\') {
                // ignore escaped characters
                currentPosition++;
            } else if (currentCharacter == character) {
                return currentPosition;
            }
        }
        return end;
    }

    /**
     * Set the indent of a new line based on the command provided in the supplied document.
     *
     * @param document -
     *            the document being parsed
     * @param command -
     *            the command being performed
     */
    protected void smartIndentAfterNewLine(IDocument document, DocumentCommand command) {

        int docLength = document.getLength();
        if ((command.offset == -1) || (docLength == 0)) {
            return;
        }

        try {
            int p = (command.offset == docLength ? command.offset - 1 : command.offset);

            int line = document.getLineOfOffset(p);
            StringBuffer buf = new StringBuffer(command.text);

            int start = document.getLineOffset(line);
            IPreferenceStore preferenceStore = getPreferenceStore();
            boolean closeBraces = preferenceStore.getBoolean(PreferenceConstants.EDITOR_CLOSE_BRACES);

            int lastChar = findLastNonWhiteSpace(document, start, command.offset);
            int whiteend = findEndOfWhiteSpace(document, start, command.offset);

            // insert closing brace on new line after an unclosed opening brace
            if (getBracketCount(document, 0, docLength, false) > 0 && closeBraces
                    && (document.getChar(lastChar) == '{')) {
                buf.append(document.get(start, whiteend - start));
                buf.append(indent);
                command.caretOffset = command.offset + buf.length();
                command.shiftsCaret = false;
                buf.append('\n');
                buf.append(document.get(start, whiteend - start));
                buf.append('}');
            } else if ((command.offset < docLength) && (document.getChar(command.offset) == '}')) {
                int indLine = findMatchingOpenBracket(document, line, command.offset, 0);
                if (indLine == -1) {
                    indLine = line;
                }
                buf.append(getIndentOfLine(document, indLine));
            } else {
                buf.append(document.get(start, whiteend - start));
                if (getBracketCount(document, start, command.offset, true) > 0) {
                    buf.append(indent);
                }
            }
            command.text = buf.toString();

        } catch (BadLocationException excp) {
            logger.warn(EditorMessages.getString("ApexEditor.AutoIndent.error.bad_location_1"));
        }
       
    }

    /**
     * Set the indent of a bracket based on the command provided in the supplied document.
     *
     * @param document -
     *            the document being parsed
     * @param command -
     *            the command being performed
     */
    protected void smartInsertAfterBracket(IDocument document, DocumentCommand command) {
        if ((command.offset == -1) || (document.getLength() == 0)) {
            return;
        }

        try {
            int p = (command.offset == document.getLength() ? command.offset - 1 : command.offset);
            int line = document.getLineOfOffset(p);
            int start = document.getLineOffset(line);
            int whiteend = findEndOfWhiteSpace(document, start, command.offset);

            // shift only when line does not contain any text up to the closing
            // bracket
            if (whiteend == command.offset) {
                // evaluate the line with the opening bracket that matches out
                // closing bracket
                int indLine = findMatchingOpenBracket(document, line, command.offset, 1);
                if ((indLine != -1) && (indLine != line)) {
                    // take the indent of the found line
                    StringBuffer replaceText = new StringBuffer(getIndentOfLine(document, indLine));
                    // add the rest of the current line including the just added
                    // close bracket
                    replaceText.append(document.get(whiteend, command.offset - whiteend));
                    replaceText.append(command.text);
                    // modify document command
                    command.length = command.offset - start;
                    command.offset = start;
                    command.text = replaceText.toString();
                }
            }
        } catch (BadLocationException excp) {
            logger.warn(EditorMessages.getString("ApexEditor.AutoIndent.error.bad_location_2")); //$NON-NLS-1$
        }
    }

    private static IPreferenceStore getPreferenceStore() {
        return ForceIdeEditorsPlugin.getDefault().getPreferenceStore();
    }

    protected int findLastNonWhiteSpace(IDocument document, int offset, int end) throws BadLocationException {
        try {
            IRegion region = document.getLineInformationOfOffset(offset);

            String lineText = document.get(offset, region.getOffset() + region.getLength() - offset);
            String[] tokens = lineText.split("\\s+"); //$NON-NLS-1$

            if (!Utils.isEmpty(tokens)) {
                for(int i = tokens.length - 1; i > -1; i--)
                {
                    String token = tokens[i];
                    if (!Utils.isEmpty(token)) {
                        offset += lineText.lastIndexOf(token) + token.length() - 1 ;
                        break;
                    }
                }
            }
        } catch (BadLocationException e) {
            logger.error("Unable to compute offset", e); //$NON-NLS-1$
        }

        return offset;
    }
}
TOP

Related Classes of com.salesforce.ide.ui.editors.apex.assistance.ApexAutoIndentStrategy

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.