Skip to content

Make SaveAs work for untitled files #1305

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 7 commits into from
May 2, 2018

Conversation

rjmholt
Copy link
Contributor

@rjmholt rjmholt commented Apr 28, 2018

Fixes #1303.

This makes the $psEditor.GetEditorContext().CurrentFile.SaveAs("blah.ps1") function work for files with an untitled schema.

It also adds some logging to make it more diagnosable (because the logic for dealing with VSCode's crazy URIs is a bit hairy).

Some cases to test (on various platforms) before we merge:

  • Take a normal powershell file, modify it and Save()
  • Take a normal powershell file, modify it and SaveAs() with a relative path (should save to path relative to the file).
  • Do the same but with an absolute path.
  • Do the same but on a different windows drive in the absolute path.
  • Open a new "untitled" file, change language mode to PowerShell and Save() (should not work)
  • Open a new "untitled" file, change language mode to PowerShell and SaveAs() with a relative path (should save relative to the workspace root).
  • Open a new "untitled" file, change language mode to PowerShell and SaveAs() to an absolute path.
  • Same as above but on a different windows drive.
  • Open a new "untitled" workspace Open VSCode with no workspace open, open an untitled document, change language mode to PowerShell and SaveAs() to an absolute path (should work).
  • Same as above but with a relative path (should not work, since workspace is "untitled" there is no workspace open).

}
// Finally open the new document
const newFileUri = vscode.Uri.file(destinationAbsolutePath);
const newFile = await vscode.workspace.openTextDocument(newFileUri);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yea! Glad to see some async/await make its way into the extension. I find it easier to read than a bunch of .then() continuations. That's probably the C# dev in me coming out but I think TS/JS devs will like it too.

Copy link
Contributor Author

@rjmholt rjmholt Apr 30, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, @SeeminglyScience set me on the right path there after I tried to put in a particularly twisted chain of .then()s when I first tried SaveAs().

// Other URI schemes are not supported
const msg = JSON.stringify(saveFileDetails);
this.log.writeVerbose(
`<ExtensionCommands>: Saving a document with scheme '${currentFileUri.scheme}' ` +
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Try <${ExtensionCommands.name}>: ...

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah nice!

// TODO: Change this to be asyncronous
fs.writeFileSync(destinationAbsolutePath, oldDocument.getText());
} catch (e) {
this.log.writeAndShowWarning("<ExtensionCommands>: " +
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ditto above comment

@@ -56,7 +56,8 @@ export class HelpCompletionFeature implements IFeature {

public onEvent(changeEvent: TextDocumentChangeEvent): void {
if (!(changeEvent && changeEvent.contentChanges)) {
this.log.write(`Bad TextDocumentChangeEvent message: ${JSON.stringify(changeEvent)}`);
this.log.writeWarning("<HelpCompletion>: " +
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Try <${HelpCompletion.name}>: ...

@rjmholt rjmholt closed this Apr 30, 2018
@rjmholt rjmholt reopened this Apr 30, 2018
@rjmholt
Copy link
Contributor Author

rjmholt commented Apr 30, 2018

Oops, clicked the wrong button there.

@rjmholt
Copy link
Contributor Author

rjmholt commented Apr 30, 2018

@rkeithhill, I ended up having to use

`<${ExtensionCommandsFeature.name}>`

My ideal would be to have some kind of IFeature extension method/convenience function that takes the type name and removes the Feature suffix, but I think that would basically require a macro to do nicely.

I'm assuming extension methods aren't a thing, particularly on interfaces, but we could maybe use a utility function. Ideally I can submit a new PR that improves extension logging.

Copy link
Contributor

@rkeithhill rkeithhill left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

Copy link
Member

@TylerLeonhardt TylerLeonhardt left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM! Just a couple nits and a question about the sync file saving.

} else {
// If not, interpret the path as relative to the workspace root
const workspaceRootUri = vscode.workspace.workspaceFolders[0].uri;
// We don't saving to a non-file URI-schemed workspace
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit pick: comment language:
"we don't saving" should be "we don't support saving"

}

await this.saveDocumentContentToAbsolutePath(currentFileUri, newFileAbsolutePath);
return EditorOperationResponse.Completed;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nitpick, pull these two lines out of catch to be more DRY? ;)

// Write it to the new document path
try {
// TODO: Change this to be asyncronous
fs.writeFileSync(destinationAbsolutePath, oldDocument.getText());
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we not already make this async? If it's too much work don't worry about it.

Synchronously make the file, asynchronously add the content to the file?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I looked into it, but writeFile is an older node API that wants a callback. I have been contemplating turning the whole call into a Promise though. Thoughts?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry if this is already known, but FYI you can await inline promises, e.g:

await new Promise<void>((resolve) => {
    fs.writeFile(destinationAbsolutePath, oldDocument.getText(), resolve);
});

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah that's handy! Thanks @SeeminglyScience

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Type system wasn't quite happy with resolve there, but I've ended up with something similar.

Copy link
Member

@TylerLeonhardt TylerLeonhardt left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM :)

@rjmholt
Copy link
Contributor Author

rjmholt commented May 2, 2018

Let me run the tests I've got above before we merge

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants