/* * $Id$ * * Copyright (c) 2004-2005 by the TeXlapse Team. * 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 */ package net.sourceforge.texlipse.bibeditor; import java.util.ArrayList; import java.util.Arrays; import java.util.Iterator; import java.util.List; import net.sourceforge.texlipse.model.ReferenceEntry; import net.sourceforge.texlipse.templates.BibTexTemplateCompletion; import net.sourceforge.texlipse.templates.ProposalsComparator; 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.IContentAssistProcessor; import org.eclipse.jface.text.contentassist.IContextInformation; import org.eclipse.jface.text.contentassist.IContextInformationValidator; /** * Handles abbrev and template completions for the BibTeX * editor. * * @author Oskar Ojala */ public class BibCompletionProcessor implements IContentAssistProcessor { private BibTexTemplateCompletion templatesCompletion = new BibTexTemplateCompletion(); private BibDocumentModel model; private AbbrevManager abbrManager; private ProposalsComparator proposalsComparator = new ProposalsComparator(); /** * Constructs a new completion processor * * @param model The document model this processor is associated with. */ public BibCompletionProcessor(BibDocumentModel model) { this.model = model; } /* (non-Javadoc) * @see org.eclipse.jface.text.contentassist.IContentAssistProcessor#computeCompletionProposals(org.eclipse.jface.text.ITextViewer, int) */ public ICompletionProposal[] computeCompletionProposals(ITextViewer viewer, int offset) { if (abbrManager == null) this.abbrManager = this.model.getAbbrManager(); String completeDoc = viewer.getDocument().get(); //get available and matching template completions String latest = resolveLatestWord(completeDoc, offset); ICompletionProposal[] templates = computeTemplateCompletions(offset, latest.length(), latest, viewer); // ---------------------- /* //calculate proposals from previously used values ICompletionProposal[] repeats = null; String line = ""; // Determine the begining of the line for (int i = offset - 1; i > 0; i--) { char c = completeDoc.charAt(i); // FIXME what if we're inside a string? if (c == '\n' || (c == ',' && completeDoc.charAt(i - 1) =='}')) { line = completeDoc.substring(i + 1, offset); break; } } // try to split the line into a field type and a field value String[] lineparts = line.split("="); if (lineparts.length == 2) { // FIXME this fails if the line is foo = "foo = bar", String field = lineparts[0].trim(); String value = lineparts[1].trim().substring(1); repeats = computeRepeatCompletions(offset, value.length(), value, field); } */ // ---------------------- //get available and matching abbrev completitions ICompletionProposal[] abbrevs = null; int completeStart = -1; for (int i = offset - 1; i > 0; i--) { char c = completeDoc.charAt(i); if (c == '=' || c == '#') { if (completeStart == -1) completeStart = i + 1; break; } else if (Character.isWhitespace(c) && completeStart == -1) completeStart = i + 1; else if (c == '{' || c == '}' || c == '"' || c == ',') abbrevs = null; } if (completeStart == -1) { abbrevs = computeAbbrevCompletions(offset, 0, ""); } else { abbrevs = computeAbbrevCompletions(offset, offset - completeStart, completeDoc.substring(completeStart, offset)); } //make combined list of repeats, abbrev and template completion proposals int size = 0; // if (repeats != null) // size += repeats.length; if (abbrevs != null) size += abbrevs.length; if (templates != null) size += templates.length; // TODO replace this with arraycopy if (size == 0) { return null; } else { int index=0; ICompletionProposal[] value = new ICompletionProposal[size]; // ---------------------- /* if (repeats != null) { for (int i=0; i < repeats.length; i++) { value[index] = repeats[i]; index++; } } */ // ---------------------- if (abbrevs != null){ for (int i=0; i < abbrevs.length; i++) { value[index] = abbrevs[i]; index++; } } if (templates != null){ for (int i=0; i < templates.length; i++) { value[index] = templates[i]; index++; } } return value; } } /* (non-Javadoc) * @see org.eclipse.jface.text.contentassist.IContentAssistProcessor#computeContextInformation(org.eclipse.jface.text.ITextViewer, int) */ public IContextInformation[] computeContextInformation(ITextViewer viewer, int offset) { return null; } /* (non-Javadoc) * @see org.eclipse.jface.text.contentassist.IContentAssistProcessor#getCompletionProposalAutoActivationCharacters() */ public char[] getCompletionProposalAutoActivationCharacters() { return new char[] {'=', '#'}; } /* (non-Javadoc) * @see org.eclipse.jface.text.contentassist.IContentAssistProcessor#getContextInformationAutoActivationCharacters() */ public char[] getContextInformationAutoActivationCharacters() { return null; } /* (non-Javadoc) * @see org.eclipse.jface.text.contentassist.IContentAssistProcessor#getErrorMessage() */ public String getErrorMessage() { return null; } /* (non-Javadoc) * @see org.eclipse.jface.text.contentassist.IContentAssistProcessor#getContextInformationValidator() */ public IContextInformationValidator getContextInformationValidator() { return null; } /** * Computes the abbreviation completions available based on the prefix. * * @param offset Cursor offset in the document * @param replacementLength Length of the text to be replaced * @param prefix The start of the abbreviation or "" * @return An array containing all possible completions */ private ICompletionProposal[] computeAbbrevCompletions(int offset, int replacementLength, String prefix) { ReferenceEntry[] abbrevs = abbrManager.getCompletions(prefix); if (abbrevs == null) return null; ICompletionProposal[] result = new ICompletionProposal[abbrevs.length]; for (int i = 0; i < abbrevs.length; i++) { result[i] = new CompletionProposal(abbrevs[i].key, offset - replacementLength, replacementLength, abbrevs[i].key.length(), null, abbrevs[i].key, null, abbrevs[i].info); } return result; } /** Computes the repeated entries completions available based on the prefix. * * @param offset Cursor offset in the document * @param replacementLength Length of the text to be replaced * @param prefix The start of the abbreviation or "" * @return An array containing all possible completions */ // private ICompletionProposal[] computeRepeatCompletions(int offset, int replacementLength, String prefix, String field) { // // BibStringTriMap<ReferenceEntry> index = // model.getSortIndex().get(field.toLowerCase()); // // if (index == null) return new ICompletionProposal[0]; // // // Special case the author field, as it is a list of elements separated by "and" // if ((field.equalsIgnoreCase("author") || field.equalsIgnoreCase("editor")) && prefix.contains(" and ")) { // prefix = (prefix.substring(prefix.lastIndexOf(" and ") + 5)).trim(); // if (prefix.endsWith(" and")) // prefix = ""; // replacementLength = prefix.length(); // } // // // Find all entries of the field that start with prefix // ArrayList<String> repeats = index.getKeys(prefix, true); // ICompletionProposal[] result = new ICompletionProposal[repeats.size()]; // // for (int i = 0; i < repeats.size(); i++) { // result[i] = new CompletionProposal(repeats.get(i), // offset - replacementLength,replacementLength, // repeats.get(i).length()); // } // return result; // } /** * Resolves the latest word immediately before the cursor position and returns it * * @param doc Document to examine * @param offset Current cursor offset * @return the offset index,from where the latest word begins * (if 0, then there is no latest word) */ public String resolveLatestWord(String doc, int offset) { int index = 1; while (offset - index >= 0) { if (Character.isWhitespace(doc.charAt(offset - index))) break; index++; } index--; if (index > 0) return doc.substring(offset - index, offset); return ""; } /** * Computes the template completions available based on the prefix. * * @param offset Cursor offset in the document * @param replacementLength Length of the text to be replaced * @param prefix The start of the abbreviation or "" * @param viewer The viewer associated with this document * @return An array containing all possible completions */ private ICompletionProposal[] computeTemplateCompletions(int offset, int replacementLength, String prefix, ITextViewer viewer) { List templateProposals = new ArrayList(); this.templatesCompletion.addTemplateProposals(viewer, offset, templateProposals); ArrayList returnProposals = new ArrayList(); for (Iterator iter = templateProposals.iterator(); iter.hasNext();) { ICompletionProposal proposal = (ICompletionProposal) iter.next(); if (proposal.getDisplayString().startsWith(prefix)) { returnProposals.add(proposal); } } ICompletionProposal[] proposals = new ICompletionProposal[returnProposals.size()]; returnProposals.toArray(proposals); Arrays.sort(proposals, proposalsComparator); return proposals; } }