@@ -10,7 +10,7 @@ import { emojify, ensureEmojis } from '../common/emoji';
1010import Logger from '../common/logger' ;
1111import { DataUri } from '../common/uri' ;
1212import { ALLOWED_USERS , JSDOC_NON_USERS , PHPDOC_NON_USERS } from '../common/user' ;
13- import { stringReplaceAsync } from '../common/utils' ;
13+ import { escapeRegExp , stringReplaceAsync } from '../common/utils' ;
1414import { GitHubRepository } from './githubRepository' ;
1515import { IAccount } from './interface' ;
1616import { updateCommentReactions } from './utils' ;
@@ -208,6 +208,7 @@ export class TemporaryComment extends CommentBase {
208208
209209const SUGGESTION_EXPRESSION = / ` ` ` s u g g e s t i o n ( \u0020 * ( \r \n | \n ) ) ( (?< suggestion > [ \s \S ] * ?) ( \r \n | \n ) ) ? ` ` ` / ;
210210const IMG_EXPRESSION = / < i m g .* s r c = [ ' " ] (?< src > .+ ?) [ ' " ] .* ?> / g;
211+ const UUID_EXPRESSION = / [ 0 - 9 a - f A - F ] { 8 } - [ 0 - 9 a - f A - F ] { 4 } - [ 1 - 5 ] [ 0 - 9 a - f A - F ] { 3 } - [ 8 9 a b A B ] [ 0 - 9 a - f A - F ] { 3 } - [ 0 - 9 a - f A - F ] { 12 } / ;
211212
212213export class GHPRComment extends CommentBase {
213214 private static ID = 'GHPRComment' ;
@@ -221,14 +222,17 @@ export class GHPRComment extends CommentBase {
221222
222223 private _rawBody : string | vscode . MarkdownString ;
223224 private replacedBody : string ;
225+ private githubRepository : GitHubRepository | undefined ;
224226
225- constructor ( private readonly context : vscode . ExtensionContext , comment : IComment , parent : GHPRCommentThread , private readonly githubRepositories ?: GitHubRepository [ ] ) {
227+ constructor ( private readonly context : vscode . ExtensionContext , comment : IComment , parent : GHPRCommentThread , githubRepositories ?: GitHubRepository [ ] ) {
226228 super ( parent ) ;
227229 this . rawComment = comment ;
228230 this . originalAuthor = {
229231 name : comment . user ?. specialDisplayName ?? comment . user ! . login ,
230232 iconPath : comment . user && comment . user . avatarUrl ? vscode . Uri . parse ( comment . user . avatarUrl ) : undefined ,
231233 } ;
234+ const url = vscode . Uri . parse ( comment . url ) ;
235+ this . githubRepository = githubRepositories ?. find ( repo => repo . remote . host === url . authority ) ;
232236
233237 const avatarUrisPromise = comment . user ? DataUri . avatarCirclesAsImageDataUris ( context , [ comment . user ] , 28 , 28 ) : Promise . resolve ( [ ] ) ;
234238 this . doSetBody ( comment . body , ! comment . user ) . then ( async ( ) => { // only refresh if there's no user. If there's a user, we'll refresh in the then.
@@ -364,20 +368,18 @@ ${args[3] ?? ''}
364368 }
365369
366370 private async replacePermalink ( body : string ) : Promise < string > {
367- const githubRepositories = this . githubRepositories ;
368- if ( ! githubRepositories || githubRepositories . length === 0 ) {
371+ const githubRepository = this . githubRepository ;
372+ if ( ! githubRepository ) {
369373 return body ;
370374 }
371375
372- const expression = new RegExp ( `https://github.com/(.+)/${ githubRepositories [ 0 ] . remote . repositoryName } /blob/([0-9a-f]{40})/(.*)#L([0-9]+)(-L([0-9]+))?` , 'g' ) ;
376+ const repoName = escapeRegExp ( githubRepository . remote . repositoryName ) ;
377+ const expression = new RegExp ( `https://github.com/(.+)/${ repoName } /blob/([0-9a-f]{40})/(.*)#L([0-9]+)(-L([0-9]+))?` , 'g' ) ;
373378 return stringReplaceAsync ( body , expression , async ( match : string , owner : string , sha : string , file : string , start : string , _endGroup ?: string , end ?: string , index ?: number ) => {
374379 if ( index && ( index > 0 ) && ( body . charAt ( index - 1 ) === '(' ) ) {
375380 return match ;
376381 }
377- const githubRepository = githubRepositories . find ( repository => repository . remote . owner . toLocaleLowerCase ( ) === owner . toLocaleLowerCase ( ) ) ;
378- if ( ! githubRepository ) {
379- return match ;
380- }
382+
381383 const startLine = parseInt ( start ) ;
382384 const endLine = end ? parseInt ( end ) : startLine + 1 ;
383385 const lineContents = await githubRepository . getLines ( sha , file , startLine , endLine ) ;
@@ -398,6 +400,15 @@ ${lineContents}
398400 } ) ;
399401 }
400402
403+ private replaceImages ( body : string ) : string {
404+ const html = this . rawComment . bodyHTML ;
405+ if ( ! html ) {
406+ return body ;
407+ }
408+
409+ return replaceImages ( body , html , this . githubRepository ?. remote . host ) ;
410+ }
411+
401412 private replaceNewlines ( body : string ) {
402413 return body . replace ( / (?< ! \s ) ( \r \n | \n ) / g, ' \n' ) ;
403414 }
@@ -416,7 +427,8 @@ ${lineContents}
416427 const permalinkReplaced = await this . replacePermalink ( body . value ) ;
417428 return this . replaceImg ( this . replaceSuggestion ( permalinkReplaced ) ) ;
418429 }
419- const newLinesReplaced = this . replaceNewlines ( body ) ;
430+ const imagesReplaced = this . replaceImages ( body ) ;
431+ const newLinesReplaced = this . replaceNewlines ( imagesReplaced ) ;
420432 const documentLanguage = ( await vscode . workspace . openTextDocument ( this . parent . uri ) ) . languageId ;
421433 const replacerRegex = new RegExp ( `([^/\[\`]|^)@(${ ALLOWED_USERS } )` , 'g' ) ;
422434 // Replace user
@@ -471,3 +483,24 @@ ${lineContents}
471483 return new vscode . MarkdownString ( this . rawComment . body ) ;
472484 }
473485}
486+
487+ export function replaceImages ( markdownBody : string , htmlBody : string , host : string = 'github.com' ) {
488+ const originalExpression = new RegExp ( `https:\/\/${ host } \/.+\/assets\/([^\/]+\/)?(?<uuid>${ UUID_EXPRESSION . source } )` ) ;
489+ let originalMatch = markdownBody . match ( originalExpression ) ;
490+ const htmlHost = escapeRegExp ( host === 'github.com' ? 'githubusercontent.com' : host ) ;
491+
492+ while ( originalMatch ) {
493+ if ( originalMatch . groups ?. uuid ) {
494+ const uuid = escapeRegExp ( originalMatch . groups . uuid ) ;
495+ const htmlExpression = new RegExp ( `https:\/\/([^"]*${ htmlHost } )\/[^?]+${ uuid } [^"]+` ) ;
496+ const htmlMatch = htmlBody . match ( htmlExpression ) ;
497+ if ( htmlMatch && htmlMatch [ 0 ] ) {
498+ markdownBody = markdownBody . replace ( originalMatch [ 0 ] , htmlMatch [ 0 ] ) ;
499+ } else {
500+ return markdownBody ;
501+ }
502+ }
503+ originalMatch = markdownBody . match ( originalExpression ) ;
504+ }
505+ return markdownBody ;
506+ }
0 commit comments