mirror of
https://github.com/Mercury-Language/mercury.git
synced 2026-04-15 17:33:38 +00:00
browser/browse.m:
Add save_and_browse_browser_term_web to be called when
"browse --web" is entered at the mdb prompt.
Add browser_term_to_html_flat_string, a helper predicate for
term_to_html.
Make portray_flat_write_browser_term work take a stream parameter
instead of writing to the current output stream. It is called by
browser_term_to_html_flat_string, writing to a string builder
stream.
browser/browser_info.m:
Add web_browser_cmd field to browser_persistent_state.
browser/mdb.m:
browser/term_to_html.m:
Add new module to generate an HTML document. The document contains a
JavaScript represention of a Mercury term.
(The JavaScript string escaping code is adapted from Julien's
mercury-json project.)
browser/percent_encoding.m:
Add new module to perform percent-encoding.
scripts/mdb_term_browser.css:
scripts/mdb_term_browser.js:
Add JavaScript and CSS files referenced by the generated HTML file
to create a tree view of a Mercury term using jstree.
scripts/32px.png:
scripts/40px.png:
scripts/throbber.gif:
scripts/jstree.min.js:
scripts/jstree.style.min.css:
Add local copy of jstree files <https://www.jstree.com/>
scripts/jquery.slim.min.js:
Add local copy of jquery <https://jquery.com/>
scripts/Mmakefile:
Install the new files into the same directory as mdbrc and other
mdb-related files.
trace/mercury_trace_browse.c:
trace/mercury_trace_browse.h:
trace/mercury_trace_cmd_browsing.c:
trace/mercury_trace_cmd_parameter.c:
trace/mercury_trace_cmd_parameter.h:
trace/mercury_trace_internal.c:
Add "browse --web" and "web_browser_cmd" commands.
doc/user_guide.texi:
Document "browse --web" and "web_browser_cmd" commands.
configure.ac:
scripts/mdbrc.in:
Set a reasonable default command to launch a web browser from mdb.
(Only tested on Linux.)
NEWS:
Announce the new feature.
.README.in:
Mention jquery and jstree licensing.
tests/debugger/Mmakefile:
tests/debugger/completion.exp:
tests/debugger/mdb_command_test.inp:
tests/debugger/save.exp2:
Update debugger tests for new commands.
239 lines
6.3 KiB
JavaScript
239 lines
6.3 KiB
JavaScript
// Copyright (C) 2017 The Mercury team.
|
|
// This file may only be copied under the terms of the GNU Library General
|
|
// Public License - see the file COPYING.LIB in the Mercury distribution.
|
|
|
|
const OPEN_DEPTH = 3;
|
|
const OPEN_MAX_ARGS = 10;
|
|
|
|
function escapeHTML(html) {
|
|
return document.createElement('div')
|
|
.appendChild(document.createTextNode(html))
|
|
.parentNode.innerHTML;
|
|
}
|
|
|
|
function term_functor_to_html(term)
|
|
{
|
|
return escapeHTML(term.functor);
|
|
}
|
|
|
|
function term_oneline_to_html(term)
|
|
{
|
|
if (term.oneline) {
|
|
return escapeHTML(term.oneline);
|
|
} else {
|
|
return term_functor_to_html(term);
|
|
}
|
|
}
|
|
|
|
function term_to_json(term)
|
|
{
|
|
const field = {
|
|
name: "term",
|
|
term: term
|
|
};
|
|
return field_to_json(field, 0, true);
|
|
}
|
|
|
|
function field_to_json(field, open_depth, root)
|
|
{
|
|
const name_html = field_name_to_html(field.name);
|
|
const term = field.term;
|
|
|
|
var initial_text;
|
|
var initial_open;
|
|
var children;
|
|
var userdata = {}
|
|
if (term.args) {
|
|
userdata.opened_text = name_html + term_functor_to_html(term);
|
|
userdata.closed_text = name_html + term_oneline_to_html(term);
|
|
initial_open = false;
|
|
if (term.oneline_elided) {
|
|
if (root) {
|
|
initial_open = true;
|
|
} else {
|
|
initial_open = (open_depth < OPEN_DEPTH &&
|
|
term.args.length < OPEN_MAX_ARGS);
|
|
}
|
|
}
|
|
if (initial_open) {
|
|
initial_text = userdata.opened_text;
|
|
} else {
|
|
initial_text = userdata.closed_text;
|
|
}
|
|
var new_depth;
|
|
if (initial_open) {
|
|
new_depth = open_depth + 1;
|
|
} else {
|
|
// Reset depth so that opening a closed node reveals
|
|
// some of its children as well.
|
|
new_depth = 0;
|
|
}
|
|
children = term.args.map(function(arg) {
|
|
return field_to_json(arg, new_depth, false);
|
|
});
|
|
} else {
|
|
initial_text = name_html + term_oneline_to_html(term);
|
|
initial_open = false;
|
|
children = false;
|
|
}
|
|
|
|
if (typeof(field.name) == "string") {
|
|
userdata.search_name = field.name;
|
|
}
|
|
userdata.search_value = term.functor.toString();
|
|
|
|
return {
|
|
text: initial_text,
|
|
state: {opened: initial_open},
|
|
children: children,
|
|
a_attr: {title: term.type},
|
|
data: userdata
|
|
};
|
|
}
|
|
|
|
function field_name_to_html(name)
|
|
{
|
|
if (typeof(name) == "number") {
|
|
return '<span class="pos">[' + name + ']</span>';
|
|
} else {
|
|
return '<span class="name">' + escapeHTML(name) + '</span>';
|
|
}
|
|
}
|
|
|
|
function short_search_callback(s, node)
|
|
{
|
|
const userdata = node.data;
|
|
if (userdata.search_name && userdata.search_name.startsWith(s)) {
|
|
return true;
|
|
}
|
|
return userdata.search_value.startsWith(s);
|
|
}
|
|
|
|
function long_search_callback(s, node)
|
|
{
|
|
const userdata = node.data;
|
|
if (userdata.search_name && userdata.search_name.indexOf(s) !== -1) {
|
|
return true;
|
|
}
|
|
return userdata.search_value.indexOf(s) !== -1;
|
|
}
|
|
|
|
function choose_search_callback(s)
|
|
{
|
|
if (s.length < 3) {
|
|
return short_search_callback;
|
|
} else {
|
|
return long_search_callback;
|
|
}
|
|
}
|
|
|
|
function setup(term)
|
|
{
|
|
const treeview = $('#treeview');
|
|
|
|
var term_stack = [term_to_json(term)];
|
|
|
|
// Create jstree instance.
|
|
treeview.jstree({
|
|
core: {
|
|
data: [term_stack[0]],
|
|
check_callback: true, // enable modifications (including rename)
|
|
multiple: false, // no multiple selection
|
|
animation: 0, // no animation
|
|
themes: {icons: false} // no icons
|
|
},
|
|
plugins: ["search", "contextmenu"],
|
|
search: {
|
|
search_callback: short_search_callback
|
|
},
|
|
contextmenu: {
|
|
select_node: false,
|
|
items: {
|
|
expand_all: {
|
|
label: "Expand all",
|
|
action: expand_all_action
|
|
},
|
|
collapse_all: {
|
|
label: "Collapse all",
|
|
action: collapse_all_action
|
|
},
|
|
view_subterm: {
|
|
label: "View subterm",
|
|
action: view_subterm_action
|
|
},
|
|
back: {
|
|
label: "Back to previous term",
|
|
action: back_action
|
|
}
|
|
}
|
|
}
|
|
});
|
|
|
|
// Keep reference to jstree instance.
|
|
const inst = treeview.jstree(true);
|
|
|
|
function expand_all_action(data) {
|
|
const obj = inst.get_node(data.reference);
|
|
inst.open_all(obj);
|
|
}
|
|
function collapse_all_action(data) {
|
|
const obj = inst.get_node(data.reference);
|
|
inst.close_all(obj);
|
|
}
|
|
function view_subterm_action(data) {
|
|
const obj = inst.get_node(data.reference);
|
|
// This seems to work...
|
|
inst.move_node(obj, treeview, 0);
|
|
inst.delete_node(inst.get_next_dom(obj, true));
|
|
|
|
term_stack.push(inst.get_json(obj));
|
|
}
|
|
function back_action(data) {
|
|
if (term_stack.length > 1) {
|
|
term_stack.pop();
|
|
inst.settings.core.data = term_stack[term_stack.length - 1];
|
|
inst.refresh();
|
|
}
|
|
}
|
|
|
|
function on_open_node(e, data) {
|
|
const node = data.node;
|
|
const userdata = node.data;
|
|
inst.rename_node(node, userdata.opened_text);
|
|
}
|
|
function on_close_node(e, data) {
|
|
const node = data.node;
|
|
const userdata = node.data;
|
|
inst.rename_node(node, userdata.closed_text);
|
|
}
|
|
function on_select_node(e, data) {
|
|
const node = data.node;
|
|
inst.toggle_node(node);
|
|
}
|
|
treeview.on('open_node.jstree', on_open_node);
|
|
treeview.on('close_node.jstree', on_close_node);
|
|
treeview.on('select_node.jstree', on_select_node);
|
|
|
|
const searchbox = $('#searchbox');
|
|
var timeout = false;
|
|
searchbox.keyup(function() {
|
|
if (timeout) {
|
|
clearTimeout(timeout);
|
|
}
|
|
timeout = setTimeout(
|
|
function() {
|
|
const v = searchbox.val();
|
|
inst.settings.search.search_callback =
|
|
choose_search_callback(v);
|
|
inst.search(v);
|
|
},
|
|
250
|
|
);
|
|
});
|
|
}
|
|
|
|
$(document).ready(function() {
|
|
setup(term);
|
|
term = null; // don't need it any more
|
|
});
|