blob: 97ab5bf0b35810009bb4a1e4fcf15be2a9ac67fd [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 'package:analysis_server/src/protocol_server.dart'
show CompletionSuggestion, RuntimeCompletionExpression, SourceEdit;
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/overlay_file_system.dart';
import 'package:analyzer/src/dart/analysis/driver.dart';
import 'package:analyzer_plugin/utilities/change_builder/change_builder_core.dart';
class RuntimeCompletionComputer {
final OverlayResourceProvider resourceProvider;
final AnalysisDriver analysisDriver;
final String code;
final int offset;
final String contextPath;
final int contextOffset;
RuntimeCompletionComputer(this.resourceProvider, this.analysisDriver,
this.code, this.offset, this.contextPath, this.contextOffset);
Future<RuntimeCompletionResult> compute() async {
var contextResult = await analysisDriver.getResult2(contextPath);
if (contextResult is! ResolvedUnitResult) {
return RuntimeCompletionResult([], []);
}
var session = contextResult.session;
const codeMarker = '__code_\_';
// Insert the code being completed at the context offset.
var changeBuilder = ChangeBuilder(session: session);
var nextImportPrefixIndex = 0;
await changeBuilder.addDartFileEdit(contextPath, (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.
var targetCode = SourceEdit.applySequence(
contextResult.content!,
changeBuilder.sourceChange.edits[0].edits,
);
// Insert the code being completed.
var 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.
var targetResult = await _withContextFileContent(targetCode, () async {
return await analysisDriver.getResult2(contextPath);
});
if (targetResult is! ResolvedUnitResult) {
return RuntimeCompletionResult([], []);
}
var contributor = DartCompletionManager(
// dartdocDirectiveInfo: server.getDartdocDirectiveInfoFor(targetResult)
);
var request = CompletionRequestImpl(
targetResult,
targetOffset,
CompletionPerformance(),
);
var suggestions = await request.performance.runRequestOperation(
(performance) async {
return await contributor.computeSuggestions(
performance,
request,
);
},
);
// Remove completions with synthetic import prefixes.
suggestions.removeWhere((s) => s.completion.startsWith('__prefix'));
// TODO(scheglov) Add support for expressions.
var expressions = <RuntimeCompletionExpression>[];
return RuntimeCompletionResult(expressions, suggestions);
}
Future<R> _withContextFileContent<R>(
String newContent, Future<R> Function() f) async {
if (resourceProvider.hasOverlay(contextPath)) {
var contextFile = resourceProvider.getFile(contextPath);
var prevOverlayContent = contextFile.readAsStringSync();
var prevOverlayStamp = contextFile.modificationStamp;
try {
resourceProvider.setOverlay(
contextPath,
content: newContent,
modificationStamp: 0,
);
analysisDriver.changeFile(contextPath);
return await f();
} finally {
resourceProvider.setOverlay(
contextPath,
content: prevOverlayContent,
modificationStamp: prevOverlayStamp,
);
analysisDriver.changeFile(contextPath);
}
} else {
try {
resourceProvider.setOverlay(
contextPath,
content: newContent,
modificationStamp: 0,
);
analysisDriver.changeFile(contextPath);
return await f();
} finally {
resourceProvider.removeOverlay(contextPath);
analysisDriver.changeFile(contextPath);
}
}
}
}
/// The result of performing runtime completion.
class RuntimeCompletionResult {
final List<RuntimeCompletionExpression> expressions;
final List<CompletionSuggestion> suggestions;
RuntimeCompletionResult(this.expressions, this.suggestions);
}