Merge VS Code 1.26.1 (#2394)

* Squash merge commits for 1.26 (#1) (#2323)

* Polish tag search as per feedback (#55269)

* Polish tag search as per feedback

* Updated regex

* Allow users to opt-out of features that send online requests in the background (#55097)

* settings sweep #54690

* Minor css tweaks to enable eoverflow elipsis in more places (#55277)

* fix an issue with titlebarheight when not scaling with zoom

* Settings descriptions update #54690

* fixes #55209

* Settings editor - many padding fixes

* More space above level 2 label

* Fixing Cannot debug npm script using Yarn #55103

* Settings editor - show ellipsis when description overflows

* Settings editor - ... fix measuring around links, relayout

* Setting descriptions

* Settings editor - fix ... for some short lines, fix select container width

* Settings editor - overlay trees so scrollable shadow is full width

* Fix #54133 - missing extension settings after reload

* Settings color token description tweak

* Settings editor - disable overflow indicator temporarily, needs to be faster

* Added command to Run the selected npm script

* fixes #54452

* fixes #54929

* fixes #55248

* prefix command with extension name

* Contribute run selected to the context menu

* node-debug@1.26.6

* Allow terminal rendererType to be swapped out at runtime

Part of #53274
Fixes #55344

* Settings editor - fix not focusing search when restoring editor
setInput must be actually async. Will be fixed naturally when we aren't using winJS promises...

* Settings editor - TOC should only expand the section with a selected item

* Bump node-debug2

* Settings editor - Tree focus outlines

* Settings editor - don't blink the scrollbar when toc selection changes
And hide TOC correctly when the editor is narrow

* Settings editor - header rows should not be selectable

* fixes #54877

* change debug assignee to isi

* Settings sweep (#54690)

* workaround for #55051

* Settings sweep (#54690)

* settings sweep

#54690

* Don't try closing tags when you type > after another >

* Describe what implementation code lens does

Fixes #55370

* fix javadoc formatter setting description

* fixes #55325

* update to officical TS version

* Settings editor - Even more padding, use semibold instead of bold

* Fix #55357 - fix TOC twistie

* fixes #55288

* explorer: refresh on di change file system provider registration

fixes #53256

* Disable push to Linux repo to test standalone publisher

* New env var to notify log level to extensions #54001

* Disable snippets in extension search (when not in suggest dropdown) (#55281)

* Disable snippits in extension search (when not in suggest dropdown)

* Add monaco input contributions

* Fix bug preventing snippetSuggestions from taking effect in sub-editors

* Latest emmet helper to fix #52366

* Fix comment updates for threads within same file

* Allow extensions to log telemetry to log files #54001

* Pull latest css grammar

* files.exclude control - use same style for "add" vs "edit"

* files.exclude control - focus/keyboard behavior

* don't show menubar too early

* files.exclude - better styling

* Place cursor at end of extensions search box on autofill (#55254)

* Place cursor at end of extensions search box on autofill

* Use position instead of selection

* fix linux build issue (empty if block)

* Settings editor - fix extension category prefixes

* Settings editor - add simple ellipsis for first line that overflows, doesn't cover case when first line does not overflow but there is more text, TODO

* File/Text search provider docs

* Fixes #52655

* Include epoch (#55008)

* Fixes #53385

* Fixes #49480

*  VS Code Insiders (Users) not opening Fixes #55353

* Better handling of the case when the extension host fails to start

* Fixes #53966

*  Remove confusing Start from wordPartLeft commands ID

* vscode-xterm@3.6.0-beta12

Fixes #55488

* Initial size is set to infinity!! Fixes #55461

* Polish embeddedEditorBackground

* configuration service misses event

* Fix #55224 - fix duplicate results in multiroot workspace from splitting the diskseach query

* Select all not working in issue reporter on mac, fixes #55424

* Disable fuzzy matching for extensions autosuggest (#55498)

* Fix clipping of extensions search border in some third party themes (#55504)

* fixes #55538

* Fix bug causing an aria alert to not be shown the third time
 (and odd numbers thereafter)

* Settings editor - work around rendering glitch with webkit-line-clamp

* Settings editor - revert earlier '...' changes

* Settings editor - move enumDescription to its own div, because it disturbs -webkit-line-clamp for some reason

* Settings editor - better overflow indicator

* Don't show existing filters in autocomplete (#55495)

* Dont show existing filters in autocomplete

* Simplify

* Settings Editor: Add aria labels for input elements Fixes: #54836 (#55543)

* fixes #55223

* Update vscode-css-languageservice to 3.0.10-next.1

* Fix #55509 - settings navigation

* Fix #55519

* Fix #55520

* FIx #55524

* Fix #55556 - include wordSeparators in all search queries, so findTextInFiles can respect isWordMatch correctly

* oss updates for endgame

* Fix unit tests

* fixes #55522

* Avoid missing manifest error from bubbling up #54757

* Settings format crawl

* Search provider - Fix FileSearchProvider to return array, not progress

* Fix #55598

* Settings editor - fix NPE rendering settings with no description

* dont render inden guides in search box (#55600)

* fixes #55454

* More settings crawl

* Another change for #55598 - maxResults applies to FileSearch and TextSearch but not FileIndex

* Fix FileSearchProvider unit tests for progress change

* fixes #55561

* Settings description update for #54690

* Update setting descriptions for online services

* Minor edits

* fixes #55513

* fixes #55451

* Fix #55612 - fix findTextInFiles cancellation

* fixes #55539

* More setting description tweaks

* Setting to disable online experiments #54354

* fixes #55507

* fixes #55515

* Show online services action only in Insiders for now

* Settings editor - change toc behavior default to 'filter'

* Settings editor - nicer filter count style during search

* Fix #55617 - search viewlet icons

* Settings editor - better styling for element count indicator

* SearchProvider - fix NPE when searching extraFileResources

* Allow extends to work without json suffix

Fixes #16905

* Remove accessability options logic entirely

Follow up on #55451

* use latest version of DAP

* fixes #55490

* fixes #55122

* fixes #52332

* Avoid assumptions about git: URIs (fixes #36236)

* relative path for descriptions

* resourece: get rid of isFile context key

fixes #48275

* Register previous ids for compatibility (#53497)

* more tuning for #48275

* no need to always re-read "files explorer"

fixes #52003

* read out active composites properly

fixes #51967

* Update link colors for hc theme to meet color contrast ratio, fixes #55651

Also updated link color for `textLinkActiveForeground` to be the same as `textLinkForeground` as it wasn't properly updated

* detect 'winpty-agent.exe'; fixes #55672

* node-debug@1.26.7

* reset counter on new label

* Settings editor - fix multiple setting links in one description

* Settings editor - color code blocks in setting descriptions, fix #55532

* Settings editor - hover color in TOC

* Settings editor - fix navigation NPE

* Settings editor - fix text control width

* Settings editor - maybe fix #55684

* Fix bug causing cursor to not move on paste

* fixes #53582

* Use ctrlCmd instead of ctrl for go down from search box

* fixes #55264

* fixes #55456

* filter for spcaes before triggering search (#55611)

* Fix #55698 - don't lose filtered TOC counts when refreshing TOC

* fixes #55421

* fixes #28979

* fixes #55576

* only add check for updates to windows/linux help

* readonly files: append decoration to label

fixes #53022

* debug: do not show toolbar while initialising

fixes #55026

* Opening launch.json should not activate debug extensions

fixes #55029

* fixes #55435

* fixes #55434

* fixes #55439

* trigger menu only on altkey up

* Fix #50555 - fix settings editor memory leak

* Fix #55712 - no need to focus 'a' anymore when restoring control focus after tree render

* fixes #55335

* proper fix for readonly model

fixes #53022

* improve FoldingRangeKind spec (for #55686)

* Use class with static fields (fixes #55494)

* Fixes #53671

* fixes #54630

* [html] should disable ionic suggestions by default. Currently forces deprecated Ionic v1 suggestions in .html files while typing. Fixes #53324

* cleanup deps

* debug issues back to andre

* update electron for smoketest

* Fix #55757 - prevent settings tabs from overflowing

* Fix #53897 - revert setting menu defaults to old editor

* Add enum descriptions to `typescript.preferences.importModuleSpecifier`

* Fix #55767 - leaking style elements from settings editor

* Fix #55521 - prevent flashing when clicking in exclude control

* Update Git modified color for contrast ratio, fixes #53140

* Revert "Merge branch 'master' of github.com:Microsoft/vscode"

This reverts commit bf46b6bfbae0cab99c2863e1244a916181fa9fbc, reversing
changes made to e275a424483dfb4ed33b428c97d5e2c441d6b917.

* Revert "Revert "Merge branch 'master' of github.com:Microsoft/vscode""

This reverts commit 53949d963f39e40757557c6526332354a31d9154.

* don't ask to install an incomplete menu

* Fix NPE in terminal AccessibilityManager

Fixes #55744

* don't display fallback menu unless we've closed the last window

* fixes #55547

* Fix smoke tests for extension search box

* Update OSSREADME.json for Electron 2.0.5

* Update distro

Includes Chromium license changes

* fix #55455

* fix #55865

* fixes #55893

* Fix bug causing workspace recommendations to go away upon ignoring a recommendation (#55805)

* Fix bug causing workspace recommendations to go away upon ignoring a recommendation

* ONly show on @recommended or @recommended:workspace

* Make more consistant

* Fix #55911

* Understand json activity (#55926)

* Understand json file activity

* Refactoring

* adding composer.json

* Distro update for experiments

* use terminal.processId for auto-attach; fixes #55918

* Reject invalid URI with vscode.openFolder (for #55891)

* improve win32 setup system vs user detection

fixes #55840

fixes #55840

delay winreg import

related to #55840

show notification earlier

related to #55840

fix #55840

update inno setup message

related to #55840

* Fix #55593 - this code only operates on local paths, so use fsPath and Uri.file instead

* Bring back the old menu due to electron 2.0 issues (#55913)

* add the old menu back for native menus

* make menu labels match

* `vscode.openFolder`: treat missing URI schema gracefully (for #55891)

* delay EH reattach; fixes #55955

* Mark all json files under appSettingsHome as settings

* Use localized strings for telemetry opt-out

* Exception when saving file editor opened from remote file provider (fixes #55051)

* Remove terminal menu from stable

Fixes 56003

* VSCode Insiders crashes on open with TypeError: Cannot read property 'lastIndexOf' of undefined. Fixes #54933

* improve fix for #55891

* fix #55916

* Improve #55891

* increase EH debugging restart delay; fixes #55955

* Revert "Don't include non-resource entries in history quick pick"

This reverts commit 37209a838e9f7e9abe6dc53ed73cdf1e03b72060.

* Diff editor: horizontal scrollbar height is smaller (fixes #56062)

* improve openFolder uri fix (correctly treat backslashes)

* fixes #56116
repair ipc for native menubar keybindings

* Fix #56240 - Open the JSON settings editor instead of the UI editor

* Fix #55536

* uriDisplay: if no formatter is registered fall back to getPathlabel

fixes #56104

* VSCode hangs when opening python file. Fixes #56377

* VS Code Hangs When Opening Specific PowerShell File. Fixes #56430

* Fix #56433 - search extraFileResources even when no folders open

* Workaround #55649

* Fix in master #56371

* Fix tests #56371

* Fix in master #56317

* increase version to 1.26.1

* Fixes #56387: Handle SIGPIPE in extension host

* fixes #56185

* Fix merge issues (part 1)

* Fix build breaks (part 1)

* Build breaks (part 2)

* Build breaks (part 3)

* More build breaks (part 4)

* Fix build breaks (part 5)

* WIP

* Fix menus

* Render query result and message panels (#2363)

* Put back query editor hot exit changes

* Fix grid changes that broke profiler (#2365)

* Update APIs for saving query editor state

* Fix restore view state for profiler and edit data

* Updating custom default themes to support 4.5:1 contrast ratio

* Test updates

* Fix Extension Manager and Windows Setup

* Update license headers

* Add appveyor and travis files back

* Fix hidden modal dropdown issue
This commit is contained in:
Karl Burtram
2018-09-04 14:55:00 -07:00
committed by GitHub
parent 3763278366
commit 81329fa7fa
2638 changed files with 118456 additions and 64012 deletions

View File

@@ -6,7 +6,7 @@
import { TPromise } from 'vs/base/common/winjs.base';
import { Registry } from 'vs/platform/registry/common/platform';
import { KeybindingsRegistry } from 'vs/platform/keybinding/common/keybindingsRegistry';
import { KeybindingsRegistry, KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry';
import { ICommandHandler, CommandsRegistry } from 'vs/platform/commands/common/commands';
import { SyncActionDescriptor, MenuRegistry, MenuId } from 'vs/platform/actions/common/actions';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
@@ -41,7 +41,7 @@ Registry.add(Extensions.WorkbenchActions, new class implements IWorkbenchActionR
// keybinding
const when = descriptor.keybindingContext;
const weight = (typeof descriptor.keybindingWeight === 'undefined' ? KeybindingsRegistry.WEIGHT.workbenchContrib() : descriptor.keybindingWeight);
const weight = (typeof descriptor.keybindingWeight === 'undefined' ? KeybindingWeight.WorkbenchContrib : descriptor.keybindingWeight);
const keybindings = descriptor.keybindings;
KeybindingsRegistry.registerKeybindingRule({
id: descriptor.id,
@@ -95,10 +95,10 @@ Registry.add(Extensions.WorkbenchActions, new class implements IWorkbenchActionR
};
}
private _triggerAndDisposeAction(instantitationService: IInstantiationService, lifecycleService: ILifecycleService, descriptor: SyncActionDescriptor, args: any): Thenable<void> {
private _triggerAndDisposeAction(instantiationService: IInstantiationService, lifecycleService: ILifecycleService, descriptor: SyncActionDescriptor, args: any): Thenable<void> {
// run action when workbench is created
return lifecycleService.when(LifecyclePhase.Running).then(() => {
const actionInstance = instantitationService.createInstance(descriptor.syncDescriptor);
const actionInstance = instantiationService.createInstance(descriptor.syncDescriptor);
try {
actionInstance.label = descriptor.label || actionInstance.label;

View File

@@ -45,7 +45,7 @@ export class Component extends Themable implements IWorkbenchComponent {
this.componentMemento = new Memento(this.id);
}
public getId(): string {
getId(): string {
return this.id;
}
@@ -73,7 +73,7 @@ export class Component extends Themable implements IWorkbenchComponent {
this.componentMemento.saveMemento();
}
public shutdown(): void {
shutdown(): void {
// Save Memento
this.saveMemento();

View File

@@ -4,7 +4,6 @@
*--------------------------------------------------------------------------------------------*/
import { IAction, IActionItem } from 'vs/base/common/actions';
import { IEditorControl } from 'vs/platform/editor/common/editor';
export interface IComposite {
@@ -41,10 +40,15 @@ export interface IComposite {
/**
* Returns the underlying control of this composite.
*/
getControl(): IEditorControl;
getControl(): ICompositeControl;
/**
* Asks the underlying control to focus.
*/
focus(): void;
}
/**
* Marker interface for the composite control
*/
export interface ICompositeControl { }

View File

@@ -45,7 +45,7 @@ export class WorkbenchContributionsRegistry implements IWorkbenchContributionsRe
private toBeInstantiated: Map<LifecyclePhase, IConstructorSignature0<IWorkbenchContribution>[]> = new Map<LifecyclePhase, IConstructorSignature0<IWorkbenchContribution>[]>();
public registerWorkbenchContribution(ctor: IWorkbenchContributionSignature, phase: LifecyclePhase = LifecyclePhase.Starting): void {
registerWorkbenchContribution(ctor: IWorkbenchContributionSignature, phase: LifecyclePhase = LifecyclePhase.Starting): void {
// Instantiate directly if we are already matching the provided phase
if (this.instantiationService && this.lifecycleService && this.lifecycleService.phase >= phase) {
@@ -64,7 +64,7 @@ export class WorkbenchContributionsRegistry implements IWorkbenchContributionsRe
}
}
public start(instantiationService: IInstantiationService, lifecycleService: ILifecycleService): void {
start(instantiationService: IInstantiationService, lifecycleService: ILifecycleService): void {
this.instantiationService = instantiationService;
this.lifecycleService = lifecycleService;

File diff suppressed because it is too large Load Diff

View File

@@ -44,39 +44,39 @@ export class BinaryEditorModel extends EditorModel {
/**
* The name of the binary resource.
*/
public getName(): string {
getName(): string {
return this.name;
}
/**
* The resource of the binary resource.
*/
public getResource(): URI {
getResource(): URI {
return this.resource;
}
/**
* The size of the binary resource if known.
*/
public getSize(): number {
getSize(): number {
return this.size;
}
/**
* The mime of the binary resource if known.
*/
public getMime(): string {
getMime(): string {
return this.mime;
}
/**
* The etag of the binary resource if known.
*/
public getETag(): string {
getETag(): string {
return this.etag;
}
public load(): TPromise<EditorModel> {
load(): TPromise<EditorModel> {
// Make sure to resolve up to date stat for file resources
if (this.fileService.canHandleResource(this.resource)) {

View File

@@ -49,27 +49,27 @@ export class DataUriEditorInput extends EditorInput {
}
}
public getResource(): URI {
getResource(): URI {
return this.resource;
}
public getTypeId(): string {
getTypeId(): string {
return DataUriEditorInput.ID;
}
public getName(): string {
getName(): string {
return this.name;
}
public getDescription(): string {
getDescription(): string {
return this.description;
}
public resolve(refresh?: boolean): TPromise<BinaryEditorModel> {
resolve(): TPromise<BinaryEditorModel> {
return this.instantiationService.createInstance(BinaryEditorModel, this.resource, this.getName()).load().then(m => m as BinaryEditorModel);
}
public matches(otherInput: any): boolean {
matches(otherInput: any): boolean {
if (super.matches(otherInput) === true) {
return true;
}

View File

@@ -16,7 +16,7 @@ import { TextDiffEditorModel } from 'vs/workbench/common/editor/textDiffEditorMo
*/
export class DiffEditorInput extends SideBySideEditorInput {
public static readonly ID = 'workbench.editors.diffEditorInput';
static readonly ID = 'workbench.editors.diffEditorInput';
private cachedModel: DiffEditorModel;
@@ -24,7 +24,7 @@ export class DiffEditorInput extends SideBySideEditorInput {
super(name, description, original, modified);
}
public getTypeId(): string {
getTypeId(): string {
return DiffEditorInput.ID;
}
@@ -36,23 +36,13 @@ export class DiffEditorInput extends SideBySideEditorInput {
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);
}
resolve(): TPromise<EditorModel> {
// 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) => {
return this.createModel().then(resolvedModel => {
if (this.cachedModel) {
this.cachedModel.dispose();
}
@@ -63,7 +53,7 @@ export class DiffEditorInput extends SideBySideEditorInput {
});
}
public getPreferredEditorId(candidates: string[]): string {
getPreferredEditorId(candidates: string[]): string {
return this.forceOpenAsBinary ? BINARY_DIFF_EDITOR_ID : TEXT_DIFF_EDITOR_ID;
}
@@ -71,9 +61,9 @@ export class DiffEditorInput extends SideBySideEditorInput {
// Join resolve call over two inputs and build diff editor model
return TPromise.join([
this.originalInput.resolve(refresh),
this.modifiedInput.resolve(refresh)
]).then((models) => {
this.originalInput.resolve(),
this.modifiedInput.resolve()
]).then(models => {
const originalEditorModel = models[0];
const modifiedEditorModel = models[1];
@@ -87,7 +77,7 @@ export class DiffEditorInput extends SideBySideEditorInput {
});
}
public dispose(): void {
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

View File

@@ -23,15 +23,15 @@ export class DiffEditorModel extends EditorModel {
this._modifiedModel = modifiedModel;
}
public get originalModel(): EditorModel {
get originalModel(): EditorModel {
return this._originalModel as EditorModel;
}
public get modifiedModel(): EditorModel {
get modifiedModel(): EditorModel {
return this._modifiedModel as EditorModel;
}
public load(): TPromise<EditorModel> {
load(): TPromise<EditorModel> {
return TPromise.join([
this._originalModel.load(),
this._modifiedModel.load()
@@ -40,11 +40,11 @@ export class DiffEditorModel extends EditorModel {
});
}
public isResolved(): boolean {
isResolved(): boolean {
return this.originalModel.isResolved() && this.modifiedModel.isResolved();
}
public dispose(): void {
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

View File

@@ -0,0 +1,692 @@
/*---------------------------------------------------------------------------------------------
* 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 { Event, Emitter, once } from 'vs/base/common/event';
import { Extensions, IEditorInputFactoryRegistry, EditorInput, toResource, IEditorIdentifier, IEditorCloseEvent, GroupIdentifier, SideBySideEditorInput, CloseDirection } from 'vs/workbench/common/editor';
import URI from 'vs/base/common/uri';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { IConfigurationService, IConfigurationChangeEvent } from 'vs/platform/configuration/common/configuration';
import { dispose, IDisposable, Disposable } from 'vs/base/common/lifecycle';
import { Registry } from 'vs/platform/registry/common/platform';
import { ResourceMap } from 'vs/base/common/map';
// {{SQL CARBON EDIT}}
import { QueryInput } from 'sql/parts/query/common/queryInput';
import * as CustomInputConverter from 'sql/parts/common/customInputConverter';
const EditorOpenPositioning = {
LEFT: 'left',
RIGHT: 'right',
FIRST: 'first',
LAST: 'last'
};
export interface EditorCloseEvent extends IEditorCloseEvent {
editor: EditorInput;
}
export interface EditorIdentifier extends IEditorIdentifier {
groupId: GroupIdentifier;
editor: EditorInput;
}
export interface IEditorOpenOptions {
pinned?: boolean;
active?: boolean;
index?: number;
}
export interface ISerializedEditorInput {
id: string;
value: string;
}
export interface ISerializedEditorGroup {
id: number;
editors: ISerializedEditorInput[];
mru: number[];
preview: number;
}
export function isSerializedEditorGroup(obj?: any): obj is ISerializedEditorGroup {
const group = obj as ISerializedEditorGroup;
return obj && typeof obj === 'object' && Array.isArray(group.editors) && Array.isArray(group.mru);
}
export class EditorGroup extends Disposable {
private static IDS = 0;
//#region events
private readonly _onDidEditorActivate = this._register(new Emitter<EditorInput>());
get onDidEditorActivate(): Event<EditorInput> { return this._onDidEditorActivate.event; }
private readonly _onDidEditorOpen = this._register(new Emitter<EditorInput>());
get onDidEditorOpen(): Event<EditorInput> { return this._onDidEditorOpen.event; }
private readonly _onDidEditorClose = this._register(new Emitter<EditorCloseEvent>());
get onDidEditorClose(): Event<EditorCloseEvent> { return this._onDidEditorClose.event; }
private readonly _onDidEditorDispose = this._register(new Emitter<EditorInput>());
get onDidEditorDispose(): Event<EditorInput> { return this._onDidEditorDispose.event; }
private readonly _onDidEditorBecomeDirty = this._register(new Emitter<EditorInput>());
get onDidEditorBecomeDirty(): Event<EditorInput> { return this._onDidEditorBecomeDirty.event; }
private readonly _onDidEditorLabelChange = this._register(new Emitter<EditorInput>());
get onDidEditorLabelChange(): Event<EditorInput> { return this._onDidEditorLabelChange.event; }
private readonly _onDidEditorMove = this._register(new Emitter<EditorInput>());
get onDidEditorMove(): Event<EditorInput> { return this._onDidEditorMove.event; }
private readonly _onDidEditorPin = this._register(new Emitter<EditorInput>());
get onDidEditorPin(): Event<EditorInput> { return this._onDidEditorPin.event; }
private readonly _onDidEditorUnpin = this._register(new Emitter<EditorInput>());
get onDidEditorUnpin(): Event<EditorInput> { return this._onDidEditorUnpin.event; }
//#endregion
private _id: GroupIdentifier;
private editors: EditorInput[] = [];
private mru: EditorInput[] = [];
private mapResourceToEditorCount: ResourceMap<number> = new ResourceMap<number>();
private preview: EditorInput; // editor in preview state
private active: EditorInput; // editor in active state
private editorOpenPositioning: 'left' | 'right' | 'first' | 'last';
constructor(
labelOrSerializedGroup: ISerializedEditorGroup,
@IInstantiationService private instantiationService: IInstantiationService,
@IConfigurationService private configurationService: IConfigurationService
) {
super();
if (isSerializedEditorGroup(labelOrSerializedGroup)) {
this.deserialize(labelOrSerializedGroup);
} else {
this._id = EditorGroup.IDS++;
}
this.onConfigurationUpdated();
this.registerListeners();
}
private registerListeners(): void {
this._register(this.configurationService.onDidChangeConfiguration(e => this.onConfigurationUpdated(e)));
}
private onConfigurationUpdated(event?: IConfigurationChangeEvent): void {
this.editorOpenPositioning = this.configurationService.getValue('workbench.editor.openPositioning');
}
get id(): GroupIdentifier {
return this._id;
}
get count(): number {
return this.editors.length;
}
getEditors(mru?: boolean): EditorInput[] {
return mru ? this.mru.slice(0) : this.editors.slice(0);
}
getEditor(index: number): EditorInput;
getEditor(resource: URI): EditorInput;
getEditor(arg1: any): EditorInput {
if (typeof arg1 === 'number') {
return this.editors[arg1];
}
const resource: URI = arg1;
if (!this.contains(resource)) {
return null; // fast check for resource opened or not
}
for (let i = 0; i < this.editors.length; i++) {
const editor = this.editors[i];
const editorResource = toResource(editor, { supportSideBySide: true });
if (editorResource && editorResource.toString() === resource.toString()) {
return editor;
}
}
return null;
}
get activeEditor(): EditorInput {
return this.active;
}
isActive(editor: EditorInput): boolean {
return this.matches(this.active, editor);
}
get previewEditor(): EditorInput {
return this.preview;
}
isPreview(editor: EditorInput): boolean {
return this.matches(this.preview, editor);
}
openEditor(editor: EditorInput, options?: IEditorOpenOptions): void {
const index = this.indexOf(editor);
const makePinned = options && options.pinned;
const makeActive = (options && options.active) || !this.activeEditor || (!makePinned && this.matches(this.preview, this.activeEditor));
// New editor
if (index === -1) {
let targetIndex: number;
const indexOfActive = this.indexOf(this.active);
// Insert into specific position
if (options && typeof options.index === 'number') {
targetIndex = options.index;
}
// Insert to the BEGINNING
else if (this.editorOpenPositioning === EditorOpenPositioning.FIRST) {
targetIndex = 0;
}
// Insert to the END
else if (this.editorOpenPositioning === EditorOpenPositioning.LAST) {
targetIndex = this.editors.length;
}
// Insert to the LEFT of active editor
else if (this.editorOpenPositioning === EditorOpenPositioning.LEFT) {
if (indexOfActive === 0 || !this.editors.length) {
targetIndex = 0; // to the left becoming first editor in list
} else {
targetIndex = indexOfActive; // to the left of active editor
}
}
// Insert to the RIGHT of active editor
else {
targetIndex = indexOfActive + 1;
}
// Insert into our list of editors if pinned or we have no preview editor
if (makePinned || !this.preview) {
this.splice(targetIndex, false, editor);
}
// Handle preview
if (!makePinned) {
// Replace existing preview with this editor if we have a preview
if (this.preview) {
const indexOfPreview = this.indexOf(this.preview);
if (targetIndex > indexOfPreview) {
targetIndex--; // accomodate for the fact that the preview editor closes
}
this.replaceEditor(this.preview, editor, targetIndex, !makeActive);
}
this.preview = editor;
}
// Listeners
this.registerEditorListeners(editor);
// Event
this._onDidEditorOpen.fire(editor);
// Handle active
if (makeActive) {
this.setActive(editor);
}
}
// Existing editor
else {
// Pin it
if (makePinned) {
this.pin(editor);
}
// Activate it
if (makeActive) {
this.setActive(editor);
}
// Respect index
if (options && typeof options.index === 'number') {
this.moveEditor(editor, options.index);
}
}
}
private registerEditorListeners(editor: EditorInput): void {
const unbind: IDisposable[] = [];
// Re-emit disposal of editor input as our own event
const onceDispose = once(editor.onDispose);
unbind.push(onceDispose(() => {
if (this.indexOf(editor) >= 0) {
this._onDidEditorDispose.fire(editor);
}
}));
// Re-Emit dirty state changes
unbind.push(editor.onDidChangeDirty(() => {
this._onDidEditorBecomeDirty.fire(editor);
}));
// Re-Emit label changes
unbind.push(editor.onDidChangeLabel(() => {
this._onDidEditorLabelChange.fire(editor);
}));
// Clean up dispose listeners once the editor gets closed
unbind.push(this.onDidEditorClose(event => {
if (event.editor.matches(editor)) {
dispose(unbind);
}
}));
}
private replaceEditor(toReplace: EditorInput, replaceWidth: EditorInput, replaceIndex: number, openNext = true): void {
const event = this.doCloseEditor(toReplace, openNext, true); // optimization to prevent multiple setActive() in one call
// We want to first add the new editor into our model before emitting the close event because
// firing the close event can trigger a dispose on the same editor that is now being added.
// This can lead into opening a disposed editor which is not what we want.
this.splice(replaceIndex, false, replaceWidth);
if (event) {
this._onDidEditorClose.fire(event);
}
}
closeEditor(editor: EditorInput, openNext = true): number {
const event = this.doCloseEditor(editor, openNext, false);
if (event) {
this._onDidEditorClose.fire(event);
return event.index;
}
return void 0;
}
private doCloseEditor(editor: EditorInput, openNext: boolean, replaced: boolean): EditorCloseEvent {
const index = this.indexOf(editor);
if (index === -1) {
return null; // not found
}
// Active Editor closed
if (openNext && this.matches(this.active, editor)) {
// More than one editor
if (this.mru.length > 1) {
this.setActive(this.mru[1]); // active editor is always first in MRU, so pick second editor after as new active
}
// One Editor
else {
this.active = null;
}
}
// Preview Editor closed
if (this.matches(this.preview, editor)) {
this.preview = null;
}
// Remove from arrays
this.splice(index, true);
// Event
return { editor, replaced, index, groupId: this.id };
}
closeEditors(except: EditorInput, direction?: CloseDirection): void {
const index = this.indexOf(except);
if (index === -1) {
return; // not found
}
// Close to the left
if (direction === CloseDirection.LEFT) {
for (let i = index - 1; i >= 0; i--) {
this.closeEditor(this.editors[i]);
}
}
// Close to the right
else if (direction === CloseDirection.RIGHT) {
for (let i = this.editors.length - 1; i > index; i--) {
this.closeEditor(this.editors[i]);
}
}
// Both directions
else {
this.mru.filter(e => !this.matches(e, except)).forEach(e => this.closeEditor(e));
}
}
closeAllEditors(): void {
// Optimize: close all non active editors first to produce less upstream work
this.mru.filter(e => !this.matches(e, this.active)).forEach(e => this.closeEditor(e));
this.closeEditor(this.active);
}
moveEditor(editor: EditorInput, toIndex: number): void {
const index = this.indexOf(editor);
if (index < 0) {
return;
}
// Move
this.editors.splice(index, 1);
this.editors.splice(toIndex, 0, editor);
// Event
this._onDidEditorMove.fire(editor);
}
setActive(editor: EditorInput): void {
const index = this.indexOf(editor);
if (index === -1) {
return; // not found
}
if (this.matches(this.active, editor)) {
return; // already active
}
this.active = editor;
// Bring to front in MRU list
this.setMostRecentlyUsed(editor);
// Event
this._onDidEditorActivate.fire(editor);
}
pin(editor: EditorInput): void {
const index = this.indexOf(editor);
if (index === -1) {
return; // not found
}
if (!this.isPreview(editor)) {
return; // can only pin a preview editor
}
// Convert the preview editor to be a pinned editor
this.preview = null;
// Event
this._onDidEditorPin.fire(editor);
}
unpin(editor: EditorInput): void {
const index = this.indexOf(editor);
if (index === -1) {
return; // not found
}
if (!this.isPinned(editor)) {
return; // can only unpin a pinned editor
}
// Set new
const oldPreview = this.preview;
this.preview = editor;
// Event
this._onDidEditorUnpin.fire(editor);
// Close old preview editor if any
this.closeEditor(oldPreview);
}
isPinned(editor: EditorInput): boolean;
isPinned(index: number): boolean;
isPinned(arg1: EditorInput | number): boolean {
let editor: EditorInput;
let index: number;
if (typeof arg1 === 'number') {
editor = this.editors[arg1];
index = arg1;
} else {
editor = arg1;
index = this.indexOf(editor);
}
if (index === -1 || !editor) {
return false; // editor not found
}
if (!this.preview) {
return true; // no preview editor
}
return !this.matches(this.preview, editor);
}
private splice(index: number, del: boolean, editor?: EditorInput): void {
const editorToDeleteOrReplace = this.editors[index];
const args: any[] = [index, del ? 1 : 0];
if (editor) {
args.push(editor);
}
// Perform on editors array
this.editors.splice.apply(this.editors, args);
// Add
if (!del && editor) {
this.mru.push(editor); // make it LRU editor
this.updateResourceMap(editor, false /* add */); // add new to resource map
}
// Remove / Replace
else {
const indexInMRU = this.indexOf(editorToDeleteOrReplace, this.mru);
// Remove
if (del && !editor) {
this.mru.splice(indexInMRU, 1); // remove from MRU
this.updateResourceMap(editorToDeleteOrReplace, true /* delete */); // remove from resource map
}
// Replace
else {
this.mru.splice(indexInMRU, 1, editor); // replace MRU at location
this.updateResourceMap(editor, false /* add */); // add new to resource map
this.updateResourceMap(editorToDeleteOrReplace, true /* delete */); // remove replaced from resource map
}
}
}
private updateResourceMap(editor: EditorInput, remove: boolean): void {
const resource = toResource(editor, { supportSideBySide: true });
if (resource) {
// It is possible to have the same resource opened twice (once as normal input and once as diff input)
// So we need to do ref counting on the resource to provide the correct picture
let counter = this.mapResourceToEditorCount.get(resource) || 0;
let newCounter: number;
if (remove) {
if (counter > 1) {
newCounter = counter - 1;
}
} else {
newCounter = counter + 1;
}
this.mapResourceToEditorCount.set(resource, newCounter);
}
}
indexOf(candidate: EditorInput, editors = this.editors): number {
if (!candidate) {
return -1;
}
for (let i = 0; i < editors.length; i++) {
if (this.matches(editors[i], candidate)) {
return i;
}
}
return -1;
}
contains(editorOrResource: EditorInput | URI): boolean;
contains(editor: EditorInput, supportSideBySide?: boolean): boolean;
contains(editorOrResource: EditorInput | URI, supportSideBySide?: boolean): boolean {
if (editorOrResource instanceof EditorInput) {
const index = this.indexOf(editorOrResource);
if (index >= 0) {
return true;
}
if (supportSideBySide && editorOrResource instanceof SideBySideEditorInput) {
const index = this.indexOf(editorOrResource.master);
if (index >= 0) {
return true;
}
}
return false;
}
const counter = this.mapResourceToEditorCount.get(editorOrResource);
return typeof counter === 'number' && counter > 0;
}
private setMostRecentlyUsed(editor: EditorInput): void {
const index = this.indexOf(editor);
if (index === -1) {
return; // editor not found
}
const mruIndex = this.indexOf(editor, this.mru);
// Remove old index
this.mru.splice(mruIndex, 1);
// Set editor to front
this.mru.unshift(editor);
}
private matches(editorA: EditorInput, editorB: EditorInput): boolean {
return !!editorA && !!editorB && editorA.matches(editorB);
}
clone(): EditorGroup {
const group = this.instantiationService.createInstance(EditorGroup, void 0);
group.editors = this.editors.slice(0);
group.mru = this.mru.slice(0);
group.mapResourceToEditorCount = this.mapResourceToEditorCount.clone();
group.preview = this.preview;
group.active = this.active;
group.editorOpenPositioning = this.editorOpenPositioning;
return group;
}
serialize(): ISerializedEditorGroup {
const registry = Registry.as<IEditorInputFactoryRegistry>(Extensions.EditorInputFactories);
// Serialize all editor inputs so that we can store them.
// Editors that cannot be serialized need to be ignored
// from mru, active and preview if any.
let serializableEditors: EditorInput[] = [];
let serializedEditors: ISerializedEditorInput[] = [];
let serializablePreviewIndex: number;
// {{SQL CARBON EDIT}}
let editors = this.editors.map(e => {
if (e instanceof QueryInput) {
return e.sql;
}
return e;
});
editors.forEach(e => {
let factory = registry.getEditorInputFactory(e.getTypeId());
if (factory) {
let value = factory.serialize(e);
if (typeof value === 'string') {
serializedEditors.push({ id: e.getTypeId(), value });
serializableEditors.push(e);
if (this.preview === e) {
serializablePreviewIndex = serializableEditors.length - 1;
}
}
}
});
// {{SQL CARBON EDIT}}
let mru = this.mru.map(e => {
if (e instanceof QueryInput) {
return e.sql;
}
return e;
});
const serializableMru = mru.map(e => this.indexOf(e, serializableEditors)).filter(i => i >= 0);
return {
id: this.id,
editors: serializedEditors,
mru: serializableMru,
preview: serializablePreviewIndex,
};
}
private deserialize(data: ISerializedEditorGroup): void {
const registry = Registry.as<IEditorInputFactoryRegistry>(Extensions.EditorInputFactories);
if (typeof data.id === 'number') {
this._id = data.id;
EditorGroup.IDS = Math.max(data.id + 1, EditorGroup.IDS); // make sure our ID generator is always larger
} else {
this._id = EditorGroup.IDS++; // backwards compatibility
}
this.editors = data.editors.map(e => {
const factory = registry.getEditorInputFactory(e.id);
if (factory) {
const editor = factory.deserialize(this.instantiationService, e.value);
this.registerEditorListeners(editor);
this.updateResourceMap(editor, false /* add */);
// {{SQL CARBON EDIT}}
return CustomInputConverter.convertEditorInput(editor, undefined, this.instantiationService);
}
return null;
}).filter(e => !!e);
this.mru = data.mru.map(i => this.editors[i]);
this.active = this.mru[0];
this.preview = this.editors[data.preview];
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -40,37 +40,37 @@ export class ResourceEditorInput extends EditorInput {
this.resource = resource;
}
public getResource(): URI {
getResource(): URI {
return this.resource;
}
public getTypeId(): string {
getTypeId(): string {
return ResourceEditorInput.ID;
}
public getName(): string {
getName(): string {
return this.name;
}
public setName(name: string): void {
setName(name: string): void {
if (this.name !== name) {
this.name = name;
this._onDidChangeLabel.fire();
}
}
public getDescription(): string {
getDescription(): string {
return this.description;
}
public setDescription(description: string): void {
setDescription(description: string): void {
if (this.description !== description) {
this.description = description;
this._onDidChangeLabel.fire();
}
}
public getTelemetryDescriptor(): object {
getTelemetryDescriptor(): object {
const descriptor = super.getTelemetryDescriptor();
descriptor['resource'] = telemetryURIDescriptor(this.resource, path => this.hashService.createSHA1(path));
@@ -82,7 +82,7 @@ export class ResourceEditorInput extends EditorInput {
return descriptor;
}
public resolve(refresh?: boolean): TPromise<ITextEditorModel> {
resolve(): TPromise<ITextEditorModel> {
if (!this.modelReference) {
this.modelReference = this.textModelResolverService.createModelReference(this.resource);
}
@@ -101,7 +101,7 @@ export class ResourceEditorInput extends EditorInput {
});
}
public matches(otherInput: any): boolean {
matches(otherInput: any): boolean {
if (super.matches(otherInput) === true) {
return true;
}
@@ -116,7 +116,7 @@ export class ResourceEditorInput extends EditorInput {
return false;
}
public dispose(): void {
dispose(): void {
if (this.modelReference) {
this.modelReference.done(ref => ref.dispose());
this.modelReference = null;

View File

@@ -24,4 +24,8 @@ export class ResourceEditorModel extends BaseTextEditorModel {
// TODO@Joao: force this class to dispose the underlying model
this.createdEditorModel = true;
}
isReadonly(): boolean {
return true;
}
}

View File

@@ -31,7 +31,7 @@ export class TextDiffEditorModel extends DiffEditorModel {
return this._modifiedModel as BaseTextEditorModel;
}
public load(): TPromise<EditorModel> {
load(): TPromise<EditorModel> {
return super.load().then(() => {
this.updateTextDiffEditorModel();
@@ -58,15 +58,19 @@ export class TextDiffEditorModel extends DiffEditorModel {
}
}
public get textDiffEditorModel(): IDiffEditorModel {
get textDiffEditorModel(): IDiffEditorModel {
return this._textDiffEditorModel;
}
public isResolved(): boolean {
isResolved(): boolean {
return !!this._textDiffEditorModel;
}
public dispose(): void {
isReadonly(): boolean {
return this.modifiedModel.isReadonly();
}
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
@@ -76,4 +80,4 @@ export class TextDiffEditorModel extends DiffEditorModel {
super.dispose();
}
}
}

View File

@@ -19,8 +19,10 @@ import { ITextSnapshot } from 'vs/platform/files/common/files';
* The base text editor model leverages the code editor model. This class is only intended to be subclassed and not instantiated.
*/
export abstract class BaseTextEditorModel extends EditorModel implements ITextEditorModel {
private textEditorModelHandle: URI;
protected createdEditorModel: boolean;
private textEditorModelHandle: URI;
private modelDisposeListener: IDisposable;
constructor(
@@ -60,10 +62,12 @@ export abstract class BaseTextEditorModel extends EditorModel implements ITextEd
});
}
public get textEditorModel(): ITextModel {
get textEditorModel(): ITextModel {
return this.textEditorModelHandle ? this.modelService.getModel(this.textEditorModelHandle) : null;
}
abstract isReadonly(): boolean;
/**
* Creates the text editor model with the provided value, modeId (can be comma separated for multiple values) and optional resource URL.
*/
@@ -124,7 +128,7 @@ export abstract class BaseTextEditorModel extends EditorModel implements ITextEd
this.modelService.updateModel(this.textEditorModel, newValue);
}
public createSnapshot(): ITextSnapshot {
createSnapshot(): ITextSnapshot {
const model = this.textEditorModel;
if (model) {
return model.createSnapshot(true /* Preserve BOM */);
@@ -133,11 +137,11 @@ export abstract class BaseTextEditorModel extends EditorModel implements ITextEd
return null;
}
public isResolved(): boolean {
isResolved(): boolean {
return !!this.textEditorModelHandle;
}
public dispose(): void {
dispose(): void {
if (this.modelDisposeListener) {
this.modelDisposeListener.dispose(); // dispose this first because it will trigger another dispose() otherwise
this.modelDisposeListener = null;

View File

@@ -8,37 +8,34 @@ import { TPromise } from 'vs/base/common/winjs.base';
import URI from 'vs/base/common/uri';
import { suggestFilename } from 'vs/base/common/mime';
import { memoize } from 'vs/base/common/decorators';
import * as labels from 'vs/base/common/labels';
import { PLAINTEXT_MODE_ID } from 'vs/editor/common/modes/modesRegistry';
import * as paths from 'vs/base/common/paths';
import * as resources from 'vs/base/common/resources';
import { EditorInput, IEncodingSupport, EncodingMode, ConfirmResult } from 'vs/workbench/common/editor';
import { EditorInput, IEncodingSupport, EncodingMode, ConfirmResult, Verbosity } 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';
import { Verbosity } from 'vs/platform/editor/common/editor';
import { IHashService } from 'vs/workbench/services/hash/common/hashService';
import { IUriDisplayService } from 'vs/platform/uriDisplay/common/uriDisplay';
/**
* An editor input to be used for untitled text buffers.
*/
export class UntitledEditorInput extends EditorInput implements IEncodingSupport {
public static readonly ID: string = 'workbench.editors.untitledEditorInput';
static readonly ID: string = 'workbench.editors.untitledEditorInput';
private _hasAssociatedFilePath: boolean;
private cachedModel: UntitledEditorModel;
private modelResolve: TPromise<UntitledEditorModel>;
private readonly _onDidModelChangeContent: Emitter<void>;
private readonly _onDidModelChangeEncoding: Emitter<void>;
private readonly _onDidModelChangeContent: Emitter<void> = this._register(new Emitter<void>());
get onDidModelChangeContent(): Event<void> { return this._onDidModelChangeContent.event; }
private toUnbind: IDisposable[];
private readonly _onDidModelChangeEncoding: Emitter<void> = this._register(new Emitter<void>());
get onDidModelChangeEncoding(): Event<void> { return this._onDidModelChangeEncoding.event; }
constructor(
private resource: URI,
@@ -47,41 +44,28 @@ export class UntitledEditorInput extends EditorInput implements IEncodingSupport
private initialValue: string,
private preferredEncoding: string,
@IInstantiationService private instantiationService: IInstantiationService,
@IWorkspaceContextService private contextService: IWorkspaceContextService,
@ITextFileService private textFileService: ITextFileService,
@IEnvironmentService private environmentService: IEnvironmentService,
@IHashService private hashService: IHashService
@IHashService private hashService: IHashService,
@IUriDisplayService private uriDisplayService: IUriDisplayService
) {
super();
this._hasAssociatedFilePath = hasAssociatedFilePath;
this.toUnbind = [];
this._onDidModelChangeContent = new Emitter<void>();
this._onDidModelChangeEncoding = new Emitter<void>();
}
public get hasAssociatedFilePath(): boolean {
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 {
getTypeId(): string {
return UntitledEditorInput.ID;
}
public getResource(): URI {
getResource(): URI {
return this.resource;
}
public getModeId(): string {
getModeId(): string {
if (this.cachedModel) {
return this.cachedModel.getModeId();
}
@@ -89,26 +73,26 @@ export class UntitledEditorInput extends EditorInput implements IEncodingSupport
return this.modeId;
}
public getName(): string {
getName(): string {
return this.hasAssociatedFilePath ? resources.basenameOrAuthority(this.resource) : this.resource.path;
}
@memoize
private get shortDescription(): string {
return paths.basename(labels.getPathLabel(resources.dirname(this.resource), void 0, this.environmentService));
return paths.basename(this.uriDisplayService.getLabel(resources.dirname(this.resource)));
}
@memoize
private get mediumDescription(): string {
return labels.getPathLabel(resources.dirname(this.resource), this.contextService, this.environmentService);
return this.uriDisplayService.getLabel(resources.dirname(this.resource), true);
}
@memoize
private get longDescription(): string {
return labels.getPathLabel(resources.dirname(this.resource), void 0, this.environmentService);
return this.uriDisplayService.getLabel(resources.dirname(this.resource));
}
public getDescription(verbosity: Verbosity = Verbosity.MEDIUM): string {
getDescription(verbosity: Verbosity = Verbosity.MEDIUM): string {
if (!this.hasAssociatedFilePath) {
return null;
}
@@ -137,15 +121,15 @@ export class UntitledEditorInput extends EditorInput implements IEncodingSupport
@memoize
private get mediumTitle(): string {
return labels.getPathLabel(this.resource, this.contextService, this.environmentService);
return this.uriDisplayService.getLabel(this.resource, true);
}
@memoize
private get longTitle(): string {
return labels.getPathLabel(this.resource, void 0, this.environmentService);
return this.uriDisplayService.getLabel(this.resource);
}
public getTitle(verbosity: Verbosity): string {
getTitle(verbosity: Verbosity): string {
if (!this.hasAssociatedFilePath) {
return this.getName();
}
@@ -166,7 +150,7 @@ export class UntitledEditorInput extends EditorInput implements IEncodingSupport
return title;
}
public isDirty(): boolean {
isDirty(): boolean {
// {{SQL CARBON EDIT}}
if (!this.savingSupported) {
return false;
@@ -185,15 +169,15 @@ export class UntitledEditorInput extends EditorInput implements IEncodingSupport
return this.hasAssociatedFilePath;
}
public confirmSave(): TPromise<ConfirmResult> {
confirmSave(): TPromise<ConfirmResult> {
return this.textFileService.confirmSave([this.resource]);
}
public save(): TPromise<boolean> {
save(): TPromise<boolean> {
return this.textFileService.save(this.resource);
}
public revert(): TPromise<boolean> {
revert(): TPromise<boolean> {
if (this.cachedModel) {
this.cachedModel.revert();
}
@@ -203,7 +187,7 @@ export class UntitledEditorInput extends EditorInput implements IEncodingSupport
return TPromise.as(true);
}
public suggestFileName(): string {
suggestFileName(): string {
if (!this.hasAssociatedFilePath) {
if (this.cachedModel) {
const modeId = this.cachedModel.getModeId();
@@ -216,7 +200,7 @@ export class UntitledEditorInput extends EditorInput implements IEncodingSupport
return this.getName();
}
public getEncoding(): string {
getEncoding(): string {
if (this.cachedModel) {
return this.cachedModel.getEncoding();
}
@@ -224,7 +208,7 @@ export class UntitledEditorInput extends EditorInput implements IEncodingSupport
return this.preferredEncoding;
}
public setEncoding(encoding: string, mode: EncodingMode /* ignored, we only have Encode */): void {
setEncoding(encoding: string, mode: EncodingMode /* ignored, we only have Encode */): void {
this.preferredEncoding = encoding;
if (this.cachedModel) {
@@ -232,7 +216,7 @@ export class UntitledEditorInput extends EditorInput implements IEncodingSupport
}
}
public resolve(): TPromise<UntitledEditorModel> {
resolve(): TPromise<UntitledEditorModel> {
// Join a model resolve if we have had one before
if (this.modelResolve) {
@@ -247,17 +231,17 @@ export class UntitledEditorInput extends EditorInput implements IEncodingSupport
}
private createModel(): UntitledEditorModel {
const model = this.instantiationService.createInstance(UntitledEditorModel, this.modeId, this.resource, this.hasAssociatedFilePath, this.initialValue, this.preferredEncoding);
const model = this._register(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()));
this._register(model.onDidChangeContent(() => this._onDidModelChangeContent.fire()));
this._register(model.onDidChangeDirty(() => this._onDidChangeDirty.fire()));
this._register(model.onDidChangeEncoding(() => this._onDidModelChangeEncoding.fire()));
return model;
}
public getTelemetryDescriptor(): object {
getTelemetryDescriptor(): object {
const descriptor = super.getTelemetryDescriptor();
descriptor['resource'] = telemetryURIDescriptor(this.getResource(), path => this.hashService.createSHA1(path));
@@ -269,7 +253,7 @@ export class UntitledEditorInput extends EditorInput implements IEncodingSupport
return descriptor;
}
public matches(otherInput: any): boolean {
matches(otherInput: any): boolean {
if (super.matches(otherInput) === true) {
return true;
}
@@ -284,19 +268,7 @@ export class UntitledEditorInput extends EditorInput implements IEncodingSupport
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;
}
dispose(): void {
this.modelResolve = void 0;
super.dispose();

View File

@@ -4,7 +4,6 @@
*--------------------------------------------------------------------------------------------*/
'use strict';
import { IDisposable, dispose } 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';
@@ -23,19 +22,20 @@ import { createTextBufferFactory } from 'vs/editor/common/model/textModel';
export class UntitledEditorModel extends BaseTextEditorModel implements IEncodingSupport {
public static DEFAULT_CONTENT_CHANGE_BUFFER_DELAY = CONTENT_CHANGE_EVENT_BUFFER_DELAY;
static DEFAULT_CONTENT_CHANGE_BUFFER_DELAY = CONTENT_CHANGE_EVENT_BUFFER_DELAY;
private toDispose: IDisposable[];
private readonly _onDidChangeContent: Emitter<void> = this._register(new Emitter<void>());
get onDidChangeContent(): Event<void> { return this._onDidChangeContent.event; }
private readonly _onDidChangeDirty: Emitter<void> = this._register(new Emitter<void>());
get onDidChangeDirty(): Event<void> { return this._onDidChangeDirty.event; }
private readonly _onDidChangeEncoding: Emitter<void> = this._register(new Emitter<void>());
get onDidChangeEncoding(): Event<void> { return this._onDidChangeEncoding.event; }
private dirty: boolean;
private readonly _onDidChangeContent: Emitter<void>;
private readonly _onDidChangeDirty: Emitter<void>;
private readonly _onDidChangeEncoding: Emitter<void>;
private versionId: number;
private contentChangeEventScheduler: RunOnceScheduler;
private configuredEncoding: string;
constructor(
@@ -53,35 +53,12 @@ export class UntitledEditorModel extends BaseTextEditorModel implements IEncodin
this.dirty = false;
this.versionId = 0;
this.toDispose = [];
this._onDidChangeContent = new Emitter<void>();
this.toDispose.push(this._onDidChangeContent);
this._onDidChangeDirty = new Emitter<void>();
this.toDispose.push(this._onDidChangeDirty);
this._onDidChangeEncoding = new Emitter<void>();
this.toDispose.push(this._onDidChangeEncoding);
this.contentChangeEventScheduler = new RunOnceScheduler(() => this._onDidChangeContent.fire(), UntitledEditorModel.DEFAULT_CONTENT_CHANGE_BUFFER_DELAY);
this.toDispose.push(this.contentChangeEventScheduler);
this.contentChangeEventScheduler = this._register(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
@@ -93,7 +70,7 @@ export class UntitledEditorModel extends BaseTextEditorModel implements IEncodin
private registerListeners(): void {
// Config Changes
this.toDispose.push(this.configurationService.onDidChangeConfiguration(e => this.onConfigurationChange()));
this._register(this.configurationService.onDidChangeConfiguration(e => this.onConfigurationChange()));
}
private onConfigurationChange(): void {
@@ -108,11 +85,11 @@ export class UntitledEditorModel extends BaseTextEditorModel implements IEncodin
}
}
public getVersionId(): number {
getVersionId(): number {
return this.versionId;
}
public getModeId(): string {
getModeId(): string {
if (this.textEditorModel) {
return this.textEditorModel.getLanguageIdentifier().language;
}
@@ -120,11 +97,11 @@ export class UntitledEditorModel extends BaseTextEditorModel implements IEncodin
return null;
}
public getEncoding(): string {
getEncoding(): string {
return this.preferredEncoding || this.configuredEncoding;
}
public setEncoding(encoding: string): void {
setEncoding(encoding: string): void {
const oldEncoding = this.getEncoding();
this.preferredEncoding = encoding;
@@ -134,7 +111,7 @@ export class UntitledEditorModel extends BaseTextEditorModel implements IEncodin
}
}
public isDirty(): boolean {
isDirty(): boolean {
return this.dirty;
}
@@ -147,18 +124,18 @@ export class UntitledEditorModel extends BaseTextEditorModel implements IEncodin
this._onDidChangeDirty.fire();
}
public getResource(): URI {
getResource(): URI {
return this.resource;
}
public revert(): void {
revert(): void {
this.setDirty(false);
// Handle content change event buffered
this.contentChangeEventScheduler.schedule();
}
public load(): TPromise<UntitledEditorModel> {
load(): TPromise<UntitledEditorModel> {
// Check for backups first
return this.backupFileService.loadBackupResource(this.resource).then(backupResource => {
@@ -185,10 +162,10 @@ export class UntitledEditorModel extends BaseTextEditorModel implements IEncodin
this.configuredEncoding = this.configurationService.getValue<string>(this.resource, 'files.encoding');
// Listen to content changes
this.toDispose.push(this.textEditorModel.onDidChangeContent(() => this.onModelContentChanged()));
this._register(this.textEditorModel.onDidChangeContent(() => this.onModelContentChanged()));
// Listen to mode changes
this.toDispose.push(this.textEditorModel.onDidChangeLanguage(() => this.onConfigurationChange())); // mode change can have impact on config
this._register(this.textEditorModel.onDidChangeLanguage(() => this.onConfigurationChange())); // mode change can have impact on config
return model;
});
@@ -228,9 +205,7 @@ export class UntitledEditorModel extends BaseTextEditorModel implements IEncodin
this.contentChangeEventScheduler.schedule();
}
public dispose(): void {
super.dispose();
this.toDispose = dispose(this.toDispose);
isReadonly(): boolean {
return false;
}
}

View File

@@ -46,7 +46,7 @@ export class Memento {
* 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 {
getMemento(storageService: IStorageService, scope: Scope = Scope.GLOBAL): object {
// Scope by Workspace
if (scope === Scope.WORKSPACE) {
@@ -73,16 +73,18 @@ export class Memento {
* Saves all data of the mementos that have been loaded to the local storage. This includes
* global and workspace scope.
*/
public saveMemento(): void {
saveMemento(): void {
// Global
if (Memento.globalMementos[this.id]) {
Memento.globalMementos[this.id].save();
const globalMemento = Memento.globalMementos[this.id];
if (globalMemento) {
globalMemento.save();
}
// Workspace
if (Memento.workspaceMementos[this.id]) {
Memento.workspaceMementos[this.id].save();
const workspaceMemento = Memento.workspaceMementos[this.id];
if (workspaceMemento) {
workspaceMemento.save();
}
}
}
@@ -98,7 +100,7 @@ class ScopedMemento {
this.mementoObj = this.loadMemento();
}
public getMemento(): object {
getMemento(): object {
return this.mementoObj;
}
@@ -112,7 +114,7 @@ class ScopedMemento {
return {};
}
public save(): void {
save(): void {
let storageScope = this.scope === Scope.GLOBAL ? StorageScope.GLOBAL : StorageScope.WORKSPACE;
if (!types.isEmptyObject(this.mementoObj)) {

View File

@@ -8,7 +8,7 @@
import { INotification, INotificationHandle, INotificationActions, INotificationProgress, NoOpNotification, Severity, NotificationMessage } from 'vs/platform/notification/common/notification';
import { toErrorMessage } from 'vs/base/common/errorMessage';
import { Event, Emitter, once } from 'vs/base/common/event';
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
import { Disposable } from 'vs/base/common/lifecycle';
import { isPromiseCanceledError, isErrorWithActions } from 'vs/base/common/errors';
export interface INotificationsModel {
@@ -44,7 +44,9 @@ export interface INotificationChangeEvent {
}
export class NotificationHandle implements INotificationHandle {
private readonly _onDidClose: Emitter<void> = new Emitter();
get onDidClose(): Event<void> { return this._onDidClose.event; }
constructor(private item: INotificationViewItem, private closeItem: (item: INotificationViewItem) => void) {
this.registerListeners();
@@ -57,58 +59,42 @@ export class NotificationHandle implements INotificationHandle {
});
}
public get onDidClose(): Event<void> {
return this._onDidClose.event;
}
public get progress(): INotificationProgress {
get progress(): INotificationProgress {
return this.item.progress;
}
public updateSeverity(severity: Severity): void {
updateSeverity(severity: Severity): void {
this.item.updateSeverity(severity);
}
public updateMessage(message: NotificationMessage): void {
updateMessage(message: NotificationMessage): void {
this.item.updateMessage(message);
}
public updateActions(actions?: INotificationActions): void {
updateActions(actions?: INotificationActions): void {
this.item.updateActions(actions);
}
public close(): void {
close(): void {
this.closeItem(this.item);
this._onDidClose.dispose();
}
}
export class NotificationsModel implements INotificationsModel {
export class NotificationsModel extends Disposable implements INotificationsModel {
private static NO_OP_NOTIFICATION = new NoOpNotification();
private _notifications: INotificationViewItem[];
private readonly _onDidNotificationChange: Emitter<INotificationChangeEvent> = this._register(new Emitter<INotificationChangeEvent>());
get onDidNotificationChange(): Event<INotificationChangeEvent> { return this._onDidNotificationChange.event; }
private readonly _onDidNotificationChange: Emitter<INotificationChangeEvent>;
private toDispose: IDisposable[];
private _notifications: INotificationViewItem[] = [];
constructor() {
this._notifications = [];
this.toDispose = [];
this._onDidNotificationChange = new Emitter<INotificationChangeEvent>();
this.toDispose.push(this._onDidNotificationChange);
}
public get notifications(): INotificationViewItem[] {
get notifications(): INotificationViewItem[] {
return this._notifications;
}
public get onDidNotificationChange(): Event<INotificationChangeEvent> {
return this._onDidNotificationChange.event;
}
public notify(notification: INotification): INotificationHandle {
notify(notification: INotification): INotificationHandle {
const item = this.createViewItem(notification);
if (!item) {
return NotificationsModel.NO_OP_NOTIFICATION; // return early if this is a no-op
@@ -187,10 +173,6 @@ export class NotificationsModel implements INotificationsModel {
return item;
}
public dispose(): void {
this.toDispose = dispose(this.toDispose);
}
}
export interface INotificationViewItem {
@@ -250,29 +232,23 @@ export interface INotificationViewItemProgress extends INotificationProgress {
dispose(): void;
}
export class NotificationViewItemProgress implements INotificationViewItemProgress {
export class NotificationViewItemProgress extends Disposable implements INotificationViewItemProgress {
private _state: INotificationViewItemProgressState;
private readonly _onDidChange: Emitter<void>;
private toDispose: IDisposable[];
private readonly _onDidChange: Emitter<void> = this._register(new Emitter<void>());
get onDidChange(): Event<void> { return this._onDidChange.event; }
constructor() {
this.toDispose = [];
this._state = Object.create(null);
super();
this._onDidChange = new Emitter<void>();
this.toDispose.push(this._onDidChange);
this._state = Object.create(null);
}
public get state(): INotificationViewItemProgressState {
get state(): INotificationViewItemProgressState {
return this._state;
}
public get onDidChange(): Event<void> {
return this._onDidChange.event;
}
public infinite(): void {
infinite(): void {
if (this._state.infinite) {
return;
}
@@ -286,7 +262,7 @@ export class NotificationViewItemProgress implements INotificationViewItemProgre
this._onDidChange.fire();
}
public done(): void {
done(): void {
if (this._state.done) {
return;
}
@@ -300,7 +276,7 @@ export class NotificationViewItemProgress implements INotificationViewItemProgre
this._onDidChange.fire();
}
public total(value: number): void {
total(value: number): void {
if (this._state.total === value) {
return;
}
@@ -313,22 +289,18 @@ export class NotificationViewItemProgress implements INotificationViewItemProgre
this._onDidChange.fire();
}
public worked(value: number): void {
if (this._state.worked === value) {
return;
worked(value: number): void {
if (typeof this._state.worked === 'number') {
this._state.worked += value;
} else {
this._state.worked = value;
}
this._state.worked = value;
this._state.infinite = void 0;
this._state.done = void 0;
this._onDidChange.fire();
}
public dispose(): void {
this.toDispose = dispose(this.toDispose);
}
}
export interface IMessageLink {
@@ -340,11 +312,12 @@ export interface IMessageLink {
export interface INotificationMessage {
raw: string;
original: NotificationMessage;
value: string;
links: IMessageLink[];
}
export class NotificationViewItem implements INotificationViewItem {
export class NotificationViewItem extends Disposable implements INotificationViewItem {
private static MAX_MESSAGE_LENGTH = 1000;
@@ -353,16 +326,20 @@ export class NotificationViewItem implements INotificationViewItem {
private static LINK_REGEX = /\[([^\]]+)\]\((https?:\/\/[^\)\s]+)\)/gi;
private _expanded: boolean;
private toDispose: IDisposable[];
private _actions: INotificationActions;
private _progress: NotificationViewItemProgress;
private readonly _onDidExpansionChange: Emitter<void>;
private readonly _onDidClose: Emitter<void>;
private readonly _onDidLabelChange: Emitter<INotificationViewItemLabelChangeEvent>;
private readonly _onDidExpansionChange: Emitter<void> = this._register(new Emitter<void>());
get onDidExpansionChange(): Event<void> { return this._onDidExpansionChange.event; }
public static create(notification: INotification): INotificationViewItem {
private readonly _onDidClose: Emitter<void> = this._register(new Emitter<void>());
get onDidClose(): Event<void> { return this._onDidClose.event; }
private readonly _onDidLabelChange: Emitter<INotificationViewItemLabelChangeEvent> = this._register(new Emitter<INotificationViewItemLabelChangeEvent>());
get onDidLabelChange(): Event<INotificationViewItemLabelChangeEvent> { return this._onDidLabelChange.event; }
static create(notification: INotification): INotificationViewItem {
if (!notification || !notification.message || isPromiseCanceledError(notification.message)) {
return null; // we need a message to show
}
@@ -421,22 +398,13 @@ export class NotificationViewItem implements INotificationViewItem {
});
return { raw, value: message, links };
return { raw, value: message, links, original: input };
}
private constructor(private _severity: Severity, private _message: INotificationMessage, private _source: string, actions?: INotificationActions) {
this.toDispose = [];
super();
this.setActions(actions);
this._onDidExpansionChange = new Emitter<void>();
this.toDispose.push(this._onDidExpansionChange);
this._onDidLabelChange = new Emitter<INotificationViewItemLabelChangeEvent>();
this.toDispose.push(this._onDidLabelChange);
this._onDidClose = new Emitter<void>();
this.toDispose.push(this._onDidClose);
}
private setActions(actions: INotificationActions): void {
@@ -456,62 +424,49 @@ export class NotificationViewItem implements INotificationViewItem {
this._expanded = actions.primary.length > 0;
}
public get onDidExpansionChange(): Event<void> {
return this._onDidExpansionChange.event;
}
public get onDidLabelChange(): Event<INotificationViewItemLabelChangeEvent> {
return this._onDidLabelChange.event;
}
public get onDidClose(): Event<void> {
return this._onDidClose.event;
}
public get canCollapse(): boolean {
get canCollapse(): boolean {
return this._actions.primary.length === 0;
}
public get expanded(): boolean {
get expanded(): boolean {
return this._expanded;
}
public get severity(): Severity {
get severity(): Severity {
return this._severity;
}
public hasProgress(): boolean {
hasProgress(): boolean {
return !!this._progress;
}
public get progress(): INotificationViewItemProgress {
get progress(): INotificationViewItemProgress {
if (!this._progress) {
this._progress = new NotificationViewItemProgress();
this.toDispose.push(this._progress);
this.toDispose.push(this._progress.onDidChange(() => this._onDidLabelChange.fire({ kind: NotificationViewItemLabelKind.PROGRESS })));
this._progress = this._register(new NotificationViewItemProgress());
this._register(this._progress.onDidChange(() => this._onDidLabelChange.fire({ kind: NotificationViewItemLabelKind.PROGRESS })));
}
return this._progress;
}
public get message(): INotificationMessage {
get message(): INotificationMessage {
return this._message;
}
public get source(): string {
get source(): string {
return this._source;
}
public get actions(): INotificationActions {
get actions(): INotificationActions {
return this._actions;
}
public updateSeverity(severity: Severity): void {
updateSeverity(severity: Severity): void {
this._severity = severity;
this._onDidLabelChange.fire({ kind: NotificationViewItemLabelKind.SEVERITY });
}
public updateMessage(input: NotificationMessage): void {
updateMessage(input: NotificationMessage): void {
const message = NotificationViewItem.parseNotificationMessage(input);
if (!message) {
return;
@@ -521,13 +476,13 @@ export class NotificationViewItem implements INotificationViewItem {
this._onDidLabelChange.fire({ kind: NotificationViewItemLabelKind.MESSAGE });
}
public updateActions(actions?: INotificationActions): void {
updateActions(actions?: INotificationActions): void {
this.setActions(actions);
this._onDidLabelChange.fire({ kind: NotificationViewItemLabelKind.ACTIONS });
}
public expand(): void {
expand(): void {
if (this._expanded || !this.canCollapse) {
return;
}
@@ -536,7 +491,7 @@ export class NotificationViewItem implements INotificationViewItem {
this._onDidExpansionChange.fire();
}
public collapse(skipEvents?: boolean): void {
collapse(skipEvents?: boolean): void {
if (!this._expanded || !this.canCollapse) {
return;
}
@@ -548,7 +503,7 @@ export class NotificationViewItem implements INotificationViewItem {
}
}
public toggle(): void {
toggle(): void {
if (this._expanded) {
this.collapse();
} else {
@@ -556,13 +511,13 @@ export class NotificationViewItem implements INotificationViewItem {
}
}
public close(): void {
close(): void {
this._onDidClose.fire();
this.toDispose = dispose(this.toDispose);
this.dispose();
}
public equals(other: INotificationViewItem): boolean {
equals(other: INotificationViewItem): boolean {
if (this.hasProgress() || other.hasProgress()) {
return false;
}

View File

@@ -9,7 +9,6 @@ import URI from 'vs/base/common/uri';
import * as paths from 'vs/base/common/paths';
import { RawContextKey, IContextKeyService, IContextKey } from 'vs/platform/contextkey/common/contextkey';
import { IModeService } from 'vs/editor/common/services/modeService';
import { IFileService } from 'vs/platform/files/common/files';
export class ResourceContextKey implements IContextKey<URI> {
@@ -19,7 +18,6 @@ export class ResourceContextKey implements IContextKey<URI> {
static Resource = new RawContextKey<URI>('resource', undefined);
static Extension = new RawContextKey<string>('resourceExtname', undefined);
static HasResource = new RawContextKey<boolean>('resourceSet', false);
static IsFile = new RawContextKey<boolean>('resourceIsFile', false);
private _resourceKey: IContextKey<URI>;
private _schemeKey: IContextKey<string>;
@@ -27,12 +25,10 @@ export class ResourceContextKey implements IContextKey<URI> {
private _langIdKey: IContextKey<string>;
private _extensionKey: IContextKey<string>;
private _hasResource: IContextKey<boolean>;
private _isFile: IContextKey<boolean>;
constructor(
@IContextKeyService contextKeyService: IContextKeyService,
@IModeService private readonly _modeService: IModeService,
@IFileService private readonly _fileService: IFileService
@IModeService private readonly _modeService: IModeService
) {
this._schemeKey = ResourceContextKey.Scheme.bindTo(contextKeyService);
this._filenameKey = ResourceContextKey.Filename.bindTo(contextKeyService);
@@ -40,7 +36,6 @@ export class ResourceContextKey implements IContextKey<URI> {
this._resourceKey = ResourceContextKey.Resource.bindTo(contextKeyService);
this._extensionKey = ResourceContextKey.Extension.bindTo(contextKeyService);
this._hasResource = ResourceContextKey.HasResource.bindTo(contextKeyService);
this._isFile = ResourceContextKey.IsFile.bindTo(contextKeyService);
}
set(value: URI) {
@@ -50,7 +45,6 @@ export class ResourceContextKey implements IContextKey<URI> {
this._langIdKey.set(value && this._modeService.getModeIdByFilenameOrFirstLine(value.fsPath));
this._extensionKey.set(value && paths.extname(value.fsPath));
this._hasResource.set(!!value);
this._isFile.set(value && this._fileService.canHandleResource(value));
}
reset(): void {
@@ -60,10 +54,9 @@ export class ResourceContextKey implements IContextKey<URI> {
this._langIdKey.reset();
this._extensionKey.reset();
this._hasResource.reset();
this._isFile.reset();
}
public get(): URI {
get(): URI {
return this._resourceKey.get();
}
}

View File

@@ -4,8 +4,8 @@
*--------------------------------------------------------------------------------------------*/
import * as nls from 'vs/nls';
import { registerColor, editorBackground, contrastBorder, transparent, editorWidgetBackground, textLinkForeground, lighten, darken } from 'vs/platform/theme/common/colorRegistry';
import { IDisposable, Disposable, dispose } from 'vs/base/common/lifecycle';
import { registerColor, editorBackground, contrastBorder, transparent, editorWidgetBackground, textLinkForeground, lighten, darken, focusBorder, activeContrastBorder, listActiveSelectionForeground, listActiveSelectionBackground } from 'vs/platform/theme/common/colorRegistry';
import { Disposable } from 'vs/base/common/lifecycle';
import { IThemeService, ITheme } from 'vs/platform/theme/common/themeService';
import { Color } from 'vs/base/common/color';
@@ -58,13 +58,25 @@ 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."));
}, nls.localize('tabActiveBorder', "Border on the bottom of an active tab. 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_TOP = registerColor('tab.activeBorderTop', {
dark: null,
light: null,
hc: null
}, nls.localize('tabActiveBorderTop', "Border to the top of an active tab. 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."));
}, nls.localize('tabActiveUnfocusedBorder', "Border on the bottom of an active tab 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_ACTIVE_BORDER_TOP = registerColor('tab.unfocusedActiveBorderTop', {
dark: transparent(TAB_ACTIVE_BORDER_TOP, 0.5),
light: transparent(TAB_ACTIVE_BORDER_TOP, 0.7),
hc: null
}, nls.localize('tabActiveUnfocusedBorderTop', "Border to the top of an active tab in an unfocused group. Tabs are the containers for editors in the editor area. Multiple tabs can be opened in one editor group. There can be multiple editor groups."));
export const TAB_HOVER_BORDER = registerColor('tab.hoverBorder', {
dark: null,
@@ -105,11 +117,23 @@ export const TAB_UNFOCUSED_INACTIVE_FOREGROUND = registerColor('tab.unfocusedIna
// < --- Editors --- >
export const EDITOR_GROUP_BACKGROUND = registerColor('editorGroup.background', {
dark: '#2D2D2D',
light: '#ECECEC',
registerColor('editorGroup.background', {
dark: null,
light: null,
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."));
}, nls.localize('editorGroupBackground', "Deprecated background color of an editor group."), false, nls.localize('deprecatedEditorGroupBackground', "Deprecated: Background color of an editor group is no longer being supported with the introduction of the grid editor layout. You can use editorGroup.emptyBackground to set the background color of empty editor groups."));
export const EDITOR_GROUP_EMPTY_BACKGROUND = registerColor('editorGroup.emptyBackground', {
dark: null,
light: null,
hc: null
}, nls.localize('editorGroupEmptyBackground', "Background color of an empty editor group. Editor groups are the containers of editors."));
export const EDITOR_GROUP_FOCUSED_EMPTY_BORDER = registerColor('editorGroup.focusedEmptyBorder', {
dark: null,
light: null,
hc: focusBorder
}, nls.localize('editorGroupFocusedEmptyBorder', "Border color of an empty editor group that is focused. Editor groups are the containers of editors."));
export const EDITOR_GROUP_HEADER_TABS_BACKGROUND = registerColor('editorGroupHeader.tabsBackground', {
dark: '#252526',
@@ -137,7 +161,7 @@ export const EDITOR_GROUP_BORDER = registerColor('editorGroup.border', {
export const EDITOR_DRAG_AND_DROP_BACKGROUND = registerColor('editorGroup.dropBackground', {
dark: Color.fromHex('#53595D').transparent(0.5),
light: Color.fromHex('#3399FF').transparent(0.18),
light: Color.fromHex('#2677CB').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."));
@@ -164,7 +188,7 @@ export const PANEL_ACTIVE_TITLE_FOREGROUND = registerColor('panelTitle.activeFor
}, 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),
dark: transparent(PANEL_ACTIVE_TITLE_FOREGROUND, 0.6),
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."));
@@ -177,7 +201,7 @@ export const PANEL_ACTIVE_TITLE_BORDER = registerColor('panelTitle.activeBorder'
export const PANEL_DRAG_AND_DROP_BACKGROUND = registerColor('panel.dropBackground', {
dark: Color.white.transparent(0.12),
light: Color.fromHex('#3399FF').transparent(0.18),
light: Color.fromHex('#2677CB').transparent(0.18),
hc: Color.white.transparent(0.12)
}, nls.localize('panelDragAndDropBackground', "Drag and drop feedback color for the panel title items. The color should have transparency so that the panel entries can still shine through. Panels are shown below the editor area and contain views like output and integrated terminal."));
@@ -364,6 +388,56 @@ export const TITLE_BAR_BORDER = registerColor('titleBar.border', {
hc: contrastBorder
}, nls.localize('titleBarBorder', "Title bar border color. Note that this color is currently only supported on macOS."));
// < --- Menubar --- >
export const MENUBAR_SELECTION_FOREGROUND = registerColor('menubar.selectionForeground', {
dark: TITLE_BAR_ACTIVE_FOREGROUND,
light: TITLE_BAR_ACTIVE_FOREGROUND,
hc: TITLE_BAR_ACTIVE_FOREGROUND
}, nls.localize('menubarSelectionForeground', "Foreground color of the selected menu item in the menubar."));
export const MENUBAR_SELECTION_BACKGROUND = registerColor('menubar.selectionBackground', {
dark: transparent(Color.white, 0.1),
light: transparent(Color.black, 0.1),
hc: null
}, nls.localize('menubarSelectionBackground', "Background color of the selected menu item in the menubar."));
export const MENUBAR_SELECTION_BORDER = registerColor('menubar.selectionBorder', {
dark: null,
light: null,
hc: activeContrastBorder
}, nls.localize('menubarSelectionBorder', "Border color of the selected menu item in the menubar."));
export const MENU_FOREGROUND = registerColor('menu.foreground', {
dark: SIDE_BAR_FOREGROUND,
light: SIDE_BAR_FOREGROUND,
hc: SIDE_BAR_FOREGROUND
}, nls.localize('menuForeground', "Foreground color of menu items."));
export const MENU_BACKGROUND = registerColor('menu.background', {
dark: SIDE_BAR_BACKGROUND,
light: SIDE_BAR_BACKGROUND,
hc: SIDE_BAR_BACKGROUND
}, nls.localize('menuBackground', "Background color of menu items."));
export const MENU_SELECTION_FOREGROUND = registerColor('menu.selectionForeground', {
dark: listActiveSelectionForeground,
light: listActiveSelectionForeground,
hc: listActiveSelectionForeground
}, nls.localize('menuSelectionForeground', "Foreground color of the selected menu item in menus."));
export const MENU_SELECTION_BACKGROUND = registerColor('menu.selectionBackground', {
dark: listActiveSelectionBackground,
light: listActiveSelectionBackground,
hc: listActiveSelectionBackground
}, nls.localize('menuSelectionBackground', "Background color of the selected menu item in menus."));
export const MENU_SELECTION_BORDER = registerColor('menu.selectionBorder', {
dark: null,
light: null,
hc: null
}, nls.localize('menuSelectionBorder', "Border color of the selected menu item in menus."));
// < --- Notifications --- >
export const NOTIFICATIONS_CENTER_BORDER = registerColor('notificationCenter.border', {
@@ -418,23 +492,17 @@ export const NOTIFICATIONS_BORDER = registerColor('notifications.border', {
* Base class for all themable workbench components.
*/
export class Themable extends Disposable {
private _toUnbind: IDisposable[];
private theme: ITheme;
protected 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;
this._register(this.themeService.onThemeChange(theme => this.onThemeChange(theme)));
}
protected onThemeChange(theme: ITheme): void {
@@ -456,10 +524,4 @@ export class Themable extends Disposable {
return color ? color.toString() : null;
}
public dispose(): void {
this._toUnbind = dispose(this._toUnbind);
super.dispose();
}
}

View File

@@ -14,38 +14,89 @@ import { IViewlet } from 'vs/workbench/common/viewlet';
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
import { IDisposable } from 'vs/base/common/lifecycle';
import { ThemeIcon } from 'vs/platform/theme/common/themeService';
import { values } from 'vs/base/common/map';
import { Registry } from 'vs/platform/registry/common/platform';
export class ViewLocation {
private static locations: Map<string, ViewLocation> = new Map<string, ViewLocation>();
static register(id: string): ViewLocation {
const viewLocation = new ViewLocation(id);
ViewLocation.locations.set(id, viewLocation);
return viewLocation;
}
static get(value: string): ViewLocation {
return ViewLocation.locations.get(value);
}
static readonly Explorer: ViewLocation = ViewLocation.register('workbench.view.explorer');
static readonly Debug: ViewLocation = ViewLocation.register('workbench.view.debug');
static readonly Extensions: ViewLocation = ViewLocation.register('workbench.view.extensions');
static readonly SCM: ViewLocation = ViewLocation.register('workbench.view.scm.views.contributed');
private constructor(private _id: string) { }
get id(): string { return this._id; }
export const TEST_VIEW_CONTAINER_ID = 'workbench.view.extension.test';
export namespace Extensions {
export const ViewContainersRegistry = 'workbench.registry.view.containers';
}
export interface IViewContainersRegistry {
/**
* An event that is triggerred when a view container is registered.
*/
readonly onDidRegister: Event<ViewContainer>;
/**
* All registered view containers
*/
readonly all: ViewContainer[];
/**
* Registers a view container with given id
* No op if a view container is already registered with the given id.
*
* @param id of the view container.
*
* @returns the registered ViewContainer.
*/
registerViewContainer(id: string): ViewContainer;
/**
* Returns the view container with given id.
*
* @param id
* @returns the view container with given id.
*/
get(id: string): ViewContainer;
}
export class ViewContainer {
protected constructor(readonly id: string) { }
}
class ViewContainersRegistryImpl implements IViewContainersRegistry {
private readonly _onDidRegister: Emitter<ViewContainer> = new Emitter<ViewContainer>();
readonly onDidRegister: Event<ViewContainer> = this._onDidRegister.event;
private viewContainers: Map<string, ViewContainer> = new Map<string, ViewContainer>();
get all(): ViewContainer[] {
return values(this.viewContainers);
}
registerViewContainer(id: string): ViewContainer {
if (!this.viewContainers.has(id)) {
const viewContainer = new class extends ViewContainer {
constructor() {
super(id);
}
};
this.viewContainers.set(id, viewContainer);
this._onDidRegister.fire(viewContainer);
}
return this.get(id);
}
get(id: string): ViewContainer {
return this.viewContainers.get(id);
}
}
Registry.add(Extensions.ViewContainersRegistry, new ViewContainersRegistryImpl());
export interface IViewDescriptor {
readonly id: string;
readonly name: string;
readonly location: ViewLocation;
readonly container: ViewContainer;
// TODO do we really need this?!
// TODO@Sandeep do we really need this?!
readonly ctor: any;
readonly when?: ContextKeyExpr;
@@ -57,6 +108,9 @@ export interface IViewDescriptor {
readonly collapsed?: boolean;
readonly canToggleVisibility?: boolean;
// Applies only to newly created views
readonly hideByDefault?: boolean;
}
export interface IViewsRegistry {
@@ -67,11 +121,9 @@ export interface IViewsRegistry {
registerViews(views: IViewDescriptor[]): void;
deregisterViews(ids: string[], location: ViewLocation): void;
deregisterViews(ids: string[], container: ViewContainer): void;
getViews(loc: ViewLocation): IViewDescriptor[];
getAllViews(): IViewDescriptor[];
getViews(loc: ViewContainer): IViewDescriptor[];
getView(id: string): IViewDescriptor;
@@ -85,20 +137,20 @@ export const ViewsRegistry: IViewsRegistry = new class implements IViewsRegistry
private readonly _onViewsDeregistered: Emitter<IViewDescriptor[]> = new Emitter<IViewDescriptor[]>();
readonly onViewsDeregistered: Event<IViewDescriptor[]> = this._onViewsDeregistered.event;
private _viewLocations: ViewLocation[] = [];
private _views: Map<ViewLocation, IViewDescriptor[]> = new Map<ViewLocation, IViewDescriptor[]>();
private _viewContainer: ViewContainer[] = [];
private _views: Map<ViewContainer, IViewDescriptor[]> = new Map<ViewContainer, IViewDescriptor[]>();
registerViews(viewDescriptors: IViewDescriptor[]): void {
if (viewDescriptors.length) {
for (const viewDescriptor of viewDescriptors) {
let views = this._views.get(viewDescriptor.location);
let views = this._views.get(viewDescriptor.container);
if (!views) {
views = [];
this._views.set(viewDescriptor.location, views);
this._viewLocations.push(viewDescriptor.location);
this._views.set(viewDescriptor.container, views);
this._viewContainer.push(viewDescriptor.container);
}
if (views.some(v => v.id === viewDescriptor.id)) {
throw new Error(localize('duplicateId', "A view with id '{0}' is already registered in the location '{1}'", viewDescriptor.id, viewDescriptor.location.id));
throw new Error(localize('duplicateId', "A view with id '{0}' is already registered in the container '{1}'", viewDescriptor.id, viewDescriptor.container.id));
}
views.push(viewDescriptor);
}
@@ -106,8 +158,8 @@ export const ViewsRegistry: IViewsRegistry = new class implements IViewsRegistry
}
}
deregisterViews(ids: string[], location: ViewLocation): void {
const views = this._views.get(location);
deregisterViews(ids: string[], container: ViewContainer): void {
const views = this._views.get(container);
if (!views) {
return;
@@ -118,29 +170,23 @@ export const ViewsRegistry: IViewsRegistry = new class implements IViewsRegistry
if (viewsToDeregister.length) {
const remaningViews = views.filter(view => ids.indexOf(view.id) === -1);
if (remaningViews.length) {
this._views.set(location, remaningViews);
this._views.set(container, remaningViews);
} else {
this._views.delete(location);
this._viewLocations.splice(this._viewLocations.indexOf(location), 1);
this._views.delete(container);
this._viewContainer.splice(this._viewContainer.indexOf(container), 1);
}
this._onViewsDeregistered.fire(viewsToDeregister);
}
this._onViewsDeregistered.fire(viewsToDeregister);
}
getViews(loc: ViewLocation): IViewDescriptor[] {
getViews(loc: ViewContainer): IViewDescriptor[] {
return this._views.get(loc) || [];
}
getAllViews(): IViewDescriptor[] {
const result: IViewDescriptor[] = [];
this._views.forEach(views => result.push(...views));
return result;
}
getView(id: string): IViewDescriptor {
for (const viewLocation of this._viewLocations) {
const viewDescriptor = (this._views.get(viewLocation) || []).filter(v => v.id === id)[0];
for (const viewContainer of this._viewContainer) {
const viewDescriptor = (this._views.get(viewContainer) || []).filter(v => v.id === id)[0];
if (viewDescriptor) {
return viewDescriptor;
}
@@ -149,18 +195,42 @@ export const ViewsRegistry: IViewsRegistry = new class implements IViewsRegistry
}
};
export interface IView {
readonly id: string;
}
export interface IViewsViewlet extends IViewlet {
openView(id: string, focus?: boolean): TPromise<void>;
openView(id: string, focus?: boolean): TPromise<IView>;
}
export const IViewsService = createDecorator<IViewsService>('viewsService');
export interface IViewsService {
_serviceBrand: any;
openView(id: string, focus?: boolean): TPromise<IView>;
}
// Custom views
export interface ITreeViewer extends IDisposable {
dataProvider: ITreeViewDataProvider;
readonly onDidExpandItem: Event<ITreeItem>;
readonly onDidCollapseItem: Event<ITreeItem>;
readonly onDidChangeSelection: Event<ITreeItem[]>;
readonly onDidChangeVisibility: Event<boolean>;
readonly visible: boolean;
refresh(treeItems?: ITreeItem[]): TPromise<void>;
setVisibility(visible: boolean): void;
@@ -178,20 +248,10 @@ export interface ITreeViewer extends IDisposable {
export interface ICustomViewDescriptor extends IViewDescriptor {
treeView?: boolean;
readonly treeViewer: ITreeViewer;
}
export const IViewsService = createDecorator<IViewsService>('viewsService');
export interface IViewsService {
_serviceBrand: any;
getTreeViewer(id: string): ITreeViewer;
openView(id: string, focus?: boolean): TPromise<void>;
}
export type TreeViewItemHandleArg = {
$treeViewId: string,
$treeItemHandle: string
@@ -228,14 +288,10 @@ export interface ITreeItem {
command?: Command;
children?: ITreeItem[];
}
export interface ITreeViewDataProvider {
onDidChange: Event<ITreeItem[] | undefined | null>;
onDispose: Event<void>;
getChildren(element?: ITreeItem): TPromise<ITreeItem[]>;
}