[analysis_server] Prepare server for handling multiple URI schemes

This is a slight refactor that should not change any behaviour extracted to make a future CL smaller. It:

1. replaces the previous `dartFiles` filter that was specifically for the `file://` scheme with a `List` that can be added to in future.
2. wraps calls to pathContext.fromUri() and pathContext.toUri() in all LSP server to go through a new class (`ClientUriConverter`) that will be extended to support mapping between to custom URI schemes (instead of `file:///`) for generated files.

Change-Id: Ie8eadcca3cfd708e4dfde07c22d411101cc9ca0b
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/346540
Reviewed-by: Brian Wilkerson <brianwilkerson@google.com>
Commit-Queue: Brian Wilkerson <brianwilkerson@google.com>
Reviewed-by: Phil Quitslund <pquitslund@google.com>
This commit is contained in:
Danny Tuppeny 2024-01-16 21:28:29 +00:00 committed by Commit Queue
parent 017749f2fe
commit da07f7a3e2
37 changed files with 175 additions and 59 deletions

View file

@ -36,6 +36,7 @@ import 'package:analysis_server/src/services/search/search_engine_internal.dart'
import 'package:analysis_server/src/services/user_prompts/dart_fix_prompt_manager.dart';
import 'package:analysis_server/src/services/user_prompts/survey_manager.dart';
import 'package:analysis_server/src/services/user_prompts/user_prompts.dart';
import 'package:analysis_server/src/utilities/client_uri_converter.dart';
import 'package:analysis_server/src/utilities/file_string_sink.dart';
import 'package:analysis_server/src/utilities/null_string_sink.dart';
import 'package:analysis_server/src/utilities/process.dart';
@ -91,6 +92,16 @@ abstract class AnalysisServer {
/// A flag indicating whether plugins are supported in this build.
static final bool supportsPlugins = true;
/// The full set of URI schemes that the server can support.
///
/// Which schemes are valid for a given server invocation may depend on the
/// clients capabilities so being present in this set does not necessarily
/// mean the scheme is valid to send to the client.
///
/// The [uriConverter] handles mapping of internal analyzer file
/// paths/references to URIs and back.
static const supportedUriSchemes = {'file'};
/// The options of this server instance.
AnalysisServerOptions options;
@ -212,6 +223,10 @@ abstract class AnalysisServer {
/// the last idle state.
final Set<String> filesResolvedSinceLastIdle = {};
/// A converter to change incoming client URIs into analyzer file references
/// (and back).
ClientUriConverter uriConverter;
AnalysisServer(
this.options,
this.sdkManager,
@ -227,6 +242,8 @@ abstract class AnalysisServer {
bool enableBlazeWatcher = false,
DartFixPromptManager? dartFixPromptManager,
}) : resourceProvider = OverlayResourceProvider(baseResourceProvider),
uriConverter =
ClientUriConverter.noop(baseResourceProvider.pathContext),
pubApi = PubApi(instrumentationService, httpClient,
Platform.environment['PUB_HOSTED_URL']) {
// We can only spawn processes (eg. to run pub commands) when backed by

View file

@ -3,6 +3,7 @@
// BSD-style license that can be found in the LICENSE file.
import 'package:analysis_server/lsp_protocol/protocol.dart';
import 'package:analysis_server/src/analysis_server.dart';
import 'package:analysis_server/src/services/refactoring/framework/refactoring_processor.dart';
/// The characters that will cause the editor to automatically commit the selected
@ -53,8 +54,10 @@ final analysisOptionsFile = TextDocumentFilterWithScheme(
final analyzingProgressToken = ProgressToken.t2('ANALYZING');
/// A [TextDocumentFilterWithScheme] for Dart file.
final dartFiles =
TextDocumentFilterWithScheme(language: 'dart', scheme: 'file');
final dartFiles = [
for (final scheme in AnalysisServer.supportedUriSchemes)
TextDocumentFilterWithScheme(language: 'dart', scheme: scheme)
];
final emptyWorkspaceEdit = WorkspaceEdit();

View file

@ -83,7 +83,7 @@ abstract class AbstractCodeActionsProducer
Diagnostic createDiagnostic(
LineInfo lineInfo, engine.ErrorsResultImpl result, AnalysisError error) {
return pluginToDiagnostic(
server.pathContext,
server.uriConverter,
(_) => lineInfo,
protocol.newAnalysisError_fromEngine(result, error),
supportedTags: supportedDiagnosticTags,
@ -133,7 +133,7 @@ abstract class AbstractCodeActionsProducer
return engine.ErrorsResultImpl(
session: session,
file: file,
uri: server.pathContext.toUri(path),
uri: server.uriConverter.toClientUri(path),
lineInfo: lineInfo,
isAugmentation: false,
isLibrary: true,

View file

@ -172,7 +172,7 @@ class DartCodeActionsProducer extends AbstractCodeActionsProducer {
final fixes = await fixContributor.computeFixes(context);
if (fixes.isNotEmpty) {
final diagnostic = toDiagnostic(
server.pathContext,
server.uriConverter,
unit,
error,
supportedTags: supportedDiagnosticTags,

View file

@ -80,7 +80,7 @@ class PluginCodeActionsProducer extends AbstractCodeActionsProducer {
Iterable<CodeActionWithPriority> _convertFixes(
plugin.AnalysisErrorFixes fixes) {
final diagnostic = pluginToDiagnostic(
server.pathContext,
server.uriConverter,
(_) => lineInfo,
fixes.error,
supportedTags: supportedDiagnosticTags,

View file

@ -77,7 +77,7 @@ class SuperHandler
return success(null);
}
return success(toLocation(pathContext, location, locationLineInfo));
return success(toLocation(uriConverter, location, locationLineInfo));
});
}
}

View file

@ -27,7 +27,7 @@ class CallHierarchyRegistrations extends FeatureRegistration
@override
ToJsonable? get options =>
CallHierarchyRegistrationOptions(documentSelector: [dartFiles]);
CallHierarchyRegistrationOptions(documentSelector: dartFiles);
@override
Method get registrationMethod => Method.textDocument_prepareCallHierarchy;
@ -393,7 +393,7 @@ mixin _CallHierarchyUtils on HandlerHelperMixin<AnalysisServer> {
name: item.displayName,
detail: item.containerName,
kind: toSymbolKind(supportedSymbolKinds, item.kind),
uri: pathContext.toUri(item.file),
uri: uriConverter.toClientUri(item.file),
range: sourceRangeToRange(lineInfo, item.codeRange),
selectionRange: sourceRangeToRange(lineInfo, item.nameRange),
);
@ -419,7 +419,7 @@ mixin _CallHierarchyUtils on HandlerHelperMixin<AnalysisServer> {
displayName: item.name,
containerName: item.detail,
kind: fromSymbolKind(item.kind),
file: pathContext.fromUri(item.uri),
file: uriConverter.fromClientUri(item.uri),
nameRange: nameRange.result,
codeRange: codeRange.result,
);

View file

@ -47,7 +47,7 @@ class ChangeWorkspaceFoldersHandler
List<String> _convertWorkspaceFolders(List<WorkspaceFolder> folders) {
return folders
.where((wf) => wf.uri.isScheme('file'))
.map((wf) => pathContext.fromUri(wf.uri))
.map((wf) => uriConverter.fromClientUri(wf.uri))
.toList();
}
}

View file

@ -834,7 +834,7 @@ class CompletionRegistrations extends FeatureRegistration
(
Method.textDocument_completion,
CompletionRegistrationOptions(
documentSelector: [dartFiles],
documentSelector: dartFiles,
triggerCharacters: dartCompletionTriggerCharacters,
allCommitCharacters:
previewCommitCharacters ? dartCompletionCommitCharacters : null,

View file

@ -148,8 +148,8 @@ class CompletionResolveHandler
final autoImportDisplayUri = libraryUri.isScheme('file')
// Compute the relative path and then put into a URI so the display
// always uses forward slashes (as a URI) regardless of platform.
? pathContext.toUri(pathContext.relative(
pathContext.fromUri(libraryUri),
? uriConverter.toClientUri(pathContext.relative(
uriConverter.fromClientUri(libraryUri),
from: pathContext.dirname(file),
))
: libraryUri;

View file

@ -217,7 +217,7 @@ class DefinitionHandler extends LspMessageHandler<TextDocumentPositionParams,
Location? _toLocation(
AnalysisNavigationParams mergedResults, NavigationTarget target) {
final targetFilePath = mergedResults.files[target.fileIndex];
final targetFileUri = pathContext.toUri(targetFilePath);
final targetFileUri = uriConverter.toClientUri(targetFilePath);
final targetLineInfo = server.getLineInfo(targetFilePath);
return targetLineInfo != null
? navigationTargetToLocation(targetFileUri, target, targetLineInfo)
@ -228,7 +228,7 @@ class DefinitionHandler extends LspMessageHandler<TextDocumentPositionParams,
LineInfo sourceLineInfo, NavigationTarget target) {
final region = mergedResults.regions.first;
final targetFilePath = mergedResults.files[target.fileIndex];
final targetFileUri = pathContext.toUri(targetFilePath);
final targetFileUri = uriConverter.toClientUri(targetFilePath);
final targetLineInfo = server.getLineInfo(targetFilePath);
return targetLineInfo != null

View file

@ -70,7 +70,7 @@ class DocumentColorRegistrations extends FeatureRegistration
@override
DocumentColorRegistrationOptions get options =>
DocumentColorRegistrationOptions(documentSelector: [dartFiles]);
DocumentColorRegistrationOptions(documentSelector: dartFiles);
@override
Method get registrationMethod => Method.textDocument_documentColor;

View file

@ -59,7 +59,7 @@ class DocumentLinkRegistrations extends FeatureRegistration
@override
ToJsonable? get options => DocumentLinkRegistrationOptions(
documentSelector: [dartFiles],
documentSelector: dartFiles,
resolveProvider: false,
);

View file

@ -115,7 +115,7 @@ class DocumentSymbolHandler extends SharedMessageHandler<DocumentSymbolParams,
} else {
// Otherwise, we need to use the original flat SymbolInformation.
final allSymbols = <SymbolInformation>[];
final documentUri = pathContext.toUri(path);
final documentUri = uriConverter.toClientUri(path);
// Adds a symbol and it's children recursively, supplying the parent
// name as required by SymbolInformation.

View file

@ -67,7 +67,7 @@ class FormatOnTypeRegistrations extends FeatureRegistration
@override
ToJsonable? get options {
return DocumentOnTypeFormattingRegistrationOptions(
documentSelector: [dartFiles], // This is currently Dart-specific
documentSelector: dartFiles, // This is currently Dart-specific
firstTriggerCharacter: dartTypeFormattingCharacters.first,
moreTriggerCharacter: dartTypeFormattingCharacters.skip(1).toList(),
);

View file

@ -65,7 +65,7 @@ class FormatRangeRegistrations extends FeatureRegistration
@override
ToJsonable? get options => DocumentRangeFormattingRegistrationOptions(
documentSelector: [dartFiles], // This is currently Dart-specific
documentSelector: dartFiles, // This is currently Dart-specific
);
@override

View file

@ -91,7 +91,7 @@ class ImplementationHandler
return null;
}
return Location(
uri: pathContext.toUri(unitElement.source.fullName),
uri: uriConverter.toClientUri(unitElement.source.fullName),
range: toRange(
unitElement.lineInfo,
element.nameOffset,

View file

@ -44,13 +44,13 @@ class InitializeMessageHandler
// Only file URIs are supported, but there's no way to signal this to
// the LSP client (and certainly not before initialization).
if (uri.isScheme('file')) {
workspacePaths.add(pathContext.fromUri(uri));
workspacePaths.add(uriConverter.fromClientUri(uri));
}
}
}
if (rootUri != null) {
if (rootUri.isScheme('file')) {
workspacePaths.add(pathContext.fromUri(rootUri));
workspacePaths.add(uriConverter.fromClientUri(rootUri));
}
} else if (rootPath != null) {
workspacePaths.add(rootPath);

View file

@ -64,7 +64,7 @@ class InlayHintRegistrations extends FeatureRegistration
@override
ToJsonable? get options => InlayHintRegistrationOptions(
documentSelector: [dartFiles],
documentSelector: dartFiles,
resolveProvider: false,
);

View file

@ -54,7 +54,7 @@ class ReferencesHandler
return convert(collector.targets, (NavigationTarget target) {
final targetFilePath = collector.files[target.fileIndex];
final targetFileUri = pathContext.toUri(targetFilePath);
final targetFileUri = uriConverter.toClientUri(targetFilePath);
final lineInfo = server.getLineInfo(targetFilePath);
return lineInfo != null
? navigationTargetToLocation(targetFileUri, target, lineInfo)
@ -88,7 +88,7 @@ class ReferencesHandler
return null;
}
return Location(
uri: pathContext.toUri(result.file),
uri: uriConverter.toClientUri(result.file),
range: toRange(
file.lineInfo,
result.sourceRange.offset,

View file

@ -251,7 +251,7 @@ class RenameHandler extends LspMessageHandler<RenameParams, WorkspaceEdit?>
if (shouldRename) {
final newPath = pathContext.join(folder, newFilename);
final renameEdit =
createRenameEdit(pathContext, declaringFile, newPath);
createRenameEdit(uriConverter, declaringFile, newPath);
workspaceEdit = mergeWorkspaceEdits([workspaceEdit, renameEdit]);
}
}

View file

@ -84,7 +84,7 @@ class SelectionRangeRegistrations extends FeatureRegistration
@override
ToJsonable? get options =>
SelectionRangeRegistrationOptions(documentSelector: [dartFiles]);
SelectionRangeRegistrationOptions(documentSelector: dartFiles);
@override
Method get registrationMethod => Method.textDocument_selectionRange;

View file

@ -133,7 +133,7 @@ class TypeDefinitionHandler extends SharedMessageHandler<TypeDefinitionParams,
/// Creates an LSP [Location] for the server [location].
Location _toLocation(plugin.Location location, LineInfo lineInfo) {
return Location(
uri: pathContext.toUri(location.file),
uri: uriConverter.toClientUri(location.file),
range: toRange(lineInfo, location.offset, location.length),
);
}
@ -161,7 +161,7 @@ class TypeDefinitionHandler extends SharedMessageHandler<TypeDefinitionParams,
return LocationLink(
originSelectionRange:
toRange(originLineInfo, originEntity.offset, originEntity.length),
targetUri: pathContext.toUri(targetLocation.file),
targetUri: uriConverter.toClientUri(targetLocation.file),
targetRange: codeRange,
targetSelectionRange: nameRange,
);
@ -200,7 +200,7 @@ class TypeDefinitionRegistrations extends FeatureRegistration
@override
ToJsonable? get options => TextDocumentRegistrationOptions(
documentSelector: [dartFiles], // This is currently Dart-specific
documentSelector: dartFiles, // This is currently Dart-specific
);
@override

View file

@ -81,7 +81,7 @@ class TypeHierarchyRegistrations extends FeatureRegistration
@override
ToJsonable? get options => TypeHierarchyRegistrationOptions(
documentSelector: [dartFiles],
documentSelector: dartFiles,
);
@override
@ -198,7 +198,7 @@ mixin _TypeHierarchyUtils on HandlerHelperMixin<AnalysisServer> {
return TypeHierarchyItem(
name: item.displayName,
kind: SymbolKind.Class,
uri: pathContext.toUri(item.file),
uri: uriConverter.toClientUri(item.file),
range: sourceRangeToRange(lineInfo, item.codeRange),
selectionRange: sourceRangeToRange(lineInfo, item.nameRange),
data: TypeHierarchyItemInfo(

View file

@ -102,7 +102,7 @@ class WorkspaceSymbolHandler extends SharedMessageHandler<WorkspaceSymbolParams,
declaration.codeLength,
);
final location = Location(
uri: pathContext.toUri(filePath),
uri: uriConverter.toClientUri(filePath),
range: range,
);

View file

@ -12,6 +12,7 @@ import 'package:analysis_server/src/lsp/handlers/handler_reject.dart';
import 'package:analysis_server/src/lsp/lsp_analysis_server.dart';
import 'package:analysis_server/src/lsp/progress.dart';
import 'package:analysis_server/src/request_handler_mixin.dart';
import 'package:analysis_server/src/utilities/client_uri_converter.dart';
import 'package:analyzer/dart/analysis/results.dart';
import 'package:analyzer/source/line_info.dart';
import 'package:analyzer/src/util/performance/operation_performance.dart';
@ -76,6 +77,8 @@ mixin HandlerHelperMixin<S extends AnalysisServer> {
S get server;
ClientUriConverter get uriConverter => server.uriConverter;
ErrorOr<T> analysisFailedError<T>(String path) => error<T>(
ServerErrorCodes.FileAnalysisFailed, 'Analysis failed for file', path);

View file

@ -305,7 +305,7 @@ class LspAnalysisServer extends AnalysisServer {
// Dart settings for each workspace folder.
for (final folder in folders)
ConfigurationItem(
scopeUri: pathContext.toUri(folder),
scopeUri: uriConverter.toClientUri(folder),
section: 'dart',
),
// Global Dart settings. This comes last to simplify matching up the
@ -362,7 +362,7 @@ class LspAnalysisServer extends AnalysisServer {
OptionalVersionedTextDocumentIdentifier getVersionedDocumentIdentifier(
String path) {
return OptionalVersionedTextDocumentIdentifier(
uri: pathContext.toUri(path), version: getDocumentVersion(path));
uri: uriConverter.toClientUri(path), version: getDocumentVersion(path));
}
@override
@ -621,7 +621,7 @@ class LspAnalysisServer extends AnalysisServer {
void publishClosingLabels(String path, List<ClosingLabel> labels) {
final params = PublishClosingLabelsParams(
uri: pathContext.toUri(path), labels: labels);
uri: uriConverter.toClientUri(path), labels: labels);
final message = NotificationMessage(
method: CustomMethods.publishClosingLabels,
params: params,
@ -643,7 +643,7 @@ class LspAnalysisServer extends AnalysisServer {
}
final params = PublishDiagnosticsParams(
uri: pathContext.toUri(path), diagnostics: errors);
uri: uriConverter.toClientUri(path), diagnostics: errors);
final message = NotificationMessage(
method: Method.textDocument_publishDiagnostics,
params: params,
@ -654,7 +654,7 @@ class LspAnalysisServer extends AnalysisServer {
void publishFlutterOutline(String path, FlutterOutline outline) {
final params = PublishFlutterOutlineParams(
uri: pathContext.toUri(path), outline: outline);
uri: uriConverter.toClientUri(path), outline: outline);
final message = NotificationMessage(
method: CustomMethods.publishFlutterOutline,
params: params,
@ -664,8 +664,8 @@ class LspAnalysisServer extends AnalysisServer {
}
void publishOutline(String path, Outline outline) {
final params =
PublishOutlineParams(uri: pathContext.toUri(path), outline: outline);
final params = PublishOutlineParams(
uri: uriConverter.toClientUri(path), outline: outline);
final message = NotificationMessage(
method: CustomMethods.publishOutline,
params: params,

View file

@ -18,6 +18,7 @@ import 'package:analysis_server/src/protocol_server.dart' as server
hide AnalysisError;
import 'package:analysis_server/src/services/completion/dart/feature_computer.dart';
import 'package:analysis_server/src/services/snippets/snippet.dart';
import 'package:analysis_server/src/utilities/client_uri_converter.dart';
import 'package:analysis_server/src/utilities/extensions/string.dart';
import 'package:analyzer/dart/analysis/results.dart' as server;
import 'package:analyzer/error/error.dart' as server;
@ -30,7 +31,6 @@ import 'package:analyzer/src/error/codes.dart';
import 'package:analyzer/src/utilities/extensions/collection.dart';
import 'package:analyzer_plugin/protocol/protocol_common.dart' as plugin;
import 'package:collection/collection.dart';
import 'package:path/path.dart' as path;
const languageSourceName = 'dart';
@ -109,13 +109,13 @@ lsp.WorkspaceEdit createPlainWorkspaceEdit(
/// Create a [WorkspaceEdit] that renames [oldPath] to [newPath].
WorkspaceEdit createRenameEdit(
path.Context pathContext, String oldPath, String newPath) {
ClientUriConverter uriConverter, String oldPath, String newPath) {
final changes =
<Either4<CreateFile, DeleteFile, RenameFile, TextDocumentEdit>>[];
final rename = RenameFile(
oldUri: pathContext.toUri(oldPath),
newUri: pathContext.toUri(newPath),
oldUri: uriConverter.toClientUri(oldPath),
newUri: uriConverter.toClientUri(newPath),
);
final renameUnion =
@ -549,7 +549,7 @@ lsp.LocationLink? navigationTargetToLocationLink(
}
lsp.Diagnostic pluginToDiagnostic(
path.Context pathContext,
ClientUriConverter uriConverter,
server.LineInfo? Function(String) getLineInfo,
plugin.AnalysisError error, {
required Set<lsp.DiagnosticTag>? supportedTags,
@ -560,7 +560,7 @@ lsp.Diagnostic pluginToDiagnostic(
if (contextMessages != null && contextMessages.isNotEmpty) {
relatedInformation = contextMessages
.map((message) => pluginToDiagnosticRelatedInformation(
pathContext, getLineInfo, message))
uriConverter, getLineInfo, message))
.whereNotNull()
.toList();
}
@ -598,11 +598,11 @@ lsp.Diagnostic pluginToDiagnostic(
}
lsp.DiagnosticRelatedInformation? pluginToDiagnosticRelatedInformation(
path.Context pathContext,
ClientUriConverter uriConverter,
server.LineInfo? Function(String) getLineInfo,
plugin.DiagnosticMessage message) {
final file = message.location.file;
final uri = pathContext.toUri(file);
final uri = uriConverter.toClientUri(file);
final lineInfo = getLineInfo(file);
// We shouldn't get context messages for something we can't get a LineInfo for
// but if we did, it's better to omit the context than fail to send the errors.
@ -1075,14 +1075,14 @@ lsp.CompletionItem toCompletionItem(
}
lsp.Diagnostic toDiagnostic(
path.Context pathContext,
ClientUriConverter uriConverter,
server.ResolvedUnitResult result,
server.AnalysisError error, {
required Set<lsp.DiagnosticTag> supportedTags,
required bool clientSupportsCodeDescription,
}) {
return pluginToDiagnostic(
pathContext,
uriConverter,
(_) => result.lineInfo,
server.newAnalysisError_fromEngine(result, error),
supportedTags: supportedTags,
@ -1170,10 +1170,10 @@ List<lsp.DocumentHighlight> toHighlights(
.toList();
}
lsp.Location toLocation(path.Context pathContext, server.Location location,
server.LineInfo lineInfo) =>
lsp.Location toLocation(ClientUriConverter uriConverter,
server.Location location, server.LineInfo lineInfo) =>
lsp.Location(
uri: pathContext.toUri(location.file),
uri: uriConverter.toClientUri(location.file),
range: toRange(
lineInfo,
location.offset,

View file

@ -22,7 +22,7 @@ class LspNotificationManager extends AbstractNotificationManager {
String filePath, List<protocol.AnalysisError> errors) {
final diagnostics = errors
.map((error) => pluginToDiagnostic(
pathContext,
server.uriConverter,
(path) => server.getLineInfo(path),
error,
supportedTags: server.lspClientCapabilities?.diagnosticTags,

View file

@ -67,7 +67,7 @@ abstract class FeatureRegistration {
/// functionality in most handlers.
List<TextDocumentFilterWithScheme> get fullySupportedTypes {
return {
dartFiles,
...dartFiles,
...pluginTypes,
}.toList();
}

View file

@ -0,0 +1,31 @@
// Copyright (c) 2024, 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:path/path.dart' as path;
/// A class for converting between internal analyzer file paths/references and
/// URIs used by clients.
///
/// The simplest form of this class simple translates between file paths and
/// `file://` URIs but depending on client capabilities some paths/URIs may be
/// re-written to support features like virtual files for macros.
class ClientUriConverter {
final path.Context _context;
/// Creates a converter that does nothing besides translation between file
/// paths and `file://` URIs.
ClientUriConverter.noop(this._context);
/// Converts a URI provided by the client into a file path/reference that can
/// be used by the analyzer.
String fromClientUri(Uri uri) {
return _context.fromUri(uri);
}
/// Converts a file path/reference from the analyzer into a URI to be sent to
/// the client.
Uri toClientUri(String filePath) {
return _context.toUri(filePath);
}
}

View file

@ -8,6 +8,7 @@ import 'package:analysis_server/lsp_protocol/protocol.dart';
import 'package:analysis_server/src/lsp/constants.dart';
import 'package:analysis_server/src/lsp/mapping.dart';
import 'package:analysis_server/src/services/completion/dart/feature_computer.dart';
import 'package:analysis_server/src/utilities/client_uri_converter.dart';
import 'package:analyzer/source/line_info.dart';
import 'package:collection/collection.dart';
import 'package:language_server_protocol/json_parsing.dart';
@ -822,6 +823,8 @@ mixin LspVerifyEditHelpersMixin on LspEditHelpersMixin {
String get projectFolderPath;
ClientUriConverter get uriConverter;
/// A function to get the current contents of a file to apply edits.
String? getCurrentFileContent(Uri uri);
@ -835,5 +838,5 @@ mixin LspVerifyEditHelpersMixin on LspEditHelpersMixin {
/// Formats a path relative to the project root always using forward slashes.
///
/// This is used in the text format for comparing edits.
String relativeUri(Uri uri) => relativePath(pathContext.fromUri(uri));
String relativeUri(Uri uri) => relativePath(uriConverter.fromClientUri(uri));
}

View file

@ -12,6 +12,7 @@ import 'package:analysis_server/src/lsp/lsp_analysis_server.dart';
import 'package:analysis_server/src/plugin/plugin_manager.dart';
import 'package:analysis_server/src/server/crash_reporting_attachments.dart';
import 'package:analysis_server/src/services/user_prompts/dart_fix_prompt_manager.dart';
import 'package:analysis_server/src/utilities/client_uri_converter.dart';
import 'package:analysis_server/src/utilities/mocks.dart';
import 'package:analyzer/instrumentation/instrumentation.dart';
import 'package:analyzer/src/dart/analysis/experiments.dart';
@ -70,6 +71,9 @@ abstract class AbstractLspAnalysisServerTest
@override
Stream<Message> get serverToClient => channel.serverToClient;
@override
ClientUriConverter get uriConverter => server.uriConverter;
DiscoveredPluginInfo configureTestPlugin({
plugin.ResponseResult? respondWith,
plugin.Notification? notification,

View file

@ -7,6 +7,7 @@ import 'dart:convert';
import 'package:analysis_server/lsp_protocol/protocol.dart';
import 'package:analysis_server/src/protocol/protocol_internal.dart';
import 'package:analysis_server/src/protocol_server.dart';
import 'package:analysis_server/src/utilities/client_uri_converter.dart';
import 'package:path/path.dart' as path;
import 'package:test/test.dart';
@ -34,6 +35,9 @@ abstract class LspOverLegacyTest extends PubPackageAnalysisServerTest
Uri get testFileUri => toUri(convertPath(testFilePath));
@override
ClientUriConverter get uriConverter => server.uriConverter;
Future<void> addOverlay(String filePath, String content) {
return handleSuccessfulRequest(
AnalysisUpdateContentParams({

View file

@ -0,0 +1,49 @@
// Copyright (c) 2024, 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/utilities/client_uri_converter.dart';
import 'package:test/test.dart';
import 'package:test_reflective_loader/test_reflective_loader.dart';
import '../../../tool/codebase/failing_tests.dart';
import '../../abstract_single_unit.dart';
void main() {
defineReflectiveSuite(() {
defineReflectiveTests(ClientUriConverterTest);
});
}
@reflectiveTest
class ClientUriConverterTest extends AbstractSingleUnitTest {
Future<void> test_noop_fromUri() async {
var converter = ClientUriConverter.noop(pathContext);
for (var fileUri in [
Uri.file(convertPath('/a/b.dart')),
Uri.file(convertPath('/a/b.txt')),
Uri.file(convertPath('/a/b.macro.dart')),
Uri.file(convertPath('/a/b')),
Uri.file(convertPath('/')),
]) {
// For no-op, should be the same as simple fromUri on pathContext.
expect(converter.fromClientUri(fileUri), pathContext.fromUri(fileUri));
}
}
Future<void> test_noop_toUri() async {
var converter = ClientUriConverter.noop(pathContext);
for (var filePath in [
convertPath('/a/b.dart'),
convertPath('/a/b.txt'),
convertPath('/a/b.macro.dart'),
convertPath('/a/b'),
convertPath('/'),
]) {
// For no-op, should be the same as simple toUri on pathContext.
expect(converter.toClientUri(filePath), pathContext.toUri(filePath));
}
}
}

View file

@ -4,6 +4,7 @@
import 'package:test_reflective_loader/test_reflective_loader.dart';
import 'client_uri_converter_test.dart' as client_uri_converter;
import 'extensions/test_all.dart' as extensions;
import 'flutter_test.dart' as flutter;
import 'import_analyzer_test.dart' as import_analyzer;
@ -15,6 +16,7 @@ import 'strings_test.dart' as strings;
void main() {
defineReflectiveSuite(() {
client_uri_converter.main();
extensions.main();
flutter.main();
import_analyzer.main();

View file

@ -56,7 +56,7 @@ final argParser = ArgParser()
'Download the latest version of the LSP spec before generating types');
final String languageServerProtocolPackagePath =
'$sdkRootPath/third_party/pkg/language_server_protocol';
path.join(sdkRootPath, 'third_party', 'pkg', 'language_server_protocol');
final String licenseComment = LineSplitter.split(
File(localLicensePath).readAsStringSync())