66import { KeyCode } from 'vs/base/common/keyCodes' ;
77import { DisposableStore } from 'vs/base/common/lifecycle' ;
88import { URI } from 'vs/base/common/uri' ;
9+ import * as dom from 'vs/base/browser/dom' ;
910import { IEditorConstructionOptions } from 'vs/editor/browser/config/editorConfiguration' ;
1011import { EditorExtensionsRegistry } from 'vs/editor/browser/editorExtensions' ;
1112import { CodeEditorWidget , ICodeEditorWidgetOptions } from 'vs/editor/browser/widget/codeEditorWidget' ;
@@ -19,8 +20,13 @@ import { TerminalSettingId } from 'vs/platform/terminal/common/terminal';
1920import { SelectionClipboardContributionID } from 'vs/workbench/contrib/codeEditor/browser/selectionClipboard' ;
2021import { getSimpleEditorOptions } from 'vs/workbench/contrib/codeEditor/browser/simpleEditorOptions' ;
2122import { ITerminalFont } from 'vs/workbench/contrib/terminal/common/terminal' ;
22- import { IXtermTerminal } from 'vs/workbench/contrib/terminal/browser/terminal' ;
23+ import { ITerminalInstance , IXtermTerminal } from 'vs/workbench/contrib/terminal/browser/terminal' ;
2324import type { Terminal } from 'xterm' ;
25+ import { TerminalCapability } from 'vs/platform/terminal/common/capabilities/capabilities' ;
26+ import { IQuickInputService , IQuickPick , IQuickPickItem } from 'vs/platform/quickinput/common/quickInput' ;
27+ import { AudioCue , IAudioCueService } from 'vs/platform/audioCues/browser/audioCueService' ;
28+ import { IContextKey , IContextKeyService } from 'vs/platform/contextkey/common/contextkey' ;
29+ import { TerminalContextKeys } from 'vs/workbench/contrib/terminal/common/terminalContextKey' ;
2430
2531const enum Constants {
2632 Scheme = 'terminal-accessible-buffer' ,
@@ -35,15 +41,22 @@ export class AccessibleBufferWidget extends DisposableStore {
3541 private _editorContainer : HTMLElement ;
3642 private _font : ITerminalFont ;
3743 private _xtermElement : HTMLElement ;
44+ private readonly _focusedContextKey : IContextKey < boolean > ;
45+ private readonly _focusTracker : dom . IFocusTracker ;
3846
3947 constructor (
40- private readonly _instanceId : number ,
48+ private readonly _instance : ITerminalInstance ,
4149 private readonly _xterm : IXtermTerminal & { raw : Terminal } ,
4250 @IInstantiationService private readonly _instantiationService : IInstantiationService ,
4351 @IModelService private readonly _modelService : IModelService ,
44- @IConfigurationService private readonly _configurationService : IConfigurationService
52+ @IConfigurationService private readonly _configurationService : IConfigurationService ,
53+ @IQuickInputService private readonly _quickInputService : IQuickInputService ,
54+ @IAudioCueService private readonly _audioCueService : IAudioCueService ,
55+ @IContextKeyService private readonly _contextKeyService : IContextKeyService ,
56+
4557 ) {
4658 super ( ) ;
59+ this . _focusedContextKey = TerminalContextKeys . accessibleBufferFocus . bindTo ( this . _contextKeyService ) ;
4760 const codeEditorWidgetOptions : ICodeEditorWidgetOptions = {
4861 isSimpleWidget : true ,
4962 contributions : EditorExtensionsRegistry . getSomeEditorContributions ( [ LinkDetector . ID , SelectionClipboardContributionID ] )
@@ -76,6 +89,9 @@ export class AccessibleBufferWidget extends DisposableStore {
7689 this . _editorContainer = document . createElement ( 'div' ) ;
7790 this . _accessibleBuffer . tabIndex = - 1 ;
7891 this . _bufferEditor = this . _instantiationService . createInstance ( CodeEditorWidget , this . _editorContainer , editorOptions , codeEditorWidgetOptions ) ;
92+ this . _focusTracker = this . add ( dom . trackFocus ( this . _editorContainer ) ) ;
93+ this . add ( this . _focusTracker . onDidFocus ( ( ) => this . _focusedContextKey . set ( true ) ) ) ;
94+ this . add ( this . _focusTracker . onDidBlur ( ( ) => this . _focusedContextKey . reset ( ) ) ) ;
7995 this . _accessibleBuffer . replaceChildren ( this . _editorContainer ) ;
8096 this . _xtermElement . insertAdjacentElement ( 'beforebegin' , this . _accessibleBuffer ) ;
8197 this . _bufferEditor . layout ( { width : this . _xtermElement . clientWidth , height : this . _xtermElement . clientHeight } ) ;
@@ -95,18 +111,18 @@ export class AccessibleBufferWidget extends DisposableStore {
95111 }
96112 } ) ) ;
97113 this . add ( this . _xterm . raw . onWriteParsed ( async ( ) => {
98- if ( this . _accessibleBuffer . classList . contains ( Constants . Active ) ) {
114+ if ( this . _focusedContextKey . get ( ) ) {
99115 await this . _updateEditor ( true ) ;
100116 }
101117 } ) ) ;
102- this . _updateEditor ( ) ;
103118 this . add ( this . _bufferEditor . onDidFocusEditorText ( async ( ) => {
104119 // if the editor is focused via tab, we need to update the model
105120 // and show it
106121 await this . _updateEditor ( ) ;
107122 this . _accessibleBuffer . classList . add ( Constants . Active ) ;
108123 this . _xtermElement . classList . add ( Constants . Hide ) ;
109124 } ) ) ;
125+ this . _updateEditor ( ) ;
110126 }
111127
112128 private _hide ( ) : void {
@@ -122,7 +138,7 @@ export class AccessibleBufferWidget extends DisposableStore {
122138 const lineNumber = lineCount + 1 ;
123139 model . pushEditOperations ( null , [ { range : { startLineNumber : lineNumber , endLineNumber : lineNumber , startColumn : 1 , endColumn : 1 } , text : await this . _getContent ( lineNumber - 1 ) } ] , ( ) => [ ] ) ;
124140 } else {
125- model = await this . _getTextModel ( URI . from ( { scheme : `${ Constants . Scheme } -${ this . _instanceId } ` , fragment : await this . _getContent ( ) } ) ) ;
141+ model = await this . _getTextModel ( URI . from ( { scheme : `${ Constants . Scheme } -${ this . _instance . instanceId } ` , fragment : await this . _getContent ( ) } ) ) ;
126142 }
127143 if ( ! model ) {
128144 throw new Error ( 'Could not create accessible buffer editor model' ) ;
@@ -145,6 +161,49 @@ export class AccessibleBufferWidget extends DisposableStore {
145161 this . _bufferEditor . setScrollTop ( this . _bufferEditor . getScrollHeight ( ) ) ;
146162 }
147163
164+ async createQuickPick ( ) : Promise < IQuickPick < IQuickPickItem > | undefined > {
165+ if ( ! this . _focusedContextKey . get ( ) ) {
166+ await this . show ( ) ;
167+ }
168+ const commands = this . _instance . capabilities . get ( TerminalCapability . CommandDetection ) ?. commands ;
169+ if ( ! commands ?. length ) {
170+ return ;
171+ }
172+ const quickPickItems : IQuickPickItem [ ] = [ ] ;
173+ for ( const command of commands ) {
174+ const line = command . marker ?. line ;
175+ if ( ! line ) {
176+ continue ;
177+ }
178+ quickPickItems . push (
179+ {
180+ label : localize ( 'terminal.integrated.symbolQuickPick.labelNoExitCode' , '{0}' , command . command ) ,
181+ meta : JSON . stringify ( { line : line + 1 , exitCode : command . exitCode } )
182+ } ) ;
183+ }
184+ const quickPick = this . _quickInputService . createQuickPick < IQuickPickItem > ( ) ;
185+ quickPick . onDidAccept ( ( ) => {
186+ const item = quickPick . activeItems [ 0 ] ;
187+ const model = this . _bufferEditor . getModel ( ) ;
188+ if ( ! model || ! item . meta ) {
189+ return ;
190+ }
191+ quickPick . hide ( ) ;
192+ const data : { line : number ; exitCode : number } = JSON . parse ( item . meta ) ;
193+ this . _bufferEditor . setSelection ( { startLineNumber : data . line , startColumn : 1 , endLineNumber : data . line , endColumn : 1 } ) ;
194+ this . _bufferEditor . revealLine ( data . line ) ;
195+ return ;
196+ } ) ;
197+ quickPick . onDidChangeActive ( ( ) => {
198+ const data = quickPick . activeItems ?. [ 0 ] ?. meta ;
199+ if ( data && JSON . parse ( data ) . exitCode ) {
200+ this . _audioCueService . playAudioCue ( AudioCue . error , true ) ;
201+ }
202+ } ) ;
203+ quickPick . items = quickPickItems . reverse ( ) ;
204+ return quickPick ;
205+ }
206+
148207 async show ( ) : Promise < void > {
149208 await this . _updateEditor ( ) ;
150209 this . _accessibleBuffer . tabIndex = - 1 ;
0 commit comments