mirror of
https://github.com/dart-lang/sdk
synced 2024-09-05 00:13:50 +00:00
Add an initial version of a 'dart language-server' command.
Change-Id: Iffb8dedf7419a421e5282e09b4584e768c47e53f Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/176485 Commit-Queue: Devon Carew <devoncarew@google.com> Reviewed-by: Brian Wilkerson <brianwilkerson@google.com>
This commit is contained in:
parent
3bef6cf639
commit
ad3c3644a4
|
@ -13,8 +13,8 @@ not have a human-friendly user interface.
|
||||||
Clients (typically tools, such as an editor) are expected to run the analysis
|
Clients (typically tools, such as an editor) are expected to run the analysis
|
||||||
server in a separate process and communicate with it using a JSON protocol. The
|
server in a separate process and communicate with it using a JSON protocol. The
|
||||||
original protocol is specified in the file [`analysis_server/doc/api.html`][api]
|
original protocol is specified in the file [`analysis_server/doc/api.html`][api]
|
||||||
and (less complete) [Language Server Protocol][lsp_spec] support is documented
|
and [Language Server Protocol][lsp_spec] support is documented in
|
||||||
in [`tool/lsp_spec/README.md`](tool/lsp_spec/README.md).
|
[`tool/lsp_spec/README.md`](tool/lsp_spec/README.md).
|
||||||
|
|
||||||
## Features and bugs
|
## Features and bugs
|
||||||
|
|
||||||
|
|
|
@ -98,6 +98,11 @@ class Driver implements ServerStarter {
|
||||||
/// The path to the data cache.
|
/// The path to the data cache.
|
||||||
static const String CACHE_FOLDER = 'cache';
|
static const String CACHE_FOLDER = 'cache';
|
||||||
|
|
||||||
|
/// The name of the flag specifying the server protocol to use.
|
||||||
|
static const String SERVER_PROTOCOL = 'protocol';
|
||||||
|
static const String PROTOCOL_ANALYZER = 'analyzer';
|
||||||
|
static const String PROTOCOL_LSP = 'lsp';
|
||||||
|
|
||||||
/// The name of the flag to use the Language Server Protocol (LSP).
|
/// The name of the flag to use the Language Server Protocol (LSP).
|
||||||
static const String USE_LSP = 'lsp';
|
static const String USE_LSP = 'lsp';
|
||||||
|
|
||||||
|
@ -124,17 +129,26 @@ class Driver implements ServerStarter {
|
||||||
/// If [sendPort] is not null, assumes this is launched in an isolate and will
|
/// If [sendPort] is not null, assumes this is launched in an isolate and will
|
||||||
/// connect to the original isolate via an [IsolateChannel].
|
/// connect to the original isolate via an [IsolateChannel].
|
||||||
@override
|
@override
|
||||||
void start(List<String> arguments, [SendPort sendPort]) {
|
void start(
|
||||||
var parser = _createArgParser();
|
List<String> arguments, {
|
||||||
|
SendPort sendPort,
|
||||||
|
bool defaultToLsp = false,
|
||||||
|
}) {
|
||||||
|
var parser = createArgParser(defaultToLsp: defaultToLsp);
|
||||||
var results = parser.parse(arguments);
|
var results = parser.parse(arguments);
|
||||||
|
|
||||||
var analysisServerOptions = AnalysisServerOptions();
|
var analysisServerOptions = AnalysisServerOptions();
|
||||||
analysisServerOptions.newAnalysisDriverLog =
|
analysisServerOptions.newAnalysisDriverLog =
|
||||||
results[ANALYSIS_DRIVER_LOG] ?? results[ANALYSIS_DRIVER_LOG_ALIAS];
|
results[ANALYSIS_DRIVER_LOG] ?? results[ANALYSIS_DRIVER_LOG_ALIAS];
|
||||||
analysisServerOptions.clientId = results[CLIENT_ID];
|
analysisServerOptions.clientId = results[CLIENT_ID];
|
||||||
analysisServerOptions.useLanguageServerProtocol = results[USE_LSP];
|
if (results.wasParsed(USE_LSP)) {
|
||||||
// For clients that don't supply their own identifier, use a default based on
|
analysisServerOptions.useLanguageServerProtocol = results[USE_LSP];
|
||||||
// whether the server will run in LSP mode or not.
|
} else {
|
||||||
|
analysisServerOptions.useLanguageServerProtocol =
|
||||||
|
results[SERVER_PROTOCOL] == PROTOCOL_LSP;
|
||||||
|
}
|
||||||
|
// For clients that don't supply their own identifier, use a default based
|
||||||
|
// on whether the server will run in LSP mode or not.
|
||||||
analysisServerOptions.clientId ??=
|
analysisServerOptions.clientId ??=
|
||||||
analysisServerOptions.useLanguageServerProtocol
|
analysisServerOptions.useLanguageServerProtocol
|
||||||
? 'unknown.client.lsp'
|
? 'unknown.client.lsp'
|
||||||
|
@ -478,11 +492,95 @@ class Driver implements ServerStarter {
|
||||||
return runZoned(callback, zoneSpecification: zoneSpecification);
|
return runZoned(callback, zoneSpecification: zoneSpecification);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
DartSdk _createDefaultSdk(String defaultSdkPath) {
|
||||||
|
var resourceProvider = PhysicalResourceProvider.INSTANCE;
|
||||||
|
return FolderBasedDartSdk(
|
||||||
|
resourceProvider,
|
||||||
|
resourceProvider.getFolder(defaultSdkPath),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Constructs a uuid combining the current date and a random integer.
|
||||||
|
String _generateUuidString() {
|
||||||
|
var millisecondsSinceEpoch = DateTime.now().millisecondsSinceEpoch;
|
||||||
|
var random = Random().nextInt(0x3fffffff);
|
||||||
|
return '$millisecondsSinceEpoch$random';
|
||||||
|
}
|
||||||
|
|
||||||
|
String _getSdkPath(ArgResults args) {
|
||||||
|
if (args[DART_SDK] != null) {
|
||||||
|
return args[DART_SDK];
|
||||||
|
} else if (args[DART_SDK_ALIAS] != null) {
|
||||||
|
return args[DART_SDK_ALIAS];
|
||||||
|
} else {
|
||||||
|
return getSdkPath();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Print information about how to use the server.
|
||||||
|
void _printUsage(
|
||||||
|
ArgParser parser,
|
||||||
|
telemetry.Analytics analytics, {
|
||||||
|
bool fromHelp = false,
|
||||||
|
}) {
|
||||||
|
print('Usage: $BINARY_NAME [flags]');
|
||||||
|
print('');
|
||||||
|
print('Supported flags are:');
|
||||||
|
print(parser.usage);
|
||||||
|
|
||||||
|
if (telemetry.SHOW_ANALYTICS_UI) {
|
||||||
|
// Print analytics status and information.
|
||||||
|
if (fromHelp) {
|
||||||
|
print('');
|
||||||
|
print(telemetry.analyticsNotice);
|
||||||
|
}
|
||||||
|
print('');
|
||||||
|
print(telemetry.createAnalyticsStatusMessage(analytics.enabled,
|
||||||
|
command: ANALYTICS_FLAG));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Read the UUID from disk, generating and storing a new one if necessary.
|
||||||
|
String _readUuid(InstrumentationService service) {
|
||||||
|
final instrumentationLocation =
|
||||||
|
PhysicalResourceProvider.INSTANCE.getStateLocation('.instrumentation');
|
||||||
|
if (instrumentationLocation == null) {
|
||||||
|
return _generateUuidString();
|
||||||
|
}
|
||||||
|
var uuidFile = File(instrumentationLocation.getChild('uuid.txt').path);
|
||||||
|
try {
|
||||||
|
if (uuidFile.existsSync()) {
|
||||||
|
var uuid = uuidFile.readAsStringSync();
|
||||||
|
if (uuid != null && uuid.length > 5) {
|
||||||
|
return uuid;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (exception, stackTrace) {
|
||||||
|
service.logException(exception, stackTrace);
|
||||||
|
}
|
||||||
|
var uuid = _generateUuidString();
|
||||||
|
try {
|
||||||
|
uuidFile.parent.createSync(recursive: true);
|
||||||
|
uuidFile.writeAsStringSync(uuid);
|
||||||
|
} catch (exception, stackTrace) {
|
||||||
|
service.logException(exception, stackTrace);
|
||||||
|
// Slightly alter the uuid to indicate it was not persisted
|
||||||
|
uuid = 'temp-$uuid';
|
||||||
|
}
|
||||||
|
return uuid;
|
||||||
|
}
|
||||||
|
|
||||||
/// Create and return the parser used to parse the command-line arguments.
|
/// Create and return the parser used to parse the command-line arguments.
|
||||||
ArgParser _createArgParser() {
|
static ArgParser createArgParser({
|
||||||
var parser = ArgParser();
|
int usageLineLength,
|
||||||
parser.addFlag(HELP_OPTION,
|
bool includeHelpFlag = true,
|
||||||
abbr: 'h', negatable: false, help: 'Print this usage information.');
|
bool defaultToLsp = false,
|
||||||
|
}) {
|
||||||
|
var parser = ArgParser(usageLineLength: usageLineLength);
|
||||||
|
if (includeHelpFlag) {
|
||||||
|
parser.addFlag(HELP_OPTION,
|
||||||
|
abbr: 'h', negatable: false, help: 'Print this usage information.');
|
||||||
|
}
|
||||||
parser.addOption(CLIENT_ID,
|
parser.addOption(CLIENT_ID,
|
||||||
valueHelp: 'name',
|
valueHelp: 'name',
|
||||||
help: 'An identifier for the analysis server client.');
|
help: 'An identifier for the analysis server client.');
|
||||||
|
@ -495,10 +593,28 @@ class Driver implements ServerStarter {
|
||||||
parser.addOption(CACHE_FOLDER,
|
parser.addOption(CACHE_FOLDER,
|
||||||
valueHelp: 'path',
|
valueHelp: 'path',
|
||||||
help: 'Override the location of the analysis server\'s cache.');
|
help: 'Override the location of the analysis server\'s cache.');
|
||||||
|
|
||||||
|
parser.addOption(
|
||||||
|
SERVER_PROTOCOL,
|
||||||
|
defaultsTo: defaultToLsp ? PROTOCOL_LSP : PROTOCOL_ANALYZER,
|
||||||
|
valueHelp: 'protocol',
|
||||||
|
allowed: [PROTOCOL_LSP, PROTOCOL_ANALYZER],
|
||||||
|
allowedHelp: {
|
||||||
|
PROTOCOL_LSP: 'The Language Server Protocol '
|
||||||
|
'(https://microsoft.github.io/language-server-protocol)',
|
||||||
|
PROTOCOL_ANALYZER: 'Dart\'s analysis server protocol '
|
||||||
|
'(https://dart.dev/go/analysis-server-protocol)',
|
||||||
|
},
|
||||||
|
help:
|
||||||
|
'Specify the protocol to use to communicate with the analysis server.',
|
||||||
|
);
|
||||||
|
// This option is hidden but still accepted; it's effectively translated to
|
||||||
|
// the 'protocol' option above.
|
||||||
parser.addFlag(USE_LSP,
|
parser.addFlag(USE_LSP,
|
||||||
defaultsTo: false,
|
defaultsTo: false,
|
||||||
negatable: false,
|
negatable: false,
|
||||||
help: 'Whether to use the Language Server Protocol (LSP).');
|
help: 'Whether to use the Language Server Protocol (LSP).',
|
||||||
|
hide: true);
|
||||||
|
|
||||||
parser.addSeparator('Server diagnostics:');
|
parser.addSeparator('Server diagnostics:');
|
||||||
|
|
||||||
|
@ -584,84 +700,6 @@ class Driver implements ServerStarter {
|
||||||
return parser;
|
return parser;
|
||||||
}
|
}
|
||||||
|
|
||||||
DartSdk _createDefaultSdk(String defaultSdkPath) {
|
|
||||||
var resourceProvider = PhysicalResourceProvider.INSTANCE;
|
|
||||||
return FolderBasedDartSdk(
|
|
||||||
resourceProvider,
|
|
||||||
resourceProvider.getFolder(defaultSdkPath),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Constructs a uuid combining the current date and a random integer.
|
|
||||||
String _generateUuidString() {
|
|
||||||
var millisecondsSinceEpoch = DateTime.now().millisecondsSinceEpoch;
|
|
||||||
var random = Random().nextInt(0x3fffffff);
|
|
||||||
return '$millisecondsSinceEpoch$random';
|
|
||||||
}
|
|
||||||
|
|
||||||
String _getSdkPath(ArgResults args) {
|
|
||||||
if (args[DART_SDK] != null) {
|
|
||||||
return args[DART_SDK];
|
|
||||||
} else if (args[DART_SDK_ALIAS] != null) {
|
|
||||||
return args[DART_SDK_ALIAS];
|
|
||||||
} else {
|
|
||||||
return getSdkPath();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Print information about how to use the server.
|
|
||||||
void _printUsage(
|
|
||||||
ArgParser parser,
|
|
||||||
telemetry.Analytics analytics, {
|
|
||||||
bool fromHelp = false,
|
|
||||||
}) {
|
|
||||||
print('Usage: $BINARY_NAME [flags]');
|
|
||||||
print('');
|
|
||||||
print('Supported flags are:');
|
|
||||||
print(parser.usage);
|
|
||||||
|
|
||||||
if (telemetry.SHOW_ANALYTICS_UI) {
|
|
||||||
// Print analytics status and information.
|
|
||||||
if (fromHelp) {
|
|
||||||
print('');
|
|
||||||
print(telemetry.analyticsNotice);
|
|
||||||
}
|
|
||||||
print('');
|
|
||||||
print(telemetry.createAnalyticsStatusMessage(analytics.enabled,
|
|
||||||
command: ANALYTICS_FLAG));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Read the UUID from disk, generating and storing a new one if necessary.
|
|
||||||
String _readUuid(InstrumentationService service) {
|
|
||||||
final instrumentationLocation =
|
|
||||||
PhysicalResourceProvider.INSTANCE.getStateLocation('.instrumentation');
|
|
||||||
if (instrumentationLocation == null) {
|
|
||||||
return _generateUuidString();
|
|
||||||
}
|
|
||||||
var uuidFile = File(instrumentationLocation.getChild('uuid.txt').path);
|
|
||||||
try {
|
|
||||||
if (uuidFile.existsSync()) {
|
|
||||||
var uuid = uuidFile.readAsStringSync();
|
|
||||||
if (uuid != null && uuid.length > 5) {
|
|
||||||
return uuid;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (exception, stackTrace) {
|
|
||||||
service.logException(exception, stackTrace);
|
|
||||||
}
|
|
||||||
var uuid = _generateUuidString();
|
|
||||||
try {
|
|
||||||
uuidFile.parent.createSync(recursive: true);
|
|
||||||
uuidFile.writeAsStringSync(uuid);
|
|
||||||
} catch (exception, stackTrace) {
|
|
||||||
service.logException(exception, stackTrace);
|
|
||||||
// Slightly alter the uuid to indicate it was not persisted
|
|
||||||
uuid = 'temp-$uuid';
|
|
||||||
}
|
|
||||||
return uuid;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Perform log files rolling.
|
/// Perform log files rolling.
|
||||||
///
|
///
|
||||||
/// Rename existing files with names `[path].(x)` to `[path].(x+1)`.
|
/// Rename existing files with names `[path].(x)` to `[path].(x+1)`.
|
||||||
|
|
|
@ -31,5 +31,6 @@ abstract class ServerStarter {
|
||||||
set instrumentationService(InstrumentationService service);
|
set instrumentationService(InstrumentationService service);
|
||||||
|
|
||||||
/// Use the given command-line [arguments] to start this server.
|
/// Use the given command-line [arguments] to start this server.
|
||||||
void start(List<String> arguments, [SendPort sendPort]);
|
void start(List<String> arguments,
|
||||||
|
{SendPort sendPort, bool defaultToLsp = false});
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,6 +20,7 @@ import 'src/commands/analyze.dart';
|
||||||
import 'src/commands/compile.dart';
|
import 'src/commands/compile.dart';
|
||||||
import 'src/commands/create.dart';
|
import 'src/commands/create.dart';
|
||||||
import 'src/commands/fix.dart';
|
import 'src/commands/fix.dart';
|
||||||
|
import 'src/commands/language_server.dart';
|
||||||
import 'src/commands/run.dart';
|
import 'src/commands/run.dart';
|
||||||
import 'src/commands/test.dart';
|
import 'src/commands/test.dart';
|
||||||
import 'src/core.dart';
|
import 'src/core.dart';
|
||||||
|
@ -96,6 +97,7 @@ class DartdevRunner extends CommandRunner<int> {
|
||||||
addCommand(CompileCommand(verbose: verbose));
|
addCommand(CompileCommand(verbose: verbose));
|
||||||
addCommand(FixCommand(verbose: verbose));
|
addCommand(FixCommand(verbose: verbose));
|
||||||
addCommand(FormatCommand(verbose: verbose));
|
addCommand(FormatCommand(verbose: verbose));
|
||||||
|
addCommand(LanguageServerCommand(verbose: verbose));
|
||||||
addCommand(MigrateCommand(verbose: verbose));
|
addCommand(MigrateCommand(verbose: verbose));
|
||||||
addCommand(pubCommand());
|
addCommand(pubCommand());
|
||||||
addCommand(RunCommand(verbose: verbose));
|
addCommand(RunCommand(verbose: verbose));
|
||||||
|
|
53
pkg/dartdev/lib/src/commands/language_server.dart
Normal file
53
pkg/dartdev/lib/src/commands/language_server.dart
Normal file
|
@ -0,0 +1,53 @@
|
||||||
|
// Copyright (c) 2021, 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:io' as io;
|
||||||
|
|
||||||
|
import 'package:analysis_server/src/server/driver.dart' as server_driver;
|
||||||
|
import 'package:args/args.dart';
|
||||||
|
|
||||||
|
import '../core.dart';
|
||||||
|
import '../utils.dart';
|
||||||
|
|
||||||
|
class LanguageServerCommand extends DartdevCommand {
|
||||||
|
static const String commandName = 'language-server';
|
||||||
|
|
||||||
|
static const String commandDescription = '''
|
||||||
|
Start Dart's analysis server.
|
||||||
|
|
||||||
|
This is a long-running process used to provide language services to IDEs and other tooling clients.
|
||||||
|
|
||||||
|
It communicates over stdin and stdout and provides services like code completion, errors and warnings, and refactorings. This command is generally not user-facing but consumed by higher level tools.
|
||||||
|
|
||||||
|
For more information about the server's capabilities and configuration, see:
|
||||||
|
|
||||||
|
https://github.com/dart-lang/sdk/tree/master/pkg/analysis_server''';
|
||||||
|
|
||||||
|
LanguageServerCommand({bool verbose = false})
|
||||||
|
: super(commandName, commandDescription, hidden: !verbose);
|
||||||
|
|
||||||
|
@override
|
||||||
|
ArgParser createArgParser() {
|
||||||
|
return server_driver.Driver.createArgParser(
|
||||||
|
usageLineLength: dartdevUsageLineLength,
|
||||||
|
includeHelpFlag: false,
|
||||||
|
defaultToLsp: true,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<int> run() async {
|
||||||
|
final driver = server_driver.Driver();
|
||||||
|
driver.start(
|
||||||
|
argResults.arguments,
|
||||||
|
defaultToLsp: true,
|
||||||
|
);
|
||||||
|
|
||||||
|
// The server will continue to run past the return from this method.
|
||||||
|
//
|
||||||
|
// On an error on startup, the server will set the dart:io exitCode value
|
||||||
|
// (or, call exit() directly).
|
||||||
|
return io.exitCode;
|
||||||
|
}
|
||||||
|
}
|
|
@ -6,6 +6,8 @@ publish_to: none
|
||||||
environment:
|
environment:
|
||||||
sdk: '>=2.6.0 <3.0.0'
|
sdk: '>=2.6.0 <3.0.0'
|
||||||
dependencies:
|
dependencies:
|
||||||
|
analysis_server:
|
||||||
|
path: ../analysis_server
|
||||||
analysis_server_client:
|
analysis_server_client:
|
||||||
path: ../analysis_server_client
|
path: ../analysis_server_client
|
||||||
analyzer:
|
analyzer:
|
||||||
|
@ -22,8 +24,7 @@ dependencies:
|
||||||
path: ../nnbd_migration
|
path: ../nnbd_migration
|
||||||
path: ^1.0.0
|
path: ^1.0.0
|
||||||
pedantic: ^1.9.0
|
pedantic: ^1.9.0
|
||||||
pub:
|
pub: any
|
||||||
path: ../../third_party/pkg/pub
|
|
||||||
stagehand: any
|
stagehand: any
|
||||||
telemetry:
|
telemetry:
|
||||||
path: ../telemetry
|
path: ../telemetry
|
||||||
|
|
101
pkg/dartdev/test/commands/language_server_test.dart
Normal file
101
pkg/dartdev/test/commands/language_server_test.dart
Normal file
|
@ -0,0 +1,101 @@
|
||||||
|
// Copyright (c) 2021, 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:convert';
|
||||||
|
import 'dart:io';
|
||||||
|
|
||||||
|
import 'package:test/test.dart';
|
||||||
|
|
||||||
|
import '../utils.dart' as utils;
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
group(
|
||||||
|
'language-server',
|
||||||
|
defineLanguageServerTests,
|
||||||
|
timeout: utils.longTimeout,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
void defineLanguageServerTests() {
|
||||||
|
utils.TestProject project;
|
||||||
|
Process process;
|
||||||
|
|
||||||
|
tearDown(() {
|
||||||
|
project?.dispose();
|
||||||
|
process?.kill();
|
||||||
|
});
|
||||||
|
|
||||||
|
Future runWithLsp(List<String> args) async {
|
||||||
|
project = utils.project();
|
||||||
|
|
||||||
|
process = await project.start(args);
|
||||||
|
|
||||||
|
final Stream<String> inStream =
|
||||||
|
process.stdout.transform<String>(utf8.decoder);
|
||||||
|
|
||||||
|
// Send an LSP init.
|
||||||
|
final String message = jsonEncode({
|
||||||
|
'jsonrpc': '2.0',
|
||||||
|
'id': 1,
|
||||||
|
'method': 'initialize',
|
||||||
|
'params': {
|
||||||
|
'processId': pid,
|
||||||
|
'clientInfo': {'name': 'dart-cli-tester'},
|
||||||
|
'capabilities': {},
|
||||||
|
'rootUri': project.dir.uri.toString(),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
process.stdin.write('Content-Length: ${message.length}\r\n');
|
||||||
|
process.stdin.write('\r\n');
|
||||||
|
process.stdin.write(message);
|
||||||
|
|
||||||
|
List<String> responses = await inStream.take(2).toList();
|
||||||
|
expect(responses, hasLength(2));
|
||||||
|
|
||||||
|
expect(responses[0], startsWith('Content-Length: '));
|
||||||
|
|
||||||
|
final json = jsonDecode(responses[1]);
|
||||||
|
expect(json['id'], 1);
|
||||||
|
expect(json['result'], isNotNull);
|
||||||
|
final result = json['result'];
|
||||||
|
expect(result['capabilities'], isNotNull);
|
||||||
|
expect(result['serverInfo'], isNotNull);
|
||||||
|
final serverInfo = result['serverInfo'];
|
||||||
|
expect(serverInfo['name'], isNotEmpty);
|
||||||
|
|
||||||
|
process.kill();
|
||||||
|
process = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
test('protocol default', () async {
|
||||||
|
return runWithLsp(['language-server']);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('protocol lsp', () async {
|
||||||
|
return runWithLsp(['language-server', '--protocol=lsp']);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('protocol analyzer', () async {
|
||||||
|
project = utils.project();
|
||||||
|
|
||||||
|
process = await project.start(['language-server', '--protocol=analyzer']);
|
||||||
|
|
||||||
|
final Stream<String> inStream = process.stdout
|
||||||
|
.transform<String>(utf8.decoder)
|
||||||
|
.transform<String>(const LineSplitter());
|
||||||
|
|
||||||
|
final line = await inStream.first;
|
||||||
|
final json = jsonDecode(line);
|
||||||
|
|
||||||
|
expect(json['event'], 'server.connected');
|
||||||
|
expect(json['params'], isNotNull);
|
||||||
|
final params = json['params'];
|
||||||
|
expect(params['version'], isNotEmpty);
|
||||||
|
expect(params['pid'], isNot(0));
|
||||||
|
|
||||||
|
process.kill();
|
||||||
|
process = null;
|
||||||
|
});
|
||||||
|
}
|
|
@ -12,6 +12,7 @@ import 'commands/fix_test.dart' as fix;
|
||||||
import 'commands/flag_test.dart' as flag;
|
import 'commands/flag_test.dart' as flag;
|
||||||
import 'commands/format_test.dart' as format;
|
import 'commands/format_test.dart' as format;
|
||||||
import 'commands/help_test.dart' as help;
|
import 'commands/help_test.dart' as help;
|
||||||
|
import 'commands/language_server_test.dart' as language_server;
|
||||||
import 'commands/migrate_test.dart' as migrate;
|
import 'commands/migrate_test.dart' as migrate;
|
||||||
import 'commands/pub_test.dart' as pub;
|
import 'commands/pub_test.dart' as pub;
|
||||||
import 'commands/run_test.dart' as run;
|
import 'commands/run_test.dart' as run;
|
||||||
|
@ -39,6 +40,7 @@ void main() {
|
||||||
help.main();
|
help.main();
|
||||||
implicit_smoke.main();
|
implicit_smoke.main();
|
||||||
invalid_smoke.main();
|
invalid_smoke.main();
|
||||||
|
language_server.main();
|
||||||
migrate.main();
|
migrate.main();
|
||||||
no_such_file.main();
|
no_such_file.main();
|
||||||
pub.main();
|
pub.main();
|
||||||
|
|
|
@ -94,6 +94,20 @@ dev_dependencies:
|
||||||
environment: {if (logAnalytics) '_DARTDEV_LOG_ANALYTICS': 'true'});
|
environment: {if (logAnalytics) '_DARTDEV_LOG_ANALYTICS': 'true'});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<Process> start(
|
||||||
|
List<String> arguments, {
|
||||||
|
String workingDir,
|
||||||
|
}) {
|
||||||
|
return Process.start(
|
||||||
|
Platform.resolvedExecutable,
|
||||||
|
[
|
||||||
|
'--no-analytics',
|
||||||
|
...arguments,
|
||||||
|
],
|
||||||
|
workingDirectory: workingDir ?? dir.path,
|
||||||
|
environment: {if (logAnalytics) '_DARTDEV_LOG_ANALYTICS': 'true'});
|
||||||
|
}
|
||||||
|
|
||||||
String _sdkRootPath;
|
String _sdkRootPath;
|
||||||
|
|
||||||
/// Return the root of the SDK.
|
/// Return the root of the SDK.
|
||||||
|
|
Loading…
Reference in a new issue