// Copyright 2012 Google Inc. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package com.google.collide.client.code.autocomplete.codegraph; import com.google.collide.client.code.autocomplete.AutocompleteController; import com.google.collide.client.code.autocomplete.AutocompleteProposal; import com.google.collide.client.code.autocomplete.AutocompleteProposals; import com.google.collide.client.code.autocomplete.AutocompleteResult; import com.google.collide.client.code.autocomplete.LanguageSpecificAutocompleter; import com.google.collide.client.code.autocomplete.SignalEventEssence; import com.google.collide.client.code.autocomplete.AutocompleteProposals.ProposalWithContext; import com.google.collide.client.codeunderstanding.CubeClient; import com.google.collide.client.documentparser.DocumentParser; import com.google.collide.client.editor.selection.SelectionModel; import com.google.collide.client.util.PathUtil; import com.google.collide.codemirror2.SyntaxType; import com.google.common.base.Preconditions; import com.google.gwt.regexp.shared.MatchResult; import com.google.gwt.regexp.shared.RegExp; /** * Implements autocompleter for abstract language statements. */ public class CodeGraphAutocompleter extends LanguageSpecificAutocompleter { private static final RegExp ID_REGEXP = RegExp.compile("[a-zA-Z\\$_][a-zA-Z\\$_0-9]*$"); private ScopeTrieBuilder scopeTrieBuilder; private final CodeGraphSource codeGraphSource; private final ProposalBuilder proposalBuilder; private final ExplicitAutocompleter explicitAutocompleter; private final LimitedContextFilePrefixIndex contextFilePrefixIndex; private final Runnable codeGraphUpdateListener = new Runnable() { @Override public void run() { scopeTrieBuilder.setCodeGraph(codeGraphSource.constructCodeGraph()); scheduleRequestForUpdatedProposals(); } }; public CodeGraphAutocompleter(SyntaxType mode, ProposalBuilder proposalBuilder, CubeClient cubeClient, LimitedContextFilePrefixIndex contextFilePrefixIndex, ExplicitAutocompleter explicitAutocompleter) { super(mode); this.explicitAutocompleter = explicitAutocompleter; this.proposalBuilder = proposalBuilder; this.contextFilePrefixIndex = contextFilePrefixIndex; this.codeGraphSource = new CodeGraphSource(cubeClient, codeGraphUpdateListener); } @Override public AutocompleteResult computeAutocompletionResult(ProposalWithContext selected) { AutocompleteProposals.Context context = selected.getContext(); String triggeringString = context.getTriggeringString(); AutocompleteProposal proposal = selected.getItem(); if (proposal instanceof TemplateProposal) { TemplateProposal templateProposal = (TemplateProposal) proposal; return templateProposal.buildResult(triggeringString, context.getIndent()); } Preconditions.checkArgument(proposal instanceof CodeGraphProposal); CodeGraphProposal selectedProposal = (CodeGraphProposal) proposal; return proposalBuilder.computeAutocompletionResult(selectedProposal, triggeringString); } /** * Finds autocompletions for a given completion query. * * <p>This method is triggered when:<ul> * <li>popup is hidden and user press ctrl-space (event consumed) * <li><b>or</b> popup is hidden and user press "." (dot applied) * <li><b>or</b> popup is shown * </ul> */ @Override public AutocompleteProposals findAutocompletions( SelectionModel selection, SignalEventEssence trigger) { Preconditions.checkNotNull(scopeTrieBuilder); if (selection.hasSelection()) { // Do not autocomplete JS/PY when something is selected. return AutocompleteProposals.EMPTY; } if (trigger == null || trigger.altKey || !trigger.shiftKey) { return proposalBuilder.getProposals(getMode(), getParser(), selection, scopeTrieBuilder); } return contextFilePrefixIndex.search(getMode(), calculateTriggeringString(selection)); } private static String calculateTriggeringString(SelectionModel selection) { String cursorLine = selection.getCursorLine().getText(); int cursorColumn = selection.getCursorColumn(); MatchResult matchResult = ID_REGEXP.exec(cursorLine.substring(0, cursorColumn)); if (matchResult == null) { return ""; } return matchResult.getGroup(0); } @Override protected void pause() { super.pause(); codeGraphSource.setPaused(true); } @Override protected void start() { super.start(); codeGraphSource.setPaused(false); Preconditions.checkNotNull(scopeTrieBuilder); if (codeGraphSource.hasUpdate()) { scopeTrieBuilder.setCodeGraph(codeGraphSource.constructCodeGraph()); } } @Override public ExplicitAction getExplicitAction(SelectionModel selectionModel, SignalEventEssence signal, boolean popupIsShown) { return explicitAutocompleter.getExplicitAction( selectionModel, signal, popupIsShown, getParser()); } @Override public void attach( DocumentParser parser, AutocompleteController controller, PathUtil filePath) { super.attach(parser, controller, filePath); CodeFile contextFile = new CodeFile(filePath); scopeTrieBuilder = new ScopeTrieBuilder(contextFile, getMode()); scopeTrieBuilder.setCodeGraph(codeGraphSource.constructCodeGraph()); } @Override public void cleanup() { codeGraphSource.cleanup(); } }