Package org.cfeclipse.cfml.editors.indentstrategies

Source Code of org.cfeclipse.cfml.editors.indentstrategies.CFEIndentStrategy

/*
* Created on Jul 1, 2004
*
* The MIT License
* Copyright (c) 2004 Oliver Tupman
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the Software
* is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package org.cfeclipse.cfml.editors.indentstrategies;

import org.cfeclipse.cfml.CFMLPlugin;
import org.cfeclipse.cfml.editors.CFMLEditor;
import org.cfeclipse.cfml.editors.ICFDocument;
import org.cfeclipse.cfml.editors.formatters.XmlDocumentFormatter;
import org.cfeclipse.cfml.editors.partitioner.CFEPartition;
import org.cfeclipse.cfml.editors.partitioner.CFEPartitioner;
import org.cfeclipse.cfml.parser.docitems.DocItem;
import org.cfeclipse.cfml.preferences.EditorPreferenceConstants;
import org.eclipse.jface.text.BadLocationException;
import org.eclipse.jface.text.DefaultIndentLineAutoEditStrategy;
import org.eclipse.jface.text.Document;
import org.eclipse.jface.text.DocumentCommand;
import org.eclipse.jface.text.IDocument;
import org.eclipse.jface.text.IRegion;
import org.eclipse.jface.text.ITextSelection;
import org.eclipse.jface.text.TextUtilities;

//import org.cfeclipse.cfml.editors.CFConfiguration;

/**
* @author Oliver Tupman
*
*/
public class CFEIndentStrategy extends DefaultIndentLineAutoEditStrategy {

  /** Auto-close double quotes */
  private boolean autoClose_DoubleQuotes = true;

  /** Auto-close single quotes */
  private boolean autoClose_SingleQuotes = true;

  /** Auto-close hashes (#) */
  private boolean autoClose_Hashes = true;

  /** Auto-insert a closing bracket */
  private boolean autoClose_Brackets = true;

  /** Auto-insert closing parenthesis */
  private boolean autoClose_Parens = true;

  /** Use smart indent */
  private boolean useSmartIndent = true;

  /** Use smart comments */
  protected boolean useSmartComments = true;

  public void setUseSmartComments(boolean useSmartComments) {
    this.useSmartComments = useSmartComments;
  }

  private boolean tabIndentSingleLine = false;
  protected String indentString = "\t";
  protected int indentSize = 4;

  protected CFMLEditor editor;
  // private CFConfiguration configuration;

  private int fAccumulatedChange = 0;

  public CFEIndentStrategy(CFMLEditor editor) {
    this.editor = editor;
  }

  protected synchronized void autoIndentAfterNewLine(IDocument d, DocumentCommand c) {

    if (c.offset == -1 || d.getLength() == 0) {
      return;
    }

    int position = (c.offset == d.getLength() ? c.offset - 1 : c.offset);
    /*
     * AntElementNode node= fModel.getProjectNode(false).getNode(position -
     * fAccumulatedChange); if (node == null) { return; }
     */

    try {
      // StringBuffer correct=
      // org.cfeclipse.cfml.editors.formatters.XmlDocumentFormatter.getLeadingWhitespace(node.getOffset(),
      // d);
      StringBuffer correct = org.cfeclipse.cfml.editors.formatters.XmlDocumentFormatter.getLeadingWhitespace(
          position, d);
      if (!nextNodeIsEndTag(c.offset, d)) {
        correct.append(XmlDocumentFormatter.createIndent());
      }
      StringBuffer buf = new StringBuffer(c.text);
      buf.append(correct);
      fAccumulatedChange += buf.length();

      int line = d.getLineOfOffset(position);
      IRegion reg = d.getLineInformation(line);
      int lineEnd = reg.getOffset() + reg.getLength();
      int contentStart = findEndOfWhiteSpace(d, c.offset, lineEnd);

      c.length = Math.max(contentStart - c.offset, 0);
      c.caretOffset = c.offset + buf.length();
      c.shiftsCaret = false;
      c.text = buf.toString();

    } catch (BadLocationException e) {
      e.printStackTrace();
      // AntUIPlugin.log(e);
    }
  }

  private boolean nextNodeIsEndTag(int offset, IDocument document) {
    if (offset + 1 > document.getLength()) {
      return false;
    }
    try {
      IRegion lineRegion = document.getLineInformationOfOffset(offset);
      offset = findEndOfWhiteSpace(document, offset, lineRegion.getOffset() + lineRegion.getLength());
      String nextChars = document.get(offset, 2).trim();
      if ("</".equals(nextChars) || "/>".equals(nextChars)) { //$NON-NLS-1$ //$NON-NLS-2$
        return true;
      }
    } catch (BadLocationException e) {
    }
    return false;
  }

  /*
   * (non-Javadoc)
   *
   * @see
   * org.eclipse.jface.text.IAutoEditStrategy#customizeDocumentCommand(org
   * .eclipse.jface.text.IDocument, org.eclipse.jface.text.DocumentCommand)
   */
  public void customizeDocumentCommand(IDocument d, DocumentCommand c) {

    if (c.length == 0 && c.text != null && isLineDelimiter(d, c.text)) {
      autoIndentAfterNewLine(d, c);
    } else if (c.text.length() > 1) {
      smartPaste(d, c);
    }
  }

  protected boolean isLineDelimiter(IDocument document, String text) {
    String[] delimiters = document.getLegalLineDelimiters();
    if (delimiters != null)
      return TextUtilities.equals(delimiters, text) > -1;
    return false;
  }

  public synchronized void reconciled() {
    fAccumulatedChange = 0;
  }

  boolean doIndent(IDocument doc, DocumentCommand docCommand) {

    boolean doIndent = true;
    if (doc instanceof ICFDocument && this.isUseSmartIndent()) {
      ICFDocument cfd = (ICFDocument) doc;
      CFEPartitioner partitioner = (CFEPartitioner) cfd.getDocumentPartitioner();
      CFEPartition prevPartition = partitioner.getPreviousPartition(docCommand.offset);
      CFEPartition nextPartition = null;
      if (prevPartition != null) {
        nextPartition = partitioner.getNextPartition(prevPartition.offset);
      }
      // System.out.println("Command at offset: " + docCommand.offset);
      // System.out.println(prevPartition);
      // System.out.println(nextPartition);
      if (nextPartition != null && prevPartition != null) {
        if (prevPartition.getType().endsWith("end_tag")
            || (prevPartition.getTagName() != null && prevPartition.getTagName().equals("cfset"))) {
          //doIndent = false;
        }
      }
    }
    return doIndent;
  }

  protected void smartPaste(IDocument document, DocumentCommand command) {
    try {
      if (command.offset == -1 || document.getLength() == 0) {
        return;
      }
      String origChange = command.text;
      int position = (command.offset == document.getLength() ? command.offset - 1 : command.offset);
      ICFDocument cfd = (ICFDocument) document;
      // CfmlTagItem node = cfd.getTagAt(command.offset, command.offset,
      // true);
      DocItem node = editor.getSelectionCursorListener().getCurrentDocItem();
      if (node == null) {
        return;
      }

      // eat any WS before the insertion to the beginning of the line
      int firstLine = 1; // don't format the first line if it has other
                // content before it
      IRegion line = document.getLineInformationOfOffset(command.offset);
      String notSelected = document.get(line.getOffset(), command.offset - line.getOffset());
      if (notSelected.trim().length() == 0) {
        command.length += notSelected.length();
        command.offset = line.getOffset();
        firstLine = 0;
      }

      // handle the indentation computation inside a temporary document
      Document temp = new Document(command.text);

      // indent the first and second line
      // compute the relative indentation difference from the second line
      // (as the first might be partially selected) and use the value to
      // indent all other lines.
      boolean isIndentDetected = false;
      StringBuffer addition = new StringBuffer();
      int insertLength = 0;
      int lines = temp.getNumberOfLines();
      for (int l = firstLine; l < lines; l++) { // we don't change the
                            // number of lines while
                            // adding indents

        IRegion r = temp.getLineInformation(l);
        int lineOffset = r.getOffset();
        int lineLength = r.getLength();

        if (lineLength == 0) { // don't modify empty lines
          continue;
        }

        if (!isIndentDetected) {

          // indent the first pasted line
            StringBuffer current = XmlDocumentFormatter.getLeadingWhitespace(lineOffset, temp);
            StringBuffer correct = XmlDocumentFormatter.getLeadingWhitespace(node.getStartPosition(), document);
            // relatively indent all pasted lines
            if (doIndent(document, command)) {
              correct.append(XmlDocumentFormatter.createIndent());
            }
            insertLength = subtractIndent(correct, current, addition);
            isIndentDetected = true;
        }

        if (insertLength > 0) {
          addIndent(temp, l, addition);
        } else if (insertLength < 0) {
          cutIndent(temp, l, -insertLength);
        }
      }

      // modify the command
      if (!origChange.equals(temp.get())) {
        fAccumulatedChange += temp.getLength();
        command.text = temp.get();
      }

    } catch (BadLocationException e) {
      e.printStackTrace();
      // AntUIPlugin.log(e);
    }
  }

