How to implement save-to-file-check-overwrite in a vscode extension?

Multi tool use
Multi tool use












0














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?










share|improve this question





























    0














    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?










    share|improve this question



























      0












      0








      0







      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?










      share|improve this question















      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






      share|improve this question















      share|improve this question













      share|improve this question




      share|improve this question








      edited 2 days ago









      Gama11

      10.8k21946




      10.8k21946










      asked Apr 18 '17 at 12:51









      Mike Lischke

      20.6k349101




      20.6k349101
























          1 Answer
          1






          active

          oldest

          votes


















          1














          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)
          }
          });

          }
          })





          share|improve this answer





















          • 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 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
          });


          }
          });














          draft saved

          draft discarded


















          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









          1














          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)
          }
          });

          }
          })





          share|improve this answer





















          • 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
















          1














          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)
          }
          });

          }
          })





          share|improve this answer





















          • 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














          1












          1








          1






          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)
          }
          });

          }
          })





          share|improve this answer












          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)
          }
          });

          }
          })






          share|improve this answer












          share|improve this answer



          share|improve this answer










          answered May 1 '17 at 14:36









          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


















          • 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


















          draft saved

          draft discarded




















































          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.




          draft saved


          draft discarded














          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





















































          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
          zq,p,rxA7TyBZ2pmqgVgFapKfCgMz9swFx kLg B pcMH3Z

          Popular posts from this blog

          Monofisismo

          Angular Downloading a file using contenturl with Basic Authentication

          Olmecas