-
Notifications
You must be signed in to change notification settings - Fork 30.3k
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
Refresh Images In Markdown Preview On Change #114083
Refresh Images In Markdown Preview On Change #114083
Conversation
d9ec8d3
to
6ab95dc
Compare
d33a79e
to
475561d
Compare
475561d
to
3c20879
Compare
3c20879
to
5fab4f9
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for the PR.
I've looked it over and provided feedback. My main question is if there is a different way to bust the browser image cache for these cases
*--------------------------------------------------------------------------------------------*/ | ||
|
||
import * as vscode from 'vscode'; | ||
import { URL } from 'url'; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This import will fail in browsers. I think you can instead what I do here:
declare const URL: typeof import('url').URL; |
This will let you use the globally defined URL
object that ships in both browsers and node
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks, I changed that. Are the tests in markdown-language-features
run in both node and browser environments?
Edit: I don't think so, otherwise CI would have detected that this import will crash.
I used to think even when VS Code runs in the browser, extensions have to be run inside a node environment.
// `src` as path, not as relative url. This is problematic for query args. | ||
const parsedUrl = new URL(url, base.toString()); | ||
const uri = vscode.Uri.parse(parsedUrl.toString()); | ||
return uri.scheme === 'file' ? uri : undefined; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We may be on a virtual file system, so we probably should not restrict this to 'file' uris. Instead you can use isWritableFilesystem
to see if a given scheme is known to VS Code
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
But createFileSystemWatcher
seems to work on file paths only, doesn't it?
I generalized the function, but changed the consumer to this:
const uri = urlToUri(src, root);
if (uri && uri.scheme === 'file' && !this._fileWatchersBySrc.has(src)) {
const watcher = vscode.workspace.createFileSystemWatcher(uri.fsPath);
// ...
}
extensions/markdown-language-features/src/features/previewContentProvider.ts
Outdated
Show resolved
Hide resolved
// image is hosted online. | ||
if (cacheKey !== undefined) { | ||
token.attrSet('src-origin', src); | ||
token.attrSet('src', tryAppendQueryArgToUrl(src, 'cacheKey', cacheKey)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks like you are using the query string make sure we don't used cached image. I wonder if VS Code should be doing this automatically though by looking at etags or similar for the resource?
Have you debugged to see why the images are being cached in the first place? Are vscode-webview-resource
request perhaps not setting the correct cache headers?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A very good idea. I changed the approach and got rid of the cache keys. Instead, responses to vscode-webview-resource
requests have an etag and a cache-control header now. Also, if-none-match
is implemented.
Basically it is a pull approach now - on every possible change, all resources with an etag are re-requested. However, responses are cached.
Before it was a push approach - only if an image actually changed, it got re-requested.
The advantages of this vs cache keys are:
- More natural code
- Does work for arbitrary webviews
- A manual preview refresh will reload resources from virtual file systems if they have an etag.
The disadvantages are:
- Does only refresh images if the webview is reloaded (I guess reloading the entire markdown preview webview is never going to be problematic, so it is fine)
- Resources with etags are not cached anymore with this change. Repeated access to such resources will lead to repeated requests. Most of them will be answered with 304 (not modified) though. If computing the etag is slow, this is going to be problematic.
try { | ||
// `vscode.Uri.joinPath` cannot be used, since it understands | ||
// `src` as path, not as relative url. This is problematic for query args. | ||
const parsedUrl = new URL(url, base.toString()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We've had lots of problems in the past around encoding/decoding of special characters in uris. Make sure to add tests for cases where the uri has percent encoded characters
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I added some tests with percent characters. I don't know the spec though and what pitfalls there are.
*/ | ||
export function tryAppendQueryArgToUrl(url: string, name: string, arg: string): string { | ||
try { | ||
const parsedUrl = new URL(url, 'file://'); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same as above, we may be on a virtual file system so generally we can't assume that we are on a file://
.
I think it's fine if you want to restrict the first version of the feature to file
uris, but if that's the case, make sure the function names make this clear
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This function is now obsolete.
Anyways, as long as the file system watcher does not support virtual file systems (i.e. accepts vscode.Uri
s instead of the file path based glob), the file://
protocol is the only one that can be supported afaik.
Thanks for your feedback! However, if The file system watcher is still required to trigger a preview refresh. |
Thanks! The Etag usage looks much cleaner and should help other webviews as well. This change should be in the next insiders build |
This PR fixes #65258.
The tests are still WIP as I haven't been able to figure out how to run them.Running
./scripts/test
ended witherrorlevel: 0
.yarn run mocha --run extensions\markdown-language-features\src\test\engine.test.ts
failed as it could not find the javascript source.yarn run mocha --run extensions\markdown-language-features\dist\test\engine.test.js
failed with "exports
is not defined".After seeing the CI, I noticed that it is an integration test and then found the launch configuration of the markdown test.
Demo:
Key of this implementation is this code: