update.ts 12.6 KB
Newer Older
E
Erich Gamma 已提交
1 2 3 4 5 6 7 8 9
/*---------------------------------------------------------------------------------------------
 *  Copyright (c) Microsoft Corporation. All rights reserved.
 *  Licensed under the MIT License. See License.txt in the project root for license information.
 *--------------------------------------------------------------------------------------------*/

'use strict';

import nls = require('vs/nls');
import severity from 'vs/base/common/severity';
J
Johannes Rieken 已提交
10
import { TPromise } from 'vs/base/common/winjs.base';
J
Joao Moreno 已提交
11
import { IAction, Action } from 'vs/base/common/actions';
I
isidor 已提交
12
import { Separator } from 'vs/base/browser/ui/actionbar/actionbar';
13
import { IMessageService, CloseAction, Severity } from 'vs/platform/message/common/message';
14 15
import pkg from 'vs/platform/node/package';
import product from 'vs/platform/node/product';
J
Joao Moreno 已提交
16
import URI from 'vs/base/common/uri';
J
Joao Moreno 已提交
17
import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService';
18
import { IActivityBarService, TextBadge } from 'vs/workbench/services/activity/common/activityBarService';
J
Joao Moreno 已提交
19
import { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation';
J
Joao Moreno 已提交
20
import { ReleaseNotesInput } from 'vs/workbench/parts/update/electron-browser/releaseNotesInput';
J
Joao Moreno 已提交
21
import { IGlobalActivity } from 'vs/workbench/browser/activity';
J
Joao Moreno 已提交
22
import { IRequestService } from 'vs/platform/request/node/request';
J
Joao Moreno 已提交
23 24
import { asText } from 'vs/base/node/request';
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
A
Alex Dima 已提交
25
import { KeybindingIO } from 'vs/workbench/services/keybinding/common/keybindingIO';
J
Joao Moreno 已提交
26
import { IOpenerService } from 'vs/platform/opener/common/opener';
J
Joao Moreno 已提交
27
import { ICommandService } from 'vs/platform/commands/common/commands';
28 29
import { IWorkbenchContribution } from 'vs/workbench/common/contributions';
import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage';
J
Joao Moreno 已提交
30
import { IUpdateService, State as UpdateState } from 'vs/platform/update/common/update';
31
import * as semver from 'semver';
32
import { OS, isLinux, isWindows } from 'vs/base/common/platform';
E
Erich Gamma 已提交
33

34 35 36 37
class ApplyUpdateAction extends Action {
	constructor( @IUpdateService private updateService: IUpdateService) {
		super('update.applyUpdate', nls.localize('updateNow', "Update Now"), null, true);
	}
E
Erich Gamma 已提交
38

39 40 41 42
	run(): TPromise<void> {
		return this.updateService.quitAndInstall();
	}
}
E
Erich Gamma 已提交
43

J
Joao Moreno 已提交
44 45 46 47 48 49 50
const NotNowAction = new Action(
	'update.later',
	nls.localize('later', "Later"),
	null,
	true,
	() => TPromise.as(true)
);
E
Erich Gamma 已提交
51

J
Joao Moreno 已提交
52
const releaseNotesCache: { [version: string]: TPromise<string>; } = Object.create(null);
N
Nick Olinger 已提交
53

J
Joao Moreno 已提交
54 55 56
export function loadReleaseNotes(accessor: ServicesAccessor, version: string): TPromise<string> {
	const requestService = accessor.get(IRequestService);
	const keybindingService = accessor.get(IKeybindingService);
J
Joao Moreno 已提交
57
	const match = /^(\d+\.\d+)\./.exec(version);
J
Joao Moreno 已提交
58 59

	if (!match) {
60
		return TPromise.wrapError<string>('not found');
J
Joao Moreno 已提交
61 62 63 64
	}

	const versionLabel = match[1].replace(/\./g, '_');
	const baseUrl = 'https://code.visualstudio.com/raw';
J
Johannes Rieken 已提交
65
	const url = `${baseUrl}/v${versionLabel}.md`;
J
Joao Moreno 已提交
66
	const unassigned = nls.localize('unassigned', "unassigned");
J
Joao Moreno 已提交
67 68 69

	const patchKeybindings = (text: string): string => {
		const kb = (match: string, kb: string) => {
70
			const keybinding = keybindingService.lookupKeybinding(kb);
J
Joao Moreno 已提交
71 72

			if (!keybinding) {
J
Joao Moreno 已提交
73
				return unassigned;
J
Joao Moreno 已提交
74 75
			}

76
			return keybinding.getLabel();
J
Joao Moreno 已提交
77 78 79
		};

		const kbstyle = (match: string, kb: string) => {
80
			const keybinding = KeybindingIO.readKeybinding(kb, OS);
J
Joao Moreno 已提交
81 82

			if (!keybinding) {
J
Joao Moreno 已提交
83
				return unassigned;
J
Joao Moreno 已提交
84 85
			}

86
			const resolvedKeybindings = keybindingService.resolveKeybinding(keybinding);
87

88 89 90 91 92
			if (resolvedKeybindings.length === 0) {
				return unassigned;
			}

			return resolvedKeybindings[0].getLabel();
J
Joao Moreno 已提交
93 94 95 96 97 98 99
		};

		return text
			.replace(/kb\(([a-z.\d\-]+)\)/gi, kb)
			.replace(/kbstyle\(([^\)]+)\)/gi, kbstyle);
	};

J
Joao Moreno 已提交
100 101
	if (!releaseNotesCache[version]) {
		releaseNotesCache[version] = requestService.request({ url })
N
Nick Olinger 已提交
102
			.then(asText)
J
Joao Moreno 已提交
103
			.then(text => patchKeybindings(text));
N
Nick Olinger 已提交
104
	}
J
Joao Moreno 已提交
105 106

	return releaseNotesCache[version];
J
Joao Moreno 已提交
107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122
}

export class OpenLatestReleaseNotesInBrowserAction extends Action {

	constructor(
		@IOpenerService private openerService: IOpenerService
	) {
		super('update.openLatestReleaseNotes', nls.localize('releaseNotes', "Release Notes"), null, true);
	}

	run(): TPromise<any> {
		const uri = URI.parse(product.releaseNotesUrl);
		return this.openerService.open(uri);
	}
}

J
Joao Moreno 已提交
123
export abstract class AbstractShowReleaseNotesAction extends Action {
J
Joao Moreno 已提交
124 125

	constructor(
B
Benjamin Pasero 已提交
126 127
		id: string,
		label: string,
J
Joao Moreno 已提交
128 129 130
		private returnValue: boolean,
		private version: string,
		@IWorkbenchEditorService private editorService: IWorkbenchEditorService,
B
Benjamin Pasero 已提交
131
		@IInstantiationService private instantiationService: IInstantiationService
J
Joao Moreno 已提交
132
	) {
J
Joao Moreno 已提交
133
		super(id, label, null, true);
J
Joao Moreno 已提交
134 135 136
	}

	run(): TPromise<boolean> {
J
Joao Moreno 已提交
137 138 139 140 141 142 143
		if (!this.enabled) {
			return TPromise.as(false);
		}

		this.enabled = false;

		return this.instantiationService.invokeFunction(loadReleaseNotes, this.version)
B
Benjamin Pasero 已提交
144
			.then(text => this.editorService.openEditor(this.instantiationService.createInstance(ReleaseNotesInput, this.version, text), { pinned: true }))
J
Joao Moreno 已提交
145 146 147 148 149
			.then(() => true)
			.then(null, () => {
				const action = this.instantiationService.createInstance(OpenLatestReleaseNotesInBrowserAction);
				return action.run().then(() => false);
			});
J
Joao Moreno 已提交
150 151
	}
}
E
Erich Gamma 已提交
152

J
Joao Moreno 已提交
153 154 155 156 157 158
export class ShowReleaseNotesAction extends AbstractShowReleaseNotesAction {

	constructor(
		returnValue: boolean,
		version: string,
		@IWorkbenchEditorService editorService: IWorkbenchEditorService,
B
Benjamin Pasero 已提交
159
		@IInstantiationService instantiationService: IInstantiationService
J
Joao Moreno 已提交
160
	) {
B
Benjamin Pasero 已提交
161
		super('update.showReleaseNotes', nls.localize('releaseNotes', "Release Notes"), returnValue, version, editorService, instantiationService);
J
Joao Moreno 已提交
162 163 164 165 166 167 168 169 170 171 172 173
	}
}

export class ShowCurrentReleaseNotesAction extends AbstractShowReleaseNotesAction {

	static ID = 'update.showCurrentReleaseNotes';
	static LABEL = nls.localize('showReleaseNotes', "Show Release Notes");

	constructor(
		id = ShowCurrentReleaseNotesAction.ID,
		label = ShowCurrentReleaseNotesAction.LABEL,
		@IWorkbenchEditorService editorService: IWorkbenchEditorService,
B
Benjamin Pasero 已提交
174
		@IInstantiationService instantiationService: IInstantiationService
J
Joao Moreno 已提交
175
	) {
B
Benjamin Pasero 已提交
176
		super(id, label, true, pkg.version, editorService, instantiationService);
J
Joao Moreno 已提交
177 178 179
	}
}

J
Joao Moreno 已提交
180 181
export class DownloadAction extends Action {

J
Joao Moreno 已提交
182
	constructor(private url: string, @IUpdateService private updateService: IUpdateService) {
J
Joao Moreno 已提交
183 184 185 186
		super('update.download', nls.localize('downloadNow', "Download Now"), null, true);
	}

	run(): TPromise<void> {
J
Joao Moreno 已提交
187
		return this.updateService.quitAndInstall();
J
Joao Moreno 已提交
188 189
	}
}
J
Joao Moreno 已提交
190

191 192
const LinkAction = (id: string, message: string, licenseUrl: string) => new Action(
	id, message, null, true,
B
Benjamin Pasero 已提交
193
	() => { window.open(licenseUrl); return TPromise.as(null); }
194 195
);

J
Joao Moreno 已提交
196
export class ProductContribution implements IWorkbenchContribution {
197 198

	private static KEY = 'releaseNotes/lastVersion';
J
Joao Moreno 已提交
199
	getId() { return 'vs.product'; }
E
Erich Gamma 已提交
200 201

	constructor(
202 203 204 205
		@IStorageService storageService: IStorageService,
		@IInstantiationService instantiationService: IInstantiationService,
		@IMessageService messageService: IMessageService,
		@IWorkbenchEditorService editorService: IWorkbenchEditorService
E
Erich Gamma 已提交
206
	) {
J
Joao Moreno 已提交
207
		const lastVersion = storageService.get(ProductContribution.KEY, StorageScope.GLOBAL, '');
208

J
Joao Moreno 已提交
209
		// was there an update? if so, open release notes
210
		if (product.releaseNotesUrl && lastVersion && pkg.version !== lastVersion) {
J
Joao Moreno 已提交
211
			instantiationService.invokeFunction(loadReleaseNotes, pkg.version).then(
B
Benjamin Pasero 已提交
212
				text => editorService.openEditor(instantiationService.createInstance(ReleaseNotesInput, pkg.version, text), { pinned: true }),
213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234
				() => {
					messageService.show(Severity.Info, {
						message: nls.localize('read the release notes', "Welcome to {0} v{1}! Would you like to read the Release Notes?", product.nameLong, pkg.version),
						actions: [
							instantiationService.createInstance(OpenLatestReleaseNotesInBrowserAction),
							CloseAction
						]
					});
				});
		}

		// should we show the new license?
		if (product.licenseUrl && lastVersion && semver.satisfies(lastVersion, '<1.0.0') && semver.satisfies(pkg.version, '>=1.0.0')) {
			messageService.show(Severity.Info, {
				message: nls.localize('licenseChanged', "Our license terms have changed, please go through them.", product.nameLong, pkg.version),
				actions: [
					LinkAction('update.showLicense', nls.localize('license', "Read License"), product.licenseUrl),
					CloseAction
				]
			});
		}

J
Joao Moreno 已提交
235 236 237 238 239
		storageService.store(ProductContribution.KEY, pkg.version, StorageScope.GLOBAL);
	}
}

export class UpdateContribution implements IWorkbenchContribution {
240

J
Joao Moreno 已提交
241 242 243 244 245 246 247
	getId() { return 'vs.update'; }

	constructor(
		@IInstantiationService instantiationService: IInstantiationService,
		@IMessageService messageService: IMessageService,
		@IUpdateService updateService: IUpdateService
	) {
248 249
		updateService.onUpdateReady(update => {
			const applyUpdateAction = instantiationService.createInstance(ApplyUpdateAction);
250
			const releaseNotesAction = instantiationService.createInstance(ShowReleaseNotesAction, false, update.version);
J
Joao Moreno 已提交
251

252
			messageService.show(severity.Info, {
253
				message: nls.localize('updateAvailable', "{0} will be updated after it restarts.", product.nameLong),
254
				actions: [applyUpdateAction, NotNowAction, releaseNotesAction]
E
Erich Gamma 已提交
255 256 257
			});
		});

258
		updateService.onUpdateAvailable(update => {
J
Joao Moreno 已提交
259
			const downloadAction = instantiationService.createInstance(DownloadAction, update.version);
260
			const releaseNotesAction = instantiationService.createInstance(ShowReleaseNotesAction, false, update.version);
J
Joao Moreno 已提交
261

262
			messageService.show(severity.Info, {
J
Joao Moreno 已提交
263
				message: nls.localize('thereIsUpdateAvailable', "There is an available update."),
J
Joao Moreno 已提交
264
				actions: [downloadAction, NotNowAction, releaseNotesAction]
J
Joao Moreno 已提交
265 266 267
			});
		});

268 269 270 271 272
		updateService.onUpdateNotAvailable(explicit => {
			if (!explicit) {
				return;
			}

273
			messageService.show(severity.Info, nls.localize('noUpdatesAvailable', "There are no updates currently available."));
E
Erich Gamma 已提交
274
		});
J
Joao Moreno 已提交
275 276

		updateService.onError(err => messageService.show(severity.Error, err));
E
Erich Gamma 已提交
277
	}
J
Joao Moreno 已提交
278 279
}

J
Joao Moreno 已提交
280
export class LightUpdateContribution implements IGlobalActivity {
J
Joao Moreno 已提交
281 282

	get id() { return 'vs.update'; }
283
	get name() { return ''; }
J
Joao Moreno 已提交
284 285 286 287
	get cssClass() { return 'update-activity'; }

	constructor(
		@IStorageService storageService: IStorageService,
J
Joao Moreno 已提交
288
		@ICommandService private commandService: ICommandService,
J
Joao Moreno 已提交
289 290
		@IInstantiationService instantiationService: IInstantiationService,
		@IMessageService messageService: IMessageService,
J
Joao Moreno 已提交
291
		@IUpdateService private updateService: IUpdateService,
J
Joao Moreno 已提交
292 293 294
		@IWorkbenchEditorService editorService: IWorkbenchEditorService,
		@IActivityBarService activityBarService: IActivityBarService
	) {
J
Joao Moreno 已提交
295
		this.updateService.onUpdateReady(() => {
296
			const badge = new TextBadge('\u21e9', () => nls.localize('updateIsReady', "New update available."));
J
Joao Moreno 已提交
297
			activityBarService.showGlobalActivity(this.id, badge);
J
Joao Moreno 已提交
298
		});
J
Joao Moreno 已提交
299

J
Joao Moreno 已提交
300
		this.updateService.onError(err => messageService.show(severity.Error, err));
J
Joao Moreno 已提交
301 302 303
	}

	getActions(): IAction[] {
I
isidor 已提交
304 305 306 307 308 309 310 311 312 313 314
		return [
			new Action('showCommandPalette', nls.localize('commandPalette', "Command Palette..."), undefined, true, () => this.commandService.executeCommand('workbench.action.showCommands')),
			new Separator(),
			new Action('openKeybindings', nls.localize('settings', "Settings"), null, true, () => this.commandService.executeCommand('workbench.action.openGlobalSettings')),
			new Action('openSettings', nls.localize('keyboardShortcuts', "Keyboard Shortcuts"), null, true, () => this.commandService.executeCommand('workbench.action.openGlobalKeybindings')),
			new Separator(),
			this.getUpdateAction()
		];
	}

	private getUpdateAction(): IAction {
J
Joao Moreno 已提交
315 316
		switch (this.updateService.state) {
			case UpdateState.Uninitialized:
I
isidor 已提交
317
				return new Action('update.notavailable', nls.localize('not available', "Updates Not Available"), undefined, false);
J
Joao Moreno 已提交
318 319

			case UpdateState.CheckingForUpdate:
I
isidor 已提交
320
				return new Action('update.checking', nls.localize('checkingForUpdates', "Checking For Updates..."), undefined, false);
J
Joao Moreno 已提交
321 322

			case UpdateState.UpdateAvailable:
323
				if (isLinux) {
I
isidor 已提交
324 325
					return new Action('update.linux.available', nls.localize('DownloadUpdate', "Download Available Update"), undefined, true, () =>
						this.updateService.quitAndInstall());
326 327 328 329 330 331
				}

				const updateAvailableLabel = isWindows
					? nls.localize('DownloadingUpdate', "Downloading Update...")
					: nls.localize('InstallingUpdate', "Installing Update...");

I
isidor 已提交
332
				return new Action('update.available', updateAvailableLabel, undefined, false);
J
Joao Moreno 已提交
333 334

			case UpdateState.UpdateDownloaded:
I
isidor 已提交
335 336
				return new Action('update.restart', nls.localize('restartToUpdate', "Restart To Update..."), undefined, true, () =>
					this.updateService.quitAndInstall());
J
Joao Moreno 已提交
337 338

			default:
I
isidor 已提交
339 340
				return new Action('update.check', nls.localize('checkForUpdates', "Check For Updates..."), undefined, this.updateService.state === UpdateState.Idle, () =>
					this.updateService.checkForUpdates(true));
J
Joao Moreno 已提交
341
		}
J
Joao Moreno 已提交
342
	}
343
}