  /**
   * Indents line <code>line</code> in <code>document</code> with
   * <code>indent</code>. Leaves leading comment signs alone.
   *
   * @param document
   *            the document
   * @param line
   *            the line
   * @param indent
   *            the indentation to insert
   * @throws BadLocationException
   *             on concurrent document modification
   */
  private void addIndent(Document document, int line, CharSequence indent) throws BadLocationException {
    IRegion region = document.getLineInformation(line);
    int insert = region.getOffset();

    // insert indent
    document.replace(insert, 0, indent.toString());
  }

  /**
   * Cuts the visual equivalent of <code>toDelete</code> characters out of the
   * indentation of line <code>line</code> in <code>document</code>.
   *
   * @param document
   *            the document
   * @param line
   *            the line
   * @param toDelete
   *            the number of space equivalents to delete.
   * @throws BadLocationException
   *             on concurrent document modification
   */
  protected void cutIndent(Document document, int line, int toDelete) throws BadLocationException {
    IRegion region = document.getLineInformation(line);
    int from = region.getOffset();
    int endOffset = region.getOffset() + region.getLength();

    int to = from;
    while (toDelete > 0 && to < endOffset) {
      char ch = document.getChar(to);
      if (!Character.isWhitespace(ch))
        break;
      toDelete -= computeVisualLength(ch);
      if (toDelete >= 0) {
        to++;
      } else {
        break;
      }
    }

    document.replace(from, to - from, null);
  }

  /**
   * Returns the visual length of a given character taking into account the
   * visual tabulator length.
   *
   * @param ch
   *            the character to measure
   * @return the visual length of <code>ch</code>
   */
  private int computeVisualLength(char ch) {
    if (ch == '\t') {
      return getVisualTabLengthPreference();
    }

    return 1;
  }

  /**
   * Returns the visual length of a given <code>CharSequence</code> taking
   * into account the visual tabulator length.
   *
   * @param seq
   *            the string to measure
   * @return the visual length of <code>seq</code>
   */
  private int computeVisualLength(CharSequence seq) {
    int size = 0;
    int tablen = getVisualTabLengthPreference();

    for (int i = 0; i < seq.length(); i++) {
      char ch = seq.charAt(i);
      if (ch == '\t') {
        size += tablen - size % tablen;
      } else {
        size++;
      }
    }
    return size;
  }

  /**
   * Computes the difference of two indentations and returns the difference in
   * length of current and correct. If the return value is positive,
   * <code>addition</code> is initialized with a substring of that length of
   * <code>correct</code>.
   *
   * @param correct
   *            the correct indentation
   * @param current
   *            the current indentation (might contain non-whitespace)
   * @param difference
   *            a string buffer - if the return value is positive, it will be
   *            cleared and set to the substring of <code>current</code> of
   *            that length
   * @return the difference in length of <code>correct</code> and
   *         <code>current</code>
   */
  private int subtractIndent(CharSequence correct, CharSequence current, StringBuffer difference) {
    int c1 = computeVisualLength(correct);
    int c2 = computeVisualLength(current);
    int diff = c1 - c2;
    if (diff <= 0) {
      return diff;
    }

    difference.setLength(0);
    int len = 0, i = 0;
    while (len < diff) {
      char c = correct.charAt(i++);
      difference.append(c);
      len += computeVisualLength(c);
    }

    return diff;
  }

  /**
   * The preference setting for the visual tabulator display.
   *
   * @return the number of spaces displayed for a tabulator in the editor
   */
  private int getVisualTabLengthPreference() {
    int tabWidth = CFMLPlugin.getDefault().getPreferenceStore().getInt(
        EditorPreferenceConstants.P_TAB_WIDTH);
    if (tabWidth == 0) {
      tabWidth = 4;
    }
    return tabWidth;
  }

  protected String getIndentString() {
    return this.indentString;
  }

  public void setIndentString(int tabWidth, boolean tabsAsSpaces) {
    indentSize = tabWidth;
    if (tabsAsSpaces) {
      // System.err.println("Indent string set to "+tabWidth+" spaces.");
      String s = new String();
      for (int i = 0; i < tabWidth; i++) {
        s += " ";
      }
      indentString = s;
    } else {
      // System.err.println("Indent string set to 1 tab.");
      indentString = "\t";
    }
  }

  /**
   * Steps through one character. Essentially a alias to stepThrough(command,
   * 1)
   *
   * @param docCommand
   */
  protected void stepThrough(DocumentCommand docCommand) {
    stepThrough(docCommand, 1);
  }

  /**
   * Steps through a number of characters
   *
   * @param docCommand
   *            - the doc command to work upon
   * @param chars2StepThru
   *            - number of characters to step through
   */
  protected void stepThrough(DocumentCommand docCommand, int chars2StepThru) {
    docCommand.text = "";
    docCommand.shiftsCaret = false;
    docCommand.caretOffset = docCommand.offset += chars2StepThru;
  }

