Skip to content

Commit 7f3e960

Browse files
committed
Move dialog functions to separate file
1 parent 0cfbf0c commit 7f3e960

File tree

14 files changed

+331
-328
lines changed

14 files changed

+331
-328
lines changed
Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
1+
import { env, Uri, window } from "vscode";
2+
3+
/**
4+
* Opens a modal dialog for the user to make a yes/no choice.
5+
*
6+
* @param message The message to show.
7+
* @param modal If true (the default), show a modal dialog box, otherwise dialog is non-modal and can
8+
* be closed even if the user does not make a choice.
9+
* @param yesTitle The text in the box indicating the affirmative choice.
10+
* @param noTitle The text in the box indicating the negative choice.
11+
*
12+
* @return
13+
* `true` if the user clicks 'Yes',
14+
* `false` if the user clicks 'No' or cancels the dialog,
15+
* `undefined` if the dialog is closed without the user making a choice.
16+
*/
17+
export async function showBinaryChoiceDialog(
18+
message: string,
19+
modal = true,
20+
yesTitle = "Yes",
21+
noTitle = "No",
22+
): Promise<boolean | undefined> {
23+
const yesItem = { title: yesTitle, isCloseAffordance: false };
24+
const noItem = { title: noTitle, isCloseAffordance: true };
25+
const chosenItem = await window.showInformationMessage(
26+
message,
27+
{ modal },
28+
yesItem,
29+
noItem,
30+
);
31+
if (!chosenItem) {
32+
return undefined;
33+
}
34+
return chosenItem?.title === yesItem.title;
35+
}
36+
37+
/**
38+
* Opens a modal dialog for the user to make a yes/no choice.
39+
*
40+
* @param message The message to show.
41+
* @param modal If true (the default), show a modal dialog box, otherwise dialog is non-modal and can
42+
* be closed even if the user does not make a choice.
43+
*
44+
* @return
45+
* `true` if the user clicks 'Yes',
46+
* `false` if the user clicks 'No' or cancels the dialog,
47+
* `undefined` if the dialog is closed without the user making a choice.
48+
*/
49+
export async function showBinaryChoiceWithUrlDialog(
50+
message: string,
51+
url: string,
52+
): Promise<boolean | undefined> {
53+
const urlItem = { title: "More Information", isCloseAffordance: false };
54+
const yesItem = { title: "Yes", isCloseAffordance: false };
55+
const noItem = { title: "No", isCloseAffordance: true };
56+
let chosenItem;
57+
58+
// Keep the dialog open as long as the user is clicking the 'more information' option.
59+
// To prevent an infinite loop, if the user clicks 'more information' 5 times, close the dialog and return cancelled
60+
let count = 0;
61+
do {
62+
chosenItem = await window.showInformationMessage(
63+
message,
64+
{ modal: true },
65+
urlItem,
66+
yesItem,
67+
noItem,
68+
);
69+
if (chosenItem === urlItem) {
70+
await env.openExternal(Uri.parse(url, true));
71+
}
72+
count++;
73+
} while (chosenItem === urlItem && count < 5);
74+
75+
if (!chosenItem || chosenItem.title === urlItem.title) {
76+
return undefined;
77+
}
78+
return chosenItem.title === yesItem.title;
79+
}
80+
81+
/**
82+
* Show an information message with a customisable action.
83+
* @param message The message to show.
84+
* @param actionMessage The call to action message.
85+
*
86+
* @return `true` if the user clicks the action, `false` if the user cancels the dialog.
87+
*/
88+
export async function showInformationMessageWithAction(
89+
message: string,
90+
actionMessage: string,
91+
): Promise<boolean> {
92+
const actionItem = { title: actionMessage, isCloseAffordance: false };
93+
const chosenItem = await window.showInformationMessage(message, actionItem);
94+
return chosenItem === actionItem;
95+
}
96+
97+
/**
98+
* Opens a modal dialog for the user to make a choice between yes/no/never be asked again.
99+
*
100+
* @param message The message to show.
101+
* @param modal If true (the default), show a modal dialog box, otherwise dialog is non-modal and can
102+
* be closed even if the user does not make a choice.
103+
* @param yesTitle The text in the box indicating the affirmative choice.
104+
* @param noTitle The text in the box indicating the negative choice.
105+
* @param neverTitle The text in the box indicating the opt out choice.
106+
*
107+
* @return
108+
* `Yes` if the user clicks 'Yes',
109+
* `No` if the user clicks 'No' or cancels the dialog,
110+
* `No, and never ask me again` if the user clicks 'No, and never ask me again',
111+
* `undefined` if the dialog is closed without the user making a choice.
112+
*/
113+
export async function showNeverAskAgainDialog(
114+
message: string,
115+
modal = true,
116+
yesTitle = "Yes",
117+
noTitle = "No",
118+
neverAskAgainTitle = "No, and never ask me again",
119+
): Promise<string | undefined> {
120+
const yesItem = { title: yesTitle, isCloseAffordance: true };
121+
const noItem = { title: noTitle, isCloseAffordance: false };
122+
const neverAskAgainItem = {
123+
title: neverAskAgainTitle,
124+
isCloseAffordance: false,
125+
};
126+
const chosenItem = await window.showInformationMessage(
127+
message,
128+
{ modal },
129+
yesItem,
130+
noItem,
131+
neverAskAgainItem,
132+
);
133+
134+
return chosenItem?.title;
135+
}

