mirror of
https://codeberg.org/ziglang/zig.git
synced 2025-12-06 05:44:20 +00:00
Merge 13b921f6ff into 9082b004b6
This commit is contained in:
commit
caf5af99be
5 changed files with 248 additions and 49 deletions
111
lib/docs/main.js
111
lib/docs/main.js
|
|
@ -56,6 +56,9 @@
|
|||
const domErrors = document.getElementById("errors");
|
||||
const domErrorsText = document.getElementById("errorsText");
|
||||
|
||||
// Chosen to prevent collisions with the IDs above.
|
||||
const navPrefix = "nav_";
|
||||
|
||||
var searchTimer = null;
|
||||
|
||||
const curNav = {
|
||||
|
|
@ -67,6 +70,8 @@
|
|||
decl: null,
|
||||
// string file name matching tarball path
|
||||
path: null,
|
||||
// string decl path within source file
|
||||
scrollToDeclPath: null,
|
||||
|
||||
// when this is populated, pressing the "view source" command will
|
||||
// navigate to this hash.
|
||||
|
|
@ -74,7 +79,9 @@
|
|||
};
|
||||
var curNavSearch = "";
|
||||
var curSearchIndex = -1;
|
||||
var imFeelingLucky = false;
|
||||
// When true: load the search result indicated by `curSearchIndex`, or the first result if
|
||||
// `curSearchIndex` isn't valid.
|
||||
var loadSearchResult = false;
|
||||
|
||||
// names of modules in the same order as wasm
|
||||
const moduleList = [];
|
||||
|
|
@ -185,7 +192,7 @@
|
|||
} else {
|
||||
return renderDecl(curNav.decl);
|
||||
}
|
||||
case 2: return renderSource(curNav.path);
|
||||
case 2: return renderSource(curNav.path, curNav.scrollToDeclPath);
|
||||
default: throw new Error("invalid navigation state");
|
||||
}
|
||||
}
|
||||
|
|
@ -229,22 +236,43 @@
|
|||
}
|
||||
}
|
||||
|
||||
function renderSource(path) {
|
||||
const decl_index = findFileRoot(path);
|
||||
if (decl_index == null) return renderNotFound();
|
||||
function renderSource(path, scroll_to_decl_path) {
|
||||
const root_index = findFileRoot(path);
|
||||
if (root_index == null) return renderNotFound();
|
||||
|
||||
renderNavFancy(decl_index, [{
|
||||
renderNavFancy(root_index, [{
|
||||
name: "[src]",
|
||||
href: location.hash,
|
||||
}]);
|
||||
|
||||
domSourceText.innerHTML = declSourceHtml(decl_index);
|
||||
|
||||
domSourceText.innerHTML = declSourceHtml(root_index, true);
|
||||
domSectSource.classList.remove("hidden");
|
||||
|
||||
const scroll_to_decl_index = findDeclPathInNamespace(root_index, scroll_to_decl_path);
|
||||
if (scroll_to_decl_index !== null) {
|
||||
const to_elem = document.getElementById(navPrefix + scroll_to_decl_index);
|
||||
if (to_elem != null) {
|
||||
// Needs a delay, else the DOM hasn't been fully updated and the scroll does nothing.
|
||||
setTimeout(function() {to_elem.scrollIntoView();}, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function renderDeclHeading(decl_index) {
|
||||
curNav.viewSourceHash = "#src/" + unwrapString(wasm_exports.decl_file_path(decl_index));
|
||||
const is_root = wasm_exports.decl_is_root(decl_index);
|
||||
if (!is_root) {
|
||||
// E.g. if `decl_index` corresponds to `root.foo.bar` we want `foo.bar`
|
||||
var subcomponents = [];
|
||||
let decl_it = decl_index;
|
||||
while (decl_it != null) {
|
||||
subcomponents.push(declIndexName(decl_it));
|
||||
decl_it = declParent(decl_it);
|
||||
}
|
||||
subcomponents.pop();
|
||||
subcomponents.reverse();
|
||||
curNav.viewSourceHash += ":" + subcomponents.join(".");
|
||||
}
|
||||
|
||||
const hdrNameSpan = domHdrName.children[0];
|
||||
const srcLink = domHdrName.children[1];
|
||||
|
|
@ -389,7 +417,7 @@
|
|||
if (members.length !== 0 || fields.length !== 0) {
|
||||
renderNamespace(decl_index, members, fields);
|
||||
} else {
|
||||
domSourceText.innerHTML = declSourceHtml(decl_index);
|
||||
domSourceText.innerHTML = declSourceHtml(decl_index, false);
|
||||
domSectSource.classList.remove("hidden");
|
||||
}
|
||||
}
|
||||
|
|
@ -419,7 +447,7 @@
|
|||
renderErrorSet(base_decl, errorSetNodeList(decl_index, errorSetNode));
|
||||
}
|
||||
|
||||
domSourceText.innerHTML = declSourceHtml(decl_index);
|
||||
domSourceText.innerHTML = declSourceHtml(decl_index, false);
|
||||
domSectSource.classList.remove("hidden");
|
||||
}
|
||||
|
||||
|
|
@ -433,7 +461,7 @@
|
|||
domTldDocs.classList.remove("hidden");
|
||||
}
|
||||
|
||||
domSourceText.innerHTML = declSourceHtml(decl_index);
|
||||
domSourceText.innerHTML = declSourceHtml(decl_index, false);
|
||||
domSectSource.classList.remove("hidden");
|
||||
}
|
||||
|
||||
|
|
@ -620,6 +648,7 @@
|
|||
curNav.tag = 0;
|
||||
curNav.decl = null;
|
||||
curNav.path = null;
|
||||
curNav.scrollToDeclPath = null;
|
||||
curNav.viewSourceHash = null;
|
||||
curNavSearch = "";
|
||||
|
||||
|
|
@ -638,7 +667,13 @@
|
|||
const source_mode = nonSearchPart.startsWith("src/");
|
||||
if (source_mode) {
|
||||
curNav.tag = 2;
|
||||
curNav.path = nonSearchPart.substring(4);
|
||||
const idpos = nonSearchPart.indexOf(":");
|
||||
if (idpos === -1) {
|
||||
curNav.path = nonSearchPart.substring(4);
|
||||
} else {
|
||||
curNav.path = nonSearchPart.substring(4, idpos);
|
||||
curNav.scrollToDeclPath = nonSearchPart.substring(idpos + 1);
|
||||
}
|
||||
} else {
|
||||
curNav.tag = 1;
|
||||
curNav.decl = findDecl(nonSearchPart);
|
||||
|
|
@ -648,10 +683,15 @@
|
|||
}
|
||||
|
||||
function onHashChange(state) {
|
||||
if (state != null) {
|
||||
const restore_search_index = state.curSearchIndex;
|
||||
if (restore_search_index !== undefined) curSearchIndex = restore_search_index;
|
||||
}
|
||||
// Use a non-null state value to prevent the window scrolling if the user goes back to this history entry.
|
||||
history.replaceState({}, "");
|
||||
navigate(location.hash);
|
||||
if (state == null) window.scrollTo({top: 0});
|
||||
if (curNavSearch !== "") domSearch.focus({preventScroll: true});
|
||||
}
|
||||
|
||||
function onPopState(ev) {
|
||||
|
|
@ -662,8 +702,8 @@
|
|||
function navigate(location_hash) {
|
||||
updateCurNav(location_hash);
|
||||
render();
|
||||
if (imFeelingLucky) {
|
||||
imFeelingLucky = false;
|
||||
if (loadSearchResult) {
|
||||
loadSearchResult = false;
|
||||
activateSelectedResult();
|
||||
}
|
||||
}
|
||||
|
|
@ -674,6 +714,19 @@
|
|||
}
|
||||
}
|
||||
|
||||
function doLoadSearchResult() {
|
||||
clearAsyncSearch();
|
||||
loadSearchResult = true;
|
||||
const old_hash = location.hash;
|
||||
location.hash = computeSearchHash();
|
||||
if (location.hash === old_hash) {
|
||||
// With certain sequences of history navigation and input, setting location.hash here
|
||||
// causes no change, and the enter key isn't acted on until another modification is made
|
||||
// to the search text. Force navigation to work around this.
|
||||
navigate(location.hash);
|
||||
}
|
||||
}
|
||||
|
||||
function activateSelectedResult() {
|
||||
if (domSectSearchResults.classList.contains("hidden")) {
|
||||
return;
|
||||
|
|
@ -685,21 +738,29 @@
|
|||
}
|
||||
if (liDom != null) {
|
||||
var aDom = liDom.children[0];
|
||||
history.replaceState({curSearchIndex: curSearchIndex}, "");
|
||||
location.href = aDom.getAttribute("href");
|
||||
curSearchIndex = -1;
|
||||
}
|
||||
domSearch.blur();
|
||||
}
|
||||
|
||||
function onSearchResultClick(ev) {
|
||||
const liDom = ev.target.parentElement;
|
||||
const search_index = Array.from(domListSearchResults.children).indexOf(liDom);
|
||||
curSearchIndex = search_index;
|
||||
doLoadSearchResult();
|
||||
|
||||
ev.preventDefault();
|
||||
ev.stopPropagation();
|
||||
}
|
||||
|
||||
function onSearchKeyDown(ev) {
|
||||
switch (ev.code) {
|
||||
case "Enter":
|
||||
if (ev.shiftKey || ev.ctrlKey || ev.altKey) return;
|
||||
|
||||
clearAsyncSearch();
|
||||
imFeelingLucky = true;
|
||||
location.hash = computeSearchHash();
|
||||
|
||||
doLoadSearchResult();
|
||||
ev.preventDefault();
|
||||
ev.stopPropagation();
|
||||
return;
|
||||
|
|
@ -846,6 +907,7 @@
|
|||
const full_name = fullyQualifiedName(match);
|
||||
aDom.textContent = full_name;
|
||||
aDom.setAttribute('href', navLinkFqn(full_name));
|
||||
aDom.addEventListener("click", onSearchResultClick);
|
||||
}
|
||||
renderSearchCursor();
|
||||
|
||||
|
|
@ -914,8 +976,8 @@
|
|||
return unwrapString(wasm_exports.decl_name(decl_index));
|
||||
}
|
||||
|
||||
function declSourceHtml(decl_index) {
|
||||
return unwrapString(wasm_exports.decl_source_html(decl_index));
|
||||
function declSourceHtml(decl_index, decl_nav_targets) {
|
||||
return unwrapString(wasm_exports.decl_source_html(decl_index, decl_nav_targets));
|
||||
}
|
||||
|
||||
function declDoctestHtml(decl_index) {
|
||||
|
|
@ -983,6 +1045,13 @@
|
|||
return result;
|
||||
}
|
||||
|
||||
function findDeclPathInNamespace(namespace_decl_index, path) {
|
||||
setInputString(path);
|
||||
const result = wasm_exports.find_decl_path_in_namespace(namespace_decl_index);
|
||||
if (result === -1) return null;
|
||||
return result;
|
||||
}
|
||||
|
||||
function findFileRoot(path) {
|
||||
setInputString(path);
|
||||
const result = wasm_exports.find_file_root();
|
||||
|
|
@ -998,7 +1067,7 @@
|
|||
|
||||
function fnErrorSet(decl_index) {
|
||||
const result = wasm_exports.fn_error_set(decl_index);
|
||||
if (result === 0) return null;
|
||||
if (result === 0 || result === -1) return null;
|
||||
return result;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -168,7 +168,7 @@ pub fn get_type_fn_return_expr(decl: *const Decl) ?Ast.Node.Index {
|
|||
|
||||
for (statements) |stmt| {
|
||||
if (ast.nodeTag(stmt) == .@"return") {
|
||||
return ast.nodeData(stmt).node;
|
||||
if (ast.nodeData(stmt).opt_node.unwrap()) |expr_node| return expr_node;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ pub var decls: std.ArrayList(Decl) = .empty;
|
|||
pub var modules: std.StringArrayHashMapUnmanaged(File.Index) = .empty;
|
||||
|
||||
file: File.Index,
|
||||
suppress_new_decls: u32 = 0,
|
||||
|
||||
/// keep in sync with "CAT_" constants in main.js
|
||||
pub const Category = union(enum(u8)) {
|
||||
|
|
@ -53,6 +54,27 @@ pub const File = struct {
|
|||
/// struct/union/enum/opaque decl node => its namespace scope
|
||||
/// local var decl node => its local variable scope
|
||||
scopes: std.AutoArrayHashMapUnmanaged(Ast.Node.Index, *Scope) = .empty,
|
||||
/// Last decl in the file (exclusive).
|
||||
decl_end: Decl.Index = .none,
|
||||
|
||||
pub const DeclIter = struct {
|
||||
idx: DeclIndexTagType,
|
||||
end: DeclIndexTagType,
|
||||
|
||||
const DeclIndexTagType = @typeInfo(Decl.Index).@"enum".tag_type;
|
||||
|
||||
pub fn next(iter: *DeclIter) ?Decl.Index {
|
||||
if (iter.idx >= iter.end) return null;
|
||||
const decl_idx = iter.idx;
|
||||
iter.idx += 1;
|
||||
return @enumFromInt(decl_idx);
|
||||
}
|
||||
|
||||
pub fn remaining(iter: DeclIter) usize {
|
||||
if (iter.idx >= iter.end) return 0;
|
||||
return iter.end - iter.idx;
|
||||
}
|
||||
};
|
||||
|
||||
pub fn lookup_token(file: *File, token: Ast.TokenIndex) Decl.Index {
|
||||
const decl_node = file.ident_decls.get(token) orelse return .none;
|
||||
|
|
@ -63,6 +85,7 @@ pub const File = struct {
|
|||
_,
|
||||
|
||||
fn add_decl(i: Index, node: Ast.Node.Index, parent_decl: Decl.Index) Oom!Decl.Index {
|
||||
assert(node == .root or parent_decl != .none);
|
||||
try decls.append(gpa, .{
|
||||
.ast_node = node,
|
||||
.file = i,
|
||||
|
|
@ -89,6 +112,18 @@ pub const File = struct {
|
|||
return file_index.get().node_decls.values()[0];
|
||||
}
|
||||
|
||||
/// Excludes the root decl.
|
||||
/// Only valid after `Walk.add_file()`.
|
||||
pub fn iterDecls(file_index: File.Index) DeclIter {
|
||||
const root_idx = @intFromEnum(file_index.findRootDecl());
|
||||
const end_idx = file_index.get().decl_end;
|
||||
assert(end_idx != .none);
|
||||
return DeclIter{
|
||||
.idx = root_idx + 1,
|
||||
.end = @intFromEnum(end_idx),
|
||||
};
|
||||
}
|
||||
|
||||
pub fn categorize_decl(file_index: File.Index, node: Ast.Node.Index) Category {
|
||||
const ast = file_index.get_ast();
|
||||
switch (ast.nodeTag(node)) {
|
||||
|
|
@ -315,23 +350,9 @@ pub const File = struct {
|
|||
if (std.mem.eql(u8, builtin_name, "@import")) {
|
||||
const str_lit_token = ast.nodeMainToken(params[0]);
|
||||
const str_bytes = ast.tokenSlice(str_lit_token);
|
||||
const file_path = std.zig.string_literal.parseAlloc(gpa, str_bytes) catch @panic("OOM");
|
||||
defer gpa.free(file_path);
|
||||
if (modules.get(file_path)) |imported_file_index| {
|
||||
return .{ .alias = File.Index.findRootDecl(imported_file_index) };
|
||||
}
|
||||
const base_path = file_index.path();
|
||||
const resolved_path = std.fs.path.resolvePosix(gpa, &.{
|
||||
base_path, "..", file_path,
|
||||
}) catch @panic("OOM");
|
||||
defer gpa.free(resolved_path);
|
||||
log.debug("from '{s}' @import '{s}' resolved='{s}'", .{
|
||||
base_path, file_path, resolved_path,
|
||||
});
|
||||
if (files.getIndex(resolved_path)) |imported_file_index| {
|
||||
return .{ .alias = File.Index.findRootDecl(@enumFromInt(imported_file_index)) };
|
||||
} else {
|
||||
log.warn("import target '{s}' did not resolve to any file", .{resolved_path});
|
||||
switch (file_index.resolve_import(str_bytes)) {
|
||||
.none => {},
|
||||
else => |decl_index| return .{ .alias = decl_index },
|
||||
}
|
||||
} else if (std.mem.eql(u8, builtin_name, "@This")) {
|
||||
if (file_index.get().node_decls.get(node)) |decl_index| {
|
||||
|
|
@ -344,6 +365,28 @@ pub const File = struct {
|
|||
return .{ .global_const = node };
|
||||
}
|
||||
|
||||
pub fn resolve_import(file_index: File.Index, str_bytes: []const u8) Decl.Index {
|
||||
const file_path = std.zig.string_literal.parseAlloc(gpa, str_bytes) catch @panic("OOM");
|
||||
defer gpa.free(file_path);
|
||||
if (modules.get(file_path)) |imported_file_index| {
|
||||
return File.Index.findRootDecl(imported_file_index);
|
||||
}
|
||||
const base_path = file_index.path();
|
||||
const resolved_path = std.fs.path.resolvePosix(gpa, &.{
|
||||
base_path, "..", file_path,
|
||||
}) catch @panic("OOM");
|
||||
defer gpa.free(resolved_path);
|
||||
log.debug("from '{s}' @import '{s}' resolved='{s}'", .{
|
||||
base_path, file_path, resolved_path,
|
||||
});
|
||||
if (files.getIndex(resolved_path)) |imported_file_index| {
|
||||
return File.Index.findRootDecl(@enumFromInt(imported_file_index));
|
||||
} else {
|
||||
log.warn("import target '{s}' did not resolve to any file", .{resolved_path});
|
||||
return .none;
|
||||
}
|
||||
}
|
||||
|
||||
fn categorize_switch(file_index: File.Index, node: Ast.Node.Index) Category {
|
||||
const ast = file_index.get_ast();
|
||||
const full = ast.fullSwitch(node).?;
|
||||
|
|
@ -401,6 +444,7 @@ pub fn add_file(file_name: []const u8, bytes: []u8) !File.Index {
|
|||
try struct_decl(&w, scope, decl_index, .root, ast.containerDeclRoot());
|
||||
|
||||
const file = file_index.get();
|
||||
file.decl_end = @enumFromInt(decls.items.len);
|
||||
shrinkToFit(&file.ident_decls);
|
||||
shrinkToFit(&file.token_parents);
|
||||
shrinkToFit(&file.node_decls);
|
||||
|
|
@ -480,6 +524,7 @@ pub const Scope = struct {
|
|||
},
|
||||
.namespace => {
|
||||
const namespace: *Namespace = @alignCast(@fieldParentPtr("base", it));
|
||||
assert(namespace.decl_index != .none);
|
||||
return namespace.decl_index;
|
||||
},
|
||||
};
|
||||
|
|
@ -556,7 +601,8 @@ fn struct_decl(
|
|||
if (namespace.doctests.get(fn_name)) |doctest_node| {
|
||||
try w.file.get().doctests.put(gpa, member, doctest_node);
|
||||
}
|
||||
const decl_index = try w.file.add_decl(member, parent_decl);
|
||||
const decl_index =
|
||||
if (w.suppress_new_decls > 0) Decl.Index.none else try w.file.add_decl(member, parent_decl);
|
||||
const body = if (ast.nodeTag(member) == .fn_decl) ast.nodeData(member).node_and_node[1].toOptional() else .none;
|
||||
try w.fn_decl(&namespace.base, decl_index, body, full);
|
||||
},
|
||||
|
|
@ -566,14 +612,22 @@ fn struct_decl(
|
|||
.simple_var_decl,
|
||||
.aligned_var_decl,
|
||||
=> {
|
||||
const decl_index = try w.file.add_decl(member, parent_decl);
|
||||
const decl_index =
|
||||
if (w.suppress_new_decls > 0) Decl.Index.none else try w.file.add_decl(member, parent_decl);
|
||||
try w.global_var_decl(&namespace.base, decl_index, ast.fullVarDecl(member).?);
|
||||
},
|
||||
|
||||
.@"comptime",
|
||||
=> try w.expr(&namespace.base, parent_decl, ast.nodeData(member).node),
|
||||
|
||||
.test_decl => try w.expr(&namespace.base, parent_decl, ast.nodeData(member).opt_token_and_node[1]),
|
||||
.test_decl => {
|
||||
// We're not interested in autodoc search within test declarations. It clutters the
|
||||
// search with irrelevant results; and the FQN of decls in the test block can
|
||||
// shadow other decls in the file, so we often can't even navigate to the results.
|
||||
w.suppress_new_decls += 1;
|
||||
try w.expr(&namespace.base, parent_decl, ast.nodeData(member).opt_token_and_node[1]);
|
||||
w.suppress_new_decls -= 1;
|
||||
},
|
||||
|
||||
else => unreachable,
|
||||
};
|
||||
|
|
@ -973,7 +1027,7 @@ fn builtin_call(
|
|||
const ast = w.file.get_ast();
|
||||
const builtin_token = ast.nodeMainToken(node);
|
||||
const builtin_name = ast.tokenSlice(builtin_token);
|
||||
if (std.mem.eql(u8, builtin_name, "@This")) {
|
||||
if (w.suppress_new_decls == 0 and std.mem.eql(u8, builtin_name, "@This")) {
|
||||
try w.file.get().node_decls.put(gpa, node, scope.getNamespaceDecl());
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -13,10 +13,15 @@ const Oom = error{OutOfMemory};
|
|||
/// Delete this to find out where URL escaping needs to be added.
|
||||
pub const missing_feature_url_escape = true;
|
||||
|
||||
/// Prevents collisions with IDs in index.html
|
||||
/// Keep in sync with the `navPrefix` constant in `main.js`.
|
||||
pub const nav_prefix: []const u8 = "nav_";
|
||||
|
||||
pub const RenderSourceOptions = struct {
|
||||
skip_doc_comments: bool = false,
|
||||
skip_comments: bool = false,
|
||||
collapse_whitespace: bool = false,
|
||||
/// Render a specific function as a link to its documentation.
|
||||
fn_link: Decl.Index = .none,
|
||||
/// Assumed to be sorted ascending.
|
||||
source_location_annotations: []const Annotation = &.{},
|
||||
|
|
@ -155,6 +160,26 @@ pub fn fileSourceHtml(
|
|||
.char_literal,
|
||||
.multiline_string_literal_line,
|
||||
=> {
|
||||
if (ast.isTokenPrecededByTags(token_index, &.{ .builtin, .l_paren }) and
|
||||
std.mem.eql(u8, "@import", ast.tokenSlice(token_index - 2)))
|
||||
{
|
||||
g.field_access_buffer.clearRetainingCapacity();
|
||||
try resolveImportLink(
|
||||
file_index,
|
||||
&g.field_access_buffer,
|
||||
ast.tokenSlice(token_index),
|
||||
);
|
||||
if (g.field_access_buffer.items.len > 0) {
|
||||
try out.appendSlice(gpa, "<a class=\"tok-str\" href=\"#");
|
||||
_ = missing_feature_url_escape;
|
||||
try out.appendSlice(gpa, g.field_access_buffer.items);
|
||||
try out.appendSlice(gpa, "\">");
|
||||
try appendEscaped(out, slice);
|
||||
try out.appendSlice(gpa, "</a>");
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
try out.appendSlice(gpa, "<span class=\"tok-str\">");
|
||||
try appendEscaped(out, slice);
|
||||
try out.appendSlice(gpa, "</span>");
|
||||
|
|
@ -381,6 +406,17 @@ fn resolveIdentLink(
|
|||
try resolveDeclLink(decl_index, out);
|
||||
}
|
||||
|
||||
fn resolveImportLink(
|
||||
file_index: Walk.File.Index,
|
||||
out: *std.ArrayListUnmanaged(u8),
|
||||
str_bytes: []const u8,
|
||||
) Oom!void {
|
||||
switch (file_index.resolve_import(str_bytes)) {
|
||||
.none => {},
|
||||
else => |decl_index| try decl_index.get().fqn(out),
|
||||
}
|
||||
}
|
||||
|
||||
fn unindent(s: []const u8, indent: usize) []const u8 {
|
||||
var indent_idx: usize = 0;
|
||||
for (s) |c| {
|
||||
|
|
|
|||
|
|
@ -8,10 +8,11 @@ const Decl = Walk.Decl;
|
|||
const ArrayList = std.ArrayList;
|
||||
const Writer = std.Io.Writer;
|
||||
|
||||
const fileSourceHtml = @import("html_render.zig").fileSourceHtml;
|
||||
const appendEscaped = @import("html_render.zig").appendEscaped;
|
||||
const resolveDeclLink = @import("html_render.zig").resolveDeclLink;
|
||||
const missing_feature_url_escape = @import("html_render.zig").missing_feature_url_escape;
|
||||
const html_render = @import("html_render.zig");
|
||||
const fileSourceHtml = html_render.fileSourceHtml;
|
||||
const appendEscaped = html_render.appendEscaped;
|
||||
const resolveDeclLink = html_render.resolveDeclLink;
|
||||
const missing_feature_url_escape = html_render.missing_feature_url_escape;
|
||||
|
||||
const gpa = std.heap.wasm_allocator;
|
||||
|
||||
|
|
@ -543,11 +544,38 @@ export fn decl_fn_proto_html(decl_index: Decl.Index, linkify_fn_name: bool) Stri
|
|||
return String.init(string_result.items);
|
||||
}
|
||||
|
||||
export fn decl_source_html(decl_index: Decl.Index) String {
|
||||
/// `decl_nav_targets`: create targets for jumping to decls. If true, asserts `decl_index` is the
|
||||
/// root decl of a file.
|
||||
export fn decl_source_html(decl_index: Decl.Index, decl_nav_targets: bool) String {
|
||||
const decl = decl_index.get();
|
||||
|
||||
var sla: std.ArrayListUnmanaged(html_render.Annotation) = .empty;
|
||||
defer sla.deinit(gpa);
|
||||
if (decl_nav_targets) {
|
||||
const root_file = decl_index.get().file;
|
||||
assert(decl_index == root_file.findRootDecl());
|
||||
|
||||
const ast = root_file.get_ast();
|
||||
var it = root_file.iterDecls();
|
||||
sla.ensureTotalCapacityPrecise(gpa, it.remaining()) catch @panic("OOM");
|
||||
while (true) {
|
||||
const inner_decl_index = (it.next() orelse break);
|
||||
const inner_decl = inner_decl_index.get();
|
||||
if (!inner_decl.is_pub()) continue;
|
||||
const decl_tok = ast.firstToken(inner_decl.ast_node);
|
||||
const tok_start = ast.tokenStart(decl_tok);
|
||||
sla.appendAssumeCapacity(.{
|
||||
.file_byte_offset = tok_start,
|
||||
.dom_id = @intFromEnum(inner_decl_index),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
string_result.clearRetainingCapacity();
|
||||
fileSourceHtml(decl.file, &string_result, decl.ast_node, .{}) catch |err| {
|
||||
fileSourceHtml(decl.file, &string_result, decl.ast_node, .{
|
||||
.source_location_annotations = sla.items,
|
||||
.annotation_prefix = html_render.nav_prefix,
|
||||
}) catch |err| {
|
||||
std.debug.panic("unable to render source: {s}", .{@errorName(err)});
|
||||
};
|
||||
return String.init(string_result.items);
|
||||
|
|
@ -847,6 +875,11 @@ export fn find_file_root() Decl.Index {
|
|||
return file.findRootDecl();
|
||||
}
|
||||
|
||||
/// Does the decl correspond to the root struct of a file?
|
||||
export fn decl_is_root(decl_index: Decl.Index) bool {
|
||||
return decl_index.get().file.findRootDecl() == decl_index;
|
||||
}
|
||||
|
||||
/// Uses `input_string`.
|
||||
/// Tries to look up the Decl component-wise but then falls back to a file path
|
||||
/// based scan.
|
||||
|
|
@ -869,6 +902,13 @@ export fn find_decl() Decl.Index {
|
|||
return .none;
|
||||
}
|
||||
|
||||
/// Uses `input_string` as a decl path.
|
||||
/// Start in the namespace corresponding to `decl_index`, find a child decl by path.
|
||||
/// The path can contain multiple components e.g. `foo.bar`.
|
||||
export fn find_decl_path_in_namespace(decl_index: Decl.Index) Decl.Index {
|
||||
return resolve_decl_path(decl_index, input_string.items) orelse .none;
|
||||
}
|
||||
|
||||
/// Set only by `categorize_decl`; read only by `get_aliasee`, valid only
|
||||
/// when `categorize_decl` returns `.alias`.
|
||||
var global_aliasee: Decl.Index = .none;
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue