Package org.cfeclipse.cfml.editors.contentassist

Source Code of org.cfeclipse.cfml.editors.contentassist.CFMLFunctionCompletionProcessor

/*
* Created on Feb 27, 2004
*
* The MIT License
* Copyright (c) 2004 Oliver B. Tupman <otupman@dts-workshop.com>
*
* 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.contentassist;

import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
import java.util.StringTokenizer;
import java.util.TreeSet;

import org.cfeclipse.cfml.dictionary.DictionaryManager;
import org.cfeclipse.cfml.dictionary.Function;
import org.cfeclipse.cfml.dictionary.SyntaxDictionary;
import org.cfeclipse.cfml.editors.ICFDocument;
import org.cfeclipse.cfml.editors.partitioner.scanners.CFPartitionScanner;
import org.cfeclipse.cfml.parser.CFDocument;
import org.cfeclipse.cfml.preferences.AutoIndentPreferenceConstants;
import org.cfeclipse.cfml.util.CFPluginImages;
import org.eclipse.jface.text.BadLocationException;
import org.eclipse.jface.text.IDocument;
import org.eclipse.jface.text.ITextViewer;
import org.eclipse.jface.text.contentassist.CompletionProposal;
import org.eclipse.jface.text.contentassist.ICompletionProposal;
import org.eclipse.jface.text.contentassist.IContextInformation;
import org.eclipse.jface.text.contentassist.IContextInformationValidator;
import org.eclipse.swt.graphics.Image;
import org.eclipse.text.edits.DeleteEdit;
import org.eclipse.text.edits.InsertEdit;
import org.eclipse.text.edits.UndoEdit;

/**
* @author OLIVER
*
* This handles CFScript code insight / completion for cfscript blocks. It
* provides a number of facilities:
*   1) Code insight
*   2) Automatic bracket insertion (and subsequent matching of any typed close bracket)
*   3) Automatic parentheses insertion (ditto)
*
* Externally the CFMLFunctionCompletionProcessor defines a number of difference charcters
* that may trigger code insight. Internally we actually use the characters defined
* in the member variable 'completionChars'. For these charcters we actually will provide
* code insight.
*
* The other characters, found in 'matchChars', are characters that we perform other non-
* code insight functions for. For example, when encountering a '{' the class will insert
* a matching '}' for the user. If the user enters a ')' or '}' the class will check to
* see whether it can find a matching opening character. If it does so, then it will
* not allow the editor to insert the closing character and simply 'skip' to the next, just
* past the existing closing character (that was probably inserted by the class earlier on).
*
* TODO: If I get this to work, stick in quote matching as well, oh, and hash (pound sign for you USA people) matching
* @r2 - if this gets tasty we may want to use it everywhere a function can be
*/
public class CFMLFunctionCompletionProcessor extends AssistContributor implements IAssistContributor {
  private static final short TAGTYPE = 0;
  //private static final short ATTRTYPE = 1;
  //private String className = "CFMLFunctionCompletionProcessor";
 
  private String DictionaryToUse = DictionaryManager.CFDIC;
 
  // Define the characters that make up the activation set.
  // There are three parts to the activation set:
 
  // 1) The standard completion chars. These are some activation characters
  //that non-opener/closer characters
  protected static final String completionChars = ".(;~\"#[\',abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";

  // 2) The opener/closer characters. This assists with the opening & closing
  //of things such as brackets
  protected static final String closerChars = "})#]\'";
  protected static final String openerChars = "{(#[\'";

  // This is a simple map between the character that closes a pair to the
  //character that does the opening.
  protected static final String close2openMatchChars = "\'\')(}{##\"\"][";
 
  /**
   * What characters cause us to wake up (for tags and attributes)
   *
   * This is based upon the two strings that define the characters for which
   * we provide code insight and the characters that we simply wish to do
   * a match-check.
   */
  public char[] getCompletionProposalAutoActivationCharacters() {
    return (completionChars + closerChars + openerChars).toCharArray();
  }

  /**
   * What characters cause us to wake up (for functions)
   */
  public char[] getContextInformationAutoActivationCharacters() {
    return new char[] { '+' };
  }
  /**
   * change the dictionary this uses to the pass dictionary name (use
   * DictionaryManager to get the name. This is here so we can re-use this
   * for the JS partition. (we should really make only one completion class
   * somehow)
   *
   * @param to the new dictionary to use
   *
   * @author Rob
   */
  public void changeDictionary(String to)
  {
    DictionaryToUse = to;
  }
 
  /**
   * Startup the completer
   */
  public CFMLFunctionCompletionProcessor(){
  }

  /*
   * @author Oliver
   *
   * SpacerCharacter
   *
   * Returns whether the character passed is a 'spacer character' (see comment for
   * FindItemStart()). Currently just tests to see whether it's a letter or not
   * and returns the result.
   */
  protected boolean SpacerCharacter(char inChar)
  {
    return !Character.isLetter(inChar);
  }
 
  /*
   * @author Oliver
   *
   * FindItemStart
   *
   * Searches backwards through inString looking for a 'spacer character'. A spacer character
   * is something that, erm, spaces elements. For example:
   *
   * ArrayAppend(myArray, ArrayNew(1
   *
   * If the cursor is at the end of the line above, the spacer is between "myArray," and
   * "ArrayNew(1". I.e. it's the space. Then again, if the line read "myArray,ArrayNew(1"
   * then the spacer character is the comma. The test for a spacer character is performed
   * by SpacerCharacter().
   */
  protected int FindItemStart(String inString, int startPos)
  {
    int strPos = startPos;
    for(; strPos > 0; strPos--)
    {
      char currChar = inString.charAt(strPos);
     
      if(currChar == ';')
        return -1;
     
      if(SpacerCharacter(currChar))
        break;
    }
   
    return strPos;
  }
 
  protected int FindItemStartIgnoreCommas(String inString, int startPos)
  {
    int strPos = startPos;
    for(; strPos > 0; strPos--)
    {
      char currChar = inString.charAt(strPos);

      if(SpacerCharacter(currChar) && currChar != ',')
        break;
    }
   
    return strPos;
  }
 
 
  protected int BracketScan(String input, int startPos)
  {
    return BalanceScan(input, '(', ')', startPos, true);
  }
 
  /*
   * @author Oliver
   *
   * BalanceScan
   *
   * Balance scan travels through a string attempting to find balance between
   * the opener and closer characters. Balance is 0.

   *         // Nope, search until we find a completion char.
        // Brackets are a special case. If we're in a nested function then
        // simply searching fo a completion char would be fine. But if we're
        // in a function that has nested functions, we need to by-pass them
        // and find the original, current function.
        // In theory we use a bracket counter to do this.

   *    *
   */
  protected int BalanceScan(String input, char opener, char closer,
      int startPos, boolean backwards)
  {
    return BalanceScan(input, opener, closer, startPos, backwards, 0);
  }
 

 
 
  protected int BalanceScan(String input, char opener, char closer,
                int startPos, boolean backwards, int balancePoint)
  {
    int searchPos = startPos;
    int balanceCount = 1;
    int increment = (backwards) ? -1 : 1;
   
    for(; searchPos > 0; searchPos+= increment)
    {
      char searchChar = input.charAt(searchPos);
     
      if(completionChars.indexOf(searchChar) != -1
         || searchChar == closer)
      {
        if(searchChar == opener)
          balanceCount--;
        else if(searchChar == closer)
          balanceCount++;
        else
          break;
        // If we reach 0, then we've no more brackets
        // and we're at the right place.
        if(balanceCount == balancePoint)
          break;
      }
    } 
   
   
    return searchPos;
  }
 
  /*
   * @author Oliver
   *
   * DeleteText
   */
  protected UndoEdit  DeleteText(IDocument doc2Alter, int offset, int length)
  {
    DeleteEdit charRemoval = new DeleteEdit(offset, length);
    UndoEdit removal = null;
    try {
      removal = charRemoval.apply(doc2Alter);
    } catch (BadLocationException e) {
      // Doing nowt right now. The Java editor doesn't, so should we?
    }
    return removal;
  }
 
  protected UndoEdit  InsertText(IDocument doc2Alter, int offset, String newText)
  {
    UndoEdit insertion = null;
    InsertEdit newEdit = new InsertEdit(offset, newText);
    try {
      newEdit.apply(doc2Alter);
    } catch(BadLocationException e) { // And context for the worst variable names goes to me!
      // Still doing nowt...
    }
    return insertion;
  }
 
 
  private void DebugFunction(ITextViewer viewer, int documentOffset)
  {
    //String mName = "DebugFunction";
    IDocument doc = viewer.getDocument();
    try {
      int start = doc.getPartition(documentOffset).getOffset(); // Not sure what this does!
     
      String prefix =  doc.get(start, documentOffset - start);
      StringTokenizer  tokeniser = new StringTokenizer(prefix, ";");
     
      prefix = prefix.replace('\n',' ')// Eliminate any non-character characters
      prefix = prefix.replace('\r',' ')// as this allows us to treat the buffer as
      prefix = prefix.replace('\t',' ')// one long string
     
     
      //Debug.println(mName, this, "Partition start: " + start);
      //Debug.println(mName, this, "Prefix is: '" + prefix + "'");
      int tokenCount = 0;
      String output = "";
      while(tokeniser.hasMoreTokens())
      {
        output+= "[" + tokenCount + "] Got this token: '" + tokeniser.nextToken() + "'\n";
        tokenCount++;
      }
      //Debug.println(mName, this, "Tokeniser output:\n" + output);
    } catch(BadLocationException e) {
      //Debug.println(mName, this, "Caught a BadLocationException");
    } catch(Exception anyE)  {
      //Debug.println(mName, this, "Caught a random exception. Message is: " + anyE.getMessage());
     
    }
   
  }
 
  /**
   * <b>HandleCloser</b> - Helper function for computeCompletionProposals()
   *
   * The calling function must establish that a character is to be closed
   * (generally a ')'). For example:
   * <pre>
   * ArrayAppend(myArray, ArrayLen())
   *                               ^
   * </pre>
   *
   * Now the CFScript completion processor automatically adds the closing bracket when
   * the user opens one. Therefore to save the keypress count of the user the completer
   * attempts to match the closing brackets typed by the user with the close brackets
   * already existing.  
   *
   * @author Oliver
   * @param document The document to work upon
   * @param scanData  The post-processing, pre-completion text of the document
   * @param currentChar The character that is the closer
   * @param matchPos The position that the match has been found at
   * @param triggerPos The completion trigger position
   * @param documentOffset Offset within the document (surely the same as the triggerPos?)
   * @return A instance of an UndoEdit if the deletion took place, otherwise null.
   */
  protected UndoEdit HandleCloser(IDocument document, String scanData, char currentChar,
                  int matchPos, int triggerPos, int documentOffset)
  {
    // TODO: Get closing character scan working on 0 starting partitions (i.e. # blocks)
    char closer = currentChar;
    char opener = openerChars.charAt(matchPos);
    if(documentOffset + 1 >= document.getLength())  // Don't want to go out of bounds
    {
      return null;
    }
    try {
      // First make sure that the next char is a closing char.
      // TODO: Do some funky forward closer matching so it doesn't have to be the next char
      char nextChar = document.getChar(documentOffset);
      if(nextChar == closer)
      {
        if(BalanceScan(scanData, opener, closer, triggerPos-1, true) > 0)
        {
          // TODO: Test to see whether this will fail at the end of the document partition
          // Should this really live here, and is there a better way of doing it?
          if(documentOffset + 1 < document.getLength())
          {
            return DeleteText(document, documentOffset, 1);
          }
        }
        //else
          //Debug.println("Not a closing bracket. Is this not one?: '" + currentChar + "'");
      }
    } catch(BadLocationException excep) {
      //Debug.println("HandleCloser", this, "Caught BadLocationException when getting a char");
    }
    return null;
  }
 
  protected int findAndCountTabs(IDocument document, int startPos)
  {
    int tabCount = 0;
    int strPos = startPos;
    try {
      for(; strPos > 0 && document.getChar(strPos) != '\t'; strPos--);
      {;}
      for(; strPos > 0 && document.getChar(strPos) == '\t'; strPos--)
      { tabCount++; }
    } catch(BadLocationException excep) {
      //Debug.println("findAndCountTabs", this, "Caught a BadLocationException whilst counting tabs.");
    }
    return tabCount;
  }
 
  /**
   * <b>HandleOpener</b> - Performs the document operations when the user triggers an 'opener' character.
   *
   * HandleOpener() takes the opener character and in general it inserts a matching closing item. Therefore
   * in the case of '(' it sticks in a ')'.
   *
   * Life is slightly more complicated for '{' because these follow a tab strategy. Ours is to simply
   * count the tabs of the line where the opener was entered and then  insert a carriage return and
   * then the tabs and then the closer. Life is made even more complicated by the fact that really the
   * routine should not enter a closer if one exists at the current tab level with &lt;x&gt; lines...
   * at least it shouldn't. Not implemented yet.
   *
   * Note that characters such as '#' and '"' are not handled because there is no way of distinguishing
   * between the user entering a closing character or performing the necessary double character ('##')
   * for CF to insert that character in a string.
   *
   * Doh, there's a solution sat somewhere in my brain but it's not popping out :( Ah well, someone brainier
   * can sort it!
   *
   * @param document - The document that the user is editing.
   * @param currentChar - The character that we are trying to opener (probably at documentOffset)
   * @param documentOffset - Offset at the document that the trigger was caused.
   * @return UndoEdit - The undo actions to perform to undo the operation.
   * @author Oliver
   */
   
  protected UndoEdit handleOpener(IDocument document, char currentChar, int documentOffset)
  {
    String extraData = "";
    //String mName = "HandlerOpener";
    switch(currentChar)
    {
      case '{':
        int tabCount = findAndCountTabs(document, documentOffset);
        extraData += "\n";
        for(int tabCnt = 0; tabCnt < tabCount; tabCnt++)
          extraData += "\t";
        /*
         * Doesn't work very nicely. Not in at the moment.
        int maxCheckLength = 100;  // Closer in 100 chars?
        int tempPos = documentOffset;
        for(; tempPos < document.getLength() && tempPos - documentOffset < maxCheckLength; tempPos++)
        {
          try {
            if(document.getChar(tempPos) == '}')
            {
              //
              // Check to see whether the tab count for the matching } is the same as the line it
              // was entered on.
             

              return null;  // Doing nothing more, so cop out of the function completely.
            }
          } catch(BadLocationException excep) {
            Debug.println(mName, this, "Caught a bad location exception during { opener.");
            return null;
          }
        }
        */

        extraData+= "}";
        break;
      case '#':
        extraData = "#";
        break;
      case '\"':
        extraData = "\"";
        break;
      case '\'':
        extraData = "\'";
        break;
      case '(':
        extraData = ")";
        break;
      case '[':
        extraData = "]";
        break;
      default:
        break;
    }
    return InsertText(document, documentOffset, extraData);   
  }
 
