How to implement save-to-file-check-overwrite in a vscode extension?
![Multi tool use Multi tool use](http://sgv.ssvwv.com/sg/ssvwvcomimagb.png)
Multi tool use
I have to implement a very typical programming pattern in a Visual Studio Code extension: save something to a file, but check if the target file exists before doing so and ask the user if it is ok to overwrite it, if it does.
Usually I would just open a file save dialog to ask the user to give me a file name and this dialog would do all necessary checks and gets the user confirmation, if necessary. In vscode however we don't have a file save dialog (however there is a feature request for it). So I'm trying to implement this with the limited means we have there. Fortunately a few weeks ago a new option parameter was added to message dialogs to allow making them modal. But somehow I can't get the timing right. Here's my code:
window.showInputBox({
placeHolder: "<Enter full file name here>",
prompt: "Enter the name to an html file to save the diagramn" }
).then((value: string) => {
if (value) {
let canWrite = true;
if (fs.existsSync(value)) {
canWrite = false;
window.showWarningMessage("The specified file exists already", { modal: true }, ...[ "Overwrite" ]).then((action: string) => {
if (action === "Overwrite") {
canWrite = true;
}
});
}
if (canWrite) {
var stream = fs.createWriteStream(value, { encoding: "utf-8", autoClose: true });
stream.on("error", function (error: any) {
window.showErrorMessage("Could not write to file '" + value + "'. " + error);
});
stream.once('open', function (fd) {
stream.write(text);
stream.end();
window.showInformationMessage("Diagram successfully written to file '" + value + "'.");
});
}
}
})
The problem is that the call to window.showWarningMessage
is non-blocking, that means while the dialog (which itself is modal) opens the code after if (canWrite)
is already executed. This is not a big issue since canWrite
is false
in this moment, however, once the showWarningMessage
thenable returns no code is executed anymore in the outer thenable (from showInputBox
), i.e. the if (canWrite)
is not executed again (as I would expect). Is it impossible to nest 2 thenables or what else am I doing wrong?
How would an experienced typescript/vscode develper approach this task?
typescript visual-studio-code vscode-extensions
add a comment |
I have to implement a very typical programming pattern in a Visual Studio Code extension: save something to a file, but check if the target file exists before doing so and ask the user if it is ok to overwrite it, if it does.
Usually I would just open a file save dialog to ask the user to give me a file name and this dialog would do all necessary checks and gets the user confirmation, if necessary. In vscode however we don't have a file save dialog (however there is a feature request for it). So I'm trying to implement this with the limited means we have there. Fortunately a few weeks ago a new option parameter was added to message dialogs to allow making them modal. But somehow I can't get the timing right. Here's my code:
window.showInputBox({
placeHolder: "<Enter full file name here>",
prompt: "Enter the name to an html file to save the diagramn" }
).then((value: string) => {
if (value) {
let canWrite = true;
if (fs.existsSync(value)) {
canWrite = false;
window.showWarningMessage("The specified file exists already", { modal: true }, ...[ "Overwrite" ]).then((action: string) => {
if (action === "Overwrite") {
canWrite = true;
}
});
}
if (canWrite) {
var stream = fs.createWriteStream(value, { encoding: "utf-8", autoClose: true });
stream.on("error", function (error: any) {
window.showErrorMessage("Could not write to file '" + value + "'. " + error);
});
stream.once('open', function (fd) {
stream.write(text);
stream.end();
window.showInformationMessage("Diagram successfully written to file '" + value + "'.");
});
}
}
})
The problem is that the call to window.showWarningMessage
is non-blocking, that means while the dialog (which itself is modal) opens the code after if (canWrite)
is already executed. This is not a big issue since canWrite
is false
in this moment, however, once the showWarningMessage
thenable returns no code is executed anymore in the outer thenable (from showInputBox
), i.e. the if (canWrite)
is not executed again (as I would expect). Is it impossible to nest 2 thenables or what else am I doing wrong?
How would an experienced typescript/vscode develper approach this task?
typescript visual-studio-code vscode-extensions
add a comment |
I have to implement a very typical programming pattern in a Visual Studio Code extension: save something to a file, but check if the target file exists before doing so and ask the user if it is ok to overwrite it, if it does.
Usually I would just open a file save dialog to ask the user to give me a file name and this dialog would do all necessary checks and gets the user confirmation, if necessary. In vscode however we don't have a file save dialog (however there is a feature request for it). So I'm trying to implement this with the limited means we have there. Fortunately a few weeks ago a new option parameter was added to message dialogs to allow making them modal. But somehow I can't get the timing right. Here's my code:
window.showInputBox({
placeHolder: "<Enter full file name here>",
prompt: "Enter the name to an html file to save the diagramn" }
).then((value: string) => {
if (value) {
let canWrite = true;
if (fs.existsSync(value)) {
canWrite = false;
window.showWarningMessage("The specified file exists already", { modal: true }, ...[ "Overwrite" ]).then((action: string) => {
if (action === "Overwrite") {
canWrite = true;
}
});
}
if (canWrite) {
var stream = fs.createWriteStream(value, { encoding: "utf-8", autoClose: true });
stream.on("error", function (error: any) {
window.showErrorMessage("Could not write to file '" + value + "'. " + error);
});
stream.once('open', function (fd) {
stream.write(text);
stream.end();
window.showInformationMessage("Diagram successfully written to file '" + value + "'.");
});
}
}
})
The problem is that the call to window.showWarningMessage
is non-blocking, that means while the dialog (which itself is modal) opens the code after if (canWrite)
is already executed. This is not a big issue since canWrite
is false
in this moment, however, once the showWarningMessage
thenable returns no code is executed anymore in the outer thenable (from showInputBox
), i.e. the if (canWrite)
is not executed again (as I would expect). Is it impossible to nest 2 thenables or what else am I doing wrong?
How would an experienced typescript/vscode develper approach this task?
typescript visual-studio-code vscode-extensions
I have to implement a very typical programming pattern in a Visual Studio Code extension: save something to a file, but check if the target file exists before doing so and ask the user if it is ok to overwrite it, if it does.
Usually I would just open a file save dialog to ask the user to give me a file name and this dialog would do all necessary checks and gets the user confirmation, if necessary. In vscode however we don't have a file save dialog (however there is a feature request for it). So I'm trying to implement this with the limited means we have there. Fortunately a few weeks ago a new option parameter was added to message dialogs to allow making them modal. But somehow I can't get the timing right. Here's my code:
window.showInputBox({
placeHolder: "<Enter full file name here>",
prompt: "Enter the name to an html file to save the diagramn" }
).then((value: string) => {
if (value) {
let canWrite = true;
if (fs.existsSync(value)) {
canWrite = false;
window.showWarningMessage("The specified file exists already", { modal: true }, ...[ "Overwrite" ]).then((action: string) => {
if (action === "Overwrite") {
canWrite = true;
}
});
}
if (canWrite) {
var stream = fs.createWriteStream(value, { encoding: "utf-8", autoClose: true });
stream.on("error", function (error: any) {
window.showErrorMessage("Could not write to file '" + value + "'. " + error);
});
stream.once('open', function (fd) {
stream.write(text);
stream.end();
window.showInformationMessage("Diagram successfully written to file '" + value + "'.");
});
}
}
})
The problem is that the call to window.showWarningMessage
is non-blocking, that means while the dialog (which itself is modal) opens the code after if (canWrite)
is already executed. This is not a big issue since canWrite
is false
in this moment, however, once the showWarningMessage
thenable returns no code is executed anymore in the outer thenable (from showInputBox
), i.e. the if (canWrite)
is not executed again (as I would expect). Is it impossible to nest 2 thenables or what else am I doing wrong?
How would an experienced typescript/vscode develper approach this task?
typescript visual-studio-code vscode-extensions
typescript visual-studio-code vscode-extensions
edited 2 days ago
Gama11
10.8k21946
10.8k21946
asked Apr 18 '17 at 12:51
Mike Lischke
20.6k349101
20.6k349101
add a comment |
add a comment |
1 Answer
1
active
oldest
votes
The showWarningMessage
is not thenable as you need, you can't nest it. Instead, you have to create your own Thenable approach, which would require a bit of refactoring.
The main idea is that your Saving must return a Promise
, and it will control the showWarningMessage
return (when needed)
function saveDiagram(text, filename): Promise<string | boolean> {
return new Promise((resolve, reject) => {
if (fs.existsSync(filename)) {
vscode.window.showWarningMessage("The specified file exists already", { modal: true }, ...[ "Overwrite" ]).then((action: string) => {
if (action === "Overwrite") {
resolve(filename);
} else {
resolve(false);
}
});
} else {
resolve(filename);
}
});
}
Also extract your Writing Diagram on Disk as a new function, to be called latter:
function writeDiagramOnDisk(text, filename) {
var stream = fs.createWriteStream(filename, { encoding: "utf-8", autoClose: true });
stream.on("error", function (error: any) {
vscode.window.showErrorMessage("Could not write to file '" + filename + "'. " + error);
});
stream.once('open', function (fd) {
stream.write(text);
stream.end();
vscode.window.showInformationMessage("Diagram successfully written to file '" + filename + "'.");
});
}
And now your extension's code will have the thenable approach, as you would expect:
vscode.window.showInputBox({
placeHolder: "<Enter full file name here>",
prompt: "Enter the name to an html file to save the diagramn" }
).then((value: string) => {
if (value) {
saveDiagram(text, value)
.then((filename) => {
if (filename) {
writeDiagramOnDisk(text, filename)
}
});
}
})
Your solution makes sense and I may use it in the future. For now I have simply moved my save code to a separate function and call that either directly or once I found an existing file and the user agreed to overwrite it.
– Mike Lischke
May 2 '17 at 15:17
add a comment |
Your Answer
StackExchange.ifUsing("editor", function () {
StackExchange.using("externalEditor", function () {
StackExchange.using("snippets", function () {
StackExchange.snippets.init();
});
});
}, "code-snippets");
StackExchange.ready(function() {
var channelOptions = {
tags: "".split(" "),
id: "1"
};
initTagRenderer("".split(" "), "".split(" "), channelOptions);
StackExchange.using("externalEditor", function() {
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled) {
StackExchange.using("snippets", function() {
createEditor();
});
}
else {
createEditor();
}
});
function createEditor() {
StackExchange.prepareEditor({
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: true,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: 10,
bindNavPrevention: true,
postfix: "",
imageUploader: {
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
},
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
});
}
});
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f43472709%2fhow-to-implement-save-to-file-check-overwrite-in-a-vscode-extension%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
1 Answer
1
active
oldest
votes
1 Answer
1
active
oldest
votes
active
oldest
votes
active
oldest
votes
The showWarningMessage
is not thenable as you need, you can't nest it. Instead, you have to create your own Thenable approach, which would require a bit of refactoring.
The main idea is that your Saving must return a Promise
, and it will control the showWarningMessage
return (when needed)
function saveDiagram(text, filename): Promise<string | boolean> {
return new Promise((resolve, reject) => {
if (fs.existsSync(filename)) {
vscode.window.showWarningMessage("The specified file exists already", { modal: true }, ...[ "Overwrite" ]).then((action: string) => {
if (action === "Overwrite") {
resolve(filename);
} else {
resolve(false);
}
});
} else {
resolve(filename);
}
});
}
Also extract your Writing Diagram on Disk as a new function, to be called latter:
function writeDiagramOnDisk(text, filename) {
var stream = fs.createWriteStream(filename, { encoding: "utf-8", autoClose: true });
stream.on("error", function (error: any) {
vscode.window.showErrorMessage("Could not write to file '" + filename + "'. " + error);
});
stream.once('open', function (fd) {
stream.write(text);
stream.end();
vscode.window.showInformationMessage("Diagram successfully written to file '" + filename + "'.");
});
}
And now your extension's code will have the thenable approach, as you would expect:
vscode.window.showInputBox({
placeHolder: "<Enter full file name here>",
prompt: "Enter the name to an html file to save the diagramn" }
).then((value: string) => {
if (value) {
saveDiagram(text, value)
.then((filename) => {
if (filename) {
writeDiagramOnDisk(text, filename)
}
});
}
})
Your solution makes sense and I may use it in the future. For now I have simply moved my save code to a separate function and call that either directly or once I found an existing file and the user agreed to overwrite it.
– Mike Lischke
May 2 '17 at 15:17
add a comment |
The showWarningMessage
is not thenable as you need, you can't nest it. Instead, you have to create your own Thenable approach, which would require a bit of refactoring.
The main idea is that your Saving must return a Promise
, and it will control the showWarningMessage
return (when needed)
function saveDiagram(text, filename): Promise<string | boolean> {
return new Promise((resolve, reject) => {
if (fs.existsSync(filename)) {
vscode.window.showWarningMessage("The specified file exists already", { modal: true }, ...[ "Overwrite" ]).then((action: string) => {
if (action === "Overwrite") {
resolve(filename);
} else {
resolve(false);
}
});
} else {
resolve(filename);
}
});
}
Also extract your Writing Diagram on Disk as a new function, to be called latter:
function writeDiagramOnDisk(text, filename) {
var stream = fs.createWriteStream(filename, { encoding: "utf-8", autoClose: true });
stream.on("error", function (error: any) {
vscode.window.showErrorMessage("Could not write to file '" + filename + "'. " + error);
});
stream.once('open', function (fd) {
stream.write(text);
stream.end();
vscode.window.showInformationMessage("Diagram successfully written to file '" + filename + "'.");
});
}
And now your extension's code will have the thenable approach, as you would expect:
vscode.window.showInputBox({
placeHolder: "<Enter full file name here>",
prompt: "Enter the name to an html file to save the diagramn" }
).then((value: string) => {
if (value) {
saveDiagram(text, value)
.then((filename) => {
if (filename) {
writeDiagramOnDisk(text, filename)
}
});
}
})
Your solution makes sense and I may use it in the future. For now I have simply moved my save code to a separate function and call that either directly or once I found an existing file and the user agreed to overwrite it.
– Mike Lischke
May 2 '17 at 15:17
add a comment |
The showWarningMessage
is not thenable as you need, you can't nest it. Instead, you have to create your own Thenable approach, which would require a bit of refactoring.
The main idea is that your Saving must return a Promise
, and it will control the showWarningMessage
return (when needed)
function saveDiagram(text, filename): Promise<string | boolean> {
return new Promise((resolve, reject) => {
if (fs.existsSync(filename)) {
vscode.window.showWarningMessage("The specified file exists already", { modal: true }, ...[ "Overwrite" ]).then((action: string) => {
if (action === "Overwrite") {
resolve(filename);
} else {
resolve(false);
}
});
} else {
resolve(filename);
}
});
}
Also extract your Writing Diagram on Disk as a new function, to be called latter:
function writeDiagramOnDisk(text, filename) {
var stream = fs.createWriteStream(filename, { encoding: "utf-8", autoClose: true });
stream.on("error", function (error: any) {
vscode.window.showErrorMessage("Could not write to file '" + filename + "'. " + error);
});
stream.once('open', function (fd) {
stream.write(text);
stream.end();
vscode.window.showInformationMessage("Diagram successfully written to file '" + filename + "'.");
});
}
And now your extension's code will have the thenable approach, as you would expect:
vscode.window.showInputBox({
placeHolder: "<Enter full file name here>",
prompt: "Enter the name to an html file to save the diagramn" }
).then((value: string) => {
if (value) {
saveDiagram(text, value)
.then((filename) => {
if (filename) {
writeDiagramOnDisk(text, filename)
}
});
}
})
The showWarningMessage
is not thenable as you need, you can't nest it. Instead, you have to create your own Thenable approach, which would require a bit of refactoring.
The main idea is that your Saving must return a Promise
, and it will control the showWarningMessage
return (when needed)
function saveDiagram(text, filename): Promise<string | boolean> {
return new Promise((resolve, reject) => {
if (fs.existsSync(filename)) {
vscode.window.showWarningMessage("The specified file exists already", { modal: true }, ...[ "Overwrite" ]).then((action: string) => {
if (action === "Overwrite") {
resolve(filename);
} else {
resolve(false);
}
});
} else {
resolve(filename);
}
});
}
Also extract your Writing Diagram on Disk as a new function, to be called latter:
function writeDiagramOnDisk(text, filename) {
var stream = fs.createWriteStream(filename, { encoding: "utf-8", autoClose: true });
stream.on("error", function (error: any) {
vscode.window.showErrorMessage("Could not write to file '" + filename + "'. " + error);
});
stream.once('open', function (fd) {
stream.write(text);
stream.end();
vscode.window.showInformationMessage("Diagram successfully written to file '" + filename + "'.");
});
}
And now your extension's code will have the thenable approach, as you would expect:
vscode.window.showInputBox({
placeHolder: "<Enter full file name here>",
prompt: "Enter the name to an html file to save the diagramn" }
).then((value: string) => {
if (value) {
saveDiagram(text, value)
.then((filename) => {
if (filename) {
writeDiagramOnDisk(text, filename)
}
});
}
})
answered May 1 '17 at 14:36
![](https://i.stack.imgur.com/7fqZR.jpg?s=32&g=1)
![](https://i.stack.imgur.com/7fqZR.jpg?s=32&g=1)
alefragnani
891211
891211
Your solution makes sense and I may use it in the future. For now I have simply moved my save code to a separate function and call that either directly or once I found an existing file and the user agreed to overwrite it.
– Mike Lischke
May 2 '17 at 15:17
add a comment |
Your solution makes sense and I may use it in the future. For now I have simply moved my save code to a separate function and call that either directly or once I found an existing file and the user agreed to overwrite it.
– Mike Lischke
May 2 '17 at 15:17
Your solution makes sense and I may use it in the future. For now I have simply moved my save code to a separate function and call that either directly or once I found an existing file and the user agreed to overwrite it.
– Mike Lischke
May 2 '17 at 15:17
Your solution makes sense and I may use it in the future. For now I have simply moved my save code to a separate function and call that either directly or once I found an existing file and the user agreed to overwrite it.
– Mike Lischke
May 2 '17 at 15:17
add a comment |
Thanks for contributing an answer to Stack Overflow!
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.
Some of your past answers have not been well-received, and you're in danger of being blocked from answering.
Please pay close attention to the following guidance:
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f43472709%2fhow-to-implement-save-to-file-check-overwrite-in-a-vscode-extension%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
y822W7X2C42F3ytHZ YH,NZWb O,tzl