/*
* Copyright (c) 2013, the Dart project authors.
*
* Licensed under the Eclipse Public License v1.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.eclipse.org/legal/epl-v10.html
*
* 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.dart.tools.ui.internal.text.correction;
import com.google.common.collect.Lists;
import com.google.common.util.concurrent.Uninterruptibles;
import com.google.dart.engine.ast.CompilationUnit;
import com.google.dart.engine.context.AnalysisContext;
import com.google.dart.engine.services.assist.AssistContext;
import com.google.dart.engine.services.correction.CorrectionProcessors;
import com.google.dart.engine.services.correction.CorrectionProposal;
import com.google.dart.engine.source.Source;
import com.google.dart.server.GetAssistsConsumer;
import com.google.dart.server.GetAvailableRefactoringsConsumer;
import com.google.dart.tools.core.DartCore;
import com.google.dart.tools.core.DartCoreDebug;
import com.google.dart.tools.internal.corext.refactoring.util.ExecutionUtils;
import com.google.dart.tools.internal.corext.refactoring.util.RunnableEx;
import com.google.dart.tools.ui.actions.ConvertGetterToMethodAction_NEW;
import com.google.dart.tools.ui.actions.ConvertGetterToMethodAction_OLD;
import com.google.dart.tools.ui.actions.ConvertMethodToGetterAction_NEW;
import com.google.dart.tools.ui.actions.ConvertMethodToGetterAction_OLD;
import com.google.dart.tools.ui.actions.DartEditorActionDefinitionIds;
import com.google.dart.tools.ui.internal.refactoring.ServiceUtils_NEW;
import com.google.dart.tools.ui.internal.refactoring.ServiceUtils_OLD;
import com.google.dart.tools.ui.internal.refactoring.actions.RenameDartElementAction_OLD;
import com.google.dart.tools.ui.internal.text.correction.proposals.ConvertGetterToMethodRefactoringProposal_NEW;
import com.google.dart.tools.ui.internal.text.correction.proposals.ConvertGetterToMethodRefactoringProposal_OLD;
import com.google.dart.tools.ui.internal.text.correction.proposals.ConvertMethodToGetterRefactoringProposal_NEW;
import com.google.dart.tools.ui.internal.text.correction.proposals.ConvertMethodToGetterRefactoringProposal_OLD;
import com.google.dart.tools.ui.internal.text.correction.proposals.FormatProposal;
import com.google.dart.tools.ui.internal.text.correction.proposals.RenameRefactoringProposal;
import com.google.dart.tools.ui.internal.text.correction.proposals.SortMembersProposal_NEW;
import com.google.dart.tools.ui.internal.text.correction.proposals.SortMembersProposal_OLD;
import com.google.dart.tools.ui.internal.text.editor.DartEditor;
import com.google.dart.tools.ui.internal.text.editor.DartSelection;
import org.dartlang.analysis.server.protocol.RefactoringKind;
import org.dartlang.analysis.server.protocol.RequestError;
import org.dartlang.analysis.server.protocol.SourceChange;
import org.eclipse.jface.action.IAction;
import org.eclipse.jface.text.contentassist.ICompletionProposal;
import org.eclipse.jface.text.quickassist.IQuickAssistProcessor;
import org.eclipse.jface.text.source.ISourceViewer;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
/**
* Standard {@link IQuickAssistProcessor} for Dart.
*
* @coverage dart.editor.ui.correction
*/
public class QuickAssistProcessor {
/**
* Adds the given server's {@link SourceChange}s as LTK proposals.
*/
static void addServerProposals(List<ICompletionProposal> proposals, List<SourceChange> changes) {
for (int i = 0; i < changes.size(); i++) {
SourceChange change = changes.get(i);
// Larger number = higher relevance. Ensure relevance higher than format or sort members.
ICompletionProposal uiProposal = ServiceUtils_NEW.toUI(change, 1000 - i);
if (uiProposal != null) {
proposals.add(uiProposal);
}
}
}
/**
* Adds service {@link CorrectionProposal} to the Eclipse {@link ICompletionProposal}s.
*/
static void addServiceProposals(List<ICompletionProposal> proposals,
CorrectionProposal[] serviceProposals) {
for (CorrectionProposal serviceProposal : serviceProposals) {
ICompletionProposal uiProposal = ServiceUtils_OLD.toUI(serviceProposal);
if (uiProposal != null) {
proposals.add(uiProposal);
}
}
}
private AssistContext context;
private DartEditor editor;
private ISourceViewer viewer;
private DartSelection selection;
private List<ICompletionProposal> proposals;
public synchronized ICompletionProposal[] getAssists(AssistContextUI contextUI) {
this.context = contextUI.getContext();
this.editor = contextUI.getEditor();
this.viewer = editor.getViewer();
this.selection = (DartSelection) editor.getSelectionProvider().getSelection();
proposals = Lists.newArrayList();
// add proposals
if (DartCoreDebug.ENABLE_ANALYSIS_SERVER) {
// add refactoring proposals
addProposal_convertGetterToMethodRefactoring();
// TODO(scheglov) add other proposals
// addProposal_renameRefactoring();
addProposal_format();
addProposal_sortMembers();
// ask server
ExecutionUtils.runLog(new RunnableEx() {
@Override
public void run() throws Exception {
final List<SourceChange> changes = Lists.newArrayList();
final CountDownLatch latch = new CountDownLatch(2);
String file = context.getFile();
DartCore.getAnalysisServer().edit_getAssists(
file,
context.getSelectionOffset(),
context.getSelectionLength(),
new GetAssistsConsumer() {
@Override
public void computedSourceChanges(List<SourceChange> _changes) {
changes.addAll(_changes);
latch.countDown();
}
@Override
public void onError(RequestError requestError) {
latch.countDown();
}
});
DartCore.getAnalysisServer().edit_getAvailableRefactorings(
file,
selection.getOffset(),
selection.getLength(),
new GetAvailableRefactoringsConsumer() {
@Override
public void computedRefactoringKinds(List<String> refactoringKinds) {
if (refactoringKinds.contains(RefactoringKind.CONVERT_METHOD_TO_GETTER)) {
addProposal_convertMethodToGetterRefactoring();
}
latch.countDown();
}
@Override
public void onError(RequestError requestError) {
latch.countDown();
}
});
Uninterruptibles.awaitUninterruptibly(latch, 2000, TimeUnit.MILLISECONDS);
addServerProposals(proposals, changes);
}
});
} else {
// not resolved yet
if (context == null) {
ExecutionUtils.runLog(new RunnableEx() {
@Override
public void run() throws Exception {
addUnresolvedProposals();
}
});
}
// use AssistContext
if (context != null) {
ExecutionUtils.runLog(new RunnableEx() {
@Override
public void run() throws Exception {
// add refactoring proposals
addProposal_convertGetterToMethodRefactoring();
addProposal_convertMethodToGetterRefactoring();
addProposal_renameRefactoring();
addProposal_format();
addProposal_sortMembers();
// ask services
com.google.dart.engine.services.correction.QuickAssistProcessor serviceProcessor;
serviceProcessor = CorrectionProcessors.getQuickAssistProcessor();
CorrectionProposal[] serviceProposals = serviceProcessor.getProposals(context);
addServiceProposals(proposals, serviceProposals);
}
});
}
}
// done
this.context = null;
this.editor = null;
this.selection = null;
try {
return proposals.toArray(new ICompletionProposal[proposals.size()]);
} finally {
proposals = null;
}
}
private void addProposal_convertGetterToMethodRefactoring() {
if (DartCoreDebug.ENABLE_ANALYSIS_SERVER) {
ConvertGetterToMethodAction_NEW action = new ConvertGetterToMethodAction_NEW(editor);
action.selectionChanged(selection);
if (action.isEnabled()) {
proposals.add(new ConvertGetterToMethodRefactoringProposal_NEW(action));
}
} else {
ConvertGetterToMethodAction_OLD action = new ConvertGetterToMethodAction_OLD(editor);
action.update(selection);
if (action.isEnabled()) {
proposals.add(new ConvertGetterToMethodRefactoringProposal_OLD(action, selection));
}
}
}
private void addProposal_convertMethodToGetterRefactoring() {
if (DartCoreDebug.ENABLE_ANALYSIS_SERVER) {
ConvertMethodToGetterAction_NEW action = new ConvertMethodToGetterAction_NEW(editor);
action.selectionChanged(selection);
if (action.isEnabled()) {
proposals.add(new ConvertMethodToGetterRefactoringProposal_NEW(action));
}
} else {
ConvertMethodToGetterAction_OLD action = new ConvertMethodToGetterAction_OLD(editor);
action.update(selection);
if (action.isEnabled()) {
proposals.add(new ConvertMethodToGetterRefactoringProposal_OLD(action, selection));
}
}
}
private void addProposal_format() {
IAction action = editor.getAction(DartEditorActionDefinitionIds.QUICK_FORMAT);
if (action != null && action.isEnabled()) {
proposals.add(new FormatProposal(action));
}
}
private void addProposal_renameRefactoring() {
RenameDartElementAction_OLD action = new RenameDartElementAction_OLD(editor);
action.update(selection);
if (action.isEnabled()) {
proposals.add(new RenameRefactoringProposal(action, selection));
}
}
private void addProposal_sortMembers() {
if (DartCoreDebug.ENABLE_ANALYSIS_SERVER) {
proposals.add(new SortMembersProposal_NEW(editor));
} else {
CompilationUnit unit = context.getCompilationUnit();
proposals.add(new SortMembersProposal_OLD(viewer, unit));
}
}
/**
* Adds proposals which can be produced for unresolved {@link CompilationUnit}.
*/
private void addUnresolvedProposals() throws Exception {
// prepare parsed CompilationUnit
CompilationUnit unit = editor.getInputUnit();
if (unit == null) {
return;
}
// prepare Source
Source inputSource = editor.getInputSource();
if (inputSource == null) {
return;
}
// prepare AnalysisContext
AnalysisContext analysisContext = editor.getInputAnalysisContext();
if (analysisContext == null) {
return;
}
// ask for corrections
CorrectionProposal[] serviceProposals = CorrectionProcessors.getQuickAssistProcessor().getProposals(
analysisContext,
inputSource,
unit,
selection.getOffset());
addServiceProposals(proposals, serviceProposals);
}
}