55'use strict' ;
66
77import * as vscode from 'vscode' ;
8- import { CheckFilesExistResult , CloseResult , OpenLocalFileArgs } from '../../common/views' ;
8+ import { CloseResult , OpenLocalFileArgs } from '../../common/views' ;
99import { openPullRequestOnGitHub } from '../commands' ;
1010import { FolderRepositoryManager } from './folderRepositoryManager' ;
1111import { GithubItemStateEnum , IAccount , IMilestone , IProject , IProjectItem , RepoAccessAndMergeMethods } from './interface' ;
1212import { IssueModel } from './issueModel' ;
1313import { getAssigneesQuickPickItems , getLabelOptions , getMilestoneFromQuickPick , getProjectFromQuickPick } from './quickPicks' ;
14- import { isInCodespaces , vscodeDevPrLink } from './utils' ;
14+ import { isInCodespaces , processPermalinks , vscodeDevPrLink } from './utils' ;
1515import { ChangeAssigneesReply , DisplayLabel , Issue , ProjectItemsReply , SubmitReviewReply , UnresolvedIdentity } from './views' ;
1616import { COPILOT_ACCOUNTS , IComment } from '../common/comment' ;
1717import { emojify , ensureEmojis } from '../common/emoji' ;
@@ -321,10 +321,13 @@ export class IssueOverviewPanel<TItem extends IssueModel = IssueModel> extends W
321321 this . _item = issue as TItem ;
322322 this . setPanelTitle ( this . buildPanelTitle ( issueModel . number , issueModel . title ) ) ;
323323
324+ // Process permalinks in bodyHTML before sending to webview
325+ issue . bodyHTML = await this . processLinksInBodyHtml ( issue . bodyHTML ) ;
326+
324327 Logger . debug ( 'pr.initialize' , IssueOverviewPanel . ID ) ;
325328 this . _postMessage ( {
326329 command : 'pr.initialize' ,
327- pullrequest : this . getInitializeContext ( currentUser , issue , timelineEvents , repositoryAccess , viewerCanEdit , assignableUsers [ this . _item . remote . remoteName ] ?? [ ] ) ,
330+ pullrequest : this . getInitializeContext ( currentUser , issue , await this . processTimelineEvents ( timelineEvents ) , repositoryAccess , viewerCanEdit , assignableUsers [ this . _item . remote . remoteName ] ?? [ ] ) ,
328331 } ) ;
329332
330333 } catch ( e ) {
@@ -447,8 +450,6 @@ export class IssueOverviewPanel<TItem extends IssueModel = IssueModel> extends W
447450 return openPullRequestOnGitHub ( this . _item , this . _telemetry ) ;
448451 case 'pr.open-local-file' :
449452 return this . openLocalFile ( message ) ;
450- case 'pr.check-files-exist' :
451- return this . checkFilesExist ( message ) ;
452453 case 'pr.debug' :
453454 return this . webviewDebug ( message ) ;
454455 default :
@@ -572,16 +573,51 @@ export class IssueOverviewPanel<TItem extends IssueModel = IssueModel> extends W
572573 Logger . debug ( message . args , IssueOverviewPanel . ID ) ;
573574 }
574575
575- private editDescription ( message : IRequestMessage < { text : string } > ) {
576- this . _item
577- . edit ( { body : message . args . text } )
578- . then ( result => {
579- this . _replyMessage ( message , { body : result . body , bodyHTML : result . bodyHTML } ) ;
580- } )
581- . catch ( e => {
582- this . _throwError ( message , e ) ;
583- vscode . window . showErrorMessage ( `Editing description failed: ${ formatError ( e ) } ` ) ;
584- } ) ;
576+ /**
577+ * Process permalinks in bodyHTML. Can be overridden by subclasses (e.g., PullRequestOverviewPanel)
578+ * to provide custom processing logic for different item types.
579+ * Returns undefined if bodyHTML is undefined.
580+ */
581+ protected async processLinksInBodyHtml ( bodyHTML : string | undefined ) : Promise < string | undefined > {
582+ if ( ! bodyHTML ) {
583+ return bodyHTML ;
584+ }
585+ return processPermalinks (
586+ bodyHTML ,
587+ this . _item . githubRepository ,
588+ this . _item . githubRepository . rootUri
589+ ) ;
590+ }
591+
592+ /**
593+ * Process permalinks in timeline events (comments, reviews, commits).
594+ * Updates bodyHTML fields for all events that contain them.
595+ */
596+ protected async processTimelineEvents ( events : TimelineEvent [ ] ) : Promise < TimelineEvent [ ] > {
597+ return Promise . all ( events . map ( async ( event ) => {
598+ if ( event . event === EventType . Commented || event . event === EventType . Reviewed || event . event === EventType . Committed ) {
599+ event . bodyHTML = await this . processLinksInBodyHtml ( event . bodyHTML ) ;
600+ // ReviewEvent also has comments array
601+ if ( event . event === EventType . Reviewed && event . comments ) {
602+ event . comments = await Promise . all ( event . comments . map ( async ( comment : IComment ) => {
603+ comment . bodyHTML = await this . processLinksInBodyHtml ( comment . bodyHTML ) ;
604+ return comment ;
605+ } ) ) ;
606+ }
607+ }
608+ return event ;
609+ } ) ) ;
610+ }
611+
612+ private async editDescription ( message : IRequestMessage < { text : string } > ) {
613+ try {
614+ const result = await this . _item . edit ( { body : message . args . text } ) ;
615+ const bodyHTML = await this . processLinksInBodyHtml ( result . bodyHTML ) ;
616+ this . _replyMessage ( message , { body : result . body , bodyHTML } ) ;
617+ } catch ( e ) {
618+ this . _throwError ( message , e ) ;
619+ vscode . window . showErrorMessage ( `Editing description failed: ${ formatError ( e ) } ` ) ;
620+ }
585621 }
586622 private editTitle ( message : IRequestMessage < { text : string } > ) {
587623 return this . _item
@@ -622,7 +658,7 @@ export class IssueOverviewPanel<TItem extends IssueModel = IssueModel> extends W
622658 if ( allAssignees ) {
623659 const newAssignees : IAccount [ ] = allAssignees . map ( item => item . user ) ;
624660 await this . _item . replaceAssignees ( newAssignees ) ;
625- const events = await this . _getTimeline ( ) ;
661+ const events = await this . processTimelineEvents ( await this . _getTimeline ( ) ) ;
626662 const reply : ChangeAssigneesReply = {
627663 assignees : newAssignees ,
628664 events
@@ -689,7 +725,7 @@ export class IssueOverviewPanel<TItem extends IssueModel = IssueModel> extends W
689725 const newAssignees = ( this . _item . assignees ?? [ ] ) . concat ( currentUser ) ;
690726 await this . _item . replaceAssignees ( newAssignees ) ;
691727 }
692- const events = await this . _getTimeline ( ) ;
728+ const events = await this . processTimelineEvents ( await this . _getTimeline ( ) ) ;
693729 const reply : ChangeAssigneesReply = {
694730 assignees : this . _item . assignees ?? [ ] ,
695731 events
@@ -707,7 +743,7 @@ export class IssueOverviewPanel<TItem extends IssueModel = IssueModel> extends W
707743 const newAssignees = ( this . _item . assignees ?? [ ] ) . concat ( copilotUser ) ;
708744 await this . _item . replaceAssignees ( newAssignees ) ;
709745 }
710- const events = await this . _getTimeline ( ) ;
746+ const events = await this . processTimelineEvents ( await this . _getTimeline ( ) ) ;
711747 const reply : ChangeAssigneesReply = {
712748 assignees : this . _item . assignees ?? [ ] ,
713749 events
@@ -730,18 +766,15 @@ export class IssueOverviewPanel<TItem extends IssueModel = IssueModel> extends W
730766 return this . _item . editIssueComment ( comment , text ) ;
731767 }
732768
733- private editComment ( message : IRequestMessage < { comment : IComment ; text : string } > ) {
734- this . editCommentPromise ( message . args . comment , message . args . text )
735- . then ( result => {
736- this . _replyMessage ( message , {
737- body : result . body ,
738- bodyHTML : result . bodyHTML ,
739- } ) ;
740- } )
741- . catch ( e => {
742- this . _throwError ( message , e ) ;
743- vscode . window . showErrorMessage ( formatError ( e ) ) ;
744- } ) ;
769+ private async editComment ( message : IRequestMessage < { comment : IComment ; text : string } > ) {
770+ try {
771+ const result = await this . editCommentPromise ( message . args . comment , message . args . text ) ;
772+ const bodyHTML = await this . processLinksInBodyHtml ( result . bodyHTML ) ;
773+ this . _replyMessage ( message , { body : result . body , bodyHTML } ) ;
774+ } catch ( e ) {
775+ this . _throwError ( message , e ) ;
776+ vscode . window . showErrorMessage ( formatError ( e ) ) ;
777+ }
745778 }
746779
747780 protected deleteCommentPromise ( comment : IComment ) : Promise < void > {
@@ -787,26 +820,6 @@ export class IssueOverviewPanel<TItem extends IssueModel = IssueModel> extends W
787820 }
788821 }
789822
790- private async checkFilesExist ( message : IRequestMessage < string [ ] > ) : Promise < void > {
791- const files = message . args ;
792- const results : CheckFilesExistResult = { } ;
793-
794- await Promise . all ( files . map ( async ( relativePath ) => {
795- const localFile = vscode . Uri . joinPath (
796- this . _item . githubRepository . rootUri ,
797- relativePath
798- ) ;
799- try {
800- const stat = await vscode . workspace . fs . stat ( localFile ) ;
801- results [ relativePath ] = stat . type === vscode . FileType . File ;
802- } catch ( e ) {
803- results [ relativePath ] = false ;
804- }
805- } ) ) ;
806-
807- return this . _replyMessage ( message , results ) ;
808- }
809-
810823 protected async close ( message : IRequestMessage < string > ) {
811824 let comment : IComment | undefined ;
812825 if ( message . args ) {
0 commit comments