11import * as path from 'path' ;
22import * as React from 'react' ;
33import * as Sarif from 'sarif' ;
4+ import * as Keys from '../result-keys' ;
45import { LocationStyle , ResolvableLocationValue } from 'semmle-bqrs' ;
56import * as octicons from './octicons' ;
6- import { className , renderLocation , ResultTableProps , zebraStripe } from './result-table-utils' ;
7+ import { className , renderLocation , ResultTableProps , zebraStripe , selectableZebraStripe } from './result-table-utils' ;
78import { PathTableResultSet } from './results' ;
89
910export type PathTableProps = ResultTableProps & { resultSet : PathTableResultSet } ;
1011export interface PathTableState {
1112 expanded : { [ k : string ] : boolean } ;
13+ selectedPathNode : undefined | Keys . PathNode ;
1214}
1315
1416interface SarifLink {
@@ -72,7 +74,7 @@ export function getPathRelativeToSourceLocationPrefix(sourceLocationPrefix: stri
7274export class PathTable extends React . Component < PathTableProps , PathTableState > {
7375 constructor ( props : PathTableProps ) {
7476 super ( props ) ;
75- this . state = { expanded : { } } ;
77+ this . state = { expanded : { } , selectedPathNode : undefined } ;
7678 }
7779
7880 /**
@@ -118,7 +120,8 @@ export class PathTable extends React.Component<PathTableProps, PathTableState> {
118120 if ( typeof part === "string" ) {
119121 result . push ( < span > { part } </ span > ) ;
120122 } else {
121- const renderedLocation = renderSarifLocationWithText ( part . text , relatedLocationsById [ part . dest ] ) ;
123+ const renderedLocation = renderSarifLocationWithText ( part . text , relatedLocationsById [ part . dest ] ,
124+ undefined ) ;
122125 result . push ( < span > { renderedLocation } </ span > ) ;
123126 }
124127 } return result ;
@@ -191,14 +194,23 @@ export class PathTable extends React.Component<PathTableProps, PathTableState> {
191194 }
192195 }
193196
194- function renderSarifLocationWithText ( text : string | undefined , loc : Sarif . Location ) : JSX . Element | undefined {
197+ const updateSelectionCallback = ( pathNodeKey : Keys . PathNode | undefined ) => {
198+ return ( ) => {
199+ this . setState ( previousState => ( {
200+ ...previousState ,
201+ selectedPathNode : pathNodeKey
202+ } ) ) ;
203+ }
204+ } ;
205+
206+ function renderSarifLocationWithText ( text : string | undefined , loc : Sarif . Location , pathNodeKey : Keys . PathNode | undefined ) : JSX . Element | undefined {
195207 const parsedLoc = parseSarifLocation ( loc ) ;
196208 switch ( parsedLoc . t ) {
197209 case 'NoLocation' :
198210 return renderNonLocation ( text , parsedLoc . hint ) ;
199211 case LocationStyle . FivePart :
200212 case LocationStyle . WholeFile :
201- return renderLocation ( parsedLoc , text , databaseUri ) ;
213+ return renderLocation ( parsedLoc , text , databaseUri , undefined , updateSelectionCallback ( pathNodeKey ) ) ;
202214 }
203215 return undefined ;
204216 }
@@ -207,7 +219,7 @@ export class PathTable extends React.Component<PathTableProps, PathTableState> {
207219 * Render sarif location as a link with the text being simply a
208220 * human-readable form of the location itself.
209221 */
210- function renderSarifLocation ( loc : Sarif . Location ) : JSX . Element | undefined {
222+ function renderSarifLocation ( loc : Sarif . Location , pathNodeKey : Keys . PathNode | undefined ) : JSX . Element | undefined {
211223 const parsedLoc = parseSarifLocation ( loc ) ;
212224 let shortLocation , longLocation : string ;
213225 switch ( parsedLoc . t ) {
@@ -216,11 +228,11 @@ export class PathTable extends React.Component<PathTableProps, PathTableState> {
216228 case LocationStyle . WholeFile :
217229 shortLocation = `${ path . basename ( parsedLoc . userVisibleFile ) } ` ;
218230 longLocation = `${ parsedLoc . userVisibleFile } ` ;
219- return renderLocation ( parsedLoc , shortLocation , databaseUri , longLocation ) ;
231+ return renderLocation ( parsedLoc , shortLocation , databaseUri , longLocation , updateSelectionCallback ( pathNodeKey ) ) ;
220232 case LocationStyle . FivePart :
221233 shortLocation = `${ path . basename ( parsedLoc . userVisibleFile ) } :${ parsedLoc . lineStart } :${ parsedLoc . colStart } ` ;
222234 longLocation = `${ parsedLoc . userVisibleFile } ` ;
223- return renderLocation ( parsedLoc , shortLocation , databaseUri , longLocation ) ;
235+ return renderLocation ( parsedLoc , shortLocation , databaseUri , longLocation , updateSelectionCallback ( pathNodeKey ) ) ;
224236 }
225237 }
226238
@@ -245,7 +257,7 @@ export class PathTable extends React.Component<PathTableProps, PathTableState> {
245257 const currentResultExpanded = this . state . expanded [ expansionIndex ] ;
246258 const indicator = currentResultExpanded ? octicons . chevronDown : octicons . chevronRight ;
247259 const location = result . locations !== undefined && result . locations . length > 0 &&
248- renderSarifLocation ( result . locations [ 0 ] ) ;
260+ renderSarifLocation ( result . locations [ 0 ] , Keys . none ) ;
249261 const locationCells = < td className = "vscode-codeql__location-cell" > { location } </ td > ;
250262
251263 if ( result . codeFlows === undefined ) {
@@ -260,12 +272,7 @@ export class PathTable extends React.Component<PathTableProps, PathTableState> {
260272 ) ;
261273 }
262274 else {
263- const paths : Sarif . ThreadFlow [ ] = [ ] ;
264- for ( const codeFlow of result . codeFlows ) {
265- for ( const threadFlow of codeFlow . threadFlows ) {
266- paths . push ( threadFlow ) ;
267- }
268- }
275+ const paths : Sarif . ThreadFlow [ ] = Keys . getAllPaths ( result ) ;
269276
270277 const indices = paths . length == 1 ?
271278 [ expansionIndex , expansionIndex + 1 ] : /* if there's exactly one path, auto-expand
@@ -288,7 +295,8 @@ export class PathTable extends React.Component<PathTableProps, PathTableState> {
288295 ) ;
289296 expansionIndex ++ ;
290297
291- paths . forEach ( path => {
298+ paths . forEach ( ( path , pathIndex ) => {
299+ const pathKey = { resultIndex, pathIndex } ;
292300 const currentPathExpanded = this . state . expanded [ expansionIndex ] ;
293301 if ( currentResultExpanded ) {
294302 const indicator = currentPathExpanded ? octicons . chevronDown : octicons . chevronRight ;
@@ -305,25 +313,27 @@ export class PathTable extends React.Component<PathTableProps, PathTableState> {
305313 expansionIndex ++ ;
306314
307315 if ( currentResultExpanded && currentPathExpanded ) {
308- let pathIndex = 1 ;
309- for ( const step of path . locations ) {
316+ const pathNodes = path . locations ;
317+ for ( let pathNodeIndex = 0 ; pathNodeIndex < pathNodes . length ; ++ pathNodeIndex ) {
318+ const pathNodeKey : Keys . PathNode = { ...pathKey , pathNodeIndex } ;
319+ const step = pathNodes [ pathNodeIndex ] ;
310320 const msg = step . location !== undefined && step . location . message !== undefined ?
311- renderSarifLocationWithText ( step . location . message . text , step . location ) :
321+ renderSarifLocationWithText ( step . location . message . text , step . location , pathNodeKey ) :
312322 '[no location]' ;
313323 const additionalMsg = step . location !== undefined ?
314- renderSarifLocation ( step . location ) :
324+ renderSarifLocation ( step . location , pathNodeKey ) :
315325 '' ;
316-
317- const stepIndex = resultIndex + pathIndex ;
326+ let isSelected = Keys . equalsNotUndefined ( this . state . selectedPathNode , pathNodeKey ) ;
327+ const stepIndex = pathNodeIndex + 1 ; // Convert to 1-based
328+ const zebraIndex = resultIndex + stepIndex ;
318329 rows . push (
319- < tr >
330+ < tr className = { isSelected ? 'vscode-codeql__selected-path-node' : undefined } >
320331 < td className = "vscode-codeql__icon-cell" > < span className = "vscode-codeql__vertical-rule" > </ span > </ td >
321332 < td className = "vscode-codeql__icon-cell" > < span className = "vscode-codeql__vertical-rule" > </ span > </ td >
322- < td { ...zebraStripe ( stepIndex , 'vscode-codeql__path-index-cell' ) } > { pathIndex } </ td >
323- < td { ...zebraStripe ( stepIndex ) } > { msg } </ td >
324- < td { ...zebraStripe ( stepIndex , 'vscode-codeql__location-cell' ) } > { additionalMsg } </ td >
333+ < td { ...selectableZebraStripe ( isSelected , zebraIndex , 'vscode-codeql__path-index-cell' ) } > { stepIndex } </ td >
334+ < td { ...selectableZebraStripe ( isSelected , zebraIndex ) } > { msg } </ td >
335+ < td { ...selectableZebraStripe ( isSelected , zebraIndex , 'vscode-codeql__location-cell' ) } > { additionalMsg } </ td >
325336 </ tr > ) ;
326- pathIndex ++ ;
327337 }
328338 }
329339 } ) ;
0 commit comments