Skip to content

Preview R Markdown files via background process#692

Merged
renkun-ken merged 21 commits intoREditorSupport:masterfrom
ElianHugh:rmarkdown-preview
Jul 9, 2021
Merged

Preview R Markdown files via background process#692
renkun-ken merged 21 commits intoREditorSupport:masterfrom
ElianHugh:rmarkdown-preview

Conversation

@ElianHugh
Copy link
Copy Markdown
Collaborator

@ElianHugh ElianHugh commented Jul 1, 2021

What problem did you solve?

Improves previewing rmd files, as per some discussion in #103. Not quite the same experience as vscode's markdown preview, but is a little less cumbersome than running rmarkdown::run manually.

The preview "engine" is controlled through a configuration setting, and currently supports rmarkdown::run or xaringan::infinite_moon_reader.

Some stuff that I think would be neat to have at some point:

  1. scroll sync with rmd source
    2. get the document's http content (similar to how the help provider functions), meaning that the iframe isn't needed

Screenshot

image

ElianHugh and others added 6 commits July 1, 2021 13:59
- Preview rmd w/o blocking the terminal
- Fixed a bug that meant the side preview was incorrectly opening in the active column
- Fixed a bug that incorrectly caused show source to open the source doc in a new column
- Rename some commands and the PreviewProvider to be more in line with vscode's markdown preview names
@renkun-ken
Copy link
Copy Markdown
Member

renkun-ken commented Jul 3, 2021

It works quite nicely already.

I wonder if it makes sense to create a R Markdown output channel to show the stdout and stderr of the background R process as it renders the document?

Comment thread src/rmarkdown/preview.ts Outdated
- Moved refresh to overflow menu
- Change how 'Show Source' retrieves the original column
- 'R Markdown' output channel for stderr and stdout
- Better track and dispose of child processes
- Progress bar now stays for the duration of loading
- Now tells the user when the document failed to knit
Comment thread src/rmarkdown/preview.ts Outdated
Comment thread src/rmarkdown/preview.ts Outdated
- Kill processes that error *after* knitting
- Fix refresh bug that caused panel to go blank
Comment thread src/util.ts Outdated
- Knitting can now be cancelled, and the process is properly killed
- Can no longer spam the preview button of the same document while knitting, preventing duplicate webviews
Comment thread src/rmarkdown/preview.ts Outdated
@renkun-ken
Copy link
Copy Markdown
Member

It looks like the shiny app started by rmarkdown::run() does copy the web resources to local (see https://github.com/rstudio/rmarkdown/blob/master/R/shiny.R#L264) and the urls are replaced to local urls whenever an auto refresh occurs. This violates the security policy of the WebView on accessing local resources.

@renkun-ken
Copy link
Copy Markdown
Member

Another approach to avoid this is to only use "rmarkdown::render()" or any other engine that is able to produce an HTML file. And we could use fs.watch() to watch the file and implement auto-render on file changes. But this will add much complexity.

@ElianHugh
Copy link
Copy Markdown
Collaborator Author

I suppose using rmarkdown::render would also ensure that syntax highlighting is maintained, and that we would have more control over the document's styling. I wonder if there is much of a difference performance and compilation wise

- busyUri probably makes more sense as a set, rather than an array
- Show rmarkdown output on error
- change return values for RMarkdownChildStore methods to what is expected of a set<T>
@renkun-ken
Copy link
Copy Markdown
Member

I suppose using rmarkdown::render would also ensure that syntax highlighting is maintained, and that we would have more control over the document's styling.

Exactly. Both mathjax and syntax highlighting should work after reload.

I wonder if there is much of a difference performance and compilation wise

I think there should not be a significant performance difference compared with rendering from a shiny app.

Changes preview engine to rmarkdown::render, and relies on fs.watch to check for updates.

This is a pretty messy implementation, and will be cleaned up shortly
@ElianHugh
Copy link
Copy Markdown
Collaborator Author

I've added a super messy implementation of the document file watcher, which appears to function as expected. Will be cleaned up shortly

Comment thread src/rmarkdown/preview.ts Outdated
Comment thread src/rmarkdown/preview.ts Outdated
@renkun-ken
Copy link
Copy Markdown
Member

I move watcherDir from session.ts to extDir in extension.ts as a global extension directory in user home. And temporary files are generated into ~/.vscode-R/tmp as tmpDir. Now session watcher uses extDir and rmarkdown preview uses tmpDir.

@ElianHugh
Copy link
Copy Markdown
Collaborator Author

I move watcherDir from session.ts to extDir in extension.ts as a global extension directory in user home. And temporary files are generated into ~/.vscode-R/tmp as tmpDir. Now session watcher uses extDir and rmarkdown preview uses tmpDir.

Thanks! Works quite nicely - I've also removed references to dir because it will just end up being tmpDir anyway

- CSS can now be toggled between vscode theme and base doc theme
- Changed store from set to map
- now cache html in RMarkdownChild, so that toggling doesn't require a re-read of the source html
- Ensure output is html
- Refactor is ongoing
@ElianHugh
Copy link
Copy Markdown
Collaborator Author

Latest push now has a 'toggle style' button similar to the plot viewer:

image

Refactor from set to map is ongoing - RMarkdownChild will have the input uri removed soon as its now superfluous

@renkun-ken
Copy link
Copy Markdown
Member

The dark theme looks good, but the output is almost invisible under many dark themes.

image

Improve styling so as to not create unreadable text
@ElianHugh
Copy link
Copy Markdown
Collaborator Author

The dark theme looks good, but the output is almost invisible under many dark themes.

image

Don't see the problem here 😅

Should be fixed following last push!

@renkun-ken
Copy link
Copy Markdown
Member

renkun-ken commented Jul 9, 2021

The theming looks good now. And vscode.env.openExternal(this.activePreview?.preview?.outputUri) works well on Ubuntu, locally. If we use an open-in-browser package, and then it won't work in remote development scenario where an HTTP server must be created to serve the HTML page. In this case, we need to rely on something like Live Preview.

@ElianHugh
Copy link
Copy Markdown
Collaborator Author

ElianHugh commented Jul 9, 2021

Makes sense to me! I had a quick look into scroll syncing and it gave me a headache. I think if we are happy with the current features then we can save that for a later date, and just iterate on the current codebase

@renkun-ken renkun-ken marked this pull request as ready for review July 9, 2021 09:32
Copy link
Copy Markdown
Member

@renkun-ken renkun-ken left a comment

Choose a reason for hiding this comment

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

LGTM

@renkun-ken renkun-ken changed the title WIP - Preview R Markdown files via background process Preview R Markdown files via background process Jul 9, 2021
@renkun-ken
Copy link
Copy Markdown
Member

renkun-ken commented Jul 9, 2021

There's one more thing. I think we need to add a setting (e.g. r.rmarkdown.preview.autoRefresh) to control whether to auto-render the document on file update. Real world documents could be long and heavy, we could use such a setting to control the default behavior and also put a button or menu on the editor toolbar to toggle the behavior.

Anyway, we can merge this nice PR and implement it later.

@renkun-ken
Copy link
Copy Markdown
Member

renkun-ken commented Jul 9, 2021

"Enable/Disable Auto Refresh" commands and r.rmarkdown.preview.autoRefresh are added.

image

User could control whether to auto-refresh for each preview now.

@ElianHugh
Copy link
Copy Markdown
Collaborator Author

ElianHugh commented Jul 9, 2021

Awesome, thanks for the great work! I wonder if it's worth thinking about forcing or having an option for granular caching, particularly one of the lower settings (i.e. cache = 1 or cache = 2).

@renkun-ken
Copy link
Copy Markdown
Member

Awesome, thanks for the great work! I wonder if it's worth thinking about forcing or having an option for granular caching, particularly one of the lower settings (i.e. cache = 1 or cache = 2).

Should the rmd author control the cache level or should we have control on it?

@ElianHugh
Copy link
Copy Markdown
Collaborator Author

Hmm, after some testing, it probably shouldn't be forced, even in a document preview. There are a few situations which can be confusing if caching is forced globally:

b <- 5
b + 5

Even if chunk 1 is changed later, chunk 2 will print 10.

@renkun-ken
Copy link
Copy Markdown
Member

@ElianHugh Do you have anything to add? If not, I'll merge this PR.

@ElianHugh
Copy link
Copy Markdown
Collaborator Author

No - looks good to me!

@renkun-ken renkun-ken merged commit 950bfae into REditorSupport:master Jul 9, 2021
@ManuelHentschel
Copy link
Copy Markdown
Member

The command R: Open Preview isn't working on windows. As far as I can tell, this is due to using the unqoted path here (which doesn't work for paths containing spaces), the linebreaks here, and the backslashes in the paths here.

Comment thread package.json
Comment on lines +529 to +576
{
"command": "r.markdown.showPreviewToSide",
"title": "Open Preview to the Side",
"icon": "$(open-preview)",
"category": "R"
},
{
"command": "r.markdown.showPreview",
"title": "Open Preview",
"icon": "$(open-preview)",
"category": "R"
},
{
"command": "r.markdown.preview.refresh",
"title": "Refresh Preview",
"icon": "$(refresh)",
"category": "R"
},
{
"command": "r.markdown.preview.openExternal",
"title": "Open in External Browser",
"icon": "$(link-external)",
"category": "R"
},
{
"command": "r.markdown.preview.showSource",
"title": "Show Source",
"icon": "$(go-to-file)",
"category": "R"
},
{
"title": "Toggle Style",
"category": "R",
"command": "r.markdown.preview.toggleStyle",
"icon": "$(symbol-color)"
},
{
"title": "Enable Auto Refresh",
"category": "R",
"command": "r.markdown.preview.enableAutoRefresh",
"icon": "$(sync)"
},
{
"title": "Disable Auto Refresh",
"category": "R",
"command": "r.markdown.preview.disableAutoRefresh",
"icon": "$(sync-ignored)"
},
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I'd suggest using "category": "R Markdown" for (most of) these commands, otherwise it's not quite obvious what they do, when invoking them from the command palette

Comment thread src/extension.ts
import { RMarkdownPreviewManager } from './rmarkdown/preview';

// global objects used in other files
export const extDir: string = path.join(os.homedir(), '.vscode-R');
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Should we give this a slightly longer/more descriptive name? For local variables, these short names are fine, but global variables should be clear without having to "look them up" in a different file

Comment thread src/rmarkdown/preview.ts
import { config, doWithProgress, getRpath, readContent, setContext, escapeHtml } from '../util';
import { extensionContext, tmpDir } from '../extension';

class RMarkdownPreview extends vscode.Disposable {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Personally, I find it easier to just give the class a dispose() method, instead of inheriting from vscode.Disposable (afaik, there is no difference in behaviour)

Comment thread src/rmarkdown/preview.ts
this.cp?.kill('SIGKILL');
this.panel?.dispose();
this.fileWatcher?.close();
fs.removeSync(this.outputUri.path);
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Also applies to some other places: vscode.Uri has a fsPath property which is more robust regarding windows drive letters, backslashes etc.

Comment thread src/rmarkdown/preview.ts
this.startFileWatcher(RMarkdownPreviewManager, uri);
}

public styleHtml(themeBool: boolean) {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

E.g. useDarkTheme is more descriptive than themeBool

@DonJayamanne
Copy link
Copy Markdown
Collaborator

DonJayamanne commented Jul 12, 2021

@renkun-ken @ElianHugh Apologies if this is a very silly question.
Can we not try to use the VS Codes Notebook UI to render markdown files (instead of vscodes markdown preview).
Its my understanding that this current implementation just displays the rmd file as HTML.

  • Does this current implementation execute cells or just display the markdown content & code in the HTML preview?
    If this is the case, would the new VS Code Notebook interface been beneficial? (we can have a readonly interface).
    The benefit is it will be more resource intensive & significantly performant.

@ElianHugh
Copy link
Copy Markdown
Collaborator Author

ElianHugh commented Jul 12, 2021

@renkun-ken @ElianHugh Apologies if this is a very silly question.
Can we not try to use the VS Codes Notebook UI to render markdown files (instead of vscodes markdown preview).
Its my understanding that this current implementation just displays the rmd file as HTML.

* Does this current implementation execute cells or just display the markdown content & code in the HTML preview?
  If this is the case, would the new VS Code Notebook interface been beneficial? (we can have a readonly interface).
  The benefit is it will be more resource intensive & significantly performant.

Hi @DonJayamanne, not a silly question at all :)

The current preview implementation executes all code cells, so that the output html file is essentially what the final .rmd output would be.

E.g., here I have a .rmd file on the left, and the preview on the right, executed parts of the cells in poorly scribbled red:

image

Apologies, but I'm not super familiar with the Notebook UI, so @renkun-ken would probably have a better answer re: using it for previews

@DonJayamanne
Copy link
Copy Markdown
Collaborator

DonJayamanne commented Jul 12, 2021

Thanks for the prompt response.

The current preview implementation executes all code cells, so that the output html file is essentially what the final .rmd

Awesome.
Nothing to change here. I was hoping we weren't executing the code, but looks like we are doing both, executing & generating HTML output in one step (using the cli).
More info about the notebook ui can be found here https://code.visualstudio.com/api/extension-guides/notebook

@renkun-ken
Copy link
Copy Markdown
Member

renkun-ken commented Jul 12, 2021

@renkun-ken @ElianHugh Apologies if this is a very silly question.
Can we not try to use the VS Codes Notebook UI to render markdown files (instead of vscodes markdown preview).
Its my understanding that this current implementation just displays the rmd file as HTML.

I don't quite understand the question. The rendering is all done by rmarkdown::render("file.Rmd"), we just call it in background and present the resulted html file in a WebView.

This is a just equivalent of RStudio feature.

@DonJayamanne
Copy link
Copy Markdown
Collaborator

This is a just equivalent of RStudio feature.

Thats fine.
My assumption was that we weren't executing the code and we were using this tool to generate HTML to simply preview the RMD with math renderer (markdown) correctly & felt the VS Codes Notebook UI would achieve that.

But that's not the case, hence we can consider my question resolved. Thanks

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