extensions/ql-vscode/src/common/vscode/external-files.ts

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,7 @@
11
import { Uri, window } from "vscode";
22
import { AppCommandManager } from "../commands";
3-
import {
4-
showAndLogExceptionWithTelemetry,
5-
showBinaryChoiceDialog,
6-
} from "../../helpers";
3+
import { showAndLogExceptionWithTelemetry } from "../../helpers";
4+
import { showBinaryChoiceDialog } from "./dialog";
75
import { redactableError } from "../../pure/errors";
86
import {
97
asError,

extensions/ql-vscode/src/databases/local-databases/database-manager.ts

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,8 @@ import {
1313
import { join } from "path";
1414
import { FullDatabaseOptions } from "./database-options";
1515
import { DatabaseItemImpl } from "./database-item-impl";
16-
import {
17-
showAndLogExceptionWithTelemetry,
18-
showNeverAskAgainDialog,
19-
} from "../../helpers";
16+
import { showAndLogExceptionWithTelemetry } from "../../helpers";
17+
import { showNeverAskAgainDialog } from "../../common/vscode/dialog";
2018
import {
2119
getFirstWorkspaceFolder,
2220
isFolderAlreadyInWorkspace,

extensions/ql-vscode/src/extension.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -60,12 +60,14 @@ import {
6060
showAndLogExceptionWithTelemetry,
6161
showAndLogInformationMessage,
6262
showAndLogWarningMessage,
63-
showBinaryChoiceDialog,
64-
showInformationMessageWithAction,
6563
tmpDir,
6664
tmpDirDisposal,
6765
prepareCodeTour,
6866
} from "./helpers";
67+
import {
68+
showBinaryChoiceDialog,
69+
showInformationMessageWithAction,
70+
} from "./common/vscode/dialog";
6971
import {
7072
asError,
7173
assertNever,

extensions/ql-vscode/src/helpers.ts

Lines changed: 2 additions & 135 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { ensureDirSync, pathExists, ensureDir, writeFile } from "fs-extra";
22
import { join } from "path";
33
import { dirSync } from "tmp-promise";
4-
import { Uri, window as Window, workspace, env } from "vscode";
4+
import { Uri, window as Window, workspace } from "vscode";
55
import { CodeQLCliServer } from "./codeql-cli/cli";
66
import { UserCancellationException } from "./common/vscode/progress";
77
import { extLogger, OutputChannelLogger } from "./common";
@@ -12,6 +12,7 @@ import { isQueryLanguage, QueryLanguage } from "./common/query-language";
1212
import { isCodespacesTemplate } from "./config";
1313
import { AppCommandManager } from "./common/commands";
1414
import { getOnDiskWorkspaceFolders } from "./common/vscode/workspace-folders";
15+
import { showBinaryChoiceDialog } from "./common/vscode/dialog";
1516

1617
// Shared temporary folder for the extension.
1718
export const tmpDir = dirSync({
@@ -139,140 +140,6 @@ async function internalShowAndLog(
139140
return result;
140141
}
141142

142-
/**
143-
* Opens a modal dialog for the user to make a yes/no choice.
144-
*
145-
* @param message The message to show.
146-
* @param modal If true (the default), show a modal dialog box, otherwise dialog is non-modal and can
147-
* be closed even if the user does not make a choice.
148-
* @param yesTitle The text in the box indicating the affirmative choice.
149-
* @param noTitle The text in the box indicating the negative choice.
150-
*
151-
* @return
152-
* `true` if the user clicks 'Yes',
153-
* `false` if the user clicks 'No' or cancels the dialog,
154-
* `undefined` if the dialog is closed without the user making a choice.
155-
*/
156-
export async function showBinaryChoiceDialog(
157-
message: string,
158-
modal = true,
159-
yesTitle = "Yes",
160-
noTitle = "No",
161-
): Promise<boolean | undefined> {
162-
const yesItem = { title: yesTitle, isCloseAffordance: false };
163-
const noItem = { title: noTitle, isCloseAffordance: true };
164-
const chosenItem = await Window.showInformationMessage(
165-
message,
166-
{ modal },
167-
yesItem,
168-
noItem,
169-
);
170-
if (!chosenItem) {
171-
return undefined;
172-
}
173-
return chosenItem?.title === yesItem.title;
174-
}
175-
176-
/**
177-
* Opens a modal dialog for the user to make a yes/no choice.
178-
*
179-
* @param message The message to show.
180-
* @param modal If true (the default), show a modal dialog box, otherwise dialog is non-modal and can
181-
* be closed even if the user does not make a choice.
182-
*
183-
* @return
184-
* `true` if the user clicks 'Yes',
185-
* `false` if the user clicks 'No' or cancels the dialog,
186-
* `undefined` if the dialog is closed without the user making a choice.
187-
*/
188-
export async function showBinaryChoiceWithUrlDialog(
189-
message: string,
190-
url: string,
191-
): Promise<boolean | undefined> {
192-
const urlItem = { title: "More Information", isCloseAffordance: false };
193-
const yesItem = { title: "Yes", isCloseAffordance: false };
194-
const noItem = { title: "No", isCloseAffordance: true };
195-
let chosenItem;
196-
197-
// Keep the dialog open as long as the user is clicking the 'more information' option.
198-
// To prevent an infinite loop, if the user clicks 'more information' 5 times, close the dialog and return cancelled
199-
let count = 0;
200-
do {
201-
chosenItem = await Window.showInformationMessage(
202-
message,
203-
{ modal: true },
204-
urlItem,
205-
yesItem,
206-
noItem,
207-
);
208-
if (chosenItem === urlItem) {
209-
await env.openExternal(Uri.parse(url, true));
210-
}
211-
count++;
212-
} while (chosenItem === urlItem && count < 5);
213-
214-
if (!chosenItem || chosenItem.title === urlItem.title) {
215-
return undefined;
216-
}
217-
return chosenItem.title === yesItem.title;
218-
}
219-
220-
/**
221-
* Show an information message with a customisable action.
222-
* @param message The message to show.
223-
* @param actionMessage The call to action message.
224-
*
225-
* @return `true` if the user clicks the action, `false` if the user cancels the dialog.
226-
*/
227-
export async function showInformationMessageWithAction(
228-
message: string,
229-
actionMessage: string,
230-
): Promise<boolean> {
231-
const actionItem = { title: actionMessage, isCloseAffordance: false };
232-
const chosenItem = await Window.showInformationMessage(message, actionItem);
233-
return chosenItem === actionItem;
234-
}
235-
236-
/**
237-
* Opens a modal dialog for the user to make a choice between yes/no/never be asked again.
238-
*
239-
* @param message The message to show.
240-
* @param modal If true (the default), show a modal dialog box, otherwise dialog is non-modal and can
241-
* be closed even if the user does not make a choice.
242-
* @param yesTitle The text in the box indicating the affirmative choice.
243-
* @param noTitle The text in the box indicating the negative choice.
244-
* @param neverTitle The text in the box indicating the opt out choice.
245-
*
246-
* @return
247-
* `Yes` if the user clicks 'Yes',
248-
* `No` if the user clicks 'No' or cancels the dialog,
249-
* `No, and never ask me again` if the user clicks 'No, and never ask me again',
250-
* `undefined` if the dialog is closed without the user making a choice.
251-
*/
252-
export async function showNeverAskAgainDialog(
253-
message: string,
254-
modal = true,
255-
yesTitle = "Yes",
256-
noTitle = "No",
257-
neverAskAgainTitle = "No, and never ask me again",
258-
): Promise<string | undefined> {
259-
const yesItem = { title: yesTitle, isCloseAffordance: true };
260-
const noItem = { title: noTitle, isCloseAffordance: false };
261-
const neverAskAgainItem = {
262-
title: neverAskAgainTitle,
263-
isCloseAffordance: false,
264-
};
265-
const chosenItem = await Window.showInformationMessage(
266-
message,
267-
{ modal },
268-
yesItem,
269-
noItem,
270-
neverAskAgainItem,
271-
);
272-
273-
return chosenItem?.title;
274-
}
275-
276143
/** Check if the current workspace is the CodeTour and open the workspace folder.
277144
* Without this, we can't run the code tour correctly.
278145
**/

extensions/ql-vscode/src/local-queries/local-queries.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,8 @@ import {
2121
findLanguage,
2222
showAndLogErrorMessage,
2323
showAndLogWarningMessage,
24-
showBinaryChoiceDialog,
2524
} from "../helpers";
25+
import { showBinaryChoiceDialog } from "../common/vscode/dialog";
2626
import { getOnDiskWorkspaceFolders } from "../common/vscode/workspace-folders";
2727
import { displayQuickQuery } from "./quick-query";
2828
import { CoreCompletedQuery, QueryRunner } from "../query-server";

extensions/ql-vscode/src/local-queries/quick-query.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import { CancellationToken, window as Window, workspace, Uri } from "vscode";
55
import { LSPErrorCodes, ResponseError } from "vscode-languageclient";
66
import { CodeQLCliServer } from "../codeql-cli/cli";
77
import { DatabaseUI } from "../databases/local-databases-ui";
8-
import { showBinaryChoiceDialog } from "../helpers";
8+
import { showBinaryChoiceDialog } from "../common/vscode/dialog";
99
import { getInitialQueryContents } from "./query-contents";
1010
import { getPrimaryDbscheme, getQlPackForDbscheme } from "../databases/qlpack";
1111
import {

extensions/ql-vscode/src/query-history/query-history-manager.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,11 @@ import {
1717
showAndLogErrorMessage,
1818
showAndLogInformationMessage,
1919
showAndLogWarningMessage,
20+
} from "../helpers";
21+
import {
2022
showBinaryChoiceDialog,
2123
showInformationMessageWithAction,
22-
} from "../helpers";
24+
} from "../common/vscode/dialog";
2325
import { extLogger } from "../common";
2426
import { URLSearchParams } from "url";
2527
import { DisposableObject } from "../pure/disposable-object";

extensions/ql-vscode/src/telemetry.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ import {
1717
import * as appInsights from "applicationinsights";
1818
import { extLogger } from "./common";
1919
import { UserCancellationException } from "./common/vscode/progress";
20-
import { showBinaryChoiceWithUrlDialog } from "./helpers";
20+
import { showBinaryChoiceWithUrlDialog } from "./common/vscode/dialog";
2121
import { RedactableError } from "./pure/errors";
2222
import { SemVer } from "semver";
2323

extensions/ql-vscode/src/variant-analysis/export-results.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import {
77
UserCancellationException,
88
withProgress,
99
} from "../common/vscode/progress";
10-
import { showInformationMessageWithAction } from "../helpers";
10+
import { showInformationMessageWithAction } from "../common/vscode/dialog";
1111
import { extLogger } from "../common";
1212
import { createGist } from "./gh-api/gh-api-client";
1313
import {

0 commit comments

Comments
 (0)