  /*
   * private String getSelectedText(){ ISelection selection =
   * editor.getSelectionProvider().getSelection(); String text = null;
   * if(selection != null && selection instanceof ITextSelection){
   * ITextSelection textSelection = (ITextSelection) selection; text =
   * textSelection.getText(); } return text; }
   */

  public void setTabIndentSingleLine(boolean state) {
    tabIndentSingleLine = state;
  }

  public boolean getTabIndentSingleLine() {
    return this.tabIndentSingleLine;
  }

  protected void singleLineIndent(IDocument d, DocumentCommand c) {

    /*
     * TODO: Need to fix the stuff in comments below Plugin currently broken
     * because of missing files and broken stuff in CFParser, so can't go
     * any further :-(
     */
    ITextSelection textSelection = (ITextSelection) editor.getSelectionProvider().getSelection();
    String selectedText = textSelection.getText();
    // System.out.println("Command offset: |"+c.offset+"|");
    // System.out.println("Command caret offset: |"+c.caretOffset+"|");
    if (selectedText.length() > 0 && (getTabIndentSingleLine())) {

      try {
        // Find the start of the line
        int lineOffset = d.getLineInformationOfOffset(c.offset).getOffset();
        // Get the string that we are about to overwrite with the
        // indentstring
        String lineStartString = d.get(lineOffset, indentString.length());
        // Replace the start of the line with the indent string and what
        // ever was there before
        d.replace(lineOffset, indentString.length(), indentString + lineStartString);
        // Update the offset of the command to reflect the changed line
        c.offset += indentString.length();
        // Set the command text to what is currently selected so we
        // don't change the document
        c.text = selectedText;
      } catch (BadLocationException e) {
        // do nothing
        System.err.println("BadLocationException caught in CFEIndentStrategy::singleLineIndent method");
      }
    } else {
      c.text = indentString;
    }

  }

  /**
   * @return Returns the autoClose_DoubleQuotes.
   */
  public boolean isAutoClose_DoubleQuotes() {
    return autoClose_DoubleQuotes;
  }

  /**
   * @param autoClose_DoubleQuotes
   *            The autoClose_DoubleQuotes to set.
   */
  public void setAutoClose_DoubleQuotes(boolean autoClose_DoubleQuotes) {
    this.autoClose_DoubleQuotes = autoClose_DoubleQuotes;
  }

  /**
   * @return Returns the autoClose_Hashes.
   */
  public boolean isAutoClose_Hashes() {
    return autoClose_Hashes;
  }

  /**
   * @param autoClose_Hashes
   *            The autoClose_Hashes to set.
   */
  public void setAutoClose_Hashes(boolean autoClose_Hashes) {
    this.autoClose_Hashes = autoClose_Hashes;
  }

  /**
   * @return Returns the autoClose_SingleQuotes.
   */
  public boolean isAutoClose_SingleQuotes() {
    return autoClose_SingleQuotes;
  }

  /**
   * @param autoClose_SingleQuotes
   *            The autoClose_SingleQuotes to set.
   */
  public void setAutoClose_SingleQuotes(boolean autoClose_SingleQuotes) {
    this.autoClose_SingleQuotes = autoClose_SingleQuotes;
  }

  /**
   * @return the autoClose_Brackets
   */
  public boolean isAutoClose_Brackets() {
    return autoClose_Brackets;
  }

  /**
   * @param autoClose_Brackets
   *            the autoClose_Brackets to set
   */
  public void setAutoClose_Brackets(boolean autoClose_Brackets) {
    this.autoClose_Brackets = autoClose_Brackets;
  }

  /**
   * @return the autoClose_Parens
   */
  public boolean isAutoClose_Parens() {
    return autoClose_Parens;
  }

  /**
   * @param autoClose_Parens
   *            the autoClose_Parens to set
   */
  public void setAutoClose_Parens(boolean autoClose_Parens) {
    this.autoClose_Parens = autoClose_Parens;
  }

  /**
   * @return the useSmartIndent
   */
  public boolean isUseSmartIndent() {
    return useSmartIndent;
  }

  /**
   * @return the useSmartComments
   */
  public boolean isUseSmartComments() {
    return useSmartComments;
  }

  /**
   * @param useSmartIndent
   *            The useSmartIndent value.
   */
  public void setUseSmartIndent(boolean useSmartIndent) {
    this.useSmartIndent = useSmartIndent;
  }

}
TOP

Related Classes of org.cfeclipse.cfml.editors.indentstrategies.CFEIndentStrategy

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.