diff --git a/src/vs/workbench/parts/output/browser/output.contribution.ts b/src/vs/workbench/parts/output/browser/output.contribution.ts index a02939d72305aed7e6b164591c12a524e65aa0ff..1e1d26d92faa1414f09d64a1427d4cecab944524 100644 --- a/src/vs/workbench/parts/output/browser/output.contribution.ts +++ b/src/vs/workbench/parts/output/browser/output.contribution.ts @@ -14,7 +14,7 @@ import { KeybindingsRegistry } from 'vs/platform/keybinding/common/keybindingsRe import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { IWorkbenchActionRegistry, Extensions as ActionExtensions } from 'vs/workbench/common/actionRegistry'; import { OutputService } from 'vs/workbench/parts/output/browser/outputServices'; -import { ToggleOutputAction, ClearOutputAction } from 'vs/workbench/parts/output/browser/outputActions'; +import { ToggleOutputAction, ClearOutputAction, ToggleOutputScrollLockAction } from 'vs/workbench/parts/output/browser/outputActions'; import { OUTPUT_MODE_ID, OUTPUT_MIME, OUTPUT_PANEL_ID, IOutputService } from 'vs/workbench/parts/output/common/output'; import { PanelRegistry, Extensions, PanelDescriptor } from 'vs/workbench/browser/panel'; import { EditorContextKeys } from 'vs/editor/common/editorCommon'; @@ -55,6 +55,9 @@ actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(ToggleOutputActi actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(ClearOutputAction, ClearOutputAction.ID, ClearOutputAction.LABEL), 'View: Clear Output', nls.localize('viewCategory', "View")); +actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(ToggleOutputScrollLockAction, ToggleOutputScrollLockAction.ID, ToggleOutputScrollLockAction.LABEL), + 'View: Toggle Output Scroll Lock', nls.localize('viewCategory', "View")); + interface IActionDescriptor { id: string; handler: ICommandHandler; diff --git a/src/vs/workbench/parts/output/browser/outputActions.ts b/src/vs/workbench/parts/output/browser/outputActions.ts index d5eaab920a4e2fffe2c355eabc6bf2b0673f3f43..0561f91f7591a3713768e78275cb34b57e34bb76 100644 --- a/src/vs/workbench/parts/output/browser/outputActions.ts +++ b/src/vs/workbench/parts/output/browser/outputActions.ts @@ -48,6 +48,23 @@ export class ClearOutputAction extends Action { } } +export class ToggleOutputScrollLockAction extends Action { + + public static ID = 'workbench.output.action.toggleOutputScrollLock'; + public static LABEL = nls.localize('toggleOutputScrollLock', "Toggle Output Scroll Lock"); + + constructor(id: string, label: string, + @IOutputService private outputService: IOutputService) { + super(id, label, 'output-action toggle-output-scroll-lock'); + } + + public run(): TPromise { + this.outputService.getActiveChannel().toggleScrollLock(); + + return TPromise.as(true); + } +} + export class SwitchOutputAction extends Action { public static ID = 'workbench.output.action.switchBetweenOutputs'; diff --git a/src/vs/workbench/parts/output/browser/outputPanel.ts b/src/vs/workbench/parts/output/browser/outputPanel.ts index ae266298cd086879ccf0b28b89284624e3e836eb..d628ecac03355d2f89f406b9cfcb76b34319e097 100644 --- a/src/vs/workbench/parts/output/browser/outputPanel.ts +++ b/src/vs/workbench/parts/output/browser/outputPanel.ts @@ -19,7 +19,7 @@ import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { EditorInput, EditorOptions } from 'vs/workbench/common/editor'; import { TextResourceEditor } from 'vs/workbench/browser/parts/editor/textResourceEditor'; import { OutputEditors, OUTPUT_PANEL_ID, IOutputService, CONTEXT_IN_OUTPUT } from 'vs/workbench/parts/output/common/output'; -import { SwitchOutputAction, SwitchOutputActionItem, ClearOutputAction } from 'vs/workbench/parts/output/browser/outputActions'; +import { SwitchOutputAction, SwitchOutputActionItem, ClearOutputAction, ToggleOutputScrollLockAction } from 'vs/workbench/parts/output/browser/outputActions'; import { IThemeService } from 'vs/workbench/services/themes/common/themeService'; import { IUntitledEditorService } from 'vs/workbench/services/untitled/common/untitledEditorService'; import { IEditorGroupService } from 'vs/workbench/services/group/common/groupService'; @@ -56,7 +56,8 @@ export class OutputPanel extends TextResourceEditor { if (!this.actions) { this.actions = [ this.instantiationService.createInstance(SwitchOutputAction), - this.instantiationService.createInstance(ClearOutputAction, ClearOutputAction.ID, ClearOutputAction.LABEL) + this.instantiationService.createInstance(ClearOutputAction, ClearOutputAction.ID, ClearOutputAction.LABEL), + this.instantiationService.createInstance(ToggleOutputScrollLockAction, ToggleOutputScrollLockAction.ID, ToggleOutputScrollLockAction.LABEL) ]; this.actions.forEach(a => { diff --git a/src/vs/workbench/parts/output/browser/outputServices.ts b/src/vs/workbench/parts/output/browser/outputServices.ts index ce2c622a9324102c231bd5ded2afef15277ef8ed..12d7822c4f82083a4e4199884d8d7f8a9306a057 100644 --- a/src/vs/workbench/parts/output/browser/outputServices.ts +++ b/src/vs/workbench/parts/output/browser/outputServices.ts @@ -41,6 +41,7 @@ export class OutputService implements IOutputService { private _onActiveOutputChannel: Emitter; private _outputLinkDetector: OutputLinkProvider; + private _outputContentProvider: OutputContentProvider; constructor( @IStorageService private storageService: IStorageService, @@ -61,8 +62,10 @@ export class OutputService implements IOutputService { this._outputLinkDetector = new OutputLinkProvider(contextService, modelService); + this._outputContentProvider = instantiationService.createInstance(OutputContentProvider, this); + // Register as text model content provider for output - textModelResolverService.registerTextModelContentProvider(OUTPUT_SCHEME, instantiationService.createInstance(OutputContentProvider, this)); + textModelResolverService.registerTextModelContentProvider(OUTPUT_SCHEME, this._outputContentProvider); } public get onOutput(): Event { @@ -89,7 +92,8 @@ export class OutputService implements IOutputService { }, append: (output: string) => this.append(id, output), show: (preserveFocus: boolean) => this.showOutput(id, preserveFocus), - clear: () => this.clearOutput(id) + clear: () => this.clearOutput(id), + toggleScrollLock: () => this._outputContentProvider.toggleScrollLock(id) }; } @@ -154,6 +158,7 @@ class OutputContentProvider implements ITextModelContentProvider { private bufferedOutput: { [channel: string]: string; }; private appendOutputScheduler: { [channel: string]: RunOnceScheduler; }; + private channelIdsWithScrollLock: Set = new Set(); private toDispose: IDisposable[]; @@ -265,9 +270,11 @@ class OutputContentProvider implements ITextModelContentProvider { model.applyEdits([EditOperation.insert(new Position(lastLine, lastLineMaxColumn), bufferedOutput)]); } - // reveal last line - const panel = this.panelService.getActivePanel(); - (panel).revealLastLine(true); + if (!this.channelIdsWithScrollLock.has(channel)) { + // reveal last line + const panel = this.panelService.getActivePanel(); + (panel).revealLastLine(true); + } } private isVisible(channel: string): boolean { @@ -276,6 +283,16 @@ class OutputContentProvider implements ITextModelContentProvider { return panel && panel.getId() === OUTPUT_PANEL_ID && this.outputService.getActiveChannel().id === channel; } + public toggleScrollLock(channelId: string): boolean { + let channelHadScrollLock = this.channelIdsWithScrollLock.has(channelId); + if (channelHadScrollLock) { + this.channelIdsWithScrollLock.delete(channelId) + } else { + this.channelIdsWithScrollLock.add(channelId); + } + return !channelHadScrollLock; + } + public provideTextContent(resource: URI): TPromise { const content = this.outputService.getChannel(resource.fsPath).output; diff --git a/src/vs/workbench/parts/output/common/output.ts b/src/vs/workbench/parts/output/common/output.ts index e3ce5b1bfa93ce82d79fddea377309a1d82ee426..a354eb925b04e96195f7ca3babc0dc91ebff80a8 100644 --- a/src/vs/workbench/parts/output/common/output.ts +++ b/src/vs/workbench/parts/output/common/output.ts @@ -124,6 +124,12 @@ export interface IOutputChannel { * Clears all received output for this channel. */ clear(): void; + + /** + * Toggles scroll lock for this channel. + * Returns a flag indicating whether the scroll lock is on/off. + */ + toggleScrollLock(): boolean; } export interface IOutputChannelIdentifier {