Skip to content
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

Set context keys for document language #608

Merged
merged 7 commits into from
Nov 26, 2024

Conversation

juliasilge
Copy link
Collaborator

This is a revamp of the work in #607.

Unfortunately after I started trying to use this in Positron, it just wasn't early enough or quick enough to set these context keys in languageAtPosition(). I believe that only gets called if we are trying to do one of these:

export type VirtualDocAction =
"completion" |
"hover" |
"signature" |
"definition" |
"format" |
"statementRange" |
"helpTopic";

It's just not enough, especially if you are switching back and forth between different files; it's very easy to get in a state where you end up with R shortcuts in Python chunks and there are lots of kinds of edits I could make that did not trigger one of these.

New idea! This PR tries out setting these context keys when we paint the background. In my experience so far, this feels a lot better, and we already are identifying all the tokens so not much extra work.

@juliasilge
Copy link
Collaborator Author

Related to posit-dev/positron#5451

@@ -162,6 +163,10 @@ async function setEditorHighlightDecorations(
blockRanges.push(vscRange(block.range));
Copy link
Collaborator

@DavisVaughan DavisVaughan Nov 26, 2024

Choose a reason for hiding this comment

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

What if highlightingConfig isn't enabled? See the if-block above.

(See broader comment about using a different approach)

// expose cell language for use in keybindings, etc
const language = mainLanguage(tokens);
vscode.commands.executeCommand('setContext', 'quartoLangId', language?.ids[0]);

Copy link
Collaborator

Choose a reason for hiding this comment

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

I do not feel it is appropriate to set the context here

Currently this function is quite pure. It is just about setting highlight decorations and is very easy to understand.

If we take a look at why it works to put this here, its because this is called from

  • triggerUpdateAllEditorsDecorations(), which is called during the onDidChangeVisibleTextEditors() hook
  • triggerUpdateActiveEditorDecorations(), which is called during the onDidOpenTextDocument() hook

That hook registration is part of activateBackgroundHighlighter(), which is called in activateCommon().


I think what we really want is:

  • In activateCommon(),
  • Add a new activateLanguageContextSetter()
  • Which registers onDidOpenTextDocument() and onDidChangeVisibleTextEditors() hooks of its own, and those hooks set the quartoMainLanguageId of the main language for the document.

Yes you have to reparse the document, but I think that is worth it over muddying the waters about what setEditorHighlightDecorations() is about, plus it gives you finer control over when the context is updated.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

This is a great idea! Implemented in c6f28f8 and working really well in posit-dev/positron#5451.

@@ -162,6 +163,10 @@ async function setEditorHighlightDecorations(
blockRanges.push(vscRange(block.range));
}

// expose cell language for use in keybindings, etc
const language = mainLanguage(tokens);
vscode.commands.executeCommand('setContext', 'quartoLangId', language?.ids[0]);
Copy link
Collaborator

@DavisVaughan DavisVaughan Nov 26, 2024

Choose a reason for hiding this comment

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

I'd consider using a non-abbreviated and very descriptive name

quarto.document.mainLanguageId
quarto.documentMainLanguageId
quartoDocumentMainLanguageId

See "quarto.assistView.isEnabled" as a nice example

It's a little tricky because we have a mix of styles right now for context key naming. Sometimes we do quarto. sometimes we dont https://github.com/search?q=repo%3Aquarto-dev%2Fquarto%20setContext&type=code

I tend to like using the . as a way to namespace it to quarto

(also including main in the name is a way for us to guard against the possibility of us eventually actually getting a per-cell context key right)

Copy link
Contributor

Choose a reason for hiding this comment

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

Why not just quarto.document.languageId vs (eventually) something like quarto.cell.languageId?

document vs cell is an evocative distinction. main wants to contrast with secondary, and that's not what you're setting up to do in the future.

Copy link
Collaborator

Choose a reason for hiding this comment

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

Works great for me!

@@ -157,12 +157,8 @@ export async function virtualDocUri(
export function languageAtPosition(tokens: Token[], position: Position) {
const block = languageBlockAtPosition(tokens, position);
if (block) {
Copy link
Collaborator

Choose a reason for hiding this comment

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

IIUC, previously it attempted to update the context key dynamically on each cell "activation", right? That does seem hard to get 100% right.

And now it tries to update the context key 1 time for whatever the main language in the document is.

I suppose that could be mildly confusing. If you have 1 python cell in a mostly R document, then clicking in the python cell and running a keybinding will run the R version. That's kinda confusing, but I'm not sure what we can do better.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I did hope for us to get this working really nicely for reticulate Quarto files (per cell), but when I tried to use the approach with languageAtPosition() in practice, it felt quite awful. So yep, I think for now this approach with the "main language" is the best option.

@juliasilge juliasilge changed the title Set context keys for cell language when background is painted Set context keys for document language Nov 26, 2024
Copy link
Collaborator

@DavisVaughan DavisVaughan left a comment

Choose a reason for hiding this comment

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

Looks great, just one main comment left about exactly when we should set the context key

Comment on lines 75 to 79
function triggerUpdateContextKeys(engine: MarkdownEngine) {
for (const editor of vscode.window.visibleTextEditors) {
setContextKeys(editor, engine);
}
}
Copy link
Collaborator

Choose a reason for hiding this comment

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

I think this is looking great!

My main comment is about this, and any other usage of visibleTextEditors

IIUC, the context key is "global". i.e. there is not a context key per document, there is exactly 1 context key set globally.

If that is the case, then the context key should probably only be set for the active text editor, not for all visible text editors (because only 1 wins!).

For example, if I open 2 qmds side by side in split panes, one that is mostly R and one that is mostly Python, then I'm not entirely sure which is going to win here because both are "visible". However, only one at a time is ever "active", and I feel like that is probably what we should be targeting with this context key.

Is it possible all we really need to do is having a single hook for onDidChangeActiveTextEditor()?

Does that make any sense?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Makes total sense, yep. Changed in 8d00a51.

Copy link
Collaborator Author

@juliasilge juliasilge Nov 26, 2024

Choose a reason for hiding this comment

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

I added the hook for onDidChangeTextDocument() (with some debounce) for the case where someone changes their document from being Python to R or vice versa. That is certainly rare, but that case does feel fairly broken if we don't include it.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

You are correct that we don't need onDidOpenTextDocument() if we have onDidChangeActiveTextEditor(). 👍

Copy link
Collaborator

Choose a reason for hiding this comment

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

I added the hook for onDidChangeTextDocument()

Makes sense because you also dont have a "main" language at the start of a document until you actually start typing and adding some chunks (or maybe there is a default, but it likely isnt right)


PR looks great nice job!

@juliasilge
Copy link
Collaborator Author

I installed the final .vsix from this PR and ran through all my normal Quarto moves, both with a current release build of Positron, dev build from posit-dev/positron#5451, and VS Code. Behaves as expected and looks good!

@juliasilge juliasilge merged commit dca4735 into main Nov 26, 2024
1 check passed
@juliasilge juliasilge deleted the revamp-quarto-lang-context-keys branch November 26, 2024 21:07
juliasilge added a commit to posit-dev/positron that referenced this pull request Nov 27, 2024
Addresses #1955 together with
quarto-dev/quarto#608

The Quarto PR provides new context keys for the main language of a
Quarto document (for example, R or Python) and we can consume those to
provide keyboard shortcuts.

There are really only a couple of R ones that we want right now which
means that there is no real change here in an R `.qmd` in behavior
compared to what is in the RStudio Keymap. 🙈 However, I think this is
still worth getting in since we've gone to the trouble of figuring it
out, it lets us offer these without the RStudio Keymap being on, we can
offer them _only_ in R `.qmd` files (right now, these also show up in
Python `.qmd` files if you have the RStudio Keymap on), and we can use
this infrastructure in the future.

I did remove these from the RStudio Keymap just to clean things up, but
it wouldn't hurt _a lot_ to keep them, if there is some reason I am not
thinking of? If we do want to remove them, we'll need to do a Quarto
release and update the bundled Quarto VS Code extension version in this
PR to keep these keybindings functional.

### QA Notes

With the new version of the Quarto extension (updated here), you can use
the keyboard shortcuts for the pipe and assignment operator in R Quarto
documents but not Python ones:

- <kbd>Alt</kbd>+<kbd>-</kbd> to get you `<-`
- <kbd>Cmd/Ctrl</kbd>+<kbd>Shift</kbd>+<kbd>M</kbd> to get you `|>`
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.

3 participants