From 4296e2fcc5679751cd07a02136dfa8b2a6e121f4 Mon Sep 17 00:00:00 2001
From: Asher <ash@coder.com>
Date: Sun, 19 May 2019 14:07:12 -0500
Subject: [PATCH 1/5] Allow setting paths for builtin exts and extra dirs

The extra directories aren't used yet, just available from the
environment service and to the shared process.
---
 packages/protocol/src/browser/client.ts     |  2 +
 packages/protocol/src/common/connection.ts  |  2 +
 packages/protocol/src/node/server.ts        |  4 +
 packages/protocol/src/proto/client.proto    |  2 +
 packages/protocol/src/proto/client_pb.d.ts  | 12 +++
 packages/protocol/src/proto/client_pb.js    | 99 ++++++++++++++++++++-
 packages/protocol/test/server.test.ts       |  2 +
 packages/server/src/cli.ts                  | 26 +++++-
 packages/server/src/vscode/sharedProcess.ts |  4 +
 packages/vscode/src/fill/paths.ts           | 28 ++++++
 10 files changed, 175 insertions(+), 6 deletions(-)

diff --git a/packages/protocol/src/browser/client.ts b/packages/protocol/src/browser/client.ts
index 77bba1b69f21..3f2d35f64031 100644
--- a/packages/protocol/src/browser/client.ts
+++ b/packages/protocol/src/browser/client.ts
@@ -278,6 +278,8 @@ export class Client {
 					shell: init.getShell(),
 					extensionsDirectory: init.getExtensionsDirectory(),
 					builtInExtensionsDirectory: init.getBuiltinExtensionsDir(),
+					extraExtensionDirectories: init.getExtraExtensionDirectoriesList(),
+					extraBuiltinExtensionDirectories: init.getExtraBuiltinExtensionDirectoriesList(),
 				};
 				this.initDataEmitter.emit(this._initData);
 				break;
diff --git a/packages/protocol/src/common/connection.ts b/packages/protocol/src/common/connection.ts
index f4f60deddce7..901bc3d54cab 100644
--- a/packages/protocol/src/common/connection.ts
+++ b/packages/protocol/src/common/connection.ts
@@ -25,6 +25,8 @@ export interface InitData {
 	readonly shell: string;
 	readonly extensionsDirectory: string;
 	readonly builtInExtensionsDirectory: string;
+	readonly extraExtensionDirectories: string[];
+	readonly extraBuiltinExtensionDirectories: string[];
 }
 
 export interface SharedProcessData {
diff --git a/packages/protocol/src/node/server.ts b/packages/protocol/src/node/server.ts
index be4abbf847d5..250f2d30f09a 100644
--- a/packages/protocol/src/node/server.ts
+++ b/packages/protocol/src/node/server.ts
@@ -15,6 +15,8 @@ export interface ServerOptions {
 	readonly cacheDirectory: string;
 	readonly builtInExtensionsDirectory: string;
 	readonly extensionsDirectory: string;
+	readonly extraExtensionDirectories?: string[];
+	readonly extraBuiltinExtensionDirectories?: string[];
 	readonly fork?: ForkProvider;
 }
 
@@ -99,6 +101,8 @@ export class Server {
 		initMsg.setTmpDirectory(os.tmpdir());
 		initMsg.setOperatingSystem(platformToProto(os.platform()));
 		initMsg.setShell(os.userInfo().shell || global.process.env.SHELL || "");
+		initMsg.setExtraExtensionDirectoriesList(this.options.extraExtensionDirectories || []);
+		initMsg.setExtraBuiltinExtensionDirectoriesList(this.options.extraBuiltinExtensionDirectories || []);
 		const srvMsg = new ServerMessage();
 		srvMsg.setInit(initMsg);
 		connection.send(srvMsg.serializeBinary());
diff --git a/packages/protocol/src/proto/client.proto b/packages/protocol/src/proto/client.proto
index 4681ecf6f49b..33c5e3425902 100644
--- a/packages/protocol/src/proto/client.proto
+++ b/packages/protocol/src/proto/client.proto
@@ -42,4 +42,6 @@ message WorkingInit {
 	string shell = 6;
 	string builtin_extensions_dir = 7;
 	string extensions_directory = 8;
+	repeated string extra_extension_directories = 9;
+	repeated string extra_builtin_extension_directories = 10;
 }
diff --git a/packages/protocol/src/proto/client_pb.d.ts b/packages/protocol/src/proto/client_pb.d.ts
index 9c4c1dd591a6..931f71c1e982 100644
--- a/packages/protocol/src/proto/client_pb.d.ts
+++ b/packages/protocol/src/proto/client_pb.d.ts
@@ -135,6 +135,16 @@ export class WorkingInit extends jspb.Message {
   getExtensionsDirectory(): string;
   setExtensionsDirectory(value: string): void;
 
+  clearExtraExtensionDirectoriesList(): void;
+  getExtraExtensionDirectoriesList(): Array<string>;
+  setExtraExtensionDirectoriesList(value: Array<string>): void;
+  addExtraExtensionDirectories(value: string, index?: number): string;
+
+  clearExtraBuiltinExtensionDirectoriesList(): void;
+  getExtraBuiltinExtensionDirectoriesList(): Array<string>;
+  setExtraBuiltinExtensionDirectoriesList(value: Array<string>): void;
+  addExtraBuiltinExtensionDirectories(value: string, index?: number): string;
+
   serializeBinary(): Uint8Array;
   toObject(includeInstance?: boolean): WorkingInit.AsObject;
   static toObject(includeInstance: boolean, msg: WorkingInit): WorkingInit.AsObject;
@@ -155,6 +165,8 @@ export namespace WorkingInit {
     shell: string,
     builtinExtensionsDir: string,
     extensionsDirectory: string,
+    extraExtensionDirectoriesList: Array<string>,
+    extraBuiltinExtensionDirectoriesList: Array<string>,
   }
 
   export enum OperatingSystem {
diff --git a/packages/protocol/src/proto/client_pb.js b/packages/protocol/src/proto/client_pb.js
index 20bf41eacd06..7dd7c8c2fe9e 100644
--- a/packages/protocol/src/proto/client_pb.js
+++ b/packages/protocol/src/proto/client_pb.js
@@ -72,7 +72,7 @@ if (goog.DEBUG && !COMPILED) {
  * @constructor
  */
 proto.WorkingInit = function(opt_data) {
-  jspb.Message.initialize(this, opt_data, 0, -1, null, null);
+  jspb.Message.initialize(this, opt_data, 0, -1, proto.WorkingInit.repeatedFields_, null);
 };
 goog.inherits(proto.WorkingInit, jspb.Message);
 if (goog.DEBUG && !COMPILED) {
@@ -759,6 +759,13 @@ proto.ServerMessage.prototype.hasSharedProcessActive = function() {
 
 
 
+/**
+ * List of repeated fields within this message type.
+ * @private {!Array<number>}
+ * @const
+ */
+proto.WorkingInit.repeatedFields_ = [9,10];
+
 
 
 if (jspb.Message.GENERATE_TO_OBJECT) {
@@ -795,7 +802,9 @@ proto.WorkingInit.toObject = function(includeInstance, msg) {
     operatingSystem: jspb.Message.getFieldWithDefault(msg, 5, 0),
     shell: jspb.Message.getFieldWithDefault(msg, 6, ""),
     builtinExtensionsDir: jspb.Message.getFieldWithDefault(msg, 7, ""),
-    extensionsDirectory: jspb.Message.getFieldWithDefault(msg, 8, "")
+    extensionsDirectory: jspb.Message.getFieldWithDefault(msg, 8, ""),
+    extraExtensionDirectoriesList: jspb.Message.getRepeatedField(msg, 9),
+    extraBuiltinExtensionDirectoriesList: jspb.Message.getRepeatedField(msg, 10)
   };
 
   if (includeInstance) {
@@ -864,6 +873,14 @@ proto.WorkingInit.deserializeBinaryFromReader = function(msg, reader) {
       var value = /** @type {string} */ (reader.readString());
       msg.setExtensionsDirectory(value);
       break;
+    case 9:
+      var value = /** @type {string} */ (reader.readString());
+      msg.addExtraExtensionDirectories(value);
+      break;
+    case 10:
+      var value = /** @type {string} */ (reader.readString());
+      msg.addExtraBuiltinExtensionDirectories(value);
+      break;
     default:
       reader.skipField();
       break;
@@ -949,6 +966,20 @@ proto.WorkingInit.serializeBinaryToWriter = function(message, writer) {
       f
     );
   }
+  f = message.getExtraExtensionDirectoriesList();
+  if (f.length > 0) {
+    writer.writeRepeatedString(
+      9,
+      f
+    );
+  }
+  f = message.getExtraBuiltinExtensionDirectoriesList();
+  if (f.length > 0) {
+    writer.writeRepeatedString(
+      10,
+      f
+    );
+  }
 };
 
 
@@ -1081,4 +1112,68 @@ proto.WorkingInit.prototype.setExtensionsDirectory = function(value) {
 };
 
 
+/**
+ * repeated string extra_extension_directories = 9;
+ * @return {!Array<string>}
+ */
+proto.WorkingInit.prototype.getExtraExtensionDirectoriesList = function() {
+  return /** @type {!Array<string>} */ (jspb.Message.getRepeatedField(this, 9));
+};
+
+
+/** @param {!Array<string>} value */
+proto.WorkingInit.prototype.setExtraExtensionDirectoriesList = function(value) {
+  jspb.Message.setField(this, 9, value || []);
+};
+
+
+/**
+ * @param {string} value
+ * @param {number=} opt_index
+ */
+proto.WorkingInit.prototype.addExtraExtensionDirectories = function(value, opt_index) {
+  jspb.Message.addToRepeatedField(this, 9, value, opt_index);
+};
+
+
+/**
+ * Clears the list making it empty but non-null.
+ */
+proto.WorkingInit.prototype.clearExtraExtensionDirectoriesList = function() {
+  this.setExtraExtensionDirectoriesList([]);
+};
+
+
+/**
+ * repeated string extra_builtin_extension_directories = 10;
+ * @return {!Array<string>}
+ */
+proto.WorkingInit.prototype.getExtraBuiltinExtensionDirectoriesList = function() {
+  return /** @type {!Array<string>} */ (jspb.Message.getRepeatedField(this, 10));
+};
+
+
+/** @param {!Array<string>} value */
+proto.WorkingInit.prototype.setExtraBuiltinExtensionDirectoriesList = function(value) {
+  jspb.Message.setField(this, 10, value || []);
+};
+
+
+/**
+ * @param {string} value
+ * @param {number=} opt_index
+ */
+proto.WorkingInit.prototype.addExtraBuiltinExtensionDirectories = function(value, opt_index) {
+  jspb.Message.addToRepeatedField(this, 10, value, opt_index);
+};
+
+
+/**
+ * Clears the list making it empty but non-null.
+ */
+proto.WorkingInit.prototype.clearExtraBuiltinExtensionDirectoriesList = function() {
+  this.setExtraBuiltinExtensionDirectoriesList([]);
+};
+
+
 goog.object.extend(exports, proto);
diff --git a/packages/protocol/test/server.test.ts b/packages/protocol/test/server.test.ts
index 4749e0948b68..0675d60f39ef 100644
--- a/packages/protocol/test/server.test.ts
+++ b/packages/protocol/test/server.test.ts
@@ -3,9 +3,11 @@ import { createClient } from "./helpers";
 describe("Server", () => {
 	const dataDirectory = "/tmp/example";
 	const workingDirectory = "/working/dir";
+	const extensionsDirectory = "/tmp/example";
 	const builtInExtensionsDirectory = "/tmp/example";
 	const cacheDirectory = "/tmp/cache";
 	const client = createClient({
+		extensionsDirectory,
 		builtInExtensionsDirectory,
 		cacheDirectory,
 		dataDirectory,
diff --git a/packages/server/src/cli.ts b/packages/server/src/cli.ts
index 41ca803c0e2d..4cc8291afed6 100644
--- a/packages/server/src/cli.ts
+++ b/packages/server/src/cli.ts
@@ -16,13 +16,20 @@ import opn = require("opn");
 
 import * as commander from "commander";
 
+const collect = <T>(value: T, previous: T[]): T[] => {
+	return previous.concat(value);
+};
+
 commander.version(process.env.VERSION || "development")
 	.name("code-server")
 	.description("Run VS Code on a remote server.")
 	.option("--cert <value>")
 	.option("--cert-key <value>")
-	.option("-e, --extensions-dir <dir>", "Set the root path for extensions.")
-	.option("-d --user-data-dir <dir>", "	Specifies the directory that user data is kept in, useful when running as root.")
+	.option("-e, --extensions-dir <dir>", "Override the main default path for user extensions.")
+	.option("--builtin-extensions-dir <dir>", "Override the main default path for built-in extensions.")
+	.option("--extra-extensions-dir [dir]", "Path to an extra user extension directory (repeatable).", collect, [])
+	.option("--extra-builtin-extensions-dir [dir]", "Path to an extra built-in extension directory (repeatable).", collect, [])
+	.option("-d --user-data-dir <dir>", "Specifies the directory that user data is kept in, useful when running as root.")
 	.option("--data-dir <value>", "DEPRECATED: Use '--user-data-dir' instead. Customize where user-data is stored.")
 	.option("-h, --host <value>", "Customize the hostname.", "0.0.0.0")
 	.option("-o, --open", "Open in the browser on startup.", false)
@@ -59,6 +66,9 @@ const bold = (text: string | number): string | number => {
 
 		readonly userDataDir?: string;
 		readonly extensionsDir?: string;
+		readonly builtinExtensionsDir?: string;
+		readonly extraExtensionsDir?: string[];
+		readonly extraBuiltinExtensionsDir?: string[];
 
 		readonly dataDir?: string;
 		readonly password?: string;
@@ -84,6 +94,10 @@ const bold = (text: string | number): string | number => {
 
 	const dataDir = path.resolve(options.userDataDir || options.dataDir || path.join(dataHome, "code-server"));
 	const extensionsDir = options.extensionsDir ? path.resolve(options.extensionsDir) : path.resolve(dataDir, "extensions");
+	const builtInExtensionsDir = options.builtinExtensionsDir ? path.resolve(options.builtinExtensionsDir)
+		: path.resolve(buildDir || path.join(__dirname, ".."), "build/extensions");
+	const extraExtensionDirs = options.extraExtensionsDir ? options.extraExtensionsDir.map((p) => path.resolve(p)) : [];
+	const extraBuiltinExtensionDirs = options.extraBuiltinExtensionsDir ? options.extraBuiltinExtensionsDir.map((p) => path.resolve(p)) : [];
 	const workingDir = path.resolve(args[0] || process.cwd());
 	const dependenciesDir = path.join(os.tmpdir(), "code-server/dependencies");
 
@@ -99,8 +113,11 @@ const bold = (text: string | number): string | number => {
 		fse.mkdirp(cacheHome),
 		fse.mkdirp(dataDir),
 		fse.mkdirp(extensionsDir),
+		fse.mkdirp(builtInExtensionsDir),
 		fse.mkdirp(workingDir),
 		fse.mkdirp(dependenciesDir),
+		...extraExtensionDirs.map((p) => fse.mkdirp(p)),
+		...extraBuiltinExtensionDirs.map((p) => fse.mkdirp(p)),
 	]);
 
 	const unpackExecutable = (binaryName: string): void => {
@@ -116,7 +133,6 @@ const bold = (text: string | number): string | number => {
 	// tslint:disable-next-line no-any
 	(<any>global).RIPGREP_LOCATION = path.join(dependenciesDir, "rg");
 
-	const builtInExtensionsDir = path.resolve(buildDir || path.join(__dirname, ".."), "build/extensions");
 	if (options.bootstrapFork) {
 		const modulePath = options.bootstrapFork;
 		if (!modulePath) {
@@ -192,7 +208,7 @@ const bold = (text: string | number): string | number => {
 	// TODO: fill in appropriate doc url
 	logger.info("Additional documentation: http://github.com/cdr/code-server");
 	logger.info("Initializing", field("data-dir", dataDir), field("extensions-dir", extensionsDir), field("working-dir", workingDir), field("log-dir", logDir));
-	const sharedProcess = new SharedProcess(dataDir, extensionsDir, builtInExtensionsDir);
+	const sharedProcess = new SharedProcess(dataDir, extensionsDir, builtInExtensionsDir, extraExtensionDirs, extraBuiltinExtensionDirs);
 	const sendSharedProcessReady = (socket: WebSocket): void => {
 		const active = new SharedProcessActive();
 		active.setSocketPath(sharedProcess.socketPath);
@@ -247,6 +263,8 @@ const bold = (text: string | number): string | number => {
 		serverOptions: {
 			extensionsDirectory: extensionsDir,
 			builtInExtensionsDirectory: builtInExtensionsDir,
+			extraExtensionDirectories: extraExtensionDirs,
+			extraBuiltinExtensionDirectories: extraBuiltinExtensionDirs,
 			dataDirectory: dataDir,
 			workingDirectory: workingDir,
 			cacheDirectory: cacheHome,
diff --git a/packages/server/src/vscode/sharedProcess.ts b/packages/server/src/vscode/sharedProcess.ts
index 1348f1d85e72..6834c2e1a364 100644
--- a/packages/server/src/vscode/sharedProcess.ts
+++ b/packages/server/src/vscode/sharedProcess.ts
@@ -39,6 +39,8 @@ export class SharedProcess {
 		private readonly userDataDir: string,
 		private readonly extensionsDir: string,
 		private readonly builtInExtensionsDir: string,
+		private readonly extraExtensionDirs: string[],
+		private readonly extraBuiltinExtensionDirs: string[],
 	) {
 		this.retry.run();
 	}
@@ -134,6 +136,8 @@ export class SharedProcess {
 						"builtin-extensions-dir": this.builtInExtensionsDir,
 						"user-data-dir": this.userDataDir,
 						"extensions-dir": this.extensionsDir,
+						"extra-extension-dirs": this.extraExtensionDirs,
+						"extra-builtin-extension-dirs": this.extraBuiltinExtensionDirs,
 					},
 					logLevel: this.logger.level,
 					sharedIPCHandle: this.socketPath,
diff --git a/packages/vscode/src/fill/paths.ts b/packages/vscode/src/fill/paths.ts
index ed284fb3de6e..88e19dc6338b 100644
--- a/packages/vscode/src/fill/paths.ts
+++ b/packages/vscode/src/fill/paths.ts
@@ -1,5 +1,8 @@
 import { InitData, SharedProcessData } from "@coder/protocol";
 
+/**
+ * Provides paths.
+ */
 class Paths {
 	private _appData: string | undefined;
 	private _defaultUserData: string | undefined;
@@ -7,6 +10,8 @@ class Paths {
 	private _extensionsDirectory: string | undefined;
 	private _builtInExtensionsDirectory: string | undefined;
 	private _workingDirectory: string | undefined;
+	private _extraExtensionDirectories: string[] | undefined;
+	private _extraBuiltinExtensionDirectories: string[] | undefined;
 
 	public get appData(): string {
 		if (typeof this._appData === "undefined") {
@@ -48,6 +53,22 @@ class Paths {
 		return this._builtInExtensionsDirectory;
 	}
 
+	public get extraExtensionDirectories(): string[] {
+		if (!this._extraExtensionDirectories) {
+			throw new Error("trying to access extra extension directories before they have been set");
+		}
+
+		return this._extraExtensionDirectories;
+	}
+
+	public get extraBuiltinExtensionDirectories(): string[] {
+		if (!this._extraBuiltinExtensionDirectories) {
+			throw new Error("trying to access extra builtin extension directories before they have been set");
+		}
+
+		return this._extraBuiltinExtensionDirectories;
+	}
+
 	public get workingDirectory(): string {
 		if (!this._workingDirectory) {
 			throw new Error("trying to access working directory before it has been set");
@@ -56,6 +77,9 @@ class Paths {
 		return this._workingDirectory;
 	}
 
+	/**
+	 * Initialize paths using the provided data.
+	 */
 	public initialize(data: InitData, sharedData: SharedProcessData): void {
 		process.env.VSCODE_LOGS = sharedData.logPath;
 		this._appData = data.dataDirectory;
@@ -64,6 +88,8 @@ class Paths {
 		this._extensionsDirectory = data.extensionsDirectory;
 		this._builtInExtensionsDirectory = data.builtInExtensionsDirectory;
 		this._workingDirectory = data.workingDirectory;
+		this._extraExtensionDirectories = data.extraExtensionDirectories;
+		this._extraBuiltinExtensionDirectories = data.extraBuiltinExtensionDirectories;
 	}
 }
 
@@ -73,4 +99,6 @@ export const getDefaultUserDataPath = (): string => _paths.defaultUserData;
 export const getWorkingDirectory = (): string => _paths.workingDirectory;
 export const getExtensionsDirectory = (): string => _paths.extensionsDirectory;
 export const getBuiltInExtensionsDirectory = (): string => _paths.builtInExtensionsDirectory;
+export const getExtraExtensionDirectories = (): string[] => _paths.extraExtensionDirectories;
+export const getExtraBuiltinExtensionDirectories = (): string[] => _paths.extraBuiltinExtensionDirectories;
 export const getSocketPath = (): string => _paths.socketPath;

From ae3bd7859c4af197b6b8e23c8737036981ff536d Mon Sep 17 00:00:00 2001
From: Asher <ash@coder.com>
Date: Sun, 19 May 2019 14:51:52 -0500
Subject: [PATCH 2/5] Utilize extra builtin extensions path

---
 scripts/vscode.patch | 62 +++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 61 insertions(+), 1 deletion(-)

diff --git a/scripts/vscode.patch b/scripts/vscode.patch
index 8e89e69e2af4..28e24a7f2d15 100644
--- a/scripts/vscode.patch
+++ b/scripts/vscode.patch
@@ -383,6 +383,52 @@ index 9952574..908a9ae 100644
 @@ -9 +9 @@ import { URI } from 'vs/base/common/uri';
 -import { isMacintosh } from 'vs/base/common/platform';
 +import { isMacintosh } from 'vs/base/browser/browser';
+diff --git a/src/vs/platform/environment/common/environment.ts b/src/vs/platform/environment/common/environment.ts
+index eb1873c..dbbacd0 100644
+--- a/src/vs/platform/environment/common/environment.ts
++++ b/src/vs/platform/environment/common/environment.ts
+@@ -120,0 +121,2 @@ export interface IEnvironmentService {
++	extraExtensionPaths: string[];
++	extraBuiltinExtensionPaths: string[];
+diff --git a/src/vs/platform/environment/node/environmentService.ts b/src/vs/platform/environment/node/environmentService.ts
+index 43866f8..e69b513 100644
+--- a/src/vs/platform/environment/node/environmentService.ts
++++ b/src/vs/platform/environment/node/environmentService.ts
+@@ -172,0 +173,8 @@ export class EnvironmentService implements IEnvironmentService {
++	@memoize
++	get extraExtensionPaths(): string[] {
++		return this._args['extra-extension-dirs'] || [];
++	}
++	@memoize
++	get extraBuiltinExtensionPaths(): string[] {
++		return this._args['extra-builtin-extension-dirs'] || [];
++	}
+diff --git a/src/vs/platform/extensionManagement/node/extensionManagementService.ts b/src/vs/platform/extensionManagement/node/extensionManagementService.ts
+index c897029..72a5c54 100644
+--- a/src/vs/platform/extensionManagement/node/extensionManagementService.ts
++++ b/src/vs/platform/extensionManagement/node/extensionManagementService.ts
+@@ -737,0 +738,8 @@ export class ExtensionManagementService extends Disposable implements IExtension
++		const extraSystemExtensionsPromise = Promise.all(
++			this.environmentService.extraBuiltinExtensionPaths
++				.map((path) => this.scanExtensions(path, ExtensionType.System))
++		).then((results) => {
++			const result = results.reduce((flat, current) => flat.concat(current), []);
++			this.logService.info('Scanned extra system extensions:', result.length);
++			return result;
++		});
+@@ -739 +747,2 @@ export class ExtensionManagementService extends Disposable implements IExtension
+-			return systemExtensionsPromise;
++			return Promise.all([systemExtensionsPromise, extraSystemExtensionsPromise])
++				.then(([systemExtensions, extraSystemExtensions]) => [...systemExtensions, ...extraSystemExtensions]);
+@@ -755,2 +764,2 @@ export class ExtensionManagementService extends Disposable implements IExtension
+-		return Promise.all([systemExtensionsPromise, devSystemExtensionsPromise])
+-			.then(([systemExtensions, devSystemExtensions]) => [...systemExtensions, ...devSystemExtensions]);
++		return Promise.all([systemExtensionsPromise, devSystemExtensionsPromise, extraSystemExtensionsPromise])
++			.then(([systemExtensions, devSystemExtensions, extraSystemExtensions]) => [...systemExtensions, ...devSystemExtensions, ...extraSystemExtensions]);
+@@ -972 +981 @@ export class ExtensionManagementService extends Disposable implements IExtension
+-}
+\ No newline at end of file
++}
 diff --git a/src/vs/platform/windows/common/windows.ts b/src/vs/platform/windows/common/windows.ts
 index cbc55b3..9d27c01 100644
 --- a/src/vs/platform/windows/common/windows.ts
@@ -999,11 +1045,25 @@ index 75f0026..2e94683 100644
 -		if (!isMacintosh && getTitleBarStyle(configurationService, environmentService) === 'custom') {
 +		if (!(isNative && isMacintosh) && getTitleBarStyle(configurationService, environmentService) === 'custom') {
 diff --git a/src/vs/workbench/services/extensions/electron-browser/cachedExtensionScanner.ts b/src/vs/workbench/services/extensions/electron-browser/cachedExtensionScanner.ts
-index 059f821..2dde675 100644
+index 059f821..b58fd28 100644
 --- a/src/vs/workbench/services/extensions/electron-browser/cachedExtensionScanner.ts
 +++ b/src/vs/workbench/services/extensions/electron-browser/cachedExtensionScanner.ts
 @@ -32,0 +33 @@ function getSystemExtensionsRoot(): string {
 +	return (require('vs/../../../../packages/vscode/src/fill/paths') as typeof import ('vs/../../../../packages/vscode/src/fill/paths')).getBuiltInExtensionsDirectory();
+@@ -280,0 +282,13 @@ export class CachedExtensionScanner {
++		const extraBuiltinExtensionDirectories = (require('vs/../../../../packages/vscode/src/fill/paths') as typeof import ('vs/../../../../packages/vscode/src/fill/paths')).getExtraBuiltinExtensionDirectories();
++		extraBuiltinExtensionDirectories.forEach((dir) => {
++			const input = new ExtensionScannerInput(version, commit, locale, devMode, dir, true, false, translations);
++			const extraBuiltinExtensions = this._scanExtensionsWithCache(
++				windowService,
++				notificationService,
++				environmentService,
++				BUILTIN_MANIFEST_CACHE_FILE,
++				new ExtensionScannerInput(version, commit, locale, devMode, dir, true, false, translations),
++				log
++			);
++			finalBuiltinExtensions = ExtensionScanner.mergeBuiltinExtensions(finalBuiltinExtensions, extraBuiltinExtensions);
++		});
 diff --git a/src/vs/workbench/services/extensions/electron-browser/extensionService.ts b/src/vs/workbench/services/extensions/electron-browser/extensionService.ts
 index b337206..0477464 100644
 --- a/src/vs/workbench/services/extensions/electron-browser/extensionService.ts

From 3d784d7d451b3f2b31ee53f96175cc33e8a92979 Mon Sep 17 00:00:00 2001
From: Asher <ash@coder.com>
Date: Sun, 19 May 2019 17:01:47 -0500
Subject: [PATCH 3/5] Utilize extra extensions directory

---
 .../vscode/src/fill/environmentService.ts     |  12 ++
 scripts/vscode.patch                          | 177 ++++++++++++++----
 2 files changed, 149 insertions(+), 40 deletions(-)

diff --git a/packages/vscode/src/fill/environmentService.ts b/packages/vscode/src/fill/environmentService.ts
index 67cf95d2e1a1..0539d1e31dfc 100644
--- a/packages/vscode/src/fill/environmentService.ts
+++ b/packages/vscode/src/fill/environmentService.ts
@@ -12,6 +12,18 @@ export class EnvironmentService extends environment.EnvironmentService {
 	public get extensionsPath(): string {
 		return paths.getExtensionsDirectory();
 	}
+
+	public get builtinExtensionsPath(): string {
+		return paths.getBuiltInExtensionsDirectory();
+	}
+
+	public get extraExtensionPaths(): string[] {
+		return paths.getExtraExtensionDirectories();
+	}
+
+	public get extraBuiltinExtensionPaths(): string[] {
+		return paths.getExtraBuiltinExtensionDirectories();
+	}
 }
 
 const target = environment as typeof environment;
diff --git a/scripts/vscode.patch b/scripts/vscode.patch
index 28e24a7f2d15..df51dd709da8 100644
--- a/scripts/vscode.patch
+++ b/scripts/vscode.patch
@@ -129,9 +129,12 @@ index f91ca2b..ef6fce9 100644
 -		const isMac = platform.isMacintosh;
 +		const isMac = browser.isMacintosh;
 diff --git a/src/vs/code/electron-browser/issue/issueReporterMain.ts b/src/vs/code/electron-browser/issue/issueReporterMain.ts
-index f08c996..f9de58c 100644
+index f08c996..7db13fa 100644
 --- a/src/vs/code/electron-browser/issue/issueReporterMain.ts
 +++ b/src/vs/code/electron-browser/issue/issueReporterMain.ts
+@@ -296 +296 @@ export class IssueReporter extends Disposable {
+-			const piiPaths = [this.environmentService.appRoot, this.environmentService.extensionsPath];
++			const piiPaths = [this.environmentService.appRoot, this.environmentService.extensionsPath, ...this.environmentService.extraExtensionPaths];
 @@ -425 +425 @@ export class IssueReporter extends Disposable {
 -			const cmdOrCtrlKey = platform.isMacintosh ? e.metaKey : e.ctrlKey;
 +			const cmdOrCtrlKey = browser.isMacintosh ? e.metaKey : e.ctrlKey;
@@ -146,25 +149,34 @@ index e0ff793..885de12 100644
 -		const cmdOrCtrlKey = platform.isMacintosh ? e.metaKey : e.ctrlKey;
 +		const cmdOrCtrlKey = browser.isMacintosh ? e.metaKey : e.ctrlKey;
 diff --git a/src/vs/code/electron-browser/sharedProcess/sharedProcessMain.ts b/src/vs/code/electron-browser/sharedProcess/sharedProcessMain.ts
-index 6fd8249..04c0933 100644
+index 6fd8249..1101558 100644
 --- a/src/vs/code/electron-browser/sharedProcess/sharedProcessMain.ts
 +++ b/src/vs/code/electron-browser/sharedProcess/sharedProcessMain.ts
 @@ -50,0 +51,2 @@ import { ServiceIdentifier } from 'vs/platform/instantiation/common/instantiatio
 +import { BackupMainService } from 'vs/platform/backup/electron-main/backupMainService';
 +import { mkdirp } from 'vs/base/node/pfs';
-@@ -93,0 +96,8 @@ function main(server: Server, initData: ISharedProcessInitData, configuration: I
+@@ -93,0 +96,11 @@ function main(server: Server, initData: ISharedProcessInitData, configuration: I
 +	Promise.all<boolean | undefined>([ // Copied from src/vs/code/electron-main/main.ts
 +		environmentService.extensionsPath,
 +		environmentService.nodeCachedDataDir,
 +		environmentService.logsPath,
 +		environmentService.globalStorageHome,
 +		environmentService.workspaceStorageHome,
-+		environmentService.backupHome
++		environmentService.backupHome,
++		environmentService.builtinExtensionsPath,
++		...environmentService.extraExtensionPaths,
++		...environmentService.extraBuiltinExtensionPaths,
 +	].map((path): undefined | Promise<boolean> => path ? mkdirp(path) : undefined));
-@@ -119,0 +130,2 @@ function main(server: Server, initData: ISharedProcessInitData, configuration: I
+@@ -119,0 +133,2 @@ function main(server: Server, initData: ISharedProcessInitData, configuration: I
 +	const backupMainService = instantiationService.createInstance(BackupMainService) as BackupMainService;
 +	backupMainService.initialize().catch(console.error);
-@@ -223,0 +236 @@ async function handshake(configuration: ISharedProcessConfiguration): Promise<vo
+@@ -124 +139 @@ function main(server: Server, initData: ISharedProcessInitData, configuration: I
+-		const { appRoot, extensionsPath, extensionDevelopmentLocationURI, isBuilt, installSourcePath } = environmentService;
++		const { appRoot, extensionsPath, extraExtensionPaths, extensionDevelopmentLocationURI, isBuilt, installSourcePath } = environmentService;
+@@ -138 +153 @@ function main(server: Server, initData: ISharedProcessInitData, configuration: I
+-				piiPaths: [appRoot, extensionsPath]
++				piiPaths: [appRoot, extensionsPath, ...extraExtensionPaths]
+@@ -223,0 +239 @@ async function handshake(configuration: ISharedProcessConfiguration): Promise<vo
 +startup({ machineId: "1" });
 diff --git a/src/vs/code/node/cli.ts b/src/vs/code/node/cli.ts
 index 1f8b17a..2a875f9 100644
@@ -404,31 +416,60 @@ index 43866f8..e69b513 100644
 +		return this._args['extra-builtin-extension-dirs'] || [];
 +	}
 diff --git a/src/vs/platform/extensionManagement/node/extensionManagementService.ts b/src/vs/platform/extensionManagement/node/extensionManagementService.ts
-index c897029..72a5c54 100644
+index c897029..f84d9b6 100644
 --- a/src/vs/platform/extensionManagement/node/extensionManagementService.ts
 +++ b/src/vs/platform/extensionManagement/node/extensionManagementService.ts
-@@ -737,0 +738,8 @@ export class ExtensionManagementService extends Disposable implements IExtension
-+		const extraSystemExtensionsPromise = Promise.all(
-+			this.environmentService.extraBuiltinExtensionPaths
+@@ -733,5 +733,9 @@ export class ExtensionManagementService extends Disposable implements IExtension
+-		const systemExtensionsPromise = this.scanExtensions(this.systemExtensionsPath, ExtensionType.System)
+-			.then(result => {
+-				this.logService.info('Scanned system extensions:', result.length);
+-				return result;
+-			});
++		const systemExtensionsPromise = Promise.all([
++			this.scanExtensions(this.systemExtensionsPath, ExtensionType.System),
++			...this.environmentService.extraBuiltinExtensionPaths
 +				.map((path) => this.scanExtensions(path, ExtensionType.System))
-+		).then((results) => {
++		]).then((results) => {
 +			const result = results.reduce((flat, current) => flat.concat(current), []);
-+			this.logService.info('Scanned extra system extensions:', result.length);
++			this.logService.info('Scanned system extensions:', result.length);
 +			return result;
 +		});
-@@ -739 +747,2 @@ export class ExtensionManagementService extends Disposable implements IExtension
--			return systemExtensionsPromise;
-+			return Promise.all([systemExtensionsPromise, extraSystemExtensionsPromise])
-+				.then(([systemExtensions, extraSystemExtensions]) => [...systemExtensions, ...extraSystemExtensions]);
-@@ -755,2 +764,2 @@ export class ExtensionManagementService extends Disposable implements IExtension
--		return Promise.all([systemExtensionsPromise, devSystemExtensionsPromise])
--			.then(([systemExtensions, devSystemExtensions]) => [...systemExtensions, ...devSystemExtensions]);
-+		return Promise.all([systemExtensionsPromise, devSystemExtensionsPromise, extraSystemExtensionsPromise])
-+			.then(([systemExtensions, devSystemExtensions, extraSystemExtensions]) => [...systemExtensions, ...devSystemExtensions, ...extraSystemExtensions]);
-@@ -972 +981 @@ export class ExtensionManagementService extends Disposable implements IExtension
--}
-\ No newline at end of file
-+}
+@@ -761 +765 @@ export class ExtensionManagementService extends Disposable implements IExtension
+-		return Promise.all([this.getUninstalledExtensions(), this.scanExtensions(this.extensionsPath, ExtensionType.User)])
++		return Promise.all([this.getUninstalledExtensions(), this.scanAllUserExtensions(this.extensionsPath, ExtensionType.User)])
+@@ -772,0 +777,7 @@ export class ExtensionManagementService extends Disposable implements IExtension
++	private scanAllUserExtensions(folderName: string, type: ExtensionType): Promise<ILocalExtension[]> {
++		return Promise.all([
++			this.scanExtensions(folderName, type),
++			...this.environmentService.extraExtensionPaths.map((p) => this.scanExtensions(p, ExtensionType.User))
++		]).then((results) => results.reduce((flat, current) => flat.concat(current), []));
++	}
++
+@@ -805 +816 @@ export class ExtensionManagementService extends Disposable implements IExtension
+-			.then(uninstalled => this.scanExtensions(this.extensionsPath, ExtensionType.User) // All user extensions
++			.then(uninstalled => this.scanAllUserExtensions(this.extensionsPath, ExtensionType.User) // All user extensions
+@@ -814 +825 @@ export class ExtensionManagementService extends Disposable implements IExtension
+-		return this.scanExtensions(this.extensionsPath, ExtensionType.User) // All user extensions
++		return this.scanAllUserExtensions(this.extensionsPath, ExtensionType.User) // All user extensions
+diff --git a/src/vs/platform/storage/node/storageMainService.ts b/src/vs/platform/storage/node/storageMainService.ts
+index 9845da1..567c195 100644
+--- a/src/vs/platform/storage/node/storageMainService.ts
++++ b/src/vs/platform/storage/node/storageMainService.ts
+@@ -169 +169,6 @@ export class StorageMainService extends Disposable implements IStorageMainServic
+-			return readdir(this.environmentService.extensionsPath).then(extensions => {
++			return Promise.all([
++				this.environmentService.extensionsPath,
++				...this.environmentService.extraExtensionPaths,
++			].map((p) => readdir(p)))
++				.then((results) => results.reduce((flat, current) => flat.concat(current), []))
++				.then(extensions => {
+diff --git a/src/vs/platform/telemetry/electron-browser/telemetryService.ts b/src/vs/platform/telemetry/electron-browser/telemetryService.ts
+index 31d0309..5b166af 100644
+--- a/src/vs/platform/telemetry/electron-browser/telemetryService.ts
++++ b/src/vs/platform/telemetry/electron-browser/telemetryService.ts
+@@ -42 +42 @@ export class TelemetryService extends Disposable implements ITelemetryService {
+-				piiPaths: [environmentService.appRoot, environmentService.extensionsPath]
++				piiPaths: [environmentService.appRoot, environmentService.extensionsPath, ...environmentService.extraExtensionPaths]
 diff --git a/src/vs/platform/windows/common/windows.ts b/src/vs/platform/windows/common/windows.ts
 index cbc55b3..9d27c01 100644
 --- a/src/vs/platform/windows/common/windows.ts
@@ -1045,25 +1086,23 @@ index 75f0026..2e94683 100644
 -		if (!isMacintosh && getTitleBarStyle(configurationService, environmentService) === 'custom') {
 +		if (!(isNative && isMacintosh) && getTitleBarStyle(configurationService, environmentService) === 'custom') {
 diff --git a/src/vs/workbench/services/extensions/electron-browser/cachedExtensionScanner.ts b/src/vs/workbench/services/extensions/electron-browser/cachedExtensionScanner.ts
-index 059f821..b58fd28 100644
+index 059f821..ca30811 100644
 --- a/src/vs/workbench/services/extensions/electron-browser/cachedExtensionScanner.ts
 +++ b/src/vs/workbench/services/extensions/electron-browser/cachedExtensionScanner.ts
 @@ -32,0 +33 @@ function getSystemExtensionsRoot(): string {
 +	return (require('vs/../../../../packages/vscode/src/fill/paths') as typeof import ('vs/../../../../packages/vscode/src/fill/paths')).getBuiltInExtensionsDirectory();
-@@ -280,0 +282,13 @@ export class CachedExtensionScanner {
-+		const extraBuiltinExtensionDirectories = (require('vs/../../../../packages/vscode/src/fill/paths') as typeof import ('vs/../../../../packages/vscode/src/fill/paths')).getExtraBuiltinExtensionDirectories();
-+		extraBuiltinExtensionDirectories.forEach((dir) => {
-+			const input = new ExtensionScannerInput(version, commit, locale, devMode, dir, true, false, translations);
-+			const extraBuiltinExtensions = this._scanExtensionsWithCache(
-+				windowService,
-+				notificationService,
-+				environmentService,
-+				BUILTIN_MANIFEST_CACHE_FILE,
-+				new ExtensionScannerInput(version, commit, locale, devMode, dir, true, false, translations),
-+				log
-+			);
-+			finalBuiltinExtensions = ExtensionScanner.mergeBuiltinExtensions(finalBuiltinExtensions, extraBuiltinExtensions);
-+		});
+@@ -191,2 +192,3 @@ export class CachedExtensionScanner {
+-			const folderStat = await pfs.stat(input.absoluteFolderPath);
+-			input.mtime = folderStat.mtime.getTime();
++			const folderStats = await Promise.all([pfs.stat(input.absoluteFolderPath), ...input.extraFolderPaths.map((p) => pfs.stat(p))]);
++			input.mtime = folderStats[0].mtime.getTime();
++			input.mtimes = folderStats.map((s) => s.mtime.getTime());
+@@ -259 +261 @@ export class CachedExtensionScanner {
+-			new ExtensionScannerInput(version, commit, locale, devMode, getSystemExtensionsRoot(), true, false, translations),
++			new ExtensionScannerInput(version, commit, locale, devMode, getSystemExtensionsRoot(), true, false, translations, environmentService.extraBuiltinExtensionPaths),
+@@ -290 +292 @@ export class CachedExtensionScanner {
+-					new ExtensionScannerInput(version, commit, locale, devMode, environmentService.extensionsPath, false, false, translations),
++					new ExtensionScannerInput(version, commit, locale, devMode, environmentService.extensionsPath, false, false, translations, environmentService.extraExtensionPaths),
 diff --git a/src/vs/workbench/services/extensions/electron-browser/extensionService.ts b/src/vs/workbench/services/extensions/electron-browser/extensionService.ts
 index b337206..0477464 100644
 --- a/src/vs/workbench/services/extensions/electron-browser/extensionService.ts
@@ -1081,6 +1120,64 @@ index 838a9c7..2308cee 100644
 @@ -192 +192 @@ function connectToRenderer(protocol: IMessagePassingProtocol): Promise<IRenderer
 -					process.kill(initData.parentPid, 0); // throws an exception if the main process doesn't exist anymore.
 +					// process.kill(initData.parentPid, 0); // throws an exception if the main process doesn't exist anymore.
+diff --git a/src/vs/workbench/services/extensions/node/extensionPoints.ts b/src/vs/workbench/services/extensions/node/extensionPoints.ts
+index 6e2179d..678fc06 100644
+--- a/src/vs/workbench/services/extensions/node/extensionPoints.ts
++++ b/src/vs/workbench/services/extensions/node/extensionPoints.ts
+@@ -445,0 +446 @@ export class ExtensionScannerInput {
++	public mtimes: number[] = [];
+@@ -455 +456,2 @@ export class ExtensionScannerInput {
+-		public readonly tanslations: Translations
++		public readonly tanslations: Translations,
++		public readonly extraFolderPaths: string[] = [],
+@@ -469,0 +472,26 @@ export class ExtensionScannerInput {
++		// Allow extra folder paths in any order. Doesn't account for duplicates though.
++		const eq = (a: string[], b: string[], atimes: number[], btimes: number[]): boolean => {
++			// Having none of it means they are equal.
++			if (!a && !b && !atimes && !btimes) {
++				return true;
++			}
++			// If at least one exists, they must all exist.
++			if (!a || !b || !atimes || !btimes) {
++				return false;
++			}
++			// Must all be the same length.
++			if (a.length !== b.length || atimes.length !== btimes.length) {
++				return false;
++			}
++			// Must contain the same paths and the same mtime for each path.
++			for (let i = 0; i < a.length; ++i) {
++				const index = b.indexOf(a[i]);
++				if (index === -1) {
++					return false;
++				}
++				if (atimes[i] !== btimes[index]) {
++					return false;
++				}
++			}
++			return true;
++		};
+@@ -479,0 +508 @@ export class ExtensionScannerInput {
++			&& eq(a.extraFolderPaths, b.extraFolderPaths, a.mtimes, b.mtimes)
+@@ -530 +559 @@ export class ExtensionScanner {
+-	 * Scan a list of extensions defined in `absoluteFolderPath`
++	 * Scan a list of extensions defined in `absoluteFolderPath` and `extraFolderPaths`
+@@ -532 +561 @@ export class ExtensionScanner {
+-	public static async scanExtensions(input: ExtensionScannerInput, log: ILog, resolver: IExtensionResolver | null = null): Promise<IExtensionDescription[]> {
++	public static async scanExtensions(input: ExtensionScannerInput, log: ILog, resolvers: IExtensionResolver | IExtensionResolver[] | null = null): Promise<IExtensionDescription[]> {
+@@ -533,0 +563 @@ export class ExtensionScanner {
++		const extraFolderPaths = input.extraFolderPaths;
+@@ -537,2 +567,4 @@ export class ExtensionScanner {
+-		if (!resolver) {
+-			resolver = new DefaultExtensionResolver(absoluteFolderPath);
++		if (!resolvers) {
++			resolvers = [absoluteFolderPath, ...extraFolderPaths].map((p) => new DefaultExtensionResolver(p));
++		} else if (!Array.isArray(resolvers)) {
++			resolvers = [resolvers];
+@@ -552 +584,2 @@ export class ExtensionScanner {
+-			let refs = await resolver.resolveExtensions();
++			let refs = await Promise.all(resolvers.map((resolver) => resolver.resolveExtensions()))
++				.then((results) => results.reduce((flat, current) => flat.concat(current), []));
 diff --git a/src/vs/workbench/services/files/node/watcher/nsfw/watcherService.ts b/src/vs/workbench/services/files/node/watcher/nsfw/watcherService.ts
 index 33d3697..af71b01 100644
 --- a/src/vs/workbench/services/files/node/watcher/nsfw/watcherService.ts

From f437287c8fc8bf46bb910a9db279dde010014f61 Mon Sep 17 00:00:00 2001
From: Asher <ash@coder.com>
Date: Sun, 19 May 2019 17:12:58 -0500
Subject: [PATCH 4/5] Fix cached mtimes for extra extension dirs

---
 scripts/vscode.patch | 10 +++++-----
 1 file changed, 5 insertions(+), 5 deletions(-)

diff --git a/scripts/vscode.patch b/scripts/vscode.patch
index df51dd709da8..3d8dc2371e87 100644
--- a/scripts/vscode.patch
+++ b/scripts/vscode.patch
@@ -1086,7 +1086,7 @@ index 75f0026..2e94683 100644
 -		if (!isMacintosh && getTitleBarStyle(configurationService, environmentService) === 'custom') {
 +		if (!(isNative && isMacintosh) && getTitleBarStyle(configurationService, environmentService) === 'custom') {
 diff --git a/src/vs/workbench/services/extensions/electron-browser/cachedExtensionScanner.ts b/src/vs/workbench/services/extensions/electron-browser/cachedExtensionScanner.ts
-index 059f821..ca30811 100644
+index 059f821..b19f292 100644
 --- a/src/vs/workbench/services/extensions/electron-browser/cachedExtensionScanner.ts
 +++ b/src/vs/workbench/services/extensions/electron-browser/cachedExtensionScanner.ts
 @@ -32,0 +33 @@ function getSystemExtensionsRoot(): string {
@@ -1096,7 +1096,7 @@ index 059f821..ca30811 100644
 -			input.mtime = folderStat.mtime.getTime();
 +			const folderStats = await Promise.all([pfs.stat(input.absoluteFolderPath), ...input.extraFolderPaths.map((p) => pfs.stat(p))]);
 +			input.mtime = folderStats[0].mtime.getTime();
-+			input.mtimes = folderStats.map((s) => s.mtime.getTime());
++			input.extraMtimes = folderStats.slice(1).map((s) => s.mtime.getTime());
 @@ -259 +261 @@ export class CachedExtensionScanner {
 -			new ExtensionScannerInput(version, commit, locale, devMode, getSystemExtensionsRoot(), true, false, translations),
 +			new ExtensionScannerInput(version, commit, locale, devMode, getSystemExtensionsRoot(), true, false, translations, environmentService.extraBuiltinExtensionPaths),
@@ -1121,11 +1121,11 @@ index 838a9c7..2308cee 100644
 -					process.kill(initData.parentPid, 0); // throws an exception if the main process doesn't exist anymore.
 +					// process.kill(initData.parentPid, 0); // throws an exception if the main process doesn't exist anymore.
 diff --git a/src/vs/workbench/services/extensions/node/extensionPoints.ts b/src/vs/workbench/services/extensions/node/extensionPoints.ts
-index 6e2179d..678fc06 100644
+index 6e2179d..099d7ea 100644
 --- a/src/vs/workbench/services/extensions/node/extensionPoints.ts
 +++ b/src/vs/workbench/services/extensions/node/extensionPoints.ts
 @@ -445,0 +446 @@ export class ExtensionScannerInput {
-+	public mtimes: number[] = [];
++	public extraMtimes: number[] = [];
 @@ -455 +456,2 @@ export class ExtensionScannerInput {
 -		public readonly tanslations: Translations
 +		public readonly tanslations: Translations,
@@ -1158,7 +1158,7 @@ index 6e2179d..678fc06 100644
 +			return true;
 +		};
 @@ -479,0 +508 @@ export class ExtensionScannerInput {
-+			&& eq(a.extraFolderPaths, b.extraFolderPaths, a.mtimes, b.mtimes)
++			&& eq(a.extraFolderPaths, b.extraFolderPaths, a.extraMtimes, b.extraMtimes)
 @@ -530 +559 @@ export class ExtensionScanner {
 -	 * Scan a list of extensions defined in `absoluteFolderPath`
 +	 * Scan a list of extensions defined in `absoluteFolderPath` and `extraFolderPaths`

From f72b43ff788b939e5675c931eef09cb48ca78e5e Mon Sep 17 00:00:00 2001
From: Asher <ash@coder.com>
Date: Sun, 19 May 2019 17:18:05 -0500
Subject: [PATCH 5/5] Simplify extension cache equality check

---
 scripts/vscode.patch | 28 +++++++++-------------------
 1 file changed, 9 insertions(+), 19 deletions(-)

diff --git a/scripts/vscode.patch b/scripts/vscode.patch
index 3d8dc2371e87..895830f340b6 100644
--- a/scripts/vscode.patch
+++ b/scripts/vscode.patch
@@ -1121,7 +1121,7 @@ index 838a9c7..2308cee 100644
 -					process.kill(initData.parentPid, 0); // throws an exception if the main process doesn't exist anymore.
 +					// process.kill(initData.parentPid, 0); // throws an exception if the main process doesn't exist anymore.
 diff --git a/src/vs/workbench/services/extensions/node/extensionPoints.ts b/src/vs/workbench/services/extensions/node/extensionPoints.ts
-index 6e2179d..099d7ea 100644
+index 6e2179d..e6f38c9 100644
 --- a/src/vs/workbench/services/extensions/node/extensionPoints.ts
 +++ b/src/vs/workbench/services/extensions/node/extensionPoints.ts
 @@ -445,0 +446 @@ export class ExtensionScannerInput {
@@ -1130,22 +1130,12 @@ index 6e2179d..099d7ea 100644
 -		public readonly tanslations: Translations
 +		public readonly tanslations: Translations,
 +		public readonly extraFolderPaths: string[] = [],
-@@ -469,0 +472,26 @@ export class ExtensionScannerInput {
+@@ -469,0 +472,16 @@ export class ExtensionScannerInput {
 +		// Allow extra folder paths in any order. Doesn't account for duplicates though.
-+		const eq = (a: string[], b: string[], atimes: number[], btimes: number[]): boolean => {
-+			// Having none of it means they are equal.
-+			if (!a && !b && !atimes && !btimes) {
-+				return true;
-+			}
-+			// If at least one exists, they must all exist.
-+			if (!a || !b || !atimes || !btimes) {
-+				return false;
-+			}
-+			// Must all be the same length.
++		const eq = (a: string[] = [], b: string[] = [], atimes: number[] = [], btimes: number[] = []): boolean => {
 +			if (a.length !== b.length || atimes.length !== btimes.length) {
 +				return false;
 +			}
-+			// Must contain the same paths and the same mtime for each path.
 +			for (let i = 0; i < a.length; ++i) {
 +				const index = b.indexOf(a[i]);
 +				if (index === -1) {
@@ -1157,24 +1147,24 @@ index 6e2179d..099d7ea 100644
 +			}
 +			return true;
 +		};
-@@ -479,0 +508 @@ export class ExtensionScannerInput {
+@@ -479,0 +498 @@ export class ExtensionScannerInput {
 +			&& eq(a.extraFolderPaths, b.extraFolderPaths, a.extraMtimes, b.extraMtimes)
-@@ -530 +559 @@ export class ExtensionScanner {
+@@ -530 +549 @@ export class ExtensionScanner {
 -	 * Scan a list of extensions defined in `absoluteFolderPath`
 +	 * Scan a list of extensions defined in `absoluteFolderPath` and `extraFolderPaths`
-@@ -532 +561 @@ export class ExtensionScanner {
+@@ -532 +551 @@ export class ExtensionScanner {
 -	public static async scanExtensions(input: ExtensionScannerInput, log: ILog, resolver: IExtensionResolver | null = null): Promise<IExtensionDescription[]> {
 +	public static async scanExtensions(input: ExtensionScannerInput, log: ILog, resolvers: IExtensionResolver | IExtensionResolver[] | null = null): Promise<IExtensionDescription[]> {
-@@ -533,0 +563 @@ export class ExtensionScanner {
+@@ -533,0 +553 @@ export class ExtensionScanner {
 +		const extraFolderPaths = input.extraFolderPaths;
-@@ -537,2 +567,4 @@ export class ExtensionScanner {
+@@ -537,2 +557,4 @@ export class ExtensionScanner {
 -		if (!resolver) {
 -			resolver = new DefaultExtensionResolver(absoluteFolderPath);
 +		if (!resolvers) {
 +			resolvers = [absoluteFolderPath, ...extraFolderPaths].map((p) => new DefaultExtensionResolver(p));
 +		} else if (!Array.isArray(resolvers)) {
 +			resolvers = [resolvers];
-@@ -552 +584,2 @@ export class ExtensionScanner {
+@@ -552 +574,2 @@ export class ExtensionScanner {
 -			let refs = await resolver.resolveExtensions();
 +			let refs = await Promise.all(resolvers.map((resolver) => resolver.resolveExtensions()))
 +				.then((results) => results.reduce((flat, current) => flat.concat(current), []));