  /**
   * Finds the start of a function from inside a function, i.e.:
   * ArrayAppend(ArrayNew(),
   *
   * @param input - the input to search through
   * @param startPos - erm, where to start
   * @return The position of the start of the function
   */
  protected int FindFuncStartFromInside(String input, int startPos)
  {
    int searchPos = startPos;
    int balanceCount = 1;
    char opener = '(';
    char closer = ')';
   
    for(; searchPos > 0 && balanceCount > 0; searchPos--)
    {
      char searchChar = input.charAt(searchPos);
     
      if(searchChar == opener)
        balanceCount--;
      else if(searchChar == closer)
        balanceCount++;
   
   
   
    return searchPos;
  }

  /**
   * gets the function prefix
   *
   * @param viewer
   *            the viewer
   * @param offset
   *            the offset left of which the prefix is detected
   * @return the detected prefix
   */
  protected String extractPrefix(ITextViewer viewer, int offset) {
    IDocument document = viewer.getDocument();
    int i = offset;
    if (i > document.getLength())
      return ""; //$NON-NLS-1$

    try {
      while (i > 0) {
        char ch = document.getChar(i - 1);
        if (ch != ';' && !Character.isJavaIdentifierPart(ch))
          break;
        i--;
      }
      return document.get(i, offset - i);
    } catch (BadLocationException e) {
      return ""; //$NON-NLS-1$
    }
  }
 

 
  /*
   * Welcome to the horror that is the computeCompletionProposals() method.
   *
   * @author Oliver
   * @see org.eclipse.jface.text.contentassist.IContentAssistProcessor#computeCompletionProposals(org.eclipse.jface.text.ITextViewer, int)
   *
   *
   */
  public ICompletionProposal[] computeCompletionProposals(ITextViewer viewer,
    int documentOffset) {
    //String mName = "computeCompletionProposals";
    HashSet proposals = new HashSet();
    //String messages = "";
    //int length = 40;
    // TODO: Use the tokeniser!
   
    if(AssistUtils.isCorrectPartitionType(viewer, documentOffset, CFPartitionScanner.J_SCRIPT)) {     
      changeDictionary(DictionaryManager.JSDIC);     
    } else if (AssistUtils.isCorrectPartitionType(viewer, documentOffset, CFPartitionScanner.CF_SCRIPT)) {
      changeDictionary(DictionaryManager.CFDIC);     
    }

 
    try {
      String invoker = viewer.getDocument().get(documentOffset-1,1);
      ICFDocument document = (ICFDocument) viewer.getDocument();
      CFDocument doc = document.getCFDocument();
      int start = document.getPartition(documentOffset).getOffset();
      String scanData =  document.get(start, documentOffset - start);
      scanData = scanData.replace('\n',' ')// Eliminate any non-character characters
      scanData = scanData.replace('\r',' ')// as this allows us to treat the buffer as
      scanData = scanData.replace('\t',' ')// one long string
      if (invoker.equals(" ")) {
              return null;
          }

      if (!",".equals(invoker)
&& !"(".equals(invoker) && !"<".equals(invoker) && !".".equals(invoker)) {
        String toBeMatched = extractPrefix(viewer,documentOffset).replaceAll("[\r\n\t]", " ").trim();
        Set allPoss = SyntaxDictionary.limitSet(doc.getFunctions(), toBeMatched);
        Set poss = SyntaxDictionary.limitSet(
            DictionaryManager.getDictionary(DictionaryToUse).getAllFunctions(),
            toBeMatched
          );
        Iterator it = poss.iterator();
        while(it.hasNext())
        {
          allPoss.add(it.next());
        }
       
        ICompletionProposal[] funks = makeSetToProposal(allPoss, documentOffset, TAGTYPE, toBeMatched.length());
        return funks;
      }
      //String originalData = scanData;  // This should never be changed.

     
      char lastChar = (scanData.length() > 0) ? scanData.charAt(scanData.length() - 1) : scanData.charAt(0);
           
      // Gonna allow debug by entering the tilde character. Entering this will cause
      // my debug function to run...
      if(lastChar == '~')
      {
        DebugFunction(viewer, documentOffset);
      }

      int triggerPos = scanData.length()-1;
      //int closerCharMatch = closerChars.indexOf(lastChar);

      // First test to see whether the last two characters are "it"...  if so then we'll
      // quit out of this
      if(lastChar == '\'' && document.getChar(documentOffset-1) == 't' &&
         document.getChar(documentOffset-2) == 'i')
      {
        //System.out.println("hit IT");
        return null;
      }
     
      if(completionChars.indexOf(lastChar) == -1 && lastChar != '{')
      {
        //System.out.println("hit this other part");
       
        int searchPos = triggerPos;
        //int bracketCount = 1;
        searchPos = BracketScan(scanData, triggerPos);
        //
        // Cut off the part that is nothing to do with the function
        // search. Later on we'll use something else to cut from so
        // we can work out which parameter we're in.
        scanData = scanData.substring(0, searchPos+1);
        //System.out.println(originalData);
        lastChar = scanData.charAt(scanData.length()-1);
        triggerPos = searchPos;
      }
      else
      {
        //System.out.println("hit opener");
        //handleOpener(document, lastChar, documentOffset);
       
        if(lastChar != '(' && lastChar != ',')
          return null;
      }

      switch(lastChar)
      {
        case '.':
          // Period for a struct or CFC
          // TODO: Implement variable insight (just hard code if for the moment).
          //messages = " - I this it\'s a period.";
          break;
        case ',':
         
          //
          // First check: is there an open bracket after a semicolon, if so
          // then we're in business.
          int prevBracket = scanData.lastIndexOf('(');
          int prevSemicolon = scanData.lastIndexOf(';');
          if(prevBracket < prevSemicolon)
            return null;

          //
          // Call FindFuncStartFromInside() to work out where the start of the function is.
          triggerPos = FindFuncStartFromInside(scanData, triggerPos) + 1;
          //System.out.println("CFMLFunctionCompletionProcessor - substring result: "+ triggerPos + "scanData: \'" + scanData.substring(0, triggerPos) + "\'\n");
        case '(':
          //messages = " - In a function... maybe";
          //Debug.println(mName, this, messages);
          int strPos = FindItemStart(scanData, triggerPos - 1);
         
          if(strPos == 0)
          {
            //messages = "Reached start of string.";
          }
          else
          {
            //System.err.println("getting here");
            //
            // TODO: Work out which argument we are currently in and highlight it
            // TODO: Possibly mark when the parameter being passed to the argument is incorrect?
            String toBeMatched = scanData.substring(strPos+1, triggerPos);
           
            //Function fun = DictionaryManager.getDictionary(DictionaryManager.CFDIC).getFunction(toBeMatched);
            //String usage = fun.toString();
             
            //if(usage == null)
            //{
              //Debug.println(mName, this, "Cannot found a match for '" + toBeMatched + "'");
            //  System.err.println("Cannot found a match for '" + toBeMatched + "'");
            //  return null;
            //}
            break;
//            Set poss = SyntaxDictionary.limitSet(
//              DictionaryManager.getDictionary(DictionaryToUse).getAllFunctions(),
//              toBeMatched
//            );
//           
//            return makeSetToProposal(poss, documentOffset, TAGTYPE, toBeMatched.length());

            //proposals.add(fun);
          }
          break;
        case ';':
          break;
        case '>':
          break;
        case '\"':
          break;
        default:
          //messages = "Received the character " + lastChar;
          //
          // Think some sort of combined bracket + spacer character search needs to
          // occur here. Not sure how though. Basically it wants to try and match
          // the originating bracket.
          //  Good demo is this one:
          // Len(asdfasdf, ArrayAppend(fred, ArrayNew(asdf), fred))
           
          //r2 QA 2004-05-15
          //quick an dirty way to see all functions (hitting ctrl+space
          //but this doesn't limit the selections and only works when
          //not in a function :-/
         
//          Set mst = DictionaryManager.getDictionary(DictionaryToUse).getAllFunctions();
//          return makeSetToProposal(
//            mst,
//            documentOffset,
//            TAGTYPE,
//            0
//          );       
          //break; 
      }
      //Debug.println(mName, this, messages);
     
    }
    catch (BadLocationException e)
    {
      e.printStackTrace();
      //Debug.println(mName, this, "Caught a bad location exception!");
    }
    catch (Exception e)
    {
      e.printStackTrace();
      //Debug.println(mName, this, "Caught exception: " + e.getMessage());
    }

   
   
    //Debug.println("");
    if(proposals.isEmpty())
      return null;
   
    //length = 10;
   
    return makeSetToProposal(proposals, documentOffset, TAGTYPE, 0); //length);
  }

  /**
   * helper function
   * @param st the set to get the information from
   * @param offset where in the document the items will be
   * @param type attribute or tag (see finals in this class)
   * @return
   */
  private ICompletionProposal[] makeSetToProposal(Set st, int offset, short type, int currentlen)
  {
    //r2 QA 2004-05-15
    //Moved to the new function object way from the original string way
    //like I first had the dictionary...
    //System.out.println("got " + offset + " " + type + " " + currentlen + " " + st.size());
    if(st != null)
    {
      st = new TreeSet(st);
     
      //build a Completion dodad with the right amount of records
      CompletionProposal[] result = new CompletionProposal[st.size()];
     
      int z=0;
      Iterator i = st.iterator();
      while(i.hasNext())
      {
        Function fun = (Function)i.next();
       
        //the toLowerCase is because of the createobject hack, we need
        //to either make a setting in prefs for upper/lower or find a
        //better way to handle the createobject case
        String name = fun.getName().toLowerCase();
        //now remove chars so when they hit enter it wont write the whole
        //word just the part they havent typed
        if(name.length() > 0)
        {
          name = name.substring(currentlen, name.length());
          //System.out.println("makeSetToProposal" + "in::" + name);
        }
       
        //the tag len and icon
        int insertlen = 0;
        Image img = null;
        //if there is anyof the function name left add a closing(
        insertlen = name.length();
        if (name.length() > 0) {
          name += "(";
          insertlen = name.length();
        }
        //default to the tag len and icon
        name += ")";
        img = CFPluginImages.get(CFPluginImages.ICON_FUNC);
       
        result[z] = new CompletionProposal(
          name,
          offset,
          0,
          insertlen,
          img,
          fun.toString(),
          null,
          fun.getHelp()
        );
        z++;
      }
      return result;
    }
   
    return null;
  }

  /** 
   * TODO this is breaking the rules. This needs to be implemented
   * not sure what it does though :-/
   */
  public IContextInformationValidator getContextInformationValidator() {
    return null;
  }

  /**
   * for functions insight
   * TODO check out cfcompletion this has changed a bit... maybe we should
   * workout some way to make these call the same place?
   */
  public IContextInformation[] computeContextInformation(ITextViewer viewer,
    int documentOffset) {
    /* String mName = "computeContextInformation";
    try
    {
      //find out the line number and get the begining of the line
      int linenum = viewer.getDocument().getLineOfOffset(documentOffset);
      int linestart = viewer.getDocument().getLineOffset(linenum);
      //get the line
      String currentline = viewer.getDocument().get(linestart,documentOffset - linestart);
      Debug.println(mName, this, "Currentline: " + currentline);
      //make it a space delimited list
      currentline = currentline.replace('\"',' ');
      currentline = currentline.replace('\'',' ');
      currentline = currentline.replace('#',' ');
      Debug.println(mName, this, "Post replace: " + currentline);
      //tokenize the bad boy
      StringTokenizer st = new StringTokenizer(currentline," ");
     
      //the last item should be our function (because of documentOffset)
      Debug.println(mName, this, "Line: " + currentline);
      String functionname = "";
      while(st.hasMoreTokens())
      {
        functionname = st.nextToken();
      }
     
     
      int bracketPos = functionname.indexOf('(') + 1;
      Debug.println(mName, this, "Bracket at " + bracketPos + ", length = " + functionname.length());
      if(bracketPos != functionname.length())
      {
        functionname = functionname.substring(0, bracketPos);
      }
     
//      remove the last char (which should be the '(')
      functionname = functionname.substring(0,functionname.length() - 1);
   
      Debug.println("computeContextInformation", this, functionname.trim());
     
      //String usage = ((SyntaxDictionaryInterface)DictionaryManager.getDictionary(DictionaryManager.CFDIC)).getFunctionUsage(functionname.trim());
      //String usage = CFSyntaxDictionary.getFunctionUsage(functionname.trim());
      // Dictionary change again
      Function fun = DictionaryManager.getDictionary(DictionaryManager.CFDIC).getFunction(functionname.trim());
      String usage = fun.toString();
     
      if(usage != null)
      {
        //bit of a hack - there are only a copule functions that have
        //several wasys to call them, so if there are more then one
        //they are sperated by ||s
        //st = new StringTokenizer(usage,"||");
       
        ////////////////////////////////////////////////////////////////
        //TODO figure out why this has to have 2 - it wont show otherwise
        //IContextInformation[] result = new IContextInformation[st.countTokens() + 1];
        IContextInformation[] result = new IContextInformation[2];
       
        int i = 0;
        //while(st.hasMoreTokens())
        //{
          //String info = st.nextToken().trim();
          result[i] = new ContextInformation(
            CFPluginImages.get(CFPluginImages.ICON_FUNC),
            //info,
            usage,
            //""
            fun.getHelp()
          );
          i++;
        //}
        result[i] = new ContextInformation(
          "",
          ""
        );
       
        return result;
      }
      return null;
     
    }catch(Exception e)
    {
      //?
    }
    */
    return null;
  }

  /**
   * yeah...
   */
  public String getErrorMessage() {
    return null;
  }

  public String getId() {
    return "cfscript.proposals";
  }

  public String getName() {
    return "CFScript Proposals";
  }

  public ICompletionProposal[] getTagProposals(IAssistState state) {
    if (!preferenceManager.getBooleanPref(AutoIndentPreferenceConstants.P_SUGGEST_FUNCTIONS)) {
      return null;
    }

    return computeCompletionProposals(state.getITextView(), state.getOffset());
  }
}
TOP

Related Classes of org.cfeclipse.cfml.editors.contentassist.CFMLFunctionCompletionProcessor

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.