mirror of
https://github.com/odin-lang/Odin.git
synced 2026-06-03 09:14:38 +00:00
Improve type printing
This commit is contained in:
@@ -45,6 +45,14 @@ base_type :: proc(t: doc.Type) -> doc.Type {
|
||||
return t
|
||||
}
|
||||
|
||||
is_type_untyped :: proc(type: doc.Type) -> bool {
|
||||
if type.kind == .Basic {
|
||||
flags := transmute(doc.Type_Flags_Basic)type.flags
|
||||
return .Untyped in flags
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
common_prefix :: proc(strs: []string) -> string {
|
||||
if len(strs) == 0 {
|
||||
return ""
|
||||
@@ -275,7 +283,9 @@ write_core_directory :: proc(w: io.Writer) {
|
||||
line_doc, _, _ := strings.partition(str(dir.pkg.docs), "\n")
|
||||
line_doc = strings.trim_space(line_doc)
|
||||
if line_doc != "" {
|
||||
fmt.wprintf(w, `<td class="pkg-line-doc">%s</td>`, line_doc)
|
||||
io.write_string(w, `<td class="pkg-line-doc">`)
|
||||
write_doc_line(w, line_doc)
|
||||
io.write_string(w, `</td>`)
|
||||
}
|
||||
}
|
||||
fmt.wprintf(w, "</tr>\n")
|
||||
@@ -289,7 +299,9 @@ write_core_directory :: proc(w: io.Writer) {
|
||||
line_doc, _, _ := strings.partition(str(child.pkg.docs), "\n")
|
||||
line_doc = strings.trim_space(line_doc)
|
||||
if line_doc != "" {
|
||||
fmt.wprintf(w, `<td class="pkg-line-doc">%s</td>`, line_doc)
|
||||
io.write_string(w, `<td class="pkg-line-doc">`)
|
||||
write_doc_line(w, line_doc)
|
||||
io.write_string(w, `</td>`)
|
||||
}
|
||||
|
||||
fmt.wprintf(w, "</tr>\n")
|
||||
@@ -305,16 +317,31 @@ is_entity_blank :: proc(e: doc.Entity_Index) -> bool {
|
||||
return name == "" || name == "_"
|
||||
}
|
||||
|
||||
write_where_clauses :: proc(w: io.Writer, where_clauses: []doc.String) {
|
||||
if len(where_clauses) != 0 {
|
||||
io.write_string(w, " where ")
|
||||
for clause, i in where_clauses {
|
||||
if i > 0 {
|
||||
io.write_string(w, ", ")
|
||||
}
|
||||
io.write_string(w, str(clause))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Write_Type_Flag :: enum {
|
||||
Is_Results,
|
||||
Variadic,
|
||||
Allow_Indent,
|
||||
Poly_Names,
|
||||
}
|
||||
Write_Type_Flags :: distinct bit_set[Write_Type_Flag]
|
||||
Type_Writer :: struct {
|
||||
w: io.Writer,
|
||||
pkg: doc.Pkg_Index,
|
||||
indent: int,
|
||||
generic_scope: map[string]bool,
|
||||
}
|
||||
|
||||
write_type :: proc(using writer: ^Type_Writer, type: doc.Type, flags: Write_Type_Flags) {
|
||||
@@ -329,30 +356,65 @@ write_type :: proc(using writer: ^Type_Writer, type: doc.Type, flags: Write_Type
|
||||
if .Param_Any_Int in e.flags { io.write_string(w, "#any_int ") }
|
||||
|
||||
init_string := str(e.init_string)
|
||||
switch init_string {
|
||||
case "#caller_location":
|
||||
switch {
|
||||
case init_string == "#caller_location":
|
||||
assert(name != "")
|
||||
io.write_string(w, name)
|
||||
io.write_string(w, " := ")
|
||||
io.write_string(w, `<a href="/core/runtime/#Source_Code_Location">`)
|
||||
io.write_string(w, init_string)
|
||||
io.write_string(w, `</a>`)
|
||||
|
||||
case strings.has_prefix(init_string, "context."):
|
||||
io.write_string(w, name)
|
||||
io.write_string(w, " := ")
|
||||
io.write_string(w, `<a href="/core/runtime/#Context">`)
|
||||
io.write_string(w, init_string)
|
||||
io.write_string(w, `</a>`)
|
||||
case:
|
||||
if name != "" {
|
||||
io.write_string(w, name)
|
||||
io.write_string(w, ": ")
|
||||
}
|
||||
padding := max(name_width-len(name), 0)
|
||||
for _ in 0..<padding {
|
||||
io.write_byte(w, ' ')
|
||||
the_type := types[e.type]
|
||||
type_flags := flags - {.Is_Results}
|
||||
if .Param_Ellipsis in e.flags {
|
||||
type_flags += {.Variadic}
|
||||
}
|
||||
|
||||
param_flags := flags - {.Is_Results}
|
||||
if .Param_Ellipsis in e.flags {
|
||||
param_flags += {.Variadic}
|
||||
#partial switch e.kind {
|
||||
case .Constant:
|
||||
assert(name != "")
|
||||
io.write_byte(w, '$')
|
||||
io.write_string(w, name)
|
||||
generic_scope[name] = true
|
||||
if !is_type_untyped(the_type) {
|
||||
io.write_string(w, ": ")
|
||||
write_type(writer, the_type, type_flags)
|
||||
io.write_string(w, " = ")
|
||||
io.write_string(w, init_string)
|
||||
} else {
|
||||
io.write_string(w, " := ")
|
||||
io.write_string(w, init_string)
|
||||
}
|
||||
return
|
||||
|
||||
case .Variable:
|
||||
if name != "" {
|
||||
io.write_string(w, name)
|
||||
io.write_string(w, ": ")
|
||||
}
|
||||
write_type(writer, the_type, type_flags)
|
||||
case .Type_Name:
|
||||
io.write_byte(w, '$')
|
||||
io.write_string(w, name)
|
||||
generic_scope[name] = true
|
||||
io.write_string(w, ": ")
|
||||
if the_type.kind == .Generic {
|
||||
io.write_string(w, "typeid")
|
||||
if ts := array(the_type.types); len(ts) == 1 {
|
||||
io.write_byte(w, '/')
|
||||
write_type(writer, types[ts[0]], type_flags)
|
||||
}
|
||||
} else {
|
||||
write_type(writer, the_type, type_flags)
|
||||
}
|
||||
}
|
||||
write_type(writer, types[e.type], param_flags)
|
||||
|
||||
if init_string != "" {
|
||||
io.write_string(w, " = ")
|
||||
@@ -361,14 +423,13 @@ write_type :: proc(using writer: ^Type_Writer, type: doc.Type, flags: Write_Type
|
||||
}
|
||||
}
|
||||
write_poly_params :: proc(using writer: ^Type_Writer, type: doc.Type, flags: Write_Type_Flags) {
|
||||
type_entites := array(type.entities)
|
||||
for entity_index, i in type_entites {
|
||||
if i > 0 {
|
||||
io.write_string(w, ", ")
|
||||
}
|
||||
write_param_entity(writer, &entities[entity_index], flags)
|
||||
if type.polymorphic_params != 0 {
|
||||
io.write_byte(w, '(')
|
||||
write_type(writer, types[type.polymorphic_params], flags+{.Poly_Names})
|
||||
io.write_byte(w, ')')
|
||||
}
|
||||
io.write_byte(w, ')')
|
||||
|
||||
write_where_clauses(w, array(type.where_clauses))
|
||||
}
|
||||
do_indent :: proc(using writer: ^Type_Writer, flags: Write_Type_Flags) {
|
||||
if .Allow_Indent not_in flags {
|
||||
@@ -400,7 +461,7 @@ write_type :: proc(using writer: ^Type_Writer, type: doc.Type, flags: Write_Type
|
||||
// ignore
|
||||
case .Basic:
|
||||
type_flags := transmute(doc.Type_Flags_Basic)type.flags
|
||||
if .Untyped in type_flags {
|
||||
if is_type_untyped(type) {
|
||||
io.write_string(w, str(type.name))
|
||||
} else {
|
||||
fmt.wprintf(w, `<a href="">%s</a>`, str(type.name))
|
||||
@@ -408,17 +469,23 @@ write_type :: proc(using writer: ^Type_Writer, type: doc.Type, flags: Write_Type
|
||||
case .Named:
|
||||
e := entities[type_entites[0]]
|
||||
name := str(type.name)
|
||||
fmt.wprintf(w, `<span>`)
|
||||
tn_pkg := files[e.pos.file].pkg
|
||||
if tn_pkg != pkg {
|
||||
fmt.wprintf(w, `%s.`, str(pkgs[tn_pkg].name))
|
||||
}
|
||||
fmt.wprintf(w, `<a class="code-typename" href="/core/{0:s}/#{1:s}">{1:s}</a></span>`, pkg_to_path[&pkgs[tn_pkg]], name)
|
||||
if n := strings.contains_rune(name, '('); n >= 0 {
|
||||
fmt.wprintf(w, `<a class="code-typename" href="/core/{0:s}/#{1:s}">{1:s}</a>`, pkg_to_path[&pkgs[tn_pkg]], name[:n])
|
||||
io.write_string(w, name[n:])
|
||||
} else {
|
||||
fmt.wprintf(w, `<a class="code-typename" href="/core/{0:s}/#{1:s}">{1:s}</a>`, pkg_to_path[&pkgs[tn_pkg]], name)
|
||||
}
|
||||
case .Generic:
|
||||
name := str(type.name)
|
||||
io.write_byte(w, '$')
|
||||
if name not_in generic_scope {
|
||||
io.write_byte(w, '$')
|
||||
}
|
||||
io.write_string(w, name)
|
||||
if len(array(type.types)) == 1 {
|
||||
if name not_in generic_scope && len(array(type.types)) == 1 {
|
||||
io.write_byte(w, '/')
|
||||
write_type(writer, types[type_types[0]], flags)
|
||||
}
|
||||
@@ -454,9 +521,7 @@ write_type :: proc(using writer: ^Type_Writer, type: doc.Type, flags: Write_Type
|
||||
case .Struct:
|
||||
type_flags := transmute(doc.Type_Flags_Struct)type.flags
|
||||
io.write_string(w, "struct")
|
||||
if .Polymorphic in type_flags {
|
||||
write_poly_params(writer, type, flags)
|
||||
}
|
||||
write_poly_params(writer, type, flags)
|
||||
if .Packed in type_flags { io.write_string(w, " #packed") }
|
||||
if .Raw_Union in type_flags { io.write_string(w, " #raw_union") }
|
||||
if custom_align := str(type.custom_align); custom_align != "" {
|
||||
@@ -483,9 +548,7 @@ write_type :: proc(using writer: ^Type_Writer, type: doc.Type, flags: Write_Type
|
||||
case .Union:
|
||||
type_flags := transmute(doc.Type_Flags_Union)type.flags
|
||||
io.write_string(w, "union")
|
||||
if .Polymorphic in type_flags {
|
||||
write_poly_params(writer, type, flags)
|
||||
}
|
||||
write_poly_params(writer, type, flags)
|
||||
if .No_Nil in type_flags { io.write_string(w, " #no_nil") }
|
||||
if .Maybe in type_flags { io.write_string(w, " #maybe") }
|
||||
if custom_align := str(type.custom_align); custom_align != "" {
|
||||
@@ -631,6 +694,25 @@ write_type :: proc(using writer: ^Type_Writer, type: doc.Type, flags: Write_Type
|
||||
}
|
||||
}
|
||||
|
||||
write_doc_line :: proc(w: io.Writer, text: string) {
|
||||
text := text
|
||||
for len(text) != 0 {
|
||||
if strings.count(text, "`") >= 2 {
|
||||
n := strings.index_byte(text, '`')
|
||||
io.write_string(w, text[:n])
|
||||
io.write_string(w, "<code class=\"code-inline\">")
|
||||
remaining := text[n+1:]
|
||||
m := strings.index_byte(remaining, '`')
|
||||
io.write_string(w, remaining[:m])
|
||||
io.write_string(w, "</code>")
|
||||
text = remaining[m+1:]
|
||||
} else {
|
||||
io.write_string(w, text)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
write_docs :: proc(w: io.Writer, pkg: ^doc.Pkg, docs: string) {
|
||||
if docs == "" {
|
||||
return
|
||||
@@ -663,8 +745,11 @@ write_docs :: proc(w: io.Writer, pkg: ^doc.Pkg, docs: string) {
|
||||
fmt.wprintln(w, "<p>")
|
||||
}
|
||||
assert(!was_code)
|
||||
|
||||
was_paragraph = true
|
||||
fmt.wprintln(w, text)
|
||||
write_doc_line(w, text)
|
||||
|
||||
io.write_byte(w, '\n')
|
||||
}
|
||||
if was_code {
|
||||
// assert(!was_paragraph, str(pkg.name))
|
||||
@@ -677,6 +762,24 @@ write_docs :: proc(w: io.Writer, pkg: ^doc.Pkg, docs: string) {
|
||||
}
|
||||
|
||||
write_pkg :: proc(w: io.Writer, path: string, pkg: ^doc.Pkg) {
|
||||
write_breadcrumbs :: proc(w: io.Writer, path: string) {
|
||||
dirs := strings.split(path, "/")
|
||||
io.write_string(w, "<ul class=\"documentation-breadcrumb\">\n")
|
||||
for dir, i in dirs {
|
||||
url := strings.join(dirs[:i+1], "/")
|
||||
short_path := strings.join(dirs[1:i+1], "/")
|
||||
if i == 0 || short_path in pkgs_to_use {
|
||||
fmt.wprintf(w, "<li><a href=\"/%s\">%s</a></li>", url, dir)
|
||||
} else {
|
||||
fmt.wprintf(w, "<li>%s</li>", dir)
|
||||
}
|
||||
}
|
||||
io.write_string(w, "</ul>\n")
|
||||
|
||||
}
|
||||
write_breadcrumbs(w, fmt.tprintf("core/%s", path))
|
||||
|
||||
|
||||
fmt.wprintf(w, "<h1>package core:%s</h1>\n", path)
|
||||
fmt.wprintln(w, "<h2>Documentation</h2>")
|
||||
docs := strings.trim_space(str(pkg.docs))
|
||||
@@ -723,7 +826,7 @@ write_pkg :: proc(w: io.Writer, path: string, pkg: ^doc.Pkg) {
|
||||
slice.sort_by_key(pkg_vars[:], entity_key)
|
||||
slice.sort_by_key(pkg_consts[:], entity_key)
|
||||
|
||||
print_index :: proc(w: io.Writer, name: string, entities: []^doc.Entity) {
|
||||
write_index :: proc(w: io.Writer, name: string, entities: []^doc.Entity) {
|
||||
fmt.wprintf(w, "<h4>%s</h4>\n", name)
|
||||
fmt.wprintln(w, `<section class="documentation-index">`)
|
||||
if len(entities) == 0 {
|
||||
@@ -740,16 +843,16 @@ write_pkg :: proc(w: io.Writer, path: string, pkg: ^doc.Pkg) {
|
||||
}
|
||||
|
||||
|
||||
print_index(w, "Procedures", pkg_procs[:])
|
||||
print_index(w, "Procedure Groups", pkg_proc_groups[:])
|
||||
print_index(w, "Types", pkg_types[:])
|
||||
print_index(w, "Variables", pkg_vars[:])
|
||||
print_index(w, "Constants", pkg_consts[:])
|
||||
write_index(w, "Procedures", pkg_procs[:])
|
||||
write_index(w, "Procedure Groups", pkg_proc_groups[:])
|
||||
write_index(w, "Types", pkg_types[:])
|
||||
write_index(w, "Variables", pkg_vars[:])
|
||||
write_index(w, "Constants", pkg_consts[:])
|
||||
|
||||
fmt.wprintln(w, "</section>")
|
||||
|
||||
|
||||
print_entity :: proc(w: io.Writer, e: ^doc.Entity) {
|
||||
write_entity :: proc(w: io.Writer, e: ^doc.Entity) {
|
||||
write_attributes :: proc(w: io.Writer, e: ^doc.Entity) {
|
||||
for attr in array(e.attributes) {
|
||||
io.write_string(w, "@(")
|
||||
@@ -764,23 +867,24 @@ write_pkg :: proc(w: io.Writer, path: string, pkg: ^doc.Pkg) {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
pkg_index := files[e.pos.file].pkg
|
||||
pkg := &pkgs[pkg_index]
|
||||
writer := &Type_Writer{
|
||||
w = w,
|
||||
pkg = pkg_index,
|
||||
}
|
||||
defer delete(writer.generic_scope)
|
||||
|
||||
name := str(e.name)
|
||||
path := pkg_to_path[pkg]
|
||||
filename := slashpath.base(str(files[e.pos.file].name))
|
||||
fmt.wprintf(w, "<h4 id=\"{0:s}\"><span><a class=\"documentation-id-link\" href=\"#%s\">{0:s}", name)
|
||||
fmt.wprintf(w, "<span class=\"a-hidden\"> ¶</span></a></span></h4>\n")
|
||||
defer if e.pos.file != 0 && e.pos.line > 0 {
|
||||
fmt.wprintf(w, "<h4 id=\"{0:s}\"><span><a class=\"documentation-id-link\" href=\"#{0:s}\">{0:s}", name)
|
||||
fmt.wprintf(w, "<span class=\"a-hidden\"> ¶</span></a></span>")
|
||||
if e.pos.file != 0 && e.pos.line > 0 {
|
||||
src_url := fmt.tprintf("%s/%s/%s#L%d", GITHUB_CORE_URL, path, filename, e.pos.line)
|
||||
fmt.wprintf(w, "<a class=\"documentation-source\" href=\"{0:s}\"><em>Source: {0:s}</em></a>", src_url)
|
||||
fmt.wprintf(w, "<div class=\"documentation-source\"><a href=\"{0:s}\"><em>Source</em></a></div>", src_url)
|
||||
}
|
||||
fmt.wprintf(w, "</h4>\n")
|
||||
|
||||
switch e.kind {
|
||||
case .Invalid, .Import_Name, .Library_Name:
|
||||
@@ -788,7 +892,21 @@ write_pkg :: proc(w: io.Writer, path: string, pkg: ^doc.Pkg) {
|
||||
case .Constant:
|
||||
fmt.wprint(w, "<pre>")
|
||||
the_type := types[e.type]
|
||||
if the_type.kind == .Basic && .Untyped in (transmute(doc.Type_Flags_Basic)the_type.flags) {
|
||||
|
||||
init_string := str(e.init_string)
|
||||
assert(init_string != "")
|
||||
|
||||
ignore_type := true
|
||||
if the_type.kind == .Basic && is_type_untyped(the_type) {
|
||||
} else {
|
||||
ignore_type = false
|
||||
type_name := str(the_type.name)
|
||||
if type_name != "" && strings.has_prefix(init_string, type_name) {
|
||||
ignore_type = true
|
||||
}
|
||||
}
|
||||
|
||||
if ignore_type {
|
||||
fmt.wprintf(w, "%s :: ", name)
|
||||
} else {
|
||||
fmt.wprintf(w, "%s: ", name)
|
||||
@@ -796,8 +914,7 @@ write_pkg :: proc(w: io.Writer, path: string, pkg: ^doc.Pkg) {
|
||||
fmt.wprintf(w, " : ")
|
||||
}
|
||||
|
||||
init_string := str(e.init_string)
|
||||
assert(init_string != "")
|
||||
|
||||
io.write_string(w, init_string)
|
||||
fmt.wprintln(w, "</pre>")
|
||||
case .Variable:
|
||||
@@ -835,17 +952,7 @@ write_pkg :: proc(w: io.Writer, path: string, pkg: ^doc.Pkg) {
|
||||
fmt.wprint(w, "<pre>")
|
||||
fmt.wprintf(w, "%s :: ", name)
|
||||
write_type(writer, types[e.type], nil)
|
||||
where_clauses := array(e.where_clauses)
|
||||
if len(where_clauses) != 0 {
|
||||
io.write_string(w, " where ")
|
||||
for clause, i in where_clauses {
|
||||
if i > 0 {
|
||||
io.write_string(w, ", ")
|
||||
}
|
||||
io.write_string(w, str(clause))
|
||||
}
|
||||
}
|
||||
|
||||
write_where_clauses(w, array(e.where_clauses))
|
||||
fmt.wprint(w, " {…}")
|
||||
fmt.wprintln(w, "</pre>")
|
||||
case .Proc_Group:
|
||||
@@ -872,24 +979,24 @@ write_pkg :: proc(w: io.Writer, path: string, pkg: ^doc.Pkg) {
|
||||
|
||||
write_docs(w, pkg, strings.trim_space(str(e.docs)))
|
||||
}
|
||||
print_entities :: proc(w: io.Writer, title: string, entities: []^doc.Entity) {
|
||||
write_entities :: proc(w: io.Writer, title: string, entities: []^doc.Entity) {
|
||||
fmt.wprintf(w, "<h3>%s</h3>\n", title)
|
||||
fmt.wprintln(w, `<section class="documentation">`)
|
||||
if len(entities) == 0 {
|
||||
io.write_string(w, "<p>This section is empty.</p>\n")
|
||||
} else {
|
||||
for e in entities {
|
||||
print_entity(w, e)
|
||||
write_entity(w, e)
|
||||
}
|
||||
}
|
||||
fmt.wprintln(w, "</section>")
|
||||
}
|
||||
|
||||
print_entities(w, "Procedures", pkg_procs[:])
|
||||
print_entities(w, "Procedure Groups", pkg_proc_groups[:])
|
||||
print_entities(w, "Types", pkg_types[:])
|
||||
print_entities(w, "Variables", pkg_vars[:])
|
||||
print_entities(w, "Constants", pkg_consts[:])
|
||||
write_entities(w, "Procedures", pkg_procs[:])
|
||||
write_entities(w, "Procedure Groups", pkg_proc_groups[:])
|
||||
write_entities(w, "Types", pkg_types[:])
|
||||
write_entities(w, "Variables", pkg_vars[:])
|
||||
write_entities(w, "Constants", pkg_consts[:])
|
||||
|
||||
|
||||
fmt.wprintln(w, "<h3>Source Files</h3>")
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
|
||||
pre {
|
||||
white-space: pre-wrap;
|
||||
word-break: break-all;
|
||||
word-break: keep-all;
|
||||
word-wrap: break-word;
|
||||
tab-size: 8;
|
||||
font-family: Consolas,Liberation Mono,Menlo,monospace!important;
|
||||
@@ -44,10 +44,15 @@ pre a {
|
||||
}
|
||||
|
||||
.documentation-source {
|
||||
display: inline;
|
||||
float: right;
|
||||
}
|
||||
|
||||
.documentation-source a {
|
||||
text-decoration: none;
|
||||
color: #666666;
|
||||
}
|
||||
.documentation-source:hover {
|
||||
.documentation-source a:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
@@ -56,4 +61,27 @@ a > .a-hidden {
|
||||
}
|
||||
a:hover > .a-hidden {
|
||||
opacity: 100;
|
||||
}
|
||||
|
||||
ul.documentation-breadcrumb {
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
ul.documentation-breadcrumb li {
|
||||
display: inline;
|
||||
}
|
||||
|
||||
ul.documentation-breadcrumb li+li:before {
|
||||
padding: 0.2rem;
|
||||
color: black;
|
||||
content: "/\00a0";
|
||||
}
|
||||
|
||||
.code-inline {
|
||||
font-family: Consolas,Liberation Mono,Menlo,monospace!important;
|
||||
background-color: #f8f8f8;
|
||||
color: #202224;
|
||||
border: 1px solid #c6c8ca;
|
||||
border-radius: 0.25rem;
|
||||
padding: 0.125rem;
|
||||
}
|
||||
Reference in New Issue
Block a user