Integrate macro_railroad_ext (#183)

* Integrate macro_railroad_ext

* Fix macro_railroad loading issue

* Remove diagram modal `display:none`

* Feat

* Ignore Cargo.lock file
This commit is contained in:
Folyd 2022-08-07 21:39:39 +08:00 committed by GitHub
parent 5f8a35ebe8
commit 82379417a5
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
13 changed files with 871 additions and 2 deletions

4
.gitignore vendored
View file

@ -2,9 +2,11 @@
.DS_Store
*.crx
*.pem
Cargo.lock
web-ext-artifacts
node_modules
target
docs/public
manifest.json
extension/manage/
extension/manage/
macro_railroad/pkg

View file

@ -9,3 +9,14 @@ assert:
# Build manage pages
manage:
@cd manage && cargo run
macro-railroad: extension/script/macro-railroad-wasm.js extension/wasm/macro-railroad.wasm
extension/script/macro-railroad-wasm.js: macro-railroad/pkg/macro-railroad.js
cp $< $@
extension/wasm/macro-railroad.wasm: macro-railroad/pkg/macro-railroad.wasm
cp $< $@
macro-railroad/pkg/macro-railroad.wasm: macro-railroad/Cargo.lock macro-railroad/Cargo.toml macro-railroad/build.rs macro-railroad/src/lib.rs
cd macro-railroad && wasm-pack build -t no-modules --out-name macro-railroad && mv pkg/macro-railroad_bg.wasm pkg/macro-railroad.wasm

2
core

@ -1 +1 @@
Subproject commit 057f688d6e5db163e91ff54dbe42b48f47aa6b95
Subproject commit 84c1e30c0f163bf67abd4f0e4b6c15248a65bfab

View file

@ -0,0 +1,12 @@
<?xml version="1.0" encoding="iso-8859-1"?>
<!-- Generator: Adobe Illustrator 18.1.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Capa_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 32 32" style="enable-background:new 0 0 32 32;" xml:space="preserve">
<g>
<g>
<path style="fill:#010002;" d="M32,26V2H0v24h14v2H8v2h16v-2h-6v-2H32z M2,24V4h28v20H2z"/>
<polygon style="fill:#010002;" points="28,14 28,6 20,6"/>
<polygon style="fill:#010002;" points="4,22 12,22 4,14"/>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 600 B

View file

@ -0,0 +1 @@
<?xml version="1.0" ?><!DOCTYPE svg PUBLIC '-//W3C//DTD SVG 1.1//EN' 'http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd'><svg height="32px" id="Layer_1" style="enable-background:new 0 0 32 32;" version="1.1" viewBox="0 0 32 32" width="32px" xml:space="preserve" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><path d="M4,10h24c1.104,0,2-0.896,2-2s-0.896-2-2-2H4C2.896,6,2,6.896,2,8S2.896,10,4,10z M28,14H4c-1.104,0-2,0.896-2,2 s0.896,2,2,2h24c1.104,0,2-0.896,2-2S29.104,14,28,14z M28,22H4c-1.104,0-2,0.896-2,2s0.896,2,2,2h24c1.104,0,2-0.896,2-2 S29.104,22,28,22z"/></svg>

After

Width:  |  Height:  |  Size: 605 B

View file

@ -0,0 +1,430 @@
let wasm_bindgen;
(function() {
const __exports = {};
let wasm;
const cachedTextDecoder = new TextDecoder('utf-8', { ignoreBOM: true, fatal: true });
cachedTextDecoder.decode();
let cachedUint8Memory0 = new Uint8Array();
function getUint8Memory0() {
if (cachedUint8Memory0.byteLength === 0) {
cachedUint8Memory0 = new Uint8Array(wasm.memory.buffer);
}
return cachedUint8Memory0;
}
function getStringFromWasm0(ptr, len) {
return cachedTextDecoder.decode(getUint8Memory0().subarray(ptr, ptr + len));
}
const heap = new Array(32).fill(undefined);
heap.push(undefined, null, true, false);
let heap_next = heap.length;
function addHeapObject(obj) {
if (heap_next === heap.length) heap.push(heap.length + 1);
const idx = heap_next;
heap_next = heap[idx];
heap[idx] = obj;
return idx;
}
function getObject(idx) { return heap[idx]; }
function dropObject(idx) {
if (idx < 36) return;
heap[idx] = heap_next;
heap_next = idx;
}
function takeObject(idx) {
const ret = getObject(idx);
dropObject(idx);
return ret;
}
/**
*/
__exports.start = function() {
wasm.start();
};
/**
* @returns {any}
*/
__exports.getRailroadDefaultCss = function() {
const ret = wasm.getRailroadDefaultCss();
return takeObject(ret);
};
/**
* @returns {any}
*/
__exports.getRailroadDigramCss = function() {
const ret = wasm.getRailroadDigramCss();
return takeObject(ret);
};
let cachedInt32Memory0 = new Int32Array();
function getInt32Memory0() {
if (cachedInt32Memory0.byteLength === 0) {
cachedInt32Memory0 = new Int32Array(wasm.memory.buffer);
}
return cachedInt32Memory0;
}
const u32CvtShim = new Uint32Array(2);
const int64CvtShim = new BigInt64Array(u32CvtShim.buffer);
let WASM_VECTOR_LEN = 0;
const cachedTextEncoder = new TextEncoder('utf-8');
const encodeString = (typeof cachedTextEncoder.encodeInto === 'function'
? function (arg, view) {
return cachedTextEncoder.encodeInto(arg, view);
}
: function (arg, view) {
const buf = cachedTextEncoder.encode(arg);
view.set(buf);
return {
read: arg.length,
written: buf.length
};
});
function passStringToWasm0(arg, malloc, realloc) {
if (realloc === undefined) {
const buf = cachedTextEncoder.encode(arg);
const ptr = malloc(buf.length);
getUint8Memory0().subarray(ptr, ptr + buf.length).set(buf);
WASM_VECTOR_LEN = buf.length;
return ptr;
}
let len = arg.length;
let ptr = malloc(len);
const mem = getUint8Memory0();
let offset = 0;
for (; offset < len; offset++) {
const code = arg.charCodeAt(offset);
if (code > 0x7F) break;
mem[ptr + offset] = code;
}
if (offset !== len) {
if (offset !== 0) {
arg = arg.slice(offset);
}
ptr = realloc(ptr, len, len = offset + arg.length * 3);
const view = getUint8Memory0().subarray(ptr + offset, ptr + len);
const ret = encodeString(arg, view);
offset += ret.written;
}
WASM_VECTOR_LEN = offset;
return ptr;
}
function _assertClass(instance, klass) {
if (!(instance instanceof klass)) {
throw new Error(`expected instance of ${klass.name}`);
}
return instance.ptr;
}
/**
* Parse the given macro_rules!()-source, returns an SVG and it's preferred width
* @param {string} src
* @param {DiagramOptions} options
* @returns {Diagram}
*/
__exports.toDiagram = function(src, options) {
try {
const retptr = wasm.__wbindgen_add_to_stack_pointer(-16);
const ptr0 = passStringToWasm0(src, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
const len0 = WASM_VECTOR_LEN;
_assertClass(options, DiagramOptions);
wasm.toDiagram(retptr, ptr0, len0, options.ptr);
var r0 = getInt32Memory0()[retptr / 4 + 0];
var r1 = getInt32Memory0()[retptr / 4 + 1];
var r2 = getInt32Memory0()[retptr / 4 + 2];
if (r2) {
throw takeObject(r1);
}
return Diagram.__wrap(r0);
} finally {
wasm.__wbindgen_add_to_stack_pointer(16);
}
};
/**
*/
class Diagram {
static __wrap(ptr) {
const obj = Object.create(Diagram.prototype);
obj.ptr = ptr;
return obj;
}
__destroy_into_raw() {
const ptr = this.ptr;
this.ptr = 0;
return ptr;
}
free() {
const ptr = this.__destroy_into_raw();
wasm.__wbg_diagram_free(ptr);
}
/**
* @returns {bigint}
*/
get width() {
try {
const retptr = wasm.__wbindgen_add_to_stack_pointer(-16);
wasm.__wbg_get_diagram_width(retptr, this.ptr);
var r0 = getInt32Memory0()[retptr / 4 + 0];
var r1 = getInt32Memory0()[retptr / 4 + 1];
u32CvtShim[0] = r0;
u32CvtShim[1] = r1;
const n0 = int64CvtShim[0];
return n0;
} finally {
wasm.__wbindgen_add_to_stack_pointer(16);
}
}
/**
* @returns {string}
*/
get svg() {
try {
const retptr = wasm.__wbindgen_add_to_stack_pointer(-16);
wasm.diagram_svg(retptr, this.ptr);
var r0 = getInt32Memory0()[retptr / 4 + 0];
var r1 = getInt32Memory0()[retptr / 4 + 1];
return getStringFromWasm0(r0, r1);
} finally {
wasm.__wbindgen_add_to_stack_pointer(16);
wasm.__wbindgen_free(r0, r1);
}
}
}
__exports.Diagram = Diagram;
/**
*/
class DiagramOptions {
static __wrap(ptr) {
const obj = Object.create(DiagramOptions.prototype);
obj.ptr = ptr;
return obj;
}
__destroy_into_raw() {
const ptr = this.ptr;
this.ptr = 0;
return ptr;
}
free() {
const ptr = this.__destroy_into_raw();
wasm.__wbg_diagramoptions_free(ptr);
}
/**
* @returns {boolean}
*/
get hideInternal() {
const ret = wasm.__wbg_get_diagramoptions_hideInternal(this.ptr);
return ret !== 0;
}
/**
* @param {boolean} arg0
*/
set hideInternal(arg0) {
wasm.__wbg_set_diagramoptions_hideInternal(this.ptr, arg0);
}
/**
* @returns {boolean}
*/
get keepGroups() {
const ret = wasm.__wbg_get_diagramoptions_keepGroups(this.ptr);
return ret !== 0;
}
/**
* @param {boolean} arg0
*/
set keepGroups(arg0) {
wasm.__wbg_set_diagramoptions_keepGroups(this.ptr, arg0);
}
/**
* @returns {boolean}
*/
get foldCommonTails() {
const ret = wasm.__wbg_get_diagramoptions_foldCommonTails(this.ptr);
return ret !== 0;
}
/**
* @param {boolean} arg0
*/
set foldCommonTails(arg0) {
wasm.__wbg_set_diagramoptions_foldCommonTails(this.ptr, arg0);
}
/**
* @returns {boolean}
*/
get showLegend() {
const ret = wasm.__wbg_get_diagramoptions_showLegend(this.ptr);
return ret !== 0;
}
/**
* @param {boolean} arg0
*/
set showLegend(arg0) {
wasm.__wbg_set_diagramoptions_showLegend(this.ptr, arg0);
}
/**
*/
constructor() {
const ret = wasm.diagramoptions_new();
return DiagramOptions.__wrap(ret);
}
}
__exports.DiagramOptions = DiagramOptions;
async function load(module, imports) {
if (typeof Response === 'function' && module instanceof Response) {
if (typeof WebAssembly.instantiateStreaming === 'function') {
try {
return await WebAssembly.instantiateStreaming(module, imports);
} catch (e) {
if (module.headers.get('Content-Type') != 'application/wasm') {
console.warn("`WebAssembly.instantiateStreaming` failed because your server does not serve wasm with `application/wasm` MIME type. Falling back to `WebAssembly.instantiate` which is slower. Original error:\n", e);
} else {
throw e;
}
}
}
const bytes = await module.arrayBuffer();
return await WebAssembly.instantiate(bytes, imports);
} else {
const instance = await WebAssembly.instantiate(module, imports);
if (instance instanceof WebAssembly.Instance) {
return { instance, module };
} else {
return instance;
}
}
}
function getImports() {
const imports = {};
imports.wbg = {};
imports.wbg.__wbg_log_97290aafbe6479da = function(arg0, arg1) {
console.log(getStringFromWasm0(arg0, arg1));
};
imports.wbg.__wbindgen_string_new = function(arg0, arg1) {
const ret = getStringFromWasm0(arg0, arg1);
return addHeapObject(ret);
};
imports.wbg.__wbg_new_693216e109162396 = function() {
const ret = new Error();
return addHeapObject(ret);
};
imports.wbg.__wbg_stack_0ddaca5d1abfb52f = function(arg0, arg1) {
const ret = getObject(arg1).stack;
const ptr0 = passStringToWasm0(ret, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
const len0 = WASM_VECTOR_LEN;
getInt32Memory0()[arg0 / 4 + 1] = len0;
getInt32Memory0()[arg0 / 4 + 0] = ptr0;
};
imports.wbg.__wbg_error_09919627ac0992f5 = function(arg0, arg1) {
try {
console.error(getStringFromWasm0(arg0, arg1));
} finally {
wasm.__wbindgen_free(arg0, arg1);
}
};
imports.wbg.__wbindgen_object_drop_ref = function(arg0) {
takeObject(arg0);
};
imports.wbg.__wbindgen_throw = function(arg0, arg1) {
throw new Error(getStringFromWasm0(arg0, arg1));
};
return imports;
}
function initMemory(imports, maybe_memory) {
}
function finalizeInit(instance, module) {
wasm = instance.exports;
init.__wbindgen_wasm_module = module;
cachedInt32Memory0 = new Int32Array();
cachedUint8Memory0 = new Uint8Array();
wasm.__wbindgen_start();
return wasm;
}
function initSync(bytes) {
const imports = getImports();
initMemory(imports);
const module = new WebAssembly.Module(bytes);
const instance = new WebAssembly.Instance(module, imports);
return finalizeInit(instance, module);
}
async function init(input) {
if (typeof input === 'undefined') {
let src;
if (typeof document === 'undefined') {
src = location.href;
} else {
src = document.currentScript.src;
}
input = src.replace(/\.js$/, '_bg.wasm');
}
const imports = getImports();
if (typeof input === 'string' || (typeof Request === 'function' && input instanceof Request) || (typeof URL === 'function' && input instanceof URL)) {
input = fetch(input);
}
initMemory(imports);
const { instance, module } = await load(await input, imports);
return finalizeInit(instance, module);
}
wasm_bindgen = Object.assign(init, __exports);
})();

View file

@ -0,0 +1,72 @@
div.railroad_container {
height: auto;
max-height: 100%;
max-width: 100%;
position: relative;
}
svg.railroad {
border: 1px solid grey;
}
div.railroad_modal {
z-index: 10000;
position: fixed;
width: 100vw;
height: 100vh;
top: 0;
left: 0;
background: rgba(0, 0, 0, 0.5);
visibility: hidden;
opacity: 0;
transition: opacity 0.5s, visibility 0s 0.5s;
}
div.railroad_modal_content > svg {
margin: auto;
position: absolute;
top: 0;
bottom: 0;
left: 0;
right: 0;
max-width: 95vw;
max-height: 95vh;
border: 2px solid black;
}
div.railroad_modal.railroad_active {
opacity: 1;
visibility: visible;
transition: opacity 0.5s;
}
div.railroad_icons {
position: absolute;
bottom: 8px;
right: 8px;
}
img.railroad_icon {
opacity: 0.3;
transition: opacity 1s;
width: 25px;
}
img.railroad_icon:hover {
opacity: 1.0;
}
div.railroad_dropdown_content {
border: 1px solid black;
visibility: hidden;
position: absolute;
overflow: auto;
z-index: 1;
background-color: rgba(240,240,240,0.95);
white-space: nowrap;
padding: 5px;
}
div.railroad_dropdown_show {
visibility: visible;
}

View file

@ -0,0 +1,162 @@
document.addEventListener("DOMContentLoaded", async () => {
await load();
});
async function load() {
if (!isRustDoc()) return;
await wasm_bindgen(chrome.runtime.getURL('wasm/macro-railroad.wasm'));
injectCss();
for (let macro of document.querySelectorAll('pre.macro')) {
let parentNode = macro.parentNode;
if (!parentNode) continue;
const macroSrc = macro.innerText;
// The div that the `pre.macro` get's moved into, together with the new diagram nodes
let newNode = document.createElement('div');
newNode.setAttribute('style', 'width: 100%;');
// The container which holds the inline-svg on the page
let svgContainer = document.createElement('div');
svgContainer.setAttribute('class', 'railroad_container');
svgContainer.appendChild(document.createElement('svg'));
// Append svg container ahead macro element to prevent noisy overflow.
newNode.appendChild(svgContainer);
newNode.appendChild(macro);
let modalContainer = createModal();
newNode.appendChild(modalContainer);
const diagramOptions = new wasm_bindgen.DiagramOptions();
let iconsContainer = createIcons(macroSrc, diagramOptions);
svgContainer.appendChild(iconsContainer);
parentNode.appendChild(newNode);
updateDiagram(macroSrc, diagramOptions);
}
}
// Returns `true` if the document's generator is "rustdoc"
function isRustDoc() {
let gen = document.querySelector('head > meta[name="generator"]');
return gen && gen.getAttribute('content') === 'rustdoc';
}
// Injects the relevant CSS into the document's <head>
function injectCss() {
let head = document.head;
let rrCss = document.createElement('style');
rrCss.setAttribute('type', 'text/css');
rrCss.textContent = wasm_bindgen.getRailroadDefaultCss();
head.appendChild(rrCss);
let mrrCss = document.createElement('style');
mrrCss.setAttribute('type', 'text/css');
mrrCss.textContent = wasm_bindgen.getRailroadDigramCss();
head.appendChild(mrrCss);
}
// The modal to go fullscreen
function createModal() {
let modalContent = document.createElement('div');
modalContent.appendChild(document.createElement('svg'));
modalContent.setAttribute('class', 'railroad_modal_content');
let modalContainer = document.createElement('div');
modalContainer.appendChild(modalContent);
modalContainer.setAttribute('class', 'railroad_modal');
modalContainer.onclick = function () {
modalContainer.classList.remove('railroad_active');
};
return modalContainer;
}
// The icons in the lower-right corner, including the options-dialog
function createIcons(macroSrc, diagramOptions) {
// The icons in the bottom-right corner
let iconsContainer = document.createElement('div');
iconsContainer.setAttribute('class', 'railroad_icons');
// The options-thingy and the options
let optionsContainer = document.createElement('div');
optionsContainer.setAttribute('style', 'position: relative; display: inline');
// The container that holds the options-list
let dropdownContainer = document.createElement('div');
dropdownContainer.setAttribute('style', 'position: absolute');
dropdownContainer.setAttribute('class', 'railroad_dropdown_content');
let optionsList = document.createElement('ul');
optionsList.setAttribute('style', 'list-style-type: none; padding: 0px; margin: 0px');
const createOption = function (key, label) {
let listItem = document.createElement('li');
let inputItem = document.createElement('input');
inputItem.setAttribute('type', 'checkbox');
// Generate random id for inputItem
let randomId = Array.from(Array(8), () => Math.floor(Math.random() * 36).toString(36)).join('');
let inputItemId = `railroad_${randomId}`;
inputItem.setAttribute('id', inputItemId);
inputItem.setAttribute('checked', diagramOptions[key]);
inputItem.onchange = () => {
diagramOptions[key] = inputItem.checked;
updateDiagram(macroSrc, diagramOptions);
};
listItem.appendChild(inputItem);
let labelItem = document.createElement('label');
labelItem.setAttribute('style', 'padding-left: 8px');
labelItem.setAttribute('for', inputItemId);
labelItem.textContent = label;
listItem.appendChild(labelItem);
return listItem;
};
optionsList.appendChild(createOption('hideInternal', 'Hide macro-internal rules'));
optionsList.appendChild(createOption('keepGroups', 'Keep groups bound'));
optionsList.appendChild(createOption('foldCommonTails', 'Fold common sections'));
optionsList.appendChild(createOption('showLegend', 'Generate legend'));
dropdownContainer.appendChild(optionsList);
let optionsIcon = document.createElement('img');
optionsIcon.setAttribute('class', 'railroad_icon');
optionsIcon.setAttribute('src', chrome.runtime.getURL('assets/options.svg'));
optionsIcon.onclick = function () {
dropdownContainer.classList.toggle('railroad_dropdown_show');
};
optionsContainer.appendChild(optionsIcon);
optionsContainer.appendChild(dropdownContainer);
iconsContainer.appendChild(optionsContainer);
// The fullscreen-toggle
let fullscreenIcon = document.createElement('img');
fullscreenIcon.setAttribute('class', 'railroad_icon');
fullscreenIcon.setAttribute('src', chrome.runtime.getURL('assets/fullscreen.svg'));
fullscreenIcon.onclick = function () {
let modalContainer = document.querySelector('.railroad_modal');
modalContainer.classList.add('railroad_active');
};
iconsContainer.appendChild(fullscreenIcon);
return iconsContainer;
}
// The update function, called when options are set and to create the initial diagram
function updateDiagram(macroSrc, diagramOptions) {
let diagram = wasm_bindgen.toDiagram(macroSrc, diagramOptions);
let svgContainer = document.querySelector('div.railroad_container');
let modalContent = document.querySelector('div.railroad_modal_content');
svgContainer.replaceChild(htmlToElement(diagram.svg), svgContainer.firstChild);
svgContainer.setAttribute('style', `max-width: ${diagram.width}px`);
modalContent.replaceChild(htmlToElement(diagram.svg), modalContent.firstChild);
}
// Convert plain HTML text to element
function htmlToElement(html) {
let div = document.createElement('div');
div.innerHTML = html;
return div.firstChild
}

Binary file not shown.

31
macro-railroad/Cargo.toml Normal file
View file

@ -0,0 +1,31 @@
[package]
name = "macro_railroad_ext"
version = "0.1.0"
authors = ["Lukas Lueg <lukas.lueg@gmail.com>", "lyshuhow@gmail.com"]
build = "build.rs"
edition = "2021"
publish = false
[lib]
crate-type = ["cdylib"]
[dependencies]
wasm-bindgen = "0.2"
railroad = "0.1"
macro_railroad = "0.1"
# The `console_error_panic_hook` crate provides better debugging of panics by
# logging them with `console.error`. This is great for development, but requires
# all the `std::fmt` and `std::panicking` infrastructure, so isn't great for
# code size when deploying. However, we don't actually care all that much about
# code-size.
console_error_panic_hook = "0.1"
[build-dependencies]
built = { version = "0.5", features = ["chrono"], default_features = false }
[profile.release]
# Tell `rustc` to optimize for small code size.
opt-level = "s"
lto = true

14
macro-railroad/build.rs Normal file
View file

@ -0,0 +1,14 @@
extern crate built;
use std::env;
use std::path;
fn main() {
let mut opts = built::Options::default();
opts.set_dependencies(true);
let src = env::var("CARGO_MANIFEST_DIR").unwrap();
let dst = path::Path::new(&env::var("OUT_DIR").unwrap()).join("built.rs");
built::write_built_file_with_opts(&opts, src.as_ref(), &dst)
.expect("Failed to acquire build-time information.");
}

118
macro-railroad/src/lib.rs Normal file
View file

@ -0,0 +1,118 @@
use railroad::RailroadNode;
use wasm_bindgen::prelude::*;
#[wasm_bindgen]
extern "C" {
#[wasm_bindgen(js_namespace = console)]
fn log(s: &str);
}
#[allow(dead_code)]
mod built_info {
include!(concat!(env!("OUT_DIR"), "/built.rs"));
}
#[wasm_bindgen(start)]
pub fn start() {
// Set panic hook to get better error messages if our code ever panics.
//
// For more details see
// https://github.com/rustwasm/console_error_panic_hook#readme
std::panic::set_hook(Box::new(console_error_panic_hook::hook));
log(&format!(
"macro_railroad built {} on {} using {}",
built_info::BUILT_TIME_UTC,
built_info::RUSTC_VERSION,
built_info::DEPENDENCIES_STR
));
}
#[wasm_bindgen(js_name = getRailroadDefaultCss)]
pub fn get_railroad_default_css() -> JsValue {
railroad::DEFAULT_CSS.into()
}
#[wasm_bindgen(js_name = getRailroadDigramCss)]
pub fn get_railroad_digram_css() -> JsValue {
macro_railroad::diagram::CSS.into()
}
#[derive(Debug)]
#[wasm_bindgen]
pub struct DiagramOptions {
#[wasm_bindgen(js_name = hideInternal)]
pub hide_internal: bool,
#[wasm_bindgen(js_name = keepGroups)]
pub keep_groups: bool,
#[wasm_bindgen(js_name = foldCommonTails)]
pub foldcommontails: bool,
#[wasm_bindgen(js_name = showLegend)]
pub show_legend: bool,
}
#[wasm_bindgen]
pub struct Diagram {
#[wasm_bindgen(readonly)]
pub width: i64,
// We can't make svg pub since wasm-bindgen require each public field to be Copy.
// See rustwasm/wasm-bindgen#1985 and rustwasm/wasm-bindgen#2775.
svg: String,
}
#[wasm_bindgen]
impl Diagram {
#[wasm_bindgen(getter)]
pub fn svg(&self) -> String {
self.svg.clone()
}
}
#[wasm_bindgen]
impl DiagramOptions {
#[wasm_bindgen(constructor)]
pub fn new() -> Self {
DiagramOptions {
hide_internal: true,
keep_groups: true,
foldcommontails: true,
show_legend: true,
}
}
}
impl Default for DiagramOptions {
fn default() -> Self {
Self::new()
}
}
/// Parse the given macro_rules!()-source, returns an SVG and it's preferred width
#[wasm_bindgen(js_name = toDiagram)]
pub fn to_diagram(src: &str, options: &DiagramOptions) -> Result<Diagram, JsValue> {
let macro_rules = macro_railroad::parser::parse(src)
.map_err(|e| format!("macro_railroad parse failed: {}", e))?;
let mut tree = macro_railroad::lowering::MacroRules::from(macro_rules);
if options.hide_internal {
tree.remove_internal();
}
if !options.keep_groups {
tree.ungroup();
}
if options.foldcommontails {
tree.foldcommontails();
}
tree.normalize();
let dia = macro_railroad::diagram::into_diagram(tree, options.show_legend);
let mut svg = dia.to_string();
if svg.ends_with('\n') {
svg.pop();
}
Ok(Diagram {
width: dia.width(),
svg,
})
}

View file

@ -20,6 +20,13 @@ local json = if browser == 'chrome' then
.addWebAccessibleResources(
resources=utils.js_files('script', ['lib', 'add-search-index']),
matches=['*://docs.rs/*', '*://doc.rust-lang.org/*'],
).addWebAccessibleResources(
resources=['wasm/macro-railroad.wasm', 'assets/fullscreen.svg', 'assets/options.svg'],
matches=[
'*://docs.rs/*',
'*://doc.rust-lang.org/*',
'file:///*',
],
) {
description: 'A handy browser extension to search Rust docs and crates, etc in the address bar instantly!',
// The production extension public key to get the constant extension id during development.
@ -29,6 +36,7 @@ else
local manifest_v2 = import 'core/manifest.libsonnet';
manifest_v2.new(name, keyword, description, version)
.addWebAccessibleResources(utils.js_files('script', ['lib', 'add-search-index']))
.addWebAccessibleResources(['wasm/macro-railroad.wasm', 'assets/fullscreen.svg', 'assets/options.svg'])
.addBackgroundScripts(['migration.js', 'settings.js', 'deminifier.js'])
.addBackgroundScripts(utils.js_files('search', ['algorithm', 'book', 'crate', 'attribute', 'caniuse', 'lint']))
.addBackgroundScripts(utils.js_files('search/docs', ['base', 'crate-doc', 'rustc']))
@ -63,4 +71,12 @@ json.addIcons(icons())
matches=['*://github.com/rust-lang/rust/blob/master/RELEASES.md*'],
js=utils.js_files('script', ['lib', 'rust-lang-release']),
css=['script/github.css'],
).addContentScript(
matches=[
'*://docs.rs/*',
'*://doc.rust-lang.org/*',
'file:///*',
],
js=utils.js_files('script', ['macro-railroad', 'macro-railroad-wasm']),
css=['script/macro-railroad.css'],
)