Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
2 changes: 1 addition & 1 deletion .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -218,7 +218,7 @@ jobs:
repository: fe-lang/fe-docs
token: ${{ secrets.DOC_DEPLOY_TOKEN }}
path: fe-docs
ref: gh-pages
ref: main

- name: Deploy docs
run: |
Expand Down
41 changes: 30 additions & 11 deletions crates/fe-web/assets/fe-doc-item.js
Original file line number Diff line number Diff line change
Expand Up @@ -38,11 +38,15 @@ var _ITEM_KIND = {
};

var _CHILD_KIND = {
field: { plural: "Fields", anchor: "field", order: 1 },
variant: { plural: "Variants", anchor: "variant", order: 0 },
method: { plural: "Methods", anchor: "tymethod", order: 4 },
assoc_type: { plural: "Associated Types", anchor: "associatedtype", order: 2 },
assoc_const: { plural: "Associated Constants", anchor: "associatedconstant", order: 3 },
field: { plural: "Fields", anchor: "field", order: 1 },
variant: { plural: "Variants", anchor: "variant", order: 0 },
method: { plural: "Methods", anchor: "tymethod", order: 6 },
assoc_type: { plural: "Associated Types", anchor: "associatedtype", order: 4 },
assoc_const: { plural: "Associated Constants", anchor: "associatedconstant", order: 5 },
// Contract-specific child kinds (emitted by crates/fe/src/extract.rs for
// Contract items — see DocChildKind::{Init,RecvHandler}).
init: { plural: "Initializer", anchor: "init", order: 2 },
recv_handler: { plural: "Message Handlers", anchor: "handler", order: 3 },
};

function _diKindStr(kind) { return (_ITEM_KIND[kind] || {}).str || kind; }
Expand Down Expand Up @@ -304,7 +308,8 @@ class FeDocItem extends HTMLElement {

_renderBreadcrumbs(item) {
var segments = item.path.split("::");
var base = this.getAttribute("base") || "";
var index = this._getIndex();
var items = (index && index.items) || [];
var html = '<nav class="breadcrumb">';
var accumulated = "";
for (var i = 0; i < segments.length; i++) {
Expand All @@ -316,9 +321,17 @@ class FeDocItem extends HTMLElement {
if (i === segments.length - 1) {
html += '<span class="breadcrumb-current">' + _diEsc(segments[i]) + "</span>";
} else {
var href = base ? base + "#" + accumulated + "/mod" : "#" + accumulated + "/mod";
html += '<a href="' + _diEsc(href) + '" class="breadcrumb-link">' +
_diEsc(segments[i]) + "</a>";
// Resolve the real kind for each ancestor segment so non-module
// containers (e.g. `msg`) link to their actual page, not `/mod`.
var suffix = "mod";
for (var k = 0; k < items.length; k++) {
if (items[k].path === accumulated) {
suffix = _diKindStr(items[k].kind);
break;
}
}
html += '<a href="' + _diEsc(this._itemHref(accumulated + "/" + suffix)) +
'" class="breadcrumb-link">' + _diEsc(segments[i]) + "</a>";
}
}
html += "</nav>";
Expand Down Expand Up @@ -371,11 +384,17 @@ class FeDocItem extends HTMLElement {
for (var j = 0; j < group.items.length; j++) {
var child = group.items[j];
var anchorId = info.anchor + "." + child.name;
var rowHref = this._anchorHref(parentUrl, anchorId);
html += '<div class="member-item" id="' + _diEsc(anchorId) + '">';
html += '<div class="member-header">';
html += '<a class="member-header-link" href="' + _diEsc(rowHref) + '">';
html += '<div class="member-header" data-row-link="true">';
html += this._renderChildSignature(child);
html += '<a href="' + this._anchorHref(parentUrl, anchorId) + '" class="anchor">\u00a7</a>';
// Nested <a> is invalid HTML, so use a <span> for the visual anchor
// glyph. Clicking anywhere in the header (including this glyph)
// navigates via the outer <a>.
html += '<span class="anchor" aria-hidden="true">\u00a7</span>';
html += "</div>";
html += "</a>";
if (child.docs) {
var childHtml = child.docs.html_body || _diEsc(child.docs.body || child.docs.summary || "");
html += '<div class="member-docs">' + childHtml + "</div>";
Expand Down
18 changes: 15 additions & 3 deletions crates/fe-web/assets/fe-doc-viewer.js
Original file line number Diff line number Diff line change
Expand Up @@ -337,12 +337,23 @@ class FeDocViewer extends HTMLElement {
? "#" + CSS.escape(anchorId) + " { background: var(--target-bg, rgba(99,102,241,0.08)); }"
: "";

// Retry a few times so the scroll lands even when the caller races the
// content render (e.g. initial page load with #path~anchor in the URL —
// _showItem rebuilds content asynchronously, so the target element may
// not exist at the first tick).
var self = this;
setTimeout(function () {
var attempts = 0;
function tryScroll() {
if (!self._contentEl) return;
var el = self._contentEl.querySelector("#" + CSS.escape(anchorId));
if (el) el.scrollIntoView({ behavior: "smooth" });
}, 100);
if (el) {
el.scrollIntoView({ behavior: "smooth", block: "start" });
return;
}
attempts++;
if (attempts < 20) setTimeout(tryScroll, 50);
}
setTimeout(tryScroll, 50);
}

// ---- SCIP Ambient Highlighting ----
Expand Down Expand Up @@ -572,6 +583,7 @@ class FeDocViewer extends HTMLElement {
mod: "module", fn: "function", struct: "struct", enum: "enum",
trait: "trait", contract: "contract", type: "type_alias",
"const": "const", impl: "impl",
msg: "msg", msg_variant: "msg_variant",
};
var kindName = kindMap[kindSuffix];
if (kindName) {
Expand Down
41 changes: 41 additions & 0 deletions crates/fe-web/assets/fe-scip-store.js
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,47 @@ function feMigrate(data) {
data.schema_version = 2;
}

if (v < 3) {
// v2 → v3: msg variants are no longer top-level DocItems; they live as
// `kind: "variant"` children of their parent msg DocItem (mirrors enum
// variants). SCIP doc_url for msg variants moved from
// `<path>/msg_variant` to `<parent>/msg~variant.<name>`.
//
// For v<3 data: drop stale top-level msg_variant items from index.items
// and rewrite any lingering `/msg_variant` SCIP doc_urls to the anchor
// form. Best-effort — consumers holding v<3 docs.json should regenerate.
if (data.index && data.index.items) {
data.index.items = data.index.items.filter(function (it) {
return it && it.kind !== "msg_variant";
});
}
if (data.scip && data.scip.symbols) {
var syms = data.scip.symbols;
for (var k in syms) {
if (!syms.hasOwnProperty(k)) continue;
var url = syms[k].doc_url;
if (!url) continue;
// /msg_variant → parent /msg~variant.<name>. Drop any legacy
// sub-anchor (e.g. ~field.x) — the router splits on the first ~,
// so a doubled tilde would produce a hash that matches no element
// ID. Migrated deep-links land on the variant row instead.
var m = url.match(/^(.*)::([^:]+)\/msg_variant(~.*)?$/);
if (m) {
syms[k].doc_url = m[1] + "/msg~variant." + m[2];
}
}
}
data.schema_version = 3;
}

if (v < 4) {
// v3 → v4: contract pages now emit `init` and `recv_handler` children
// (the init block and each recv arm). No structural rewrite is needed
// for old data; downstream consumers just won't see these rows until
// they regenerate docs.json.
data.schema_version = 4;
}

return data;
}

Expand Down
15 changes: 11 additions & 4 deletions crates/fe-web/assets/fe-search.js
Original file line number Diff line number Diff line change
Expand Up @@ -69,12 +69,18 @@ class FeSearch extends HTMLElement {
if (scip) {
try {
var results = JSON.parse(scip.search(query));
if (results.length > 0) {
for (var k = 0; k < results.length; k++) {
var r = results[k];
// Drop results without a doc_url — they would render as unclickable
// links that land back on the current page. Users hate those.
var clickable = [];
for (var ck = 0; ck < results.length; ck++) {
if (results[ck].doc_url) clickable.push(results[ck]);
}
if (clickable.length > 0) {
for (var k = 0; k < clickable.length; k++) {
var r = clickable[k];
var a = document.createElement("a");
a.className = "search-result";
a.href = "#" + (r.doc_url || "");
a.href = "#" + r.doc_url;
a.setAttribute("role", "option");

var badge = document.createElement("span");
Expand Down Expand Up @@ -104,6 +110,7 @@ class FeSearch extends HTMLElement {
module: "mod", function: "fn", struct: "struct", enum: "enum",
trait: "trait", contract: "contract", type_alias: "type",
const: "const", impl: "impl", impl_trait: "impl",
msg: "msg",
};

var q = query.toLowerCase();
Expand Down
26 changes: 22 additions & 4 deletions crates/fe-web/assets/fe-web.js
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@
const: { str: "const", plural: "Constants", order: 7 },
impl: { str: "impl", plural: "Implementations", order: 8 },
impl_trait: { str: "impl", plural: "Trait Implementations", order: 9 },
msg: { str: "msg", plural: "Messages", order: 1 },
};

function kindStr(kind) {
Expand Down Expand Up @@ -140,9 +141,14 @@
var CHILD_KIND_INFO = {
field: { plural: "Fields", anchor: "field", order: 1 },
variant: { plural: "Variants", anchor: "variant", order: 0 },
method: { plural: "Methods", anchor: "tymethod", order: 4 },
assoc_type: { plural: "Associated Types", anchor: "associatedtype", order: 2 },
assoc_const: { plural: "Associated Constants", anchor: "associatedconstant", order: 3 },
method: { plural: "Methods", anchor: "tymethod", order: 6 },
assoc_type: { plural: "Associated Types", anchor: "associatedtype", order: 4 },
assoc_const: { plural: "Associated Constants", anchor: "associatedconstant", order: 5 },
// Contract init block & recv arm handlers (see crates/fe/src/extract.rs
// DocChildKind::{Init,RecvHandler}). Kept in sync with _CHILD_KIND in
// fe-doc-item.js.
init: { plural: "Initializer", anchor: "init", order: 2 },
recv_handler: { plural: "Message Handlers", anchor: "handler", order: 3 },
};

// ============================================================================
Expand Down Expand Up @@ -313,6 +319,8 @@

function renderBreadcrumbs(item) {
var segments = item.path.split("::");
var index = window.FE_DOC_INDEX || { items: [] };
var items = index.items || [];
var html = '<nav class="breadcrumb">';
var accumulated = "";
segments.forEach(function (seg, i) {
Expand All @@ -325,7 +333,16 @@
if (isLast) {
html += '<span class="breadcrumb-current">' + esc(seg) + "</span>";
} else {
html += '<a href="' + itemHref(accumulated + "/mod") + '" class="breadcrumb-link">' + esc(seg) + "</a>";
// Use the ancestor item's real kind (e.g. `msg`) when it exists in
// the index; fall back to `mod` for pure module path segments.
var suffix = "mod";
for (var k = 0; k < items.length; k++) {
if (items[k].path === accumulated) {
suffix = kindStr(items[k].kind);
break;
}
}
html += '<a href="' + itemHref(accumulated + "/" + suffix) + '" class="breadcrumb-link">' + esc(seg) + "</a>";
}
});
html += "</nav>";
Expand Down Expand Up @@ -726,6 +743,7 @@
mod: "module", fn: "function", struct: "struct", enum: "enum",
trait: "trait", contract: "contract", type: "type_alias",
const: "const", impl: "impl",
msg: "msg", msg_variant: "msg_variant",
};
var kindName = kindMap[kindSuffix];
if (kindName) {
Expand Down
Loading
Loading