11import * as fs from 'fs-extra' ;
22import * as yaml from 'js-yaml' ;
33import * as path from 'path' ;
4- import { CancellationToken , ExtensionContext , window as Window , workspace , Uri } from 'vscode' ;
4+ import {
5+ CancellationToken ,
6+ ExtensionContext ,
7+ window as Window ,
8+ workspace ,
9+ Uri
10+ } from 'vscode' ;
511import { ErrorCodes , ResponseError } from 'vscode-languageclient' ;
612import { CodeQLCliServer } from './cli' ;
713import { DatabaseUI } from './databases-ui' ;
8- import { logger } from './logging' ;
914import {
1015 getInitialQueryContents ,
1116 getPrimaryDbscheme ,
1217 getQlPackForDbscheme ,
13- showAndLogErrorMessage ,
1418 showBinaryChoiceDialog ,
1519} from './helpers' ;
1620import {
@@ -21,23 +25,35 @@ import {
2125const QUICK_QUERIES_DIR_NAME = 'quick-queries' ;
2226const QUICK_QUERY_QUERY_NAME = 'quick-query.ql' ;
2327const QUICK_QUERY_WORKSPACE_FOLDER_NAME = 'Quick Queries' ;
28+ const QLPACK_FILE_HEADER = '# This is an automatically generated file.\n\n' ;
2429
2530export function isQuickQueryPath ( queryPath : string ) : boolean {
2631 return path . basename ( queryPath ) === QUICK_QUERY_QUERY_NAME ;
2732}
2833
29- function getQuickQueriesDir ( ctx : ExtensionContext ) : string {
34+ async function getQuickQueriesDir ( ctx : ExtensionContext ) : Promise < string > {
3035 const storagePath = ctx . storagePath ;
3136 if ( storagePath === undefined ) {
3237 throw new Error ( 'Workspace storage path is undefined' ) ;
3338 }
3439 const queriesPath = path . join ( storagePath , QUICK_QUERIES_DIR_NAME ) ;
35- fs . ensureDir ( queriesPath , { mode : 0o700 } ) ;
40+ await fs . ensureDir ( queriesPath , { mode : 0o700 } ) ;
3641 return queriesPath ;
3742}
3843
44+ function updateQuickQueryDir ( queriesDir : string , index : number , len : number ) {
45+ workspace . updateWorkspaceFolders (
46+ index ,
47+ len ,
48+ { uri : Uri . file ( queriesDir ) , name : QUICK_QUERY_WORKSPACE_FOLDER_NAME }
49+ ) ;
50+ }
3951
40-
52+ function findExistingQuickQueryEditor ( ) {
53+ return Window . visibleTextEditors . find ( editor =>
54+ path . basename ( editor . document . uri . fsPath ) === QUICK_QUERY_QUERY_NAME
55+ ) ;
56+ }
4157
4258/**
4359 * Show a buffer the user can enter a simple query into.
@@ -50,26 +66,18 @@ export async function displayQuickQuery(
5066 token : CancellationToken
5167) {
5268
53- function updateQuickQueryDir ( queriesDir : string , index : number , len : number ) {
54- workspace . updateWorkspaceFolders (
55- index ,
56- len ,
57- { uri : Uri . file ( queriesDir ) , name : QUICK_QUERY_WORKSPACE_FOLDER_NAME }
58- ) ;
59- }
60-
6169 try {
62- const workspaceFolders = workspace . workspaceFolders || [ ] ;
63- const queriesDir = await getQuickQueriesDir ( ctx ) ;
64-
6570 // If there is already a quick query open, don't clobber it, just
6671 // show it.
67- const existing = workspace . textDocuments . find ( doc => path . basename ( doc . uri . fsPath ) === QUICK_QUERY_QUERY_NAME ) ;
68- if ( existing !== undefined ) {
69- Window . showTextDocument ( existing ) ;
72+ const existing = findExistingQuickQueryEditor ( ) ;
73+ if ( existing ) {
74+ await Window . showTextDocument ( existing . document ) ;
7075 return ;
7176 }
7277
78+ const workspaceFolders = workspace . workspaceFolders || [ ] ;
79+ const queriesDir = await getQuickQueriesDir ( ctx ) ;
80+
7381 // We need to have a multi-root workspace to make quick query work
7482 // at all. Changing the workspace from single-root to multi-root
7583 // causes a restart of the whole extension host environment, so we
@@ -88,10 +96,11 @@ export async function displayQuickQuery(
8896 }
8997
9098 const index = workspaceFolders . findIndex ( folder => folder . name === QUICK_QUERY_WORKSPACE_FOLDER_NAME ) ;
91- if ( index === - 1 )
99+ if ( index === - 1 ) {
92100 updateQuickQueryDir ( queriesDir , workspaceFolders . length , 0 ) ;
93- else
101+ } else {
94102 updateQuickQueryDir ( queriesDir , index , 1 ) ;
103+ }
95104
96105 // We're going to infer which qlpack to use from the current database
97106 const dbItem = await databaseUI . getDatabaseItem ( progress , token ) ;
@@ -102,31 +111,38 @@ export async function displayQuickQuery(
102111 const datasetFolder = await dbItem . getDatasetFolder ( cliServer ) ;
103112 const dbscheme = await getPrimaryDbscheme ( datasetFolder ) ;
104113 const qlpack = await getQlPackForDbscheme ( cliServer , dbscheme ) ;
105-
106- const quickQueryQlpackYaml : any = {
107- name : 'quick-query' ,
108- version : '1.0.0' ,
109- libraryPathDependencies : [ qlpack ]
110- } ;
111-
112- const qlFile = path . join ( queriesDir , QUICK_QUERY_QUERY_NAME ) ;
113114 const qlPackFile = path . join ( queriesDir , 'qlpack.yml' ) ;
114- await fs . writeFile ( qlFile , getInitialQueryContents ( dbItem . language , dbscheme ) , 'utf8' ) ;
115- await fs . writeFile ( qlPackFile , yaml . safeDump ( quickQueryQlpackYaml ) , 'utf8' ) ;
116- Window . showTextDocument ( await workspace . openTextDocument ( qlFile ) ) ;
117- }
118-
119- // TODO: clean up error handling for top-level commands like this
120- catch ( e ) {
121- if ( e instanceof UserCancellationException ) {
122- logger . log ( e . message ) ;
115+ const qlFile = path . join ( queriesDir , QUICK_QUERY_QUERY_NAME ) ;
116+ const shouldRewrite = await checkShouldRewrite ( qlPackFile , qlpack ) ;
117+
118+ // Only rewrite the qlpack file if the database has changed
119+ if ( shouldRewrite ) {
120+ const quickQueryQlpackYaml : any = {
121+ name : 'quick-query' ,
122+ version : '1.0.0' ,
123+ libraryPathDependencies : [ qlpack ]
124+ } ;
125+ await fs . writeFile ( qlPackFile , QLPACK_FILE_HEADER + yaml . safeDump ( quickQueryQlpackYaml ) , 'utf8' ) ;
123126 }
124- else if ( e instanceof ResponseError && e . code == ErrorCodes . RequestCancelled ) {
125- logger . log ( e . message ) ;
127+
128+ if ( shouldRewrite || ! ( await fs . pathExists ( qlFile ) ) ) {
129+ await fs . writeFile ( qlFile , getInitialQueryContents ( dbItem . language , dbscheme ) , 'utf8' ) ;
126130 }
127- else if ( e instanceof Error )
128- showAndLogErrorMessage ( e . message ) ;
129- else
131+
132+ await Window . showTextDocument ( await workspace . openTextDocument ( qlFile ) ) ;
133+ } catch ( e ) {
134+ if ( e instanceof ResponseError && e . code == ErrorCodes . RequestCancelled ) {
135+ throw new UserCancellationException ( e . message ) ;
136+ } else {
130137 throw e ;
138+ }
139+ }
140+ }
141+
142+ async function checkShouldRewrite ( qlPackFile : string , newDependency : string ) {
143+ if ( ! ( await fs . pathExists ( qlPackFile ) ) ) {
144+ return true ;
131145 }
146+ const qlPackContents : any = yaml . safeLoad ( await fs . readFile ( qlPackFile , 'utf8' ) ) ;
147+ return qlPackContents . libraryPathDependencies ?. [ 0 ] !== newDependency ;
132148}
0 commit comments