@@ -3,15 +3,14 @@ import * as legacyMessages from "./pure/legacy-messages";
33import { DatabaseInfo , QueryMetadata } from "./common/interface-types" ;
44import { join , parse , dirname , basename } from "path" ;
55import {
6- ConfigurationTarget ,
76 Range ,
87 TextDocument ,
98 TextEditor ,
109 Uri ,
1110 window ,
11+ workspace ,
1212} from "vscode" ;
13- import { isCanary , AUTOSAVE_SETTING } from "./config" ;
14- import { UserCancellationException } from "./common/vscode/progress" ;
13+ import { isCanary , VSCODE_SAVE_BEFORE_START_SETTING } from "./config" ;
1514import {
1615 pathExists ,
1716 readFile ,
@@ -491,55 +490,78 @@ async function getSelectedPosition(
491490 } ;
492491}
493492
493+ type SaveBeforeStartMode =
494+ | "nonUntitledEditorsInActiveGroup"
495+ | "allEditorsInActiveGroup"
496+ | "none" ;
497+
494498/**
495- * Prompts the user to save `document` if it has unsaved changes.
496- *
497- * @param document The document to save.
498- *
499- * @returns true if we should save changes and false if we should continue without saving changes.
500- * @throws UserCancellationException if we should abort whatever operation triggered this prompt
499+ * Saves dirty files before running queries, based on the user's settings.
501500 */
502- export async function promptUserToSaveChanges (
503- document : TextDocument ,
504- ) : Promise < boolean > {
505- if ( document . isDirty ) {
506- if ( AUTOSAVE_SETTING . getValue ( ) ) {
507- return true ;
508- } else {
509- const yesItem = { title : "Yes" , isCloseAffordance : false } ;
510- const alwaysItem = { title : "Always Save" , isCloseAffordance : false } ;
511- const noItem = {
512- title : "No (run version on disk)" ,
513- isCloseAffordance : false ,
514- } ;
515- const cancelItem = { title : "Cancel" , isCloseAffordance : true } ;
516- const message = `Query file '${ basename (
517- document . uri . fsPath ,
518- ) } ' has unsaved changes. Save now?`;
519- const chosenItem = await window . showInformationMessage (
520- message ,
521- { modal : true } ,
522- yesItem ,
523- alwaysItem ,
524- noItem ,
525- cancelItem ,
526- ) ;
501+ export async function saveBeforeStart ( ) : Promise < void > {
502+ const mode : SaveBeforeStartMode =
503+ ( VSCODE_SAVE_BEFORE_START_SETTING . getValue < string > ( ) as SaveBeforeStartMode ) ??
504+ "nonUntitledEditorsInActiveGroup" ;
527505
528- if ( chosenItem === alwaysItem ) {
529- await AUTOSAVE_SETTING . updateValue ( true , ConfigurationTarget . Workspace ) ;
530- return true ;
531- }
506+ switch ( mode ) {
507+ case "nonUntitledEditorsInActiveGroup" :
508+ await saveAllInGroup ( false ) ;
509+ break ;
532510
533- if ( chosenItem === yesItem ) {
534- return true ;
535- }
511+ case "allEditorsInActiveGroup" :
512+ await saveAllInGroup ( true ) ;
513+ break ;
536514
537- if ( chosenItem === cancelItem ) {
538- throw new UserCancellationException ( "Query run cancelled." , true ) ;
515+ case "none" :
516+ break ;
517+
518+ default :
519+ // Unexpected value. Fall back to the default behavior.
520+ await saveAllInGroup ( false ) ;
521+ break ;
522+ }
523+ }
524+
525+ // Used in tests
526+ export async function saveAllInGroup ( includeUntitled : boolean ) : Promise < void > {
527+ // There's no good way to get from a `Tab` to a `TextDocument`, so we'll collect all of the dirty
528+ // documents indexed by their URI, and then compare those URIs against the URIs of the tabs.
529+ const dirtyDocumentUris = new Map < string , TextDocument > ( ) ;
530+ for ( const openDocument of workspace . textDocuments ) {
531+ if ( openDocument . isDirty ) {
532+ console . warn ( `${ openDocument . uri . toString ( ) } is dirty.` ) ;
533+ if ( ! openDocument . isUntitled || includeUntitled ) {
534+ dirtyDocumentUris . set ( openDocument . uri . toString ( ) , openDocument ) ;
535+ }
536+ }
537+ }
538+ if ( dirtyDocumentUris . size > 0 ) {
539+ console . warn ( `${ window . tabGroups . all . length } tab groups open` ) ;
540+ console . warn ( `${ workspace . textDocuments . length } documents open` ) ;
541+ const tabGroup = window . tabGroups . activeTabGroup ;
542+ console . warn ( `${ tabGroup . tabs . length } tabs open in active group` ) ;
543+ for ( const tab of tabGroup . tabs ) {
544+ const input = tab . input ;
545+ // The `input` property can be of an arbitrary type, depending on the underlying tab type. For
546+ // text editors (and potentially others), it's an object with a `uri` property. That's all we
547+ // need to know to match it up with a dirty document.
548+ if ( typeof input === "object" ) {
549+ const uri = ( input as any ) . uri ;
550+ if ( uri instanceof Uri ) {
551+ const document = dirtyDocumentUris . get ( uri . toString ( ) ) ;
552+ if ( document !== undefined ) {
553+ console . warn ( `Saving ${ uri . toString ( ) } ` ) ;
554+ await document . save ( ) ;
555+ // Remove the URI from the dirty list so we don't wind up saving the same file twice
556+ // if it's open in multiple editors.
557+ dirtyDocumentUris . delete ( uri . toString ( ) ) ;
558+ } else {
559+ console . warn ( `Can't find ${ uri . toString ( ) } ` ) ;
560+ }
561+ }
539562 }
540563 }
541564 }
542- return false ;
543565}
544566
545567/**
0 commit comments