vscode.dart 8.3 KB
Newer Older
1 2 3 4 5 6 7 8
// Copyright 2018 The Chromium 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/platform.dart';
import '../base/version.dart';
9
import '../convert.dart';
10
import '../doctor.dart';
11 12 13 14

// Include VS Code insiders (useful for debugging).
const bool _includeInsiders = false;

15 16 17 18 19

const String extensionIdentifier = 'Dart-Code.flutter';
const String extensionMarketplaceUrl =
  'https://marketplace.visualstudio.com/items?itemName=$extensionIdentifier';

20
class VsCode {
21
  VsCode._(this.directory, this.extensionDirectory, { Version version, this.edition })
22
      : version = version ?? Version.unknown {
23 24

    if (!fs.isDirectorySync(directory)) {
25
      _validationMessages.add(ValidationMessage.error('VS Code not found at $directory'));
26
      return;
27 28
    } else {
      _validationMessages.add(ValidationMessage('VS Code at $directory'));
29 30 31 32
    }

    // If the extensions directory doesn't exist at all, the listSync()
    // below will fail, so just bail out early.
33 34
    final ValidationMessage notInstalledMessage = ValidationMessage.error(
          'Flutter extension not installed; install from\n$extensionMarketplaceUrl');
35
    if (!fs.isDirectorySync(extensionDirectory)) {
36
      _validationMessages.add(notInstalledMessage);
37 38 39 40
      return;
    }

    // Check for presence of extension.
41
    final String extensionIdentifierLower = extensionIdentifier.toLowerCase();
42 43 44
    final Iterable<FileSystemEntity> extensionDirs = fs
        .directory(extensionDirectory)
        .listSync()
45 46
        .whereType<Directory>()
        .where((Directory d) => d.basename.toLowerCase().startsWith(extensionIdentifierLower));
47 48 49 50 51

    if (extensionDirs.isNotEmpty) {
      final FileSystemEntity extensionDir = extensionDirs.first;

      _isValid = true;
52
      _extensionVersion = Version.parse(
53
          extensionDir.basename.substring('$extensionIdentifier-'.length));
54 55 56
      _validationMessages.add(ValidationMessage('Flutter extension version $_extensionVersion'));
    } else {
      _validationMessages.add(notInstalledMessage);
57 58 59
    }
  }

60 61 62 63 64
  factory VsCode.fromDirectory(
    String installPath,
    String extensionDirectory, {
    String edition,
  }) {
65 66 67 68 69
    final String packageJsonPath =
        fs.path.join(installPath, 'resources', 'app', 'package.json');
    final String versionString = _getVersionFromPackageJson(packageJsonPath);
    Version version;
    if (versionString != null)
70 71
      version = Version.parse(versionString);
    return VsCode._(installPath, extensionDirectory, version: version, edition: edition);
72 73
  }

74 75 76 77 78 79 80
  final String directory;
  final String extensionDirectory;
  final Version version;
  final String edition;

  bool _isValid = false;
  Version _extensionVersion;
81
  final List<ValidationMessage> _validationMessages = <ValidationMessage>[];
82

83
  bool get isValid => _isValid;
84
  String get productName => 'VS Code' + (edition != null ? ', $edition' : '');
85

86
  Iterable<ValidationMessage> get validationMessages => _validationMessages;
87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108

  static List<VsCode> allInstalled() {
    if (platform.isMacOS)
      return _installedMacOS();
    else if (platform.isWindows)
      return _installedWindows();
    else if (platform.isLinux)
      return _installedLinux();
    else
      // 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() {
109
    return _findInstalled(<_VsCodeInstallLocation>[
110
      _VsCodeInstallLocation(
111 112 113
        fs.path.join('/Applications', 'Visual Studio Code.app', 'Contents'),
        '.vscode',
      ),
114
      _VsCodeInstallLocation(
115 116 117
        fs.path.join(homeDirPath, 'Applications', 'Visual Studio Code.app', 'Contents'),
        '.vscode',
      ),
118
      _VsCodeInstallLocation(
119 120 121 122
        fs.path.join('/Applications', 'Visual Studio Code - Insiders.app', 'Contents'),
        '.vscode-insiders',
        isInsiders: true,
      ),
123
      _VsCodeInstallLocation(
124 125 126
        fs.path.join(homeDirPath, 'Applications', 'Visual Studio Code - Insiders.app', 'Contents'),
        '.vscode-insiders',
        isInsiders: true,
127
      ),
128
    ]);
129 130 131 132 133
  }

  // Windows:
  //   $programfiles(x86)\Microsoft VS Code
  //   $programfiles(x86)\Microsoft VS Code Insiders
134 135 136
  // User install:
  //   $localappdata\Programs\Microsoft VS Code
  //   $localappdata\Programs\Microsoft VS Code Insiders
137
  // TODO(dantup): Confirm these are correct for 64bit
138 139 140 141 142 143 144 145
  //   $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 = platform.environment['programfiles(x86)'];
    final String progFiles = platform.environment['programfiles'];
146
    final String localAppData = platform.environment['localappdata'];
147

148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179
    final List<_VsCodeInstallLocation> searchLocations =
        <_VsCodeInstallLocation>[];

    if (localAppData != null) {
      searchLocations.add(_VsCodeInstallLocation(
          fs.path.join(localAppData, 'Programs\\Microsoft VS Code'),
          '.vscode'));
    }
    searchLocations.add(_VsCodeInstallLocation(
        fs.path.join(progFiles86, 'Microsoft VS Code'), '.vscode',
        edition: '32-bit edition'));
    searchLocations.add(_VsCodeInstallLocation(
        fs.path.join(progFiles, 'Microsoft VS Code'), '.vscode',
        edition: '64-bit edition'));
    if (localAppData != null) {
      searchLocations.add(_VsCodeInstallLocation(
          fs.path.join(localAppData, 'Programs\\Microsoft VS Code Insiders'),
          '.vscode-insiders',
          isInsiders: true));
    }
    searchLocations.add(_VsCodeInstallLocation(
        fs.path.join(progFiles86, 'Microsoft VS Code Insiders'),
        '.vscode-insiders',
        edition: '32-bit edition',
        isInsiders: true));
    searchLocations.add(_VsCodeInstallLocation(
        fs.path.join(progFiles, 'Microsoft VS Code Insiders'),
        '.vscode-insiders',
        edition: '64-bit edition',
        isInsiders: true));

    return _findInstalled(searchLocations);
180 181 182 183 184 185 186 187 188
  }

  // 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() {
189 190 191 192
    return _findInstalled(<_VsCodeInstallLocation>[
      const _VsCodeInstallLocation('/usr/share/code', '.vscode'),
      const _VsCodeInstallLocation('/usr/share/code-insiders', '.vscode-insiders', isInsiders: true),
    ]);
193 194
  }

195
  static List<VsCode> _findInstalled(List<_VsCodeInstallLocation> allLocations) {
196
    final Iterable<_VsCodeInstallLocation> searchLocations =
197 198 199
      _includeInsiders
        ? allLocations
        : allLocations.where((_VsCodeInstallLocation p) => p.isInsiders != true);
200 201 202

    final List<VsCode> results = <VsCode>[];

203
    for (_VsCodeInstallLocation searchLocation in searchLocations) {
204
      if (fs.isDirectorySync(searchLocation.installPath)) {
205
        final String extensionDirectory =
206
            fs.path.join(homeDirPath, searchLocation.extensionsFolder, 'extensions');
207
        results.add(VsCode.fromDirectory(searchLocation.installPath, extensionDirectory, edition: searchLocation.edition));
208 209 210 211 212 213 214 215
      }
    }

    return results;
  }

  @override
  String toString() =>
216
      'VS Code ($version)${_extensionVersion != Version.unknown ? ', Flutter ($_extensionVersion)' : ''}';
217 218 219 220 221

  static String _getVersionFromPackageJson(String packageJsonPath) {
    if (!fs.isFileSync(packageJsonPath))
      return null;
    final String jsonString = fs.file(packageJsonPath).readAsStringSync();
222
    final Map<String, dynamic> jsonObject = json.decode(jsonString);
223
    return jsonObject['version'];
224 225
  }
}
226 227

class _VsCodeInstallLocation {
228
  const _VsCodeInstallLocation(this.installPath, this.extensionsFolder, { this.edition, bool isInsiders })
229
    : isInsiders = isInsiders ?? false;
230 231 232 233 234
  final String installPath;
  final String extensionsFolder;
  final String edition;
  final bool isInsiders;
}