Unverified Commit f8750b16 authored by Jonah Williams's avatar Jonah Williams Committed by GitHub

[flutter_tools] do not error doctor on missing vs code extension (#66780)

In cases where the VSCode plugins are not located, display links to where they can be downloaded but do not surface an error.
parent 94e83671
......@@ -78,7 +78,7 @@ class _DefaultDoctorValidatorsProvider implements DoctorValidatorsProvider {
final List<DoctorValidator> ideValidators = <DoctorValidator>[
...AndroidStudioValidator.allValidators(globals.config, globals.platform, globals.fs, globals.userMessages),
...IntelliJValidator.installedValidators(globals.fs, globals.platform),
...VsCodeValidator.installedValidators,
...VsCodeValidator.installedValidators(globals.fs, globals.platform),
];
final ProxyValidator proxyValidator = ProxyValidator(platform: globals.platform);
_validators = <DoctorValidator>[
......
......@@ -2,27 +2,27 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'package:meta/meta.dart';
import '../base/file_system.dart';
import '../base/platform.dart';
import '../base/utils.dart';
import '../base/version.dart';
import '../convert.dart';
import '../doctor.dart';
import '../globals.dart' as globals;
// Include VS Code insiders (useful for debugging).
const bool _includeInsiders = false;
const String extensionIdentifier = 'Dart-Code.flutter';
const String extensionMarketplaceUrl =
'https://marketplace.visualstudio.com/items?itemName=$extensionIdentifier';
class VsCode {
VsCode._(this.directory, this.extensionDirectory, { Version version, this.edition })
VsCode._(this.directory, this.extensionDirectory, { Version version, this.edition, @required FileSystem fileSystem})
: version = version ?? Version.unknown {
if (!globals.fs.isDirectorySync(directory)) {
if (!fileSystem.isDirectorySync(directory)) {
_validationMessages.add(ValidationMessage.error('VS Code not found at $directory'));
return;
} else {
......@@ -31,16 +31,18 @@ class VsCode {
// If the extensions directory doesn't exist at all, the listSync()
// below will fail, so just bail out early.
const ValidationMessage notInstalledMessage = ValidationMessage.error(
'Flutter extension not installed; install from\n$extensionMarketplaceUrl');
if (!globals.fs.isDirectorySync(extensionDirectory)) {
const ValidationMessage notInstalledMessage = ValidationMessage(
'Flutter extension can be installed from:',
contextUrl: extensionMarketplaceUrl,
);
if (!fileSystem.isDirectorySync(extensionDirectory)) {
_validationMessages.add(notInstalledMessage);
return;
}
// Check for presence of extension.
final String extensionIdentifierLower = extensionIdentifier.toLowerCase();
final Iterable<FileSystemEntity> extensionDirs = globals.fs
final Iterable<FileSystemEntity> extensionDirs = fileSystem
.directory(extensionDirectory)
.listSync()
.whereType<Directory>()
......@@ -49,7 +51,6 @@ class VsCode {
if (extensionDirs.isNotEmpty) {
final FileSystemEntity extensionDir = extensionDirs.first;
_isValid = true;
_extensionVersion = Version.parse(
extensionDir.basename.substring('$extensionIdentifier-'.length));
_validationMessages.add(ValidationMessage('Flutter extension version $_extensionVersion'));
......@@ -62,15 +63,16 @@ class VsCode {
String installPath,
String extensionDirectory, {
String edition,
@required FileSystem fileSystem,
}) {
final String packageJsonPath =
globals.fs.path.join(installPath, 'resources', 'app', 'package.json');
final String versionString = _getVersionFromPackageJson(packageJsonPath);
fileSystem.path.join(installPath, 'resources', 'app', 'package.json');
final String versionString = _getVersionFromPackageJson(packageJsonPath, fileSystem);
Version version;
if (versionString != null) {
version = Version.parse(versionString);
}
return VsCode._(installPath, extensionDirectory, version: version, edition: edition);
return VsCode._(installPath, extensionDirectory, version: version, edition: edition, fileSystem: fileSystem);
}
final String directory;
......@@ -78,11 +80,9 @@ class VsCode {
final Version version;
final String edition;
bool _isValid = false;
Version _extensionVersion;
final List<ValidationMessage> _validationMessages = <ValidationMessage>[];
bool get isValid => _isValid;
String get productName => 'VS Code' + (edition != null ? ', $edition' : '');
Iterable<ValidationMessage> get validationMessages => _validationMessages;
......@@ -92,13 +92,13 @@ class VsCode {
Platform platform,
) {
if (platform.isMacOS) {
return _installedMacOS(fileSystem);
return _installedMacOS(fileSystem, platform);
}
if (platform.isWindows) {
return _installedWindows(fileSystem, platform);
}
if (platform.isLinux) {
return _installedLinux(fileSystem);
return _installedLinux(fileSystem, platform);
}
// VS Code isn't supported on the other platforms.
return <VsCode>[];
......@@ -112,7 +112,7 @@ class VsCode {
// macOS Extensions:
// $HOME/.vscode/extensions
// $HOME/.vscode-insiders/extensions
static List<VsCode> _installedMacOS(FileSystem fileSystem) {
static List<VsCode> _installedMacOS(FileSystem fileSystem, Platform platform) {
return _findInstalled(<_VsCodeInstallLocation>[
_VsCodeInstallLocation(
fileSystem.path.join('/Applications', 'Visual Studio Code.app', 'Contents'),
......@@ -120,7 +120,7 @@ class VsCode {
),
_VsCodeInstallLocation(
fileSystem.path.join(
globals.fsUtils.homeDirPath,
FileSystemUtils(fileSystem: fileSystem, platform: platform).homeDirPath,
'Applications',
'Visual Studio Code.app',
'Contents',
......@@ -134,7 +134,7 @@ class VsCode {
),
_VsCodeInstallLocation(
fileSystem.path.join(
globals.fsUtils.homeDirPath,
FileSystemUtils(fileSystem: fileSystem, platform: platform).homeDirPath,
'Applications',
'Visual Studio Code - Insiders.app',
'Contents',
......@@ -142,7 +142,7 @@ class VsCode {
'.vscode-insiders',
isInsiders: true,
),
], fileSystem);
], fileSystem, platform);
}
// Windows:
......@@ -206,7 +206,7 @@ class VsCode {
isInsiders: true,
),
];
return _findInstalled(searchLocations, fileSystem);
return _findInstalled(searchLocations, fileSystem, platform);
}
// Linux:
......@@ -215,7 +215,7 @@ class VsCode {
// Linux Extensions:
// $HOME/.vscode/extensions
// $HOME/.vscode-insiders/extensions
static List<VsCode> _installedLinux(FileSystem fileSystem) {
static List<VsCode> _installedLinux(FileSystem fileSystem, Platform platform) {
return _findInstalled(<_VsCodeInstallLocation>[
const _VsCodeInstallLocation('/usr/share/code', '.vscode'),
const _VsCodeInstallLocation(
......@@ -223,12 +223,13 @@ class VsCode {
'.vscode-insiders',
isInsiders: true,
),
], fileSystem);
], fileSystem, platform);
}
static List<VsCode> _findInstalled(
List<_VsCodeInstallLocation> allLocations,
FileSystem fileSystem,
Platform platform,
) {
final Iterable<_VsCodeInstallLocation> searchLocations =
_includeInsiders
......@@ -240,7 +241,7 @@ class VsCode {
for (final _VsCodeInstallLocation searchLocation in searchLocations) {
if (fileSystem.isDirectorySync(searchLocation.installPath)) {
final String extensionDirectory = fileSystem.path.join(
globals.fsUtils.homeDirPath,
FileSystemUtils(fileSystem: fileSystem, platform: platform).homeDirPath,
searchLocation.extensionsFolder,
'extensions',
);
......@@ -248,6 +249,7 @@ class VsCode {
searchLocation.installPath,
extensionDirectory,
edition: searchLocation.edition,
fileSystem: fileSystem,
));
}
}
......@@ -259,16 +261,15 @@ class VsCode {
String toString() =>
'VS Code ($version)${_extensionVersion != Version.unknown ? ', Flutter ($_extensionVersion)' : ''}';
static String _getVersionFromPackageJson(String packageJsonPath) {
if (!globals.fs.isFileSync(packageJsonPath)) {
static String _getVersionFromPackageJson(String packageJsonPath, FileSystem fileSystem) {
if (!fileSystem.isFileSync(packageJsonPath)) {
return null;
}
final String jsonString = globals.fs.file(packageJsonPath).readAsStringSync();
final String jsonString = fileSystem.file(packageJsonPath).readAsStringSync();
try {
final Map<String, dynamic> jsonObject = castStringKeyedMap(json.decode(jsonString));
return jsonObject['version'] as String;
} on FormatException catch (err) {
globals.printTrace('Error parsing VSCode $packageJsonPath:\n$err');
} on FormatException {
return null;
}
}
......
......@@ -2,10 +2,11 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import '../base/file_system.dart';
import '../base/platform.dart';
import '../base/user_messages.dart';
import '../base/version.dart';
import '../doctor.dart';
import '../globals.dart' as globals;
import 'vscode.dart';
class VsCodeValidator extends DoctorValidator {
......@@ -13,9 +14,9 @@ class VsCodeValidator extends DoctorValidator {
final VsCode _vsCode;
static Iterable<DoctorValidator> get installedValidators {
static Iterable<DoctorValidator> installedValidators(FileSystem fileSystem, Platform platform) {
return VsCode
.allInstalled(globals.fs, globals.platform)
.allInstalled(fileSystem, platform)
.map<DoctorValidator>((VsCode vsCode) => VsCodeValidator(vsCode));
}
......@@ -25,12 +26,8 @@ class VsCodeValidator extends DoctorValidator {
? null
: userMessages.vsCodeVersion(_vsCode.version.toString());
final ValidationType validationType = _vsCode.isValid
? ValidationType.installed
: ValidationType.partial;
return ValidationResult(
validationType,
ValidationType.installed,
_vsCode.validationMessages.toList(),
statusInfo: vsCodeVersionText,
);
......
......@@ -188,7 +188,7 @@ void main() {
testUsingContext('vs code validator when extension missing', () async {
final ValidationResult result = await VsCodeValidatorTestTargets.installedWithoutExtension.validate();
expect(result.type, ValidationType.partial);
expect(result.type, ValidationType.installed);
expect(result.statusInfo, 'version 1.2.3');
expect(result.messages, hasLength(2));
......@@ -198,8 +198,9 @@ void main() {
message = result.messages
.firstWhere((ValidationMessage m) => m.message.startsWith('Flutter '));
expect(message.message, startsWith('Flutter extension not installed'));
expect(message.isError, isTrue);
expect(message.message, startsWith('Flutter extension can be installed from'));
expect(message.contextUrl, 'https://marketplace.visualstudio.com/items?itemName=Dart-Code.flutter');
expect(message.isError, false);
}, overrides: noColorTerminalOverride);
group('device validator', () {
......@@ -1221,7 +1222,7 @@ class FakeSmallGroupDoctor extends Doctor {
class VsCodeValidatorTestTargets extends VsCodeValidator {
VsCodeValidatorTestTargets._(String installDirectory, String extensionDirectory, {String edition})
: super(VsCode.fromDirectory(installDirectory, extensionDirectory, edition: edition));
: super(VsCode.fromDirectory(installDirectory, extensionDirectory, edition: edition, fileSystem: globals.fs));
static VsCodeValidatorTestTargets get installedWithExtension =>
VsCodeValidatorTestTargets._(validInstall, validExtensions);
......
......@@ -2,31 +2,22 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'package:file/file.dart';
import 'package:file/memory.dart';
import 'package:flutter_tools/src/base/file_system.dart';
import 'package:flutter_tools/src/base/logger.dart';
import 'package:flutter_tools/src/base/version.dart';
import 'package:flutter_tools/src/globals.dart' as globals;
import 'package:flutter_tools/src/vscode/vscode.dart';
import '../../src/common.dart';
import '../../src/context.dart';
void main() {
testUsingContext('VsCode.fromDirectory does not crash when packages.json is malformed', () {
final BufferLogger bufferLogger = globals.logger as BufferLogger;
testWithoutContext('VsCode.fromDirectory does not crash when packages.json is malformed', () {
final MemoryFileSystem fileSystem = MemoryFileSystem.test();
// Create invalid JSON file.
globals.fs.file(globals.fs.path.join('', 'resources', 'app', 'package.json'))
fileSystem.file(fileSystem.path.join('', 'resources', 'app', 'package.json'))
..createSync(recursive: true)
..writeAsStringSync('{');
final VsCode vsCode = VsCode.fromDirectory('', '');
final VsCode vsCode = VsCode.fromDirectory('', '', fileSystem: fileSystem);
expect(vsCode.version, Version.unknown);
expect(bufferLogger.traceText, contains('Error parsing VSCode'));
}, overrides: <Type, Generator>{
FileSystem: () => MemoryFileSystem.test(),
ProcessManager: () => FakeProcessManager.any(),
});
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment