blob: dbba4ea0f3ed5840c3f5bd598439956e911821f3 [file] [log] [blame]
// Copyright (c) 2018, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
import 'dart:async';
import 'package:analysis_server/src/protocol_server.dart'
show
CompletionSuggestion,
RuntimeCompletionExpression,
RuntimeCompletionVariable,
SourceEdit;
import 'package:analysis_server/src/provisional/completion/completion_core.dart';
import 'package:analysis_server/src/services/completion/completion_core.dart';
import 'package:analysis_server/src/services/completion/completion_performance.dart';
import 'package:analysis_server/src/services/completion/dart/completion_manager.dart';
import 'package:analyzer/dart/analysis/results.dart';
import 'package:analyzer/file_system/file_system.dart';
import 'package:analyzer/src/dart/analysis/driver.dart';
import 'package:analyzer/src/dart/analysis/file_state.dart';
import 'package:analyzer_plugin/protocol/protocol_common.dart';
import 'package:analyzer_plugin/utilities/change_builder/change_builder_dart.dart';
class RuntimeCompletionComputer {
final ResourceProvider resourceProvider;
final FileContentOverlay fileContentOverlay;
final AnalysisDriver analysisDriver;
final String code;
final int offset;
final String contextFile;
final int contextOffset;
final List<RuntimeCompletionVariable> variables;
final List<RuntimeCompletionExpression> expressions;
RuntimeCompletionComputer(
this.resourceProvider,
this.fileContentOverlay,
this.analysisDriver,
this.code,
this.offset,
this.contextFile,
this.contextOffset,
this.variables,
this.expressions);
Future<RuntimeCompletionResult> compute() async {
// TODO(brianwilkerson) Determine whether this await is necessary.
await null;
var contextResult = await analysisDriver.getResult(contextFile);
var session = contextResult.session;
const codeMarker = '__code_\_';
// Insert the code being completed at the context offset.
var changeBuilder = new DartChangeBuilder(session);
int nextImportPrefixIndex = 0;
await changeBuilder.addFileEdit(contextFile, (builder) {
builder.addInsertion(contextOffset, (builder) {
builder.writeln('{');
// TODO(scheglov) Use variables.
builder.write(codeMarker);
builder.writeln(';');
builder.writeln('}');
});
}, importPrefixGenerator: (uri) => '__prefix${nextImportPrefixIndex++}');
// Compute the patched context file content.
String targetCode = SourceEdit.applySequence(
contextResult.content,
changeBuilder.sourceChange.edits[0].edits,
);
// Insert the code being completed.
int targetOffset = targetCode.indexOf(codeMarker) + offset;
targetCode = targetCode.replaceAll(codeMarker, code);
// Update the context file content to include the code being completed.
// Then resolve it, and restore the file to its initial state.
ResolvedUnitResult targetResult;
String contentFileOverlay = fileContentOverlay[contextFile];
try {
fileContentOverlay[contextFile] = targetCode;
analysisDriver.changeFile(contextFile);
targetResult = await analysisDriver.getResult(contextFile);
} finally {
fileContentOverlay[contextFile] = contentFileOverlay;
analysisDriver.changeFile(contextFile);
}
CompletionContributor contributor = new DartCompletionManager();
CompletionRequestImpl request = new CompletionRequestImpl(
targetResult,
targetOffset,
new CompletionPerformance(),
);
var suggestions = await contributor.computeSuggestions(request);
// Remove completions with synthetic import prefixes.
suggestions.removeWhere((s) => s.completion.startsWith('__prefix'));
// TODO(scheglov) Add support for expressions.
var expressions = <RuntimeCompletionExpression>[];
return new RuntimeCompletionResult(expressions, suggestions);
}
}
/// The result of performing runtime completion.
class RuntimeCompletionResult {
final List<RuntimeCompletionExpression> expressions;
final List<CompletionSuggestion> suggestions;
RuntimeCompletionResult(this.expressions, this.suggestions);
}