@@ -68,6 +68,7 @@ import { XtermTerminal } from 'vs/workbench/contrib/terminal/browser/xterm/xterm
6868import { ITerminalCommand , TerminalCapability } from 'vs/workbench/contrib/terminal/common/capabilities/capabilities' ;
6969import { TerminalCapabilityStoreMultiplexer } from 'vs/workbench/contrib/terminal/common/capabilities/terminalCapabilityStore' ;
7070import { IEnvironmentVariableInfo } from 'vs/workbench/contrib/terminal/common/environmentVariable' ;
71+ import { getCommandHistory } from 'vs/workbench/contrib/terminal/common/history' ;
7172import { DEFAULT_COMMANDS_TO_SKIP_SHELL , INavigationMode , ITerminalBackend , ITerminalProcessManager , ITerminalProfileResolverService , ProcessState , ShellIntegrationExitCode , TerminalCommandId , TERMINAL_CREATION_COMMANDS , TERMINAL_VIEW_ID } from 'vs/workbench/contrib/terminal/common/terminal' ;
7273import { TerminalContextKeys } from 'vs/workbench/contrib/terminal/common/terminalContextKey' ;
7374import { formatMessageForTerminal } from 'vs/workbench/contrib/terminal/common/terminalStrings' ;
@@ -399,11 +400,17 @@ export class TerminalInstance extends Disposable implements ITerminalInstance {
399400 this . _register ( this . capabilities . onDidAddCapability ( e => {
400401 this . _logService . debug ( 'terminalInstance added capability' , e ) ;
401402 if ( e === TerminalCapability . CwdDetection ) {
402- this . capabilities . get ( TerminalCapability . CwdDetection ) ?. onDidChangeCwd ( ( e ) => {
403+ this . capabilities . get ( TerminalCapability . CwdDetection ) ?. onDidChangeCwd ( e => {
403404 this . _cwd = e ;
404405 this . _xtermOnKey ?. dispose ( ) ;
405406 this . refreshTabLabels ( this . title , TitleEventSource . Config ) ;
406407 } ) ;
408+ } else if ( e === TerminalCapability . CommandDetection ) {
409+ this . capabilities . get ( TerminalCapability . CommandDetection ) ?. onCommandFinished ( e => {
410+ if ( e . command . trim ( ) . length > 0 ) {
411+ this . _instantiationService . invokeFunction ( getCommandHistory ) ?. add ( e . command , { shellType : this . _shellType } ) ;
412+ }
413+ } ) ;
407414 }
408415 } ) ) ;
409416 this . _register ( this . capabilities . onDidRemoveCapability ( e => this . _logService . debug ( 'terminalInstance removed capability' , e ) ) ) ;
@@ -756,65 +763,104 @@ export class TerminalInstance extends Disposable implements ITerminalInstance {
756763 }
757764
758765 async runRecent ( type : 'command' | 'cwd' ) : Promise < void > {
759- const commands = this . capabilities . get ( TerminalCapability . CommandDetection ) ?. commands ;
760- if ( ! commands || ! this . xterm ) {
766+ if ( ! this . xterm ) {
761767 return ;
762768 }
763769 type Item = IQuickPickItem & { command ?: ITerminalCommand } ;
764- const items : Item [ ] = [ ] ;
770+ let items : ( Item | IQuickPickItem | IQuickPickSeparator ) [ ] = [ ] ;
771+ const commandMap : Set < string > = new Set ( ) ;
772+
773+ const removeFromCommandHistoryButton : IQuickInputButton = {
774+ iconClass : ThemeIcon . asClassName ( Codicon . close ) ,
775+ tooltip : nls . localize ( 'removeCommand' , "Remove from Command History" )
776+ } ;
777+
765778 if ( type === 'command' ) {
766- for ( const entry of commands ) {
767- // trim off any whitespace and/or line endings
768- const label = entry . command . trim ( ) ;
769- if ( label . length === 0 ) {
770- continue ;
771- }
772- let detail = '' ;
773- if ( entry . cwd ) {
774- detail += `cwd: ${ entry . cwd } ` ;
775- }
776- if ( entry . exitCode ) {
777- // Since you cannot get the last command's exit code on pwsh, just whether it failed
778- // or not, -1 is treated specially as simply failed
779- if ( entry . exitCode === - 1 ) {
780- detail += 'failed' ;
781- } else {
782- detail += `exitCode: ${ entry . exitCode } ` ;
779+ const commands = this . capabilities . get ( TerminalCapability . CommandDetection ) ?. commands ;
780+ // Current session history
781+ if ( commands && commands . length > 0 ) {
782+ for ( const entry of commands ) {
783+ // trim off any whitespace and/or line endings
784+ const label = entry . command . trim ( ) ;
785+ if ( label . length === 0 ) {
786+ continue ;
787+ }
788+ let description = fromNow ( entry . timestamp , true ) ;
789+ if ( entry . cwd ) {
790+ description += ` @ ${ entry . cwd } ` ;
783791 }
792+ if ( entry . exitCode ) {
793+ // Since you cannot get the last command's exit code on pwsh, just whether it failed
794+ // or not, -1 is treated specially as simply failed
795+ if ( entry . exitCode === - 1 ) {
796+ description += ' failed' ;
797+ } else {
798+ description += ` exitCode: ${ entry . exitCode } ` ;
799+ }
800+ }
801+ description = description . trim ( ) ;
802+ const iconClass = ThemeIcon . asClassName ( Codicon . output ) ;
803+ const buttons : IQuickInputButton [ ] = [ {
804+ iconClass,
805+ tooltip : nls . localize ( 'viewCommandOutput' , "View Command Output" ) ,
806+ alwaysVisible : true
807+ } ] ;
808+ // Merge consecutive commands
809+ const lastItem = items . length > 0 ? items [ items . length - 1 ] : undefined ;
810+ if ( lastItem ?. type !== 'separator' && lastItem ?. label === label ) {
811+ lastItem . id = entry . timestamp . toString ( ) ;
812+ lastItem . description = description ;
813+ continue ;
814+ }
815+ items . push ( {
816+ label,
817+ description,
818+ id : entry . timestamp . toString ( ) ,
819+ command : entry ,
820+ buttons : ( ! entry . endMarker ?. isDisposed && ! entry . marker ?. isDisposed && ( entry . endMarker ! . line - entry . marker ! . line > 0 ) ) ? buttons : undefined
821+ } ) ;
822+ commandMap . add ( label ) ;
784823 }
785- detail = detail . trim ( ) ;
786- const iconClass = ThemeIcon . asClassName ( Codicon . output ) ;
787- const buttons : IQuickInputButton [ ] = [ {
788- iconClass,
789- tooltip : nls . localize ( 'viewCommandOutput' , "View Command Output" ) ,
790- alwaysVisible : true
791- } ] ;
792- // Merge consecutive commands
793- if ( items . length > 0 && items [ items . length - 1 ] . label === label ) {
794- items [ items . length - 1 ] . id = entry . timestamp . toString ( ) ;
795- items [ items . length - 1 ] . detail = detail ;
796- continue ;
824+ items = items . reverse ( ) ;
825+ items . unshift ( { type : 'separator' , label : 'current session' } ) ;
826+ }
827+
828+ // Gather previous session history
829+ const history = this . _instantiationService . invokeFunction ( getCommandHistory ) ;
830+ const previousSessionItems : IQuickPickItem [ ] = [ ] ;
831+ for ( const [ label , info ] of history . entries ) {
832+ // Only add previous session item if it's not in this session
833+ if ( ! commandMap . has ( label ) && info . shellType === this . shellType ) {
834+ previousSessionItems . push ( {
835+ label,
836+ buttons : [ removeFromCommandHistoryButton ]
837+ } ) ;
797838 }
798- items . push ( {
799- label,
800- description : fromNow ( entry . timestamp , true ) ,
801- detail,
802- id : entry . timestamp . toString ( ) ,
803- command : entry ,
804- buttons : ( ! entry . endMarker ?. isDisposed && ! entry . marker ?. isDisposed && ( entry . endMarker ! . line - entry . marker ! . line > 0 ) ) ? buttons : undefined
805- } ) ;
839+ }
840+ if ( previousSessionItems . length > 0 ) {
841+ items . push (
842+ { type : 'separator' , label : 'previous sessions' } ,
843+ ...previousSessionItems
844+ ) ;
806845 }
807846 } else {
808847 const cwds = this . capabilities . get ( TerminalCapability . CwdDetection ) ?. cwds || [ ] ;
809848 for ( const label of cwds ) {
810849 items . push ( { label } ) ;
811850 }
851+ items = items . reverse ( ) ;
852+ }
853+ if ( items . length === 0 ) {
854+ return ;
812855 }
813856 const outputProvider = this . _instantiationService . createInstance ( TerminalOutputProvider ) ;
814857 const quickPick = this . _quickInputService . createQuickPick ( ) ;
815- quickPick . items = items . reverse ( ) ;
858+ quickPick . items = items ;
816859 return new Promise < void > ( r => {
817860 quickPick . onDidTriggerItemButton ( async e => {
861+ if ( e . button === removeFromCommandHistoryButton ) {
862+ this . _instantiationService . invokeFunction ( getCommandHistory ) ?. remove ( e . item . label ) ;
863+ }
818864 const selectedCommand = ( e . item as Item ) . command ;
819865 const output = selectedCommand ?. getOutput ( ) ;
820866 if ( output && selectedCommand ?. command ) {
0 commit comments