11import { DisposableObject } from "semmle-vscode-utils" ;
2- import { WebviewPanel , ExtensionContext , window as Window , ViewColumn , Uri } from "vscode" ;
3- import * as path from 'path' ;
2+ import {
3+ WebviewPanel ,
4+ ExtensionContext ,
5+ window as Window ,
6+ ViewColumn ,
7+ Uri ,
8+ } from "vscode" ;
9+ import * as path from "path" ;
410
511import { tmpDir } from "../run-queries" ;
612import { CompletedQuery } from "../query-results" ;
7- import { CompareViewMessage } from "../interface-types" ;
13+ import {
14+ FromCompareViewMessage ,
15+ ToCompareViewMessage ,
16+ QueryCompareResult ,
17+ } from "../interface-types" ;
818import { Logger } from "../logging" ;
919import { CodeQLCliServer } from "../cli" ;
1020import { DatabaseManager } from "../databases" ;
11- import { getHtmlForWebview , WebviewReveal } from "../webview-utils" ;
12- import { showAndLogErrorMessage } from "../helpers" ;
21+ import {
22+ getHtmlForWebview ,
23+ jumpToLocation ,
24+ } from "../webview-utils" ;
25+ import { adaptSchema , adaptBqrs , RawResultSet } from "../adapt" ;
26+ import { BQRSInfo } from "../bqrs-cli-types" ;
27+ import resultsDiff from "./resultsDiff" ;
1328
1429interface ComparePair {
1530 from : CompletedQuery ;
@@ -19,6 +34,8 @@ interface ComparePair {
1934export class CompareInterfaceManager extends DisposableObject {
2035 private comparePair : ComparePair | undefined ;
2136 private panel : WebviewPanel | undefined ;
37+ private panelLoaded = false ;
38+ private panelLoadedCallBacks : ( ( ) => void ) [ ] = [ ] ;
2239
2340 constructor (
2441 public ctx : ExtensionContext ,
@@ -29,20 +46,59 @@ export class CompareInterfaceManager extends DisposableObject {
2946 super ( ) ;
3047 }
3148
32- showResults ( from : CompletedQuery , to : CompletedQuery , forceReveal = WebviewReveal . NotForced ) {
49+ async showResults (
50+ from : CompletedQuery ,
51+ to : CompletedQuery ,
52+ selectedResultSetName ?: string
53+ ) {
3354 this . comparePair = { from, to } ;
34- if ( forceReveal === WebviewReveal . Forced ) {
35- this . getPanel ( ) . reveal ( undefined , true ) ;
55+ this . getPanel ( ) . reveal ( undefined , true ) ;
56+
57+ await this . waitForPanelLoaded ( ) ;
58+ const [
59+ commonResultSetNames ,
60+ currentResultSetName ,
61+ fromResultSet ,
62+ toResultSet ,
63+ ] = await this . findCommonResultSetNames ( from , to , selectedResultSetName ) ;
64+ if ( currentResultSetName ) {
65+ await this . postMessage ( {
66+ t : "setComparisons" ,
67+ stats : {
68+ fromQuery : {
69+ // since we split the description into several rows
70+ // only run interpolation if the label is user-defined
71+ // otherwise we will wind up with duplicated rows
72+ name : from . options . label
73+ ? from . interpolate ( from . getLabel ( ) )
74+ : from . queryName ,
75+ status : from . statusString ,
76+ time : from . time ,
77+ } ,
78+ toQuery : {
79+ name : to . options . label
80+ ? to . interpolate ( from . getLabel ( ) )
81+ : to . queryName ,
82+ status : to . statusString ,
83+ time : to . time ,
84+ } ,
85+ } ,
86+ columns : fromResultSet . schema . columns ,
87+ commonResultSetNames,
88+ currentResultSetName : currentResultSetName ,
89+ rows : this . compareResults ( fromResultSet , toResultSet ) ,
90+ datebaseUri : to . database . databaseUri ,
91+ } ) ;
3692 }
3793 }
3894
3995 getPanel ( ) : WebviewPanel {
4096 if ( this . panel == undefined ) {
4197 const { ctx } = this ;
4298 const panel = ( this . panel = Window . createWebviewPanel (
43- "compareView" , // internal name
44- "Compare CodeQL Query Results" , // user-visible name
45- { viewColumn : ViewColumn . Beside , preserveFocus : true } ,
99+ "compareView" ,
100+ "Compare CodeQL Query Results" ,
101+ { viewColumn : ViewColumn . Active , preserveFocus : true } ,
46102 {
47103 enableScripts : true ,
48104 enableFindWidget : true ,
@@ -54,7 +110,10 @@ export class CompareInterfaceManager extends DisposableObject {
54110 }
55111 ) ) ;
56112 this . panel . onDidDispose (
57- ( ) => this . panel = undefined ,
113+ ( ) => {
114+ this . panel = undefined ;
115+ this . comparePair = undefined ;
116+ } ,
58117 null ,
59118 ctx . subscriptions
60119 ) ;
@@ -64,10 +123,14 @@ export class CompareInterfaceManager extends DisposableObject {
64123 ) ;
65124
66125 const stylesheetPathOnDisk = Uri . file (
67- ctx . asAbsolutePath ( "out/compareView .css" )
126+ ctx . asAbsolutePath ( "out/resultsView .css" )
68127 ) ;
69128
70- panel . webview . html = getHtmlForWebview ( panel . webview , scriptPathOnDisk , stylesheetPathOnDisk ) ;
129+ panel . webview . html = getHtmlForWebview (
130+ panel . webview ,
131+ scriptPathOnDisk ,
132+ stylesheetPathOnDisk
133+ ) ;
71134 panel . webview . onDidReceiveMessage (
72135 async ( e ) => this . handleMsgFromView ( e ) ,
73136 undefined ,
@@ -77,9 +140,103 @@ export class CompareInterfaceManager extends DisposableObject {
77140 return this . panel ;
78141 }
79142
80- private async handleMsgFromView ( msg : CompareViewMessage ) : Promise < void > {
81- /** TODO */
82- showAndLogErrorMessage ( JSON . stringify ( msg ) ) ;
83- showAndLogErrorMessage ( JSON . stringify ( this . comparePair ) ) ;
143+ private waitForPanelLoaded ( ) : Promise < void > {
144+ return new Promise ( ( resolve ) => {
145+ if ( this . panelLoaded ) {
146+ resolve ( ) ;
147+ } else {
148+ this . panelLoadedCallBacks . push ( resolve ) ;
149+ }
150+ } ) ;
151+ }
152+
153+ private async handleMsgFromView ( msg : FromCompareViewMessage ) : Promise < void > {
154+ switch ( msg . t ) {
155+ case "compareViewLoaded" :
156+ this . panelLoaded = true ;
157+ this . panelLoadedCallBacks . forEach ( ( cb ) => cb ( ) ) ;
158+ this . panelLoadedCallBacks = [ ] ;
159+ break ;
160+
161+ case "changeCompare" :
162+ this . changeTable ( msg . newResultSetName ) ;
163+ break ;
164+
165+ case "viewSourceFile" :
166+ await jumpToLocation ( msg , this . databaseManager , this . logger ) ;
167+ break ;
168+ }
169+ }
170+
171+ private postMessage ( msg : ToCompareViewMessage ) : Thenable < boolean > {
172+ return this . getPanel ( ) . webview . postMessage ( msg ) ;
173+ }
174+
175+ private async findCommonResultSetNames (
176+ from : CompletedQuery ,
177+ to : CompletedQuery ,
178+ selectedResultSetName : string | undefined
179+ ) : Promise < [ string [ ] , string , RawResultSet , RawResultSet ] > {
180+ const fromSchemas = await this . cliServer . bqrsInfo (
181+ from . query . resultsPaths . resultsPath
182+ ) ;
183+ const toSchemas = await this . cliServer . bqrsInfo (
184+ to . query . resultsPaths . resultsPath
185+ ) ;
186+ const fromSchemaNames = fromSchemas [ "result-sets" ] . map (
187+ ( schema ) => schema . name
188+ ) ;
189+ const toSchemaNames = toSchemas [ "result-sets" ] . map ( ( schema ) => schema . name ) ;
190+ const commonResultSetNames = fromSchemaNames . filter ( ( name ) =>
191+ toSchemaNames . includes ( name )
192+ ) ;
193+ const currentResultSetName = selectedResultSetName || commonResultSetNames [ 0 ] ;
194+ const fromResultSet = await this . getResultSet (
195+ fromSchemas ,
196+ currentResultSetName ,
197+ from . query . resultsPaths . resultsPath
198+ ) ;
199+ const toResultSet = await this . getResultSet (
200+ toSchemas ,
201+ currentResultSetName ,
202+ to . query . resultsPaths . resultsPath
203+ ) ;
204+ return [
205+ commonResultSetNames ,
206+ currentResultSetName ,
207+ fromResultSet ,
208+ toResultSet ,
209+ ] ;
210+ }
211+
212+ private async changeTable ( newResultSetName : string ) {
213+ if ( ! this . comparePair ?. from || ! this . comparePair . to ) {
214+ return ;
215+ }
216+ await this . showResults ( this . comparePair . from , this . comparePair . to , newResultSetName ) ;
217+ }
218+
219+ private async getResultSet (
220+ bqrsInfo : BQRSInfo ,
221+ resultSetName : string ,
222+ resultsPath : string
223+ ) : Promise < RawResultSet > {
224+ const schema = bqrsInfo [ "result-sets" ] . find (
225+ ( schema ) => schema . name === resultSetName
226+ ) ;
227+ if ( ! schema ) {
228+ throw new Error ( `Schema ${ resultSetName } not found.` ) ;
229+ }
230+ const chunk = await this . cliServer . bqrsDecode ( resultsPath , resultSetName ) ;
231+ const adaptedSchema = adaptSchema ( schema ) ;
232+ return adaptBqrs ( adaptedSchema , chunk ) ;
233+ }
234+
235+ private compareResults (
236+ fromResults : RawResultSet ,
237+ toResults : RawResultSet
238+ ) : QueryCompareResult {
239+ // Only compare columns that have the same name
240+ return resultsDiff ( fromResults , toResults ) ;
84241 }
85242}
0 commit comments