Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
cb73f23
fmt
AbdulrhmnGhanem Jul 1, 2021
820d3b0
Add export button and recieve its message.
AbdulrhmnGhanem Jul 2, 2021
b635d60
Add export plot functionality to `Plots` plots.
AbdulrhmnGhanem Jul 3, 2021
3590b5d
Add export plot functionality to `Plotly` plots.
AbdulrhmnGhanem Jul 3, 2021
ae0cc30
Add export plot functionality to `Vega` plots.
AbdulrhmnGhanem Jul 3, 2021
a6911a7
Add export plot functionality to `png`/`gif` plots.
AbdulrhmnGhanem Jul 3, 2021
5304922
Remove Plotly builtin export button.
AbdulrhmnGhanem Jul 3, 2021
0226111
Add copy to clipboard functionality.
AbdulrhmnGhanem Jul 4, 2021
8eb8418
eslint
AbdulrhmnGhanem Jul 4, 2021
99e30d4
Run eslint on js files.
AbdulrhmnGhanem Jul 4, 2021
6887b74
Change save button text to `Save Plot`.
AbdulrhmnGhanem Jul 4, 2021
90beaf3
Update CHANGELOG.md
AbdulrhmnGhanem Jul 4, 2021
753d2e2
fixup! Add copy to clipboard functionality.
AbdulrhmnGhanem Jul 4, 2021
0423861
Rename `language-julia.export-plot` -> `language-julia.save-plot`.
AbdulrhmnGhanem Jul 5, 2021
1a0fcdd
clean up
AbdulrhmnGhanem Jul 5, 2021
e85da29
Use browser clipboard intead of the native one.
AbdulrhmnGhanem Jul 8, 2021
ebe67e9
Remove clipboard script.
AbdulrhmnGhanem Jul 8, 2021
6c4ecc2
Merge branch 'master' into export-plot
AbdulrhmnGhanem Jul 8, 2021
966a976
Remove temp-dir.
AbdulrhmnGhanem Jul 8, 2021
824acb4
Use `SaveDialog` to choose plot path.
AbdulrhmnGhanem Jul 21, 2021
f710ca1
Show warning if saving plot failed.
AbdulrhmnGhanem Jul 21, 2021
cc5586b
Show warning if copying plot failed.
AbdulrhmnGhanem Jul 21, 2021
de9c95b
Merge branch 'master' into export-plot
AbdulrhmnGhanem Jul 21, 2021
e8aeb05
Revert code formatting.
AbdulrhmnGhanem Jul 21, 2021
c733d67
Merge remote-tracking branch 'origin/master' into export-plot
AbdulrhmnGhanem Sep 4, 2021
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@ All notable changes to the Julia extension will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).

