From b7cb5dde267ec8d508e4cdc55d8d087c05cc9af5 Mon Sep 17 00:00:00 2001 From: Alex Carney Date: Sun, 29 Sep 2024 22:47:34 +0100 Subject: [PATCH] sphinx-agent: Rewrite internal links As part of its initial setup, the injected `webview.js` script now rewrites any `a.internal` links to include the port number of the current websocket connection. This ensures that as the user navigates by clicking on links on the page the websocket connection to the language server is preserved. Also by doing an initial scroll sync on page load, this ensures that the editor is kept in sync with the change! There is a chance for this to be a bit flaky as this in direct conflict with the initial sync an editor might want to make if it initiates the preview of a page. By introducing a small delay on the sync made by the webview, we are relying on the editor winning the race and getting its message in first... I'm sure that will never cause an issue in the future! --- lib/esbonio/changes/704.enhancement.md | 1 + lib/esbonio/changes/906.fix.md | 4 +- .../esbonio/sphinx_agent/static/webview.js | 78 ++++++++++++++----- 3 files changed, 64 insertions(+), 19 deletions(-) create mode 100644 lib/esbonio/changes/704.enhancement.md diff --git a/lib/esbonio/changes/704.enhancement.md b/lib/esbonio/changes/704.enhancement.md new file mode 100644 index 000000000..c6555312d --- /dev/null +++ b/lib/esbonio/changes/704.enhancement.md @@ -0,0 +1 @@ +When clicking on internal links of a previewed page, the corresponding source file will be automatically opened in the editor diff --git a/lib/esbonio/changes/906.fix.md b/lib/esbonio/changes/906.fix.md index 218dfe35d..7fd2cc9de 100644 --- a/lib/esbonio/changes/906.fix.md +++ b/lib/esbonio/changes/906.fix.md @@ -1 +1,3 @@ -The `esbonio.preview.showLineMarkers` option should now work again +The `esbonio.preview.showLineMarkers` option should now work again. + +When clicking on internal links of a previewed page, the websocket connection to the language server is now preserved. diff --git a/lib/esbonio/esbonio/sphinx_agent/static/webview.js b/lib/esbonio/esbonio/sphinx_agent/static/webview.js index e18dacb6c..c659f530b 100644 --- a/lib/esbonio/esbonio/sphinx_agent/static/webview.js +++ b/lib/esbonio/esbonio/sphinx_agent/static/webview.js @@ -2,6 +2,60 @@ // which allows the webpage to talk with the preview server and coordinate details such as refreshes // and page scrolling. +/** + * Rewrite internal links so that the link between the webview and + * language server is maintained across pages. + */ +function rewriteInternalLinks(wsPort) { + if (!wsPort) { + return + } + + const links = Array.from(document.querySelectorAll("a.internal")) + + for (let link of links) { + let uri + try { + uri = new URL(link.href) + } catch (err) { + console.debug(`Skipping link ${link.href}, ${err}`) + continue + } + + if (!uri.search) { + uri.search = `?ws=${wsPort}` + } else if (!uri.searchParams.get('ws')) { + uri.search += `&ws=${wsPort}` + } + + link.href = uri.toString() + } +} + +/** + * Sync the webview's scroll position with the editor + */ +function syncScrollPosition() { + const target = findEditorScrollTarget() + if (!target) { + console.debug('No target found') + return + } + + const uri = target[0] + const line = target[1] + + if (!uri || !line) { + console.debug('Missing uri or line') + return + } + + // TODO: Rate limits. + sendMessage( + { jsonrpc: "2.0", method: "editor/scroll", params: { uri: uri, line: line } } + ) +} + /** * Get the uri and line number of the given marker * @@ -117,7 +171,7 @@ function scrollViewTo(uri, linum) { const t = (linum - previousLine) / Math.max(currentLine - previousLine, 1) const y = (1 - t) * previousPos + t * currentPos - console.table({line: linum, previous: previousLine, current: currentLine, t: t, y: y}) + // console.table({line: linum, previous: previousLine, current: currentLine, t: t, y: y}) window.scrollTo(0, y - 60) } @@ -216,29 +270,15 @@ function handle(message) { } window.addEventListener("scroll", (event) => { - const target = findEditorScrollTarget() - if (!target) { - return - } - - const uri = target[0] - const line = target[1] - - if (!uri || !line) { - return - } - - // TODO: Rate limits. - sendMessage( - { jsonrpc: "2.0", method: "editor/scroll", params: { uri: uri, line: line } } - ) - + syncScrollPosition() }) // Connection opened socket.addEventListener("open", (event) => { console.debug("Connected.") connected = true + + setTimeout(syncScrollPosition, 50) }); // Listen for messages @@ -251,6 +291,8 @@ function main() { renderLineMarkers() } + rewriteInternalLinks(ws) + // Are we in an