mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-02-11 02:32:35 -05:00
SQL Operations Studio Public Preview 1 (0.23) release source code
This commit is contained in:
191
src/vs/workbench/common/actionRegistry.ts
Normal file
191
src/vs/workbench/common/actionRegistry.ts
Normal file
@@ -0,0 +1,191 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* 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 { TPromise } from 'vs/base/common/winjs.base';
|
||||
import collections = require('vs/base/common/collections');
|
||||
import { Registry } from 'vs/platform/registry/common/platform';
|
||||
import { IAction } from 'vs/base/common/actions';
|
||||
import { KeybindingsRegistry, ICommandAndKeybindingRule } from 'vs/platform/keybinding/common/keybindingsRegistry';
|
||||
import { IPartService } from 'vs/workbench/services/part/common/partService';
|
||||
import { ICommandHandler } from 'vs/platform/commands/common/commands';
|
||||
import { SyncActionDescriptor } from 'vs/platform/actions/common/actions';
|
||||
import { IMessageService } from 'vs/platform/message/common/message';
|
||||
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import Severity from 'vs/base/common/severity';
|
||||
|
||||
export const Extensions = {
|
||||
WorkbenchActions: 'workbench.contributions.actions'
|
||||
};
|
||||
|
||||
export interface IActionProvider {
|
||||
getActions(): IAction[];
|
||||
}
|
||||
|
||||
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.
|
||||
*/
|
||||
registerWorkbenchAction(descriptor: SyncActionDescriptor, alias: string, category?: string): void;
|
||||
|
||||
/**
|
||||
* Unregisters a workbench action from the platform.
|
||||
*/
|
||||
unregisterWorkbenchAction(id: string): boolean;
|
||||
|
||||
/**
|
||||
* Returns the workbench action descriptor for the given id or null if none.
|
||||
*/
|
||||
getWorkbenchAction(id: string): SyncActionDescriptor;
|
||||
|
||||
/**
|
||||
* Returns an array of registered workbench actions known to the platform.
|
||||
*/
|
||||
getWorkbenchActions(): SyncActionDescriptor[];
|
||||
|
||||
/**
|
||||
* Returns the alias associated with the given action or null if none.
|
||||
*/
|
||||
getAlias(actionId: string): string;
|
||||
|
||||
/**
|
||||
* Returns the category for the given action or null if none.
|
||||
*/
|
||||
getCategory(actionId: string): string;
|
||||
}
|
||||
|
||||
interface IActionMeta {
|
||||
alias: string;
|
||||
category?: string;
|
||||
}
|
||||
|
||||
class WorkbenchActionRegistry implements IWorkbenchActionRegistry {
|
||||
private workbenchActions: collections.IStringDictionary<SyncActionDescriptor>;
|
||||
private mapActionIdToMeta: { [id: string]: IActionMeta; };
|
||||
|
||||
constructor() {
|
||||
this.workbenchActions = Object.create(null);
|
||||
this.mapActionIdToMeta = Object.create(null);
|
||||
}
|
||||
|
||||
public registerWorkbenchAction(descriptor: SyncActionDescriptor, alias: string, category?: string): void {
|
||||
if (!this.workbenchActions[descriptor.id]) {
|
||||
this.workbenchActions[descriptor.id] = descriptor;
|
||||
registerWorkbenchCommandFromAction(descriptor);
|
||||
|
||||
let meta: IActionMeta = { alias };
|
||||
if (typeof category === 'string') {
|
||||
meta.category = category;
|
||||
}
|
||||
|
||||
this.mapActionIdToMeta[descriptor.id] = meta;
|
||||
}
|
||||
}
|
||||
|
||||
public unregisterWorkbenchAction(id: string): boolean {
|
||||
if (!this.workbenchActions[id]) {
|
||||
return false;
|
||||
}
|
||||
|
||||
delete this.workbenchActions[id];
|
||||
delete this.mapActionIdToMeta[id];
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public getWorkbenchAction(id: string): SyncActionDescriptor {
|
||||
return this.workbenchActions[id] || null;
|
||||
}
|
||||
|
||||
public getCategory(id: string): string {
|
||||
return (this.mapActionIdToMeta[id] && this.mapActionIdToMeta[id].category) || null;
|
||||
}
|
||||
|
||||
public getAlias(id: string): string {
|
||||
return (this.mapActionIdToMeta[id] && this.mapActionIdToMeta[id].alias) || null;
|
||||
}
|
||||
|
||||
public getWorkbenchActions(): SyncActionDescriptor[] {
|
||||
return collections.values(this.workbenchActions);
|
||||
}
|
||||
|
||||
public setWorkbenchActions(actions: SyncActionDescriptor[]): void {
|
||||
this.workbenchActions = Object.create(null);
|
||||
this.mapActionIdToMeta = Object.create(null);
|
||||
|
||||
actions.forEach(action => this.registerWorkbenchAction(action, ''), this);
|
||||
}
|
||||
}
|
||||
|
||||
Registry.add(Extensions.WorkbenchActions, new WorkbenchActionRegistry());
|
||||
|
||||
function registerWorkbenchCommandFromAction(descriptor: SyncActionDescriptor): void {
|
||||
let when = descriptor.keybindingContext;
|
||||
let weight = (typeof descriptor.keybindingWeight === 'undefined' ? KeybindingsRegistry.WEIGHT.workbenchContrib() : descriptor.keybindingWeight);
|
||||
let keybindings = descriptor.keybindings;
|
||||
|
||||
let desc: ICommandAndKeybindingRule = {
|
||||
id: descriptor.id,
|
||||
handler: createCommandHandler(descriptor),
|
||||
weight: weight,
|
||||
when: when,
|
||||
primary: keybindings && keybindings.primary,
|
||||
secondary: keybindings && keybindings.secondary,
|
||||
win: keybindings && keybindings.win,
|
||||
mac: keybindings && keybindings.mac,
|
||||
linux: keybindings && keybindings.linux
|
||||
};
|
||||
|
||||
KeybindingsRegistry.registerCommandAndKeybindingRule(desc);
|
||||
}
|
||||
|
||||
export function createCommandHandler(descriptor: SyncActionDescriptor): ICommandHandler {
|
||||
return (accessor, args) => {
|
||||
|
||||
let messageService = accessor.get(IMessageService);
|
||||
let instantiationService = accessor.get(IInstantiationService);
|
||||
let telemetryService = accessor.get(ITelemetryService);
|
||||
let partService = accessor.get(IPartService);
|
||||
|
||||
TPromise.as(triggerAndDisposeAction(instantiationService, telemetryService, partService, descriptor, args)).done(null, (err) => {
|
||||
messageService.show(Severity.Error, err);
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
export function triggerAndDisposeAction(instantitationService: IInstantiationService, telemetryService: ITelemetryService, partService: IPartService, descriptor: SyncActionDescriptor, args: any): TPromise<any> {
|
||||
let actionInstance = instantitationService.createInstance(descriptor.syncDescriptor);
|
||||
actionInstance.label = descriptor.label || actionInstance.label;
|
||||
|
||||
// don't run the action when not enabled
|
||||
if (!actionInstance.enabled) {
|
||||
actionInstance.dispose();
|
||||
|
||||
return void 0;
|
||||
}
|
||||
|
||||
const from = args && args.from || 'keybinding';
|
||||
if (telemetryService) {
|
||||
telemetryService.publicLog('workbenchActionExecuted', { id: actionInstance.id, from });
|
||||
}
|
||||
|
||||
// run action when workbench is created
|
||||
return partService.joinCreation().then(() => {
|
||||
try {
|
||||
return TPromise.as(actionInstance.run(undefined, { from })).then(() => {
|
||||
actionInstance.dispose();
|
||||
}, (err) => {
|
||||
actionInstance.dispose();
|
||||
return TPromise.wrapError(err);
|
||||
});
|
||||
} catch (err) {
|
||||
actionInstance.dispose();
|
||||
return TPromise.wrapError(err);
|
||||
}
|
||||
});
|
||||
}
|
||||
81
src/vs/workbench/common/component.ts
Normal file
81
src/vs/workbench/common/component.ts
Normal file
@@ -0,0 +1,81 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* 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 { IDisposable } from 'vs/base/common/lifecycle';
|
||||
import { Scope, Memento } from 'vs/workbench/common/memento';
|
||||
import { IStorageService } from 'vs/platform/storage/common/storage';
|
||||
import { IThemeService } from 'vs/platform/theme/common/themeService';
|
||||
import { Themable } from 'vs/workbench/common/theme';
|
||||
|
||||
/**
|
||||
* Base class of any core/ui component in the workbench. Examples include services, extensions, parts, viewlets and quick open.
|
||||
* Provides some convinience methods to participate in the workbench lifecycle (dispose, shutdown) and
|
||||
* loading and saving settings through memento.
|
||||
*/
|
||||
export interface IWorkbenchComponent extends IDisposable {
|
||||
|
||||
/**
|
||||
* The unique identifier of this component.
|
||||
*/
|
||||
getId(): string;
|
||||
|
||||
/**
|
||||
* Called when the browser containing the container is closed.
|
||||
*
|
||||
* Use this function to store settings that you want to restore next time. Should not be used to free resources
|
||||
* because dispose() is being called for this purpose and shutdown() has a chance to be vetoed by the user.
|
||||
*/
|
||||
shutdown(): void;
|
||||
}
|
||||
|
||||
export class Component extends Themable implements IWorkbenchComponent {
|
||||
private id: string;
|
||||
private componentMemento: Memento;
|
||||
|
||||
constructor(
|
||||
id: string,
|
||||
themeService: IThemeService
|
||||
) {
|
||||
super(themeService);
|
||||
|
||||
this.id = id;
|
||||
this.componentMemento = new Memento(this.id);
|
||||
}
|
||||
|
||||
public getId(): string {
|
||||
return this.id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a JSON Object that represents the data of this memento. The optional
|
||||
* parameter scope allows to specify the scope of the memento to load. If not
|
||||
* provided, the scope will be global, Scope.WORKSPACE can be used to
|
||||
* scope the memento to the workspace.
|
||||
*
|
||||
* Mementos are shared across components with the same id. This means that multiple components
|
||||
* with the same id will store data into the same data structure.
|
||||
*/
|
||||
protected getMemento(storageService: IStorageService, scope: Scope = Scope.GLOBAL): object {
|
||||
return this.componentMemento.getMemento(storageService, scope);
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves all data of the mementos that have been loaded to the local storage. This includes
|
||||
* global and workspace scope.
|
||||
*
|
||||
* Mementos are shared across components with the same id. This means that multiple components
|
||||
* with the same id will store data into the same data structure.
|
||||
*/
|
||||
protected saveMemento(): void {
|
||||
this.componentMemento.saveMemento();
|
||||
}
|
||||
|
||||
public shutdown(): void {
|
||||
|
||||
// Save Memento
|
||||
this.saveMemento();
|
||||
}
|
||||
}
|
||||
50
src/vs/workbench/common/composite.ts
Normal file
50
src/vs/workbench/common/composite.ts
Normal file
@@ -0,0 +1,50 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { IAction, IActionItem } from 'vs/base/common/actions';
|
||||
import { IEditorControl } from 'vs/platform/editor/common/editor';
|
||||
|
||||
export interface IComposite {
|
||||
|
||||
/**
|
||||
* Returns the unique identifier of this composite.
|
||||
*/
|
||||
getId(): string;
|
||||
|
||||
/**
|
||||
* Returns the name of this composite to show in the title area.
|
||||
*/
|
||||
getTitle(): string;
|
||||
|
||||
/**
|
||||
* Returns the primary actions of the composite.
|
||||
*/
|
||||
getActions(): IAction[];
|
||||
|
||||
/**
|
||||
* Returns the secondary actions of the composite.
|
||||
*/
|
||||
getSecondaryActions(): IAction[];
|
||||
|
||||
/**
|
||||
* Returns an array of actions to show in the context menu of the composite
|
||||
*/
|
||||
getContextMenuActions(): IAction[];
|
||||
|
||||
/**
|
||||
* Returns the action item for a specific action.
|
||||
*/
|
||||
getActionItem(action: IAction): IActionItem;
|
||||
|
||||
/**
|
||||
* Returns the underlying control of this composite.
|
||||
*/
|
||||
getControl(): IEditorControl;
|
||||
|
||||
/**
|
||||
* Asks the underlying control to focus.
|
||||
*/
|
||||
focus(): void;
|
||||
}
|
||||
60
src/vs/workbench/common/contributions.ts
Normal file
60
src/vs/workbench/common/contributions.ts
Normal file
@@ -0,0 +1,60 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* 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 { Registry, BaseRegistry } from 'vs/platform/registry/common/platform';
|
||||
import { IInstantiationService, IConstructorSignature0 } from 'vs/platform/instantiation/common/instantiation';
|
||||
|
||||
// --- Workbench Contribution Registry
|
||||
|
||||
/**
|
||||
* A workbench contribution that will be loaded when the workbench starts and disposed when the workbench shuts down.
|
||||
*/
|
||||
export interface IWorkbenchContribution {
|
||||
|
||||
/**
|
||||
* The unique identifier of this workbench contribution.
|
||||
*/
|
||||
getId(): string;
|
||||
}
|
||||
|
||||
export namespace Extensions {
|
||||
export const Workbench = 'workbench.contributions.kind';
|
||||
}
|
||||
|
||||
export type IWorkbenchContributionSignature = IConstructorSignature0<IWorkbenchContribution>;
|
||||
|
||||
export interface IWorkbenchContributionsRegistry {
|
||||
|
||||
/**
|
||||
* Registers a workbench contribution to the platform that will be loaded when the workbench starts and disposed when
|
||||
* the workbench shuts down.
|
||||
*/
|
||||
registerWorkbenchContribution(contribution: IWorkbenchContributionSignature): void;
|
||||
|
||||
/**
|
||||
* Returns all workbench contributions that are known to the platform.
|
||||
*/
|
||||
getWorkbenchContributions(): IWorkbenchContribution[];
|
||||
|
||||
setInstantiationService(service: IInstantiationService): void;
|
||||
}
|
||||
|
||||
class WorkbenchContributionsRegistry extends BaseRegistry<IWorkbenchContribution> implements IWorkbenchContributionsRegistry {
|
||||
|
||||
public registerWorkbenchContribution(ctor: IWorkbenchContributionSignature): void {
|
||||
super._register(ctor);
|
||||
}
|
||||
|
||||
public getWorkbenchContributions(): IWorkbenchContribution[] {
|
||||
return super._getInstances();
|
||||
}
|
||||
|
||||
public setWorkbenchContributions(contributions: IWorkbenchContribution[]): void {
|
||||
super._setInstances(contributions);
|
||||
}
|
||||
}
|
||||
|
||||
Registry.add(Extensions.Workbench, new WorkbenchContributionsRegistry());
|
||||
898
src/vs/workbench/common/editor.ts
Normal file
898
src/vs/workbench/common/editor.ts
Normal file
@@ -0,0 +1,898 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* 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 { TPromise } from 'vs/base/common/winjs.base';
|
||||
import Event, { Emitter, once } from 'vs/base/common/event';
|
||||
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 } from 'vs/platform/editor/common/editor';
|
||||
import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors';
|
||||
import { IInstantiationService, IConstructorSignature0 } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { RawContextKey } from 'vs/platform/contextkey/common/contextkey';
|
||||
|
||||
export const TextCompareEditorVisible = new RawContextKey<boolean>('textCompareEditorVisible', false);
|
||||
|
||||
export enum ConfirmResult {
|
||||
SAVE,
|
||||
DONT_SAVE,
|
||||
CANCEL
|
||||
}
|
||||
|
||||
export interface IEditorDescriptor {
|
||||
|
||||
getId(): string;
|
||||
|
||||
getName(): string;
|
||||
|
||||
describes(obj: any): boolean;
|
||||
}
|
||||
|
||||
export const Extensions = {
|
||||
Editors: 'workbench.contributions.editors'
|
||||
};
|
||||
|
||||
/**
|
||||
* Text diff editor id.
|
||||
*/
|
||||
export const TEXT_DIFF_EDITOR_ID = 'workbench.editors.textDiffEditor';
|
||||
|
||||
/**
|
||||
* Binary diff editor id.
|
||||
*/
|
||||
export const BINARY_DIFF_EDITOR_ID = 'workbench.editors.binaryResourceDiffEditor';
|
||||
|
||||
export interface IFileInputFactory {
|
||||
createFileInput(resource: URI, encoding: string, instantiationService: IInstantiationService): IFileEditorInput;
|
||||
}
|
||||
|
||||
export interface IEditorRegistry {
|
||||
|
||||
/**
|
||||
* Registers an editor to the platform for the given input type. The second parameter also supports an
|
||||
* array of input classes to be passed in. If the more than one editor is registered for the same editor
|
||||
* input, the input itself will be asked which editor it prefers if this method is provided. Otherwise
|
||||
* the first editor in the list will be returned.
|
||||
*
|
||||
* @param editorInputDescriptor a constructor function that returns an instance of EditorInput for which the
|
||||
* registered editor should be used for.
|
||||
*/
|
||||
registerEditor(descriptor: IEditorDescriptor, editorInputDescriptor: SyncDescriptor<EditorInput>): void;
|
||||
registerEditor(descriptor: IEditorDescriptor, editorInputDescriptor: SyncDescriptor<EditorInput>[]): void;
|
||||
|
||||
/**
|
||||
* Returns the editor descriptor for the given input or null if none.
|
||||
*/
|
||||
getEditor(input: EditorInput): IEditorDescriptor;
|
||||
|
||||
/**
|
||||
* Returns the editor descriptor for the given identifier or null if none.
|
||||
*/
|
||||
getEditorById(editorId: string): IEditorDescriptor;
|
||||
|
||||
/**
|
||||
* Returns an array of registered editors known to the platform.
|
||||
*/
|
||||
getEditors(): IEditorDescriptor[];
|
||||
|
||||
/**
|
||||
* Registers the file input factory to use for file inputs.
|
||||
*/
|
||||
registerFileInputFactory(factory: IFileInputFactory): void;
|
||||
|
||||
/**
|
||||
* Returns the file input factory to use for file inputs.
|
||||
*/
|
||||
getFileInputFactory(): IFileInputFactory;
|
||||
|
||||
/**
|
||||
* Registers a editor input factory for the given editor input to the registry. An editor input factory
|
||||
* is capable of serializing and deserializing editor inputs from string data.
|
||||
*
|
||||
* @param editorInputId the identifier of the editor input
|
||||
* @param factory the editor input factory for serialization/deserialization
|
||||
*/
|
||||
registerEditorInputFactory(editorInputId: string, ctor: IConstructorSignature0<IEditorInputFactory>): void;
|
||||
|
||||
/**
|
||||
* Returns the editor input factory for the given editor input.
|
||||
*
|
||||
* @param editorInputId the identifier of the editor input
|
||||
*/
|
||||
getEditorInputFactory(editorInputId: string): IEditorInputFactory;
|
||||
|
||||
setInstantiationService(service: IInstantiationService): void;
|
||||
}
|
||||
|
||||
export interface IEditorInputFactory {
|
||||
|
||||
/**
|
||||
* Returns a string representation of the provided editor input that contains enough information
|
||||
* to deserialize back to the original editor input from the deserialize() method.
|
||||
*/
|
||||
serialize(editorInput: EditorInput): string;
|
||||
|
||||
/**
|
||||
* Returns an editor input from the provided serialized form of the editor input. This form matches
|
||||
* the value returned from the serialize() method.
|
||||
*/
|
||||
deserialize(instantiationService: IInstantiationService, serializedEditorInput: string): EditorInput;
|
||||
}
|
||||
|
||||
/**
|
||||
* Editor inputs are lightweight objects that can be passed to the workbench API to open inside the editor part.
|
||||
* Each editor input is mapped to an editor that is capable of opening it through the Platform facade.
|
||||
*/
|
||||
export abstract class EditorInput implements IEditorInput {
|
||||
private _onDispose: Emitter<void>;
|
||||
protected _onDidChangeDirty: Emitter<void>;
|
||||
protected _onDidChangeLabel: Emitter<void>;
|
||||
|
||||
private disposed: boolean;
|
||||
|
||||
constructor() {
|
||||
this._onDidChangeDirty = new Emitter<void>();
|
||||
this._onDidChangeLabel = new Emitter<void>();
|
||||
this._onDispose = new Emitter<void>();
|
||||
|
||||
this.disposed = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fired when the dirty state of this input changes.
|
||||
*/
|
||||
public get onDidChangeDirty(): Event<void> {
|
||||
return this._onDidChangeDirty.event;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fired when the label this input changes.
|
||||
*/
|
||||
public get onDidChangeLabel(): Event<void> {
|
||||
return this._onDidChangeLabel.event;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fired when the model gets disposed.
|
||||
*/
|
||||
public get onDispose(): Event<void> {
|
||||
return this._onDispose.event;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the name of this input that can be shown to the user. Examples include showing the name of the input
|
||||
* above the editor area when the input is shown.
|
||||
*/
|
||||
public getName(): string {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the description of this input that can be shown to the user. Examples include showing the description of
|
||||
* the input above the editor area to the side of the name of the input.
|
||||
*/
|
||||
public getDescription(): string {
|
||||
return null;
|
||||
}
|
||||
|
||||
public getTitle(verbosity?: Verbosity): string {
|
||||
return this.getName();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the unique type identifier of this input.
|
||||
*/
|
||||
public abstract getTypeId(): string;
|
||||
|
||||
/**
|
||||
* Returns the preferred editor for this input. A list of candidate editors is passed in that whee registered
|
||||
* for the input. This allows subclasses to decide late which editor to use for the input on a case by case basis.
|
||||
*/
|
||||
public getPreferredEditorId(candidates: string[]): string {
|
||||
if (candidates && candidates.length > 0) {
|
||||
return candidates[0];
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a descriptor suitable for telemetry events or null if none is available.
|
||||
*
|
||||
* Subclasses should extend if they can contribute.
|
||||
*/
|
||||
public getTelemetryDescriptor(): object {
|
||||
return { typeId: this.getTypeId() };
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a type of EditorModel that represents the resolved input. Subclasses should
|
||||
* override to provide a meaningful model. The optional second argument allows to specify
|
||||
* if the EditorModel should be refreshed before returning it. Depending on the implementation
|
||||
* this could mean to refresh the editor model contents with the version from disk.
|
||||
*/
|
||||
public abstract resolve(refresh?: boolean): TPromise<IEditorModel>;
|
||||
|
||||
/**
|
||||
* An editor that is dirty will be asked to be saved once it closes.
|
||||
*/
|
||||
public isDirty(): boolean {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves the editor if it is dirty. Subclasses return a promise with a boolean indicating the success of the operation.
|
||||
*/
|
||||
public save(): TPromise<boolean> {
|
||||
return TPromise.as(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverts the editor if it is dirty. Subclasses return a promise with a boolean indicating the success of the operation.
|
||||
*/
|
||||
public revert(): TPromise<boolean> {
|
||||
return TPromise.as(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when this input is no longer opened in any editor. Subclasses can free resources as needed.
|
||||
*/
|
||||
public close(): void {
|
||||
this.dispose();
|
||||
}
|
||||
|
||||
/**
|
||||
* Subclasses can set this to false if it does not make sense to split the editor input.
|
||||
*/
|
||||
public supportsSplitEditor(): boolean {
|
||||
// {{SQL CARBON EDIT}}
|
||||
return false; // TODO reenable when multiple Angular components of the same type can be open simultaneously
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if this input is identical to the otherInput.
|
||||
*/
|
||||
public matches(otherInput: any): boolean {
|
||||
return this === otherInput;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when an editor input is no longer needed. Allows to free up any resources taken by
|
||||
* resolving the editor input.
|
||||
*/
|
||||
public dispose(): void {
|
||||
this.disposed = true;
|
||||
this._onDispose.fire();
|
||||
|
||||
this._onDidChangeDirty.dispose();
|
||||
this._onDidChangeLabel.dispose();
|
||||
this._onDispose.dispose();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether this input was disposed or not.
|
||||
*/
|
||||
public isDisposed(): boolean {
|
||||
return this.disposed;
|
||||
}
|
||||
}
|
||||
|
||||
export enum EncodingMode {
|
||||
|
||||
/**
|
||||
* Instructs the encoding support to encode the current input with the provided encoding
|
||||
*/
|
||||
Encode,
|
||||
|
||||
/**
|
||||
* Instructs the encoding support to decode the current input with the provided encoding
|
||||
*/
|
||||
Decode
|
||||
}
|
||||
|
||||
export interface IEncodingSupport {
|
||||
|
||||
/**
|
||||
* Gets the encoding of the input if known.
|
||||
*/
|
||||
getEncoding(): string;
|
||||
|
||||
/**
|
||||
* Sets the encoding for the input for saving.
|
||||
*/
|
||||
setEncoding(encoding: string, mode: EncodingMode): void;
|
||||
}
|
||||
|
||||
/**
|
||||
* This is a tagging interface to declare an editor input being capable of dealing with files. It is only used in the editor registry
|
||||
* to register this kind of input to the platform.
|
||||
*/
|
||||
export interface IFileEditorInput extends IEditorInput, IEncodingSupport {
|
||||
|
||||
/**
|
||||
* Gets the absolute file resource URI this input is about.
|
||||
*/
|
||||
getResource(): URI;
|
||||
|
||||
/**
|
||||
* Sets the preferred encodingt to use for this input.
|
||||
*/
|
||||
setPreferredEncoding(encoding: string): void;
|
||||
|
||||
/**
|
||||
* Forces this file input to open as binary instead of text.
|
||||
*/
|
||||
setForceOpenAsBinary(): void;
|
||||
}
|
||||
|
||||
/**
|
||||
* Side by side editor inputs that have a master and details side.
|
||||
*/
|
||||
export class SideBySideEditorInput extends EditorInput {
|
||||
|
||||
public static ID: string = 'workbench.editorinputs.sidebysideEditorInput';
|
||||
|
||||
private _toUnbind: IDisposable[];
|
||||
|
||||
constructor(private name: string, private description: string, private _details: EditorInput, private _master: EditorInput) {
|
||||
super();
|
||||
this._toUnbind = [];
|
||||
this.registerListeners();
|
||||
}
|
||||
|
||||
get master(): EditorInput {
|
||||
return this._master;
|
||||
}
|
||||
|
||||
get details(): EditorInput {
|
||||
return this._details;
|
||||
}
|
||||
|
||||
public isDirty(): boolean {
|
||||
return this.master.isDirty();
|
||||
}
|
||||
|
||||
public confirmSave(): ConfirmResult {
|
||||
return this.master.confirmSave();
|
||||
}
|
||||
|
||||
public save(): TPromise<boolean> {
|
||||
return this.master.save();
|
||||
}
|
||||
|
||||
public revert(): TPromise<boolean> {
|
||||
return this.master.revert();
|
||||
}
|
||||
|
||||
public getTelemetryDescriptor(): object {
|
||||
const descriptor = this.master.getTelemetryDescriptor();
|
||||
return objects.assign(descriptor, super.getTelemetryDescriptor());
|
||||
}
|
||||
|
||||
private registerListeners(): void {
|
||||
|
||||
// When the details or master input gets disposed, dispose this diff editor input
|
||||
const onceDetailsDisposed = once(this.details.onDispose);
|
||||
this._toUnbind.push(onceDetailsDisposed(() => {
|
||||
if (!this.isDisposed()) {
|
||||
this.dispose();
|
||||
}
|
||||
}));
|
||||
|
||||
const onceMasterDisposed = once(this.master.onDispose);
|
||||
this._toUnbind.push(onceMasterDisposed(() => {
|
||||
if (!this.isDisposed()) {
|
||||
this.dispose();
|
||||
}
|
||||
}));
|
||||
|
||||
// Reemit some events from the master side to the outside
|
||||
this._toUnbind.push(this.master.onDidChangeDirty(() => this._onDidChangeDirty.fire()));
|
||||
this._toUnbind.push(this.master.onDidChangeLabel(() => this._onDidChangeLabel.fire()));
|
||||
}
|
||||
|
||||
public get toUnbind() {
|
||||
return this._toUnbind;
|
||||
}
|
||||
|
||||
public resolve(refresh?: boolean): TPromise<EditorModel> {
|
||||
return TPromise.as(null);
|
||||
}
|
||||
|
||||
getTypeId(): string {
|
||||
return SideBySideEditorInput.ID;
|
||||
}
|
||||
|
||||
public getName(): string {
|
||||
return this.name;
|
||||
}
|
||||
|
||||
public getDescription(): string {
|
||||
return this.description;
|
||||
}
|
||||
|
||||
public supportsSplitEditor(): boolean {
|
||||
return false;
|
||||
}
|
||||
|
||||
public matches(otherInput: any): boolean {
|
||||
if (super.matches(otherInput) === true) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (otherInput) {
|
||||
if (!(otherInput instanceof SideBySideEditorInput)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const otherDiffInput = <SideBySideEditorInput>otherInput;
|
||||
return this.details.matches(otherDiffInput.details) && this.master.matches(otherDiffInput.master);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public dispose(): void {
|
||||
this._toUnbind = dispose(this._toUnbind);
|
||||
super.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
export interface ITextEditorModel extends IEditorModel {
|
||||
textEditorModel: IModel;
|
||||
}
|
||||
|
||||
/**
|
||||
* The editor model is the heavyweight counterpart of editor input. Depending on the editor input, it
|
||||
* connects to the disk to retrieve content and may allow for saving it back or reverting it. Editor models
|
||||
* are typically cached for some while because they are expensive to construct.
|
||||
*/
|
||||
export class EditorModel extends Disposable implements IEditorModel {
|
||||
private _onDispose: Emitter<void>;
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
this._onDispose = new Emitter<void>();
|
||||
}
|
||||
|
||||
/**
|
||||
* Fired when the model gets disposed.
|
||||
*/
|
||||
public get onDispose(): Event<void> {
|
||||
return this._onDispose.event;
|
||||
}
|
||||
|
||||
/**
|
||||
* Causes this model to load returning a promise when loading is completed.
|
||||
*/
|
||||
public load(): TPromise<EditorModel> {
|
||||
return TPromise.as(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether this model was loaded or not.
|
||||
*/
|
||||
public isResolved(): boolean {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Subclasses should implement to free resources that have been claimed through loading.
|
||||
*/
|
||||
public dispose(): void {
|
||||
this._onDispose.fire();
|
||||
this._onDispose.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The editor options is the base class of options that can be passed in when opening an editor.
|
||||
*/
|
||||
export class EditorOptions implements IEditorOptions {
|
||||
|
||||
/**
|
||||
* Helper to create EditorOptions inline.
|
||||
*/
|
||||
public static create(settings: IEditorOptions): EditorOptions {
|
||||
const options = new EditorOptions();
|
||||
|
||||
options.preserveFocus = settings.preserveFocus;
|
||||
options.forceOpen = settings.forceOpen;
|
||||
options.revealIfVisible = settings.revealIfVisible;
|
||||
options.revealIfOpened = settings.revealIfOpened;
|
||||
options.pinned = settings.pinned;
|
||||
options.index = settings.index;
|
||||
options.inactive = settings.inactive;
|
||||
|
||||
return options;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tells the editor to not receive keyboard focus when the editor is being opened. By default,
|
||||
* the editor will receive keyboard focus on open.
|
||||
*/
|
||||
public preserveFocus: boolean;
|
||||
|
||||
/**
|
||||
* Tells the editor to replace the editor input in the editor even if it is identical to the one
|
||||
* already showing. By default, the editor will not replace the input if it is identical to the
|
||||
* one showing.
|
||||
*/
|
||||
public forceOpen: boolean;
|
||||
|
||||
/**
|
||||
* Will reveal the editor if it is already opened and visible in any of the opened editor groups.
|
||||
*/
|
||||
public revealIfVisible: boolean;
|
||||
|
||||
/**
|
||||
* Will reveal the editor if it is already opened (even when not visible) in any of the opened editor groups.
|
||||
*/
|
||||
public revealIfOpened: boolean;
|
||||
|
||||
/**
|
||||
* An editor that is pinned remains in the editor stack even when another editor is being opened.
|
||||
* An editor that is not pinned will always get replaced by another editor that is not pinned.
|
||||
*/
|
||||
public pinned: boolean;
|
||||
|
||||
/**
|
||||
* The index in the document stack where to insert the editor into when opening.
|
||||
*/
|
||||
public index: number;
|
||||
|
||||
/**
|
||||
* An active editor that is opened will show its contents directly. Set to true to open an editor
|
||||
* in the background.
|
||||
*/
|
||||
public inactive: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* Base Text Editor Options.
|
||||
*/
|
||||
export class TextEditorOptions extends EditorOptions {
|
||||
private startLineNumber: number;
|
||||
private startColumn: number;
|
||||
private endLineNumber: number;
|
||||
private endColumn: number;
|
||||
|
||||
private revealInCenterIfOutsideViewport: boolean;
|
||||
private editorViewState: IEditorViewState;
|
||||
|
||||
public static from(input?: IBaseResourceInput): TextEditorOptions {
|
||||
if (!input || !input.options) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return TextEditorOptions.create(input.options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper to convert options bag to real class
|
||||
*/
|
||||
public static create(options: ITextEditorOptions = Object.create(null)): TextEditorOptions {
|
||||
const textEditorOptions = new TextEditorOptions();
|
||||
|
||||
if (options.selection) {
|
||||
const selection = options.selection;
|
||||
textEditorOptions.selection(selection.startLineNumber, selection.startColumn, selection.endLineNumber, selection.endColumn);
|
||||
}
|
||||
|
||||
if (options.viewState) {
|
||||
textEditorOptions.editorViewState = options.viewState as IEditorViewState;
|
||||
}
|
||||
|
||||
if (options.forceOpen) {
|
||||
textEditorOptions.forceOpen = true;
|
||||
}
|
||||
|
||||
if (options.revealIfVisible) {
|
||||
textEditorOptions.revealIfVisible = true;
|
||||
}
|
||||
|
||||
if (options.revealIfOpened) {
|
||||
textEditorOptions.revealIfOpened = true;
|
||||
}
|
||||
|
||||
if (options.preserveFocus) {
|
||||
textEditorOptions.preserveFocus = true;
|
||||
}
|
||||
|
||||
if (options.revealInCenterIfOutsideViewport) {
|
||||
textEditorOptions.revealInCenterIfOutsideViewport = true;
|
||||
}
|
||||
|
||||
if (options.pinned) {
|
||||
textEditorOptions.pinned = true;
|
||||
}
|
||||
|
||||
if (options.inactive) {
|
||||
textEditorOptions.inactive = true;
|
||||
}
|
||||
|
||||
if (typeof options.index === 'number') {
|
||||
textEditorOptions.index = options.index;
|
||||
}
|
||||
|
||||
return textEditorOptions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns if this options object has objects defined for the editor.
|
||||
*/
|
||||
public hasOptionsDefined(): boolean {
|
||||
return !!this.editorViewState || (!types.isUndefinedOrNull(this.startLineNumber) && !types.isUndefinedOrNull(this.startColumn));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tells the editor to set show the given selection when the editor is being opened.
|
||||
*/
|
||||
public selection(startLineNumber: number, startColumn: number, endLineNumber: number = startLineNumber, endColumn: number = startColumn): EditorOptions {
|
||||
this.startLineNumber = startLineNumber;
|
||||
this.startColumn = startColumn;
|
||||
this.endLineNumber = endLineNumber;
|
||||
this.endColumn = endColumn;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a TextEditorOptions inline to be used when the editor is opening.
|
||||
*/
|
||||
public static fromEditor(editor: IEditor, settings?: IEditorOptions): TextEditorOptions {
|
||||
const options = TextEditorOptions.create(settings);
|
||||
|
||||
// View state
|
||||
options.editorViewState = editor.saveViewState();
|
||||
|
||||
return options;
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply the view state or selection to the given editor.
|
||||
*
|
||||
* @return if something was applied
|
||||
*/
|
||||
public apply(editor: IEditor, scrollType: ScrollType): boolean {
|
||||
|
||||
// View state
|
||||
return this.applyViewState(editor, scrollType);
|
||||
}
|
||||
|
||||
private applyViewState(editor: IEditor, scrollType: ScrollType): boolean {
|
||||
let gotApplied = false;
|
||||
|
||||
// First try viewstate
|
||||
if (this.editorViewState) {
|
||||
editor.restoreViewState(this.editorViewState);
|
||||
gotApplied = true;
|
||||
}
|
||||
|
||||
// Otherwise check for selection
|
||||
else if (!types.isUndefinedOrNull(this.startLineNumber) && !types.isUndefinedOrNull(this.startColumn)) {
|
||||
|
||||
// Select
|
||||
if (!types.isUndefinedOrNull(this.endLineNumber) && !types.isUndefinedOrNull(this.endColumn)) {
|
||||
const range = {
|
||||
startLineNumber: this.startLineNumber,
|
||||
startColumn: this.startColumn,
|
||||
endLineNumber: this.endLineNumber,
|
||||
endColumn: this.endColumn
|
||||
};
|
||||
editor.setSelection(range);
|
||||
if (this.revealInCenterIfOutsideViewport) {
|
||||
editor.revealRangeInCenterIfOutsideViewport(range, scrollType);
|
||||
} else {
|
||||
editor.revealRangeInCenter(range, scrollType);
|
||||
}
|
||||
}
|
||||
|
||||
// Reveal
|
||||
else {
|
||||
const pos = {
|
||||
lineNumber: this.startLineNumber,
|
||||
column: this.startColumn
|
||||
};
|
||||
editor.setPosition(pos);
|
||||
if (this.revealInCenterIfOutsideViewport) {
|
||||
editor.revealPositionInCenterIfOutsideViewport(pos, scrollType);
|
||||
} else {
|
||||
editor.revealPositionInCenter(pos, scrollType);
|
||||
}
|
||||
}
|
||||
|
||||
gotApplied = true;
|
||||
}
|
||||
|
||||
return gotApplied;
|
||||
}
|
||||
}
|
||||
|
||||
export interface IStacksModelChangeEvent {
|
||||
group: IEditorGroup;
|
||||
editor?: IEditorInput;
|
||||
structural?: boolean;
|
||||
}
|
||||
|
||||
export interface IEditorStacksModel {
|
||||
|
||||
onModelChanged: Event<IStacksModelChangeEvent>;
|
||||
|
||||
onWillCloseEditor: Event<IEditorCloseEvent>;
|
||||
onEditorClosed: Event<IEditorCloseEvent>;
|
||||
|
||||
groups: IEditorGroup[];
|
||||
activeGroup: IEditorGroup;
|
||||
isActive(group: IEditorGroup): boolean;
|
||||
|
||||
getGroup(id: GroupIdentifier): IEditorGroup;
|
||||
|
||||
positionOfGroup(group: IEditorGroup): Position;
|
||||
groupAt(position: Position): IEditorGroup;
|
||||
|
||||
next(jumpGroups: boolean, cycleAtEnd?: boolean): IEditorIdentifier;
|
||||
previous(jumpGroups: boolean, cycleAtStart?: boolean): IEditorIdentifier;
|
||||
|
||||
isOpen(resource: URI): boolean;
|
||||
|
||||
toString(): string;
|
||||
}
|
||||
|
||||
export interface IEditorGroup {
|
||||
|
||||
id: GroupIdentifier;
|
||||
label: string;
|
||||
count: number;
|
||||
activeEditor: IEditorInput;
|
||||
previewEditor: IEditorInput;
|
||||
|
||||
getEditor(index: number): IEditorInput;
|
||||
getEditor(resource: URI): IEditorInput;
|
||||
indexOf(editor: IEditorInput): number;
|
||||
|
||||
contains(editorOrResource: IEditorInput | URI): boolean;
|
||||
|
||||
getEditors(mru?: boolean): IEditorInput[];
|
||||
isActive(editor: IEditorInput): boolean;
|
||||
isPreview(editor: IEditorInput): boolean;
|
||||
isPinned(index: number): boolean;
|
||||
isPinned(editor: IEditorInput): boolean;
|
||||
}
|
||||
|
||||
export interface IEditorIdentifier {
|
||||
group: IEditorGroup;
|
||||
editor: IEditorInput;
|
||||
}
|
||||
|
||||
export interface IEditorContext extends IEditorIdentifier {
|
||||
event?: any;
|
||||
}
|
||||
|
||||
export interface IEditorCloseEvent extends IEditorIdentifier {
|
||||
pinned: boolean;
|
||||
index: number;
|
||||
}
|
||||
|
||||
export type GroupIdentifier = number;
|
||||
|
||||
export const EditorOpenPositioning = {
|
||||
LEFT: 'left',
|
||||
RIGHT: 'right',
|
||||
FIRST: 'first',
|
||||
LAST: 'last'
|
||||
};
|
||||
|
||||
export interface IWorkbenchEditorConfiguration {
|
||||
workbench: {
|
||||
editor: {
|
||||
showTabs: boolean;
|
||||
tabCloseButton: 'left' | 'right' | 'off';
|
||||
showIcons: boolean;
|
||||
enablePreview: boolean;
|
||||
enablePreviewFromQuickOpen: boolean;
|
||||
closeOnFileDelete: boolean;
|
||||
openPositioning: 'left' | 'right' | 'first' | 'last';
|
||||
revealIfOpen: boolean;
|
||||
swipeToNavigate: boolean
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
export const ActiveEditorMovePositioning = {
|
||||
FIRST: 'first',
|
||||
LAST: 'last',
|
||||
LEFT: 'left',
|
||||
RIGHT: 'right',
|
||||
CENTER: 'center',
|
||||
POSITION: 'position',
|
||||
};
|
||||
|
||||
export const ActiveEditorMovePositioningBy = {
|
||||
TAB: 'tab',
|
||||
GROUP: 'group'
|
||||
};
|
||||
|
||||
export interface ActiveEditorMoveArguments {
|
||||
to?: string;
|
||||
by?: string;
|
||||
value?: number;
|
||||
}
|
||||
|
||||
export const EditorCommands = {
|
||||
MoveActiveEditor: 'moveActiveEditor'
|
||||
};
|
||||
|
||||
export interface IResourceOptions {
|
||||
supportSideBySide?: boolean;
|
||||
filter?: 'file' | 'untitled' | ['file', 'untitled'] | ['untitled', 'file'];
|
||||
}
|
||||
|
||||
export function hasResource(editor: IEditorInput, options?: IResourceOptions): boolean {
|
||||
return !!toResource(editor, options);
|
||||
}
|
||||
|
||||
export function toResource(editor: IEditorInput, options?: IResourceOptions): URI {
|
||||
if (!editor) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Check for side by side if we are asked to
|
||||
if (options && options.supportSideBySide && editor instanceof SideBySideEditorInput) {
|
||||
editor = editor.master;
|
||||
}
|
||||
|
||||
const resource = doGetEditorResource(editor);
|
||||
if (!options || !options.filter) {
|
||||
return resource; // return early if no filter is specified
|
||||
}
|
||||
|
||||
if (!resource) {
|
||||
return null;
|
||||
}
|
||||
|
||||
let includeFiles: boolean;
|
||||
let includeUntitled: boolean;
|
||||
if (Array.isArray(options.filter)) {
|
||||
includeFiles = (options.filter.indexOf('file') >= 0);
|
||||
includeUntitled = (options.filter.indexOf('untitled') >= 0);
|
||||
} else {
|
||||
includeFiles = (options.filter === 'file');
|
||||
includeUntitled = (options.filter === 'untitled');
|
||||
}
|
||||
|
||||
if (includeFiles && resource.scheme === 'file') {
|
||||
return resource;
|
||||
}
|
||||
|
||||
if (includeUntitled && resource.scheme === 'untitled') {
|
||||
return resource;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
// TODO@Ben every editor should have an associated resource
|
||||
function doGetEditorResource(editor: IEditorInput): URI {
|
||||
if (editor instanceof EditorInput && typeof (<any>editor).getResource === 'function') {
|
||||
const candidate = (<any>editor).getResource();
|
||||
if (candidate instanceof URI) {
|
||||
return candidate;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
68
src/vs/workbench/common/editor/binaryEditorModel.ts
Normal file
68
src/vs/workbench/common/editor/binaryEditorModel.ts
Normal file
@@ -0,0 +1,68 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* 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 { TPromise } from 'vs/base/common/winjs.base';
|
||||
import { EditorModel } from 'vs/workbench/common/editor';
|
||||
import URI from 'vs/base/common/uri';
|
||||
import { IFileService } from 'vs/platform/files/common/files';
|
||||
|
||||
/**
|
||||
* An editor model that just represents a resource that can be loaded.
|
||||
*/
|
||||
export class BinaryEditorModel extends EditorModel {
|
||||
private name: string;
|
||||
private resource: URI;
|
||||
private size: number;
|
||||
private etag: string;
|
||||
|
||||
constructor(
|
||||
resource: URI,
|
||||
name: string,
|
||||
@IFileService private fileService: IFileService
|
||||
) {
|
||||
super();
|
||||
|
||||
this.name = name;
|
||||
this.resource = resource;
|
||||
}
|
||||
|
||||
/**
|
||||
* The name of the binary resource.
|
||||
*/
|
||||
public getName(): string {
|
||||
return this.name;
|
||||
}
|
||||
|
||||
/**
|
||||
* The resource of the binary resource.
|
||||
*/
|
||||
public getResource(): URI {
|
||||
return this.resource;
|
||||
}
|
||||
|
||||
/**
|
||||
* The size of the binary file if known.
|
||||
*/
|
||||
public getSize(): number {
|
||||
return this.size;
|
||||
}
|
||||
|
||||
/**
|
||||
* The etag of the binary file if known.
|
||||
*/
|
||||
public getETag(): string {
|
||||
return this.etag;
|
||||
}
|
||||
|
||||
public load(): TPromise<EditorModel> {
|
||||
return this.fileService.resolveFile(this.resource).then(stat => {
|
||||
this.etag = stat.etag;
|
||||
this.size = stat.size;
|
||||
|
||||
return this;
|
||||
});
|
||||
}
|
||||
}
|
||||
102
src/vs/workbench/common/editor/diffEditorInput.ts
Normal file
102
src/vs/workbench/common/editor/diffEditorInput.ts
Normal file
@@ -0,0 +1,102 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* 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 { TPromise } from 'vs/base/common/winjs.base';
|
||||
import { EditorModel, EditorInput, SideBySideEditorInput, TEXT_DIFF_EDITOR_ID, BINARY_DIFF_EDITOR_ID } from 'vs/workbench/common/editor';
|
||||
import { BaseTextEditorModel } from 'vs/workbench/common/editor/textEditorModel';
|
||||
import { DiffEditorModel } from 'vs/workbench/common/editor/diffEditorModel';
|
||||
import { TextDiffEditorModel } from 'vs/workbench/common/editor/textDiffEditorModel';
|
||||
|
||||
/**
|
||||
* The base editor input for the diff editor. It is made up of two editor inputs, the original version
|
||||
* and the modified version.
|
||||
*/
|
||||
export class DiffEditorInput extends SideBySideEditorInput {
|
||||
|
||||
public static ID = 'workbench.editors.diffEditorInput';
|
||||
|
||||
private cachedModel: DiffEditorModel;
|
||||
|
||||
constructor(name: string, description: string, original: EditorInput, modified: EditorInput, private forceOpenAsBinary?: boolean) {
|
||||
super(name, description, original, modified);
|
||||
}
|
||||
|
||||
public getTypeId(): string {
|
||||
return DiffEditorInput.ID;
|
||||
}
|
||||
|
||||
get originalInput(): EditorInput {
|
||||
return this.details;
|
||||
}
|
||||
|
||||
get modifiedInput(): EditorInput {
|
||||
return this.master;
|
||||
}
|
||||
|
||||
public resolve(refresh?: boolean): TPromise<EditorModel> {
|
||||
let modelPromise: TPromise<EditorModel>;
|
||||
|
||||
// Use Cached Model
|
||||
if (this.cachedModel && !refresh) {
|
||||
modelPromise = TPromise.as<EditorModel>(this.cachedModel);
|
||||
}
|
||||
|
||||
// Create Model - we never reuse our cached model if refresh is true because we cannot
|
||||
// decide for the inputs within if the cached model can be reused or not. There may be
|
||||
// inputs that need to be loaded again and thus we always recreate the model and dispose
|
||||
// the previous one - if any.
|
||||
else {
|
||||
modelPromise = this.createModel(refresh);
|
||||
}
|
||||
|
||||
return modelPromise.then((resolvedModel: DiffEditorModel) => {
|
||||
if (this.cachedModel) {
|
||||
this.cachedModel.dispose();
|
||||
}
|
||||
|
||||
this.cachedModel = resolvedModel;
|
||||
|
||||
return this.cachedModel;
|
||||
});
|
||||
}
|
||||
|
||||
public getPreferredEditorId(candidates: string[]): string {
|
||||
return this.forceOpenAsBinary ? BINARY_DIFF_EDITOR_ID : TEXT_DIFF_EDITOR_ID;
|
||||
}
|
||||
|
||||
private createModel(refresh?: boolean): TPromise<DiffEditorModel> {
|
||||
|
||||
// Join resolve call over two inputs and build diff editor model
|
||||
return TPromise.join([
|
||||
this.originalInput.resolve(refresh),
|
||||
this.modifiedInput.resolve(refresh)
|
||||
]).then((models) => {
|
||||
const originalEditorModel = models[0];
|
||||
const modifiedEditorModel = models[1];
|
||||
|
||||
// If both are text models, return textdiffeditor model
|
||||
if (modifiedEditorModel instanceof BaseTextEditorModel && originalEditorModel instanceof BaseTextEditorModel) {
|
||||
return new TextDiffEditorModel(<BaseTextEditorModel>originalEditorModel, <BaseTextEditorModel>modifiedEditorModel);
|
||||
}
|
||||
|
||||
// Otherwise return normal diff model
|
||||
return new DiffEditorModel(originalEditorModel, modifiedEditorModel);
|
||||
});
|
||||
}
|
||||
|
||||
public dispose(): void {
|
||||
|
||||
// Free the diff editor model but do not propagate the dispose() call to the two inputs
|
||||
// We never created the two inputs (original and modified) so we can not dispose
|
||||
// them without sideeffects.
|
||||
if (this.cachedModel) {
|
||||
this.cachedModel.dispose();
|
||||
this.cachedModel = null;
|
||||
}
|
||||
|
||||
super.dispose();
|
||||
}
|
||||
}
|
||||
55
src/vs/workbench/common/editor/diffEditorModel.ts
Normal file
55
src/vs/workbench/common/editor/diffEditorModel.ts
Normal file
@@ -0,0 +1,55 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* 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 { TPromise } from 'vs/base/common/winjs.base';
|
||||
import { EditorModel } from 'vs/workbench/common/editor';
|
||||
import { IEditorModel } from 'vs/platform/editor/common/editor';
|
||||
|
||||
/**
|
||||
* The base editor model for the diff editor. It is made up of two editor models, the original version
|
||||
* and the modified version.
|
||||
*/
|
||||
export class DiffEditorModel extends EditorModel {
|
||||
protected _originalModel: IEditorModel;
|
||||
protected _modifiedModel: IEditorModel;
|
||||
|
||||
constructor(originalModel: IEditorModel, modifiedModel: IEditorModel) {
|
||||
super();
|
||||
|
||||
this._originalModel = originalModel;
|
||||
this._modifiedModel = modifiedModel;
|
||||
}
|
||||
|
||||
public get originalModel(): EditorModel {
|
||||
return this._originalModel as EditorModel;
|
||||
}
|
||||
|
||||
public get modifiedModel(): EditorModel {
|
||||
return this._modifiedModel as EditorModel;
|
||||
}
|
||||
|
||||
public load(): TPromise<EditorModel> {
|
||||
return TPromise.join([
|
||||
this._originalModel.load(),
|
||||
this._modifiedModel.load()
|
||||
]).then(() => {
|
||||
return this;
|
||||
});
|
||||
}
|
||||
|
||||
public isResolved(): boolean {
|
||||
return this.originalModel.isResolved() && this.modifiedModel.isResolved();
|
||||
}
|
||||
|
||||
public dispose(): void {
|
||||
|
||||
// Do not propagate the dispose() call to the two models inside. We never created the two models
|
||||
// (original and modified) so we can not dispose them without sideeffects. Rather rely on the
|
||||
// models getting disposed when their related inputs get disposed from the diffEditorInput.
|
||||
|
||||
super.dispose();
|
||||
}
|
||||
}
|
||||
1285
src/vs/workbench/common/editor/editorStacksModel.ts
Normal file
1285
src/vs/workbench/common/editor/editorStacksModel.ts
Normal file
File diff suppressed because it is too large
Load Diff
117
src/vs/workbench/common/editor/rangeDecorations.ts
Normal file
117
src/vs/workbench/common/editor/rangeDecorations.ts
Normal file
@@ -0,0 +1,117 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { IDisposable } from 'vs/base/common/lifecycle';
|
||||
import URI from 'vs/base/common/uri';
|
||||
import Event, { Emitter } from 'vs/base/common/event';
|
||||
import * as editorCommon from 'vs/editor/common/editorCommon';
|
||||
import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService';
|
||||
import { toResource } from 'vs/workbench/common/editor';
|
||||
import { isEqual } from 'vs/base/common/paths';
|
||||
import { IRange } from 'vs/editor/common/core/range';
|
||||
import { CursorChangeReason, ICursorPositionChangedEvent } from 'vs/editor/common/controller/cursorEvents';
|
||||
import { ModelDecorationOptions } from 'vs/editor/common/model/textModelWithDecorations';
|
||||
|
||||
export interface IRangeHighlightDecoration {
|
||||
resource: URI;
|
||||
range: IRange;
|
||||
isWholeLine?: boolean;
|
||||
}
|
||||
|
||||
export class RangeHighlightDecorations implements IDisposable {
|
||||
|
||||
private rangeHighlightDecorationId: string = null;
|
||||
private editor: editorCommon.ICommonCodeEditor = null;
|
||||
private editorDisposables: IDisposable[] = [];
|
||||
|
||||
private _onHighlightRemoved: Emitter<void> = new Emitter<void>();
|
||||
public readonly onHighlghtRemoved: Event<void> = this._onHighlightRemoved.event;
|
||||
|
||||
constructor( @IWorkbenchEditorService private editorService: IWorkbenchEditorService) {
|
||||
}
|
||||
|
||||
public removeHighlightRange() {
|
||||
if (this.editor && this.editor.getModel() && this.rangeHighlightDecorationId) {
|
||||
this.editor.deltaDecorations([this.rangeHighlightDecorationId], []);
|
||||
this._onHighlightRemoved.fire();
|
||||
}
|
||||
this.rangeHighlightDecorationId = null;
|
||||
}
|
||||
|
||||
public highlightRange(range: IRangeHighlightDecoration, editor?: editorCommon.ICommonCodeEditor) {
|
||||
editor = editor ? editor : this.getEditor(range);
|
||||
if (editor) {
|
||||
this.doHighlightRange(editor, range);
|
||||
}
|
||||
}
|
||||
|
||||
private doHighlightRange(editor: editorCommon.ICommonCodeEditor, selectionRange: IRangeHighlightDecoration) {
|
||||
this.removeHighlightRange();
|
||||
editor.changeDecorations((changeAccessor: editorCommon.IModelDecorationsChangeAccessor) => {
|
||||
this.rangeHighlightDecorationId = changeAccessor.addDecoration(selectionRange.range, this.createRangeHighlightDecoration(selectionRange.isWholeLine));
|
||||
});
|
||||
this.setEditor(editor);
|
||||
}
|
||||
|
||||
private getEditor(resourceRange: IRangeHighlightDecoration): editorCommon.ICommonCodeEditor {
|
||||
const fileResource = toResource(this.editorService.getActiveEditorInput(), { filter: 'file' });
|
||||
if (fileResource) {
|
||||
if (isEqual(fileResource.fsPath, resourceRange.resource.fsPath)) {
|
||||
return <editorCommon.ICommonCodeEditor>this.editorService.getActiveEditor().getControl();
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private setEditor(editor: editorCommon.ICommonCodeEditor) {
|
||||
if (this.editor !== editor) {
|
||||
this.disposeEditorListeners();
|
||||
this.editor = editor;
|
||||
this.editorDisposables.push(this.editor.onDidChangeCursorPosition((e: ICursorPositionChangedEvent) => {
|
||||
if (
|
||||
e.reason === CursorChangeReason.NotSet
|
||||
|| e.reason === CursorChangeReason.Explicit
|
||||
|| e.reason === CursorChangeReason.Undo
|
||||
|| e.reason === CursorChangeReason.Redo
|
||||
) {
|
||||
this.removeHighlightRange();
|
||||
}
|
||||
}));
|
||||
this.editorDisposables.push(this.editor.onDidChangeModel(() => { this.removeHighlightRange(); }));
|
||||
this.editorDisposables.push(this.editor.onDidDispose(() => {
|
||||
this.removeHighlightRange();
|
||||
this.editor = null;
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
private disposeEditorListeners() {
|
||||
this.editorDisposables.forEach(disposable => disposable.dispose());
|
||||
this.editorDisposables = [];
|
||||
}
|
||||
|
||||
private static _WHOLE_LINE_RANGE_HIGHLIGHT = ModelDecorationOptions.register({
|
||||
stickiness: editorCommon.TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges,
|
||||
className: 'rangeHighlight',
|
||||
isWholeLine: true
|
||||
});
|
||||
|
||||
private static _RANGE_HIGHLIGHT = ModelDecorationOptions.register({
|
||||
stickiness: editorCommon.TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges,
|
||||
className: 'rangeHighlight'
|
||||
});
|
||||
|
||||
private createRangeHighlightDecoration(isWholeLine: boolean = true): ModelDecorationOptions {
|
||||
return (isWholeLine ? RangeHighlightDecorations._WHOLE_LINE_RANGE_HIGHLIGHT : RangeHighlightDecorations._RANGE_HIGHLIGHT);
|
||||
}
|
||||
|
||||
public dispose() {
|
||||
if (this.editor && this.editor.getModel()) {
|
||||
this.removeHighlightRange();
|
||||
this.disposeEditorListeners();
|
||||
this.editor = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
119
src/vs/workbench/common/editor/resourceEditorInput.ts
Normal file
119
src/vs/workbench/common/editor/resourceEditorInput.ts
Normal file
@@ -0,0 +1,119 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* 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 { TPromise } from 'vs/base/common/winjs.base';
|
||||
import { EditorInput, ITextEditorModel } from 'vs/workbench/common/editor';
|
||||
import URI from 'vs/base/common/uri';
|
||||
import { IReference } from 'vs/base/common/lifecycle';
|
||||
import { telemetryURIDescriptor } from 'vs/platform/telemetry/common/telemetryUtils';
|
||||
import { ITextModelService } from 'vs/editor/common/services/resolverService';
|
||||
import { ResourceEditorModel } from 'vs/workbench/common/editor/resourceEditorModel';
|
||||
|
||||
/**
|
||||
* A read-only text editor input whos contents are made of the provided resource that points to an existing
|
||||
* code editor model.
|
||||
*/
|
||||
export class ResourceEditorInput extends EditorInput {
|
||||
|
||||
static ID: string = 'workbench.editors.resourceEditorInput';
|
||||
|
||||
private modelReference: TPromise<IReference<ITextEditorModel>>;
|
||||
private resource: URI;
|
||||
private name: string;
|
||||
private description: string;
|
||||
|
||||
constructor(
|
||||
name: string,
|
||||
description: string,
|
||||
resource: URI,
|
||||
@ITextModelService private textModelResolverService: ITextModelService
|
||||
) {
|
||||
super();
|
||||
|
||||
this.name = name;
|
||||
this.description = description;
|
||||
this.resource = resource;
|
||||
}
|
||||
|
||||
public getResource(): URI {
|
||||
return this.resource;
|
||||
}
|
||||
|
||||
public getTypeId(): string {
|
||||
return ResourceEditorInput.ID;
|
||||
}
|
||||
|
||||
public getName(): string {
|
||||
return this.name;
|
||||
}
|
||||
|
||||
public setName(name: string): void {
|
||||
if (this.name !== name) {
|
||||
this.name = name;
|
||||
this._onDidChangeLabel.fire();
|
||||
}
|
||||
}
|
||||
|
||||
public getDescription(): string {
|
||||
return this.description;
|
||||
}
|
||||
|
||||
public setDescription(description: string): void {
|
||||
if (this.description !== description) {
|
||||
this.description = description;
|
||||
this._onDidChangeLabel.fire();
|
||||
}
|
||||
}
|
||||
|
||||
public getTelemetryDescriptor(): object {
|
||||
const descriptor = super.getTelemetryDescriptor();
|
||||
descriptor['resource'] = telemetryURIDescriptor(this.resource);
|
||||
|
||||
return descriptor;
|
||||
}
|
||||
|
||||
public resolve(refresh?: boolean): TPromise<ITextEditorModel> {
|
||||
if (!this.modelReference) {
|
||||
this.modelReference = this.textModelResolverService.createModelReference(this.resource);
|
||||
}
|
||||
|
||||
return this.modelReference.then(ref => {
|
||||
const model = ref.object;
|
||||
|
||||
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 model;
|
||||
});
|
||||
}
|
||||
|
||||
public matches(otherInput: any): boolean {
|
||||
if (super.matches(otherInput) === true) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (otherInput instanceof ResourceEditorInput) {
|
||||
let otherResourceEditorInput = <ResourceEditorInput>otherInput;
|
||||
|
||||
// Compare by properties
|
||||
return otherResourceEditorInput.resource.toString() === this.resource.toString();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public dispose(): void {
|
||||
if (this.modelReference) {
|
||||
this.modelReference.done(ref => ref.dispose());
|
||||
this.modelReference = null;
|
||||
}
|
||||
|
||||
super.dispose();
|
||||
}
|
||||
}
|
||||
27
src/vs/workbench/common/editor/resourceEditorModel.ts
Normal file
27
src/vs/workbench/common/editor/resourceEditorModel.ts
Normal file
@@ -0,0 +1,27 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* 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 { BaseTextEditorModel } from 'vs/workbench/common/editor/textEditorModel';
|
||||
import URI from 'vs/base/common/uri';
|
||||
import { IModeService } from 'vs/editor/common/services/modeService';
|
||||
import { IModelService } from 'vs/editor/common/services/modelService';
|
||||
|
||||
/**
|
||||
* An editor model whith an in-memory, readonly content that is backed by an existing editor model.
|
||||
*/
|
||||
export class ResourceEditorModel extends BaseTextEditorModel {
|
||||
|
||||
constructor(
|
||||
resource: URI,
|
||||
@IModeService modeService: IModeService,
|
||||
@IModelService modelService: IModelService
|
||||
) {
|
||||
super(modelService, modeService, resource);
|
||||
|
||||
// TODO@Joao: force this class to dispose the underlying model
|
||||
this.createdEditorModel = true;
|
||||
}
|
||||
}
|
||||
79
src/vs/workbench/common/editor/textDiffEditorModel.ts
Normal file
79
src/vs/workbench/common/editor/textDiffEditorModel.ts
Normal file
@@ -0,0 +1,79 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* 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 { TPromise } from 'vs/base/common/winjs.base';
|
||||
import { IDiffEditorModel } from 'vs/editor/common/editorCommon';
|
||||
import { EditorModel } from 'vs/workbench/common/editor';
|
||||
import { BaseTextEditorModel } from 'vs/workbench/common/editor/textEditorModel';
|
||||
import { DiffEditorModel } from 'vs/workbench/common/editor/diffEditorModel';
|
||||
|
||||
/**
|
||||
* The base text editor model for the diff editor. It is made up of two text editor models, the original version
|
||||
* and the modified version.
|
||||
*/
|
||||
export class TextDiffEditorModel extends DiffEditorModel {
|
||||
private _textDiffEditorModel: IDiffEditorModel;
|
||||
|
||||
constructor(originalModel: BaseTextEditorModel, modifiedModel: BaseTextEditorModel) {
|
||||
super(originalModel, modifiedModel);
|
||||
|
||||
this.updateTextDiffEditorModel();
|
||||
}
|
||||
|
||||
get originalModel(): BaseTextEditorModel {
|
||||
return this._originalModel as BaseTextEditorModel;
|
||||
}
|
||||
|
||||
get modifiedModel(): BaseTextEditorModel {
|
||||
return this._modifiedModel as BaseTextEditorModel;
|
||||
}
|
||||
|
||||
public load(): TPromise<EditorModel> {
|
||||
return super.load().then(() => {
|
||||
this.updateTextDiffEditorModel();
|
||||
|
||||
return this;
|
||||
});
|
||||
}
|
||||
|
||||
private updateTextDiffEditorModel(): void {
|
||||
if (this.originalModel.isResolved() && this.modifiedModel.isResolved()) {
|
||||
|
||||
// Create new
|
||||
if (!this._textDiffEditorModel) {
|
||||
this._textDiffEditorModel = {
|
||||
original: this.originalModel.textEditorModel,
|
||||
modified: this.modifiedModel.textEditorModel
|
||||
};
|
||||
}
|
||||
|
||||
// Update existing
|
||||
else {
|
||||
this._textDiffEditorModel.original = this.originalModel.textEditorModel;
|
||||
this._textDiffEditorModel.modified = this.modifiedModel.textEditorModel;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public get textDiffEditorModel(): IDiffEditorModel {
|
||||
return this._textDiffEditorModel;
|
||||
}
|
||||
|
||||
public isResolved(): boolean {
|
||||
return !!this._textDiffEditorModel;
|
||||
}
|
||||
|
||||
public dispose(): void {
|
||||
|
||||
// Free the diff editor model but do not propagate the dispose() call to the two models
|
||||
// inside. We never created the two models (original and modified) so we can not dispose
|
||||
// them without sideeffects. Rather rely on the models getting disposed when their related
|
||||
// inputs get disposed from the diffEditorInput.
|
||||
this._textDiffEditorModel = null;
|
||||
|
||||
super.dispose();
|
||||
}
|
||||
}
|
||||
169
src/vs/workbench/common/editor/textEditorModel.ts
Normal file
169
src/vs/workbench/common/editor/textEditorModel.ts
Normal file
@@ -0,0 +1,169 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* 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 { TPromise } from 'vs/base/common/winjs.base';
|
||||
import { EndOfLinePreference, IModel } from 'vs/editor/common/editorCommon';
|
||||
import { IMode } from 'vs/editor/common/modes';
|
||||
import { EditorModel } from 'vs/workbench/common/editor';
|
||||
import URI from 'vs/base/common/uri';
|
||||
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';
|
||||
|
||||
/**
|
||||
* The base text editor model leverages the code editor model. This class is only intended to be subclassed and not instantiated.
|
||||
*/
|
||||
export abstract class BaseTextEditorModel extends EditorModel implements ITextEditorModel {
|
||||
private textEditorModelHandle: URI;
|
||||
protected createdEditorModel: boolean;
|
||||
private modelDisposeListener: IDisposable;
|
||||
|
||||
constructor(
|
||||
@IModelService protected modelService: IModelService,
|
||||
@IModeService protected modeService: IModeService,
|
||||
textEditorModelHandle?: URI
|
||||
) {
|
||||
super();
|
||||
|
||||
if (textEditorModelHandle) {
|
||||
this.handleExistingModel(textEditorModelHandle);
|
||||
}
|
||||
}
|
||||
|
||||
private handleExistingModel(textEditorModelHandle: URI): void {
|
||||
|
||||
// We need the resource to point to an existing model
|
||||
const model = this.modelService.getModel(textEditorModelHandle);
|
||||
if (!model) {
|
||||
throw new Error(`Document with resource ${textEditorModelHandle.toString()} does not exist`);
|
||||
}
|
||||
|
||||
this.textEditorModelHandle = textEditorModelHandle;
|
||||
|
||||
// Make sure we clean up when this model gets disposed
|
||||
this.registerModelDisposeListener(model);
|
||||
}
|
||||
|
||||
private registerModelDisposeListener(model: IModel): void {
|
||||
if (this.modelDisposeListener) {
|
||||
this.modelDisposeListener.dispose();
|
||||
}
|
||||
|
||||
this.modelDisposeListener = model.onWillDispose(() => {
|
||||
this.textEditorModelHandle = null; // make sure we do not dispose code editor model again
|
||||
this.dispose();
|
||||
});
|
||||
}
|
||||
|
||||
public get textEditorModel(): IModel {
|
||||
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> {
|
||||
const firstLineText = this.getFirstLineText(value);
|
||||
const mode = this.getOrCreateMode(this.modeService, modeId, firstLineText);
|
||||
|
||||
// To avoid flickering, give the mode at most 50ms to load. If the mode doesn't load in 50ms, proceed creating the model with a mode promise
|
||||
return TPromise.any<any>([TPromise.timeout(50), mode]).then(() => {
|
||||
return this.doCreateTextEditorModel(value, mode, resource);
|
||||
});
|
||||
}
|
||||
|
||||
private doCreateTextEditorModel(value: string | IRawTextSource, mode: TPromise<IMode>, resource: URI): EditorModel {
|
||||
let model = resource && this.modelService.getModel(resource);
|
||||
if (!model) {
|
||||
model = this.modelService.createModel(value, mode, resource);
|
||||
this.createdEditorModel = true;
|
||||
|
||||
// Make sure we clean up when this model gets disposed
|
||||
this.registerModelDisposeListener(model);
|
||||
} else {
|
||||
this.modelService.updateModel(model, value);
|
||||
this.modelService.setMode(model, mode);
|
||||
}
|
||||
|
||||
this.textEditorModelHandle = model.uri;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
protected getFirstLineText(value: string | IRawTextSource): string {
|
||||
if (typeof value === 'string') {
|
||||
const firstLineText = value.substr(0, 100);
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the mode for the given identifier. Subclasses can override to provide their own implementation of this lookup.
|
||||
*
|
||||
* @param firstLineText optional first line of the text buffer to set the mode on. This can be used to guess a mode from content.
|
||||
*/
|
||||
protected getOrCreateMode(modeService: IModeService, modeId: string, firstLineText?: string): TPromise<IMode> {
|
||||
return modeService.getOrCreateMode(modeId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 {
|
||||
if (!this.textEditorModel) {
|
||||
return;
|
||||
}
|
||||
|
||||
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 {
|
||||
const model = this.textEditorModel;
|
||||
if (model) {
|
||||
return model.getValue(EndOfLinePreference.TextDefined, true /* Preserve BOM */);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public isResolved(): boolean {
|
||||
return !!this.textEditorModelHandle;
|
||||
}
|
||||
|
||||
public dispose(): void {
|
||||
if (this.modelDisposeListener) {
|
||||
this.modelDisposeListener.dispose(); // dispose this first because it will trigger another dispose() otherwise
|
||||
this.modelDisposeListener = null;
|
||||
}
|
||||
|
||||
if (this.textEditorModelHandle && this.createdEditorModel) {
|
||||
this.modelService.destroyModel(this.textEditorModelHandle);
|
||||
}
|
||||
|
||||
this.textEditorModelHandle = null;
|
||||
this.createdEditorModel = false;
|
||||
|
||||
super.dispose();
|
||||
}
|
||||
}
|
||||
223
src/vs/workbench/common/editor/untitledEditorInput.ts
Normal file
223
src/vs/workbench/common/editor/untitledEditorInput.ts
Normal file
@@ -0,0 +1,223 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* 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 { TPromise } from 'vs/base/common/winjs.base';
|
||||
import URI from 'vs/base/common/uri';
|
||||
import { suggestFilename } from 'vs/base/common/mime';
|
||||
import labels = require('vs/base/common/labels');
|
||||
import { PLAINTEXT_MODE_ID } from 'vs/editor/common/modes/modesRegistry';
|
||||
import paths = require('vs/base/common/paths');
|
||||
import { EditorInput, IEncodingSupport, EncodingMode, ConfirmResult } from 'vs/workbench/common/editor';
|
||||
import { UntitledEditorModel } from 'vs/workbench/common/editor/untitledEditorModel';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace';
|
||||
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
|
||||
import Event, { Emitter } from 'vs/base/common/event';
|
||||
import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles';
|
||||
import { telemetryURIDescriptor } from 'vs/platform/telemetry/common/telemetryUtils';
|
||||
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
|
||||
|
||||
/**
|
||||
* An editor input to be used for untitled text buffers.
|
||||
*/
|
||||
export class UntitledEditorInput extends EditorInput implements IEncodingSupport {
|
||||
|
||||
public static ID: string = 'workbench.editors.untitledEditorInput';
|
||||
|
||||
private _hasAssociatedFilePath: boolean;
|
||||
private cachedModel: UntitledEditorModel;
|
||||
private modelResolve: TPromise<UntitledEditorModel>;
|
||||
|
||||
private _onDidModelChangeContent: Emitter<void>;
|
||||
private _onDidModelChangeEncoding: Emitter<void>;
|
||||
|
||||
private toUnbind: IDisposable[];
|
||||
|
||||
constructor(
|
||||
private resource: URI,
|
||||
hasAssociatedFilePath: boolean,
|
||||
private modeId: string,
|
||||
private initialValue: string,
|
||||
private preferredEncoding: string,
|
||||
@IInstantiationService private instantiationService: IInstantiationService,
|
||||
@IWorkspaceContextService private contextService: IWorkspaceContextService,
|
||||
@ITextFileService private textFileService: ITextFileService,
|
||||
@IEnvironmentService private environmentService: IEnvironmentService
|
||||
) {
|
||||
super();
|
||||
|
||||
this._hasAssociatedFilePath = hasAssociatedFilePath;
|
||||
this.toUnbind = [];
|
||||
|
||||
this._onDidModelChangeContent = new Emitter<void>();
|
||||
this._onDidModelChangeEncoding = new Emitter<void>();
|
||||
}
|
||||
|
||||
public get hasAssociatedFilePath(): boolean {
|
||||
return this._hasAssociatedFilePath;
|
||||
}
|
||||
|
||||
public get onDidModelChangeContent(): Event<void> {
|
||||
return this._onDidModelChangeContent.event;
|
||||
}
|
||||
|
||||
public get onDidModelChangeEncoding(): Event<void> {
|
||||
return this._onDidModelChangeEncoding.event;
|
||||
}
|
||||
|
||||
public getTypeId(): string {
|
||||
return UntitledEditorInput.ID;
|
||||
}
|
||||
|
||||
public getResource(): URI {
|
||||
return this.resource;
|
||||
}
|
||||
|
||||
public getModeId(): string {
|
||||
if (this.cachedModel) {
|
||||
return this.cachedModel.getModeId();
|
||||
}
|
||||
|
||||
return this.modeId;
|
||||
}
|
||||
|
||||
public getName(): string {
|
||||
return this.hasAssociatedFilePath ? paths.basename(this.resource.fsPath) : this.resource.fsPath;
|
||||
}
|
||||
|
||||
public getDescription(): string {
|
||||
return this.hasAssociatedFilePath ? labels.getPathLabel(paths.dirname(this.resource.fsPath), this.contextService, this.environmentService) : null;
|
||||
}
|
||||
|
||||
public isDirty(): boolean {
|
||||
if (this.cachedModel) {
|
||||
return this.cachedModel.isDirty();
|
||||
}
|
||||
|
||||
// A disposed input is never dirty, even if it was restored from backup
|
||||
if (this.isDisposed()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// untitled files with an associated path or associated resource
|
||||
return this.hasAssociatedFilePath;
|
||||
}
|
||||
|
||||
public confirmSave(): ConfirmResult {
|
||||
return this.textFileService.confirmSave([this.resource]);
|
||||
}
|
||||
|
||||
public save(): TPromise<boolean> {
|
||||
return this.textFileService.save(this.resource);
|
||||
}
|
||||
|
||||
public revert(): TPromise<boolean> {
|
||||
if (this.cachedModel) {
|
||||
this.cachedModel.revert();
|
||||
}
|
||||
|
||||
this.dispose(); // a reverted untitled editor is no longer valid, so we dispose it
|
||||
|
||||
return TPromise.as(true);
|
||||
}
|
||||
|
||||
public suggestFileName(): string {
|
||||
if (!this.hasAssociatedFilePath) {
|
||||
if (this.cachedModel) {
|
||||
const modeId = this.cachedModel.getModeId();
|
||||
if (modeId !== PLAINTEXT_MODE_ID) { // do not suggest when the mode ID is simple plain text
|
||||
return suggestFilename(modeId, this.getName());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return this.getName();
|
||||
}
|
||||
|
||||
public getEncoding(): string {
|
||||
if (this.cachedModel) {
|
||||
return this.cachedModel.getEncoding();
|
||||
}
|
||||
|
||||
return this.preferredEncoding;
|
||||
}
|
||||
|
||||
public setEncoding(encoding: string, mode: EncodingMode /* ignored, we only have Encode */): void {
|
||||
this.preferredEncoding = encoding;
|
||||
|
||||
if (this.cachedModel) {
|
||||
this.cachedModel.setEncoding(encoding);
|
||||
}
|
||||
}
|
||||
|
||||
public resolve(): TPromise<UntitledEditorModel> {
|
||||
|
||||
// Join a model resolve if we have had one before
|
||||
if (this.modelResolve) {
|
||||
return this.modelResolve;
|
||||
}
|
||||
|
||||
// Otherwise Create Model and load
|
||||
this.cachedModel = this.createModel();
|
||||
this.modelResolve = this.cachedModel.load();
|
||||
|
||||
return this.modelResolve;
|
||||
}
|
||||
|
||||
private createModel(): UntitledEditorModel {
|
||||
const model = this.instantiationService.createInstance(UntitledEditorModel, this.modeId, this.resource, this.hasAssociatedFilePath, this.initialValue, this.preferredEncoding);
|
||||
|
||||
// re-emit some events from the model
|
||||
this.toUnbind.push(model.onDidChangeContent(() => this._onDidModelChangeContent.fire()));
|
||||
this.toUnbind.push(model.onDidChangeDirty(() => this._onDidChangeDirty.fire()));
|
||||
this.toUnbind.push(model.onDidChangeEncoding(() => this._onDidModelChangeEncoding.fire()));
|
||||
|
||||
return model;
|
||||
}
|
||||
|
||||
public getTelemetryDescriptor(): object {
|
||||
const descriptor = super.getTelemetryDescriptor();
|
||||
descriptor['resource'] = telemetryURIDescriptor(this.getResource());
|
||||
|
||||
return descriptor;
|
||||
}
|
||||
|
||||
public matches(otherInput: any): boolean {
|
||||
if (super.matches(otherInput) === true) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// {{SQL CARBON EDIT}}
|
||||
let isUntitledInput: boolean = otherInput instanceof UntitledEditorInput;
|
||||
let isQueryInput: boolean = otherInput && otherInput.sql && otherInput.sql instanceof UntitledEditorInput;
|
||||
if (isUntitledInput || isQueryInput) {
|
||||
const otherUntitledEditorInput = isUntitledInput ? <UntitledEditorInput>otherInput : <UntitledEditorInput>otherInput.sql;
|
||||
|
||||
// Otherwise compare by properties
|
||||
return otherUntitledEditorInput.resource.toString() === this.resource.toString();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public dispose(): void {
|
||||
this._onDidModelChangeContent.dispose();
|
||||
this._onDidModelChangeEncoding.dispose();
|
||||
|
||||
// Listeners
|
||||
dispose(this.toUnbind);
|
||||
|
||||
// Model
|
||||
if (this.cachedModel) {
|
||||
this.cachedModel.dispose();
|
||||
this.cachedModel = null;
|
||||
}
|
||||
|
||||
this.modelResolve = void 0;
|
||||
|
||||
super.dispose();
|
||||
}
|
||||
}
|
||||
238
src/vs/workbench/common/editor/untitledEditorModel.ts
Normal file
238
src/vs/workbench/common/editor/untitledEditorModel.ts
Normal file
@@ -0,0 +1,238 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* 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 { IDisposable } from 'vs/base/common/lifecycle';
|
||||
import { TPromise } from 'vs/base/common/winjs.base';
|
||||
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 { IFilesConfiguration, CONTENT_CHANGE_EVENT_BUFFER_DELAY } from 'vs/platform/files/common/files';
|
||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
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';
|
||||
|
||||
export class UntitledEditorModel extends BaseTextEditorModel implements IEncodingSupport {
|
||||
|
||||
public static DEFAULT_CONTENT_CHANGE_BUFFER_DELAY = CONTENT_CHANGE_EVENT_BUFFER_DELAY;
|
||||
|
||||
private textModelChangeListener: IDisposable;
|
||||
private configurationChangeListener: IDisposable;
|
||||
|
||||
private dirty: boolean;
|
||||
private _onDidChangeContent: Emitter<void>;
|
||||
private _onDidChangeDirty: Emitter<void>;
|
||||
private _onDidChangeEncoding: Emitter<void>;
|
||||
|
||||
private versionId: number;
|
||||
|
||||
private contentChangeEventScheduler: RunOnceScheduler;
|
||||
|
||||
private configuredEncoding: string;
|
||||
|
||||
constructor(
|
||||
private modeId: string,
|
||||
private resource: URI,
|
||||
private hasAssociatedFilePath: boolean,
|
||||
private initialValue: string,
|
||||
private preferredEncoding: string,
|
||||
@IModeService modeService: IModeService,
|
||||
@IModelService modelService: IModelService,
|
||||
@IBackupFileService private backupFileService: IBackupFileService,
|
||||
@ITextFileService private textFileService: ITextFileService,
|
||||
@IConfigurationService private configurationService: IConfigurationService
|
||||
) {
|
||||
super(modelService, modeService);
|
||||
|
||||
this.dirty = false;
|
||||
this.versionId = 0;
|
||||
|
||||
this._onDidChangeContent = new Emitter<void>();
|
||||
this._onDidChangeDirty = new Emitter<void>();
|
||||
this._onDidChangeEncoding = new Emitter<void>();
|
||||
|
||||
this.contentChangeEventScheduler = new RunOnceScheduler(() => this._onDidChangeContent.fire(), UntitledEditorModel.DEFAULT_CONTENT_CHANGE_BUFFER_DELAY);
|
||||
|
||||
this.registerListeners();
|
||||
}
|
||||
|
||||
public get onDidChangeContent(): Event<void> {
|
||||
return this._onDidChangeContent.event;
|
||||
}
|
||||
|
||||
public get onDidChangeDirty(): Event<void> {
|
||||
return this._onDidChangeDirty.event;
|
||||
}
|
||||
|
||||
public get onDidChangeEncoding(): Event<void> {
|
||||
return this._onDidChangeEncoding.event;
|
||||
}
|
||||
|
||||
protected getOrCreateMode(modeService: IModeService, modeId: string, firstLineText?: string): TPromise<IMode> {
|
||||
if (!modeId || modeId === PLAINTEXT_MODE_ID) {
|
||||
return modeService.getOrCreateModeByFilenameOrFirstLine(this.resource.fsPath, firstLineText); // lookup mode via resource path if the provided modeId is unspecific
|
||||
}
|
||||
|
||||
return super.getOrCreateMode(modeService, modeId, firstLineText);
|
||||
}
|
||||
|
||||
private registerListeners(): void {
|
||||
|
||||
// Config Changes
|
||||
this.configurationChangeListener = this.configurationService.onDidUpdateConfiguration(e => this.onConfigurationChange(this.configurationService.getConfiguration<IFilesConfiguration>()));
|
||||
}
|
||||
|
||||
private onConfigurationChange(configuration: IFilesConfiguration): void {
|
||||
this.configuredEncoding = configuration && configuration.files && configuration.files.encoding;
|
||||
}
|
||||
|
||||
public getVersionId(): number {
|
||||
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;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public getEncoding(): string {
|
||||
return this.preferredEncoding || this.configuredEncoding;
|
||||
}
|
||||
|
||||
public setEncoding(encoding: string): void {
|
||||
const oldEncoding = this.getEncoding();
|
||||
this.preferredEncoding = encoding;
|
||||
|
||||
// Emit if it changed
|
||||
if (oldEncoding !== this.preferredEncoding) {
|
||||
this._onDidChangeEncoding.fire();
|
||||
}
|
||||
}
|
||||
|
||||
public isDirty(): boolean {
|
||||
return this.dirty;
|
||||
}
|
||||
|
||||
private setDirty(dirty: boolean): void {
|
||||
if (this.dirty === dirty) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.dirty = dirty;
|
||||
this._onDidChangeDirty.fire();
|
||||
}
|
||||
|
||||
public getResource(): URI {
|
||||
return this.resource;
|
||||
}
|
||||
|
||||
public revert(): void {
|
||||
this.setDirty(false);
|
||||
|
||||
// Handle content change event buffered
|
||||
this.contentChangeEventScheduler.schedule();
|
||||
}
|
||||
|
||||
public load(): TPromise<UntitledEditorModel> {
|
||||
|
||||
// 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 null;
|
||||
}).then(backupContent => {
|
||||
|
||||
// untitled associated to file path are dirty right away as well as untitled with content
|
||||
this.setDirty(this.hasAssociatedFilePath || !!backupContent);
|
||||
|
||||
return this.doLoad(backupContent || this.initialValue || '').then(model => {
|
||||
const configuration = this.configurationService.getConfiguration<IFilesConfiguration>();
|
||||
|
||||
// Encoding
|
||||
this.configuredEncoding = configuration && configuration.files && configuration.files.encoding;
|
||||
|
||||
// Listen to content changes
|
||||
this.textModelChangeListener = this.textEditorModel.onDidChangeContent(() => this.onModelContentChanged());
|
||||
|
||||
return model;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
private doLoad(content: string): TPromise<UntitledEditorModel> {
|
||||
|
||||
// Create text editor model if not yet done
|
||||
if (!this.textEditorModel) {
|
||||
return this.createTextEditorModel(content, this.resource, this.modeId).then(model => this);
|
||||
}
|
||||
|
||||
// Otherwise update
|
||||
else {
|
||||
this.updateTextEditorModel(content);
|
||||
}
|
||||
|
||||
return TPromise.as<UntitledEditorModel>(this);
|
||||
}
|
||||
|
||||
private onModelContentChanged(): void {
|
||||
this.versionId++;
|
||||
|
||||
// mark the untitled editor as non-dirty once its content becomes empty and we do
|
||||
// not have an associated path set. we never want dirty indicator in that case.
|
||||
if (!this.hasAssociatedFilePath && this.textEditorModel.getLineCount() === 1 && this.textEditorModel.getLineContent(1) === '') {
|
||||
this.setDirty(false);
|
||||
}
|
||||
|
||||
// turn dirty otherwise
|
||||
else {
|
||||
this.setDirty(true);
|
||||
}
|
||||
|
||||
// Handle content change event buffered
|
||||
this.contentChangeEventScheduler.schedule();
|
||||
}
|
||||
|
||||
public dispose(): void {
|
||||
super.dispose();
|
||||
|
||||
if (this.textModelChangeListener) {
|
||||
this.textModelChangeListener.dispose();
|
||||
this.textModelChangeListener = null;
|
||||
}
|
||||
|
||||
if (this.configurationChangeListener) {
|
||||
this.configurationChangeListener.dispose();
|
||||
this.configurationChangeListener = null;
|
||||
}
|
||||
|
||||
this.contentChangeEventScheduler.dispose();
|
||||
|
||||
this._onDidChangeContent.dispose();
|
||||
this._onDidChangeDirty.dispose();
|
||||
this._onDidChangeEncoding.dispose();
|
||||
}
|
||||
}
|
||||
124
src/vs/workbench/common/memento.ts
Normal file
124
src/vs/workbench/common/memento.ts
Normal file
@@ -0,0 +1,124 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* 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 types = require('vs/base/common/types');
|
||||
import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage';
|
||||
|
||||
/**
|
||||
* Supported memento scopes.
|
||||
*/
|
||||
export enum Scope {
|
||||
|
||||
/**
|
||||
* The memento will be scoped to all workspaces of this domain.
|
||||
*/
|
||||
GLOBAL,
|
||||
|
||||
/**
|
||||
* The memento will be scoped to the current workspace.
|
||||
*/
|
||||
WORKSPACE
|
||||
}
|
||||
|
||||
/**
|
||||
* A memento provides access to a datastructure that is persisted and restored as part of the workbench lifecycle.
|
||||
*/
|
||||
export class Memento {
|
||||
|
||||
// Mementos are static to ensure that for a given component with an id only ever one memento gets loaded
|
||||
private static globalMementos: { [id: string]: ScopedMemento } = {};
|
||||
private static workspaceMementos: { [id: string]: ScopedMemento } = {};
|
||||
|
||||
private static COMMON_PREFIX = 'memento/';
|
||||
|
||||
private id: string;
|
||||
|
||||
constructor(id: string) {
|
||||
this.id = Memento.COMMON_PREFIX + id.toLowerCase();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a JSON Object that represents the data of this memento. The optional
|
||||
* parameter scope allows to specify the scope of the memento to load. If not
|
||||
* provided, the scope will be global, Memento.Scope.WORKSPACE can be used to
|
||||
* scope the memento to the workspace.
|
||||
*/
|
||||
public getMemento(storageService: IStorageService, scope: Scope = Scope.GLOBAL): object {
|
||||
|
||||
// Scope by Workspace
|
||||
if (scope === Scope.WORKSPACE) {
|
||||
let workspaceMemento = Memento.workspaceMementos[this.id];
|
||||
if (!workspaceMemento) {
|
||||
workspaceMemento = new ScopedMemento(this.id, scope, storageService);
|
||||
Memento.workspaceMementos[this.id] = workspaceMemento;
|
||||
}
|
||||
|
||||
return workspaceMemento.getMemento();
|
||||
}
|
||||
|
||||
// Use global scope
|
||||
let globalMemento = Memento.globalMementos[this.id];
|
||||
if (!globalMemento) {
|
||||
globalMemento = new ScopedMemento(this.id, scope, storageService);
|
||||
Memento.globalMementos[this.id] = globalMemento;
|
||||
}
|
||||
|
||||
return globalMemento.getMemento();
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves all data of the mementos that have been loaded to the local storage. This includes
|
||||
* global and workspace scope.
|
||||
*/
|
||||
public saveMemento(): void {
|
||||
|
||||
// Global
|
||||
if (Memento.globalMementos[this.id]) {
|
||||
Memento.globalMementos[this.id].save();
|
||||
}
|
||||
|
||||
// Workspace
|
||||
if (Memento.workspaceMementos[this.id]) {
|
||||
Memento.workspaceMementos[this.id].save();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class ScopedMemento {
|
||||
private id: string;
|
||||
private mementoObj: object;
|
||||
private scope: Scope;
|
||||
|
||||
constructor(id: string, scope: Scope, private storageService: IStorageService) {
|
||||
this.id = id;
|
||||
this.scope = scope;
|
||||
this.mementoObj = this.loadMemento();
|
||||
}
|
||||
|
||||
public getMemento(): object {
|
||||
return this.mementoObj;
|
||||
}
|
||||
|
||||
private loadMemento(): object {
|
||||
let storageScope = this.scope === Scope.GLOBAL ? StorageScope.GLOBAL : StorageScope.WORKSPACE;
|
||||
let memento = this.storageService.get(this.id, storageScope);
|
||||
if (memento) {
|
||||
return JSON.parse(memento);
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
public save(): void {
|
||||
let storageScope = this.scope === Scope.GLOBAL ? StorageScope.GLOBAL : StorageScope.WORKSPACE;
|
||||
|
||||
if (!types.isEmptyObject(this.mementoObj)) {
|
||||
this.storageService.store(this.id, JSON.stringify(this.mementoObj), storageScope);
|
||||
} else {
|
||||
this.storageService.remove(this.id, storageScope);
|
||||
}
|
||||
}
|
||||
}
|
||||
9
src/vs/workbench/common/panel.ts
Normal file
9
src/vs/workbench/common/panel.ts
Normal file
@@ -0,0 +1,9 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* 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 { IComposite } from 'vs/workbench/common/composite';
|
||||
|
||||
export interface IPanel extends IComposite { }
|
||||
176
src/vs/workbench/common/resources.ts
Normal file
176
src/vs/workbench/common/resources.ts
Normal file
@@ -0,0 +1,176 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* 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 URI from 'vs/base/common/uri';
|
||||
import objects = require('vs/base/common/objects');
|
||||
import paths = require('vs/base/common/paths');
|
||||
import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace';
|
||||
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
|
||||
import Event, { Emitter } from 'vs/base/common/event';
|
||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
import { ParsedExpression, IExpression } from 'vs/base/common/glob';
|
||||
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';
|
||||
|
||||
export class ResourceContextKey implements IContextKey<URI> {
|
||||
|
||||
static Scheme = new RawContextKey<string>('resourceScheme', undefined);
|
||||
static Filename = new RawContextKey<string>('resourceFilename', undefined);
|
||||
static LangId = new RawContextKey<string>('resourceLangId', undefined);
|
||||
static Resource = new RawContextKey<URI>('resource', undefined);
|
||||
|
||||
private _resourceKey: IContextKey<URI>;
|
||||
private _schemeKey: IContextKey<string>;
|
||||
private _filenameKey: IContextKey<string>;
|
||||
private _langIdKey: IContextKey<string>;
|
||||
|
||||
constructor(
|
||||
@IContextKeyService contextKeyService: IContextKeyService,
|
||||
@IModeService private _modeService: IModeService
|
||||
) {
|
||||
this._schemeKey = ResourceContextKey.Scheme.bindTo(contextKeyService);
|
||||
this._filenameKey = ResourceContextKey.Filename.bindTo(contextKeyService);
|
||||
this._langIdKey = ResourceContextKey.LangId.bindTo(contextKeyService);
|
||||
this._resourceKey = ResourceContextKey.Resource.bindTo(contextKeyService);
|
||||
}
|
||||
|
||||
set(value: URI) {
|
||||
this._resourceKey.set(value);
|
||||
this._schemeKey.set(value && value.scheme);
|
||||
this._filenameKey.set(value && basename(value.fsPath));
|
||||
this._langIdKey.set(value && this._modeService.getModeIdByFilenameOrFirstLine(value.fsPath));
|
||||
}
|
||||
|
||||
reset(): void {
|
||||
this._schemeKey.reset();
|
||||
this._langIdKey.reset();
|
||||
this._resourceKey.reset();
|
||||
}
|
||||
|
||||
public get(): URI {
|
||||
return this._resourceKey.get();
|
||||
}
|
||||
}
|
||||
|
||||
export class ResourceGlobMatcher {
|
||||
|
||||
private static readonly NO_ROOT = null;
|
||||
|
||||
private _onExpressionChange: Emitter<void>;
|
||||
private toUnbind: IDisposable[];
|
||||
private mapRootToParsedExpression: Map<string, ParsedExpression>;
|
||||
private mapRootToExpressionConfig: Map<string, IExpression>;
|
||||
|
||||
constructor(
|
||||
private globFn: (root?: URI) => IExpression,
|
||||
private parseFn: (expression: IExpression) => ParsedExpression,
|
||||
@IWorkspaceContextService private contextService: IWorkspaceContextService,
|
||||
@IConfigurationService private configurationService: IConfigurationService
|
||||
) {
|
||||
this.toUnbind = [];
|
||||
|
||||
this.mapRootToParsedExpression = new Map<string, ParsedExpression>();
|
||||
this.mapRootToExpressionConfig = new Map<string, IExpression>();
|
||||
|
||||
this._onExpressionChange = new Emitter<void>();
|
||||
this.toUnbind.push(this._onExpressionChange);
|
||||
|
||||
this.updateExcludes(false);
|
||||
|
||||
this.registerListeners();
|
||||
}
|
||||
|
||||
public get onExpressionChange(): Event<void> {
|
||||
return this._onExpressionChange.event;
|
||||
}
|
||||
|
||||
private registerListeners(): void {
|
||||
this.toUnbind.push(this.configurationService.onDidUpdateConfiguration(() => this.onConfigurationChanged()));
|
||||
this.toUnbind.push(this.contextService.onDidChangeWorkspaceRoots(() => this.onDidChangeWorkspaceRoots()));
|
||||
}
|
||||
|
||||
private onConfigurationChanged(): void {
|
||||
this.updateExcludes(true);
|
||||
}
|
||||
|
||||
private onDidChangeWorkspaceRoots(): void {
|
||||
this.updateExcludes(true);
|
||||
}
|
||||
|
||||
private updateExcludes(fromEvent: boolean): void {
|
||||
let changed = false;
|
||||
|
||||
// Add excludes per workspaces that got added
|
||||
if (this.contextService.hasWorkspace()) {
|
||||
this.contextService.getWorkspace().roots.forEach(root => {
|
||||
const rootExcludes = this.globFn(root);
|
||||
if (!this.mapRootToExpressionConfig.has(root.toString()) || !objects.equals(this.mapRootToExpressionConfig.get(root.toString()), rootExcludes)) {
|
||||
changed = true;
|
||||
|
||||
this.mapRootToParsedExpression.set(root.toString(), this.parseFn(rootExcludes));
|
||||
this.mapRootToExpressionConfig.set(root.toString(), objects.clone(rootExcludes));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Remove excludes per workspace no longer present
|
||||
this.mapRootToExpressionConfig.forEach((value, root) => {
|
||||
if (root === ResourceGlobMatcher.NO_ROOT) {
|
||||
return; // always keep this one
|
||||
}
|
||||
|
||||
if (!this.contextService.getRoot(URI.parse(root))) {
|
||||
this.mapRootToParsedExpression.delete(root);
|
||||
this.mapRootToExpressionConfig.delete(root);
|
||||
|
||||
changed = true;
|
||||
}
|
||||
});
|
||||
|
||||
// Always set for resources outside root as well
|
||||
const globalExcludes = this.globFn();
|
||||
if (!this.mapRootToExpressionConfig.has(ResourceGlobMatcher.NO_ROOT) || !objects.equals(this.mapRootToExpressionConfig.get(ResourceGlobMatcher.NO_ROOT), globalExcludes)) {
|
||||
changed = true;
|
||||
|
||||
this.mapRootToParsedExpression.set(ResourceGlobMatcher.NO_ROOT, this.parseFn(globalExcludes));
|
||||
this.mapRootToExpressionConfig.set(ResourceGlobMatcher.NO_ROOT, objects.clone(globalExcludes));
|
||||
}
|
||||
|
||||
if (fromEvent && changed) {
|
||||
this._onExpressionChange.fire();
|
||||
}
|
||||
}
|
||||
|
||||
public matches(resource: URI): boolean {
|
||||
const root = this.contextService.getRoot(resource);
|
||||
|
||||
let expressionForRoot: ParsedExpression;
|
||||
if (root && this.mapRootToParsedExpression.has(root.toString())) {
|
||||
expressionForRoot = this.mapRootToParsedExpression.get(root.toString());
|
||||
} else {
|
||||
expressionForRoot = this.mapRootToParsedExpression.get(ResourceGlobMatcher.NO_ROOT);
|
||||
}
|
||||
|
||||
// If the resource if from a workspace, convert its absolute path to a relative
|
||||
// path so that glob patterns have a higher probability to match. For example
|
||||
// a glob pattern of "src/**" will not match on an absolute path "/folder/src/file.txt"
|
||||
// but can match on "src/file.txt"
|
||||
let resourcePathToMatch: string;
|
||||
if (root) {
|
||||
resourcePathToMatch = paths.normalize(paths.relative(root.fsPath, resource.fsPath));
|
||||
} else {
|
||||
resourcePathToMatch = resource.fsPath;
|
||||
}
|
||||
|
||||
return !!expressionForRoot(resourcePathToMatch);
|
||||
}
|
||||
|
||||
public dispose(): void {
|
||||
this.toUnbind = dispose(this.toUnbind);
|
||||
}
|
||||
}
|
||||
441
src/vs/workbench/common/theme.ts
Normal file
441
src/vs/workbench/common/theme.ts
Normal file
@@ -0,0 +1,441 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import nls = require('vs/nls');
|
||||
import { registerColor, editorBackground, contrastBorder, transparent, 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';
|
||||
|
||||
// < --- Tabs --- >
|
||||
|
||||
export const TAB_ACTIVE_BACKGROUND = registerColor('tab.activeBackground', {
|
||||
dark: editorBackground,
|
||||
light: editorBackground,
|
||||
hc: editorBackground
|
||||
}, nls.localize('tabActiveBackground', "Active 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_INACTIVE_BACKGROUND = registerColor('tab.inactiveBackground', {
|
||||
dark: '#2D2D2D',
|
||||
light: '#ECECEC',
|
||||
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_BORDER = registerColor('tab.border', {
|
||||
dark: '#252526',
|
||||
light: '#F3F3F3',
|
||||
hc: contrastBorder
|
||||
}, nls.localize('tabBorder', "Border to separate tabs from each other. 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_BORDER = registerColor('tab.activeBorder', {
|
||||
dark: null,
|
||||
light: null,
|
||||
hc: null
|
||||
}, nls.localize('tabActiveBorder', "Border to highlight active tabs. 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_ACTIVE_BORDER = registerColor('tab.unfocusedActiveBorder', {
|
||||
dark: transparent(TAB_ACTIVE_BORDER, 0.5),
|
||||
light: transparent(TAB_ACTIVE_BORDER, 0.7),
|
||||
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_ACTIVE_FOREGROUND = registerColor('tab.activeForeground', {
|
||||
dark: Color.white,
|
||||
light: '#333333',
|
||||
hc: Color.white
|
||||
}, nls.localize('tabActiveForeground', "Active tab foreground color in an active 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_INACTIVE_FOREGROUND = registerColor('tab.inactiveForeground', {
|
||||
dark: transparent(TAB_ACTIVE_FOREGROUND, 0.5),
|
||||
light: transparent(TAB_ACTIVE_FOREGROUND, 0.5),
|
||||
hc: Color.white
|
||||
}, nls.localize('tabInactiveForeground', "Inactive tab foreground color in an active 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_UNFOCUSED_ACTIVE_FOREGROUND = registerColor('tab.unfocusedActiveForeground', {
|
||||
dark: transparent(TAB_ACTIVE_FOREGROUND, 0.5),
|
||||
light: transparent(TAB_ACTIVE_FOREGROUND, 0.7),
|
||||
hc: Color.white
|
||||
}, nls.localize('tabUnfocusedActiveForeground', "Active tab foreground color 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_UNFOCUSED_INACTIVE_FOREGROUND = registerColor('tab.unfocusedInactiveForeground', {
|
||||
dark: transparent(TAB_INACTIVE_FOREGROUND, 0.5),
|
||||
light: transparent(TAB_INACTIVE_FOREGROUND, 0.5),
|
||||
hc: Color.white
|
||||
}, nls.localize('tabUnfocusedInactiveForeground', "Inactive tab foreground color 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."));
|
||||
|
||||
|
||||
// < --- Editors --- >
|
||||
|
||||
export const EDITOR_GROUP_BACKGROUND = registerColor('editorGroup.background', {
|
||||
dark: '#2D2D2D',
|
||||
light: '#ECECEC',
|
||||
hc: null
|
||||
}, nls.localize('editorGroupBackground', "Background color of an editor group. Editor groups are the containers of editors. The background color shows up when dragging editor groups around."));
|
||||
|
||||
export const EDITOR_GROUP_HEADER_TABS_BACKGROUND = registerColor('editorGroupHeader.tabsBackground', {
|
||||
dark: '#252526',
|
||||
light: '#F3F3F3',
|
||||
hc: null
|
||||
}, nls.localize('tabsContainerBackground', "Background color of the editor group title header when tabs are enabled. Editor groups are the containers of editors."));
|
||||
|
||||
export const EDITOR_GROUP_HEADER_TABS_BORDER = registerColor('editorGroupHeader.tabsBorder', {
|
||||
dark: null,
|
||||
light: null,
|
||||
hc: contrastBorder
|
||||
}, nls.localize('tabsContainerBorder', "Border color of the editor group title header when tabs are enabled. Editor groups are the containers of editors."));
|
||||
|
||||
export const EDITOR_GROUP_HEADER_NO_TABS_BACKGROUND = registerColor('editorGroupHeader.noTabsBackground', {
|
||||
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."));
|
||||
|
||||
export const EDITOR_GROUP_BORDER = registerColor('editorGroup.border', {
|
||||
dark: '#444444',
|
||||
light: '#E7E7E7',
|
||||
hc: contrastBorder
|
||||
}, nls.localize('editorGroupBorder', "Color to separate multiple editor groups from each other. Editor groups are the containers of editors."));
|
||||
|
||||
export const EDITOR_DRAG_AND_DROP_BACKGROUND = registerColor('editorGroup.dropBackground', {
|
||||
dark: Color.fromHex('#53595D').transparent(0.5),
|
||||
light: Color.fromHex('#3399FF').transparent(0.18),
|
||||
hc: null
|
||||
}, nls.localize('editorDragAndDropBackground', "Background color when dragging editors around. The color should have transparency so that the editor contents can still shine through."));
|
||||
|
||||
|
||||
|
||||
// < --- Panels --- >
|
||||
|
||||
export const PANEL_BACKGROUND = registerColor('panel.background', {
|
||||
dark: editorBackground,
|
||||
light: editorBackground,
|
||||
hc: editorBackground
|
||||
}, nls.localize('panelBackground', "Panel background color. Panels are shown below the editor area and contain views like output and integrated terminal."));
|
||||
|
||||
export const PANEL_BORDER = registerColor('panel.border', {
|
||||
dark: Color.fromHex('#808080').transparent(0.35),
|
||||
light: Color.fromHex('#808080').transparent(0.35),
|
||||
hc: contrastBorder
|
||||
}, nls.localize('panelBorder', "Panel border color on the top separating to the editor. Panels are shown below the editor area and contain views like output and integrated terminal."));
|
||||
|
||||
export const PANEL_ACTIVE_TITLE_FOREGROUND = registerColor('panelTitle.activeForeground', {
|
||||
dark: '#E7E7E7',
|
||||
light: '#424242',
|
||||
hc: Color.white
|
||||
}, nls.localize('panelActiveTitleForeground', "Title color for the active panel. Panels are shown below the editor area and contain views like output and integrated terminal."));
|
||||
|
||||
export const PANEL_INACTIVE_TITLE_FOREGROUND = registerColor('panelTitle.inactiveForeground', {
|
||||
dark: transparent(PANEL_ACTIVE_TITLE_FOREGROUND, 0.5),
|
||||
light: transparent(PANEL_ACTIVE_TITLE_FOREGROUND, 0.75),
|
||||
hc: Color.white
|
||||
}, nls.localize('panelInactiveTitleForeground', "Title color for the inactive panel. Panels are shown below the editor area and contain views like output and integrated terminal."));
|
||||
|
||||
export const PANEL_ACTIVE_TITLE_BORDER = registerColor('panelTitle.activeBorder', {
|
||||
dark: PANEL_BORDER,
|
||||
light: PANEL_BORDER,
|
||||
hc: contrastBorder
|
||||
}, nls.localize('panelActiveTitleBorder', "Border color for the active panel title. Panels are shown below the editor area and contain views like output and integrated terminal."));
|
||||
|
||||
|
||||
|
||||
// < --- Status --- >
|
||||
|
||||
export const STATUS_BAR_FOREGROUND = registerColor('statusBar.foreground', {
|
||||
dark: '#FFFFFF',
|
||||
light: '#FFFFFF',
|
||||
hc: '#FFFFFF'
|
||||
}, nls.localize('statusBarForeground', "Status bar foreground color when a workspace is opened. The status bar is shown in the bottom of the window."));
|
||||
|
||||
export const STATUS_BAR_NO_FOLDER_FOREGROUND = registerColor('statusBar.noFolderForeground', {
|
||||
dark: STATUS_BAR_FOREGROUND,
|
||||
light: STATUS_BAR_FOREGROUND,
|
||||
hc: STATUS_BAR_FOREGROUND
|
||||
}, nls.localize('statusBarNoFolderForeground', "Status bar foreground color when no folder is opened. The status bar is shown in the bottom of the window."));
|
||||
|
||||
export const STATUS_BAR_BACKGROUND = registerColor('statusBar.background', {
|
||||
dark: '#007ACC',
|
||||
light: '#007ACC',
|
||||
hc: null
|
||||
}, nls.localize('statusBarBackground', "Status bar background color when a workspace is opened. The status bar is shown in the bottom of the window."));
|
||||
|
||||
export const STATUS_BAR_NO_FOLDER_BACKGROUND = registerColor('statusBar.noFolderBackground', {
|
||||
dark: '#68217A',
|
||||
light: '#68217A',
|
||||
hc: null
|
||||
}, nls.localize('statusBarNoFolderBackground', "Status bar background color when no folder is opened. The status bar is shown in the bottom of the window."));
|
||||
|
||||
export const STATUS_BAR_BORDER = registerColor('statusBar.border', {
|
||||
dark: null,
|
||||
light: null,
|
||||
hc: contrastBorder
|
||||
}, nls.localize('statusBarBorder', "Status bar border color separating to the sidebar and editor. The status bar is shown in the bottom of the window."));
|
||||
|
||||
export const STATUS_BAR_NO_FOLDER_BORDER = registerColor('statusBar.noFolderBorder', {
|
||||
dark: STATUS_BAR_BORDER,
|
||||
light: STATUS_BAR_BORDER,
|
||||
hc: STATUS_BAR_BORDER
|
||||
}, nls.localize('statusBarNoFolderBorder', "Status bar border color separating to the sidebar and editor when no folder is opened. The status bar is shown in the bottom of the window."));
|
||||
|
||||
export const STATUS_BAR_ITEM_ACTIVE_BACKGROUND = registerColor('statusBarItem.activeBackground', {
|
||||
dark: Color.white.transparent(0.18),
|
||||
light: Color.white.transparent(0.18),
|
||||
hc: Color.white.transparent(0.18)
|
||||
}, nls.localize('statusBarItemActiveBackground', "Status bar item background color when clicking. The status bar is shown in the bottom of the window."));
|
||||
|
||||
export const STATUS_BAR_ITEM_HOVER_BACKGROUND = registerColor('statusBarItem.hoverBackground', {
|
||||
dark: Color.white.transparent(0.12),
|
||||
light: Color.white.transparent(0.12),
|
||||
hc: Color.white.transparent(0.12)
|
||||
}, nls.localize('statusBarItemHoverBackground', "Status bar item background color when hovering. The status bar is shown in the bottom of the window."));
|
||||
|
||||
export const STATUS_BAR_PROMINENT_ITEM_BACKGROUND = registerColor('statusBarItem.prominentBackground', {
|
||||
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."));
|
||||
|
||||
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."));
|
||||
|
||||
|
||||
|
||||
// < --- Activity Bar --- >
|
||||
|
||||
export const ACTIVITY_BAR_BACKGROUND = registerColor('activityBar.background', {
|
||||
dark: '#333333',
|
||||
light: '#2C2C2C',
|
||||
hc: '#000000'
|
||||
}, nls.localize('activityBarBackground', "Activity bar background color. The activity bar is showing on the far left or right and allows to switch between views of the side bar."));
|
||||
|
||||
export const ACTIVITY_BAR_FOREGROUND = registerColor('activityBar.foreground', {
|
||||
dark: Color.white,
|
||||
light: Color.white,
|
||||
hc: Color.white
|
||||
}, nls.localize('activityBarForeground', "Activity bar foreground color (e.g. used for the icons). The activity bar is showing on the far left or right and allows to switch between views of the side bar."));
|
||||
|
||||
export const ACTIVITY_BAR_BORDER = registerColor('activityBar.border', {
|
||||
dark: null,
|
||||
light: null,
|
||||
hc: contrastBorder
|
||||
}, nls.localize('activityBarBorder', "Activity bar border color separating to the side bar. The activity bar is showing on the far left or right and allows to switch between views of the side bar."));
|
||||
|
||||
export const ACTIVITY_BAR_DRAG_AND_DROP_BACKGROUND = registerColor('activityBar.dropBackground', {
|
||||
dark: Color.white.transparent(0.12),
|
||||
light: Color.white.transparent(0.12),
|
||||
hc: Color.white.transparent(0.12),
|
||||
}, nls.localize('activityBarDragAndDropBackground', "Drag and drop feedback color for the activity bar items. The color should have transparency so that the activity bar entries can still shine through. The activity bar is showing on the far left or right and allows to switch between views of the side bar."));
|
||||
|
||||
export const ACTIVITY_BAR_BADGE_BACKGROUND = registerColor('activityBarBadge.background', {
|
||||
dark: '#007ACC',
|
||||
light: '#007ACC',
|
||||
hc: '#000000'
|
||||
}, nls.localize('activityBarBadgeBackground', "Activity notification badge background color. The activity bar is showing on the far left or right and allows to switch between views of the side bar."));
|
||||
|
||||
export const ACTIVITY_BAR_BADGE_FOREGROUND = registerColor('activityBarBadge.foreground', {
|
||||
dark: Color.white,
|
||||
light: Color.white,
|
||||
hc: Color.white
|
||||
}, nls.localize('activityBarBadgeForeground', "Activity notification badge foreground color. The activity bar is showing on the far left or right and allows to switch between views of the side bar."));
|
||||
|
||||
|
||||
|
||||
// < --- Side Bar --- >
|
||||
|
||||
export const SIDE_BAR_BACKGROUND = registerColor('sideBar.background', {
|
||||
dark: '#252526',
|
||||
light: '#F3F3F3',
|
||||
hc: '#000000'
|
||||
}, nls.localize('sideBarBackground', "Side bar background color. The side bar is the container for views like explorer and search."));
|
||||
|
||||
export const SIDE_BAR_FOREGROUND = registerColor('sideBar.foreground', {
|
||||
dark: null,
|
||||
light: null,
|
||||
hc: null
|
||||
}, nls.localize('sideBarForeground', "Side bar foreground color. The side bar is the container for views like explorer and search."));
|
||||
|
||||
export const SIDE_BAR_BORDER = registerColor('sideBar.border', {
|
||||
dark: null,
|
||||
light: null,
|
||||
hc: contrastBorder
|
||||
}, nls.localize('sideBarBorder', "Side bar border color on the side separating to the editor. The side bar is the container for views like explorer and search."));
|
||||
|
||||
export const SIDE_BAR_TITLE_FOREGROUND = registerColor('sideBarTitle.foreground', {
|
||||
dark: SIDE_BAR_FOREGROUND,
|
||||
light: SIDE_BAR_FOREGROUND,
|
||||
hc: SIDE_BAR_FOREGROUND
|
||||
}, nls.localize('sideBarTitleForeground', "Side bar title foreground color. The side bar is the container for views like explorer and search."));
|
||||
|
||||
export const SIDE_BAR_DRAG_AND_DROP_BACKGROUND = registerColor('sideBar.dropBackground', {
|
||||
dark: Color.white.transparent(0.12),
|
||||
light: Color.white.transparent(0.12),
|
||||
hc: Color.white.transparent(0.12),
|
||||
}, nls.localize('sideBarDragAndDropBackground', "Drag and drop feedback color for the side bar sections. The color should have transparency so that the side bar sections can still shine through. The side bar is the container for views like explorer and search."));
|
||||
|
||||
export const SIDE_BAR_SECTION_HEADER_BACKGROUND = registerColor('sideBarSectionHeader.background', {
|
||||
dark: Color.fromHex('#808080').transparent(0.2),
|
||||
light: Color.fromHex('#808080').transparent(0.2),
|
||||
hc: null
|
||||
}, nls.localize('sideBarSectionHeaderBackground', "Side bar section header background color. The side bar is the container for views like explorer and search."));
|
||||
|
||||
export const SIDE_BAR_SECTION_HEADER_FOREGROUND = registerColor('sideBarSectionHeader.foreground', {
|
||||
dark: SIDE_BAR_FOREGROUND,
|
||||
light: SIDE_BAR_FOREGROUND,
|
||||
hc: SIDE_BAR_FOREGROUND
|
||||
}, nls.localize('sideBarSectionHeaderForeground', "Side bar section header foreground color. The side bar is the container for views like explorer and search."));
|
||||
|
||||
|
||||
|
||||
// < --- Title Bar --- >
|
||||
|
||||
export const TITLE_BAR_ACTIVE_FOREGROUND = registerColor('titleBar.activeForeground', {
|
||||
dark: '#CCCCCC',
|
||||
light: '#333333',
|
||||
hc: '#FFFFFF'
|
||||
}, nls.localize('titleBarActiveForeground', "Title bar foreground when the window is active. Note that this color is currently only supported on macOS."));
|
||||
|
||||
export const TITLE_BAR_INACTIVE_FOREGROUND = registerColor('titleBar.inactiveForeground', {
|
||||
dark: transparent(TITLE_BAR_ACTIVE_FOREGROUND, 0.6),
|
||||
light: transparent(TITLE_BAR_ACTIVE_FOREGROUND, 0.6),
|
||||
hc: null
|
||||
}, nls.localize('titleBarInactiveForeground', "Title bar foreground when the window is inactive. Note that this color is currently only supported on macOS."));
|
||||
|
||||
export const TITLE_BAR_ACTIVE_BACKGROUND = registerColor('titleBar.activeBackground', {
|
||||
dark: '#3C3C3C',
|
||||
light: '#DDDDDD',
|
||||
hc: '#000000'
|
||||
}, nls.localize('titleBarActiveBackground', "Title bar background when the window is active. Note that this color is currently only supported on macOS."));
|
||||
|
||||
export const TITLE_BAR_INACTIVE_BACKGROUND = registerColor('titleBar.inactiveBackground', {
|
||||
dark: transparent(TITLE_BAR_ACTIVE_BACKGROUND, 0.6),
|
||||
light: transparent(TITLE_BAR_ACTIVE_BACKGROUND, 0.6),
|
||||
hc: null
|
||||
}, nls.localize('titleBarInactiveBackground', "Title bar background when the window is inactive. Note that this color is currently only supported on macOS."));
|
||||
|
||||
export const TITLE_BAR_BORDER = registerColor('titleBar.border', {
|
||||
dark: null,
|
||||
light: null,
|
||||
hc: null
|
||||
}, nls.localize('titleBarBorder', "Title bar border color. Note that this color is currently only supported on macOS."));
|
||||
|
||||
// < --- 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',
|
||||
hc: contrastBorder
|
||||
}, nls.localize('notificationsInfoBackground', "Notifications info background color. Notifications slide in from the top 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',
|
||||
hc: contrastBorder
|
||||
}, nls.localize('notificationsWarningBackground', "Notifications warning background color. Notifications slide in from the top of the window."));
|
||||
|
||||
export const NOTIFICATIONS_WARNING_FOREGROUND = registerColor('notification.warningForeground', {
|
||||
dark: NOTIFICATIONS_FOREGROUND,
|
||||
light: NOTIFICATIONS_FOREGROUND,
|
||||
hc: null
|
||||
}, nls.localize('notificationsWarningForeground', "Notifications warning foreground color. Notifications slide in from the top 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_ERROR_FOREGROUND = registerColor('notification.errorForeground', {
|
||||
dark: NOTIFICATIONS_FOREGROUND,
|
||||
light: NOTIFICATIONS_FOREGROUND,
|
||||
hc: null
|
||||
}, nls.localize('notificationsErrorForeground', "Notifications error foreground color. Notifications slide in from the top of the window."));
|
||||
|
||||
/**
|
||||
* Base class for all themable workbench components.
|
||||
*/
|
||||
export class Themable extends Disposable {
|
||||
private _toUnbind: IDisposable[];
|
||||
private theme: ITheme;
|
||||
|
||||
constructor(
|
||||
protected themeService: IThemeService
|
||||
) {
|
||||
super();
|
||||
|
||||
this._toUnbind = [];
|
||||
this.theme = themeService.getTheme();
|
||||
|
||||
// Hook up to theme changes
|
||||
this._toUnbind.push(this.themeService.onThemeChange(theme => this.onThemeChange(theme)));
|
||||
}
|
||||
|
||||
protected get toUnbind() {
|
||||
return this._toUnbind;
|
||||
}
|
||||
|
||||
protected onThemeChange(theme: ITheme): void {
|
||||
this.theme = theme;
|
||||
|
||||
this.updateStyles();
|
||||
}
|
||||
|
||||
protected updateStyles(): void {
|
||||
// Subclasses to override
|
||||
}
|
||||
|
||||
protected getColor(id: string, modify?: (color: Color, theme: ITheme) => Color): string {
|
||||
let color = this.theme.getColor(id);
|
||||
|
||||
if (color && modify) {
|
||||
color = modify(color, this.theme);
|
||||
}
|
||||
|
||||
return color ? color.toString() : null;
|
||||
}
|
||||
|
||||
public dispose(): void {
|
||||
this._toUnbind = dispose(this._toUnbind);
|
||||
|
||||
super.dispose();
|
||||
}
|
||||
}
|
||||
15
src/vs/workbench/common/viewlet.ts
Normal file
15
src/vs/workbench/common/viewlet.ts
Normal file
@@ -0,0 +1,15 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* 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 { IComposite } from 'vs/workbench/common/composite';
|
||||
|
||||
export interface IViewlet extends IComposite {
|
||||
|
||||
/**
|
||||
* Returns the minimal width needed to avoid any content horizontal truncation
|
||||
*/
|
||||
getOptimalWidth(): number;
|
||||
}
|
||||
Reference in New Issue
Block a user