## [Unreleased]
### Added
* Export Plot(save/copy) buttons to plot pane([#2267](https://github.com/julia-vscode/julia-vscode/pull/2267))

### Fixed
* Fixed REPL stacktraces file path links for Windows. Paths with tilda symbol now expand to the correct HOMEPATH. Paths with spaces are handled correctly ([#2261](https://github.com/julia-vscode/julia-vscode/pull/2261))

Expand Down
41 changes: 41 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,16 @@
"title": "Julia: Delete plot",
"icon": "$(trash)"
},
{
"command": "language-julia.copy-plot",
"title": "Julia: Copy Plot",
"icon": "$(explorer-view-icon)"
},
{
"command": "language-julia.save-plot",
"title": "Julia: Save Plot",
"icon": "$(save-as)"
},
{
"command": "language-julia.plotpane-delete-all",
"title": "Julia: Delete All Plots"
Expand Down Expand Up @@ -369,6 +379,16 @@
"command": "language-julia.plotpane-delete",
"group": "navigation@0"
},
{
"when": "jlplotpaneFocus",
"command": "language-julia.copy-plot",
"group": "navigation@1"
},
{
"when": "jlplotpaneFocus",
"command": "language-julia.save-plot",
"group": "navigation@1"
},
{
"when": "jlplotpaneFocus",
"command": "language-julia.plotpane-next",
Expand Down Expand Up @@ -426,6 +446,14 @@
"when": "false",
"command": "language-julia.plotpane-delete"
},
{
"when": "false",
"command": "language-julia.copy-plot"
},
{
"when": "false",
"command": "language-julia.save-plot"
},
{
"when": "false",
"command": "language-julia.plotpane-next"
Expand Down Expand Up @@ -644,6 +672,14 @@
"key": "delete",
"when": "jlplotpaneFocus"
},
{
"command": "language-julia.copy-plot",
"when": "jlplotpaneFocus"
},
{
"command": "language-julia.save-plot",
"when": "jlplotpaneFocus"
},
{
"command": "language-julia.plotpane-delete-all",
"key": "shift+delete",
Expand Down Expand Up @@ -1049,6 +1085,11 @@
"description": "Functions or modules that are set to compiled mode when setting the defaults.",
"scope": "window"
},
"julia.plots.path": {
"type": "string",
"description": "The output directory to save plots to",
"scope": "window"
},
"julia.notebookController": {
"type": "boolean",
"default": false,
Expand Down
156 changes: 137 additions & 19 deletions scripts/plots/main_plot_webview.js
Original file line number Diff line number Diff line change
@@ -1,42 +1,160 @@
'use strict'

const vscode = acquireVsCodeApi()

function postMessageToHost(type, val) {
function postMessageToHost(type, value) {
if (type) {
vscode.postMessage({
type: type,
value: val
type,
value,
})
}
}

function getPlotElement() {
let plot_element = document.getElementById('plot-element')
const plot_element = document.getElementById('plot-element')
if (!plot_element) {
return document.getElementsByTagName('body')[0]
}

let canvas = plot_element.getElementsByTagName('canvas')[0]
if (canvas) {
return canvas
} else {
return plot_element
}
const canvas = plot_element.getElementsByTagName('canvas')[0]
return canvas ?? plot_element
}

let interval
function getImage() {
const plot = getPlotElement()
let width = plot.offsetWidth
let height = plot.offsetHeight

html2canvas(plot, { height, width }).then((canvas) => {
postMessageToHost('thumbnail', canvas.toDataURL('png'))
clearInterval(interval)
}, (reason) => {
console.error('Error in taking thumbnail: ', reason)
})
const width = plot.offsetWidth
const height = plot.offsetHeight

html2canvas(plot, { height, width }).then(
(canvas) => {
postMessageToHost('thumbnail', canvas.toDataURL('png'))
clearInterval(interval)
},
(reason) => {
console.error('Error in taking thumbnail: ', reason)
}
)
}

function isPlotly() {
return document.querySelector('#plot-element .plotly') !== null
}

function isVega() {
return document.querySelector('#plot-element.vega-embed') !== null
}

const SAVE_PLOT_MESSAGE_TYPE = 'savePlot'
const REQUEST_SAVE_PLOT_TYPE = 'requestSavePlot'
const REQUEST_COPY_PLOT_TYPE = 'requestCopyPlot'
const COPY_FAILED_MESSAGE_TYPE = 'copyFailed'

/**
* Fires when a plot request(save/copy) is received, sends a message to the host with
* i. The plot data url,
* ii. The index of the plot.
* @param {number} index
*/
function handlePlotSaveRequest(index) {
const plot = getPlotElement()
if (isPlotly()) {
Plotly.Snapshot.toImage(plot, { format: 'svg' }).once('success', (url) => {
const svg = decodeURIComponent(url).replace(/data:image\/svg\+xml,/, '')

postMessageToHost(SAVE_PLOT_MESSAGE_TYPE, { svg, index })
})
} else if (isVega()) {
const svg = document.querySelector('#plot-element svg').outerHTML

postMessageToHost(SAVE_PLOT_MESSAGE_TYPE, { svg, index })
} else {
const { src } = plot

const svg = src.includes('image/svg')
? decodeURIComponent(src).replace(/data:image\/svg\+xml,/, '')
: null
const png = src.includes('image/png')
? src.replace(/data:image\/png;base64,/, '')
: null
const gif = src.includes('image/gif')
? src.replace(/data:image\/gif;base64,/, '')
: null

postMessageToHost(SAVE_PLOT_MESSAGE_TYPE, { svg, png, gif, index })
}
}

function handlePlotCopyRequest() {
const plot = document.querySelector('svg') || getPlotElement()
const isSvg = document.querySelector('svg') !== null

const width = plot.offsetWidth
const height = plot.offsetHeight

if (isSvg) {
const canvas = document.createElement('canvas')
const ctx = canvas.getContext('2d')

const image = new Image()
const data = new XMLSerializer().serializeToString(plot)
const blob = new Blob([data], { type: 'image/svg+xml;charset=utf-8' })
const url = window.URL.createObjectURL(blob)

image.onload = () => {
canvas.width = image.naturalWidth
canvas.height = image.naturalHeight
ctx.drawImage(image, 0, 0)
window.URL.revokeObjectURL(url)

canvas.toBlob((blob) => {
navigator.clipboard.write([
new ClipboardItem({
[blob.type]: blob,
}),
])
})
}
image.src = url
} else {
html2canvas(plot, { height, width }).then(
(canvas) => {
canvas.toBlob((blob) => {
navigator.clipboard.write([
new ClipboardItem({
[blob.type]: blob,
}),
])
})
},
(reason) => {
postMessageToHost(COPY_FAILED_MESSAGE_TYPE)
console.error(new Error(reason))
}
)
}
}

window.addEventListener('load', getImage)
window.addEventListener('load', () => {
// Remove Plotly builtin export button; it's nonfunctional in VSCode and can confuse users.
document.querySelector(
'[data-title="Download plot as a png"]'
).style.display = 'none'
})

window.addEventListener('message', ({ data }) => {
switch (data.type) {
case REQUEST_SAVE_PLOT_TYPE:
handlePlotSaveRequest(data.body.index)
break
case REQUEST_COPY_PLOT_TYPE:
handlePlotCopyRequest()
break
default:
console.error(new Error('Unknown plot request!'))
}
})

interval = setInterval(getImage, 1000)
Loading