diff --git a/lib/ui.coffee b/lib/ui.coffee index caf613e8..27b3ebea 100644 --- a/lib/ui.coffee +++ b/lib/ui.coffee @@ -31,7 +31,7 @@ module.exports = @subs.dispose() consumeInk: (@ink) -> - @views.ink = @ink + @views.activate(@ink) @selector.activate(@ink) @docpane.activate(@ink) @progress.activate(@ink) diff --git a/lib/ui/docs.js b/lib/ui/docs.js index 3f936872..59565492 100644 --- a/lib/ui/docs.js +++ b/lib/ui/docs.js @@ -2,7 +2,7 @@ import { CompositeDisposable } from 'atom' import { client } from '../connection' -const views = require('./views') +import * as views from './views' import goto from '../runtime/goto' const { diff --git a/lib/ui/views.coffee b/lib/ui/views.coffee deleted file mode 100644 index dc278d92..00000000 --- a/lib/ui/views.coffee +++ /dev/null @@ -1,137 +0,0 @@ -Highlighter = require './highlighter' - -{client} = require '../connection' -{once} = require '../misc' - -getlazy = client.import 'getlazy' - -module.exports = views = - dom: ({tag, attrs, contents}, opts) -> - view = document.createElement tag - for k, v of attrs - if v instanceof Array then v = v.join ' ' - view.setAttribute k, v - if contents? - if contents.constructor isnt Array - contents = [contents] - for child in contents - view.appendChild @render child, opts - view - - html: ({content, block = false}) -> - view = @render if block then @tags.div() else @tags.span() - view.innerHTML = content - view = if view.children.length == 1 then view.children[0] else view - - tree: ({head, children, expand}, opts) -> - @ink.tree.treeView(@render(head, opts), - children.map((x)=>@render(@tags.div([x]), opts)), - expand: expand) - - lazy: ({head, id}, opts) -> - conn = client.conn - if opts.registerLazy? - opts.registerLazy id - else - console.warn 'Unregistered lazy view' - view = @ink.tree.treeView @render(head, opts), [], - onToggle: once => - return unless client.conn == conn - getlazy(id).then (children) => - body = view.querySelector ':scope > .body' - children.map((x) => @render(@tags.div([x]), opts)).forEach (x) => - body.appendChild(@ink.ansiToHTML(x)) - - subtree: ({label, child}, opts) -> - @render (if child.type == "tree" - type: "tree" - head: @tags.span [label, child.head] - children: child.children - # children: child.children.map((x) => @tags.span "gutted", x) - else - @tags.span "gutted", [label, child]), opts - - copy: ({view, text}, opts) -> - view = @render view, opts - atom.commands.add view, - 'core:copy': (e) -> - atom.clipboard.write text - e.stopPropagation() - view - - link: ({file, line, contents}) -> - view = @render @tags.a {href: '#'}, contents - # TODO: maybe need to dispose of the tooltip onclick and readd them, but - # that doesn't seem to be necessary - if @ink.Opener.isUntitled(file) - tt = atom.tooltips.add view, title: -> 'untitled' - else - tt = atom.tooltips.add view, title: -> file - view.onclick = (e) => - @ink.Opener.open(file, line, { - pending: atom.config.get('core.allowPendingPaneItems') - }) - e.stopPropagation() - view.addEventListener 'DOMNodeRemovedFromDocument', => - tt.dispose() - view - - number: ({value, full}) -> - rounded = value.toPrecision(3) - rounded += '…' unless rounded.toString().length >= full.length - view = @render @tags.span 'syntax--constant syntax--numeric', rounded - isfull = false - view.onclick = (e) -> - view.innerText = if !isfull then full else rounded - isfull = !isfull - e.stopPropagation() - view - - code: ({text, attrs, scope}) -> - grammar = atom.grammars.grammarForScopeName("source.julia") - block = attrs?.block || false - highlighted = Highlighter.highlight(text, grammar, {scopePrefix: 'syntax--', block}) - @render {type: 'html', block, content: highlighted} - - latex: ({attrs, text}) -> - block = attrs?.block || false - latex = @ink.KaTeX.texify(text, block) - @render {type: 'html', block, content: latex} - - views: - dom: (a...) -> views.dom a... - html: (a...) -> views.html a... - tree: (a...) -> views.tree a... - lazy: (a...) -> views.lazy a... - subtree: (a...) -> views.subtree a... - link: (a...) -> views.link a... - copy: (a...) -> views.copy a... - number: (a...) -> views.number a... - code: (a...) -> views.code a... - latex: (a...) -> views.latex a... - - render: (data, opts = {}) -> - if @views.hasOwnProperty(data.type) - r = @views[data.type](data, opts) - @ink.ansiToHTML(r) - r - else if data?.constructor is String - new Text data - else - @render "julia-client: can't render #{data?.type}" - - tag: (tag, attrs, contents) -> - if attrs?.constructor is String - attrs = class: attrs - if attrs?.constructor isnt Object - [contents, attrs] = [attrs, undefined] - type: 'dom' - tag: tag - attrs: attrs - contents: contents - - tags: {} - -['div', 'span', 'a', 'strong', 'table', 'tr', 'td', 'webview'].forEach (tag) -> - views.tags[tag] = (attrs, contents) -> - views.tag tag, attrs, contents diff --git a/lib/ui/views.js b/lib/ui/views.js new file mode 100644 index 00000000..9261c237 --- /dev/null +++ b/lib/ui/views.js @@ -0,0 +1,187 @@ +'use babel' +import * as Highlighter from './highlighter'; +import client from '../connection/client'; +import { once } from '../misc'; + +const getlazy = client.import('getlazy'); + +let ink; +export function activate (ink_in) { + ink = ink_in +} + +export function dom({tag, attrs, contents}, opts) { + const view = document.createElement(tag); + for (let k in attrs) { + let v = attrs[k]; + if (v instanceof Array) { v = v.join(' '); } + view.setAttribute(k, v); + } + if (contents != null) { + if (contents.constructor !== Array) { + contents = [contents]; + } + for (let child of contents) { + view.appendChild(render(child, opts)); + } + } + return view; +} + +export function html(...args) { + const obj = args[0], + { + content + } = obj, + val = obj.block, + block = val != null ? val : false; + let view = render(block ? tags.div() : tags.span()); + view.innerHTML = content; + return view = view.children.length === 1 ? view.children[0] : view; +} + +export function tree({head, children, expand}, opts) { + ink.tree.treeView(render(head, opts), + children.map(x=> render(tags.div([x]), opts)), + {expand}); +} + +export function lazy({head, id}, opts) { + const conn = client.conn; + if (opts.registerLazy != null) { + opts.registerLazy(id); + } else { + console.warn('Unregistered lazy view'); + } + let view; + return view = ink.tree.treeView(render(head, opts), [], { + onToggle: once(() => { + if (client.conn !== conn) { return; } + getlazy(id).then(children => { + const body = view.querySelector(':scope > .body'); + children.map(x => render(tags.div([x]), opts)).forEach(x => { + body.appendChild(ink.ansiToHTML(x)); + }); + }); + }) + } + ); +} + +export function subtree({label, child}, opts) { + return render((child.type === "tree" ?{ + type: "tree", + head: tags.span([label, child.head]), + children: child.children + } + // children: child.children.map((x) => @tags.span "gutted", x) + : + tags.span("gutted", [label, child])), opts); +} + +export function copy({view, text}, opts) { + view = render(view, opts); + atom.commands.add(view, { + 'core:copy'(e) { + atom.clipboard.write(text); + e.stopPropagation(); + } + } + ); + return view; +} + +export function link({file, line, contents}) { + const view = render(tags.a({href: '#'}, contents)); + // TODO: maybe need to dispose of the tooltip onclick and readd them, but + // that doesn't seem to be necessary + let tt; + if (ink.Opener.isUntitled(file)) { + tt = atom.tooltips.add(view, {title() { return 'untitled'; }}); + } else { + tt = atom.tooltips.add(view, {title() { return file; }}); + } + view.onclick = e => { + ink.Opener.open(file, line, { + pending: atom.config.get('core.allowPendingPaneItems') + }); + e.stopPropagation(); + }; + view.addEventListener('DOMNodeRemovedFromDocument', () => { + tt.dispose(); + }); + return view; +} + +export function number({value, full}) { + let rounded = value.toPrecision(3); + if (rounded.toString().length < full.length) { rounded += '…'; } + const view = render(tags.span('syntax--constant syntax--numeric', rounded)); + let isfull = false; + view.onclick = function(e) { + view.innerText = !isfull ? full : rounded; + isfull = !isfull; + e.stopPropagation(); + }; + return view; +} + +export function code({text, attrs, scope}) { + const grammar = atom.grammars.grammarForScopeName("source.julia"); + const block = (attrs != null ? attrs.block : undefined) || false; // attrs?.block || false + const highlighted = Highlighter.highlight(text, grammar, {scopePrefix: 'syntax--', block}); + return render({type: 'html', block, content: highlighted}); +} + +export function latex({attrs, text}) { + const block = (attrs != null ? attrs.block : undefined) || false; // attrs?.block || false + const latex = ink.KaTeX.texify(text, block); + return render({type: 'html', block, content: latex}); +} + +export const views = { + // TODO do we need ...a + dom(...a) { return dom(...a); }, + html(...a) { return html(...a); }, + tree(...a) { return tree(...a); }, + lazy(...a) { return lazy(...a); }, + subtree(...a) { return subtree(...a); }, + link(...a) { return link(...a); }, + copy(...a) { return copy(...a); }, + number(...a) { return number(...a); }, + code(...a) { return code(...a); }, + latex(...a) { return latex(...a); } +} + +export function render(data, opts = {}) { + if (views.hasOwnProperty(data.type)) { + const r = views[data.type](data, opts); + ink.ansiToHTML(r); + return r; + } else if ((data != null ? data.constructor : undefined) === String) { // data?.constructor === String + return new Text(data); + } else { + return render(`julia-client: can't render ${(data != null ? data.type : undefined)}`); // data?.type + } +} + +export function tag(tag, attrs, contents) { + if ((attrs != null ? attrs.constructor : undefined) === String) { // attrs?.constructor === String + attrs = {class: attrs}; + } + if ((attrs != null ? attrs.constructor : undefined) !== Object) { // attrs?.constructor !== Object + [contents, attrs] = [attrs, undefined]; + } + return { + type: 'dom', + tag, + attrs, + contents + }; +} + +export let tags = {} +const tags_arr = ['div', 'span', 'a', 'strong', 'table', 'tr', 'td', 'webview'] +for (let _tag of tags_arr) { + tags[_tag] = (attrs, contents) => tag(_tag, attrs, contents) +}