@@ -16,11 +16,12 @@ import { Codicon } from 'vs/base/common/codicons';
16
16
import { Emitter , Event } from 'vs/base/common/event' ;
17
17
import { FuzzyScore } from 'vs/base/common/filters' ;
18
18
import { IMarkdownString , MarkdownString } from 'vs/base/common/htmlContent' ;
19
- import { Disposable , DisposableStore , IDisposable } from 'vs/base/common/lifecycle' ;
19
+ import { Disposable , DisposableStore , IDisposable , toDisposable } from 'vs/base/common/lifecycle' ;
20
20
import { ResourceMap } from 'vs/base/common/map' ;
21
21
import { FileAccess } from 'vs/base/common/network' ;
22
22
import { ThemeIcon } from 'vs/base/common/themables' ;
23
23
import { withNullAsUndefined } from 'vs/base/common/types' ;
24
+ import { URI } from 'vs/base/common/uri' ;
24
25
import { EditorExtensionsRegistry } from 'vs/editor/browser/editorExtensions' ;
25
26
import { CodeEditorWidget } from 'vs/editor/browser/widget/codeEditorWidget' ;
26
27
import { EDITOR_FONT_DEFAULTS , IEditorOptions } from 'vs/editor/common/config/editorOptions' ;
@@ -49,6 +50,8 @@ import { defaultButtonStyles } from 'vs/platform/theme/browser/defaultStyles';
49
50
import { MenuPreventer } from 'vs/workbench/contrib/codeEditor/browser/menuPreventer' ;
50
51
import { SelectionClipboardContributionID } from 'vs/workbench/contrib/codeEditor/browser/selectionClipboard' ;
51
52
import { getSimpleEditorOptions } from 'vs/workbench/contrib/codeEditor/browser/simpleEditorOptions' ;
53
+ import { IInteractiveSessionCodeBlockActionContext } from 'vs/workbench/contrib/interactiveSession/browser/actions/interactiveSessionCodeblockActions' ;
54
+ import { IInteractiveSessionCodeBlockInfo } from 'vs/workbench/contrib/interactiveSession/browser/interactiveSession' ;
52
55
import { InteractiveSessionFollowups } from 'vs/workbench/contrib/interactiveSession/browser/interactiveSessionFollowups' ;
53
56
import { InteractiveSessionEditorOptions } from 'vs/workbench/contrib/interactiveSession/browser/interactiveSessionOptions' ;
54
57
import { CONTEXT_RESPONSE_HAS_PROVIDER_ID , CONTEXT_RESPONSE_VOTE } from 'vs/workbench/contrib/interactiveSession/common/interactiveSessionContextKeys' ;
@@ -87,6 +90,9 @@ export class InteractiveListItemRenderer extends Disposable implements ITreeRend
87
90
static readonly cursorCharacter = '\u258c' ;
88
91
static readonly ID = 'item' ;
89
92
93
+ private readonly codeBlocksByResponseId = new Map < string , IInteractiveSessionCodeBlockInfo [ ] > ( ) ;
94
+ private readonly codeBlocksByEditorUri = new ResourceMap < IInteractiveSessionCodeBlockInfo > ( ) ;
95
+
90
96
private readonly renderer : MarkdownRenderer ;
91
97
92
98
protected readonly _onDidClickFollowup = this . _register ( new Emitter < IInteractiveSessionReplyFollowup > ( ) ) ;
@@ -152,6 +158,15 @@ export class InteractiveListItemRenderer extends Disposable implements ITreeRend
152
158
return 8 ;
153
159
}
154
160
161
+ getCodeBlockInfosForResponse ( response : IInteractiveResponseViewModel ) : IInteractiveSessionCodeBlockInfo [ ] {
162
+ const codeBlocks = this . codeBlocksByResponseId . get ( response . id ) ;
163
+ return codeBlocks ?? [ ] ;
164
+ }
165
+
166
+ getCodeBlockInfoForEditor ( uri : URI ) : IInteractiveSessionCodeBlockInfo | undefined {
167
+ return this . codeBlocksByEditorUri . get ( uri ) ;
168
+ }
169
+
155
170
setVisible ( visible : boolean ) : void {
156
171
this . _isVisible = visible ;
157
172
}
@@ -385,10 +400,13 @@ export class InteractiveListItemRenderer extends Disposable implements ITreeRend
385
400
const usedSlashCommand = slashCommands . find ( s => markdown . value . startsWith ( `/${ s . command } ` ) ) ;
386
401
const toRender = usedSlashCommand ? markdown . value . slice ( usedSlashCommand . command . length + 2 ) : markdown . value ;
387
402
markdown = new MarkdownString ( toRender ) ;
403
+
404
+ const codeblocks : IInteractiveSessionCodeBlockInfo [ ] = [ ] ;
388
405
const result = this . renderer . render ( markdown , {
389
406
fillInIncompleteTokens,
390
407
codeBlockRendererSync : ( languageId , text ) => {
391
- const ref = this . renderCodeBlock ( { languageId, text, codeBlockIndex : codeBlockIndex ++ , element, parentContextKeyService : templateData . contextKeyService } , disposables ) ;
408
+ const data = { languageId, text, codeBlockIndex : codeBlockIndex ++ , element, parentContextKeyService : templateData . contextKeyService } ;
409
+ const ref = this . renderCodeBlock ( data , disposables ) ;
392
410
393
411
// Attach this after updating text/layout of the editor, so it should only be fired when the size updates later (horizontal scrollbar, wrapping)
394
412
// not during a renderElement OR a progressive render (when we will be firing this event anyway at the end of the render)
@@ -397,11 +415,28 @@ export class InteractiveListItemRenderer extends Disposable implements ITreeRend
397
415
this . _onDidChangeItemHeight . fire ( { element, height : templateData . rowContainer . offsetHeight } ) ;
398
416
} ) ) ;
399
417
418
+ if ( isResponseVM ( element ) ) {
419
+ const info = {
420
+ codeBlockIndex : data . codeBlockIndex ,
421
+ element,
422
+ focus ( ) {
423
+ ref . object . focus ( ) ;
424
+ }
425
+ } ;
426
+ codeblocks . push ( info ) ;
427
+ this . codeBlocksByEditorUri . set ( ref . object . textModel . uri , info ) ;
428
+ disposables . add ( toDisposable ( ( ) => this . codeBlocksByEditorUri . delete ( ref . object . textModel . uri ) ) ) ;
429
+ }
400
430
disposablesList . push ( ref ) ;
401
431
return ref . object . element ;
402
432
}
403
433
} ) ;
404
434
435
+ if ( isResponseVM ( element ) ) {
436
+ this . codeBlocksByResponseId . set ( element . id , codeblocks ) ;
437
+ disposables . add ( toDisposable ( ( ) => this . codeBlocksByResponseId . delete ( element . id ) ) ) ;
438
+ }
439
+
405
440
if ( usedSlashCommand ) {
406
441
const slashCommandElement = $ ( 'span.interactive-slash-command' , { title : usedSlashCommand . detail } , `/${ usedSlashCommand . command } ` ) ;
407
442
if ( result . element . firstChild ?. nodeName . toLowerCase ( ) === 'p' ) {
@@ -522,17 +557,10 @@ interface IInteractiveResultCodeBlockPart {
522
557
readonly textModel : ITextModel ;
523
558
layout ( width : number ) : void ;
524
559
render ( data : IInteractiveResultCodeBlockData , width : number ) : void ;
560
+ focus ( ) : void ;
525
561
dispose ( ) : void ;
526
562
}
527
563
528
- export interface IInteractiveSessionCodeBlockInfo {
529
- codeBlockIndex : number ;
530
- element : IInteractiveResponseViewModel ;
531
- }
532
-
533
- // Enable actions to look this up by editor URI. An alternative would be writing lots of details to element attributes.
534
- export const codeBlockInfoByModelUri = new ResourceMap < IInteractiveSessionCodeBlockInfo > ( ) ;
535
-
536
564
const defaultCodeblockPadding = 10 ;
537
565
538
566
class CodeBlockPart extends Disposable implements IInteractiveResultCodeBlockPart {
@@ -620,6 +648,10 @@ class CodeBlockPart extends Disposable implements IInteractiveResultCodeBlockPar
620
648
this . editor . setModel ( this . textModel ) ;
621
649
}
622
650
651
+ focus ( ) : void {
652
+ this . editor . focus ( ) ;
653
+ }
654
+
623
655
private updatePaddingForLayout ( ) {
624
656
// scrollWidth = "the width of the content that needs to be scrolled"
625
657
// contentWidth = "the width of the area where content is displayed"
@@ -669,16 +701,7 @@ class CodeBlockPart extends Disposable implements IInteractiveResultCodeBlockPar
669
701
670
702
this . layout ( width ) ;
671
703
672
- if ( isResponseVM ( data . element ) && data . element . providerResponseId ) {
673
- codeBlockInfoByModelUri . set ( this . textModel . uri , {
674
- element : data . element ,
675
- codeBlockIndex : data . codeBlockIndex ,
676
- } ) ;
677
- } else {
678
- codeBlockInfoByModelUri . delete ( this . textModel . uri ) ;
679
- }
680
-
681
- this . toolbar . context = < IInteractiveSessionCodeBlockInfo > {
704
+ this . toolbar . context = < IInteractiveSessionCodeBlockActionContext > {
682
705
code : data . text ,
683
706
codeBlockIndex : data . codeBlockIndex ,
684
707
element : data . element ,
0 commit comments