1- import { join } from "path" ;
2- import { Uri , workspace , window as Window } from "vscode" ;
1+ import { basename , dirname , join } from "path" ;
2+ import { Uri , window as Window , workspace } from "vscode" ;
33import { CodeQLCliServer } from "../codeql-cli/cli" ;
44import { BaseLogger } from "../common/logging" ;
55import { Credentials } from "../common/authentication" ;
66import { QueryLanguage } from "../common/query-language" ;
7- import {
8- getFirstWorkspaceFolder ,
9- isFolderAlreadyInWorkspace ,
10- } from "../common/vscode/workspace-folders" ;
7+ import { getFirstWorkspaceFolder } from "../common/vscode/workspace-folders" ;
118import { getErrorMessage } from "../common/helpers-pure" ;
129import { QlPackGenerator } from "./qlpack-generator" ;
1310import { DatabaseItem , DatabaseManager } from "../databases/local-databases" ;
@@ -24,8 +21,9 @@ import {
2421 isCodespacesTemplate ,
2522 setQlPackLocation ,
2623} from "../config" ;
27- import { existsSync } from "fs-extra" ;
24+ import { lstat , pathExists } from "fs-extra" ;
2825import { askForLanguage } from "../codeql-cli/query-language" ;
26+ import { QueryTreeViewItem } from "../queries-panel/query-tree-view-item" ;
2927
3028type QueryLanguagesToDatabaseMap = Record < string , string > ;
3129
@@ -51,6 +49,7 @@ export class SkeletonQueryWizard {
5149 private readonly logger : BaseLogger ,
5250 private readonly databaseManager : DatabaseManager ,
5351 private readonly databaseStoragePath : string | undefined ,
52+ private readonly selectedItems : readonly QueryTreeViewItem [ ] ,
5453 private language : QueryLanguage | undefined = undefined ,
5554 ) { }
5655
@@ -70,9 +69,9 @@ export class SkeletonQueryWizard {
7069
7170 this . qlPackStoragePath = await this . determineStoragePath ( ) ;
7271
73- const skeletonPackAlreadyExists =
74- existsSync ( join ( this . qlPackStoragePath , this . folderName ) ) ||
75- isFolderAlreadyInWorkspace ( this . folderName ) ;
72+ const skeletonPackAlreadyExists = await pathExists (
73+ join ( this . qlPackStoragePath , this . folderName ) ,
74+ ) ;
7675
7776 if ( skeletonPackAlreadyExists ) {
7877 // just create a new example query file in skeleton QL pack
@@ -109,7 +108,41 @@ export class SkeletonQueryWizard {
109108 } ) ;
110109 }
111110
112- public async determineStoragePath ( ) {
111+ public async determineStoragePath ( ) : Promise < string > {
112+ if ( this . selectedItems . length === 0 ) {
113+ return this . determineRootStoragePath ( ) ;
114+ }
115+
116+ const storagePath = await this . determineStoragePathFromSelection ( ) ;
117+
118+ // If the user has selected a folder or file within a folder that matches the current
119+ // folder name, we should create a query rather than a query pack
120+ if ( basename ( storagePath ) === this . folderName ) {
121+ return dirname ( storagePath ) ;
122+ }
123+
124+ return storagePath ;
125+ }
126+
127+ private async determineStoragePathFromSelection ( ) : Promise < string > {
128+ // Just like VS Code's "New File" command, if the user has selected multiple files/folders in the queries panel,
129+ // we will create the new file in the same folder as the first selected item.
130+ // See https://github.com/microsoft/vscode/blob/a8b7239d0311d4915b57c837972baf4b01394491/src/vs/workbench/contrib/files/browser/fileActions.ts#L893-L900
131+ const selectedItem = this . selectedItems [ 0 ] ;
132+
133+ const path = selectedItem . path ;
134+
135+ // We use stat to protect against outdated query tree items
136+ const fileStat = await lstat ( path ) ;
137+
138+ if ( fileStat . isDirectory ( ) ) {
139+ return path ;
140+ }
141+
142+ return dirname ( path ) ;
143+ }
144+
145+ public async determineRootStoragePath ( ) {
113146 const firstStorageFolder = getFirstWorkspaceFolder ( ) ;
114147
115148 if ( isCodespacesTemplate ( ) ) {
@@ -118,7 +151,7 @@ export class SkeletonQueryWizard {
118151
119152 let storageFolder = getQlPackLocation ( ) ;
120153
121- if ( storageFolder === undefined || ! existsSync ( storageFolder ) ) {
154+ if ( storageFolder === undefined || ! ( await pathExists ( storageFolder ) ) ) {
122155 storageFolder = await Window . showInputBox ( {
123156 title :
124157 "Please choose a folder in which to create your new query pack. You can change this in the extension settings." ,
@@ -131,7 +164,7 @@ export class SkeletonQueryWizard {
131164 throw new UserCancellationException ( "No storage folder entered." ) ;
132165 }
133166
134- if ( ! existsSync ( storageFolder ) ) {
167+ if ( ! ( await pathExists ( storageFolder ) ) ) {
135168 throw new UserCancellationException (
136169 "Invalid folder. Must be a folder that already exists." ,
137170 ) ;
@@ -208,7 +241,7 @@ export class SkeletonQueryWizard {
208241 await qlPackGenerator . createExampleQlFile ( this . fileName ) ;
209242 } catch ( e : unknown ) {
210243 void this . logger . log (
211- `Could not create skeleton QL pack : ${ getErrorMessage ( e ) } ` ,
244+ `Could not create query example file : ${ getErrorMessage ( e ) } ` ,
212245 ) ;
213246 }
214247 }
0 commit comments