Skip to content

Save dirty documents before evaluating queries#2538

Merged
dbartol merged 28 commits intomainfrom
dbartol/save-before-start
Jul 11, 2023
Merged

Save dirty documents before evaluating queries#2538
dbartol merged 28 commits intomainfrom
dbartol/save-before-start

Conversation

@dbartol
Copy link
Copy Markdown
Contributor

@dbartol dbartol commented Jun 21, 2023

Fixes #2390

When I refactored some of the query evaluation code, I broke the existing mechanism for saving dirty documents before evaluation. I still display the prompt to save, but then ignored the result and never saved the documents 🤦.

When using the VS Code debugger UI, there's already a setting named debug.saveBeforeStart that controls which documents to save before launching a debug session. When launching a query via a built-in VS Code command like "Start Debugging", VS Code saves the documents automatically. I've tried to emulate that behavior for the code paths that don't go through VS Code commands, including the non-debugger "CodeQL: Quick evaluation" and "CodeQL: Run query on selected database". I've also added that behavior to "CodeQL: Debug selection" when using an existing debug session, since it doesn't go through VS Code's debug launch code path.

Depend on the user setting, VS Code will either save all dirty documents in the active tab group, including or excluding untitled documents, or it will not save anything. There's no exposed command that does this saving, so I had to emulate the behavior as best I could. I'm sure it's not exactly equivalent, but it should be close enough that nobody will notice.

I marked our existing CodeQL-specific setting for saving before evaluation as deprecated and ignored, pointing users to the core VS Code setting.

Testing the emulation was tricky, because TextDocument's properties and methods are marked either non-configurable or readonly, so Jest can't spy on them. Instead, see the comments for how I tested using public VS Code APIs and one terrible hack. The Interwebs provided some incantations to allow the spying, but they all involved replacing methods on the prototype of Object so they seemed a bit scary. If someone more comfortable with JavaScript and mocking can suggest a better solution, I'm happy to change it.

@dbartol dbartol requested a review from a team as a code owner June 21, 2023 18:28
@dbartol dbartol added the Complexity: Low A good task for newcomers to learn, or experienced team members to complete quickly. label Jun 21, 2023
Comment thread extensions/ql-vscode/src/config.ts Outdated
workspaceFolderValue?: T;
}

const VSCODE_DEBUG_SETTING = new Setting("debug", undefined, "ql");
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I'm not sure how language scope works. If someone sets this in the workspace/machine/user scope, will it still be available for the language?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

To be honest, I'm not sure either. How VS Code computes the override behavior between, say, a language-independent workspace setting and a language-specific user setting is up to VS Code, but doesn't change how we implement this feature.

Comment thread extensions/ql-vscode/src/run-queries-shared.ts
Comment thread extensions/ql-vscode/src/run-queries-shared.ts Outdated
Comment thread extensions/ql-vscode/src/run-queries-shared.ts Outdated
Comment thread extensions/ql-vscode/src/run-queries-shared.ts Outdated
return new Promise((resolve) => setTimeout(() => resolve("timeout"), ms));
}

jest.setTimeout(10000); // A little extra time for the save dialog to appear.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I'm not sure, but I think that setting this value outside of a describe block makes it global. Inside of a describe block means it is only applicable in for those tests (I may have this mixed up with mocha, though).

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

This file has been removed.

expect(cleanDoc.isDirty).toBe(false);
});

it("should not save dirty documents in other tab groups", async () => {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

This feature is surprising to me. I would expect all docs in the workspace to be saved when I invoke the debugger. Is this the behaviour of this setting in the vscode?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Also, is this why you can't use the saveAll API method?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

🤦 I was tricked by the name, but the VS Code source code just calls the internal equivalent of saveAll(). The actual behavior for other languages saves tabs in other groups as well. I'll fix it.

My bet is that somebody thought that saving only the current tab group was a good idea early on, so that's what the setting was named. Then, when every developer everywhere tried it and hated it, they just made it implicitly mean "save all".

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I've changed to code to just use saveAll(). The handling of untitled documents appears to be slightly different, but close enough that I won't bother emulating the VS Code behavior precisely.

The original test for saveAllInGroup() is now irrelevant, so instead I added a debugger test case to make sure the documents get saved at the right points. Since the source files are modified by the test, this required making a temporary copy of the data directory to use as the workspace, instead of opening the committed data directory directly. I had to move the test queries into the data directory that we use as the workspace folder, too, since we were pulling them directly from source.

Copy link
Copy Markdown
Contributor

@aeisenberg aeisenberg left a comment

Choose a reason for hiding this comment

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

Nice. I'm assuming there's just a mixup in the tests. If so, 👍🏼.

@dbartol dbartol enabled auto-merge June 22, 2023 22:21

await editor.edit((editBuilder) => {
editBuilder.insert(new Position(0, 0), "/* another comment */");
if (process.platform !== "win32") {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Is this functionality broken on windows, or is it just that the test is failing? Would you be able to add a comment to clarify this?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Still struggling to fix a test hang that only repros on CI. Just ignore this until I get CI passing.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

In what I believe is the third 🤦 of this PR, I finally determined that the timeouts are not due to the "Save As" dialog popping up on an unexpected dirty document, but rather due to Workspace Trust, which triggers a prompt because I changed the tests to copy their workspace data to a temp folder before opening. I've disabled Workspace Trust when invoking the CLI integration tests.

Comment thread extensions/ql-vscode/src/run-queries-shared.ts Fixed
@dbartol dbartol force-pushed the dbartol/save-before-start branch from fbbfede to d9a1aa8 Compare June 26, 2023 19:53
@dbartol dbartol merged commit 3e59859 into main Jul 11, 2023
@dbartol dbartol deleted the dbartol/save-before-start branch July 11, 2023 19:55
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Complexity: Low A good task for newcomers to learn, or experienced team members to complete quickly.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Quick eval doesn't save file first

3 participants