Skip to content
Snippets Groups Projects
Commit 13c772ff authored by Markus Alexander Kuppe's avatar Markus Alexander Kuppe
Browse files

Provide code completion for PCal statements - triggered by CTRL-space -

based on keyword prefixes.

[Feature][Toolbox][Changelog]
parent ad36f390
Branches
Tags
No related merge requests found
......@@ -25,6 +25,7 @@ import org.eclipse.swt.custom.StyledText;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.ui.editors.text.TextSourceViewerConfiguration;
import org.lamport.tla.toolbox.editor.basic.pcal.PCalCompletionProcessor;
import org.lamport.tla.toolbox.editor.basic.pcal.PCalHover;
import org.lamport.tla.toolbox.editor.basic.tla.TLAAnnotationHover;
import org.lamport.tla.toolbox.editor.basic.tla.TLACompletionProcessor;
......@@ -118,12 +119,17 @@ public class TLASourceViewerConfiguration extends TextSourceViewerConfiguration
*/
public IContentAssistant getContentAssistant(ISourceViewer sourceViewer)
{
ContentAssistant assistant = new ContentAssistant();
assistant.setDocumentPartitioning(getConfiguredDocumentPartitioning(sourceViewer));
assistant.setContentAssistProcessor(new TLACompletionProcessor(), IDocument.DEFAULT_CONTENT_TYPE);
assistant.setContentAssistProcessor(new PCalCompletionProcessor(), TLAPartitionScanner.TLA_PCAL);
assistant.enableAutoActivation(true);
assistant.setAutoActivationDelay(500);
assistant.setInformationControlCreator(new IInformationControlCreator() {
public IInformationControl createInformationControl(final Shell parent) {
return new DefaultInformationControl(parent, (DefaultInformationControl.IInformationPresenter) null);
}
});
assistant.setProposalPopupOrientation(IContentAssistant.PROPOSAL_OVERLAY);
assistant.setContextInformationPopupOrientation(IContentAssistant.CONTEXT_INFO_ABOVE);
assistant.setContextInformationPopupBackground(TLAEditorActivator.getDefault().getTLAColorProvider().getColor(
......
package org.lamport.tla.toolbox.editor.basic;
import java.util.ArrayList;
import java.util.List;
import java.util.SortedMap;
import java.util.TreeMap;
import org.eclipse.core.runtime.Assert;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.jface.text.BadLocationException;
import org.eclipse.jface.text.IDocument;
import org.eclipse.jface.text.IRegion;
import org.eclipse.jface.text.ITextViewer;
import org.eclipse.jface.text.contentassist.BoldStylerProvider;
import org.eclipse.jface.text.contentassist.ContextInformation;
import org.eclipse.jface.text.contentassist.ICompletionProposal;
import org.eclipse.jface.text.contentassist.ICompletionProposalExtension5;
import org.eclipse.jface.text.contentassist.ICompletionProposalExtension7;
import org.eclipse.jface.text.contentassist.IContextInformation;
import org.eclipse.jface.text.contentassist.IContextInformationValidator;
import org.eclipse.jface.viewers.StyledString;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.graphics.Point;
import org.lamport.tla.toolbox.editor.basic.tla.ITLAReserveredWords;
import org.lamport.tla.toolbox.editor.basic.util.DocumentHelper;
public abstract class ToolboxCompletionProcessor {
protected final SortedMap<String, List<CompletionProposalTemplate>> proposals = new TreeMap<String, List<CompletionProposalTemplate>>();
public ICompletionProposal[] computeCompletionProposals(ITextViewer viewer, int offset) {
final IDocument document = viewer.getDocument();
// get the selection range
final Point selectedRange = viewer.getSelectedRange();
final List<ICompletionProposal> propList = new ArrayList<ICompletionProposal>();
try {
if (selectedRange.y > 0) {
// the range is non-empty
final String text = document.get(selectedRange.x, selectedRange.y);
computeWordProposals(text, offset, propList);
} else {
// the range is empty, no selection in the editor
// get the region
final IRegion wordRegion = DocumentHelper.getRegionExpandedBackwards(document, offset,
DocumentHelper.getDefaultWordDetector());
final String word = document.get(wordRegion.getOffset(), wordRegion.getLength());
computeWordProposals(word, offset, propList);
}
} catch (final BadLocationException ignore) {
}
return propList.toArray(new ICompletionProposal[propList.size()]);
}
/**
* Syntax-based proposal based for word beginning
*/
private void computeWordProposals(final String word, final int offset, final List<ICompletionProposal> propositionList) {
final int qualifierLength = word.length();
final int replacementOffset = offset - qualifierLength;
// keyword and other static proposals
for (List<CompletionProposalTemplate> list : filterPrefix(proposals, word).values()) {
// and add to result list
for (CompletionProposalTemplate template : list) {
propositionList.add(template.getProposal(replacementOffset, qualifierLength));
}
}
}
public static SortedMap<String, List<CompletionProposalTemplate>> filterPrefix(SortedMap<String, List<CompletionProposalTemplate>> baseMap, String prefix) {
if (prefix.length() > 0) {
final char nextLetter = (char) (prefix.charAt(prefix.length() - 1) + 1);
String end = prefix.substring(0, prefix.length() - 1) + nextLetter;
return baseMap.subMap(prefix, end);
}
return baseMap;
}
public IContextInformation[] computeContextInformation(ITextViewer viewer, int offset) {
// Retrieve selected range
final Point selectedRange = viewer.getSelectedRange();
if (selectedRange.y > 0) {
// Text is selected. Create a context information array.
final IContextInformation[] contextInfos = new ContextInformation[ITLAReserveredWords.ALL_WORDS_ARRAY.length];
// Create one context information item for each style
for (int i = 0; i < ITLAReserveredWords.ALL_WORDS_ARRAY.length; i++) {
contextInfos[i] = new ContextInformation(null, ITLAReserveredWords.ALL_WORDS_ARRAY[i] + " Style");
}
return contextInfos;
}
return new ContextInformation[0];
}
/* (non-Javadoc)
* @see org.eclipse.jface.text.contentassist.IContentAssistProcessor#getCompletionProposalAutoActivationCharacters()
*/
public char[] getCompletionProposalAutoActivationCharacters() {
return null;
}
/* (non-Javadoc)
* @see org.eclipse.jface.text.contentassist.IContentAssistProcessor#getContextInformationAutoActivationCharacters()
*/
public char[] getContextInformationAutoActivationCharacters() {
return null;
}
/* (non-Javadoc)
* @see org.eclipse.jface.text.contentassist.IContentAssistProcessor#getContextInformationValidator()
*/
public IContextInformationValidator getContextInformationValidator() {
return null;
}
/* (non-Javadoc)
* @see org.eclipse.jface.text.contentassist.IContentAssistProcessor#getErrorMessage()
*/
public String getErrorMessage() {
return null;
}
protected static class CompletionProposalTemplate {
private final String fReplacementString;
private final Image fImage;
private final IContextInformation fContextInformation;
private final String fAdditionalProposalInfo;
private final String fDisplayString;
public CompletionProposalTemplate(String replacementString, Image image, String dipslayString,
IContextInformation contextInformation, String additionalProposalInfo) {
this.fReplacementString = replacementString;
this.fImage = image;
this.fDisplayString = dipslayString;
this.fContextInformation = contextInformation;
this.fAdditionalProposalInfo = additionalProposalInfo;
}
public CompletionProposalTemplate(String replacementString, Image image, String dipslayString,
String additionalProposalInfo) {
this.fReplacementString = replacementString;
this.fImage = image;
this.fDisplayString = dipslayString;
this.fContextInformation = null;
this.fAdditionalProposalInfo = additionalProposalInfo;
}
public CompletionProposalTemplate(String replacementString, String dipslayString,
String additionalProposalInfo) {
this.fReplacementString = replacementString;
this.fImage = null;
this.fDisplayString = dipslayString;
this.fContextInformation = null;
this.fAdditionalProposalInfo = additionalProposalInfo;
}
public CompletionProposalTemplate(String replacementString) {
this.fReplacementString = replacementString;
this.fImage = null; // Image for keywords?!
this.fContextInformation = null;
this.fAdditionalProposalInfo = null;
this.fDisplayString = null;
}
public ICompletionProposal getProposal(int replacementOffset, int qualifierLength) {
return new ToolboxCompletionProposal(fReplacementString, replacementOffset, qualifierLength,
fReplacementString.length() - 1, fImage, fDisplayString, fContextInformation,
fAdditionalProposalInfo);
}
}
public static class ToolboxCompletionProposal implements ICompletionProposal, ICompletionProposalExtension5, ICompletionProposalExtension7 {
/** The string to be displayed in the completion proposal popup. */
private String fDisplayString;
/** The replacement string. */
private String fReplacementString;
/** The replacement offset. */
private int fReplacementOffset;
/** The replacement length. */
private int fReplacementLength;
/** The cursor position after this proposal has been applied. */
private int fCursorPosition;
/** The image to be displayed in the completion proposal popup. */
private Image fImage;
/** The context information of this proposal. */
private IContextInformation fContextInformation;
/** The additional info of this proposal. */
private String fAdditionalProposalInfo;
/**
* Creates a new completion proposal based on the provided information. The
* replacement string is considered being the display string too. All remaining
* fields are set to <code>null</code>.
*
* @param replacementString
* the actual string to be inserted into the document
* @param replacementOffset
* the offset of the text to be replaced
* @param replacementLength
* the length of the text to be replaced
* @param cursorPosition
* the position of the cursor following the insert relative to
* replacementOffset
*/
public ToolboxCompletionProposal(String replacementString, int replacementOffset, int replacementLength,
int cursorPosition) {
this(replacementString, replacementOffset, replacementLength, cursorPosition, null, null, null, null);
}
/**
* Creates a new completion proposal. All fields are initialized based on the
* provided information.
*
* @param replacementString
* the actual string to be inserted into the document
* @param replacementOffset
* the offset of the text to be replaced
* @param replacementLength
* the length of the text to be replaced
* @param cursorPosition
* the position of the cursor following the insert relative to
* replacementOffset
* @param image
* the image to display for this proposal
* @param displayString
* the string to be displayed for the proposal
* @param contextInformation
* the context information associated with this proposal
* @param additionalProposalInfo
* the additional information associated with this proposal
*/
public ToolboxCompletionProposal(String replacementString, int replacementOffset, int replacementLength,
int cursorPosition, Image image, String displayString, IContextInformation contextInformation,
String additionalProposalInfo) {
Assert.isNotNull(replacementString);
Assert.isTrue(replacementOffset >= 0);
Assert.isTrue(replacementLength >= 0);
Assert.isTrue(cursorPosition >= 0);
fReplacementString = replacementString;
fReplacementOffset = replacementOffset;
fReplacementLength = replacementLength;
fCursorPosition = cursorPosition;
fImage = image;
fDisplayString = displayString;
fContextInformation = contextInformation;
fAdditionalProposalInfo = additionalProposalInfo;
}
public void apply(IDocument document) {
try {
document.replace(fReplacementOffset, fReplacementLength, fReplacementString);
} catch (BadLocationException x) {
// ignore
}
}
public Point getSelection(IDocument document) {
return new Point(fReplacementOffset + fCursorPosition, 0);
}
public IContextInformation getContextInformation() {
return fContextInformation;
}
public Image getImage() {
return fImage;
}
public String getDisplayString() {
if (fDisplayString != null)
return fDisplayString;
return fReplacementString;
}
public String getAdditionalProposalInfo() {
return fAdditionalProposalInfo;
}
public Object getAdditionalProposalInfo(IProgressMonitor monitor) {
return getAdditionalProposalInfo();
}
public StyledString getStyledDisplayString(IDocument document, int offset,
BoldStylerProvider boldStylerProvider) {
final StyledString styledString = new StyledString();
styledString.append(getDisplayString());
styledString.append(": ");
styledString.append(fReplacementString, StyledString.COUNTER_STYLER);
return styledString;
}
}
}
package org.lamport.tla.toolbox.editor.basic.pcal;
import org.lamport.tla.toolbox.editor.basic.tla.ITLAReserveredWords;
/**
* @author Simon Zambrovski
* @version $Id$
......@@ -11,7 +13,7 @@ public interface IPCalReservedWords {
public final static String AWAIT = "await";
public final static String BEGIN = "begin";
public final static String CALL = "call";
public final static String DEFINE = "define";
public final static String DEFINE = ITLAReserveredWords.PDEFINE;
public final static String DO = "do";
public final static String EITHER = "either";
public final static String ELSE = "else";
......
package org.lamport.tla.toolbox.editor.basic.pcal;
import java.util.ArrayList;
import java.util.List;
import org.eclipse.jface.text.contentassist.IContentAssistProcessor;
import org.lamport.tla.toolbox.editor.basic.ToolboxCompletionProcessor;
public class PCalCompletionProcessor extends ToolboxCompletionProcessor implements IContentAssistProcessor {
public PCalCompletionProcessor() {
// No sense to add "--algorithm" because the 'PCalCompletionProcessor is only
// hot when cursor/caret inside a PCal algorithm. Thus "--algorithm" is part of
// TLACompletionProcessor.
// proposals.put(IPCalReservedWords.ALGORITHM, "--algorithm AName {\n}");
List<CompletionProposalTemplate> templates = new ArrayList<CompletionProposalTemplate>();
templates.add(new CompletionProposalTemplate("process (ProcName \\in {\"p1\",\"p2\"}) { label: skip; }",
IPCalReservedWords.PROCESS, IPCalReservedWords.PROCESS_HELP));
templates.add(new CompletionProposalTemplate("process (ProcName = Id) { label: skip; }",
IPCalReservedWords.PROCESS, IPCalReservedWords.PROCESS_HELP));
proposals.put(IPCalReservedWords.PROCESS, templates);
templates = new ArrayList<ToolboxCompletionProcessor.CompletionProposalTemplate>();
templates.add(
new CompletionProposalTemplate("if (TRUE) { skip; };", "if-then", IPCalReservedWords.IFTHENELSE_HELP));
templates.add(new CompletionProposalTemplate("if (TRUE) { skip; }; else { skip; };", "if-then-else",
IPCalReservedWords.IFTHENELSE_HELP));
templates.add(new CompletionProposalTemplate("if (TRUE) { skip; }; elseif { skip; };", "if-then-elseif",
IPCalReservedWords.IFTHENELSE_HELP));
proposals.put(IPCalReservedWords.IF, templates);
proposals.put(IPCalReservedWords.VARIABLE,
getSingleProposal("variable x = TRUE;", IPCalReservedWords.VARIABLE, IPCalReservedWords.VARIABLE_HELP));
proposals.put(IPCalReservedWords.VARIABLES, getSingleProposal("variables x = TRUE ; y \\in {1,2,3} ; z;",
IPCalReservedWords.VARIABLES, IPCalReservedWords.VARIABLE_HELP));
proposals.put(IPCalReservedWords.PROCEDURE,
getSingleProposal("procedure PName (param1, ..., paramN) { label: skip; }", IPCalReservedWords.PROCEDURE,
IPCalReservedWords.PROCEDURE_HELP));
proposals.put(IPCalReservedWords.CALL, getSingleProposal("call PName (expr1, ..., exprN)",
IPCalReservedWords.CALL, IPCalReservedWords.PROCEDURE_HELP));
proposals.put(IPCalReservedWords.WHILE, getSingleProposal("label: while (TRUE) { skip; };",
IPCalReservedWords.WHILE, IPCalReservedWords.WHILE_HELP));
proposals.put(IPCalReservedWords.EITHER, getSingleProposal("either { skip; } or { skip; } or { skip; };",
IPCalReservedWords.EITHER, IPCalReservedWords.EITHEROR_HELP));
proposals.put(IPCalReservedWords.ASSERT,
getSingleProposal("assert TRUE;", IPCalReservedWords.ASSERT, IPCalReservedWords.ASSERT_HELP));
proposals.put(IPCalReservedWords.GOTO,
getSingleProposal("goto label;", IPCalReservedWords.GOTO, IPCalReservedWords.GOTO_HELP));
proposals.put(IPCalReservedWords.PRINT,
getSingleProposal("print \"msg\";", IPCalReservedWords.PRINT, IPCalReservedWords.PRINT_HELP));
proposals.put(IPCalReservedWords.WITH, getSingleProposal("with ( i \\in {1,2,3} ) { skip; }",
IPCalReservedWords.WITH, IPCalReservedWords.WITH_HELP));
proposals.put(IPCalReservedWords.MACRO, getSingleProposal("macro P(param1, ... , paramN) { skip; }",
IPCalReservedWords.MACRO, IPCalReservedWords.MACRO_HELP));
proposals.put(IPCalReservedWords.DEFINE,
getSingleProposal("define { Op1(param1, ... , paramN) == TRUE Op2(...) == TRUE }",
IPCalReservedWords.DEFINE, IPCalReservedWords.DEFINE_HELP));
}
private static List<CompletionProposalTemplate> getSingleProposal(String replacementString, String displayString, String additionalInformation) {
final List<CompletionProposalTemplate> proposal = new ArrayList<CompletionProposalTemplate>();
proposal.add(new CompletionProposalTemplate(replacementString, displayString, additionalInformation));
return proposal;
}
}
package org.lamport.tla.toolbox.editor.basic.tla;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import org.eclipse.jface.text.BadLocationException;
import org.eclipse.jface.text.IDocument;
import org.eclipse.jface.text.IRegion;
import org.eclipse.jface.text.ITextViewer;
import org.eclipse.jface.text.contentassist.CompletionProposal;
import org.eclipse.jface.text.contentassist.ContextInformation;
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;
import org.eclipse.swt.graphics.Point;
import org.lamport.tla.toolbox.editor.basic.TLAEditorActivator;
import org.lamport.tla.toolbox.editor.basic.util.DocumentHelper;
import org.lamport.tla.toolbox.editor.basic.ToolboxCompletionProcessor;
import org.lamport.tla.toolbox.editor.basic.pcal.IPCalReservedWords;
/**
* Syntactic auto-completion processor
* @author Simon Zambrovski
* @version $Id$
*/
public class TLACompletionProcessor implements IContentAssistProcessor
public class TLACompletionProcessor extends ToolboxCompletionProcessor implements IContentAssistProcessor
{
private String[] proposals = ITLAReserveredWords.ALL_WORDS_ARRAY;
/* (non-Javadoc)
* @see org.eclipse.jface.text.contentassist.IContentAssistProcessor#computeCompletionProposals(org.eclipse.jface.text.ITextViewer, int)
*/
public ICompletionProposal[] computeCompletionProposals(ITextViewer viewer, int offset)
{
/*
// show all proposals without checking the context
ICompletionProposal[] result = new ICompletionProposal[fgProposals.length];
for (int i = 0; i < fgProposals.length; i++)
{
IContextInformation info = new ContextInformation(fgProposals[i], "");
result[i] = new CompletionProposal(fgProposals[i], offset, 0, fgProposals[i].length(), null,
fgProposals[i], info, "");
}
return result;
*/
IDocument document = viewer.getDocument();
// get the selection range
Point selectedRange = viewer.getSelectedRange();
List propList = new ArrayList();
try
{
if (selectedRange.y > 0)
{
// the range is non-empty
String text = document.get(selectedRange.x, selectedRange.y);
computeWordProposals(text, offset, propList);
} else
{
// the range is empty, no selection in the editor
// get the region
IRegion wordRegion = DocumentHelper.getRegionExpandedBackwards(document, offset, DocumentHelper
.getDefaultWordDetector());
String word = document.get(wordRegion.getOffset(), wordRegion.getLength());
TLAEditorActivator.getDefault().logDebug("Content assist for '" + word + "'" + wordRegion );
computeWordProposals(word, offset, propList);
}
} catch (BadLocationException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
}
ICompletionProposal[] proposals = new ICompletionProposal[propList.size()];
propList.toArray(proposals);
return proposals;
}
/**
* Syntax-based proposal based for word beginning
* @param word
* @param offset
* @param propositionList
*/
private void computeWordProposals(String word, int offset, List propositionList)
{
int qualifierLength = word.length();
// Loop through all proposals
for (int i = 0; i < proposals.length; i++)
{
String proposalText = proposals[i];
// Check if proposal matches qualifier
if (proposalText.startsWith(word))
{
// compute whole proposal text
String text = proposalText + " ";
// Derive cursor position
int cursor = proposalText.length();
// Construct proposal
CompletionProposal proposal = new CompletionProposal(text, offset - qualifierLength, qualifierLength,
cursor);
// and add to result list
propositionList.add(proposal);
}
public TLACompletionProcessor() {
final List<String> asList = Arrays.asList(ITLAReserveredWords.ALL_WORDS_ARRAY);
for (String string : asList) {
final List<CompletionProposalTemplate> proposal = new ArrayList<CompletionProposalTemplate>(1);
proposal.add(new CompletionProposalTemplate(string + " "));
proposals.put(string, proposal);
}
// Define PCal "algorithm" here because the PCalCompletionProcessor is only
// active inside an algorithm definition (chicken or egg problem).
final List<CompletionProposalTemplate> l = new ArrayList<CompletionProposalTemplate>(1);
l.add(new CompletionProposalTemplate(
"(***************************************************************************\r\n"
+ "--algorithm AlgorithmName {\r\n}\r\n"
+ " ***************************************************************************)\r\n",
IPCalReservedWords.ALGORITHM, IPCalReservedWords.ALGORITHM_HELP));
proposals.put(ITLAReserveredWords.ALGORITHM, l);
}
/* (non-Javadoc)
* @see org.eclipse.jface.text.contentassist.IContentAssistProcessor#computeContextInformation(org.eclipse.jface.text.ITextViewer, int)
*/
public IContextInformation[] computeContextInformation(ITextViewer viewer, int offset)
{
public IContextInformation[] computeContextInformation(ITextViewer viewer, int offset) {
// Retrieve selected range
Point selectedRange = viewer.getSelectedRange();
if (selectedRange.y > 0)
{
final Point selectedRange = viewer.getSelectedRange();
if (selectedRange.y > 0) {
// Text is selected. Create a context information array.
ContextInformation[] contextInfos = new ContextInformation[ITLAReserveredWords.ALL_WORDS_ARRAY.length];
final IContextInformation[] contextInfos = new ContextInformation[ITLAReserveredWords.ALL_WORDS_ARRAY.length];
// Create one context information item for each style
for (int i = 0; i < ITLAReserveredWords.ALL_WORDS_ARRAY.length; i++)
for (int i = 0; i < ITLAReserveredWords.ALL_WORDS_ARRAY.length; i++) {
contextInfos[i] = new ContextInformation(null, ITLAReserveredWords.ALL_WORDS_ARRAY[i] + " Style");
}
return contextInfos;
}
return new ContextInformation[0];
}
/* (non-Javadoc)
* @see org.eclipse.jface.text.contentassist.IContentAssistProcessor#getCompletionProposalAutoActivationCharacters()
*/
public char[] getCompletionProposalAutoActivationCharacters()
{
return null;
}
/* (non-Javadoc)
* @see org.eclipse.jface.text.contentassist.IContentAssistProcessor#getContextInformationAutoActivationCharacters()
*/
public char[] getContextInformationAutoActivationCharacters()
{
return null;
}
/* (non-Javadoc)
* @see org.eclipse.jface.text.contentassist.IContentAssistProcessor#getContextInformationValidator()
*/
public IContextInformationValidator getContextInformationValidator()
{
return null;
}
/* (non-Javadoc)
* @see org.eclipse.jface.text.contentassist.IContentAssistProcessor#getErrorMessage()
*/
public String getErrorMessage()
{
return null;
}
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment