/* * Copyright (c) 2007 BUSINESS OBJECTS SOFTWARE LIMITED * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * * Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * * Neither the name of Business Objects nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ /* * AutoCompleteHelper.java * Created: Mar 26, 2007 * By: Greg McClement */ package org.openquark.cal.services; import java.util.ArrayList; import java.util.List; import org.openquark.cal.compiler.LanguageInfo; import org.openquark.util.Pair; /** * Helper class for performing auto-completions in editors of cal source code. * This is currently used by the CAL Eclipse plug-in and the GEM cutter. * * @author Greg McClement */ public class AutoCompleteHelper{ /** * The container of the string that is being auto-completed */ private final Document doc; public interface Document{ char getChar(int offset); String get(int startIndex, int length); } public AutoCompleteHelper(Document doc){ this.doc = doc; } /** * Get the last uncompleted word. For example "ab" in "Cal.Core.Prelude.ab". This will return * "" for strings such as "Cal.Core.Prelude." */ public String getLastIncompleteIdentifier(int offset){ int n = offset-1; for (; n >= 0; n--) { final char c = doc.getChar(n); if (!LanguageInfo.isCALVarPart(c)){ return doc.get(n + 1, offset-n-1); } } // scanned the identifier to the start of the file return doc.get(n + 1, offset-n-1); } /** * Starts at the current position and scans until the start of the string or a * non-space character is found. Returns the index of the non-whitespace * character or -1 if the scan goes to the start of the string. */ private int skipSpaces(int offset){ while(offset >= 0){ char c = doc.getChar(offset); if (!Character.isWhitespace(c)){ return offset; } offset--; } return offset; } /** * Scans from the current position until the start of a CAL identifier. Returns the index * of the first character of the identifier or -1 if the scan goes past the start of the * string. */ private int getStartOfIdentifier(int offset){ while(offset > 0){ char c = doc.getChar(--offset); if (!LanguageInfo.isCALVarPart(c)){ return offset + 1; } } return offset; } /** * Get the scoping information before the last uncompleted word. For example, this returns * "Cal.Core.Prelude" for the string "Cal.Core.Prelude.abs". The return value is an array. * The first value is a string representing the scoping ("Cal.Core.Prelude"), then second value * is an array that contains the start offset of each part of the hierarchical name. This * is helpful for determine what part of the string to replace during auto-completion. */ public Pair<String, List<Integer>> getIdentifierScoping(int offset) { // The start position of each component in the scoping part. ArrayList<Integer> componentPositions = new ArrayList<Integer>(); // Skip the last incompleted word char c; int startOfLastIndentifier = getStartOfIdentifier(offset); componentPositions.add(Integer.valueOf(startOfLastIndentifier)); StringBuilder stringBuilder = new StringBuilder(); while (true) { offset = skipSpaces(startOfLastIndentifier - 1); if (offset < 0){ break; } c = doc.getChar(offset); if (c == '.'){ final int endOfNextIdentifier = skipSpaces(offset-1); if (endOfNextIdentifier < 0){ break; } final int startOfNextIdentifier = getStartOfIdentifier(endOfNextIdentifier); if (startOfLastIndentifier < 0){ break; } if (stringBuilder.length() > 0){ stringBuilder.insert(0, '.'); } stringBuilder.insert(0, doc.get(startOfNextIdentifier, endOfNextIdentifier - startOfNextIdentifier + 1)); startOfLastIndentifier = startOfNextIdentifier; componentPositions.add(0, Integer.valueOf(startOfLastIndentifier)); } else{ break; } } return new Pair<String, List<Integer>>(stringBuilder.toString(), componentPositions); } }