| // 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; |
| } |