Merge from vscode 93309f060778f6480a7d2a13913e6e7c624e9bc7

This commit is contained in:
ADS Merger
2020-03-26 07:08:23 +00:00
parent 685e0ccf7e
commit f5134198e2
87 changed files with 942 additions and 631 deletions

View File

@@ -5,7 +5,7 @@
@font-face {
font-family: "codicon";
src: url("./codicon.ttf?3d9ee7d873425ff0bc441f48a1de0c54") format("truetype");
src: url("./codicon.ttf?70edf2c63384951357ed8517386759dd") format("truetype");
}
.codicon[class*='codicon-'] {

View File

@@ -21,6 +21,7 @@ import { asArray } from 'vs/base/common/arrays';
import { ScanCodeUtils, ScanCode } from 'vs/base/common/scanCode';
import { isMacintosh } from 'vs/base/common/platform';
import { StandardMouseEvent } from 'vs/base/browser/mouseEvent';
import { Separator } from 'vs/base/browser/ui/actionbar/actionbar';
const $ = DOM.$;
@@ -90,7 +91,7 @@ export class MenuBar extends Disposable {
private menuStyle: IMenuStyles | undefined;
private overflowLayoutScheduled: IDisposable | undefined = undefined;
constructor(private container: HTMLElement, private options: IMenuBarOptions = {}) {
constructor(private container: HTMLElement, private options: IMenuBarOptions = {}, private compactMenuActions?: IAction[]) {
super();
this.container.setAttribute('role', 'menubar');
@@ -490,6 +491,11 @@ export class MenuBar extends Disposable {
this.container.insertBefore(this.overflowMenu.buttonElement, this.menuCache[this.numMenusShown].buttonElement);
this.overflowMenu.buttonElement.style.visibility = 'visible';
}
if (this.compactMenuActions && this.compactMenuActions.length) {
this.overflowMenu.actions.push(new Separator());
this.overflowMenu.actions.push(...this.compactMenuActions);
}
} else {
DOM.removeNode(this.overflowMenu.buttonElement);
this.container.appendChild(this.overflowMenu.buttonElement);

View File

@@ -74,26 +74,24 @@ export class Storage extends Disposable implements IStorage {
private static readonly DEFAULT_FLUSH_DELAY = 100;
private readonly _onDidChangeStorage: Emitter<string> = this._register(new Emitter<string>());
readonly onDidChangeStorage: Event<string> = this._onDidChangeStorage.event;
private readonly _onDidChangeStorage = this._register(new Emitter<string>());
readonly onDidChangeStorage = this._onDidChangeStorage.event;
private state = StorageState.None;
private cache: Map<string, string> = new Map<string, string>();
private cache = new Map<string, string>();
private flushDelayer: ThrottledDelayer<void>;
private readonly flushDelayer = this._register(new ThrottledDelayer<void>(Storage.DEFAULT_FLUSH_DELAY));
private pendingDeletes: Set<string> = new Set<string>();
private pendingInserts: Map<string, string> = new Map();
private pendingDeletes = new Set<string>();
private pendingInserts = new Map<string, string>();
constructor(
protected database: IStorageDatabase,
private options: IStorageOptions = Object.create(null)
protected readonly database: IStorageDatabase,
private readonly options: IStorageOptions = Object.create(null)
) {
super();
this.flushDelayer = this._register(new ThrottledDelayer(Storage.DEFAULT_FLUSH_DELAY));
this.registerListeners();
}
@@ -146,7 +144,7 @@ export class Storage extends Disposable implements IStorage {
async init(): Promise<void> {
if (this.state !== StorageState.None) {
return Promise.resolve(); // either closed or already initialized
return; // either closed or already initialized
}
this.state = StorageState.Initialized;
@@ -155,7 +153,7 @@ export class Storage extends Disposable implements IStorage {
// return early if we know the storage file does not exist. this is a performance
// optimization to not load all items of the underlying storage if we know that
// there can be no items because the storage does not exist.
return Promise.resolve();
return;
}
this.cache = await this.database.getItems();
@@ -296,13 +294,13 @@ export class InMemoryStorageDatabase implements IStorageDatabase {
readonly onDidChangeItemsExternal = Event.None;
private items = new Map<string, string>();
private readonly items = new Map<string, string>();
getItems(): Promise<Map<string, string>> {
return Promise.resolve(this.items);
async getItems(): Promise<Map<string, string>> {
return this.items;
}
updateItems(request: IUpdateRequest): Promise<void> {
async updateItems(request: IUpdateRequest): Promise<void> {
if (request.insert) {
request.insert.forEach((value, key) => this.items.set(key, value));
}
@@ -310,11 +308,7 @@ export class InMemoryStorageDatabase implements IStorageDatabase {
if (request.delete) {
request.delete.forEach(key => this.items.delete(key));
}
return Promise.resolve();
}
close(): Promise<void> {
return Promise.resolve();
}
async close(): Promise<void> { }
}

View File

@@ -13,9 +13,9 @@ import { fill } from 'vs/base/common/arrays';
import { IStorageDatabase, IStorageItemsChangeEvent, IUpdateRequest } from 'vs/base/parts/storage/common/storage';
interface IDatabaseConnection {
db: Database;
readonly db: Database;
isInMemory: boolean;
readonly isInMemory: boolean;
isErroneous?: boolean;
lastError?: string;
@@ -39,21 +39,13 @@ export class SQLiteStorageDatabase implements IStorageDatabase {
private static readonly BUSY_OPEN_TIMEOUT = 2000; // timeout in ms to retry when opening DB fails with SQLITE_BUSY
private static readonly MAX_HOST_PARAMETERS = 256; // maximum number of parameters within a statement
private path: string;
private name: string;
private readonly name = basename(this.path);
private logger: SQLiteStorageDatabaseLogger;
private readonly logger = new SQLiteStorageDatabaseLogger(this.options.logging);
private whenConnected: Promise<IDatabaseConnection>;
private readonly whenConnected = this.connect(this.path);
constructor(path: string, options: ISQLiteStorageDatabaseOptions = Object.create(null)) {
this.path = path;
this.name = basename(path);
this.logger = new SQLiteStorageDatabaseLogger(options.logging);
this.whenConnected = this.connect(path);
}
constructor(private path: string, private options: ISQLiteStorageDatabaseOptions = Object.create(null)) { }
async getItems(): Promise<Map<string, string>> {
const connection = await this.whenConnected;
@@ -166,7 +158,7 @@ export class SQLiteStorageDatabase implements IStorageDatabase {
return new Promise((resolve, reject) => {
connection.db.close(closeError => {
if (closeError) {
this.handleSQLiteError(connection, closeError, `[storage ${this.name}] close(): ${closeError}`);
this.handleSQLiteError(connection, `[storage ${this.name}] close(): ${closeError}`);
}
// Return early if this storage was created only in-memory
@@ -296,7 +288,7 @@ export class SQLiteStorageDatabase implements IStorageDatabase {
}
}
private handleSQLiteError(connection: IDatabaseConnection, error: Error & { code?: string }, msg: string): void {
private handleSQLiteError(connection: IDatabaseConnection, msg: string): void {
connection.isErroneous = true;
connection.lastError = msg;
@@ -328,7 +320,7 @@ export class SQLiteStorageDatabase implements IStorageDatabase {
};
// Errors
connection.db.on('error', error => this.handleSQLiteError(connection, error, `[storage ${this.name}] Error (event): ${error}`));
connection.db.on('error', error => this.handleSQLiteError(connection, `[storage ${this.name}] Error (event): ${error}`));
// Tracing
if (this.logger.isTracing) {
@@ -342,7 +334,7 @@ export class SQLiteStorageDatabase implements IStorageDatabase {
return new Promise((resolve, reject) => {
connection.db.exec(sql, error => {
if (error) {
this.handleSQLiteError(connection, error, `[storage ${this.name}] exec(): ${error}`);
this.handleSQLiteError(connection, `[storage ${this.name}] exec(): ${error}`);
return reject(error);
}
@@ -356,7 +348,7 @@ export class SQLiteStorageDatabase implements IStorageDatabase {
return new Promise((resolve, reject) => {
connection.db.get(sql, (error, row) => {
if (error) {
this.handleSQLiteError(connection, error, `[storage ${this.name}] get(): ${error}`);
this.handleSQLiteError(connection, `[storage ${this.name}] get(): ${error}`);
return reject(error);
}
@@ -370,7 +362,7 @@ export class SQLiteStorageDatabase implements IStorageDatabase {
return new Promise((resolve, reject) => {
connection.db.all(sql, (error, rows) => {
if (error) {
this.handleSQLiteError(connection, error, `[storage ${this.name}] all(): ${error}`);
this.handleSQLiteError(connection, `[storage ${this.name}] all(): ${error}`);
return reject(error);
}
@@ -389,7 +381,7 @@ export class SQLiteStorageDatabase implements IStorageDatabase {
connection.db.run('END TRANSACTION', error => {
if (error) {
this.handleSQLiteError(connection, error, `[storage ${this.name}] transaction(): ${error}`);
this.handleSQLiteError(connection, `[storage ${this.name}] transaction(): ${error}`);
return reject(error);
}
@@ -404,7 +396,7 @@ export class SQLiteStorageDatabase implements IStorageDatabase {
const stmt = connection.db.prepare(sql);
const statementErrorListener = (error: Error) => {
this.handleSQLiteError(connection, error, `[storage ${this.name}] prepare(): ${error} (${sql}). Details: ${errorDetails()}`);
this.handleSQLiteError(connection, `[storage ${this.name}] prepare(): ${error} (${sql}). Details: ${errorDetails()}`);
};
stmt.on('error', statementErrorListener);

View File

@@ -538,7 +538,7 @@ suite('URI', () => {
assert.throws(() => assertJoined(('foo:'), 'bazz', ''));
assert.throws(() => new URL('bazz', 'foo:'));
assert.throws(() => assertJoined(('foo://bar'), 'bazz', ''));
assert.throws(() => new URL('bazz', 'foo://bar'));
// assert.throws(() => new URL('bazz', 'foo://bar')); Edge,Chrome => throw, Safari => foo://bar/bazz, Firefox ??
});
test('URI#joinPath (posix)', function () {

View File

@@ -3,7 +3,7 @@
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { IWorkbenchConstructionOptions, create, URI, Emitter, UriComponents, ICredentialsProvider, IURLCallbackProvider, IWorkspaceProvider, IWorkspace, IApplicationLink } from 'vs/workbench/workbench.web.api';
import { IWorkbenchConstructionOptions, create, URI, Emitter, UriComponents, ICredentialsProvider, IURLCallbackProvider, IWorkspaceProvider, IWorkspace } from 'vs/workbench/workbench.web.api';
import { generateUuid } from 'vs/base/common/uuid';
import { CancellationToken } from 'vs/base/common/cancellation';
import { streamToBuffer } from 'vs/base/common/buffer';
@@ -12,10 +12,6 @@ import { request } from 'vs/base/parts/request/browser/request';
import { isFolderToOpen, isWorkspaceToOpen } from 'vs/platform/windows/common/windows';
import { isEqual } from 'vs/base/common/resources';
import { isStandalone } from 'vs/base/browser/browser';
import product from 'vs/platform/product/common/product';
import { Schemas } from 'vs/base/common/network';
import { posix } from 'vs/base/common/path';
import { localize } from 'vs/nls';
interface ICredential {
service: string;
@@ -342,30 +338,11 @@ class WorkspaceProvider implements IWorkspaceProvider {
}
}
// Application links ("Open in Desktop")
let applicationLinks: IApplicationLink[] | undefined = undefined;
if (workspace) {
const workspaceUri = isWorkspaceToOpen(workspace) ? workspace.workspaceUri : isFolderToOpen(workspace) ? workspace.folderUri : undefined;
if (workspaceUri) {
applicationLinks = [{
uri: URI.from({
scheme: product.quality === 'stable' ? 'vscode' : 'vscode-insiders',
authority: Schemas.vscodeRemote,
path: posix.join(posix.sep, workspaceUri.authority, workspaceUri.path),
query: workspaceUri.query,
fragment: workspaceUri.fragment,
}),
label: localize('openInDesktop', "Open in Desktop")
}];
}
}
// Finally create workbench
create(document.body, {
...config,
workspaceProvider: new WorkspaceProvider(workspace, payload),
urlCallbackProvider: new PollingURLCallbackProvider(),
credentialsProvider: new LocalStorageCredentialsProvider(),
applicationLinks: applicationLinks
credentialsProvider: new LocalStorageCredentialsProvider()
});
})();

View File

@@ -417,9 +417,9 @@ export interface CompletionItemLabel {
name: string;
/**
* The signature without the return type. Render after `name`.
* The parameters without the return type. Render after `name`.
*/
signature?: string;
parameters?: string;
/**
* The fully qualified name, like package name or file path. Rendered after `signature`.

View File

@@ -10,6 +10,7 @@ import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { INotificationService } from 'vs/platform/notification/common/notification';
import { ICommandService } from 'vs/platform/commands/common/commands';
import { stripCodicons } from 'vs/base/common/codicons';
export abstract class AbstractEditorCommandsQuickAccessProvider extends AbstractCommandsQuickAccessProvider {
@@ -40,7 +41,7 @@ export abstract class AbstractEditorCommandsQuickAccessProvider extends Abstract
editorCommandPicks.push({
commandId: editorAction.id,
commandAlias: editorAction.alias,
label: editorAction.label || editorAction.id,
label: stripCodicons(editorAction.label) || editorAction.id,
});
}

View File

@@ -63,7 +63,7 @@ interface ISuggestionTemplateData {
colorspan: HTMLElement;
iconLabel: IconLabel;
iconContainer: HTMLElement;
signatureLabel: HTMLElement;
parametersLabel: HTMLElement;
qualifierLabel: HTMLElement;
/**
* Showing either `CompletionItem#details` or `CompletionItemLabel#type`
@@ -151,7 +151,7 @@ class ItemRenderer implements IListRenderer<CompletionItem, ISuggestionTemplateD
data.iconLabel = new IconLabel(data.left, { supportHighlights: true, supportCodicons: true });
data.disposables.add(data.iconLabel);
data.signatureLabel = append(data.left, $('span.signature-label'));
data.parametersLabel = append(data.left, $('span.signature-label'));
data.qualifierLabel = append(data.left, $('span.qualifier-label'));
data.detailsLabel = append(data.right, $('span.details-label'));
@@ -239,12 +239,12 @@ class ItemRenderer implements IListRenderer<CompletionItem, ISuggestionTemplateD
data.iconLabel.setLabel(textLabel, undefined, labelOptions);
if (typeof suggestion.label === 'string') {
data.signatureLabel.textContent = '';
data.parametersLabel.textContent = '';
data.qualifierLabel.textContent = '';
data.detailsLabel.textContent = (suggestion.detail || '').replace(/\n.*$/m, '');
addClass(data.root, 'string-label');
} else {
data.signatureLabel.textContent = (suggestion.label.signature || '').replace(/\n.*$/m, '');
data.parametersLabel.textContent = (suggestion.label.parameters || '').replace(/\n.*$/m, '');
data.qualifierLabel.textContent = (suggestion.label.qualifier || '').replace(/\n.*$/m, '');
data.detailsLabel.textContent = (suggestion.label.type || '').replace(/\n.*$/m, '');
removeClass(data.root, 'string-label');

4
src/vs/monaco.d.ts vendored
View File

@@ -5416,9 +5416,9 @@ declare namespace monaco.languages {
*/
name: string;
/**
* The signature without the return type. Render after `name`.
* The parameters without the return type. Render after `name`.
*/
signature?: string;
parameters?: string;
/**
* The fully qualified name, like package name or file path. Rendered after `signature`.
*/

View File

@@ -126,6 +126,7 @@ export class MenuId {
static readonly TimelineTitle = new MenuId('TimelineTitle');
static readonly TimelineTitleContext = new MenuId('TimelineTitleContext');
static readonly AccountsContext = new MenuId('AccountsContext');
static readonly WebMenuActions = new MenuId('MenubarWebMenu');
readonly id: number;
readonly _debugName: string;

View File

@@ -49,7 +49,6 @@ export const OPTIONS: OptionDescriptions<Required<ParsedArgs>> = {
'waitMarkerFilePath': { type: 'string' },
'locale': { type: 'string', cat: 'o', args: 'locale', description: localize('locale', "The locale to use (e.g. en-US or zh-TW).") },
'user-data-dir': { type: 'string', cat: 'o', args: 'dir', description: localize('userDataDir', "Specifies the directory that user data is kept in. Can be used to open multiple distinct instances of Code.") },
'telemetry': { type: 'boolean', cat: 'o', description: localize('telemetry', "Shows all telemetry events which VS code collects.") },
'help': { type: 'boolean', cat: 'o', alias: 'h', description: localize('help', "Print usage.") },
'extensions-dir': { type: 'string', deprecates: 'extensionHomePath', cat: 'e', args: 'dir', description: localize('extensionHomePath', "Set the root path for extensions.") },
@@ -76,6 +75,7 @@ export const OPTIONS: OptionDescriptions<Required<ParsedArgs>> = {
'inspect-brk-extensions': { type: 'string', deprecates: 'debugBrkPluginHost', args: 'port', cat: 't', description: localize('inspect-brk-extensions', "Allow debugging and profiling of extensions with the extension host being paused after start. Check the developer tools for the connection URI.") },
'disable-gpu': { type: 'boolean', cat: 't', description: localize('disableGPU', "Disable GPU hardware acceleration.") },
'max-memory': { type: 'string', cat: 't', description: localize('maxMemory', "Max memory size for a window (in Mbytes).") },
'telemetry': { type: 'boolean', cat: 't', description: localize('telemetry', "Shows all telemetry events which VS code collects.") },
'remote': { type: 'string' },
'locate-extension': { type: 'string[]' },

View File

@@ -21,6 +21,7 @@ import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { isPromiseCanceledError } from 'vs/base/common/errors';
import { INotificationService } from 'vs/platform/notification/common/notification';
import { toErrorMessage } from 'vs/base/common/errorMessage';
import { IStorageKeysSyncRegistryService } from 'vs/platform/userDataSync/common/storageKeys';
export interface ICommandQuickPick extends IPickerQuickAccessItem {
commandId: string;
@@ -202,10 +203,15 @@ export class CommandsHistory extends Disposable {
constructor(
@IStorageService private readonly storageService: IStorageService,
@IConfigurationService private readonly configurationService: IConfigurationService
@IConfigurationService private readonly configurationService: IConfigurationService,
@IStorageKeysSyncRegistryService storageKeysSyncRegistryService: IStorageKeysSyncRegistryService
) {
super();
// opt-in to syncing
storageKeysSyncRegistryService.registerStorageKey({ key: CommandsHistory.PREF_KEY_CACHE, version: 1 });
storageKeysSyncRegistryService.registerStorageKey({ key: CommandsHistory.PREF_KEY_COUNTER, version: 1 });
this.updateConfiguration();
this.load();

View File

@@ -63,12 +63,22 @@ export interface IPickerQuickAccessProviderOptions {
canAcceptInBackground?: boolean;
}
export type FastAndSlowPicksType<T> = { picks: Array<T | IQuickPickSeparator>, additionalPicks: Promise<Array<T | IQuickPickSeparator>> };
export type Pick<T> = T | IQuickPickSeparator;
export type PicksWithActive<T> = { items: ReadonlyArray<Pick<T>>, active?: T };
export type Picks<T> = ReadonlyArray<Pick<T>> | PicksWithActive<T>;
export type FastAndSlowPicks<T> = { picks: Picks<T>, additionalPicks: Promise<Picks<T>> };
export type FastAndSlowPicksWithActive<T> = { picks: PicksWithActive<T>, additionalPicks: PicksWithActive<Picks<T>> };
function isFastAndSlowPicksType<T>(obj: unknown): obj is FastAndSlowPicksType<T> {
const candidate = obj as FastAndSlowPicksType<T>;
function isPicksWithActive<T>(obj: unknown): obj is PicksWithActive<T> {
const candidate = obj as PicksWithActive<T>;
return Array.isArray(candidate.picks) && candidate.additionalPicks instanceof Promise;
return Array.isArray(candidate.items);
}
function isFastAndSlowPicks<T>(obj: unknown): obj is FastAndSlowPicks<T> {
const candidate = obj as FastAndSlowPicks<T>;
return !!candidate.picks && candidate.additionalPicks instanceof Promise;
}
export abstract class PickerQuickAccessProvider<T extends IPickerQuickAccessItem> extends Disposable implements IQuickAccessProvider {
@@ -103,15 +113,26 @@ export abstract class PickerQuickAccessProvider<T extends IPickerQuickAccessItem
// Collect picks and support both long running and short or combined
const picksToken = picksCts.token;
const res = this.getPicks(picker.value.substr(this.prefix.length).trim(), picksDisposables, picksToken);
const providedPicks = this.getPicks(picker.value.substr(this.prefix.length).trim(), picksDisposables, picksToken);
function applyPicks(picks: Picks<T>): void {
if (isPicksWithActive(picks)) {
picker.items = picks.items;
if (picks.active) {
picker.activeItems = [picks.active];
}
} else {
picker.items = picks;
}
}
// No Picks
if (res === null) {
if (providedPicks === null) {
// Ignore
}
// Fast and Slow Picks
else if (isFastAndSlowPicksType(res)) {
else if (isFastAndSlowPicks(providedPicks)) {
let fastPicksHandlerDone = false;
let slowPicksHandlerDone = false;
@@ -129,7 +150,7 @@ export abstract class PickerQuickAccessProvider<T extends IPickerQuickAccessItem
}
if (!slowPicksHandlerDone) {
picker.items = res.picks;
applyPicks(providedPicks.picks);
}
} finally {
fastPicksHandlerDone = true;
@@ -142,13 +163,34 @@ export abstract class PickerQuickAccessProvider<T extends IPickerQuickAccessItem
(async () => {
picker.busy = true;
try {
const additionalPicks = await res.additionalPicks;
const awaitedAdditionalPicks = await providedPicks.additionalPicks;
if (picksToken.isCancellationRequested) {
return;
}
let picks: ReadonlyArray<Pick<T>>;
let activePick: Pick<T> | undefined = undefined;
if (isPicksWithActive(providedPicks.picks)) {
picks = providedPicks.picks.items;
activePick = providedPicks.picks.active;
} else {
picks = providedPicks.picks;
}
let additionalPicks: ReadonlyArray<Pick<T>>;
let additionalActivePick: Pick<T> | undefined = undefined;
if (isPicksWithActive(awaitedAdditionalPicks)) {
additionalPicks = awaitedAdditionalPicks.items;
additionalActivePick = awaitedAdditionalPicks.active;
} else {
additionalPicks = awaitedAdditionalPicks;
}
if (additionalPicks.length > 0 || !fastPicksHandlerDone) {
picker.items = [...res.picks, ...additionalPicks];
applyPicks({
items: [...picks, ...additionalPicks],
active: activePick as T || additionalActivePick as T // {{SQL CARBON EDIT}} strict-null-checks
});
}
} finally {
if (!picksToken.isCancellationRequested) {
@@ -162,20 +204,20 @@ export abstract class PickerQuickAccessProvider<T extends IPickerQuickAccessItem
}
// Fast Picks
else if (Array.isArray(res)) {
picker.items = res;
else if (!(providedPicks instanceof Promise)) {
applyPicks(providedPicks);
}
// Slow Picks
else {
picker.busy = true;
try {
const items = await res;
const awaitedPicks = await providedPicks;
if (picksToken.isCancellationRequested) {
return;
}
picker.items = items;
applyPicks(awaitedPicks);
} finally {
if (!picksToken.isCancellationRequested) {
picker.busy = false;
@@ -251,5 +293,5 @@ export abstract class PickerQuickAccessProvider<T extends IPickerQuickAccessItem
* @returns the picks either directly, as promise or combined fast and slow results.
* Pickers can return `null` to signal that no change in picks is needed.
*/
protected abstract getPicks(filter: string, disposables: DisposableStore, token: CancellationToken): Array<T | IQuickPickSeparator> | Promise<Array<T | IQuickPickSeparator>> | FastAndSlowPicksType<T> | null;
protected abstract getPicks(filter: string, disposables: DisposableStore, token: CancellationToken): Picks<T> | Promise<Picks<T>> | FastAndSlowPicks<T> | null;
}

View File

@@ -9,7 +9,6 @@ import { IStorageChangeEvent, IStorageMainService } from 'vs/platform/storage/no
import { IUpdateRequest, IStorageDatabase, IStorageItemsChangeEvent } from 'vs/base/parts/storage/common/storage';
import { mapToSerializable, serializableToMap, values } from 'vs/base/common/map';
import { Disposable, IDisposable, dispose } from 'vs/base/common/lifecycle';
import { onUnexpectedError } from 'vs/base/common/errors';
import { ILogService } from 'vs/platform/log/common/log';
import { generateUuid } from 'vs/base/common/uuid';
import { instanceStorageKey, firstSessionDateStorageKey, lastSessionDateStorageKey, currentSessionDateStorageKey, crashReporterIdStorageKey } from 'vs/platform/telemetry/common/telemetry';
@@ -50,8 +49,7 @@ export class GlobalStorageDatabaseChannel extends Disposable implements IServerC
try {
await this.storageMainService.initialize();
} catch (error) {
onUnexpectedError(error);
this.logService.error(error);
this.logService.error(`[storage] init(): Unable to init global storage due to ${error}`);
}
// This is unique to the application instance and thereby

View File

@@ -14,7 +14,6 @@ import { join } from 'vs/base/common/path';
import { copy, exists, mkdirp, writeFile } from 'vs/base/node/pfs';
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
import { IWorkspaceInitializationPayload, isWorkspaceIdentifier, isSingleFolderWorkspaceInitializationPayload } from 'vs/platform/workspaces/common/workspaces';
import { onUnexpectedError } from 'vs/base/common/errors';
import { assertIsDefined } from 'vs/base/common/types';
import { RunOnceScheduler, runWhenIdle } from 'vs/base/common/async';
@@ -31,7 +30,7 @@ export class NativeStorageService extends Disposable implements IStorageService
private readonly _onWillSaveState = this._register(new Emitter<IWillSaveStateEvent>());
readonly onWillSaveState = this._onWillSaveState.event;
private globalStorage: IStorage;
private readonly globalStorage = new Storage(this.globalStorageDatabase);
private workspaceStoragePath: string | undefined;
private workspaceStorage: IStorage | undefined;
@@ -43,14 +42,13 @@ export class NativeStorageService extends Disposable implements IStorageService
private runWhenIdleDisposable: IDisposable | undefined = undefined;
constructor(
globalStorageDatabase: IStorageDatabase,
private globalStorageDatabase: IStorageDatabase,
@ILogService private readonly logService: ILogService,
@IEnvironmentService private readonly environmentService: IEnvironmentService
) {
super();
// Global Storage
this.globalStorage = new Storage(globalStorageDatabase);
// Global Storage change events
this._register(this.globalStorage.onDidChangeStorage(key => this.handleDidChangeStorage(key, StorageScope.GLOBAL)));
}
@@ -95,15 +93,16 @@ export class NativeStorageService extends Disposable implements IStorageService
// Create workspace storage and initialize
mark('willInitWorkspaceStorage');
try {
await this.createWorkspaceStorage(useInMemoryStorage ? SQLiteStorageDatabase.IN_MEMORY_PATH : join(result.path, NativeStorageService.WORKSPACE_STORAGE_NAME), result.wasCreated ? StorageHint.STORAGE_DOES_NOT_EXIST : undefined).init();
const workspaceStorage = this.createWorkspaceStorage(
useInMemoryStorage ? SQLiteStorageDatabase.IN_MEMORY_PATH : join(result.path, NativeStorageService.WORKSPACE_STORAGE_NAME),
result.wasCreated ? StorageHint.STORAGE_DOES_NOT_EXIST : undefined
);
await workspaceStorage.init();
} finally {
mark('didInitWorkspaceStorage');
}
} catch (error) {
onUnexpectedError(error);
// Upon error, fallback to in-memory storage
return this.createWorkspaceStorage(SQLiteStorageDatabase.IN_MEMORY_PATH).init();
this.logService.error(`[storage] initializeWorkspaceStorage(): Unable to init workspace storage due to ${error}`);
}
}
@@ -156,6 +155,7 @@ export class NativeStorageService extends Disposable implements IStorageService
}
if (meta) {
const logService = this.logService;
const workspaceStorageMetaPath = join(this.getWorkspaceStorageFolderPath(payload), NativeStorageService.WORKSPACE_META_NAME);
(async function () {
try {
@@ -164,7 +164,7 @@ export class NativeStorageService extends Disposable implements IStorageService
await writeFile(workspaceStorageMetaPath, JSON.stringify(meta, undefined, 2));
}
} catch (error) {
onUnexpectedError(error);
logService.error(error);
}
})();
}

View File

@@ -60,6 +60,11 @@ declare module 'vscode' {
readonly changed: string[];
}
/**
* **WARNING** When writing an AuthenticationProvider, `id` should be treated as part of your extension's
* API, changing it is a breaking change for all extensions relying on the provider. The id is
* treated case-sensitively.
*/
export interface AuthenticationProvider {
/**
* Used as an identifier for extensions trying to work with a particular
@@ -1676,6 +1681,8 @@ declare module 'vscode' {
* This metadata is ignored for markdown cell.
*/
runnable: boolean;
executionOrder?: number;
}
export interface NotebookCell {
@@ -1828,9 +1835,9 @@ declare module 'vscode' {
name: string;
/**
* The signature without the return type. Render after `name`.
* The parameters without the return type. Render after `name`.
*/
signature?: string;
parameters?: string;
/**
* The fully qualified name, like package name or file path. Rendered after `signature`.

View File

@@ -34,7 +34,8 @@ const BUILT_IN_AUTH_DEPENDENTS: AuthDependent[] = [
export class MainThreadAuthenticationProvider extends Disposable {
private _sessionMenuItems = new Map<string, IDisposable[]>();
private _sessionIds: string[] = [];
private _accounts = new Map<string, string[]>(); // Map account name to session ids
private _sessions = new Map<string, string>(); // Map account id to name
constructor(
private readonly _proxy: ExtHostAuthenticationShape,
@@ -44,10 +45,6 @@ export class MainThreadAuthenticationProvider extends Disposable {
) {
super();
if (!dependents.length) {
return;
}
this.registerCommandsAndContextMenuItems();
}
@@ -86,21 +83,23 @@ export class MainThreadAuthenticationProvider extends Disposable {
}
private registerCommandsAndContextMenuItems(): void {
this._register(CommandsRegistry.registerCommand({
id: `signIn${this.id}`,
handler: (accessor, args) => {
this.setPermissionsForAccount(accessor.get(IQuickInputService), true);
},
}));
this._register(MenuRegistry.appendMenuItem(MenuId.AccountsContext, {
group: '2_providers',
command: {
if (this.dependents.length) {
this._register(CommandsRegistry.registerCommand({
id: `signIn${this.id}`,
title: nls.localize('addAccount', "Sign in to {0}", this.displayName)
},
order: 3
}));
handler: (accessor, args) => {
this.setPermissionsForAccount(accessor.get(IQuickInputService), true);
},
}));
this._register(MenuRegistry.appendMenuItem(MenuId.AccountsContext, {
group: '2_providers',
command: {
id: `signIn${this.id}`,
title: nls.localize('addAccount', "Sign in to {0}", this.displayName)
},
order: 3
}));
}
this._proxy.$getSessions(this.id).then(sessions => {
sessions.forEach(session => this.registerSession(session));
@@ -108,7 +107,16 @@ export class MainThreadAuthenticationProvider extends Disposable {
}
private registerSession(session: modes.AuthenticationSession) {
this._sessionIds.push(session.id);
this._sessions.set(session.id, session.accountName);
const existingSessionsForAccount = this._accounts.get(session.accountName);
if (existingSessionsForAccount) {
this._accounts.set(session.accountName, existingSessionsForAccount.concat(session.id));
return;
} else {
this._accounts.set(session.accountName, [session.id]);
}
const menuItem = MenuRegistry.appendMenuItem(MenuId.AccountsContext, {
group: '1_accounts',
command: {
@@ -131,7 +139,8 @@ export class MainThreadAuthenticationProvider extends Disposable {
quickPick.onDidAccept(e => {
const selected = quickPick.selectedItems[0];
if (selected.label === 'Sign Out') {
this.logout(session.id);
const sessionsForAccount = this._accounts.get(session.accountName);
sessionsForAccount?.forEach(sessionId => this.logout(sessionId));
}
quickPick.dispose();
@@ -145,7 +154,7 @@ export class MainThreadAuthenticationProvider extends Disposable {
},
});
this._sessionMenuItems.set(session.id, [menuItem, manageCommand]);
this._sessionMenuItems.set(session.accountName, [menuItem, manageCommand]);
}
async getSessions(): Promise<ReadonlyArray<modes.AuthenticationSession>> {
@@ -158,22 +167,29 @@ export class MainThreadAuthenticationProvider extends Disposable {
});
}
async updateSessionItems(): Promise<void> {
const currentSessions = await this._proxy.$getSessions(this.id);
const removedSessionIds = this._sessionIds.filter(id => !currentSessions.some(session => session.id === id));
const addedSessions = currentSessions.filter(session => !this._sessionIds.some(id => id === session.id));
async updateSessionItems(event: modes.AuthenticationSessionsChangeEvent): Promise<void> {
const { added, removed } = event;
const session = await this._proxy.$getSessions(this.id);
const addedSessions = session.filter(session => added.some(id => id === session.id));
removedSessionIds.forEach(id => {
const disposeables = this._sessionMenuItems.get(id);
if (disposeables) {
disposeables.forEach(disposeable => disposeable.dispose());
this._sessionMenuItems.delete(id);
removed.forEach(sessionId => {
const accountName = this._sessions.get(sessionId);
if (accountName) {
let sessionsForAccount = this._accounts.get(accountName) || [];
const sessionIndex = sessionsForAccount.indexOf(sessionId);
sessionsForAccount.splice(sessionIndex);
if (!sessionsForAccount.length) {
const disposeables = this._sessionMenuItems.get(accountName);
if (disposeables) {
disposeables.forEach(disposeable => disposeable.dispose());
this._sessionMenuItems.delete(accountName);
}
}
}
});
addedSessions.forEach(session => this.registerSession(session));
this._sessionIds = currentSessions.map(session => session.id);
}
login(scopes: string[]): Promise<modes.AuthenticationSession> {

View File

@@ -752,18 +752,23 @@ class MainThreadCustomEditorModel extends Disposable implements ICustomEditorMod
});
}
public async save(_options?: ISaveOptions): Promise<boolean> {
public async save(options?: ISaveOptions): Promise<boolean> {
return !!await this.saveCustomEditor(options);
}
public async saveCustomEditor(_options?: ISaveOptions): Promise<URI | undefined> {
if (!this._editable) {
return false;
return undefined;
}
// TODO: handle save untitled case
await createCancelablePromise(token => this._proxy.$onSave(this._editorResource, this.viewType, token));
this.change(() => {
this._savePoint = this._currentEditIndex;
});
return true;
return this._editorResource;
}
public async saveAs(resource: URI, targetResource: URI, _options?: ISaveOptions): Promise<boolean> {
public async saveCustomEditorAs(resource: URI, targetResource: URI, _options?: ISaveOptions): Promise<boolean> {
if (this._editable) {
await this._proxy.$onSaveAs(this._editorResource, this.viewType, targetResource);
this.change(() => {

View File

@@ -3,18 +3,39 @@
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as vscode from 'vscode';
import { ExtHostNotebookShape, IMainContext, MainThreadNotebookShape, MainContext, ICellDto, NotebookCellsSplice, NotebookCellOutputsSplice, CellKind, CellOutputKind } from 'vs/workbench/api/common/extHost.protocol';
import { IExtensionDescription } from 'vs/platform/extensions/common/extensions';
import { Disposable as VSCodeDisposable } from './extHostTypes';
import { URI, UriComponents } from 'vs/base/common/uri';
import { DisposableStore, Disposable } from 'vs/base/common/lifecycle';
import { readonly } from 'vs/base/common/errors';
import { Emitter, Event } from 'vs/base/common/event';
import { ExtHostDocumentsAndEditors } from 'vs/workbench/api/common/extHostDocumentsAndEditors';
import { INotebookDisplayOrder, ITransformedDisplayOutputDto, IOrderedMimeType, IStreamOutput, IErrorOutput, mimeTypeSupportedByCore, IOutput, sortMimeTypes, diff, CellUri } from 'vs/workbench/contrib/notebook/common/notebookCommon';
import { Disposable, DisposableStore, IDisposable } from 'vs/base/common/lifecycle';
import { ISplice } from 'vs/base/common/sequence';
import { URI, UriComponents } from 'vs/base/common/uri';
import { IExtensionDescription } from 'vs/platform/extensions/common/extensions';
import { CellKind, CellOutputKind, ExtHostNotebookShape, ICellDto, IMainContext, MainContext, MainThreadNotebookShape, NotebookCellOutputsSplice, NotebookCellsSplice } from 'vs/workbench/api/common/extHost.protocol';
import { ExtHostCommands } from 'vs/workbench/api/common/extHostCommands';
import { ExtHostDocumentsAndEditors } from 'vs/workbench/api/common/extHostDocumentsAndEditors';
import { CellUri, diff, IErrorOutput, INotebookDisplayOrder, IOrderedMimeType, IOutput, IStreamOutput, ITransformedDisplayOutputDto, mimeTypeSupportedByCore, sortMimeTypes } from 'vs/workbench/contrib/notebook/common/notebookCommon';
import * as vscode from 'vscode';
import { Disposable as VSCodeDisposable } from './extHostTypes';
interface IObservable<T> {
proxy: T;
onDidChange: Event<void>;
}
function getObservable<T extends Object>(obj: T): IObservable<T> {
const onDidChange = new Emitter<void>();
const proxy = new Proxy(obj, {
set(target: T, p: PropertyKey, value: any, _receiver: any): boolean {
target[p as keyof T] = value;
onDidChange.fire();
return true;
}
});
return {
proxy,
onDidChange: onDidChange.event
};
}
const notebookDocumentMetadataDefaults: vscode.NotebookDocumentMetadata = {
editable: true,
@@ -22,8 +43,7 @@ const notebookDocumentMetadataDefaults: vscode.NotebookDocumentMetadata = {
cellRunnable: true
};
export class ExtHostCell implements vscode.NotebookCell {
export class ExtHostCell extends Disposable implements vscode.NotebookCell {
public source: string[];
private _outputs: any[];
private _onDidChangeOutputs = new Emitter<ISplice<vscode.CellOutput>[]>();
@@ -31,6 +51,9 @@ export class ExtHostCell implements vscode.NotebookCell {
private _textDocument: vscode.TextDocument | undefined;
private _initalVersion: number = -1;
private _outputMapping = new Set<vscode.CellOutput>();
private _metadata: vscode.NotebookCellMetadata;
private _metadataChangeListener: IDisposable;
constructor(
private viewType: string,
@@ -41,11 +64,19 @@ export class ExtHostCell implements vscode.NotebookCell {
public cellKind: CellKind,
public language: string,
outputs: any[],
private _metadata: vscode.NotebookCellMetadata | undefined,
_metadata: vscode.NotebookCellMetadata | undefined,
private _proxy: MainThreadNotebookShape
) {
super();
this.source = this._content.split(/\r|\n|\r\n/g);
this._outputs = outputs;
const observableMetadata = getObservable(_metadata || {} as any);
this._metadata = observableMetadata.proxy;
this._metadataChangeListener = this._register(observableMetadata.onDidChange(() => {
this.updateMetadata();
}));
}
get outputs() {
@@ -76,13 +107,18 @@ export class ExtHostCell implements vscode.NotebookCell {
}
set metadata(newMetadata: vscode.NotebookCellMetadata | undefined) {
const newMetadataWithDefaults: vscode.NotebookCellMetadata | undefined = newMetadata ? {
editable: newMetadata.editable,
runnable: newMetadata.runnable
} : undefined;
this._metadataChangeListener.dispose();
const observableMetadata = getObservable(newMetadata || {} as any); // TODO defaults
this._metadata = observableMetadata.proxy;
this._metadataChangeListener = this._register(observableMetadata.onDidChange(() => {
this.updateMetadata();
}));
this._metadata = newMetadataWithDefaults;
this._proxy.$updateNotebookCellMetadata(this.viewType, this.documentUri, this.handle, newMetadataWithDefaults);
this.updateMetadata();
}
private updateMetadata(): Promise<void> {
return this._proxy.$updateNotebookCellMetadata(this.viewType, this.documentUri, this.handle, this._metadata);
}
getContent(): string {

View File

@@ -1362,7 +1362,7 @@ export enum CompletionItemTag {
export interface CompletionItemLabel {
name: string;
signature?: string;
parameters?: string;
qualifier?: string;
type?: string;
}

View File

@@ -510,6 +510,16 @@ export class CustomMenubarControl extends MenubarControl {
}
if (firstTime) {
const webActions = [];
const webMenu = this.menuService.createMenu(MenuId.WebMenuActions, this.contextKeyService);
for (const groups of webMenu.getActions()) {
const [, actions] = groups;
for (const action of actions) {
action.label = mnemonicMenuLabel(this.calculateActionLabel(action));
webActions.push(action);
}
}
this.menubar = this._register(new MenuBar(
this.container, {
enableMnemonics: this.currentEnableMenuBarMnemonics,
@@ -517,7 +527,7 @@ export class CustomMenubarControl extends MenubarControl {
visibility: this.currentMenubarVisibility,
getKeybinding: (action) => this.keybindingService.lookupKeybinding(action.id),
compactMode: this.currentCompactMenuMode
}));
}, webActions.length > 0 ? webActions : undefined));
this.accessibilityService.alwaysUnderlineAccessKeys().then(val => {
this.alwaysOnMnemonics = val;

View File

@@ -1022,7 +1022,11 @@ export class ViewPaneContainer extends Component implements IViewPaneContainer {
if (this.dimension) {
const totalWeight = this.viewsModel.visibleViewDescriptors.reduce((totalWeight, { weight }) => totalWeight + (weight || 20), 0);
for (const viewDescriptor of this.viewsModel.visibleViewDescriptors) {
sizes.set(viewDescriptor.id, this.dimension.height * (viewDescriptor.weight || 20) / totalWeight);
if (this.orientation === Orientation.VERTICAL) {
sizes.set(viewDescriptor.id, this.dimension.height * (viewDescriptor.weight || 20) / totalWeight);
} else {
sizes.set(viewDescriptor.id, this.dimension.width * (viewDescriptor.weight || 20) / totalWeight);
}
}
}
return sizes;

View File

@@ -7,7 +7,7 @@ import 'vs/css!./media/views';
import { Disposable, IDisposable, toDisposable, DisposableStore } from 'vs/base/common/lifecycle';
import { IViewDescriptorService, ViewContainer, IViewDescriptor, IViewContainersRegistry, Extensions as ViewExtensions, IView, ViewContainerLocation, IViewsService, IViewPaneContainer, getVisbileViewContextKey } from 'vs/workbench/common/views';
import { Registry } from 'vs/platform/registry/common/platform';
import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage';
import { IStorageService, StorageScope, IWorkspaceStorageChangeEvent } from 'vs/platform/storage/common/storage';
import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet';
import { ContextKeyExpr, IContextKey, IContextKeyService, RawContextKey } from 'vs/platform/contextkey/common/contextkey';
import { Event, Emitter } from 'vs/base/common/event';
@@ -34,6 +34,7 @@ import { IConfigurationService } from 'vs/platform/configuration/common/configur
import { IWorkbenchLayoutService } from 'vs/workbench/services/layout/browser/layoutService';
import { URI } from 'vs/base/common/uri';
import { IProgressIndicator } from 'vs/platform/progress/common/progress';
import { IStorageKeysSyncRegistryService } from 'vs/platform/userDataSync/common/storageKeys';
export interface IViewState {
visibleGlobal: boolean | undefined;
@@ -101,30 +102,46 @@ export class ContributableViewsModel extends Disposable {
}
setVisible(id: string, visible: boolean, size?: number): void {
const { visibleIndex, viewDescriptor, state } = this.find(id);
this.doSetVisible([{ id, visible, size }]);
}
if (!viewDescriptor.canToggleVisibility) {
throw new Error(`Can't toggle this view's visibility`);
protected doSetVisible(viewDescriptors: { id: string, visible: boolean, size?: number }[]): void {
const added: IAddedViewDescriptorRef[] = [];
const removed: IViewDescriptorRef[] = [];
for (const { id, visible, size } of viewDescriptors) {
const { visibleIndex, viewDescriptor, state } = this.find(id);
if (!viewDescriptor.canToggleVisibility) {
throw new Error(`Can't toggle this view's visibility`);
}
if (this.isViewDescriptorVisible(viewDescriptor) === visible) {
return;
}
if (viewDescriptor.workspace) {
state.visibleWorkspace = visible;
} else {
state.visibleGlobal = visible;
}
if (typeof size === 'number') {
state.size = size;
}
if (visible) {
added.push({ index: visibleIndex, viewDescriptor, size: state.size, collapsed: !!state.collapsed });
} else {
removed.push({ index: visibleIndex, viewDescriptor });
}
}
if (this.isViewDescriptorVisible(viewDescriptor) === visible) {
return;
if (added.length) {
this._onDidAdd.fire(added);
}
if (viewDescriptor.workspace) {
state.visibleWorkspace = visible;
} else {
state.visibleGlobal = visible;
}
if (typeof size === 'number') {
state.size = size;
}
if (visible) {
this._onDidAdd.fire([{ index: visibleIndex, viewDescriptor, size: state.size, collapsed: !!state.collapsed }]);
} else {
this._onDidRemove.fire([{ index: visibleIndex, viewDescriptor }]);
if (removed.length) {
this._onDidRemove.fire(removed);
}
}
@@ -318,6 +335,7 @@ export class PersistentContributableViewsModel extends ContributableViewsModel {
viewletStateStorageId: string,
@IViewDescriptorService viewDescriptorService: IViewDescriptorService,
@IStorageService storageService: IStorageService,
@IStorageKeysSyncRegistryService storageKeysSyncRegistryService: IStorageKeysSyncRegistryService
) {
const globalViewsStateStorageId = `${viewletStateStorageId}.hidden`;
const viewStates = PersistentContributableViewsModel.loadViewsStates(viewletStateStorageId, globalViewsStateStorageId, storageService);
@@ -334,6 +352,30 @@ export class PersistentContributableViewsModel extends ContributableViewsModel {
Event.map(this.onDidMove, ({ from, to }) => [from, to]),
Event.map(this.onDidChangeViewState, viewDescriptorRef => [viewDescriptorRef]))
(viewDescriptorRefs => this.saveViewsStates()));
storageKeysSyncRegistryService.registerStorageKey({ key: this.globalViewsStateStorageId, version: 1 });
this._globalViewsStatesValue = this.getStoredGlobalViewsStatesValue();
this._register(this.storageService.onDidChangeStorage(e => this.onDidStorageChange(e)));
}
private onDidStorageChange(e: IWorkspaceStorageChangeEvent): void {
if (e.key === this.globalViewsStateStorageId && e.scope === StorageScope.GLOBAL
&& this.globalViewsStatesValue !== this.getStoredGlobalViewsStatesValue() /* This checks if current window changed the value or not */) {
this._globalViewsStatesValue = undefined;
const storedViewsVisibilityStates = PersistentContributableViewsModel.loadGlobalViewsState(this.globalViewsStateStorageId, this.storageService, StorageScope.GLOBAL);
const changedViews: { id: string, visible: boolean }[] = [];
for (const [id, state] of storedViewsVisibilityStates) {
const viewState = this.viewStates.get(id);
if (viewState) {
if (viewState.visibleGlobal !== !state.isHidden) {
changedViews.push({ id, visible: !state.isHidden });
}
}
}
if (changedViews.length) {
this.doSetVisible(changedViews);
}
}
}
private saveViewsStates(): void {
@@ -372,9 +414,32 @@ export class PersistentContributableViewsModel extends ContributableViewsModel {
order: !viewDescriptor.workspace && viewState ? viewState.order : undefined
});
}
this.storageService.store(this.globalViewsStateStorageId, JSON.stringify(values(storedViewsVisibilityStates)), StorageScope.GLOBAL);
this.globalViewsStatesValue = JSON.stringify(values(storedViewsVisibilityStates));
}
private _globalViewsStatesValue: string | undefined;
private get globalViewsStatesValue(): string {
if (!this._globalViewsStatesValue) {
this._globalViewsStatesValue = this.getStoredGlobalViewsStatesValue();
}
return this._globalViewsStatesValue;
}
private set globalViewsStatesValue(globalViewsStatesValue: string) {
if (this.globalViewsStatesValue !== globalViewsStatesValue) {
this._globalViewsStatesValue = globalViewsStatesValue;
this.setStoredGlobalViewsStatesValue(globalViewsStatesValue);
}
}
private getStoredGlobalViewsStatesValue(): string {
return this.storageService.get(this.globalViewsStateStorageId, StorageScope.GLOBAL, '[]');
}
private setStoredGlobalViewsStatesValue(value: string): void {
this.storageService.store(this.globalViewsStateStorageId, value, StorageScope.GLOBAL);
}
private static loadViewsStates(workspaceViewsStateStorageId: string, globalViewsStateStorageId: string, storageService: IStorageService): Map<string, IViewState> {
const viewStates = new Map<string, IViewState>();

View File

@@ -133,9 +133,6 @@ export class Workbench extends Layout {
// Configure emitter leak warning threshold
setGlobalLeakWarningThreshold(175);
// ARIA
setARIAContainer(document.body);
// Services
const instantiationService = this.initServices(this.serviceCollection);
@@ -324,6 +321,7 @@ export class Workbench extends Layout {
private renderWorkbench(instantiationService: IInstantiationService, notificationService: NotificationService, storageService: IStorageService, configurationService: IConfigurationService): void {
// ARIA
setARIAContainer(this.container);
this.container.setAttribute('role', 'application');
// State specific classes

View File

@@ -22,6 +22,7 @@ export interface IWorkbenchActionRegistry {
/**
* Registers a workbench action to the platform. Workbench actions are not
* visible by default and can only be invoked through a keybinding if provided.
* @deprecated Register directly with KeybindingsRegistry and MenuRegistry or use registerAction2 instead.
*/
registerWorkbenchAction(descriptor: SyncActionDescriptor, alias: string, category?: string, when?: ContextKeyExpr): IDisposable;
}

View File

@@ -121,21 +121,33 @@ suite.skip('BackupRestorer', () => { // {{SQL CARBON EDIT}} TODO @anthonydresser
const resource = editor.resource;
if (isEqual(resource, untitledFile1)) {
const model = await accessor.textFileService.untitled.resolve({ untitledResource: resource });
assert.equal(model.textEditorModel.getValue(), 'untitled-1');
if (model.textEditorModel.getValue() !== 'untitled-1') {
const backupContents = await backupFileService.getBackupContents(untitledFile1);
assert.fail(`Unable to restore backup for resource ${untitledFile1.toString()}. Backup contents: ${backupContents}`);
}
model.dispose();
counter++;
} else if (isEqual(resource, untitledFile2)) {
const model = await accessor.textFileService.untitled.resolve({ untitledResource: resource });
assert.equal(model.textEditorModel.getValue(), 'untitled-2');
if (model.textEditorModel.getValue() !== 'untitled-2') {
const backupContents = await backupFileService.getBackupContents(untitledFile2);
assert.fail(`Unable to restore backup for resource ${untitledFile2.toString()}. Backup contents: ${backupContents}`);
}
model.dispose();
counter++;
} else if (isEqual(resource, fooFile)) {
const model = await accessor.textFileService.files.get(resource!)?.load();
assert.equal(model?.textEditorModel?.getValue(), 'fooFile');
const model = await accessor.textFileService.files.get(fooFile!)?.load();
if (model?.textEditorModel?.getValue() !== 'fooFile') {
const backupContents = await backupFileService.getBackupContents(fooFile);
assert.fail(`Unable to restore backup for resource ${fooFile.toString()}. Backup contents: ${backupContents}`);
}
counter++;
} else {
const model = await accessor.textFileService.files.get(resource!)?.load();
assert.equal(model?.textEditorModel?.getValue(), 'barFile');
const model = await accessor.textFileService.files.get(barFile!)?.load();
if (model?.textEditorModel?.getValue() !== 'barFile') {
const backupContents = await backupFileService.getBackupContents(barFile);
assert.fail(`Unable to restore backup for resource ${barFile.toString()}. Backup contents: ${backupContents}`);
}
counter++;
}
}

View File

@@ -70,6 +70,12 @@ class InspectEditorTokensController extends Disposable implements IEditorContrib
this._register(this._editor.onDidChangeModel((e) => this.stop()));
this._register(this._editor.onDidChangeModelLanguage((e) => this.stop()));
this._register(this._editor.onKeyUp((e) => e.keyCode === KeyCode.Escape && this.stop()));
this._register(this._themeService.onDidColorThemeChange(_ => {
if (this._widget) {
this.stop();
this.launch();
}
}));
}
public dispose(): void {

View File

@@ -6,6 +6,7 @@
import { memoize } from 'vs/base/common/decorators';
import { Lazy } from 'vs/base/common/lazy';
import { IReference } from 'vs/base/common/lifecycle';
import { Schemas } from 'vs/base/common/network';
import { basename } from 'vs/base/common/path';
import { isEqual } from 'vs/base/common/resources';
import { assertIsDefined } from 'vs/base/common/types';
@@ -99,6 +100,10 @@ export class CustomEditorInput extends LazilyResolvedWebviewEditorInput {
return this._modelRef ? this._modelRef.object.isReadonly() : false;
}
public isUntitled(): boolean {
return this.resource.scheme === Schemas.untitled;
}
public isDirty(): boolean {
if (!this._modelRef) {
return false;
@@ -120,8 +125,16 @@ export class CustomEditorInput extends LazilyResolvedWebviewEditorInput {
public async save(groupId: GroupIdentifier, options?: ISaveOptions): Promise<IEditorInput | undefined> {
const modelRef = assertIsDefined(this._modelRef);
const result = await modelRef.object.save(options);
return result ? this : undefined;
const target = await modelRef.object.saveCustomEditor(options);
if (!target) {
return undefined; // save cancelled
}
if (!isEqual(target, this.resource)) {
return this.customEditorService.createInput(target, this.viewType, groupId);
}
return this;
}
public async saveAs(groupId: GroupIdentifier, options?: ISaveOptions): Promise<IEditorInput | undefined> {
@@ -133,7 +146,7 @@ export class CustomEditorInput extends LazilyResolvedWebviewEditorInput {
return undefined; // save cancelled
}
if (!await modelRef.object.saveAs(this._editorResource, target, options)) {
if (!await modelRef.object.saveCustomEditorAs(this._editorResource, target, options)) {
return undefined;
}
@@ -160,29 +173,29 @@ export class CustomEditorInput extends LazilyResolvedWebviewEditorInput {
}
move(group: GroupIdentifier, newResource: URI): { editor: IEditorInput } | undefined {
// See if we can keep using the same custom editor provider
const editorInfo = this.customEditorService.getCustomEditor(this.viewType);
if (editorInfo?.matches(newResource)) {
// We can keep using the same custom editor provider
if (!this._moveHandler) {
return {
editor: this.customEditorService.createInput(newResource, this.viewType, group),
};
}
this._moveHandler(newResource);
const newEditor = this.instantiationService.createInstance(CustomEditorInput,
newResource,
this.viewType,
this.id,
new Lazy(() => undefined!)); // this webview is replaced in the transfer call
this.transfer(newEditor);
newEditor.updateGroup(group);
return { editor: newEditor };
} else {
// const possible = this.customEditorService.getContributedCustomEditors(newResource);
return { editor: this.editorService.createEditorInput({ resource: newResource, forceFile: true }) };
return { editor: this.doMove(group, newResource) };
}
return { editor: this.editorService.createEditorInput({ resource: newResource, forceFile: true }) };
}
private doMove(group: GroupIdentifier, newResource: URI): IEditorInput {
if (!this._moveHandler) {
return this.customEditorService.createInput(newResource, this.viewType, group);
}
this._moveHandler(newResource);
const newEditor = this.instantiationService.createInstance(CustomEditorInput,
newResource,
this.viewType,
this.id,
new Lazy(() => undefined!)); // this webview is replaced in the transfer call
this.transfer(newEditor);
newEditor.updateGroup(group);
return newEditor;
}

View File

@@ -327,8 +327,8 @@ export class CustomEditorService extends Disposable implements ICustomEditorServ
private async handleMovedFileInOpenedFileEditors(_oldResource: URI, newResource: URI): Promise<void> {
// See if the new resource can be opened in a custom editor
const possibleEditors = this.getAllCustomEditors(newResource).allEditors;
if (!possibleEditors.length) {
const possibleEditors = this.getAllCustomEditors(newResource);
if (!possibleEditors.allEditors.length) {
return;
}
@@ -354,19 +354,25 @@ export class CustomEditorService extends Disposable implements ICustomEditorServ
return;
}
// If there is, show a single prompt for all editors to see if the user wants to re-open them
//
// TODO: instead of prompting eagerly, it'd likly be better to replace all the editors with
// ones that would prompt when they first become visible
await new Promise(resolve => setTimeout(resolve, 50));
const pickedViewType = await this.showOpenWithPrompt(newResource);
if (!pickedViewType) {
let viewType: string | undefined;
if (possibleEditors.defaultEditor) {
viewType = possibleEditors.defaultEditor.id;
} else {
// If there is, show a single prompt for all editors to see if the user wants to re-open them
//
// TODO: instead of prompting eagerly, it'd likly be better to replace all the editors with
// ones that would prompt when they first become visible
await new Promise(resolve => setTimeout(resolve, 50));
viewType = await this.showOpenWithPrompt(newResource);
}
if (!viewType) {
return;
}
for (const [group, entries] of editorsToReplace) {
this.editorService.replaceEditors(entries.map(editor => {
const replacement = this.createInput(newResource, pickedViewType, group);
const replacement = this.createInput(newResource, viewType!, group);
return {
editor,
replacement,

View File

@@ -26,7 +26,15 @@ interface IWebviewEditorsExtensionPoint {
const webviewEditorsContribution: IJSONSchema = {
description: nls.localize('contributes.customEditors', 'Contributed custom editors.'),
type: 'array',
defaultSnippets: [{ body: [{ viewType: '', displayName: '' }] }],
defaultSnippets: [{
body: [{
[WebviewEditorContribution.viewType]: '$1',
[WebviewEditorContribution.displayName]: '$2',
[WebviewEditorContribution.selector]: [{
filenamePattern: '$3'
}],
}]
}],
items: {
type: 'object',
required: [
@@ -48,6 +56,11 @@ const webviewEditorsContribution: IJSONSchema = {
description: nls.localize('contributes.selector', 'Set of globs that the custom editor is enabled for.'),
items: {
type: 'object',
defaultSnippets: [{
body: {
filenamePattern: '$1',
}
}],
properties: {
filenamePattern: {
type: 'string',
@@ -64,7 +77,7 @@ const webviewEditorsContribution: IJSONSchema = {
CustomEditorPriority.option,
CustomEditorPriority.builtin,
],
enumDescriptions: [
markdownEnumDescriptions: [
nls.localize('contributes.priority.default', 'Editor is automatically used for a resource if no other default custom editors are registered for it.'),
nls.localize('contributes.priority.option', 'Editor is not automatically used but can be selected by a user.'),
nls.localize('contributes.priority.builtin', 'Editor automatically used if no other `default` or `builtin` editors are registered for the resource.'),

View File

@@ -57,8 +57,8 @@ export interface ICustomEditorModel extends IDisposable {
revert(options?: IRevertOptions): Promise<void>;
save(options?: ISaveOptions): Promise<boolean>;
saveAs(resource: URI, targetResource: URI, currentOptions?: ISaveOptions): Promise<boolean>;
saveCustomEditor(options?: ISaveOptions): Promise<URI | undefined>;
saveCustomEditorAs(resource: URI, targetResource: URI, currentOptions?: ISaveOptions): Promise<boolean>;
}
export const enum CustomEditorPriority {

View File

@@ -68,11 +68,11 @@ export class CustomTextEditorModel extends Disposable implements ICustomEditorMo
return this.textFileService.revert(this.resource, options);
}
public async save(options?: ISaveOptions): Promise<boolean> {
return !!await this.textFileService.save(this.resource, options);
public saveCustomEditor(options?: ISaveOptions): Promise<URI | undefined> {
return this.textFileService.save(this.resource, options);
}
public async saveAs(resource: URI, targetResource: URI, options?: ISaveOptions): Promise<boolean> {
public async saveCustomEditorAs(resource: URI, targetResource: URI, options?: ISaveOptions): Promise<boolean> {
return !!await this.textFileService.saveAs(resource, targetResource, options);
}
}

View File

@@ -103,6 +103,7 @@ const VIEW_CONTAINER: ViewContainer = Registry.as<IViewContainersRegistry>(ViewE
Registry.as<IViewsRegistry>(ViewExtensions.ViewsRegistry).registerViews([{
id: REPL_VIEW_ID,
name: nls.localize({ comment: ['Debug is a noun in this context, not a verb.'], key: 'debugPanel' }, 'Debug Console'),
containerIcon: 'codicon-debug-console',
canToggleVisibility: false,
canMoveView: true,
ctorDescriptor: new SyncDescriptor(Repl),

View File

@@ -36,7 +36,7 @@ import { IDecorationOptions } from 'vs/editor/common/editorCommon';
import { transparent, editorForeground } from 'vs/platform/theme/common/colorRegistry';
import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService';
import { FocusSessionActionViewItem } from 'vs/workbench/contrib/debug/browser/debugActionViewItems';
import { CompletionContext, CompletionList, CompletionProviderRegistry, CompletionItem, completionKindFromString, CompletionItemKind } from 'vs/editor/common/modes';
import { CompletionContext, CompletionList, CompletionProviderRegistry, CompletionItem, completionKindFromString, CompletionItemKind, CompletionItemInsertTextRule } from 'vs/editor/common/modes';
import { first } from 'vs/base/common/arrays';
import { ITreeNode, ITreeContextMenuEvent, IAsyncDataSource } from 'vs/base/browser/ui/tree/tree';
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
@@ -147,13 +147,24 @@ export class Repl extends ViewPane implements IHistoryNavigationWidget {
if (response && response.body && response.body.targets) {
response.body.targets.forEach(item => {
if (item && item.label) {
let insertTextRules: CompletionItemInsertTextRule | undefined = undefined;
let insertText = item.text || item.label;
if (typeof item.selectionStart === 'number') {
// If a debug completion item sets a selection we need to use snippets to make sure the selection is selected #90974
insertTextRules = CompletionItemInsertTextRule.InsertAsSnippet;
const selectionLength = typeof item.selectionLength === 'number' ? item.selectionLength : 0;
const placeholder = selectionLength > 0 ? '${1:' + insertText.substr(item.selectionStart, selectionLength) + '}$0' : '$0';
insertText = insertText.substr(0, item.selectionStart) + placeholder + insertText.substr(item.selectionStart + selectionLength);
}
suggestions.push({
label: item.label,
insertText: item.text || item.label,
insertText,
kind: completionKindFromString(item.type || 'property'),
filterText: (item.start && item.length) ? text.substr(item.start, item.length).concat(item.label) : undefined,
range: computeRange(item.length || overwriteBefore),
sortText: item.sortText
sortText: item.sortText,
insertTextRules
});
}
});

View File

@@ -247,6 +247,9 @@ function convertPaths(msg: DebugProtocol.ProtocolMessage, fixSourcePath: (toDA:
export function getVisibleAndSorted<T extends { presentation?: IConfigPresentation }>(array: T[]): T[] {
return array.filter(config => !config.presentation?.hidden).sort((first, second) => {
if (!first.presentation) {
if (!second.presentation) {
return 0;
}
return 1;
}
if (!second.presentation) {
@@ -271,6 +274,10 @@ export function getVisibleAndSorted<T extends { presentation?: IConfigPresentati
function compareOrders(first: number | undefined, second: number | undefined): number {
if (typeof first !== 'number') {
if (typeof second !== 'number') {
return 0;
}
return 1;
}
if (typeof second !== 'number') {

View File

@@ -207,6 +207,7 @@ export class ExtensionEditor extends BaseEditor {
const root = append(parent, $('.extension-editor'));
root.tabIndex = 0; // this is required for the focus tracker on the editor
root.style.outline = 'none';
root.setAttribute('role', 'document');
const header = append(root, $('.header'));
const iconContainer = append(header, $('.icon-container'));

View File

@@ -32,6 +32,7 @@ import { isWindows } from 'vs/base/common/platform';
import { Schemas } from 'vs/base/common/network';
import { IPreferencesService } from 'vs/workbench/services/preferences/common/preferences';
import { SaveReason } from 'vs/workbench/common/editor';
import { IStorageKeysSyncRegistryService } from 'vs/platform/userDataSync/common/storageKeys';
export const CONFLICT_RESOLUTION_CONTEXT = 'saveConflictResolutionContext';
export const CONFLICT_RESOLUTION_SCHEME = 'conflictResolution';
@@ -53,10 +54,14 @@ export class TextFileSaveErrorHandler extends Disposable implements ISaveErrorHa
@IEditorService private readonly editorService: IEditorService,
@ITextModelService textModelService: ITextModelService,
@IInstantiationService private readonly instantiationService: IInstantiationService,
@IStorageService private readonly storageService: IStorageService
@IStorageService private readonly storageService: IStorageService,
@IStorageKeysSyncRegistryService storageKeysSyncRegistryService: IStorageKeysSyncRegistryService
) {
super();
// opt-in to syncing
storageKeysSyncRegistryService.registerStorageKey({ key: LEARN_MORE_DIRTY_WRITE_IGNORE_KEY, version: 1 });
this.messages = new ResourceMap<INotificationHandle>();
this.conflictResolutionContext = new RawContextKey<boolean>(CONFLICT_RESOLUTION_CONTEXT, false).bindTo(contextKeyService);

View File

@@ -26,11 +26,14 @@ import { minimumTranslatedStrings } from 'vs/workbench/contrib/localizations/bro
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { CancellationToken } from 'vs/base/common/cancellation';
import { ExtensionType } from 'vs/platform/extensions/common/extensions';
import { IStorageKeysSyncRegistryService } from 'vs/platform/userDataSync/common/storageKeys';
// Register action to configure locale and related settings
const registry = Registry.as<IWorkbenchActionRegistry>(Extensions.WorkbenchActions);
registry.registerWorkbenchAction(SyncActionDescriptor.create(ConfigureLocaleAction, ConfigureLocaleAction.ID, ConfigureLocaleAction.LABEL), 'Configure Display Language');
const LANGUAGEPACK_SUGGESTION_IGNORE_STORAGE_KEY = 'extensionsAssistant/languagePackSuggestionIgnore';
export class LocalizationWorkbenchContribution extends Disposable implements IWorkbenchContribution {
constructor(
@INotificationService private readonly notificationService: INotificationService,
@@ -41,9 +44,12 @@ export class LocalizationWorkbenchContribution extends Disposable implements IWo
@IExtensionManagementService private readonly extensionManagementService: IExtensionManagementService,
@IExtensionGalleryService private readonly galleryService: IExtensionGalleryService,
@IViewletService private readonly viewletService: IViewletService,
@ITelemetryService private readonly telemetryService: ITelemetryService
@ITelemetryService private readonly telemetryService: ITelemetryService,
@IStorageKeysSyncRegistryService storageKeysSyncRegistryService: IStorageKeysSyncRegistryService
) {
super();
storageKeysSyncRegistryService.registerStorageKey({ key: LANGUAGEPACK_SUGGESTION_IGNORE_STORAGE_KEY, version: 1 });
this.checkAndInstall();
this._register(this.extensionManagementService.onDidInstallExtension(e => this.onDidInstallExtension(e)));
}
@@ -77,7 +83,7 @@ export class LocalizationWorkbenchContribution extends Disposable implements IWo
private checkAndInstall(): void {
const language = platform.language;
const locale = platform.locale;
const languagePackSuggestionIgnoreList = <string[]>JSON.parse(this.storageService.get('extensionsAssistant/languagePackSuggestionIgnore', StorageScope.GLOBAL, '[]'));
const languagePackSuggestionIgnoreList = <string[]>JSON.parse(this.storageService.get(LANGUAGEPACK_SUGGESTION_IGNORE_STORAGE_KEY, StorageScope.GLOBAL, '[]'));
if (!this.galleryService.isEnabled()) {
return;
@@ -168,7 +174,7 @@ export class LocalizationWorkbenchContribution extends Disposable implements IWo
run: () => {
languagePackSuggestionIgnoreList.push(language);
this.storageService.store(
'extensionsAssistant/languagePackSuggestionIgnore',
LANGUAGEPACK_SUGGESTION_IGNORE_STORAGE_KEY,
JSON.stringify(languagePackSuggestionIgnoreList),
StorageScope.GLOBAL
);

View File

@@ -20,11 +20,11 @@ export const COPY_CELL_DOWN_COMMAND_ID = 'workbench.notebook.cell.copyDown';
export const EXECUTE_CELL_COMMAND_ID = 'workbench.notebook.cell.execute';
// Cell sizing related
export const CELL_MARGIN = 32;
export const CELL_MARGIN = 20;
export const CELL_RUN_GUTTER = 32; // TODO should be dynamic based on execution order width, and runnable enablement
export const EDITOR_TOP_PADDING = 8;
export const EDITOR_BOTTOM_PADDING = 8;
export const EDITOR_TOOLBAR_HEIGHT = 22;
export const RUN_BUTTON_WIDTH = 20;
// Cell context keys
export const NOTEBOOK_CELL_TYPE_CONTEXT_KEY = 'notebookCellType'; // code, markdown

View File

@@ -16,6 +16,7 @@ import { CellRenderTemplate, CellEditState, ICellViewModel, INotebookEditor, KEY
import { INotebookService } from 'vs/workbench/contrib/notebook/browser/notebookService';
import { CellKind, NOTEBOOK_EDITOR_CURSOR_BOUNDARY } from 'vs/workbench/contrib/notebook/common/notebookCommon';
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
import { EditorContextKeys } from 'vs/editor/common/editorContextKeys';
const enum CellToolbarOrder {
MoveCellUp,
@@ -709,9 +710,9 @@ registerAction2(class extends Action2 {
id: 'workbench.action.notebook.cursorDown',
title: 'Notebook Cursor Move Down',
keybinding: {
when: ContextKeyExpr.and(NOTEBOOK_EDITOR_FOCUSED, ContextKeyExpr.has(InputFocusedContextKey), NOTEBOOK_EDITOR_CURSOR_BOUNDARY.notEqualsTo('top'), NOTEBOOK_EDITOR_CURSOR_BOUNDARY.notEqualsTo('none')),
when: ContextKeyExpr.and(NOTEBOOK_EDITOR_FOCUSED, ContextKeyExpr.has(InputFocusedContextKey), EditorContextKeys.editorTextFocus, NOTEBOOK_EDITOR_CURSOR_BOUNDARY.notEqualsTo('top'), NOTEBOOK_EDITOR_CURSOR_BOUNDARY.notEqualsTo('none')),
primary: KeyCode.DownArrow,
weight: KeybindingWeight.WorkbenchContrib
weight: KeybindingWeight.EditorContrib // smaller than Suggest Widget, etc
}
});
}
@@ -748,9 +749,9 @@ registerAction2(class extends Action2 {
id: 'workbench.action.notebook.cursorUp',
title: 'Notebook Cursor Move Up',
keybinding: {
when: ContextKeyExpr.and(NOTEBOOK_EDITOR_FOCUSED, ContextKeyExpr.has(InputFocusedContextKey), NOTEBOOK_EDITOR_CURSOR_BOUNDARY.notEqualsTo('bottom'), NOTEBOOK_EDITOR_CURSOR_BOUNDARY.notEqualsTo('none')),
when: ContextKeyExpr.and(NOTEBOOK_EDITOR_FOCUSED, ContextKeyExpr.has(InputFocusedContextKey), EditorContextKeys.editorTextFocus, NOTEBOOK_EDITOR_CURSOR_BOUNDARY.notEqualsTo('bottom'), NOTEBOOK_EDITOR_CURSOR_BOUNDARY.notEqualsTo('none')),
primary: KeyCode.UpArrow,
weight: KeybindingWeight.WorkbenchContrib
weight: KeybindingWeight.EditorContrib // smaller than Suggest Widget, etc
},
});
}

View File

@@ -30,7 +30,7 @@
position: relative;
}
.monaco-workbench .part.editor > .content .notebook-editor .monaco-list-row .cell {
.monaco-workbench .part.editor > .content .notebook-editor > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row .cell {
display: flex;
}
@@ -87,16 +87,16 @@
}
.monaco-workbench .part.editor > .content .notebook-editor .monaco-list-row {
.monaco-workbench .part.editor > .content .notebook-editor > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row {
overflow: visible !important;
}
.monaco-workbench .part.editor > .content .notebook-editor .monaco-list-row:focus-within {
.monaco-workbench .part.editor > .content .notebook-editor > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row:focus-within {
z-index: 10;
}
.monaco-workbench .part.editor > .content .notebook-editor .monaco-list-row .menu {
.monaco-workbench .part.editor > .content .notebook-editor > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row .menu {
position: absolute;
left: 0;
top: 28px;
@@ -107,40 +107,65 @@
}
.monaco-workbench .part.editor > .content .notebook-editor .monaco-list-row .menu.mouseover,
.monaco-workbench .part.editor > .content .notebook-editor .monaco-list-row:hover .menu {
.monaco-workbench .part.editor > .content .notebook-editor > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row .menu.mouseover,
.monaco-workbench .part.editor > .content .notebook-editor > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row:hover .menu {
visibility: visible;
}
.monaco-workbench .part.editor > .content .notebook-editor .monaco-list .monaco-list-row:hover {
.monaco-workbench .part.editor > .content .notebook-editor > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row:hover {
outline: none !important;
}
.monaco-workbench .part.editor > .content .notebook-editor .monaco-list .monaco-list-row.selected,
.monaco-workbench .part.editor > .content .notebook-editor .monaco-list .monaco-list-row.focused {
.monaco-workbench .part.editor > .content .notebook-editor > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row.selected,
.monaco-workbench .part.editor > .content .notebook-editor > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row.focused {
outline: none !important;
}
.monaco-workbench .part.editor > .content .notebook-editor .monaco-list-row .menu.mouseover,
.monaco-workbench .part.editor > .content .notebook-editor .monaco-list-row .menu:hover {
.monaco-workbench .part.editor > .content .notebook-editor > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row .menu.mouseover,
.monaco-workbench .part.editor > .content .notebook-editor > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row .menu:hover {
cursor: pointer;
}
.monaco-workbench .part.editor > .content .notebook-editor .monaco-list-row > .monaco-toolbar {
.monaco-workbench .part.editor > .content .notebook-editor > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row > .monaco-toolbar {
visibility: hidden;
margin-right: 24px;
}
.monaco-workbench .part.editor > .content .notebook-editor .monaco-list-row .cell .run-button-container .monaco-toolbar {
margin-top: 8px;
.monaco-workbench .part.editor > .content .notebook-editor > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row .cell .run-button-container {
position: relative;
}
.monaco-workbench .part.editor > .content .notebook-editor > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row .cell .run-button-container .monaco-toolbar {
margin-top: 5px;
visibility: hidden;
}
.monaco-workbench .part.editor > .content .notebook-editor .monaco-list-row:hover .cell .run-button-container .monaco-toolbar,
.monaco-workbench .part.editor > .content .notebook-editor .monaco-list-row.focused .cell .run-button-container .monaco-toolbar {
.monaco-workbench .part.editor > .content .notebook-editor > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row .cell .run-button-container .monaco-toolbar .codicon-play {
margin-right: 8px;
}
.monaco-workbench .part.editor > .content .notebook-editor > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row:hover .cell.runnable .run-button-container .monaco-toolbar,
.monaco-workbench .part.editor > .content .notebook-editor > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row.focused .cell.runnable .run-button-container .monaco-toolbar {
visibility: visible;
}
.monaco-workbench .part.editor > .content .notebook-editor > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row .cell .run-button-container .execution-count-label {
position: absolute;
top: 2px;
font-size: 12px;
visibility: visible;
white-space: pre;
width: 100%;
text-align: center;
padding-right: 2px;
box-sizing: border-box;
}
.monaco-workbench .part.editor > .content .notebook-editor > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row:hover .cell .run-button-container .execution-count-label,
.monaco-workbench .part.editor > .content .notebook-editor > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row.focused .cell .run-button-container .execution-count-label {
visibility: hidden;
}
.monaco-workbench .part.editor > .content .notebook-editor .cell .cell-editor-container {
position: relative;
}
@@ -149,13 +174,13 @@
top: 0px;
}
.monaco-workbench .part.editor > .content .notebook-editor .monaco-list-row.focused .monaco-toolbar,
.monaco-workbench .part.editor > .content .notebook-editor .monaco-list-row:hover .monaco-toolbar {
.monaco-workbench .part.editor > .content .notebook-editor > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row.focused .monaco-toolbar,
.monaco-workbench .part.editor > .content .notebook-editor > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row:hover .monaco-toolbar {
visibility: visible;
}
.monaco-workbench .part.editor > .content .notebook-editor .monaco-tree.focused.no-focused-item:focus:before,
.monaco-workbench .part.editor > .content .notebook-editor .monaco-list:not(.element-focused):focus:before {
.monaco-workbench .part.editor > .content .notebook-editor > .cell-list-container > .monaco-list:not(.element-focused):focus:before {
outline: none !important;
}
@@ -167,7 +192,7 @@
width: 6px;
border-left-width: 2px;
border-left-style: solid;
left: 28px;
left: 20px;
top: 22px;
bottom: 8px;
visibility: hidden;

View File

@@ -15,7 +15,7 @@ import { FindMatch } from 'vs/editor/common/model';
import { RawContextKey } from 'vs/platform/contextkey/common/contextkey';
import { OutputRenderer } from 'vs/workbench/contrib/notebook/browser/view/output/outputRenderer';
import { IModelDecorationsChangeAccessor, NotebookViewModel, CellViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/notebookViewModel';
import { CellKind, IOutput, IRenderOutput, NotebookCellMetadata } from 'vs/workbench/contrib/notebook/common/notebookCommon';
import { CellKind, IOutput, IRenderOutput, NotebookCellMetadata, NotebookDocumentMetadata } from 'vs/workbench/contrib/notebook/common/notebookCommon';
import { NOTEBOOK_EDITABLE_CONTEXT_KEY } from 'vs/workbench/contrib/notebook/browser/constants';
export const KEYBINDING_CONTEXT_NOTEBOOK_FIND_WIDGET_FOCUSED = new RawContextKey<boolean>('notebookFindWidgetFocused', false);
@@ -72,6 +72,7 @@ export interface ICellViewModel {
focusMode: CellFocusMode;
getText(): string;
metadata: NotebookCellMetadata | undefined;
getEvaluatedMetadata(documentMetadata: NotebookDocumentMetadata | undefined): NotebookCellMetadata;
}
export interface INotebookEditor {
@@ -249,6 +250,7 @@ export interface CellRenderTemplate {
focusIndicator?: HTMLElement;
runToolbar?: ToolBar;
runButtonContainer?: HTMLElement;
executionOrderLabel?: HTMLElement;
editingContainer?: HTMLElement;
outputContainer?: HTMLElement;
editor?: CodeEditorWidget;

View File

@@ -14,7 +14,6 @@ import { IEditorOptions } from 'vs/editor/common/config/editorOptions';
import { BareFontInfo } from 'vs/editor/common/config/fontInfo';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { IStorageService } from 'vs/platform/storage/common/storage';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
@@ -29,7 +28,6 @@ import { OutputRenderer } from 'vs/workbench/contrib/notebook/browser/view/outpu
import { BackLayerWebView } from 'vs/workbench/contrib/notebook/browser/view/renderers/backLayerWebView';
import { CodeCellRenderer, MarkdownCellRenderer, NotebookCellListDelegate } from 'vs/workbench/contrib/notebook/browser/view/renderers/cellRenderer';
import { IOutput, CellKind, CellUri } from 'vs/workbench/contrib/notebook/common/notebookCommon';
import { IWebviewService } from 'vs/workbench/contrib/webview/browser/webview';
import { getExtraColor } from 'vs/workbench/contrib/welcome/walkThrough/common/walkThroughUtils';
import { IEditorGroup, IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService';
import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
@@ -42,7 +40,7 @@ import { NotebookViewModel, INotebookEditorViewState, IModelDecorationsChangeAcc
import { IEditorGroupView } from 'vs/workbench/browser/parts/editor/editor';
import { CodeCellViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/codeCellViewModel';
import { Range } from 'vs/editor/common/core/range';
import { CELL_MARGIN, RUN_BUTTON_WIDTH } from 'vs/workbench/contrib/notebook/browser/constants';
import { CELL_MARGIN, CELL_RUN_GUTTER } from 'vs/workbench/contrib/notebook/browser/constants';
import { Color, RGBA } from 'vs/base/common/color';
import { NotebookEventDispatcher, NotebookLayoutChangedEvent } from 'vs/workbench/contrib/notebook/browser/viewModel/eventDispatcher';
@@ -113,11 +111,9 @@ export class NotebookEditor extends BaseEditor implements INotebookEditor {
@IThemeService themeService: IThemeService,
@IInstantiationService private readonly instantiationService: IInstantiationService,
@IStorageService storageService: IStorageService,
@IWebviewService private webviewService: IWebviewService,
@INotebookService private notebookService: INotebookService,
@IEditorGroupsService editorGroupService: IEditorGroupsService,
@IConfigurationService private readonly configurationService: IConfigurationService,
@IEnvironmentService private readonly environmentSerice: IEnvironmentService,
@IContextKeyService private readonly contextKeyService: IContextKeyService,
) {
super(NotebookEditor.ID, telemetryService, themeService, storageService);
@@ -223,7 +219,7 @@ export class NotebookEditor extends BaseEditor implements INotebookEditor {
);
this.control = new NotebookCodeEditors(this.list, this.renderedEditors);
this.webview = new BackLayerWebView(this.webviewService, this.notebookService, this, this.environmentSerice);
this.webview = this.instantiationService.createInstance(BackLayerWebView, this);
this._register(this.webview.onMessage(message => {
if (this.viewModel) {
this.notebookService.onDidReceiveMessage(this.viewModel.viewType, this.viewModel.uri, message);
@@ -334,7 +330,7 @@ export class NotebookEditor extends BaseEditor implements INotebookEditor {
private async attachModel(input: NotebookEditorInput, model: NotebookEditorModel) {
if (!this.webview) {
this.webview = new BackLayerWebView(this.webviewService, this.notebookService, this, this.environmentSerice);
this.webview = this.instantiationService.createInstance(BackLayerWebView, this);
this.list?.rowsContainer.insertAdjacentElement('afterbegin', this.webview!.element);
}
@@ -798,8 +794,10 @@ registerThemingParticipant((theme, collector) => {
}
// Cell Margin
collector.addRule(`.monaco-workbench .part.editor > .content .notebook-editor .monaco-list-row > div.cell { padding: 8px ${CELL_MARGIN}px 8px ${CELL_MARGIN}px; }`);
collector.addRule(`.monaco-workbench .part.editor > .content .notebook-editor .output { margin: 8px ${CELL_MARGIN}px 8px ${CELL_MARGIN + RUN_BUTTON_WIDTH}px }`);
collector.addRule(`.monaco-workbench .part.editor > .content .notebook-editor .monaco-list-row > div.cell { margin: 8px ${CELL_MARGIN}px 8px ${CELL_MARGIN}px; }`);
collector.addRule(`.monaco-workbench .part.editor > .content .notebook-editor .output { margin: 8px ${CELL_MARGIN}px 8px ${CELL_MARGIN + CELL_RUN_GUTTER}px }`);
collector.addRule(`.monaco-workbench .part.editor > .content .notebook-editor .cell .cell-editor-container { width: calc(100% - ${RUN_BUTTON_WIDTH}px); }`);
collector.addRule(`.monaco-workbench .part.editor > .content .notebook-editor .cell .cell-editor-container { width: calc(100% - ${CELL_RUN_GUTTER}px); }`);
collector.addRule(`.monaco-workbench .part.editor > .content .notebook-editor .monaco-list-row > div.cell.markdown { padding-left: ${CELL_RUN_GUTTER}px; }`);
collector.addRule(`.monaco-workbench .part.editor > .content .notebook-editor .cell .run-button-container { width: ${CELL_RUN_GUTTER}px; }`);
});

View File

@@ -15,8 +15,9 @@ import { IOutput } from 'vs/workbench/contrib/notebook/common/notebookCommon';
import { IWebviewService, WebviewElement } from 'vs/workbench/contrib/webview/browser/webview';
import { WebviewResourceScheme } from 'vs/workbench/contrib/webview/common/resourceLoader';
import { CodeCellViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/codeCellViewModel';
import { CELL_MARGIN } from 'vs/workbench/contrib/notebook/browser/constants';
import { CELL_MARGIN, CELL_RUN_GUTTER } from 'vs/workbench/contrib/notebook/browser/constants';
import { Emitter, Event } from 'vs/base/common/event';
import { IOpenerService } from 'vs/platform/opener/common/opener';
export interface IDimentionMessage {
__vscode_notebook_message: boolean;
@@ -43,11 +44,13 @@ export interface ICreationRequestMessage {
id: string;
outputId: string;
top: number;
left: number;
}
export interface IContentWidgetTopRequest {
id: string;
top: number;
left: number;
}
export interface IViewScrollTopRequestMessage {
@@ -85,7 +88,13 @@ export class BackLayerWebView extends Disposable {
public readonly onMessage: Event<any> = this._onMessage.event;
constructor(public webviewService: IWebviewService, public notebookService: INotebookService, public notebookEditor: INotebookEditor, public environmentSerice: IEnvironmentService) {
constructor(
public notebookEditor: INotebookEditor,
@IWebviewService webviewService: IWebviewService,
@IOpenerService openerService: IOpenerService,
@IEnvironmentService private readonly environmentSerice: IEnvironmentService,
@INotebookService private readonly notebookService: INotebookService,
) {
super();
this.element = document.createElement('div');
@@ -194,6 +203,7 @@ export class BackLayerWebView extends Disposable {
let outputNode = document.createElement('div');
outputNode.style.position = 'absolute';
outputNode.style.top = event.data.top + 'px';
outputNode.style.left = event.data.left + 'px';
outputNode.id = outputId;
let content = event.data.content;
@@ -258,6 +268,10 @@ export class BackLayerWebView extends Disposable {
this.webview = this._createInset(webviewService, content);
this.webview.mountTo(this.element);
this._register(this.webview.onDidClickLink(link => {
openerService.open(link, { fromUserGesture: true });
}));
this._register(this.webview.onDidWheel(e => {
this.notebookEditor.triggerScroll(e);
}));
@@ -331,7 +345,8 @@ export class BackLayerWebView extends Disposable {
return {
id: id,
top: outputOffset
top: outputOffset,
left: CELL_RUN_GUTTER
};
});
@@ -355,7 +370,8 @@ export class BackLayerWebView extends Disposable {
content: shadowContent,
id: cell.id,
outputId: outputId,
top: initialTop
top: initialTop,
left: CELL_RUN_GUTTER
};
this.webview.sendMessage(message);

View File

@@ -6,6 +6,7 @@
import { getZoomLevel } from 'vs/base/browser/browser';
import * as DOM from 'vs/base/browser/dom';
import { IListRenderer, IListVirtualDelegate } from 'vs/base/browser/ui/list/list';
import { ProgressBar } from 'vs/base/browser/ui/progressbar/progressbar';
import { ToolBar } from 'vs/base/browser/ui/toolbar/toolbar';
import { IAction } from 'vs/base/common/actions';
import { DisposableStore } from 'vs/base/common/lifecycle';
@@ -16,15 +17,16 @@ import { CodeEditorWidget } from 'vs/editor/browser/widget/codeEditorWidget';
import { IEditorOptions } from 'vs/editor/common/config/editorOptions';
import { BareFontInfo } from 'vs/editor/common/config/fontInfo';
import { ContextAwareMenuEntryActionViewItem } from 'vs/platform/actions/browser/menuEntryActionViewItem';
import { MenuItemAction, IMenu } from 'vs/platform/actions/common/actions';
import { IMenu, MenuItemAction } from 'vs/platform/actions/common/actions';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
import { IContextMenuService } from 'vs/platform/contextview/browser/contextView';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
import { INotificationService } from 'vs/platform/notification/common/notification';
import { EDITOR_BOTTOM_PADDING, EDITOR_TOOLBAR_HEIGHT, EDITOR_TOP_PADDING, NOTEBOOK_CELL_EDITABLE_CONTEXT_KEY, NOTEBOOK_CELL_TYPE_CONTEXT_KEY, NOTEBOOK_CELL_MARKDOWN_EDIT_MODE_CONTEXT_KEY } from 'vs/workbench/contrib/notebook/browser/constants';
import { CellRenderTemplate, CellRunState, ICellViewModel, INotebookEditor, CellEditState } from 'vs/workbench/contrib/notebook/browser/notebookBrowser';
import { EDITOR_BOTTOM_PADDING, EDITOR_TOOLBAR_HEIGHT, EDITOR_TOP_PADDING, NOTEBOOK_CELL_EDITABLE_CONTEXT_KEY, NOTEBOOK_CELL_MARKDOWN_EDIT_MODE_CONTEXT_KEY, NOTEBOOK_CELL_TYPE_CONTEXT_KEY } from 'vs/workbench/contrib/notebook/browser/constants';
import { ExecuteCellAction, INotebookCellActionContext } from 'vs/workbench/contrib/notebook/browser/contrib/notebookActions';
import { CellEditState, CellRenderTemplate, CellRunState, ICellViewModel, INotebookEditor } from 'vs/workbench/contrib/notebook/browser/notebookBrowser';
import { CellMenus } from 'vs/workbench/contrib/notebook/browser/view/renderers/cellMenus';
import { CodeCell } from 'vs/workbench/contrib/notebook/browser/view/renderers/codeCell';
import { StatefullMarkdownCell } from 'vs/workbench/contrib/notebook/browser/view/renderers/markdownCell';
@@ -32,8 +34,6 @@ import { CodeCellViewModel } from 'vs/workbench/contrib/notebook/browser/viewMod
import { MarkdownCellViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/markdownCellViewModel';
import { CellViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/notebookViewModel';
import { CellKind } from 'vs/workbench/contrib/notebook/common/notebookCommon';
import { INotebookCellActionContext, ExecuteCellAction } from 'vs/workbench/contrib/notebook/browser/contrib/notebookActions';
import { ProgressBar } from 'vs/base/browser/ui/progressbar/progressbar';
const $ = DOM.$;
@@ -221,8 +221,8 @@ export class MarkdownCellRenderer extends AbstractCellRenderer implements IListR
const contextKeyService = this.contextKeyService.createScoped(templateData.container);
contextKeyService.createKey(NOTEBOOK_CELL_TYPE_CONTEXT_KEY, 'markdown');
const cellEditableKey = contextKeyService.createKey(NOTEBOOK_CELL_EDITABLE_CONTEXT_KEY, !!(element.metadata?.editable));
elementDisposable.add(element.onDidChangeMetadata((e) => {
cellEditableKey.set(!!e?.editable);
elementDisposable.add(element.onDidChangeMetadata(() => {
cellEditableKey.set(!!element.metadata?.editable);
}));
const editModeKey = contextKeyService.createKey(NOTEBOOK_CELL_MARKDOWN_EDIT_MODE_CONTEXT_KEY, element.editState === CellEditState.Editing);
@@ -286,6 +286,8 @@ export class CodeCellRenderer extends AbstractCellRenderer implements IListRende
])();
disposables.add(runToolbar);
const executionOrderLabel = DOM.append(runButtonContainer, $('div.execution-count-label'));
const editorContainer = DOM.append(cellContainer, $('.cell-editor-container'));
const editor = this.instantiationService.createInstance(CodeEditorWidget, editorContainer, {
...this.editorOptions,
@@ -314,6 +316,7 @@ export class CodeCellRenderer extends AbstractCellRenderer implements IListRende
toolbar,
runToolbar,
runButtonContainer,
executionOrderLabel,
outputContainer,
editor,
disposables,
@@ -352,21 +355,36 @@ export class CodeCellRenderer extends AbstractCellRenderer implements IListRende
}
}));
function renderExecutionOrder() {
const executionOrdeerLabel = typeof element.metadata?.executionOrder === 'number' ? `[ ${element.metadata.executionOrder} ]` :
'[ ]';
templateData.executionOrderLabel!.innerText = executionOrdeerLabel;
}
const contextKeyService = this.contextKeyService.createScoped(templateData.container);
contextKeyService.createKey(NOTEBOOK_CELL_TYPE_CONTEXT_KEY, 'code');
const cellEditableKey = contextKeyService.createKey(NOTEBOOK_CELL_EDITABLE_CONTEXT_KEY, !!(element.metadata?.editable));
const updateForMetadata = () => {
const metadata = element.getEvaluatedMetadata(this.notebookEditor.viewModel!.notebookDocument.metadata);
DOM.toggleClass(templateData.cellContainer, 'runnable', !!metadata.runnable);
renderExecutionOrder();
cellEditableKey.set(!!metadata.editable);
};
updateForMetadata();
elementDisposable.add(element.onDidChangeMetadata(() => {
updateForMetadata();
}));
this.setupCellToolbarActions(contextKeyService, templateData, elementDisposable);
const toolbarContext = <INotebookCellActionContext>{
cell: element,
cellTemplate: templateData,
notebookEditor: this.notebookEditor,
$mid: 12
};
const contextKeyService = this.contextKeyService.createScoped(templateData.container);
contextKeyService.createKey(NOTEBOOK_CELL_TYPE_CONTEXT_KEY, 'code');
const cellEditableKey = contextKeyService.createKey(NOTEBOOK_CELL_EDITABLE_CONTEXT_KEY, !!(element.metadata?.editable));
elementDisposable.add(element.onDidChangeMetadata((e) => {
cellEditableKey.set(!!e?.editable);
}));
this.setupCellToolbarActions(contextKeyService, templateData, elementDisposable);
templateData.toolbar.context = toolbarContext;
templateData.runToolbar!.context = toolbarContext;
}

View File

@@ -83,7 +83,7 @@ export class CodeCell extends Disposable {
}));
templateData.editor?.updateOptions({ readOnly: !(viewCell.getEvaluatedMetadata(notebookEditor.viewModel?.metadata).editable) });
this._register(viewCell.onDidChangeMetadata((e) => {
this._register(viewCell.onDidChangeMetadata(() => {
templateData.editor?.updateOptions({ readOnly: !(viewCell.getEvaluatedMetadata(notebookEditor.viewModel?.metadata).editable) });
}));

View File

@@ -32,8 +32,8 @@ export abstract class BaseCellViewModel extends Disposable {
readonly onDidChangeEditorAttachState = this._onDidChangeEditorAttachState.event;
protected readonly _onDidChangeCursorSelection: Emitter<void> = this._register(new Emitter<void>());
public readonly onDidChangeCursorSelection: Event<void> = this._onDidChangeCursorSelection.event;
protected readonly _onDidChangeMetadata: Emitter<NotebookCellMetadata | undefined> = this._register(new Emitter<NotebookCellMetadata | undefined>());
public readonly onDidChangeMetadata: Event<NotebookCellMetadata | undefined> = this._onDidChangeMetadata.event;
protected readonly _onDidChangeMetadata: Emitter<void> = this._register(new Emitter<void>());
public readonly onDidChangeMetadata: Event<void> = this._onDidChangeMetadata.event;
protected readonly _onDidChangeLanguage: Emitter<string> = this._register(new Emitter<string>());
public readonly onDidChangeLanguage: Event<string> = this._onDidChangeLanguage.event;
get handle() {
@@ -106,8 +106,8 @@ export abstract class BaseCellViewModel extends Disposable {
this._onDidChangeLanguage.fire(e);
}));
this._register(cell.onDidChangeMetadata((e) => {
this._onDidChangeMetadata.fire(e);
this._register(cell.onDidChangeMetadata(() => {
this._onDidChangeMetadata.fire();
}));
}

View File

@@ -8,7 +8,7 @@ import * as UUID from 'vs/base/common/uuid';
import * as model from 'vs/editor/common/model';
import { ITextModelService } from 'vs/editor/common/services/resolverService';
import { PrefixSumComputer } from 'vs/editor/common/viewModel/prefixSumComputer';
import { EDITOR_BOTTOM_PADDING, EDITOR_TOOLBAR_HEIGHT, EDITOR_TOP_PADDING, CELL_MARGIN, RUN_BUTTON_WIDTH } from 'vs/workbench/contrib/notebook/browser/constants';
import { EDITOR_BOTTOM_PADDING, EDITOR_TOOLBAR_HEIGHT, EDITOR_TOP_PADDING, CELL_MARGIN, CELL_RUN_GUTTER } from 'vs/workbench/contrib/notebook/browser/constants';
import { CellEditState, ICellViewModel, CellFindMatch, CodeCellLayoutChangeEvent, CodeCellLayoutInfo } from 'vs/workbench/contrib/notebook/browser/notebookBrowser';
import { CellKind, ICell, NotebookCellOutputsSplice } from 'vs/workbench/contrib/notebook/common/notebookCommon';
import { BaseCellViewModel } from './baseCellViewModel';
@@ -72,6 +72,10 @@ export class CodeCellViewModel extends BaseCellViewModel implements ICellViewMod
}));
}
this._register(this.cell.onDidChangeMetadata(() => {
this._onDidChangeMetadata.fire();
}));
this._outputCollection = new Array(this.cell.outputs.length);
this._buffer = null;
@@ -105,7 +109,7 @@ export class CodeCellViewModel extends BaseCellViewModel implements ICellViewMod
? EDITOR_TOOLBAR_HEIGHT + this.editorHeight + EDITOR_TOP_PADDING + EDITOR_BOTTOM_PADDING + 16 + outputTotalHeight
: EDITOR_TOOLBAR_HEIGHT + this.editorHeight + EDITOR_TOP_PADDING + EDITOR_BOTTOM_PADDING + outputTotalHeight;
const indicatorHeight = totalHeight - EDITOR_TOOLBAR_HEIGHT - 16;
const editorWidth = state.outerWidth !== undefined ? state.outerWidth - CELL_MARGIN * 2 - RUN_BUTTON_WIDTH : 0;
const editorWidth = state.outerWidth !== undefined ? state.outerWidth - CELL_MARGIN * 2 - CELL_RUN_GUTTER : 0;
this._layoutInfo = {
fontInfo: state.font || null,
editorHeight: this._editorHeight,

View File

@@ -15,8 +15,8 @@ export class NotebookCellTextModel implements ICell {
private _onDidChangeContent = new Emitter<void>();
onDidChangeContent: Event<void> = this._onDidChangeContent.event;
private _onDidChangeMetadata = new Emitter<NotebookCellMetadata | undefined>();
onDidChangeMetadata: Event<NotebookCellMetadata | undefined> = this._onDidChangeMetadata.event;
private _onDidChangeMetadata = new Emitter<void>();
onDidChangeMetadata: Event<void> = this._onDidChangeMetadata.event;
private _onDidChangeLanguage = new Emitter<string>();
onDidChangeLanguage: Event<string> = this._onDidChangeLanguage.event;
@@ -44,7 +44,7 @@ export class NotebookCellTextModel implements ICell {
set metadata(newMetadata: NotebookCellMetadata | undefined) {
this._metadata = newMetadata;
this._onDidChangeMetadata.fire(this._metadata);
this._onDidChangeMetadata.fire();
}
get language() {

View File

@@ -45,6 +45,7 @@ export interface NotebookDocumentMetadata {
export interface NotebookCellMetadata {
editable?: boolean;
runnable?: boolean;
executionOrder?: number;
}
export interface INotebookDisplayOrder {
@@ -135,8 +136,8 @@ export interface ICell {
outputs: IOutput[];
metadata?: NotebookCellMetadata;
onDidChangeOutputs?: Event<NotebookCellOutputsSplice[]>;
onDidChangeMetadata: Event<NotebookCellMetadata | undefined>;
onDidChangeLanguage: Event<string>;
onDidChangeMetadata: Event<void>;
resolveTextBufferFactory(): PieceTreeTextBufferFactory;
// TODO@rebornix it should be later on replaced by moving textmodel resolution into CellTextModel
contentChange(): void;

View File

@@ -83,4 +83,81 @@ suite('NotebookViewModel', () => {
}
);
});
test('metadata', function () {
withTestNotebook(
instantiationService,
blukEditService,
undoRedoService,
[
[['var a = 1;'], 'javascript', CellKind.Code, [], {}],
[['var b = 2;'], 'javascript', CellKind.Code, [], { editable: true, runnable: true }],
[['var c = 3;'], 'javascript', CellKind.Code, [], { editable: true, runnable: false }],
[['var d = 4;'], 'javascript', CellKind.Code, [], { editable: false, runnable: true }],
[['var e = 5;'], 'javascript', CellKind.Code, [], { editable: false, runnable: false }],
],
(editor, viewModel) => {
viewModel.notebookDocument.metadata = { editable: true, cellRunnable: true, cellEditable: true };
assert.deepEqual(viewModel.viewCells[0].getEvaluatedMetadata(viewModel.metadata), {
editable: true,
runnable: true
});
assert.deepEqual(viewModel.viewCells[1].getEvaluatedMetadata(viewModel.metadata), {
editable: true,
runnable: true
});
assert.deepEqual(viewModel.viewCells[2].getEvaluatedMetadata(viewModel.metadata), {
editable: true,
runnable: false
});
assert.deepEqual(viewModel.viewCells[3].getEvaluatedMetadata(viewModel.metadata), {
editable: false,
runnable: true
});
assert.deepEqual(viewModel.viewCells[4].getEvaluatedMetadata(viewModel.metadata), {
editable: false,
runnable: false
});
viewModel.notebookDocument.metadata = { editable: true, cellRunnable: false, cellEditable: true };
assert.deepEqual(viewModel.viewCells[0].getEvaluatedMetadata(viewModel.metadata), {
editable: true,
runnable: false
});
assert.deepEqual(viewModel.viewCells[1].getEvaluatedMetadata(viewModel.metadata), {
editable: true,
runnable: true
});
assert.deepEqual(viewModel.viewCells[2].getEvaluatedMetadata(viewModel.metadata), {
editable: true,
runnable: false
});
assert.deepEqual(viewModel.viewCells[3].getEvaluatedMetadata(viewModel.metadata), {
editable: false,
runnable: true
});
assert.deepEqual(viewModel.viewCells[4].getEvaluatedMetadata(viewModel.metadata), {
editable: false,
runnable: false
});
viewModel.notebookDocument.metadata = { editable: true, cellRunnable: false, cellEditable: false };
assert.deepEqual(viewModel.viewCells[0].getEvaluatedMetadata(viewModel.metadata), {
editable: false,
runnable: false
});
}
);
});
});

View File

@@ -25,11 +25,10 @@ export class TestCell implements ICell {
uri: URI;
private _onDidChangeOutputs = new Emitter<NotebookCellOutputsSplice[]>();
onDidChangeOutputs: Event<NotebookCellOutputsSplice[]> = this._onDidChangeOutputs.event;
private _onDidChangeMetadata = new Emitter<NotebookCellMetadata>();
onDidChangeMetadata: Event<NotebookCellMetadata> = this._onDidChangeMetadata.event;
private _onDidChangeLanguage = new Emitter<string>();
onDidChangeLanguage: Event<string> = this._onDidChangeLanguage.event;
private _onDidChangeMetadata = new Emitter<void>();
onDidChangeMetadata: Event<void> = this._onDidChangeMetadata.event;
private _isDirty: boolean = false;
private _outputs: IOutput[];

View File

@@ -1,111 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { localize } from 'vs/nls';
import { STATUS_BAR_PROMINENT_ITEM_BACKGROUND, STATUS_BAR_PROMINENT_ITEM_FOREGROUND } from 'vs/workbench/common/theme';
import { themeColorFromId } from 'vs/platform/theme/common/themeService';
import { Disposable } from 'vs/base/common/lifecycle';
import { Registry } from 'vs/platform/registry/common/platform';
import { IWorkbenchContribution, IWorkbenchContributionsRegistry, Extensions as WorkbenchContributionsExtensions } from 'vs/workbench/common/contributions';
import { StatusbarAlignment, IStatusbarService, IStatusbarEntry } from 'vs/workbench/services/statusbar/common/statusbar';
import { LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle';
import { Action } from 'vs/base/common/actions';
import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace';
import { IWorkbenchActionRegistry, Extensions as ActionExtensions } from 'vs/workbench/common/actions';
import { SyncActionDescriptor } from 'vs/platform/actions/common/actions';
import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService';
import { IApplicationLink } from 'vs/workbench/workbench.web.api';
import { IQuickInputService } from 'vs/platform/quickinput/common/quickInput';
import { IOpenerService } from 'vs/platform/opener/common/opener';
export class OpenInDesktopIndicator extends Disposable implements IWorkbenchContribution {
constructor(
@IStatusbarService private readonly statusbarService: IStatusbarService,
@IWorkspaceContextService workspaceService: IWorkspaceContextService,
@IWorkbenchEnvironmentService environmentService: IWorkbenchEnvironmentService
) {
super();
const links = environmentService.options?.applicationLinks;
if (Array.isArray(links) && links?.length > 0) {
this.installOpenInDesktopIndicator(links);
}
}
private installOpenInDesktopIndicator(links: readonly IApplicationLink[]): void {
// Register action to trigger "Open In Desktop"
const registry = Registry.as<IWorkbenchActionRegistry>(ActionExtensions.WorkbenchActions);
registry.registerWorkbenchAction(SyncActionDescriptor.create(OpenInDesktopAction, OpenInDesktopAction.ID, OpenInDesktopAction.LABEL), 'Open Workspace in Desktop');
// Show in status bar
const properties: IStatusbarEntry = {
backgroundColor: themeColorFromId(STATUS_BAR_PROMINENT_ITEM_BACKGROUND),
color: themeColorFromId(STATUS_BAR_PROMINENT_ITEM_FOREGROUND),
text: links.length === 1 ? links[0].label : localize('openInDesktop', "Open in Desktop..."),
command: 'workbench.web.openWorkspaceInDesktop'
};
this.statusbarService.addEntry(properties, 'status.openInDesktop', properties.text, StatusbarAlignment.LEFT, Number.MAX_VALUE /* first entry */);
}
}
const workbenchContributionsRegistry = Registry.as<IWorkbenchContributionsRegistry>(WorkbenchContributionsExtensions.Workbench);
workbenchContributionsRegistry.registerWorkbenchContribution(OpenInDesktopIndicator, LifecyclePhase.Starting);
export class OpenInDesktopAction extends Action {
static readonly ID = 'workbench.web.openWorkspaceInDesktop';
static readonly LABEL = localize('openWorkspaceInDesktop', "Open Workspace in Desktop");
constructor(
id: string,
label: string,
@IQuickInputService private readonly quickInputService: IQuickInputService,
@IWorkbenchEnvironmentService private readonly environmentService: IWorkbenchEnvironmentService,
@IOpenerService private readonly openerService: IOpenerService
) {
super(id, label);
}
async run(): Promise<boolean> {
const links = this.environmentService.options?.applicationLinks;
if (Array.isArray(links)) {
if (links.length === 1) {
return this.openApplicationLink(links[0]);
}
return this.runWithPicker(links);
}
return true;
}
private async runWithPicker(links: readonly IApplicationLink[]): Promise<boolean> {
// Show a picker with choices
const quickPick = this.quickInputService.createQuickPick<IApplicationLink>();
quickPick.items = links;
quickPick.placeholder = OpenInDesktopAction.LABEL;
quickPick.canSelectMany = false;
quickPick.onDidAccept(() => {
const selectedItems = quickPick.selectedItems;
if (selectedItems.length === 1) {
this.openApplicationLink(selectedItems[0]);
}
quickPick.hide();
});
quickPick.show();
return true;
}
private async openApplicationLink(link: IApplicationLink): Promise<boolean> {
this.openerService.open(link.uri, { openExternal: true });
return true;
}
}

View File

@@ -69,6 +69,8 @@ const VIEW_CONTAINER: ViewContainer = Registry.as<IViewContainersRegistry>(ViewC
Registry.as<IViewsRegistry>(ViewContainerExtensions.ViewsRegistry).registerViews([{
id: OUTPUT_VIEW_ID,
name: nls.localize('output', "Output"),
containerIcon: 'codicon-output',
canMoveView: true,
canToggleVisibility: false,
ctorDescriptor: new SyncDescriptor(OutputViewPane),
}], VIEW_CONTAINER);

View File

@@ -50,6 +50,7 @@ import { IPreferencesService, ISearchResult, ISettingsEditorModel, ISettingsEdit
import { SettingsEditor2Input } from 'vs/workbench/services/preferences/common/preferencesEditorInput';
import { Settings2EditorModel } from 'vs/workbench/services/preferences/common/preferencesModels';
import { IEditorModel } from 'vs/platform/editor/common/editor';
import { IStorageKeysSyncRegistryService } from 'vs/platform/userDataSync/common/storageKeys';
function createGroupIterator(group: SettingsTreeGroupElement): Iterator<ITreeElement<SettingsTreeGroupChild>> {
const groupsIt = Iterator.fromArray(group.children);
@@ -70,6 +71,7 @@ interface IFocusEventFromScroll extends KeyboardEvent {
fromScroll: true;
}
const SETTINGS_AUTOSAVE_NOTIFIED_KEY = 'hasNotifiedOfSettingsAutosave';
const SETTINGS_EDITOR_STATE_KEY = 'settingsEditorState';
export class SettingsEditor2 extends BaseEditor {
@@ -160,7 +162,8 @@ export class SettingsEditor2 extends BaseEditor {
@IStorageService private readonly storageService: IStorageService,
@INotificationService private readonly notificationService: INotificationService,
@IEditorGroupsService protected editorGroupService: IEditorGroupsService,
@IKeybindingService private readonly keybindingService: IKeybindingService
@IKeybindingService private readonly keybindingService: IKeybindingService,
@IStorageKeysSyncRegistryService storageKeysSyncRegistryService: IStorageKeysSyncRegistryService,
) {
super(SettingsEditor2.ID, telemetryService, themeService, storageService);
this.delayedFilterLogging = new Delayer<void>(1000);
@@ -186,6 +189,8 @@ export class SettingsEditor2 extends BaseEditor {
this.onConfigUpdate(e.affectedKeys);
}
}));
storageKeysSyncRegistryService.registerStorageKey({ key: SETTINGS_AUTOSAVE_NOTIFIED_KEY, version: 1 });
}
get minimumWidth(): number { return 375; }
@@ -712,8 +717,8 @@ export class SettingsEditor2 extends BaseEditor {
}
private notifyNoSaveNeeded() {
if (!this.storageService.getBoolean('hasNotifiedOfSettingsAutosave', StorageScope.GLOBAL, false)) {
this.storageService.store('hasNotifiedOfSettingsAutosave', true, StorageScope.GLOBAL);
if (!this.storageService.getBoolean(SETTINGS_AUTOSAVE_NOTIFIED_KEY, StorageScope.GLOBAL, false)) {
this.storageService.store(SETTINGS_AUTOSAVE_NOTIFIED_KEY, true, StorageScope.GLOBAL);
this.notificationService.info(localize('settingsNoSaveNeeded', "Your changes are automatically saved as you edit."));
}
}

View File

@@ -23,6 +23,7 @@ import { INotificationService } from 'vs/platform/notification/common/notificati
import { DefaultQuickAccessFilterValue } from 'vs/platform/quickinput/common/quickAccess';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { IWorkbenchQuickOpenConfiguration } from 'vs/workbench/browser/quickopen';
import { stripCodicons } from 'vs/base/common/codicons';
export class CommandsQuickAccessProvider extends AbstractEditorCommandsQuickAccessProvider {
@@ -114,7 +115,7 @@ export class CommandsQuickAccessProvider extends AbstractEditorCommandsQuickAcce
globalCommandPicks.push({
commandId: action.item.id,
commandAlias,
label
label: stripCodicons(label)
});
}

View File

@@ -4,8 +4,8 @@
*--------------------------------------------------------------------------------------------*/
import 'vs/css!./media/anythingQuickAccess';
import { IQuickPickSeparator, IQuickInputButton, IKeyMods, quickPickItemScorerAccessor, QuickPickItemScorerAccessor, IQuickPick } from 'vs/platform/quickinput/common/quickInput';
import { IPickerQuickAccessItem, PickerQuickAccessProvider, TriggerAction, FastAndSlowPicksType } from 'vs/platform/quickinput/browser/pickerQuickAccess';
import { IQuickInputButton, IKeyMods, quickPickItemScorerAccessor, QuickPickItemScorerAccessor, IQuickPick } from 'vs/platform/quickinput/common/quickInput';
import { IPickerQuickAccessItem, PickerQuickAccessProvider, TriggerAction, FastAndSlowPicks, Picks, PicksWithActive } from 'vs/platform/quickinput/browser/pickerQuickAccess';
import { prepareQuery, IPreparedQuery, compareItemsByScore, scoreItem, ScorerCache } from 'vs/base/common/fuzzyScorer';
import { IFileQueryBuilderOptions, QueryBuilder } from 'vs/workbench/contrib/search/common/queryBuilder';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
@@ -88,7 +88,8 @@ export class AnythingQuickAccessProvider extends PickerQuickAccessProvider<IAnyt
lastOriginalFilter: string | undefined = undefined;
lastFilter: string | undefined = undefined;
lastRange: IRange | undefined = undefined;
lastActiveGlobalPick: IAnythingQuickPickItem | undefined = undefined;
lastGlobalPicks: PicksWithActive<IAnythingQuickPickItem> | undefined = undefined;
isQuickNavigating: boolean | undefined = undefined;
@@ -116,7 +117,7 @@ export class AnythingQuickAccessProvider extends PickerQuickAccessProvider<IAnyt
this.lastOriginalFilter = undefined;
this.lastFilter = undefined;
this.lastRange = undefined;
this.lastActiveGlobalPick = undefined;
this.lastGlobalPicks = undefined;
this.editorViewState = undefined;
}
@@ -240,7 +241,7 @@ export class AnythingQuickAccessProvider extends PickerQuickAccessProvider<IAnyt
return toDisposable(() => this.clearDecorations(activeEditorControl));
}
protected getPicks(originalFilter: string, disposables: DisposableStore, token: CancellationToken): Promise<Array<IAnythingQuickPickItem | IQuickPickSeparator>> | FastAndSlowPicksType<IAnythingQuickPickItem> | null {
protected getPicks(originalFilter: string, disposables: DisposableStore, token: CancellationToken): Picks<IAnythingQuickPickItem> | Promise<Picks<IAnythingQuickPickItem>> | FastAndSlowPicks<IAnythingQuickPickItem> | null {
// Find a suitable range from the pattern looking for ":", "#" or ","
// unless we have the `@` editor symbol character inside the filter
@@ -269,16 +270,25 @@ export class AnythingQuickAccessProvider extends PickerQuickAccessProvider<IAnyt
this.pickState.lastOriginalFilter = originalFilter;
this.pickState.lastFilter = filter;
// Remember last active pick (unless editor symbol)
// Remember our pick state before returning new picks
// unless an editor symbol is selected. We can use this
// state to return back to the global pick when the
// user is narrowing back out of editor symbols.
const picks = this.pickState.picker?.items;
const activePick = this.pickState.picker?.activeItems[0];
if (activePick && !isEditorSymbolQuickPickItem(activePick)) {
this.pickState.lastActiveGlobalPick = activePick;
if (picks && activePick) {
if (!isEditorSymbolQuickPickItem(activePick)) {
this.pickState.lastGlobalPicks = {
items: picks,
active: activePick
};
}
}
return this.doGetPicks(filter, disposables, token);
}
private doGetPicks(filter: string, disposables: DisposableStore, token: CancellationToken): Promise<Array<IAnythingQuickPickItem | IQuickPickSeparator>> | FastAndSlowPicksType<IAnythingQuickPickItem> | null {
private doGetPicks(filter: string, disposables: DisposableStore, token: CancellationToken): Picks<IAnythingQuickPickItem> | Promise<Picks<IAnythingQuickPickItem>> | FastAndSlowPicks<IAnythingQuickPickItem> | null {
const query = prepareQuery(filter);
// Return early if we have editor symbol picks. We support this by:
@@ -289,6 +299,14 @@ export class AnythingQuickAccessProvider extends PickerQuickAccessProvider<IAnyt
return editorSymbolPicks;
}
// If we have a known last active editor symbol pick, we try to restore
// the last global pick to support the case of narrowing out from a
// editor symbol search back into the global search
const activePick = this.pickState.picker?.activeItems[0];
if (isEditorSymbolQuickPickItem(activePick) && this.pickState.lastGlobalPicks) {
return this.pickState.lastGlobalPicks;
}
// Otherwise return normally with history and file/symbol results
const historyEditorPicks = this.getEditorHistoryPicks(query);
@@ -304,7 +322,7 @@ export class AnythingQuickAccessProvider extends PickerQuickAccessProvider<IAnyt
],
// Slow picks: files and symbols
additionalPicks: (async (): Promise<Array<IAnythingQuickPickItem | IQuickPickSeparator>> => {
additionalPicks: (async (): Promise<Picks<IAnythingQuickPickItem>> => {
// Exclude any result that is already present in editor history
const additionalPicksExcludes = new ResourceMap<boolean>();
@@ -636,13 +654,13 @@ export class AnythingQuickAccessProvider extends PickerQuickAccessProvider<IAnyt
private readonly editorSymbolsQuickAccess = this.instantiationService.createInstance(GotoSymbolQuickAccessProvider);
private getEditorSymbolPicks(query: IPreparedQuery, disposables: DisposableStore, token: CancellationToken): Promise<Array<IAnythingQuickPickItem | IQuickPickSeparator>> | null {
private getEditorSymbolPicks(query: IPreparedQuery, disposables: DisposableStore, token: CancellationToken): Promise<Picks<IAnythingQuickPickItem>> | null {
const filter = query.original.split(GotoSymbolQuickAccessProvider.PREFIX)[1]?.trim();
if (typeof filter !== 'string') {
return null; // we need to be searched for editor symbols via `@`
}
const activeGlobalPick = this.pickState.lastActiveGlobalPick;
const activeGlobalPick = this.pickState.lastGlobalPicks?.active;
if (!activeGlobalPick) {
return null; // we need an active global pick to find symbols for
}
@@ -655,7 +673,7 @@ export class AnythingQuickAccessProvider extends PickerQuickAccessProvider<IAnyt
return this.doGetEditorSymbolPicks(activeGlobalPick, activeGlobalResource, filter, disposables, token);
}
private async doGetEditorSymbolPicks(activeGlobalPick: IAnythingQuickPickItem, activeGlobalResource: URI, filter: string, disposables: DisposableStore, token: CancellationToken): Promise<Array<IAnythingQuickPickItem | IQuickPickSeparator>> {
private async doGetEditorSymbolPicks(activeGlobalPick: IAnythingQuickPickItem, activeGlobalResource: URI, filter: string, disposables: DisposableStore, token: CancellationToken): Promise<Picks<IAnythingQuickPickItem>> {
// Bring the editor to front to review symbols to go to
try {

View File

@@ -390,6 +390,7 @@ Registry.as<panel.PanelRegistry>(panel.Extensions.Panels).setDefaultPanelId(TERM
Registry.as<IViewsRegistry>(ViewContainerExtensions.ViewsRegistry).registerViews([{
id: TERMINAL_VIEW_ID,
name: nls.localize('terminal', "Terminal"),
containerIcon: 'codicon-terminal',
canToggleVisibility: false,
canMoveView: true,
ctorDescriptor: new SyncDescriptor(TerminalViewPane)
@@ -459,10 +460,7 @@ actionRegistry.registerWorkbenchAction(SyncActionDescriptor.create(ManageWorkspa
actionRegistry.registerWorkbenchAction(SyncActionDescriptor.create(RenameTerminalAction, RenameTerminalAction.ID, RenameTerminalAction.LABEL), 'Terminal: Rename', category);
actionRegistry.registerWorkbenchAction(SyncActionDescriptor.create(FocusTerminalFindWidgetAction, FocusTerminalFindWidgetAction.ID, FocusTerminalFindWidgetAction.LABEL, {
primary: KeyMod.CtrlCmd | KeyCode.KEY_F
}, KEYBINDING_CONTEXT_TERMINAL_FOCUS), 'Terminal: Focus Find Widget', category);
actionRegistry.registerWorkbenchAction(SyncActionDescriptor.create(FocusTerminalFindWidgetAction, FocusTerminalFindWidgetAction.ID, FocusTerminalFindWidgetAction.LABEL, {
primary: KeyMod.CtrlCmd | KeyCode.KEY_F
}, KEYBINDING_CONTEXT_TERMINAL_FIND_WIDGET_FOCUSED), 'Terminal: Focus Find Widget', category);
}, ContextKeyExpr.or(KEYBINDING_CONTEXT_TERMINAL_FIND_WIDGET_FOCUSED, KEYBINDING_CONTEXT_TERMINAL_FOCUS)), 'Terminal: Focus Find Widget', category);
actionRegistry.registerWorkbenchAction(SyncActionDescriptor.create(HideTerminalFindWidgetAction, HideTerminalFindWidgetAction.ID, HideTerminalFindWidgetAction.LABEL, {
primary: KeyCode.Escape,
secondary: [KeyMod.Shift | KeyCode.Escape]
@@ -550,43 +548,25 @@ actionRegistry.registerWorkbenchAction(SyncActionDescriptor.create(NavigationMod
}, ContextKeyExpr.and(KEYBINDING_CONTEXT_TERMINAL_A11Y_TREE_FOCUS, CONTEXT_ACCESSIBILITY_MODE_ENABLED)), 'Terminal: Exit Navigation Mode', category);
actionRegistry.registerWorkbenchAction(SyncActionDescriptor.create(NavigationModeFocusPreviousTerminalAction, NavigationModeFocusPreviousTerminalAction.ID, NavigationModeFocusPreviousTerminalAction.LABEL, {
primary: KeyMod.CtrlCmd | KeyCode.UpArrow
}, ContextKeyExpr.and(KEYBINDING_CONTEXT_TERMINAL_FOCUS, CONTEXT_ACCESSIBILITY_MODE_ENABLED)), 'Terminal: Focus Previous Line (Navigation Mode)', category);
actionRegistry.registerWorkbenchAction(SyncActionDescriptor.create(NavigationModeFocusPreviousTerminalAction, NavigationModeFocusPreviousTerminalAction.ID, NavigationModeFocusPreviousTerminalAction.LABEL, {
primary: KeyMod.CtrlCmd | KeyCode.UpArrow
}, ContextKeyExpr.and(KEYBINDING_CONTEXT_TERMINAL_A11Y_TREE_FOCUS, CONTEXT_ACCESSIBILITY_MODE_ENABLED)), 'Terminal: Focus Previous Line (Navigation Mode)', category);
}, ContextKeyExpr.or(ContextKeyExpr.and(KEYBINDING_CONTEXT_TERMINAL_A11Y_TREE_FOCUS, CONTEXT_ACCESSIBILITY_MODE_ENABLED), ContextKeyExpr.and(KEYBINDING_CONTEXT_TERMINAL_FOCUS, CONTEXT_ACCESSIBILITY_MODE_ENABLED))), 'Terminal: Focus Previous Line (Navigation Mode)', category);
actionRegistry.registerWorkbenchAction(SyncActionDescriptor.create(NavigationModeFocusNextTerminalAction, NavigationModeFocusNextTerminalAction.ID, NavigationModeFocusNextTerminalAction.LABEL, {
primary: KeyMod.CtrlCmd | KeyCode.DownArrow
}, ContextKeyExpr.and(KEYBINDING_CONTEXT_TERMINAL_FOCUS, CONTEXT_ACCESSIBILITY_MODE_ENABLED)), 'Terminal: Focus Next Line (Navigation Mode)', category);
actionRegistry.registerWorkbenchAction(SyncActionDescriptor.create(NavigationModeFocusNextTerminalAction, NavigationModeFocusNextTerminalAction.ID, NavigationModeFocusNextTerminalAction.LABEL, {
primary: KeyMod.CtrlCmd | KeyCode.DownArrow
}, ContextKeyExpr.and(KEYBINDING_CONTEXT_TERMINAL_A11Y_TREE_FOCUS, CONTEXT_ACCESSIBILITY_MODE_ENABLED)), 'Terminal: Focus Next Line (Navigation Mode)', category);
}, ContextKeyExpr.or(ContextKeyExpr.and(KEYBINDING_CONTEXT_TERMINAL_A11Y_TREE_FOCUS, CONTEXT_ACCESSIBILITY_MODE_ENABLED), ContextKeyExpr.and(KEYBINDING_CONTEXT_TERMINAL_FOCUS, CONTEXT_ACCESSIBILITY_MODE_ENABLED))), 'Terminal: Focus Next Line (Navigation Mode)', category);
actionRegistry.registerWorkbenchAction(SyncActionDescriptor.create(SelectToPreviousLineAction, SelectToPreviousLineAction.ID, SelectToPreviousLineAction.LABEL), 'Terminal: Select To Previous Line', category);
actionRegistry.registerWorkbenchAction(SyncActionDescriptor.create(SelectToNextLineAction, SelectToNextLineAction.ID, SelectToNextLineAction.LABEL), 'Terminal: Select To Next Line', category);
actionRegistry.registerWorkbenchAction(SyncActionDescriptor.create(ToggleEscapeSequenceLoggingAction, ToggleEscapeSequenceLoggingAction.ID, ToggleEscapeSequenceLoggingAction.LABEL), 'Terminal: Toggle Escape Sequence Logging', category);
actionRegistry.registerWorkbenchAction(SyncActionDescriptor.create(ToggleRegexCommand, ToggleRegexCommand.ID, ToggleRegexCommand.LABEL, {
primary: KeyMod.Alt | KeyCode.KEY_R,
mac: { primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyCode.KEY_R }
}, KEYBINDING_CONTEXT_TERMINAL_FIND_WIDGET_FOCUSED), 'Terminal: Toggle find using regex', category);
actionRegistry.registerWorkbenchAction(SyncActionDescriptor.create(ToggleRegexCommand, ToggleRegexCommand.ID, ToggleRegexCommand.LABEL, {
primary: KeyMod.Alt | KeyCode.KEY_R,
mac: { primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyCode.KEY_R }
}, KEYBINDING_CONTEXT_TERMINAL_FOCUS), 'Terminal: Toggle find using regex', category);
}, ContextKeyExpr.or(KEYBINDING_CONTEXT_TERMINAL_FOCUS, KEYBINDING_CONTEXT_TERMINAL_FIND_WIDGET_FOCUSED)), 'Terminal: Toggle find using regex', category);
actionRegistry.registerWorkbenchAction(SyncActionDescriptor.create(ToggleWholeWordCommand, ToggleWholeWordCommand.ID, ToggleWholeWordCommand.LABEL, {
primary: KeyMod.Alt | KeyCode.KEY_W,
mac: { primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyCode.KEY_W }
}, KEYBINDING_CONTEXT_TERMINAL_FIND_WIDGET_FOCUSED), 'Terminal: Toggle find using whole word', category);
actionRegistry.registerWorkbenchAction(SyncActionDescriptor.create(ToggleWholeWordCommand, ToggleWholeWordCommand.ID, ToggleWholeWordCommand.LABEL, {
primary: KeyMod.Alt | KeyCode.KEY_W,
mac: { primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyCode.KEY_W }
}, KEYBINDING_CONTEXT_TERMINAL_FOCUS), 'Terminal: Toggle find using whole word', category);
}, ContextKeyExpr.or(KEYBINDING_CONTEXT_TERMINAL_FOCUS, KEYBINDING_CONTEXT_TERMINAL_FIND_WIDGET_FOCUSED)), 'Terminal: Toggle find using whole word', category);
actionRegistry.registerWorkbenchAction(SyncActionDescriptor.create(ToggleCaseSensitiveCommand, ToggleCaseSensitiveCommand.ID, ToggleCaseSensitiveCommand.LABEL, {
primary: KeyMod.Alt | KeyCode.KEY_C,
mac: { primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyCode.KEY_C }
}, KEYBINDING_CONTEXT_TERMINAL_FIND_WIDGET_FOCUSED), 'Terminal: Toggle find using case sensitive', category);
actionRegistry.registerWorkbenchAction(SyncActionDescriptor.create(ToggleCaseSensitiveCommand, ToggleCaseSensitiveCommand.ID, ToggleCaseSensitiveCommand.LABEL, {
primary: KeyMod.Alt | KeyCode.KEY_C,
mac: { primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyCode.KEY_C }
}, KEYBINDING_CONTEXT_TERMINAL_FOCUS), 'Terminal: Toggle find using case sensitive', category);
}, ContextKeyExpr.or(KEYBINDING_CONTEXT_TERMINAL_FOCUS, KEYBINDING_CONTEXT_TERMINAL_FIND_WIDGET_FOCUSED)), 'Terminal: Toggle find using case sensitive', category);
actionRegistry.registerWorkbenchAction(SyncActionDescriptor.create(FindNext, FindNext.ID, FindNext.LABEL, {
primary: KeyCode.F3,
mac: { primary: KeyMod.CtrlCmd | KeyCode.KEY_G, secondary: [KeyCode.F3] }

View File

@@ -776,12 +776,14 @@ export class TimelinePane extends ViewPane {
// Refresh the view on focus to update the relative timestamps
this.onDidFocus(() => this.refreshDebounced(), this, this.visibilityDisposables);
super.setVisible(visible);
this.onActiveEditorChanged();
} else {
this.visibilityDisposables?.dispose();
}
super.setVisible(visible);
super.setVisible(visible);
}
}
protected layoutBody(height: number, width: number): void {

View File

@@ -53,6 +53,7 @@ import { IProductService } from 'vs/platform/product/common/productService';
import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage';
import { IOpenerService } from 'vs/platform/opener/common/opener';
import { timeout } from 'vs/base/common/async';
import { distinct } from 'vs/base/common/arrays';
const enum AuthStatus {
Initializing = 'Initializing',
@@ -98,19 +99,19 @@ const getActivityTitle = (label: string, userDataSyncService: IUserDataSyncServi
const getIdentityTitle = (label: string, authenticationProviderId: string, account: AuthenticationSession | undefined, authenticationService: IAuthenticationService): string => {
return account ? `${label} (${authenticationService.getDisplayName(authenticationProviderId)}:${account.accountName})` : label;
};
const turnOnSyncCommand = { id: 'workbench.userData.actions.syncStart', title: localize('turn on sync with category', "Sync: Turn on Sync") };
const signInCommand = { id: 'workbench.userData.actions.signin', title: localize('sign in', "Sync: Sign in to sync") };
const stopSyncCommand = { id: 'workbench.userData.actions.stopSync', title(authenticationProviderId: string, account: AuthenticationSession | undefined, authenticationService: IAuthenticationService) { return getIdentityTitle(localize('stop sync', "Sync: Turn off Sync"), authenticationProviderId, account, authenticationService); } };
const resolveSettingsConflictsCommand = { id: 'workbench.userData.actions.resolveSettingsConflicts', title: localize('showConflicts', "Sync: Show Settings Conflicts") };
const resolveKeybindingsConflictsCommand = { id: 'workbench.userData.actions.resolveKeybindingsConflicts', title: localize('showKeybindingsConflicts', "Sync: Show Keybindings Conflicts") };
const resolveSnippetsConflictsCommand = { id: 'workbench.userData.actions.resolveSnippetsConflicts', title: localize('showSnippetsConflicts', "Sync: Show User Snippets Conflicts") };
const configureSyncCommand = { id: 'workbench.userData.actions.configureSync', title: localize('configure sync', "Sync: Configure") };
const turnOnSyncCommand = { id: 'workbench.userData.actions.syncStart', title: localize('turn on sync with category', "Preferences Sync: Turn on...") };
const signInCommand = { id: 'workbench.userData.actions.signin', title: localize('sign in', "Preferences Sync: Sign in to sync") };
const stopSyncCommand = { id: 'workbench.userData.actions.stopSync', title(authenticationProviderId: string, account: AuthenticationSession | undefined, authenticationService: IAuthenticationService) { return getIdentityTitle(localize('stop sync', "Preferences Sync: Turn off"), authenticationProviderId, account, authenticationService); } };
const resolveSettingsConflictsCommand = { id: 'workbench.userData.actions.resolveSettingsConflicts', title: localize('showConflicts', "Preferences Sync: Show Settings Conflicts") };
const resolveKeybindingsConflictsCommand = { id: 'workbench.userData.actions.resolveKeybindingsConflicts', title: localize('showKeybindingsConflicts', "Preferences Sync: Show Keybindings Conflicts") };
const resolveSnippetsConflictsCommand = { id: 'workbench.userData.actions.resolveSnippetsConflicts', title: localize('showSnippetsConflicts', "Preferences Sync: Show User Snippets Conflicts") };
const configureSyncCommand = { id: 'workbench.userData.actions.configureSync', title: localize('configure sync', "Preferences Sync: Configure...") };
const showSyncActivityCommand = {
id: 'workbench.userData.actions.showSyncActivity', title(userDataSyncService: IUserDataSyncService): string {
return getActivityTitle(localize('show sync log', "Sync: Show Log"), userDataSyncService);
return getActivityTitle(localize('show sync log', "Preferences Sync: Show Log"), userDataSyncService);
}
};
const showSyncSettingsCommand = { id: 'workbench.userData.actions.syncSettings', title: localize('sync settings', "Sync: Settings"), };
const showSyncSettingsCommand = { id: 'workbench.userData.actions.syncSettings', title: localize('sync settings', "Preferences Sync: Show Settings"), };
export class UserDataSyncWorkbenchContribution extends Disposable implements IWorkbenchContribution {
@@ -249,9 +250,10 @@ export class UserDataSyncWorkbenchContribution extends Disposable implements IWo
private async showSwitchAccountPicker(sessions: readonly AuthenticationSession[]): Promise<void> {
return new Promise((resolve, _) => {
const quickPick = this.quickInputService.createQuickPick<{ label: string, session: AuthenticationSession }>();
quickPick.title = localize('chooseAccountTitle', "Sync: Choose Account");
quickPick.title = localize('chooseAccountTitle', "Preferences Sync: Choose Account");
quickPick.placeholder = localize('chooseAccount', "Choose an account you would like to use for settings sync");
quickPick.items = sessions.map(session => {
const dedupedSessions = distinct(sessions, (session) => session.accountName);
quickPick.items = dedupedSessions.map(session => {
return {
label: session.accountName,
session: session
@@ -577,7 +579,7 @@ export class UserDataSyncWorkbenchContribution extends Disposable implements IWo
if (this.userDataSyncService.status !== SyncStatus.Uninitialized && this.userDataSyncEnablementService.isEnabled() && this.authenticationState.get() === AuthStatus.SignedOut) {
badge = new NumberBadge(1, () => localize('sign in to sync', "Sign in to Sync"));
} else if (this.userDataSyncService.conflicts.length) {
badge = new NumberBadge(this.userDataSyncService.conflicts.reduce((result, syncResourceConflict) => { return result + syncResourceConflict.conflicts.length; }, 0), () => localize('has conflicts', "Sync: Conflicts Detected"));
badge = new NumberBadge(this.userDataSyncService.conflicts.reduce((result, syncResourceConflict) => { return result + syncResourceConflict.conflicts.length; }, 0), () => localize('has conflicts', "Preferences Sync: Conflicts Detected"));
}
if (badge) {
@@ -608,7 +610,7 @@ export class UserDataSyncWorkbenchContribution extends Disposable implements IWo
const disposables: DisposableStore = new DisposableStore();
const quickPick = this.quickInputService.createQuickPick<ConfigureSyncQuickPickItem>();
disposables.add(quickPick);
quickPick.title = localize('turn on title', "Sync: Turn On");
quickPick.title = localize('turn on title', "Preferences Sync: Turn On");
quickPick.ok = false;
quickPick.customButton = true;
if (this.authenticationState.get() === AuthStatus.SignedIn) {
@@ -713,7 +715,7 @@ export class UserDataSyncWorkbenchContribution extends Disposable implements IWo
const disposables: DisposableStore = new DisposableStore();
const quickPick = this.quickInputService.createQuickPick<ConfigureSyncQuickPickItem>();
disposables.add(quickPick);
quickPick.title = localize('turn on sync', "Turn on Sync");
quickPick.title = localize('configure sync', "Preferences Sync: Configure...");
quickPick.placeholder = localize('configure sync placeholder', "Choose what to sync");
quickPick.canSelectMany = true;
quickPick.ignoreFocusOut = true;
@@ -888,7 +890,7 @@ export class UserDataSyncWorkbenchContribution extends Disposable implements IWo
group: '5_sync',
command: {
id: turnOnSyncCommand.id,
title: localize('global activity turn on sync', "Turn on Sync...")
title: localize('global activity turn on sync', "Preferences Sync: Turn on...")
},
when: turnOnSyncWhenContext,
order: 1
@@ -901,7 +903,7 @@ export class UserDataSyncWorkbenchContribution extends Disposable implements IWo
group: '5_sync',
command: {
id: turnOnSyncCommand.id,
title: localize('global activity turn on sync', "Turn on Sync...")
title: localize('global activity turn on sync', "Preferences Sync: Turn on...")
},
when: turnOnSyncWhenContext,
});
@@ -913,7 +915,7 @@ export class UserDataSyncWorkbenchContribution extends Disposable implements IWo
constructor() {
super({
id: signInCommand.id,
title: localize('sign in 2', "Sync: Sign in to sync (1)"),
title: localize('sign in 2', "Preferences Sync: Sign in to sync (1)"),
menu: {
group: '5_sync',
id: MenuId.GlobalActivity,
@@ -939,7 +941,7 @@ export class UserDataSyncWorkbenchContribution extends Disposable implements IWo
group: '5_sync',
command: {
id: resolveSettingsConflictsCommand.id,
title: localize('resolveConflicts_global', "Sync: Show Settings Conflicts (1)"),
title: localize('resolveConflicts_global', "Preferences Sync: Show Settings Conflicts (1)"),
},
when: resolveSettingsConflictsWhenContext,
order: 2
@@ -948,7 +950,7 @@ export class UserDataSyncWorkbenchContribution extends Disposable implements IWo
group: '5_sync',
command: {
id: resolveSettingsConflictsCommand.id,
title: localize('resolveConflicts_global', "Sync: Show Settings Conflicts (1)"),
title: localize('resolveConflicts_global', "Preferences Sync: Show Settings Conflicts (1)"),
},
when: resolveSettingsConflictsWhenContext,
order: 2
@@ -966,7 +968,7 @@ export class UserDataSyncWorkbenchContribution extends Disposable implements IWo
group: '5_sync',
command: {
id: resolveKeybindingsConflictsCommand.id,
title: localize('resolveKeybindingsConflicts_global', "Sync: Show Keybindings Conflicts (1)"),
title: localize('resolveKeybindingsConflicts_global', "Preferences Sync: Show Keybindings Conflicts (1)"),
},
when: resolveKeybindingsConflictsWhenContext,
order: 2
@@ -975,7 +977,7 @@ export class UserDataSyncWorkbenchContribution extends Disposable implements IWo
group: '5_sync',
command: {
id: resolveKeybindingsConflictsCommand.id,
title: localize('resolveKeybindingsConflicts_global', "Sync: Show Keybindings Conflicts (1)"),
title: localize('resolveKeybindingsConflicts_global', "Preferences Sync: Show Keybindings Conflicts (1)"),
},
when: resolveKeybindingsConflictsWhenContext,
order: 2
@@ -996,7 +998,7 @@ export class UserDataSyncWorkbenchContribution extends Disposable implements IWo
group: '5_sync',
command: {
id: resolveSnippetsConflictsCommand.id,
title: localize('resolveSnippetsConflicts_global', "Sync: Show User Snippets Conflicts ({0})", conflicts?.length || 1),
title: localize('resolveSnippetsConflicts_global', "Preferences Sync: Show User Snippets Conflicts ({0})", conflicts?.length || 1),
},
when: resolveSnippetsConflictsWhenContext,
order: 2
@@ -1005,7 +1007,7 @@ export class UserDataSyncWorkbenchContribution extends Disposable implements IWo
group: '5_sync',
command: {
id: resolveSnippetsConflictsCommand.id,
title: localize('resolveSnippetsConflicts_global', "Sync: Show User Snippets Conflicts ({0})", conflicts?.length || 1),
title: localize('resolveSnippetsConflicts_global', "Preferences Sync: Show User Snippets Conflicts ({0})", conflicts?.length || 1),
},
when: resolveSnippetsConflictsWhenContext,
order: 2
@@ -1023,7 +1025,7 @@ export class UserDataSyncWorkbenchContribution extends Disposable implements IWo
constructor() {
super({
id: 'workbench.userData.actions.syncStatus',
title: localize('sync is on', "Sync is on"),
title: localize('sync is on', "Preferences Sync: On"),
menu: [
{
id: MenuId.GlobalActivity,
@@ -1262,8 +1264,8 @@ class AcceptChangesContribution extends Disposable implements IEditorContributio
const result = await this.dialogService.confirm({
type: 'info',
title: isRemote
? localize('Sync accept remote', "Sync: {0}", acceptRemoteLabel)
: localize('Sync accept local', "Sync: {0}", acceptLocalLabel),
? localize('Sync accept remote', "Preferences Sync: {0}", acceptRemoteLabel)
: localize('Sync accept local', "Preferences Sync: {0}", acceptLocalLabel),
message: isRemote
? localize('confirm replace and overwrite local', "Would you like to accept remote {0} and replace local {1}?", syncAreaLabel.toLowerCase(), syncAreaLabel.toLowerCase())
: localize('confirm replace and overwrite remote', "Would you like to accept local {0} and replace remote {1}?", syncAreaLabel.toLowerCase(), syncAreaLabel.toLowerCase()),

View File

@@ -36,7 +36,7 @@ export class UserDataSyncViewContribution implements IWorkbenchContribution {
return Registry.as<IViewContainersRegistry>(Extensions.ViewContainersRegistry).registerViewContainer(
{
id: 'workbench.view.sync',
name: localize('sync', "Sync"),
name: localize('sync preferences', "Preferences Sync"),
ctorDescriptor: new SyncDescriptor(
ViewPaneContainer,
['workbench.view.sync', `workbench.view.sync.state`, { mergeViewWithContainerWhenSingleView: true }]
@@ -80,7 +80,7 @@ export class UserDataSyncViewContribution implements IWorkbenchContribution {
title: remote ?
{ value: localize('workbench.action.showSyncRemoteBackup', "Show Remote Backup"), original: `Show Remote Backup` }
: { value: localize('workbench.action.showSyncLocalBackup', "Show Local Backup"), original: `Show Local Backup` },
category: { value: localize('sync', "Sync"), original: `Sync` },
category: { value: localize('sync preferences', "Preferences Sync"), original: `Preferences Sync` },
menu: {
id: MenuId.CommandPalette,
when: CONTEXT_SYNC_ENABLEMENT

View File

@@ -33,7 +33,8 @@ registerAction2(class OpenSyncBackupsFolder extends Action2 {
constructor() {
super({
id: 'workbench.userData.actions.openSyncBackupsFolder',
title: localize('Open Backup folder', "Sync: Open Local Backups Folder"),
title: { value: localize('Open Backup folder', "Open Local Backups Folder"), original: 'Open Local Backups Folder' },
category: { value: localize('sync preferences', "Preferences Sync"), original: `Preferences Sync` },
menu: {
id: MenuId.CommandPalette,
when: CONTEXT_SYNC_STATE.notEqualsTo(SyncStatus.Uninitialized),

View File

@@ -233,11 +233,10 @@ export class ElectronWebviewBasedWebview extends BaseWebview<WebviewTag> impleme
const webviewAndContents = this._register(new WebviewTagHandle(this.element!));
const session = this._register(new WebviewSession(webviewAndContents));
this._protocolProvider = new WebviewProtocolProvider
(webviewAndContents,
() => this.extension ? this.extension.location : undefined,
() => (this.content.options.localResourceRoots || []),
fileService);
this._protocolProvider = new WebviewProtocolProvider(webviewAndContents,
() => this.extension?.location,
() => (this.content.options.localResourceRoots || []),
fileService);
this._register(this._protocolProvider);
this._register(new WebviewPortMappingProvider(

View File

@@ -8,7 +8,7 @@ import { localize } from 'vs/nls';
export default () => `
<div class="welcomePageContainer">
<div class="welcomePage">
<div class="welcomePage" role="document">
<div class="title">
<h1 class="caption">${escape(localize('welcomePage.vscode', "Visual Studio Code"))}</h1>
<p class="subtitle detail">${escape(localize({ key: 'welcomePage.editingEvolved', comment: ['Shown as subtitle on the Welcome page.'] }, "Editing evolved"))}</p>

View File

@@ -88,7 +88,7 @@ export class AuthenticationService extends Disposable implements IAuthentication
this._onDidChangeSessions.fire({ providerId: id, event: event });
const provider = this._authenticationProviders.get(id);
if (provider) {
provider.updateSessionItems();
provider.updateSessionItems(event);
}
}

View File

@@ -20,6 +20,7 @@ import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/
import { VSBuffer } from 'vs/base/common/buffer';
import { TextSnapshotReadable, stringToSnapshot } from 'vs/workbench/services/textfile/common/textfiles';
import { Disposable } from 'vs/base/common/lifecycle';
import { ILogService } from 'vs/platform/log/common/log';
export interface IBackupFilesModel {
resolve(backupRoot: URI): Promise<IBackupFilesModel>;
@@ -114,7 +115,8 @@ export class BackupFileService implements IBackupFileService {
constructor(
@IWorkbenchEnvironmentService private environmentService: IWorkbenchEnvironmentService,
@IFileService protected fileService: IFileService
@IFileService protected fileService: IFileService,
@ILogService private readonly logService: ILogService
) {
this.impl = this.initialize();
}
@@ -128,7 +130,7 @@ export class BackupFileService implements IBackupFileService {
private initialize(): BackupFileServiceImpl | InMemoryBackupFileService {
const backupWorkspaceResource = this.environmentService.configuration.backupWorkspaceResource;
if (backupWorkspaceResource) {
return new BackupFileServiceImpl(backupWorkspaceResource, this.hashPath, this.fileService);
return new BackupFileServiceImpl(backupWorkspaceResource, this.hashPath, this.fileService, this.logService);
}
return new InMemoryBackupFileService(this.hashPath);
@@ -194,7 +196,8 @@ class BackupFileServiceImpl extends Disposable implements IBackupFileService {
constructor(
backupWorkspaceResource: URI,
private readonly hashPath: (resource: URI) => string,
@IFileService private readonly fileService: IFileService
@IFileService private readonly fileService: IFileService,
@ILogService private readonly logService: ILogService
) {
super();
@@ -372,7 +375,9 @@ class BackupFileServiceImpl extends Disposable implements IBackupFileService {
// the meta-end marker ('\n') and as such the backup can only be invalid. We bail out
// here if that is the case.
if (!metaEndFound) {
throw new Error(`Backup: Could not find meta end marker in ${backupResource}. The file is probably corrupt.`);
this.logService.error(`Backup: Could not find meta end marker in ${backupResource}. The file is probably corrupt.`);
return undefined;
}
return { value: factory, meta };

View File

@@ -62,12 +62,13 @@ export class NodeTestBackupFileService extends BackupFileService {
constructor(workspaceBackupPath: string) {
const environmentService = new TestBackupEnvironmentService(workspaceBackupPath);
const fileService = new FileService(new NullLogService());
const diskFileSystemProvider = new DiskFileSystemProvider(new NullLogService());
const logService = new NullLogService();
const fileService = new FileService(logService);
const diskFileSystemProvider = new DiskFileSystemProvider(logService);
fileService.registerProvider(Schemas.file, diskFileSystemProvider);
fileService.registerProvider(Schemas.userData, new FileUserDataProvider(environmentService.appSettingsHome, environmentService.backupHome, diskFileSystemProvider, environmentService));
super(environmentService, fileService);
super(environmentService, fileService, logService);
this.fileService = fileService;
this.backupResourceJoiners = [];
@@ -99,6 +100,14 @@ export class NodeTestBackupFileService extends BackupFileService {
this.discardBackupJoiners.pop()!();
}
}
async getBackupContents(resource: URI): Promise<string> {
const backupResource = this.toBackupResource(resource);
const fileContents = await this.fileService.readFile(backupResource);
return fileContents.value.toString();
}
}
suite('BackupFileService', () => {
@@ -473,7 +482,7 @@ suite('BackupFileService', () => {
await testResolveBackup(fooBarFile, contents, meta, null);
});
test('should throw an error when restoring invalid backup', async () => {
test('should ignore invalid backups', async () => {
const contents = 'test\nand more stuff';
await service.backup(fooBarFile, createTextBufferFactory(contents).create(DefaultEndOfLine.LF).createSnapshot(false), 1);
@@ -485,14 +494,14 @@ suite('BackupFileService', () => {
await service.fileService.writeFile(service.toBackupResource(fooBarFile), VSBuffer.fromString(''));
let err: Error;
let err: Error | undefined = undefined;
try {
await service.resolve<IBackupTestMetaData>(fooBarFile);
} catch (error) {
err = error;
}
assert.ok(err!);
assert.ok(!err);
});
async function testResolveBackup(resource: URI, contents: string, meta?: IBackupTestMetaData, expectedMeta?: IBackupTestMetaData | null) {

View File

@@ -11,6 +11,7 @@ import { Event } from 'vs/base/common/event';
import { registerSingleton } from 'vs/platform/instantiation/common/extensions';
import { IAction, Action } from 'vs/base/common/actions';
import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage';
import { IStorageKeysSyncRegistryService } from 'vs/platform/userDataSync/common/storageKeys';
export class NotificationService extends Disposable implements INotificationService {
@@ -19,7 +20,10 @@ export class NotificationService extends Disposable implements INotificationServ
private _model: INotificationsModel = this._register(new NotificationsModel());
get model(): INotificationsModel { return this._model; }
constructor(@IStorageService private readonly storageService: IStorageService) {
constructor(
@IStorageService private readonly storageService: IStorageService,
@IStorageKeysSyncRegistryService private readonly storageKeysSyncRegistryService: IStorageKeysSyncRegistryService
) {
super();
}
@@ -64,10 +68,15 @@ export class NotificationService extends Disposable implements INotificationServ
let handle: INotificationHandle;
if (notification.neverShowAgain) {
const scope = notification.neverShowAgain.scope === NeverShowAgainScope.WORKSPACE ? StorageScope.WORKSPACE : StorageScope.GLOBAL;
const id = notification.neverShowAgain.id;
// opt-in to syncing if global
if (scope === StorageScope.GLOBAL) {
this.storageKeysSyncRegistryService.registerStorageKey({ key: id, version: 1 });
}
// If the user already picked to not show the notification
// again, we return with a no-op notification here
const id = notification.neverShowAgain.id;
if (this.storageService.getBoolean(id, scope)) {
return new NoOpNotification();
}
@@ -115,10 +124,15 @@ export class NotificationService extends Disposable implements INotificationServ
// Handle neverShowAgain option accordingly
if (options?.neverShowAgain) {
const scope = options.neverShowAgain.scope === NeverShowAgainScope.WORKSPACE ? StorageScope.WORKSPACE : StorageScope.GLOBAL;
const id = options.neverShowAgain.id;
// opt-in to syncing if global
if (scope === StorageScope.GLOBAL) {
this.storageKeysSyncRegistryService.registerStorageKey({ key: id, version: 1 });
}
// If the user already picked to not show the notification
// again, we return with a no-op notification here
const id = options.neverShowAgain.id;
if (this.storageService.getBoolean(id, scope)) {
return new NoOpNotification();
}

View File

@@ -68,7 +68,14 @@ export class ProgressService extends Disposable implements IProgressService {
case ProgressLocation.Notification:
return this.withNotificationProgress({ ...options, location }, task, onDidCancel);
case ProgressLocation.Window:
return this.withWindowProgress({ ...options, location }, task);
if ((options as IProgressWindowOptions).command) {
// Window progress with command get's shown in the status bar
return this.withWindowProgress({ ...options, location }, task);
}
// Window progress without command can be shown as silent notification
// which will first appear in the status bar and can then be brought to
// the front when clicking.
return this.withNotificationProgress({ ...options, silent: true, location: ProgressLocation.Notification }, task, onDidCancel);
case ProgressLocation.Explorer:
return this.withViewletProgress('workbench.view.explorer', task, { ...options, location });
case ProgressLocation.Scm:

View File

@@ -6,7 +6,7 @@
import { registerSingleton } from 'vs/platform/instantiation/common/extensions';
import { IWorkspacesService, IWorkspaceFolderCreationData, IWorkspaceIdentifier, IEnterWorkspaceResult, IRecentlyOpened, restoreRecentlyOpened, IRecent, isRecentFile, isRecentFolder, toStoreData, IStoredWorkspaceFolder, getStoredWorkspaceFolder, WORKSPACE_EXTENSION, IStoredWorkspace } from 'vs/platform/workspaces/common/workspaces';
import { URI } from 'vs/base/common/uri';
import { Event, Emitter } from 'vs/base/common/event';
import { Emitter } from 'vs/base/common/event';
import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage';
import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace';
import { ILogService } from 'vs/platform/log/common/log';
@@ -16,6 +16,7 @@ import { IFileService, FileOperationError, FileOperationResult } from 'vs/platfo
import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService';
import { joinPath } from 'vs/base/common/resources';
import { VSBuffer } from 'vs/base/common/buffer';
import { IStorageKeysSyncRegistryService } from 'vs/platform/userDataSync/common/storageKeys';
export class BrowserWorkspacesService extends Disposable implements IWorkspacesService {
@@ -23,18 +24,22 @@ export class BrowserWorkspacesService extends Disposable implements IWorkspacesS
_serviceBrand: undefined;
private readonly _onRecentlyOpenedChange: Emitter<void> = this._register(new Emitter<void>());
readonly onRecentlyOpenedChange: Event<void> = this._onRecentlyOpenedChange.event;
private readonly _onRecentlyOpenedChange = this._register(new Emitter<void>());
readonly onRecentlyOpenedChange = this._onRecentlyOpenedChange.event;
constructor(
@IStorageService private readonly storageService: IStorageService,
@IWorkspaceContextService private readonly workspaceService: IWorkspaceContextService,
@ILogService private readonly logService: ILogService,
@IFileService private readonly fileService: IFileService,
@IWorkbenchEnvironmentService private readonly environmentService: IWorkbenchEnvironmentService
@IWorkbenchEnvironmentService private readonly environmentService: IWorkbenchEnvironmentService,
@IStorageKeysSyncRegistryService storageKeysSyncRegistryService: IStorageKeysSyncRegistryService
) {
super();
// opt-in to syncing
storageKeysSyncRegistryService.registerStorageKey({ key: BrowserWorkspacesService.RECENTLY_OPENED_KEY, version: 1 });
// Opening a workspace should push it as most
// recently used to the workspaces history
this.addWorkspaceToRecentlyOpened();

View File

@@ -12,7 +12,7 @@ import { IInstantiationService } from 'vs/platform/instantiation/common/instanti
import { TestServiceAccessor, workbenchInstantiationService } from 'vs/workbench/test/browser/workbenchTestServices';
import { DisposableStore, toDisposable, IDisposable } from 'vs/base/common/lifecycle';
import { timeout } from 'vs/base/common/async';
import { PickerQuickAccessProvider, FastAndSlowPicksType } from 'vs/platform/quickinput/browser/pickerQuickAccess';
import { PickerQuickAccessProvider, FastAndSlowPicks } from 'vs/platform/quickinput/browser/pickerQuickAccess';
suite('QuickAccess', () => {
@@ -239,7 +239,7 @@ suite('QuickAccess', () => {
super('bothFastAndSlow');
}
protected getPicks(filter: string, disposables: DisposableStore, token: CancellationToken): FastAndSlowPicksType<IQuickPickItem> {
protected getPicks(filter: string, disposables: DisposableStore, token: CancellationToken): FastAndSlowPicks<IQuickPickItem> {
fastAndSlowProviderCalled = true;
return {

View File

@@ -64,27 +64,6 @@ interface IShowCandidate {
(host: string, port: number, detail: string): Thenable<boolean>;
}
interface IApplicationLink {
/**
* A link that is opened in the OS. If you want to open VSCode it must
* follow our expected structure of links:
*
* <vscode|vscode-insiders>://<file|vscode-remote>/<remote-authority>/<path>
*
* For example:
*
* vscode://vscode-remote/vsonline+2005711d/home/vsonline/workspace for
* a remote folder in VSO or vscode://file/home/workspace for a local folder.
*/
uri: URI;
/**
* A label for the application link to display.
*/
label: string;
}
interface ICommand {
/**
@@ -186,18 +165,6 @@ interface IWorkbenchConstructionOptions {
*/
readonly resolveCommonTelemetryProperties?: ICommontTelemetryPropertiesResolver;
/**
* Provide entries for the "Open in Desktop" feature.
*
* Depending on the returned elements the behaviour is:
* - no elements: there will not be a "Open in Desktop" affordance
* - 1 element: there will be a "Open in Desktop" affordance that opens on click
* and it will use the label provided by the link
* - N elements: there will be a "Open in Desktop" affordance that opens
* a picker on click to select which application to open.
*/
readonly applicationLinks?: readonly IApplicationLink[];
/**
* A set of optional commands that should be registered with the commands
* registry.
@@ -311,9 +278,6 @@ export {
// External Uris
IExternalUriResolver,
// Protocol Links
IApplicationLink,
// Commands
ICommand
};

View File

@@ -132,7 +132,4 @@ import 'vs/workbench/contrib/welcome/telemetryOptOut/browser/telemetryOptOut.con
// Issues
import 'vs/workbench/contrib/issue/browser/issue.contribution';
// Open In Desktop
import 'vs/workbench/contrib/openInDesktop/browser/openInDesktop.web.contribution';
//#endregion