mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-02-16 10:58:30 -05:00
Merge VS Code 1.21 source code (#1067)
* Initial VS Code 1.21 file copy with patches * A few more merges * Post npm install * Fix batch of build breaks * Fix more build breaks * Fix more build errors * Fix more build breaks * Runtime fixes 1 * Get connection dialog working with some todos * Fix a few packaging issues * Copy several node_modules to package build to fix loader issues * Fix breaks from master * A few more fixes * Make tests pass * First pass of license header updates * Second pass of license header updates * Fix restore dialog issues * Remove add additional themes menu items * fix select box issues where the list doesn't show up * formatting * Fix editor dispose issue * Copy over node modules to correct location on all platforms
This commit is contained in:
@@ -9,11 +9,10 @@ import { Registry } from 'vs/platform/registry/common/platform';
|
||||
import { KeybindingsRegistry } from 'vs/platform/keybinding/common/keybindingsRegistry';
|
||||
import { ICommandHandler, CommandsRegistry } from 'vs/platform/commands/common/commands';
|
||||
import { SyncActionDescriptor, MenuRegistry, MenuId } from 'vs/platform/actions/common/actions';
|
||||
import { IMessageService } from 'vs/platform/message/common/message';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import Severity from 'vs/base/common/severity';
|
||||
import { IDisposable, combinedDisposable } from 'vs/base/common/lifecycle';
|
||||
import { ILifecycleService, LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle';
|
||||
import { INotificationService } from 'vs/platform/notification/common/notification';
|
||||
|
||||
export const Extensions = {
|
||||
WorkbenchActions: 'workbench.contributions.actions'
|
||||
@@ -86,12 +85,12 @@ Registry.add(Extensions.WorkbenchActions, new class implements IWorkbenchActionR
|
||||
|
||||
private _createCommandHandler(descriptor: SyncActionDescriptor): ICommandHandler {
|
||||
return (accessor, args) => {
|
||||
const messageService = accessor.get(IMessageService);
|
||||
const notificationService = accessor.get(INotificationService);
|
||||
const instantiationService = accessor.get(IInstantiationService);
|
||||
const lifecycleService = accessor.get(ILifecycleService);
|
||||
|
||||
TPromise.as(this._triggerAndDisposeAction(instantiationService, lifecycleService, descriptor, args)).then(null, (err) => {
|
||||
messageService.show(Severity.Error, err);
|
||||
TPromise.as(this._triggerAndDisposeAction(instantiationService, lifecycleService, descriptor, args)).then(null, err => {
|
||||
notificationService.error(err);
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
@@ -10,11 +10,13 @@ import * as objects from 'vs/base/common/objects';
|
||||
import types = require('vs/base/common/types');
|
||||
import URI from 'vs/base/common/uri';
|
||||
import { IDisposable, dispose, Disposable } from 'vs/base/common/lifecycle';
|
||||
import { IEditor, IEditorViewState, IModel, ScrollType } from 'vs/editor/common/editorCommon';
|
||||
import { IEditorInput, IEditorModel, IEditorOptions, ITextEditorOptions, IBaseResourceInput, Position, Verbosity, IEditor as IBaseEditor } from 'vs/platform/editor/common/editor';
|
||||
import { IEditor, IEditorViewState, ScrollType } from 'vs/editor/common/editorCommon';
|
||||
import { IEditorInput, IEditorModel, IEditorOptions, ITextEditorOptions, IBaseResourceInput, Position, Verbosity, IEditor as IBaseEditor, IRevertOptions } from 'vs/platform/editor/common/editor';
|
||||
import { IInstantiationService, IConstructorSignature0 } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { RawContextKey } from 'vs/platform/contextkey/common/contextkey';
|
||||
import { Registry } from 'vs/platform/registry/common/platform';
|
||||
import { ITextModel } from 'vs/editor/common/model';
|
||||
import { Schemas } from 'vs/base/common/network';
|
||||
|
||||
export const TextCompareEditorVisible = new RawContextKey<boolean>('textCompareEditorVisible', false);
|
||||
|
||||
@@ -29,13 +31,18 @@ export enum ConfirmResult {
|
||||
*/
|
||||
export const TEXT_DIFF_EDITOR_ID = 'workbench.editors.textDiffEditor';
|
||||
|
||||
export const PREFERENCES_EDITOR_ID = 'workbench.editor.preferencesEditor';
|
||||
|
||||
/**
|
||||
* Binary diff editor id.
|
||||
*/
|
||||
export const BINARY_DIFF_EDITOR_ID = 'workbench.editors.binaryResourceDiffEditor';
|
||||
|
||||
export interface IFileInputFactory {
|
||||
|
||||
createFileInput(resource: URI, encoding: string, instantiationService: IInstantiationService): IFileEditorInput;
|
||||
|
||||
isFileInput(obj: any): obj is IFileEditorInput;
|
||||
}
|
||||
|
||||
export interface IEditorInputFactoryRegistry {
|
||||
@@ -200,8 +207,8 @@ export abstract class EditorInput implements IEditorInput {
|
||||
/**
|
||||
* Subclasses should bring up a proper dialog for the user if the editor is dirty and return the result.
|
||||
*/
|
||||
public confirmSave(): ConfirmResult {
|
||||
return ConfirmResult.DONT_SAVE;
|
||||
public confirmSave(): TPromise<ConfirmResult> {
|
||||
return TPromise.wrap(ConfirmResult.DONT_SAVE);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -214,7 +221,7 @@ export abstract class EditorInput implements IEditorInput {
|
||||
/**
|
||||
* Reverts the editor if it is dirty. Subclasses return a promise with a boolean indicating the success of the operation.
|
||||
*/
|
||||
public revert(): TPromise<boolean> {
|
||||
public revert(options?: IRevertOptions): TPromise<boolean> {
|
||||
return TPromise.as(true);
|
||||
}
|
||||
|
||||
@@ -351,7 +358,7 @@ export interface IFileEditorInput extends IEditorInput, IEncodingSupport {
|
||||
*/
|
||||
export class SideBySideEditorInput extends EditorInput {
|
||||
|
||||
public static ID: string = 'workbench.editorinputs.sidebysideEditorInput';
|
||||
public static readonly ID: string = 'workbench.editorinputs.sidebysideEditorInput';
|
||||
|
||||
private _toUnbind: IDisposable[];
|
||||
|
||||
@@ -373,7 +380,7 @@ export class SideBySideEditorInput extends EditorInput {
|
||||
return this.master.isDirty();
|
||||
}
|
||||
|
||||
public confirmSave(): ConfirmResult {
|
||||
public confirmSave(): TPromise<ConfirmResult> {
|
||||
return this.master.confirmSave();
|
||||
}
|
||||
|
||||
@@ -460,7 +467,7 @@ export class SideBySideEditorInput extends EditorInput {
|
||||
}
|
||||
|
||||
export interface ITextEditorModel extends IEditorModel {
|
||||
textEditorModel: IModel;
|
||||
textEditorModel: ITextModel;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -788,8 +795,14 @@ export interface IEditorIdentifier {
|
||||
editor: IEditorInput;
|
||||
}
|
||||
|
||||
export interface IEditorContext extends IEditorIdentifier {
|
||||
event?: any;
|
||||
/**
|
||||
* The editor commands context is used for editor commands (e.g. in the editor title)
|
||||
* and we must ensure that the context is serializable because it potentially travels
|
||||
* to the extension host!
|
||||
*/
|
||||
export interface IEditorCommandsContext {
|
||||
groupId: GroupIdentifier;
|
||||
editorIndex?: number;
|
||||
}
|
||||
|
||||
export interface IEditorCloseEvent extends IEditorIdentifier {
|
||||
@@ -853,7 +866,7 @@ export const EditorCommands = {
|
||||
|
||||
export interface IResourceOptions {
|
||||
supportSideBySide?: boolean;
|
||||
filter?: 'file' | 'untitled' | ['file', 'untitled'] | ['untitled', 'file'];
|
||||
filter?: string | string[];
|
||||
}
|
||||
|
||||
export function toResource(editor: IEditorInput, options?: IResourceOptions): URI {
|
||||
@@ -878,18 +891,18 @@ export function toResource(editor: IEditorInput, options?: IResourceOptions): UR
|
||||
let includeFiles: boolean;
|
||||
let includeUntitled: boolean;
|
||||
if (Array.isArray(options.filter)) {
|
||||
includeFiles = (options.filter.indexOf('file') >= 0);
|
||||
includeUntitled = (options.filter.indexOf('untitled') >= 0);
|
||||
includeFiles = (options.filter.indexOf(Schemas.file) >= 0);
|
||||
includeUntitled = (options.filter.indexOf(Schemas.untitled) >= 0);
|
||||
} else {
|
||||
includeFiles = (options.filter === 'file');
|
||||
includeUntitled = (options.filter === 'untitled');
|
||||
includeFiles = (options.filter === Schemas.file);
|
||||
includeUntitled = (options.filter === Schemas.untitled);
|
||||
}
|
||||
|
||||
if (includeFiles && resource.scheme === 'file') {
|
||||
if (includeFiles && resource.scheme === Schemas.file) {
|
||||
return resource;
|
||||
}
|
||||
|
||||
if (includeUntitled && resource.scheme === 'untitled') {
|
||||
if (includeUntitled && resource.scheme === Schemas.untitled) {
|
||||
return resource;
|
||||
}
|
||||
|
||||
@@ -946,4 +959,4 @@ export const Extensions = {
|
||||
EditorInputFactories: 'workbench.contributions.editor.inputFactories'
|
||||
};
|
||||
|
||||
Registry.add(Extensions.EditorInputFactories, new EditorInputFactoryRegistry());
|
||||
Registry.add(Extensions.EditorInputFactories, new EditorInputFactoryRegistry());
|
||||
|
||||
@@ -18,7 +18,7 @@ import { DataUri } from 'vs/workbench/common/resources';
|
||||
*/
|
||||
export class DataUriEditorInput extends EditorInput {
|
||||
|
||||
static ID: string = 'workbench.editors.dataUriEditorInput';
|
||||
static readonly ID: string = 'workbench.editors.dataUriEditorInput';
|
||||
|
||||
private resource: URI;
|
||||
private name: string;
|
||||
|
||||
@@ -19,7 +19,7 @@ import { IHashService } from 'vs/workbench/services/hash/common/hashService';
|
||||
*/
|
||||
export class ResourceEditorInput extends EditorInput {
|
||||
|
||||
static ID: string = 'workbench.editors.resourceEditorInput';
|
||||
static readonly ID: string = 'workbench.editors.resourceEditorInput';
|
||||
|
||||
private modelReference: TPromise<IReference<ITextEditorModel>>;
|
||||
private resource: URI;
|
||||
@@ -93,7 +93,8 @@ export class ResourceEditorInput extends EditorInput {
|
||||
if (!(model instanceof ResourceEditorModel)) {
|
||||
ref.dispose();
|
||||
this.modelReference = null;
|
||||
return TPromise.wrapError<ITextEditorModel>(new Error(`Unexpected model for ResourceInput: ${this.resource}`)); // TODO@Ben eventually also files should be supported, but we guard due to the dangerous dispose of the model in dispose()
|
||||
|
||||
return TPromise.wrapError<ITextEditorModel>(new Error(`Unexpected model for ResourceInput: ${this.resource}`));
|
||||
}
|
||||
|
||||
return model;
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
'use strict';
|
||||
|
||||
import { TPromise } from 'vs/base/common/winjs.base';
|
||||
import { EndOfLinePreference, IModel } from 'vs/editor/common/editorCommon';
|
||||
import { ITextModel, ITextBufferFactory } from 'vs/editor/common/model';
|
||||
import { IMode } from 'vs/editor/common/modes';
|
||||
import { EditorModel } from 'vs/workbench/common/editor';
|
||||
import URI from 'vs/base/common/uri';
|
||||
@@ -13,7 +13,7 @@ import { ITextEditorModel } from 'vs/editor/common/services/resolverService';
|
||||
import { IModeService } from 'vs/editor/common/services/modeService';
|
||||
import { IModelService } from 'vs/editor/common/services/modelService';
|
||||
import { IDisposable } from 'vs/base/common/lifecycle';
|
||||
import { IRawTextSource } from 'vs/editor/common/model/textSource';
|
||||
import { ITextSnapshot } from 'vs/platform/files/common/files';
|
||||
|
||||
/**
|
||||
* The base text editor model leverages the code editor model. This class is only intended to be subclassed and not instantiated.
|
||||
@@ -49,7 +49,7 @@ export abstract class BaseTextEditorModel extends EditorModel implements ITextEd
|
||||
this.registerModelDisposeListener(model);
|
||||
}
|
||||
|
||||
private registerModelDisposeListener(model: IModel): void {
|
||||
private registerModelDisposeListener(model: ITextModel): void {
|
||||
if (this.modelDisposeListener) {
|
||||
this.modelDisposeListener.dispose();
|
||||
}
|
||||
@@ -60,20 +60,20 @@ export abstract class BaseTextEditorModel extends EditorModel implements ITextEd
|
||||
});
|
||||
}
|
||||
|
||||
public get textEditorModel(): IModel {
|
||||
public get textEditorModel(): ITextModel {
|
||||
return this.textEditorModelHandle ? this.modelService.getModel(this.textEditorModelHandle) : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the text editor model with the provided value, modeId (can be comma separated for multiple values) and optional resource URL.
|
||||
*/
|
||||
protected createTextEditorModel(value: string | IRawTextSource, resource?: URI, modeId?: string): TPromise<EditorModel> {
|
||||
protected createTextEditorModel(value: ITextBufferFactory, resource?: URI, modeId?: string): TPromise<EditorModel> {
|
||||
const firstLineText = this.getFirstLineText(value);
|
||||
const mode = this.getOrCreateMode(this.modeService, modeId, firstLineText);
|
||||
return TPromise.as(this.doCreateTextEditorModel(value, mode, resource));
|
||||
}
|
||||
|
||||
private doCreateTextEditorModel(value: string | IRawTextSource, mode: TPromise<IMode>, resource: URI): EditorModel {
|
||||
private doCreateTextEditorModel(value: ITextBufferFactory, mode: TPromise<IMode>, resource: URI): EditorModel {
|
||||
let model = resource && this.modelService.getModel(resource);
|
||||
if (!model) {
|
||||
model = this.modelService.createModel(value, mode, resource);
|
||||
@@ -91,24 +91,17 @@ export abstract class BaseTextEditorModel extends EditorModel implements ITextEd
|
||||
return this;
|
||||
}
|
||||
|
||||
protected getFirstLineText(value: string | IRawTextSource): string {
|
||||
if (typeof value === 'string') {
|
||||
const firstLineText = value.substr(0, 100);
|
||||
protected getFirstLineText(value: ITextBufferFactory | ITextModel): string {
|
||||
|
||||
let crIndex = firstLineText.indexOf('\r');
|
||||
if (crIndex < 0) {
|
||||
crIndex = firstLineText.length;
|
||||
}
|
||||
|
||||
let lfIndex = firstLineText.indexOf('\n');
|
||||
if (lfIndex < 0) {
|
||||
lfIndex = firstLineText.length;
|
||||
}
|
||||
|
||||
return firstLineText.substr(0, Math.min(crIndex, lfIndex));
|
||||
} else {
|
||||
return value.lines[0].substr(0, 100);
|
||||
// text buffer factory
|
||||
const textBufferFactory = value as ITextBufferFactory;
|
||||
if (typeof textBufferFactory.getFirstLineText === 'function') {
|
||||
return textBufferFactory.getFirstLineText(100);
|
||||
}
|
||||
|
||||
// text model
|
||||
const textSnapshot = value as ITextModel;
|
||||
return textSnapshot.getLineContent(1).substr(0, 100);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -123,7 +116,7 @@ export abstract class BaseTextEditorModel extends EditorModel implements ITextEd
|
||||
/**
|
||||
* Updates the text editor model with the provided value. If the value is the same as the model has, this is a no-op.
|
||||
*/
|
||||
protected updateTextEditorModel(newValue: string | IRawTextSource): void {
|
||||
protected updateTextEditorModel(newValue: ITextBufferFactory): void {
|
||||
if (!this.textEditorModel) {
|
||||
return;
|
||||
}
|
||||
@@ -131,13 +124,10 @@ export abstract class BaseTextEditorModel extends EditorModel implements ITextEd
|
||||
this.modelService.updateModel(this.textEditorModel, newValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the textual value of this editor model or null if it has not yet been created.
|
||||
*/
|
||||
public getValue(): string {
|
||||
public createSnapshot(): ITextSnapshot {
|
||||
const model = this.textEditorModel;
|
||||
if (model) {
|
||||
return model.getValue(EndOfLinePreference.TextDefined, true /* Preserve BOM */);
|
||||
return model.createSnapshot(true /* Preserve BOM */);
|
||||
}
|
||||
|
||||
return null;
|
||||
|
||||
@@ -29,7 +29,7 @@ import { IHashService } from 'vs/workbench/services/hash/common/hashService';
|
||||
*/
|
||||
export class UntitledEditorInput extends EditorInput implements IEncodingSupport {
|
||||
|
||||
public static ID: string = 'workbench.editors.untitledEditorInput';
|
||||
public static readonly ID: string = 'workbench.editors.untitledEditorInput';
|
||||
|
||||
private _hasAssociatedFilePath: boolean;
|
||||
private cachedModel: UntitledEditorModel;
|
||||
@@ -180,7 +180,7 @@ export class UntitledEditorInput extends EditorInput implements IEncodingSupport
|
||||
return this.hasAssociatedFilePath;
|
||||
}
|
||||
|
||||
public confirmSave(): ConfirmResult {
|
||||
public confirmSave(): TPromise<ConfirmResult> {
|
||||
return this.textFileService.confirmSave([this.resource]);
|
||||
}
|
||||
|
||||
|
||||
@@ -10,16 +10,16 @@ import { IEncodingSupport } from 'vs/workbench/common/editor';
|
||||
import { BaseTextEditorModel } from 'vs/workbench/common/editor/textEditorModel';
|
||||
import URI from 'vs/base/common/uri';
|
||||
import { PLAINTEXT_MODE_ID } from 'vs/editor/common/modes/modesRegistry';
|
||||
import { EndOfLinePreference } from 'vs/editor/common/editorCommon';
|
||||
import { CONTENT_CHANGE_EVENT_BUFFER_DELAY } from 'vs/platform/files/common/files';
|
||||
import { IModeService } from 'vs/editor/common/services/modeService';
|
||||
import { IModelService } from 'vs/editor/common/services/modelService';
|
||||
import { IMode } from 'vs/editor/common/modes';
|
||||
import Event, { Emitter } from 'vs/base/common/event';
|
||||
import { RunOnceScheduler } from 'vs/base/common/async';
|
||||
import { IBackupFileService, BACKUP_FILE_RESOLVE_OPTIONS } from 'vs/workbench/services/backup/common/backup';
|
||||
import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles';
|
||||
import { IBackupFileService } from 'vs/workbench/services/backup/common/backup';
|
||||
import { ITextResourceConfigurationService } from 'vs/editor/common/services/resourceConfiguration';
|
||||
import { ITextBufferFactory } from 'vs/editor/common/model';
|
||||
import { createTextBufferFactory } from 'vs/editor/common/model/textModel';
|
||||
|
||||
export class UntitledEditorModel extends BaseTextEditorModel implements IEncodingSupport {
|
||||
|
||||
@@ -47,7 +47,6 @@ export class UntitledEditorModel extends BaseTextEditorModel implements IEncodin
|
||||
@IModeService modeService: IModeService,
|
||||
@IModelService modelService: IModelService,
|
||||
@IBackupFileService private backupFileService: IBackupFileService,
|
||||
@ITextFileService private textFileService: ITextFileService,
|
||||
@ITextResourceConfigurationService private configurationService: ITextResourceConfigurationService
|
||||
) {
|
||||
super(modelService, modeService);
|
||||
@@ -113,14 +112,6 @@ export class UntitledEditorModel extends BaseTextEditorModel implements IEncodin
|
||||
return this.versionId;
|
||||
}
|
||||
|
||||
public getValue(): string {
|
||||
if (this.textEditorModel) {
|
||||
return this.textEditorModel.getValue(EndOfLinePreference.TextDefined, true /* Preserve BOM */);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public getModeId(): string {
|
||||
if (this.textEditorModel) {
|
||||
return this.textEditorModel.getLanguageIdentifier().language;
|
||||
@@ -172,18 +163,24 @@ export class UntitledEditorModel extends BaseTextEditorModel implements IEncodin
|
||||
// Check for backups first
|
||||
return this.backupFileService.loadBackupResource(this.resource).then(backupResource => {
|
||||
if (backupResource) {
|
||||
return this.textFileService.resolveTextContent(backupResource, BACKUP_FILE_RESOLVE_OPTIONS).then(rawTextContent => {
|
||||
return this.backupFileService.parseBackupContent(rawTextContent.value);
|
||||
});
|
||||
return this.backupFileService.resolveBackupContent(backupResource);
|
||||
}
|
||||
|
||||
return null;
|
||||
}).then(backupContent => {
|
||||
}).then(backupTextBufferFactory => {
|
||||
const hasBackup = !!backupTextBufferFactory;
|
||||
|
||||
// untitled associated to file path are dirty right away as well as untitled with content
|
||||
this.setDirty(this.hasAssociatedFilePath || !!backupContent);
|
||||
this.setDirty(this.hasAssociatedFilePath || hasBackup);
|
||||
|
||||
return this.doLoad(backupContent || this.initialValue || '').then(model => {
|
||||
let untitledContents: ITextBufferFactory;
|
||||
if (backupTextBufferFactory) {
|
||||
untitledContents = backupTextBufferFactory;
|
||||
} else {
|
||||
untitledContents = createTextBufferFactory(this.initialValue || '');
|
||||
}
|
||||
|
||||
return this.doLoad(untitledContents).then(model => {
|
||||
// Encoding
|
||||
this.configuredEncoding = this.configurationService.getValue<string>(this.resource, 'files.encoding');
|
||||
|
||||
@@ -198,7 +195,7 @@ export class UntitledEditorModel extends BaseTextEditorModel implements IEncodin
|
||||
});
|
||||
}
|
||||
|
||||
private doLoad(content: string): TPromise<UntitledEditorModel> {
|
||||
private doLoad(content: ITextBufferFactory): TPromise<UntitledEditorModel> {
|
||||
|
||||
// Create text editor model if not yet done
|
||||
if (!this.textEditorModel) {
|
||||
@@ -236,4 +233,4 @@ export class UntitledEditorModel extends BaseTextEditorModel implements IEncodin
|
||||
|
||||
this.toDispose = dispose(this.toDispose);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
591
src/vs/workbench/common/notifications.ts
Normal file
591
src/vs/workbench/common/notifications.ts
Normal file
@@ -0,0 +1,591 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
'use strict';
|
||||
|
||||
import { INotification, INotificationHandle, INotificationActions, INotificationProgress, NoOpNotification, Severity, NotificationMessage } from 'vs/platform/notification/common/notification';
|
||||
import { toErrorMessage } from 'vs/base/common/errorMessage';
|
||||
import Event, { Emitter, once } from 'vs/base/common/event';
|
||||
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
|
||||
import { isPromiseCanceledError, isErrorWithActions } from 'vs/base/common/errors';
|
||||
|
||||
export interface INotificationsModel {
|
||||
|
||||
readonly notifications: INotificationViewItem[];
|
||||
readonly onDidNotificationChange: Event<INotificationChangeEvent>;
|
||||
|
||||
notify(notification: INotification): INotificationHandle;
|
||||
}
|
||||
|
||||
export enum NotificationChangeType {
|
||||
ADD,
|
||||
CHANGE,
|
||||
REMOVE
|
||||
}
|
||||
|
||||
export interface INotificationChangeEvent {
|
||||
|
||||
/**
|
||||
* The index this notification has in the list of notifications.
|
||||
*/
|
||||
index: number;
|
||||
|
||||
/**
|
||||
* The notification this change is about.
|
||||
*/
|
||||
item: INotificationViewItem;
|
||||
|
||||
/**
|
||||
* The kind of notification change.
|
||||
*/
|
||||
kind: NotificationChangeType;
|
||||
}
|
||||
|
||||
export class NotificationHandle implements INotificationHandle {
|
||||
private _onDidDispose: Emitter<void> = new Emitter();
|
||||
|
||||
constructor(private item: INotificationViewItem, private disposeItem: (item: INotificationViewItem) => void) {
|
||||
this.registerListeners();
|
||||
}
|
||||
|
||||
private registerListeners(): void {
|
||||
once(this.item.onDidDispose)(() => {
|
||||
this._onDidDispose.fire();
|
||||
this._onDidDispose.dispose();
|
||||
});
|
||||
}
|
||||
|
||||
public get onDidDispose(): Event<void> {
|
||||
return this._onDidDispose.event;
|
||||
}
|
||||
|
||||
public get progress(): INotificationProgress {
|
||||
return this.item.progress;
|
||||
}
|
||||
|
||||
public updateSeverity(severity: Severity): void {
|
||||
this.item.updateSeverity(severity);
|
||||
}
|
||||
|
||||
public updateMessage(message: NotificationMessage): void {
|
||||
this.item.updateMessage(message);
|
||||
}
|
||||
|
||||
public updateActions(actions?: INotificationActions): void {
|
||||
this.item.updateActions(actions);
|
||||
}
|
||||
|
||||
public dispose(): void {
|
||||
this.disposeItem(this.item);
|
||||
this._onDidDispose.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
export class NotificationsModel implements INotificationsModel {
|
||||
|
||||
private static NO_OP_NOTIFICATION = new NoOpNotification();
|
||||
|
||||
private _notifications: INotificationViewItem[];
|
||||
|
||||
private _onDidNotificationChange: Emitter<INotificationChangeEvent>;
|
||||
private toDispose: IDisposable[];
|
||||
|
||||
constructor() {
|
||||
this._notifications = [];
|
||||
this.toDispose = [];
|
||||
|
||||
this._onDidNotificationChange = new Emitter<INotificationChangeEvent>();
|
||||
this.toDispose.push(this._onDidNotificationChange);
|
||||
}
|
||||
|
||||
public get notifications(): INotificationViewItem[] {
|
||||
return this._notifications;
|
||||
}
|
||||
|
||||
public get onDidNotificationChange(): Event<INotificationChangeEvent> {
|
||||
return this._onDidNotificationChange.event;
|
||||
}
|
||||
|
||||
public notify(notification: INotification): INotificationHandle {
|
||||
const item = this.createViewItem(notification);
|
||||
if (!item) {
|
||||
return NotificationsModel.NO_OP_NOTIFICATION; // return early if this is a no-op
|
||||
}
|
||||
|
||||
// Deduplicate
|
||||
const duplicate = this.findNotification(item);
|
||||
if (duplicate) {
|
||||
duplicate.dispose();
|
||||
}
|
||||
|
||||
// Add to list as first entry
|
||||
this._notifications.splice(0, 0, item);
|
||||
|
||||
// Events
|
||||
this._onDidNotificationChange.fire({ item, index: 0, kind: NotificationChangeType.ADD });
|
||||
|
||||
// Wrap into handle
|
||||
return new NotificationHandle(item, item => this.disposeItem(item));
|
||||
}
|
||||
|
||||
private disposeItem(item: INotificationViewItem): void {
|
||||
const liveItem = this.findNotification(item);
|
||||
if (liveItem && liveItem !== item) {
|
||||
liveItem.dispose(); // item could have been replaced with another one, make sure to dispose the live item
|
||||
} else {
|
||||
item.dispose(); // otherwise just dispose the item that was passed in
|
||||
}
|
||||
}
|
||||
|
||||
private findNotification(item: INotificationViewItem): INotificationViewItem {
|
||||
for (let i = 0; i < this._notifications.length; i++) {
|
||||
const notification = this._notifications[i];
|
||||
if (notification.equals(item)) {
|
||||
return notification;
|
||||
}
|
||||
}
|
||||
|
||||
return void 0;
|
||||
}
|
||||
|
||||
private createViewItem(notification: INotification): INotificationViewItem {
|
||||
const item = NotificationViewItem.create(notification);
|
||||
if (!item) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Item Events
|
||||
const onItemChangeEvent = () => {
|
||||
const index = this._notifications.indexOf(item);
|
||||
if (index >= 0) {
|
||||
this._onDidNotificationChange.fire({ item, index, kind: NotificationChangeType.CHANGE });
|
||||
}
|
||||
};
|
||||
|
||||
const itemExpansionChangeListener = item.onDidExpansionChange(() => onItemChangeEvent());
|
||||
|
||||
const itemLabelChangeListener = item.onDidLabelChange(e => {
|
||||
// a label change in the area of actions or the message is a change that potentially has an impact
|
||||
// on the size of the notification and as such we emit a change event so that viewers can redraw
|
||||
if (e.kind === NotificationViewItemLabelKind.ACTIONS || e.kind === NotificationViewItemLabelKind.MESSAGE) {
|
||||
onItemChangeEvent();
|
||||
}
|
||||
});
|
||||
|
||||
once(item.onDidDispose)(() => {
|
||||
itemExpansionChangeListener.dispose();
|
||||
itemLabelChangeListener.dispose();
|
||||
|
||||
const index = this._notifications.indexOf(item);
|
||||
if (index >= 0) {
|
||||
this._notifications.splice(index, 1);
|
||||
this._onDidNotificationChange.fire({ item, index, kind: NotificationChangeType.REMOVE });
|
||||
}
|
||||
});
|
||||
|
||||
return item;
|
||||
}
|
||||
|
||||
public dispose(): void {
|
||||
this.toDispose = dispose(this.toDispose);
|
||||
}
|
||||
}
|
||||
|
||||
export interface INotificationViewItem {
|
||||
readonly severity: Severity;
|
||||
readonly message: INotificationMessage;
|
||||
readonly source: string;
|
||||
readonly actions: INotificationActions;
|
||||
readonly progress: INotificationViewItemProgress;
|
||||
|
||||
readonly expanded: boolean;
|
||||
readonly canCollapse: boolean;
|
||||
|
||||
readonly onDidExpansionChange: Event<void>;
|
||||
readonly onDidDispose: Event<void>;
|
||||
readonly onDidLabelChange: Event<INotificationViewItemLabelChangeEvent>;
|
||||
|
||||
expand(): void;
|
||||
collapse(skipEvents?: boolean): void;
|
||||
toggle(): void;
|
||||
|
||||
hasProgress(): boolean;
|
||||
|
||||
updateSeverity(severity: Severity): void;
|
||||
updateMessage(message: NotificationMessage): void;
|
||||
updateActions(actions?: INotificationActions): void;
|
||||
|
||||
dispose(): void;
|
||||
|
||||
equals(item: INotificationViewItem);
|
||||
}
|
||||
|
||||
export function isNotificationViewItem(obj: any): obj is INotificationViewItem {
|
||||
return obj instanceof NotificationViewItem;
|
||||
}
|
||||
|
||||
export enum NotificationViewItemLabelKind {
|
||||
SEVERITY,
|
||||
MESSAGE,
|
||||
ACTIONS,
|
||||
PROGRESS
|
||||
}
|
||||
|
||||
export interface INotificationViewItemLabelChangeEvent {
|
||||
kind: NotificationViewItemLabelKind;
|
||||
}
|
||||
|
||||
export interface INotificationViewItemProgressState {
|
||||
infinite?: boolean;
|
||||
total?: number;
|
||||
worked?: number;
|
||||
done?: boolean;
|
||||
}
|
||||
|
||||
export interface INotificationViewItemProgress extends INotificationProgress {
|
||||
readonly state: INotificationViewItemProgressState;
|
||||
|
||||
dispose(): void;
|
||||
}
|
||||
|
||||
export class NotificationViewItemProgress implements INotificationViewItemProgress {
|
||||
private _state: INotificationViewItemProgressState;
|
||||
|
||||
private _onDidChange: Emitter<void>;
|
||||
private toDispose: IDisposable[];
|
||||
|
||||
constructor() {
|
||||
this.toDispose = [];
|
||||
this._state = Object.create(null);
|
||||
|
||||
this._onDidChange = new Emitter<void>();
|
||||
this.toDispose.push(this._onDidChange);
|
||||
}
|
||||
|
||||
public get state(): INotificationViewItemProgressState {
|
||||
return this._state;
|
||||
}
|
||||
|
||||
public get onDidChange(): Event<void> {
|
||||
return this._onDidChange.event;
|
||||
}
|
||||
|
||||
public infinite(): void {
|
||||
if (this._state.infinite) {
|
||||
return;
|
||||
}
|
||||
|
||||
this._state.infinite = true;
|
||||
|
||||
this._state.total = void 0;
|
||||
this._state.worked = void 0;
|
||||
this._state.done = void 0;
|
||||
|
||||
this._onDidChange.fire();
|
||||
}
|
||||
|
||||
public done(): void {
|
||||
if (this._state.done) {
|
||||
return;
|
||||
}
|
||||
|
||||
this._state.done = true;
|
||||
|
||||
this._state.infinite = void 0;
|
||||
this._state.total = void 0;
|
||||
this._state.worked = void 0;
|
||||
|
||||
this._onDidChange.fire();
|
||||
}
|
||||
|
||||
public total(value: number): void {
|
||||
if (this._state.total === value) {
|
||||
return;
|
||||
}
|
||||
|
||||
this._state.total = value;
|
||||
|
||||
this._state.infinite = void 0;
|
||||
this._state.done = void 0;
|
||||
|
||||
this._onDidChange.fire();
|
||||
}
|
||||
|
||||
public worked(value: number): void {
|
||||
if (this._state.worked === value) {
|
||||
return;
|
||||
}
|
||||
|
||||
this._state.worked = value;
|
||||
|
||||
this._state.infinite = void 0;
|
||||
this._state.done = void 0;
|
||||
|
||||
this._onDidChange.fire();
|
||||
}
|
||||
|
||||
public dispose(): void {
|
||||
this.toDispose = dispose(this.toDispose);
|
||||
}
|
||||
}
|
||||
|
||||
export interface IMessageLink {
|
||||
name: string;
|
||||
href: string;
|
||||
offset: number;
|
||||
length: number;
|
||||
}
|
||||
|
||||
export interface INotificationMessage {
|
||||
raw: string;
|
||||
value: string;
|
||||
links: IMessageLink[];
|
||||
}
|
||||
|
||||
export class NotificationViewItem implements INotificationViewItem {
|
||||
|
||||
private static MAX_MESSAGE_LENGTH = 1000;
|
||||
|
||||
// Example link: "Some message with [link text](http://link.href)."
|
||||
// RegEx: [, anything not ], ], (, http:|https:, //, no whitespace)
|
||||
private static LINK_REGEX = /\[([^\]]+)\]\((https?:\/\/[^\)\s]+)\)/gi;
|
||||
|
||||
private _expanded: boolean;
|
||||
private toDispose: IDisposable[];
|
||||
|
||||
private _actions: INotificationActions;
|
||||
private _progress: NotificationViewItemProgress;
|
||||
|
||||
private _onDidExpansionChange: Emitter<void>;
|
||||
private _onDidDispose: Emitter<void>;
|
||||
private _onDidLabelChange: Emitter<INotificationViewItemLabelChangeEvent>;
|
||||
|
||||
public static create(notification: INotification): INotificationViewItem {
|
||||
if (!notification || !notification.message || isPromiseCanceledError(notification.message)) {
|
||||
return null; // we need a message to show
|
||||
}
|
||||
|
||||
let severity: Severity;
|
||||
if (typeof notification.severity === 'number') {
|
||||
severity = notification.severity;
|
||||
} else {
|
||||
severity = Severity.Info;
|
||||
}
|
||||
|
||||
const message = NotificationViewItem.parseNotificationMessage(notification.message);
|
||||
if (!message) {
|
||||
return null; // we need a message to show
|
||||
}
|
||||
|
||||
let actions: INotificationActions;
|
||||
if (notification.actions) {
|
||||
actions = notification.actions;
|
||||
} else if (isErrorWithActions(notification.message)) {
|
||||
actions = { primary: notification.message.actions };
|
||||
}
|
||||
|
||||
return new NotificationViewItem(severity, message, notification.source, actions);
|
||||
}
|
||||
|
||||
private static parseNotificationMessage(input: NotificationMessage): INotificationMessage {
|
||||
let message: string;
|
||||
|
||||
if (input instanceof Error) {
|
||||
message = toErrorMessage(input, false);
|
||||
} else if (typeof input === 'string') {
|
||||
message = input;
|
||||
}
|
||||
|
||||
if (!message) {
|
||||
return null; // we need a message to show
|
||||
}
|
||||
|
||||
const raw = message;
|
||||
|
||||
// Make sure message is in the limits
|
||||
if (message.length > NotificationViewItem.MAX_MESSAGE_LENGTH) {
|
||||
message = `${message.substr(0, NotificationViewItem.MAX_MESSAGE_LENGTH)}...`;
|
||||
}
|
||||
|
||||
// Remove newlines from messages as we do not support that and it makes link parsing hard
|
||||
message = message.replace(/(\r\n|\n|\r)/gm, ' ').trim();
|
||||
|
||||
// Parse Links
|
||||
const links: IMessageLink[] = [];
|
||||
message.replace(NotificationViewItem.LINK_REGEX, (matchString: string, name: string, href: string, offset: number) => {
|
||||
links.push({ name, href, offset, length: matchString.length });
|
||||
|
||||
return matchString;
|
||||
});
|
||||
|
||||
|
||||
return { raw, value: message, links };
|
||||
}
|
||||
|
||||
private constructor(private _severity: Severity, private _message: INotificationMessage, private _source: string, actions?: INotificationActions) {
|
||||
this.toDispose = [];
|
||||
|
||||
this.setActions(actions);
|
||||
|
||||
this._onDidExpansionChange = new Emitter<void>();
|
||||
this.toDispose.push(this._onDidExpansionChange);
|
||||
|
||||
this._onDidLabelChange = new Emitter<INotificationViewItemLabelChangeEvent>();
|
||||
this.toDispose.push(this._onDidLabelChange);
|
||||
|
||||
this._onDidDispose = new Emitter<void>();
|
||||
this.toDispose.push(this._onDidDispose);
|
||||
}
|
||||
|
||||
private setActions(actions: INotificationActions): void {
|
||||
if (!actions) {
|
||||
actions = { primary: [], secondary: [] };
|
||||
}
|
||||
|
||||
if (!Array.isArray(actions.primary)) {
|
||||
actions.primary = [];
|
||||
}
|
||||
|
||||
if (!Array.isArray(actions.secondary)) {
|
||||
actions.secondary = [];
|
||||
}
|
||||
|
||||
this._actions = actions;
|
||||
this._expanded = actions.primary.length > 0;
|
||||
|
||||
this.toDispose.push(...actions.primary);
|
||||
this.toDispose.push(...actions.secondary);
|
||||
}
|
||||
|
||||
public get onDidExpansionChange(): Event<void> {
|
||||
return this._onDidExpansionChange.event;
|
||||
}
|
||||
|
||||
public get onDidLabelChange(): Event<INotificationViewItemLabelChangeEvent> {
|
||||
return this._onDidLabelChange.event;
|
||||
}
|
||||
|
||||
public get onDidDispose(): Event<void> {
|
||||
return this._onDidDispose.event;
|
||||
}
|
||||
|
||||
public get canCollapse(): boolean {
|
||||
return this._actions.primary.length === 0;
|
||||
}
|
||||
|
||||
public get expanded(): boolean {
|
||||
return this._expanded;
|
||||
}
|
||||
|
||||
public get severity(): Severity {
|
||||
return this._severity;
|
||||
}
|
||||
|
||||
public hasProgress(): boolean {
|
||||
return !!this._progress;
|
||||
}
|
||||
|
||||
public get progress(): INotificationViewItemProgress {
|
||||
if (!this._progress) {
|
||||
this._progress = new NotificationViewItemProgress();
|
||||
this.toDispose.push(this._progress);
|
||||
this.toDispose.push(this._progress.onDidChange(() => this._onDidLabelChange.fire({ kind: NotificationViewItemLabelKind.PROGRESS })));
|
||||
}
|
||||
|
||||
return this._progress;
|
||||
}
|
||||
|
||||
public get message(): INotificationMessage {
|
||||
return this._message;
|
||||
}
|
||||
|
||||
public get source(): string {
|
||||
return this._source;
|
||||
}
|
||||
|
||||
public get actions(): INotificationActions {
|
||||
return this._actions;
|
||||
}
|
||||
|
||||
public updateSeverity(severity: Severity): void {
|
||||
this._severity = severity;
|
||||
this._onDidLabelChange.fire({ kind: NotificationViewItemLabelKind.SEVERITY });
|
||||
}
|
||||
|
||||
public updateMessage(input: NotificationMessage): void {
|
||||
const message = NotificationViewItem.parseNotificationMessage(input);
|
||||
if (!message) {
|
||||
return;
|
||||
}
|
||||
|
||||
this._message = message;
|
||||
this._onDidLabelChange.fire({ kind: NotificationViewItemLabelKind.MESSAGE });
|
||||
}
|
||||
|
||||
public updateActions(actions?: INotificationActions): void {
|
||||
this.setActions(actions);
|
||||
|
||||
this._onDidLabelChange.fire({ kind: NotificationViewItemLabelKind.ACTIONS });
|
||||
}
|
||||
|
||||
public expand(): void {
|
||||
if (this._expanded || !this.canCollapse) {
|
||||
return;
|
||||
}
|
||||
|
||||
this._expanded = true;
|
||||
this._onDidExpansionChange.fire();
|
||||
}
|
||||
|
||||
public collapse(skipEvents?: boolean): void {
|
||||
if (!this._expanded || !this.canCollapse) {
|
||||
return;
|
||||
}
|
||||
|
||||
this._expanded = false;
|
||||
|
||||
if (!skipEvents) {
|
||||
this._onDidExpansionChange.fire();
|
||||
}
|
||||
}
|
||||
|
||||
public toggle(): void {
|
||||
if (this._expanded) {
|
||||
this.collapse();
|
||||
} else {
|
||||
this.expand();
|
||||
}
|
||||
}
|
||||
|
||||
public dispose(): void {
|
||||
this._onDidDispose.fire();
|
||||
|
||||
this.toDispose = dispose(this.toDispose);
|
||||
}
|
||||
|
||||
public equals(other: INotificationViewItem): boolean {
|
||||
if (this._source !== other.source) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const primaryActions = this._actions.primary;
|
||||
const otherPrimaryActions = other.actions.primary;
|
||||
if (primaryActions.length !== otherPrimaryActions.length) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (this._message.value !== other.message.value) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (let i = 0; i < primaryActions.length; i++) {
|
||||
if (primaryActions[i].id !== otherPrimaryActions[i].id) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -10,6 +10,7 @@ import paths = require('vs/base/common/paths');
|
||||
import { basename } from 'vs/base/common/paths';
|
||||
import { RawContextKey, IContextKeyService, IContextKey } from 'vs/platform/contextkey/common/contextkey';
|
||||
import { IModeService } from 'vs/editor/common/services/modeService';
|
||||
import { IFileService } from 'vs/platform/files/common/files';
|
||||
|
||||
export class ResourceContextKey implements IContextKey<URI> {
|
||||
|
||||
@@ -18,22 +19,29 @@ export class ResourceContextKey implements IContextKey<URI> {
|
||||
static LangId = new RawContextKey<string>('resourceLangId', undefined);
|
||||
static Resource = new RawContextKey<URI>('resource', undefined);
|
||||
static Extension = new RawContextKey<string>('resourceExtname', undefined);
|
||||
static HasResource = new RawContextKey<boolean>('resourceSet', false);
|
||||
static IsFile = new RawContextKey<boolean>('resourceIsFile', false);
|
||||
|
||||
private _resourceKey: IContextKey<URI>;
|
||||
private _schemeKey: IContextKey<string>;
|
||||
private _filenameKey: IContextKey<string>;
|
||||
private _langIdKey: IContextKey<string>;
|
||||
private _extensionKey: IContextKey<string>;
|
||||
private _hasResource: IContextKey<boolean>;
|
||||
private _isFile: IContextKey<boolean>;
|
||||
|
||||
constructor(
|
||||
@IContextKeyService contextKeyService: IContextKeyService,
|
||||
@IModeService private _modeService: IModeService
|
||||
@IModeService private readonly _modeService: IModeService,
|
||||
@IFileService private readonly _fileService: IFileService
|
||||
) {
|
||||
this._schemeKey = ResourceContextKey.Scheme.bindTo(contextKeyService);
|
||||
this._filenameKey = ResourceContextKey.Filename.bindTo(contextKeyService);
|
||||
this._langIdKey = ResourceContextKey.LangId.bindTo(contextKeyService);
|
||||
this._resourceKey = ResourceContextKey.Resource.bindTo(contextKeyService);
|
||||
this._extensionKey = ResourceContextKey.Extension.bindTo(contextKeyService);
|
||||
this._hasResource = ResourceContextKey.HasResource.bindTo(contextKeyService);
|
||||
this._isFile = ResourceContextKey.IsFile.bindTo(contextKeyService);
|
||||
}
|
||||
|
||||
set(value: URI) {
|
||||
@@ -42,6 +50,8 @@ export class ResourceContextKey implements IContextKey<URI> {
|
||||
this._filenameKey.set(value && basename(value.fsPath));
|
||||
this._langIdKey.set(value && this._modeService.getModeIdByFilenameOrFirstLine(value.fsPath));
|
||||
this._extensionKey.set(value && paths.extname(value.fsPath));
|
||||
this._hasResource.set(!!value);
|
||||
this._isFile.set(value && this._fileService.canHandleResource(value));
|
||||
}
|
||||
|
||||
reset(): void {
|
||||
@@ -50,6 +60,8 @@ export class ResourceContextKey implements IContextKey<URI> {
|
||||
this._resourceKey.reset();
|
||||
this._langIdKey.reset();
|
||||
this._extensionKey.reset();
|
||||
this._hasResource.reset();
|
||||
this._isFile.reset();
|
||||
}
|
||||
|
||||
public get(): URI {
|
||||
|
||||
@@ -4,11 +4,24 @@
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import nls = require('vs/nls');
|
||||
import { registerColor, editorBackground, contrastBorder, transparent, lighten, darken } from 'vs/platform/theme/common/colorRegistry';
|
||||
import { registerColor, editorBackground, contrastBorder, transparent, editorWidgetBackground, textLinkForeground, lighten, darken } from 'vs/platform/theme/common/colorRegistry';
|
||||
import { IDisposable, Disposable, dispose } from 'vs/base/common/lifecycle';
|
||||
import { IThemeService, ITheme } from 'vs/platform/theme/common/themeService';
|
||||
import { Color } from 'vs/base/common/color';
|
||||
|
||||
// < --- Workbench (not customizable) --- >
|
||||
|
||||
export function WORKBENCH_BACKGROUND(theme: ITheme): Color {
|
||||
switch (theme.type) {
|
||||
case 'dark':
|
||||
return Color.fromHex('#252526');
|
||||
case 'light':
|
||||
return Color.fromHex('#F3F3F3');
|
||||
default:
|
||||
return Color.fromHex('#000000');
|
||||
}
|
||||
}
|
||||
|
||||
// < --- Tabs --- >
|
||||
|
||||
export const TAB_ACTIVE_BACKGROUND = registerColor('tab.activeBackground', {
|
||||
@@ -23,6 +36,18 @@ export const TAB_INACTIVE_BACKGROUND = registerColor('tab.inactiveBackground', {
|
||||
hc: null
|
||||
}, nls.localize('tabInactiveBackground', "Inactive tab background color. Tabs are the containers for editors in the editor area. Multiple tabs can be opened in one editor group. There can be multiple editor groups."));
|
||||
|
||||
export const TAB_HOVER_BACKGROUND = registerColor('tab.hoverBackground', {
|
||||
dark: null,
|
||||
light: null,
|
||||
hc: null
|
||||
}, nls.localize('tabHoverBackground', "Tab background color when hovering. Tabs are the containers for editors in the editor area. Multiple tabs can be opened in one editor group. There can be multiple editor groups."));
|
||||
|
||||
export const TAB_UNFOCUSED_HOVER_BACKGROUND = registerColor('tab.unfocusedHoverBackground', {
|
||||
dark: transparent(TAB_HOVER_BACKGROUND, 0.5),
|
||||
light: transparent(TAB_HOVER_BACKGROUND, 0.7),
|
||||
hc: null
|
||||
}, nls.localize('tabUnfocusedHoverBackground', "Tab background color in an unfocused group when hovering. Tabs are the containers for editors in the editor area. Multiple tabs can be opened in one editor group. There can be multiple editor groups."));
|
||||
|
||||
export const TAB_BORDER = registerColor('tab.border', {
|
||||
dark: '#252526',
|
||||
light: '#F3F3F3',
|
||||
@@ -41,6 +66,18 @@ export const TAB_UNFOCUSED_ACTIVE_BORDER = registerColor('tab.unfocusedActiveBor
|
||||
hc: null
|
||||
}, nls.localize('tabActiveUnfocusedBorder', "Border to highlight active tabs in an unfocused group. Tabs are the containers for editors in the editor area. Multiple tabs can be opened in one editor group. There can be multiple editor groups."));
|
||||
|
||||
export const TAB_HOVER_BORDER = registerColor('tab.hoverBorder', {
|
||||
dark: null,
|
||||
light: null,
|
||||
hc: null
|
||||
}, nls.localize('tabHoverBorder', "Border to highlight tabs when hovering. Tabs are the containers for editors in the editor area. Multiple tabs can be opened in one editor group. There can be multiple editor groups."));
|
||||
|
||||
export const TAB_UNFOCUSED_HOVER_BORDER = registerColor('tab.unfocusedHoverBorder', {
|
||||
dark: transparent(TAB_HOVER_BORDER, 0.5),
|
||||
light: transparent(TAB_HOVER_BORDER, 0.7),
|
||||
hc: null
|
||||
}, nls.localize('tabUnfocusedHoverBorder', "Border to highlight tabs in an unfocused group when hovering. Tabs are the containers for editors in the editor area. Multiple tabs can be opened in one editor group. There can be multiple editor groups."));
|
||||
|
||||
export const TAB_ACTIVE_FOREGROUND = registerColor('tab.activeForeground', {
|
||||
dark: Color.white,
|
||||
light: '#333333',
|
||||
@@ -90,7 +127,7 @@ export const EDITOR_GROUP_HEADER_NO_TABS_BACKGROUND = registerColor('editorGroup
|
||||
dark: editorBackground,
|
||||
light: editorBackground,
|
||||
hc: editorBackground
|
||||
}, nls.localize('editorGroupHeaderBackground', "Background color of the editor group title header when tabs are disabled. Editor groups are the containers of editors."));
|
||||
}, nls.localize('editorGroupHeaderBackground', "Background color of the editor group title header when tabs are disabled (`\"workbench.editor.showTabs\": false`). Editor groups are the containers of editors."));
|
||||
|
||||
export const EDITOR_GROUP_BORDER = registerColor('editorGroup.border', {
|
||||
dark: '#444444',
|
||||
@@ -199,13 +236,13 @@ export const STATUS_BAR_PROMINENT_ITEM_BACKGROUND = registerColor('statusBarItem
|
||||
dark: '#388A34',
|
||||
light: '#388A34',
|
||||
hc: '#3883A4'
|
||||
}, nls.localize('statusBarProminentItemBackground', "Status bar prominent items background color. Prominent items stand out from other status bar entries to indicate importance. The status bar is shown in the bottom of the window."));
|
||||
}, nls.localize('statusBarProminentItemBackground', "Status bar prominent items background color. Prominent items stand out from other status bar entries to indicate importance. Change mode `Toggle Tab Key Moves Focus` from command palette to see an example. The status bar is shown in the bottom of the window."));
|
||||
|
||||
export const STATUS_BAR_PROMINENT_ITEM_HOVER_BACKGROUND = registerColor('statusBarItem.prominentHoverBackground', {
|
||||
dark: '#369432',
|
||||
light: '#369432',
|
||||
hc: '#369432'
|
||||
}, nls.localize('statusBarProminentItemHoverBackground', "Status bar prominent items background color when hovering. Prominent items stand out from other status bar entries to indicate importance. The status bar is shown in the bottom of the window."));
|
||||
}, nls.localize('statusBarProminentItemHoverBackground', "Status bar prominent items background color when hovering. Prominent items stand out from other status bar entries to indicate importance. Change mode `Toggle Tab Key Moves Focus` from command palette to see an example. The status bar is shown in the bottom of the window."));
|
||||
|
||||
|
||||
|
||||
@@ -329,71 +366,53 @@ export const TITLE_BAR_BORDER = registerColor('titleBar.border', {
|
||||
|
||||
// < --- Notifications --- >
|
||||
|
||||
export const NOTIFICATIONS_FOREGROUND = registerColor('notification.foreground', {
|
||||
dark: '#EEEEEE',
|
||||
light: '#EEEEEE',
|
||||
hc: '#FFFFFF'
|
||||
}, nls.localize('notificationsForeground', "Notifications foreground color. Notifications slide in from the top of the window."));
|
||||
|
||||
export const NOTIFICATIONS_BACKGROUND = registerColor('notification.background', {
|
||||
dark: '#333333',
|
||||
light: '#2C2C2C',
|
||||
hc: '#000000'
|
||||
}, nls.localize('notificationsBackground', "Notifications background color. Notifications slide in from the top of the window."));
|
||||
|
||||
export const NOTIFICATIONS_BUTTON_BACKGROUND = registerColor('notification.buttonBackground', {
|
||||
dark: '#0E639C',
|
||||
light: '#007ACC',
|
||||
hc: null
|
||||
}, nls.localize('notificationsButtonBackground', "Notifications button background color. Notifications slide in from the top of the window."));
|
||||
|
||||
export const NOTIFICATIONS_BUTTON_HOVER_BACKGROUND = registerColor('notification.buttonHoverBackground', {
|
||||
dark: lighten(NOTIFICATIONS_BUTTON_BACKGROUND, 0.2),
|
||||
light: darken(NOTIFICATIONS_BUTTON_BACKGROUND, 0.2),
|
||||
hc: null
|
||||
}, nls.localize('notificationsButtonHoverBackground', "Notifications button background color when hovering. Notifications slide in from the top of the window."));
|
||||
|
||||
export const NOTIFICATIONS_BUTTON_FOREGROUND = registerColor('notification.buttonForeground', {
|
||||
dark: Color.white,
|
||||
light: Color.white,
|
||||
hc: Color.white
|
||||
}, nls.localize('notificationsButtonForeground', "Notifications button foreground color. Notifications slide in from the top of the window."));
|
||||
|
||||
export const NOTIFICATIONS_INFO_BACKGROUND = registerColor('notification.infoBackground', {
|
||||
dark: '#007acc',
|
||||
light: '#007acc',
|
||||
export const NOTIFICATIONS_CENTER_BORDER = registerColor('notificationCenter.border', {
|
||||
dark: null,
|
||||
light: null,
|
||||
hc: contrastBorder
|
||||
}, nls.localize('notificationsInfoBackground', "Notifications info background color. Notifications slide in from the top of the window."));
|
||||
}, nls.localize('notificationCenterBorder', "Notifications center border color. Notifications slide in from the bottom right of the window."));
|
||||
|
||||
export const NOTIFICATIONS_INFO_FOREGROUND = registerColor('notification.infoForeground', {
|
||||
dark: NOTIFICATIONS_FOREGROUND,
|
||||
light: NOTIFICATIONS_FOREGROUND,
|
||||
hc: null
|
||||
}, nls.localize('notificationsInfoForeground', "Notifications info foreground color. Notifications slide in from the top of the window."));
|
||||
|
||||
export const NOTIFICATIONS_WARNING_BACKGROUND = registerColor('notification.warningBackground', {
|
||||
dark: '#B89500',
|
||||
light: '#B89500',
|
||||
export const NOTIFICATIONS_TOAST_BORDER = registerColor('notificationToast.border', {
|
||||
dark: null,
|
||||
light: null,
|
||||
hc: contrastBorder
|
||||
}, nls.localize('notificationsWarningBackground', "Notifications warning background color. Notifications slide in from the top of the window."));
|
||||
}, nls.localize('notificationToastBorder', "Notification toast border color. Notifications slide in from the bottom right of the window."));
|
||||
|
||||
export const NOTIFICATIONS_WARNING_FOREGROUND = registerColor('notification.warningForeground', {
|
||||
dark: NOTIFICATIONS_FOREGROUND,
|
||||
light: NOTIFICATIONS_FOREGROUND,
|
||||
export const NOTIFICATIONS_FOREGROUND = registerColor('notifications.foreground', {
|
||||
dark: null,
|
||||
light: null,
|
||||
hc: null
|
||||
}, nls.localize('notificationsWarningForeground', "Notifications warning foreground color. Notifications slide in from the top of the window."));
|
||||
}, nls.localize('notificationsForeground', "Notifications foreground color. Notifications slide in from the bottom right of the window."));
|
||||
|
||||
export const NOTIFICATIONS_ERROR_BACKGROUND = registerColor('notification.errorBackground', {
|
||||
dark: '#BE1100',
|
||||
light: '#BE1100',
|
||||
hc: contrastBorder
|
||||
}, nls.localize('notificationsErrorBackground', "Notifications error background color. Notifications slide in from the top of the window."));
|
||||
export const NOTIFICATIONS_BACKGROUND = registerColor('notifications.background', {
|
||||
dark: editorWidgetBackground,
|
||||
light: editorWidgetBackground,
|
||||
hc: editorWidgetBackground
|
||||
}, nls.localize('notificationsBackground', "Notifications background color. Notifications slide in from the bottom right of the window."));
|
||||
|
||||
export const NOTIFICATIONS_ERROR_FOREGROUND = registerColor('notification.errorForeground', {
|
||||
dark: NOTIFICATIONS_FOREGROUND,
|
||||
light: NOTIFICATIONS_FOREGROUND,
|
||||
export const NOTIFICATIONS_LINKS = registerColor('notificationLink.foreground', {
|
||||
dark: textLinkForeground,
|
||||
light: textLinkForeground,
|
||||
hc: textLinkForeground
|
||||
}, nls.localize('notificationsLink', "Notification links foreground color. Notifications slide in from the bottom right of the window."));
|
||||
|
||||
export const NOTIFICATIONS_CENTER_HEADER_FOREGROUND = registerColor('notificationCenterHeader.foreground', {
|
||||
dark: null,
|
||||
light: null,
|
||||
hc: null
|
||||
}, nls.localize('notificationsErrorForeground', "Notifications error foreground color. Notifications slide in from the top of the window."));
|
||||
}, nls.localize('notificationCenterHeaderForeground', "Notifications center header foreground color. Notifications slide in from the bottom right of the window."));
|
||||
|
||||
export const NOTIFICATIONS_CENTER_HEADER_BACKGROUND = registerColor('notificationCenterHeader.background', {
|
||||
dark: lighten(NOTIFICATIONS_BACKGROUND, 0.3),
|
||||
light: darken(NOTIFICATIONS_BACKGROUND, 0.05),
|
||||
hc: NOTIFICATIONS_BACKGROUND
|
||||
}, nls.localize('notificationCenterHeaderBackground', "Notifications center header background color. Notifications slide in from the bottom right of the window."));
|
||||
|
||||
export const NOTIFICATIONS_BORDER = registerColor('notifications.border', {
|
||||
dark: NOTIFICATIONS_CENTER_HEADER_BACKGROUND,
|
||||
light: NOTIFICATIONS_CENTER_HEADER_BACKGROUND,
|
||||
hc: NOTIFICATIONS_CENTER_HEADER_BACKGROUND
|
||||
}, nls.localize('notificationsBorder', "Notifications border color separating from other notifications in the notifications center. Notifications slide in from the bottom right of the window."));
|
||||
|
||||
/**
|
||||
* Base class for all themable workbench components.
|
||||
@@ -443,4 +462,4 @@ export class Themable extends Disposable {
|
||||
|
||||
super.dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4,8 +4,193 @@
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { TPromise } from 'vs/base/common/winjs.base';
|
||||
import Event from 'vs/base/common/event';
|
||||
import { Command } from 'vs/editor/common/modes';
|
||||
import { UriComponents } from 'vs/base/common/uri';
|
||||
import Event, { Emitter } from 'vs/base/common/event';
|
||||
import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey';
|
||||
import { ITreeViewDataProvider } from 'vs/workbench/common/views';
|
||||
import { localize } from 'vs/nls';
|
||||
import { IViewlet } from 'vs/workbench/common/viewlet';
|
||||
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { IDisposable } from 'vs/base/common/lifecycle';
|
||||
import { ThemeIcon } from 'vs/platform/theme/common/themeService';
|
||||
|
||||
export class ViewLocation {
|
||||
|
||||
static readonly Explorer = new ViewLocation('workbench.view.explorer');
|
||||
static readonly Debug = new ViewLocation('workbench.view.debug');
|
||||
static readonly Extensions = new ViewLocation('workbench.view.extensions');
|
||||
|
||||
constructor(private _id: string) {
|
||||
}
|
||||
|
||||
get id(): string {
|
||||
return this._id;
|
||||
}
|
||||
|
||||
static getContributedViewLocation(value: string): ViewLocation {
|
||||
switch (value) {
|
||||
case 'explorer': return ViewLocation.Explorer;
|
||||
case 'debug': return ViewLocation.Debug;
|
||||
}
|
||||
return void 0;
|
||||
}
|
||||
}
|
||||
|
||||
export interface IViewDescriptor {
|
||||
|
||||
readonly id: string;
|
||||
|
||||
readonly name: string;
|
||||
|
||||
readonly location: ViewLocation;
|
||||
|
||||
// TODO do we really need this?!
|
||||
readonly ctor: any;
|
||||
|
||||
readonly when?: ContextKeyExpr;
|
||||
|
||||
readonly order?: number;
|
||||
|
||||
readonly weight?: number;
|
||||
|
||||
readonly collapsed?: boolean;
|
||||
|
||||
readonly canToggleVisibility?: boolean;
|
||||
}
|
||||
|
||||
export interface IViewsRegistry {
|
||||
|
||||
readonly onViewsRegistered: Event<IViewDescriptor[]>;
|
||||
|
||||
readonly onViewsDeregistered: Event<IViewDescriptor[]>;
|
||||
|
||||
registerViews(views: IViewDescriptor[]): void;
|
||||
|
||||
deregisterViews(ids: string[], location: ViewLocation): void;
|
||||
|
||||
getViews(loc: ViewLocation): IViewDescriptor[];
|
||||
|
||||
getAllViews(): IViewDescriptor[];
|
||||
|
||||
getView(id: string): IViewDescriptor;
|
||||
|
||||
}
|
||||
|
||||
export const ViewsRegistry: IViewsRegistry = new class implements IViewsRegistry {
|
||||
|
||||
private _onViewsRegistered: Emitter<IViewDescriptor[]> = new Emitter<IViewDescriptor[]>();
|
||||
readonly onViewsRegistered: Event<IViewDescriptor[]> = this._onViewsRegistered.event;
|
||||
|
||||
private _onViewsDeregistered: Emitter<IViewDescriptor[]> = new Emitter<IViewDescriptor[]>();
|
||||
readonly onViewsDeregistered: Event<IViewDescriptor[]> = this._onViewsDeregistered.event;
|
||||
|
||||
private _viewLocations: ViewLocation[] = [];
|
||||
private _views: Map<ViewLocation, IViewDescriptor[]> = new Map<ViewLocation, IViewDescriptor[]>();
|
||||
|
||||
registerViews(viewDescriptors: IViewDescriptor[]): void {
|
||||
if (viewDescriptors.length) {
|
||||
for (const viewDescriptor of viewDescriptors) {
|
||||
let views = this._views.get(viewDescriptor.location);
|
||||
if (!views) {
|
||||
views = [];
|
||||
this._views.set(viewDescriptor.location, views);
|
||||
this._viewLocations.push(viewDescriptor.location);
|
||||
}
|
||||
if (views.some(v => v.id === viewDescriptor.id)) {
|
||||
throw new Error(localize('duplicateId', "A view with id '{0}' is already registered in the location '{1}'", viewDescriptor.id, viewDescriptor.location.id));
|
||||
}
|
||||
views.push(viewDescriptor);
|
||||
}
|
||||
this._onViewsRegistered.fire(viewDescriptors);
|
||||
}
|
||||
}
|
||||
|
||||
deregisterViews(ids: string[], location: ViewLocation): void {
|
||||
const views = this._views.get(location);
|
||||
|
||||
if (!views) {
|
||||
return;
|
||||
}
|
||||
|
||||
const viewsToDeregister = views.filter(view => ids.indexOf(view.id) !== -1);
|
||||
|
||||
if (viewsToDeregister.length) {
|
||||
const remaningViews = views.filter(view => ids.indexOf(view.id) === -1);
|
||||
if (remaningViews.length) {
|
||||
this._views.set(location, remaningViews);
|
||||
} else {
|
||||
this._views.delete(location);
|
||||
this._viewLocations.splice(this._viewLocations.indexOf(location), 1);
|
||||
}
|
||||
}
|
||||
|
||||
this._onViewsDeregistered.fire(viewsToDeregister);
|
||||
}
|
||||
|
||||
getViews(loc: ViewLocation): IViewDescriptor[] {
|
||||
return this._views.get(loc) || [];
|
||||
}
|
||||
|
||||
getAllViews(): IViewDescriptor[] {
|
||||
const result: IViewDescriptor[] = [];
|
||||
this._views.forEach(views => result.push(...views));
|
||||
return result;
|
||||
}
|
||||
|
||||
getView(id: string): IViewDescriptor {
|
||||
for (const viewLocation of this._viewLocations) {
|
||||
const viewDescriptor = (this._views.get(viewLocation) || []).filter(v => v.id === id)[0];
|
||||
if (viewDescriptor) {
|
||||
return viewDescriptor;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
export interface IViewsViewlet extends IViewlet {
|
||||
|
||||
openView(id: string, focus?: boolean): TPromise<void>;
|
||||
|
||||
}
|
||||
|
||||
// Custom views
|
||||
|
||||
export interface ITreeViewer extends IDisposable {
|
||||
|
||||
dataProvider: ITreeViewDataProvider;
|
||||
|
||||
refresh(treeItems?: ITreeItem[]): TPromise<void>;
|
||||
|
||||
setVisibility(visible: boolean): void;
|
||||
|
||||
focus(): void;
|
||||
|
||||
layout(height: number): void;
|
||||
|
||||
show(container: HTMLElement);
|
||||
|
||||
getOptimalWidth(): number;
|
||||
|
||||
reveal(item: ITreeItem, parentChain: ITreeItem[], options: { select?: boolean }): TPromise<void>;
|
||||
}
|
||||
|
||||
export interface ICustomViewDescriptor extends IViewDescriptor {
|
||||
|
||||
treeView?: boolean;
|
||||
|
||||
}
|
||||
|
||||
export const ICustomViewsService = createDecorator<ICustomViewsService>('customViewsService');
|
||||
|
||||
export interface ICustomViewsService {
|
||||
_serviceBrand: any;
|
||||
|
||||
getTreeViewer(id: string): ITreeViewer;
|
||||
|
||||
openView(id: string, focus?: boolean): TPromise<void>;
|
||||
}
|
||||
|
||||
export type TreeViewItemHandleArg = {
|
||||
$treeViewId: string,
|
||||
@@ -24,19 +209,26 @@ export interface ITreeItem {
|
||||
|
||||
parentHandle: string;
|
||||
|
||||
label: string;
|
||||
collapsibleState: TreeItemCollapsibleState;
|
||||
|
||||
label?: string;
|
||||
|
||||
icon?: string;
|
||||
|
||||
iconDark?: string;
|
||||
|
||||
themeIcon?: ThemeIcon;
|
||||
|
||||
resourceUri?: UriComponents;
|
||||
|
||||
tooltip?: string;
|
||||
|
||||
contextValue?: string;
|
||||
|
||||
command?: Command;
|
||||
|
||||
children?: ITreeItem[];
|
||||
|
||||
collapsibleState?: TreeItemCollapsibleState;
|
||||
}
|
||||
|
||||
export interface ITreeViewDataProvider {
|
||||
@@ -45,7 +237,5 @@ export interface ITreeViewDataProvider {
|
||||
|
||||
onDispose: Event<void>;
|
||||
|
||||
getElements(): TPromise<ITreeItem[]>;
|
||||
|
||||
getChildren(element: ITreeItem): TPromise<ITreeItem[]>;
|
||||
getChildren(element?: ITreeItem): TPromise<ITreeItem[]>;
|
||||
}
|
||||
Reference in New Issue
Block a user