blob: b11004656b1f2fe0c0ac39eebda69c26acd9770c [file] [log] [blame]
// Copyright 2014 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import '../base/common.dart';
import '../base/file_system.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 })
: version = version ?? Version.unknown {
if (!globals.fs.isDirectorySync(directory)) {
_validationMessages.add(ValidationMessage.error('VS Code not found at $directory'));
return;
} else {
_validationMessages.add(ValidationMessage('VS Code at $directory'));
}
// If the extensions directory doesn't exist at all, the listSync()
// below will fail, so just bail out early.
final ValidationMessage notInstalledMessage = ValidationMessage.error(
'Flutter extension not installed; install from\n$extensionMarketplaceUrl');
if (!globals.fs.isDirectorySync(extensionDirectory)) {
_validationMessages.add(notInstalledMessage);
return;
}
// Check for presence of extension.
final String extensionIdentifierLower = extensionIdentifier.toLowerCase();
final Iterable<FileSystemEntity> extensionDirs = globals.fs
.directory(extensionDirectory)
.listSync()
.whereType<Directory>()
.where((Directory d) => d.basename.toLowerCase().startsWith(extensionIdentifierLower));
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'));
} else {
_validationMessages.add(notInstalledMessage);
}
}
factory VsCode.fromDirectory(
String installPath,
String extensionDirectory, {
String edition,
}) {
final String packageJsonPath =
globals.fs.path.join(installPath, 'resources', 'app', 'package.json');
final String versionString = _getVersionFromPackageJson(packageJsonPath);
Version version;
if (versionString != null) {
version = Version.parse(versionString);
}
return VsCode._(installPath, extensionDirectory, version: version, edition: edition);
}
final String directory;
final String extensionDirectory;
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;
static List<VsCode> allInstalled() {
if (globals.platform.isMacOS) {
return _installedMacOS();
}
if (globals.platform.isWindows) {
return _installedWindows();
}
if (globals.platform.isLinux) {
return _installedLinux();
}
// VS Code isn't supported on the other platforms.
return <VsCode>[];
}
// macOS:
// /Applications/Visual Studio Code.app/Contents/
// /Applications/Visual Studio Code - Insiders.app/Contents/
// $HOME/Applications/Visual Studio Code.app/Contents/
// $HOME/Applications/Visual Studio Code - Insiders.app/Contents/
// macOS Extensions:
// $HOME/.vscode/extensions
// $HOME/.vscode-insiders/extensions
static List<VsCode> _installedMacOS() {
return _findInstalled(<_VsCodeInstallLocation>[
_VsCodeInstallLocation(
globals.fs.path.join('/Applications', 'Visual Studio Code.app', 'Contents'),
'.vscode',
),
_VsCodeInstallLocation(
globals.fs.path.join(homeDirPath, 'Applications', 'Visual Studio Code.app', 'Contents'),
'.vscode',
),
_VsCodeInstallLocation(
globals.fs.path.join('/Applications', 'Visual Studio Code - Insiders.app', 'Contents'),
'.vscode-insiders',
isInsiders: true,
),
_VsCodeInstallLocation(
globals.fs.path.join(homeDirPath, 'Applications', 'Visual Studio Code - Insiders.app', 'Contents'),
'.vscode-insiders',
isInsiders: true,
),
]);
}
// Windows:
// $programfiles(x86)\Microsoft VS Code
// $programfiles(x86)\Microsoft VS Code Insiders
// User install:
// $localappdata\Programs\Microsoft VS Code
// $localappdata\Programs\Microsoft VS Code Insiders
// TODO(dantup): Confirm these are correct for 64bit
// $programfiles\Microsoft VS Code
// $programfiles\Microsoft VS Code Insiders
// Windows Extensions:
// $HOME/.vscode/extensions
// $HOME/.vscode-insiders/extensions
static List<VsCode> _installedWindows() {
final String progFiles86 = globals.platform.environment['programfiles(x86)'];
final String progFiles = globals.platform.environment['programfiles'];
final String localAppData = globals.platform.environment['localappdata'];
final List<_VsCodeInstallLocation> searchLocations =
<_VsCodeInstallLocation>[];
if (localAppData != null) {
searchLocations.add(_VsCodeInstallLocation(
globals.fs.path.join(localAppData, 'Programs\\Microsoft VS Code'),
'.vscode'));
}
searchLocations.add(_VsCodeInstallLocation(
globals.fs.path.join(progFiles86, 'Microsoft VS Code'), '.vscode',
edition: '32-bit edition'));
searchLocations.add(_VsCodeInstallLocation(
globals.fs.path.join(progFiles, 'Microsoft VS Code'), '.vscode',
edition: '64-bit edition'));
if (localAppData != null) {
searchLocations.add(_VsCodeInstallLocation(
globals.fs.path.join(localAppData, 'Programs\\Microsoft VS Code Insiders'),
'.vscode-insiders',
isInsiders: true));
}
searchLocations.add(_VsCodeInstallLocation(
globals.fs.path.join(progFiles86, 'Microsoft VS Code Insiders'),
'.vscode-insiders',
edition: '32-bit edition',
isInsiders: true));
searchLocations.add(_VsCodeInstallLocation(
globals.fs.path.join(progFiles, 'Microsoft VS Code Insiders'),
'.vscode-insiders',
edition: '64-bit edition',
isInsiders: true));
return _findInstalled(searchLocations);
}
// Linux:
// /usr/share/code/bin/code
// /usr/share/code-insiders/bin/code-insiders
// Linux Extensions:
// $HOME/.vscode/extensions
// $HOME/.vscode-insiders/extensions
static List<VsCode> _installedLinux() {
return _findInstalled(<_VsCodeInstallLocation>[
const _VsCodeInstallLocation('/usr/share/code', '.vscode'),
const _VsCodeInstallLocation('/usr/share/code-insiders', '.vscode-insiders', isInsiders: true),
]);
}
static List<VsCode> _findInstalled(List<_VsCodeInstallLocation> allLocations) {
final Iterable<_VsCodeInstallLocation> searchLocations =
_includeInsiders
? allLocations
: allLocations.where((_VsCodeInstallLocation p) => p.isInsiders != true);
final List<VsCode> results = <VsCode>[];
for (final _VsCodeInstallLocation searchLocation in searchLocations) {
if (globals.fs.isDirectorySync(searchLocation.installPath)) {
final String extensionDirectory =
globals.fs.path.join(homeDirPath, searchLocation.extensionsFolder, 'extensions');
results.add(VsCode.fromDirectory(searchLocation.installPath, extensionDirectory, edition: searchLocation.edition));
}
}
return results;
}
@override
String toString() =>
'VS Code ($version)${_extensionVersion != Version.unknown ? ', Flutter ($_extensionVersion)' : ''}';
static String _getVersionFromPackageJson(String packageJsonPath) {
if (!globals.fs.isFileSync(packageJsonPath)) {
return null;
}
final String jsonString = globals.fs.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');
return null;
}
}
}
class _VsCodeInstallLocation {
const _VsCodeInstallLocation(this.installPath, this.extensionsFolder, { this.edition, bool isInsiders })
: isInsiders = isInsiders ?? false;
final String installPath;
final String extensionsFolder;
final String edition;
final bool isInsiders;
}