diff --git a/Makefile b/Makefile index a7aecbb2d..d3d3c6a2d 100644 --- a/Makefile +++ b/Makefile @@ -8,22 +8,34 @@ CC=clang OS=$(shell uname) ifeq ($(OS), Darwin) + ARCH=$(shell uname -m) - LLVM_CONFIG=llvm-config + LLVM_CONFIG= - # LLVM Version Setting - LLVM_VERSION_PATTERN="^11\." - LLVM_VERSION="11" + # allow for arm only llvm's with version 13 ifeq ($(ARCH), arm64) - LLVM_VERSION="13" - LLVM_VERSION_PATTERN="^13" - endif + LLVM_VERSIONS = "13.%.%" + else + # allow for x86 / amd64 all llvm versions begining from 11 + LLVM_VERSIONS = "13.%.%" "12.0.1" "11.1.0" + endif - ifneq ($(shell llvm-config --version | grep $(LLVM_VERSION_PATTERN)),) + LLVM_VERSION_PATTERN_SEPERATOR = )|( + LLVM_VERSION_PATTERNS_ESCAPED_DOT = $(subst .,\.,$(LLVM_VERSIONS)) + LLVM_VERSION_PATTERNS_REPLACE_PERCENT = $(subst %,.*,$(LLVM_VERSION_PATTERNS_ESCAPED_DOT)) + LLVM_VERSION_PATTERN_REMOVE_ELEMENTS = $(subst " ",$(LLVM_VERSION_PATTERN_SEPERATOR),$(LLVM_VERSION_PATTERNS_REPLACE_PERCENT)) + LLMV_VERSION_PATTERN_REMOVE_SINGLE_STR = $(subst ",,$(LLVM_VERSION_PATTERN_REMOVE_ELEMENTS)) + LLVM_VERSION_PATTERN = "^(($(LLMV_VERSION_PATTERN_REMOVE_SINGLE_STR)))" + + ifneq ($(shell llvm-config --version | grep -E $(LLVM_VERSION_PATTERN)),) LLVM_CONFIG=llvm-config else - $(error "Requirement: llvm-config must be version $(LLVM_VERSION)") - endif + ifeq ($(ARCH), arm64) + $(error "Requirement: llvm-config must be base version 13 for arm64") + else + $(error "Requirement: llvm-config must be base version greater than 11 for amd64/x86") + endif + endif LDFLAGS:=$(LDFLAGS) -liconv CFLAGS:=$(CFLAGS) $(shell $(LLVM_CONFIG) --cxxflags --ldflags) diff --git a/core/compress/common.odin b/core/compress/common.odin index 41f292b6f..5f5ef2413 100644 --- a/core/compress/common.odin +++ b/core/compress/common.odin @@ -5,6 +5,9 @@ List of contributors: Jeroen van Rijn: Initial implementation, optimization. */ + + +// package compress is a collection of utilities to aid with other compression packages package compress import "core:io" diff --git a/core/encoding/hxa/doc.odin b/core/encoding/hxa/doc.odin index 16b94a243..230d6ea66 100644 --- a/core/encoding/hxa/doc.odin +++ b/core/encoding/hxa/doc.odin @@ -27,7 +27,7 @@ // Construction history, or BSP trees would make the format too large to serve its purpose. // The facilities of the formats to store meta data should make the format flexible enough // for most uses. Adding HxA support should be something anyone can do in a days work. - +// // Structure: // ---------- // HxA is designed to be extremely simple to parse, and is therefore based around conventions. It has @@ -45,17 +45,17 @@ // of a number of named layers. All layers in the stack have the same number of elements. Each layer // describes one property of the primitive. Each layer can have multiple channels and each layer can // store data of a different type. - +// // HaX stores 3 kinds of nodes // - Pixel data. // - Polygon geometry data. // - Meta data only. - +// // Pixel Nodes stores pixels in a layer stack. A layer may store things like Albedo, Roughness, // Reflectance, Light maps, Masks, Normal maps, and Displacement. Layers use the channels of the // layers to store things like color. The length of the layer stack is determined by the type and // dimensions stored in the - +// // Geometry data is stored in 3 separate layer stacks for: vertex data, corner data and face data. The // vertex data stores things like verities, blend shapes, weight maps, and vertex colors. The first // layer in a vertex stack has to be a 3 channel layer named "position" describing the base position @@ -63,7 +63,7 @@ // for things like UV, normals, and adjacency. The first layer in a corner stack has to be a 1 channel // integer layer named "index" describing the vertices used to form polygons. The last value in each // polygon has a negative - 1 index to indicate the end of the polygon. - +// // Example: // A quad and a tri with the vertex index: // [0, 1, 2, 3] [1, 4, 2] @@ -72,7 +72,7 @@ // The face stack stores values per face. the length of the face stack has to match the number of // negative values in the index layer in the corner stack. The face stack can be used to store things // like material index. - +// // Storage // ------- // All data is stored in little endian byte order with no padding. The layout mirrors the structs diff --git a/core/fmt/doc.odin b/core/fmt/doc.odin index 5984da950..668fc9bc6 100644 --- a/core/fmt/doc.odin +++ b/core/fmt/doc.odin @@ -64,6 +64,7 @@ If not present, the width is whatever is necessary to represent the value. Precision is specified after the (optional) width followed by a period followed by a decimal number. If no period is present, a default precision is used. A period with no following number specifies a precision of 0. + Examples: %f default width, default precision %8f width 8, default precision @@ -84,7 +85,6 @@ Other flags: add leading 0z for dozenal (%#z) add leading 0x or 0X for hexadecimal (%#x or %#X) remove leading 0x for %p (%#p) - ' ' (space) leave a space for elided sign in numbers (% d) 0 pad with leading zeros rather than spaces diff --git a/core/image/common.odin b/core/image/common.odin index 3ec8e15be..d72b770d5 100644 --- a/core/image/common.odin +++ b/core/image/common.odin @@ -6,6 +6,8 @@ Jeroen van Rijn: Initial implementation, optimization. Ginger Bill: Cosmetic changes. */ + +// package image implements a general 2D image library to be used with other image related packages package image import "core:bytes" diff --git a/core/image/png/png.odin b/core/image/png/png.odin index da76a4588..bff0afde3 100644 --- a/core/image/png/png.odin +++ b/core/image/png/png.odin @@ -6,6 +6,11 @@ Jeroen van Rijn: Initial implementation. Ginger Bill: Cosmetic changes. */ + + +// package png implements a PNG image reader +// +// The PNG specification is at https://www.w3.org/TR/PNG/. package png import "core:compress" diff --git a/core/math/big/api.odin b/core/math/big/api.odin index c9be04da0..bf19e83b6 100644 --- a/core/math/big/api.odin +++ b/core/math/big/api.odin @@ -2,12 +2,10 @@ Copyright 2021 Jeroen van Rijn . Made available under Odin's BSD-3 license. - An arbitrary precision mathematics implementation in Odin. - For the theoretical underpinnings, see Knuth's The Art of Computer Programming, Volume 2, section 4.3. - The code started out as an idiomatic source port of libTomMath, which is in the public domain, with thanks. - This file collects public proc maps and their aliases. */ + + package math_big /* diff --git a/core/math/big/common.odin b/core/math/big/common.odin index 31ad54b14..2b34a9163 100644 --- a/core/math/big/common.odin +++ b/core/math/big/common.odin @@ -1,11 +1,9 @@ /* Copyright 2021 Jeroen van Rijn . Made available under Odin's BSD-3 license. - - An arbitrary precision mathematics implementation in Odin. - For the theoretical underpinnings, see Knuth's The Art of Computer Programming, Volume 2, section 4.3. - The code started out as an idiomatic source port of libTomMath, which is in the public domain, with thanks. */ + + package math_big import "core:intrinsics" diff --git a/core/math/big/doc.odin b/core/math/big/doc.odin new file mode 100644 index 000000000..0f9b88d01 --- /dev/null +++ b/core/math/big/doc.odin @@ -0,0 +1,6 @@ +/* +A BigInt implementation in Odin. +For the theoretical underpinnings, see Knuth's The Art of Computer Programming, Volume 2, section 4.3. +The code started out as an idiomatic source port of libTomMath, which is in the public domain, with thanks. +*/ +package math_big diff --git a/core/math/big/helpers.odin b/core/math/big/helpers.odin index 6d13d32bb..6c4b5dd01 100644 --- a/core/math/big/helpers.odin +++ b/core/math/big/helpers.odin @@ -1,11 +1,9 @@ /* Copyright 2021 Jeroen van Rijn . Made available under Odin's BSD-3 license. - - An arbitrary precision mathematics implementation in Odin. - For the theoretical underpinnings, see Knuth's The Art of Computer Programming, Volume 2, section 4.3. - The code started out as an idiomatic source port of libTomMath, which is in the public domain, with thanks. */ + + package math_big import "core:intrinsics" diff --git a/core/math/big/internal.odin b/core/math/big/internal.odin index 437f6e5fc..dbcd16509 100644 --- a/core/math/big/internal.odin +++ b/core/math/big/internal.odin @@ -1,12 +1,7 @@ -//+ignore /* Copyright 2021 Jeroen van Rijn . Made available under Odin's BSD-3 license. - A BigInt implementation in Odin. - For the theoretical underpinnings, see Knuth's The Art of Computer Programming, Volume 2, section 4.3. - The code started out as an idiomatic source port of libTomMath, which is in the public domain, with thanks. - ========================== Low-level routines ========================== IMPORTANT: `internal_*` procedures make certain assumptions about their input. @@ -29,6 +24,9 @@ TODO: Handle +/- Infinity and NaN. */ + + +//+ignore package math_big import "core:mem" diff --git a/core/math/big/logical.odin b/core/math/big/logical.odin index e7e55cc47..b5de4cabf 100644 --- a/core/math/big/logical.odin +++ b/core/math/big/logical.odin @@ -8,6 +8,8 @@ This file contains logical operations like `and`, `or` and `xor`. */ + + package math_big /* diff --git a/core/math/big/prime.odin b/core/math/big/prime.odin index eb0cd644c..3cce69675 100644 --- a/core/math/big/prime.odin +++ b/core/math/big/prime.odin @@ -8,6 +8,8 @@ This file contains prime finding operations. */ + + package math_big import rnd "core:math/rand" diff --git a/core/math/big/private.odin b/core/math/big/private.odin index 9989a208a..419f2103f 100644 --- a/core/math/big/private.odin +++ b/core/math/big/private.odin @@ -15,6 +15,8 @@ These aren't exported for the same reasons. */ + + package math_big import "core:intrinsics" diff --git a/core/math/big/public.odin b/core/math/big/public.odin index 2673a262f..3227d7bc4 100644 --- a/core/math/big/public.odin +++ b/core/math/big/public.odin @@ -8,6 +8,8 @@ This file contains basic arithmetic operations like `add`, `sub`, `mul`, `div`, ... */ + + package math_big import "core:intrinsics" diff --git a/core/math/big/radix.odin b/core/math/big/radix.odin index 760c49d77..2b758dc35 100644 --- a/core/math/big/radix.odin +++ b/core/math/big/radix.odin @@ -12,6 +12,8 @@ - Use Barrett reduction for non-powers-of-two. - Also look at extracting and splatting several digits at once. */ + + package math_big import "core:intrinsics" diff --git a/core/math/big/tune.odin b/core/math/big/tune.odin index d67ff61b4..78a20c12b 100644 --- a/core/math/big/tune.odin +++ b/core/math/big/tune.odin @@ -1,4 +1,3 @@ -//+ignore /* Copyright 2021 Jeroen van Rijn . Made available under Odin's BSD-3 license. @@ -7,6 +6,8 @@ For the theoretical underpinnings, see Knuth's The Art of Computer Programming, Volume 2, section 4.3. The code started out as an idiomatic source port of libTomMath, which is in the public domain, with thanks. */ + +//+ignore package math_big import "core:time" diff --git a/core/odin/doc-format/doc_format.odin b/core/odin/doc-format/doc_format.odin index 83cd89ca2..59eafdc09 100644 --- a/core/odin/doc-format/doc_format.odin +++ b/core/odin/doc-format/doc_format.odin @@ -11,7 +11,7 @@ String :: distinct Array(byte) Version_Type_Major :: 0 Version_Type_Minor :: 2 -Version_Type_Patch :: 2 +Version_Type_Patch :: 3 Version_Type :: struct { major, minor, patch: u8, @@ -107,6 +107,8 @@ Entity_Flag :: enum u32le { Var_Thread_Local = 40, Var_Static = 41, + + Private = 50, } Entity_Flags :: distinct bit_set[Entity_Flag; u64le] @@ -122,6 +124,10 @@ Entity :: struct { _: u32le, // reserved for init comment: String, docs: String, + // May be used by (Struct fields and procedure fields): + // .Variable + // .Constant + field_group_index: i32le, // May used by: // .Variable diff --git a/core/runtime/core_builtin_matrix.odin b/core/runtime/core_builtin_matrix.odin index 08dca288e..53589587c 100644 --- a/core/runtime/core_builtin_matrix.odin +++ b/core/runtime/core_builtin_matrix.odin @@ -146,14 +146,14 @@ matrix2x2_inverse_transpose :: proc "contextless" (x: $M/matrix[2, 2]$T) -> (y: d := x[0, 0]*x[1, 1] - x[0, 1]*x[1, 0] when intrinsics.type_is_integer(T) { y[0, 0] = +x[1, 1] / d - y[1, 0] = -x[1, 0] / d - y[0, 1] = -x[0, 1] / d + y[1, 0] = -x[0, 1] / d + y[0, 1] = -x[1, 0] / d y[1, 1] = +x[0, 0] / d } else { id := 1 / d y[0, 0] = +x[1, 1] * id - y[1, 0] = -x[1, 0] * id - y[0, 1] = -x[0, 1] * id + y[1, 0] = -x[0, 1] * id + y[0, 1] = -x[1, 0] * id y[1, 1] = +x[0, 0] * id } return @@ -214,16 +214,16 @@ matrix1x1_inverse :: proc "contextless" (x: $M/matrix[1, 1]$T) -> (y: M) { matrix2x2_inverse :: proc "contextless" (x: $M/matrix[2, 2]$T) -> (y: M) { d := x[0, 0]*x[1, 1] - x[0, 1]*x[1, 0] when intrinsics.type_is_integer(T) { - y[0, 0] = x[1, 1] / d - y[0, 1] = x[1, 0] / d - y[1, 0] = x[0, 1] / d - y[1, 1] = x[0, 0] / d + y[0, 0] = +x[1, 1] / d + y[0, 1] = -x[0, 1] / d + y[1, 0] = -x[1, 0] / d + y[1, 1] = +x[0, 0] / d } else { id := 1 / d - y[0, 0] = x[1, 1] * id - y[0, 1] = x[1, 0] * id - y[1, 0] = x[0, 1] * id - y[1, 1] = x[0, 0] * id + y[0, 0] = +x[1, 1] * id + y[0, 1] = -x[0, 1] * id + y[1, 0] = -x[1, 0] * id + y[1, 1] = +x[0, 0] * id } return } diff --git a/core/runtime/entry_unix.odin b/core/runtime/entry_unix.odin index 67d2cbcb7..dd1e06625 100644 --- a/core/runtime/entry_unix.odin +++ b/core/runtime/entry_unix.odin @@ -5,13 +5,13 @@ package runtime import "core:intrinsics" when ODIN_BUILD_MODE == .Dynamic { - @(link_name="_odin_entry_point", linkage="strong", require, link_section=".init") + @(link_name="_odin_entry_point", linkage="strong", require/*, link_section=".init"*/) _odin_entry_point :: proc "c" () { context = default_context() #force_no_inline _startup_runtime() intrinsics.__entry_point() } - @(link_name="_odin_exit_point", linkage="strong", require, link_section=".fini") + @(link_name="_odin_exit_point", linkage="strong", require/*, link_section=".fini"*/) _odin_exit_point :: proc "c" () { context = default_context() #force_no_inline _cleanup_runtime() diff --git a/core/sync/barrier.odin b/core/sync/barrier.odin index 997fde82d..13c870a3b 100644 --- a/core/sync/barrier.odin +++ b/core/sync/barrier.odin @@ -1,44 +1,43 @@ package sync -// A barrier enabling multiple threads to synchronize the beginning of some computation /* - * Example: - * - * package example - * - * import "core:fmt" - * import "core:sync" - * import "core:thread" - * - * barrier := &sync.Barrier{}; - * - * main :: proc() { - * fmt.println("Start"); - * - * THREAD_COUNT :: 4; - * threads: [THREAD_COUNT]^thread.Thread; - * - * sync.barrier_init(barrier, THREAD_COUNT); - * defer sync.barrier_destroy(barrier); - * - * - * for _, i in threads { - * threads[i] = thread.create_and_start(proc(t: ^thread.Thread) { - * // Same messages will be printed together but without any interleaving - * fmt.println("Getting ready!"); - * sync.barrier_wait(barrier); - * fmt.println("Off their marks they go!"); - * }); - * } - * - * for t in threads { - * thread.destroy(t); // join and free thread - * } - * fmt.println("Finished"); - * } - * - */ +A barrier enabling multiple threads to synchronize the beginning of some computation +Example: + + package example + + import "core:fmt" + import "core:sync" + import "core:thread" + + barrier := &sync.Barrier{}; + + main :: proc() { + fmt.println("Start"); + + THREAD_COUNT :: 4; + threads: [THREAD_COUNT]^thread.Thread; + + sync.barrier_init(barrier, THREAD_COUNT); + defer sync.barrier_destroy(barrier); + + + for _, i in threads { + threads[i] = thread.create_and_start(proc(t: ^thread.Thread) { + // Same messages will be printed together but without any interleaving + fmt.println("Getting ready!"); + sync.barrier_wait(barrier); + fmt.println("Off their marks they go!"); + }); + } + + for t in threads { + thread.destroy(t); // join and free thread + } + fmt.println("Finished"); + } +*/ Barrier :: struct { mutex: Blocking_Mutex, cond: Condition, diff --git a/core/sync/sync2/extended.odin b/core/sync/sync2/extended.odin index d6a99fe04..deb48a22d 100644 --- a/core/sync/sync2/extended.odin +++ b/core/sync/sync2/extended.odin @@ -67,44 +67,41 @@ wait_group_wait_with_timeout :: proc(wg: ^Wait_Group, duration: time.Duration) - -// A barrier enabling multiple threads to synchronize the beginning of some computation /* - * Example: - * - * package example - * - * import "core:fmt" - * import "core:sync" - * import "core:thread" - * - * barrier := &sync.Barrier{} - * - * main :: proc() { - * fmt.println("Start") - * - * THREAD_COUNT :: 4 - * threads: [THREAD_COUNT]^thread.Thread - * - * sync.barrier_init(barrier, THREAD_COUNT) - * defer sync.barrier_destroy(barrier) - * - * - * for _, i in threads { - * threads[i] = thread.create_and_start(proc(t: ^thread.Thread) { - * // Same messages will be printed together but without any interleaving - * fmt.println("Getting ready!") - * sync.barrier_wait(barrier) - * fmt.println("Off their marks they go!") - * }) - * } - * - * for t in threads { - * thread.destroy(t) // join and free thread - * } - * fmt.println("Finished") - * } - * - */ +A barrier enabling multiple threads to synchronize the beginning of some computation + +Example: + package example + + import "core:fmt" + import "core:sync" + import "core:thread" + + barrier := &sync.Barrier{} + + main :: proc() { + fmt.println("Start") + + THREAD_COUNT :: 4 + threads: [THREAD_COUNT]^thread.Thread + + sync.barrier_init(barrier, THREAD_COUNT) + + for _, i in threads { + threads[i] = thread.create_and_start(proc(t: ^thread.Thread) { + // Same messages will be printed together but without any interleaving + fmt.println("Getting ready!") + sync.barrier_wait(barrier) + fmt.println("Off their marks they go!") + }) + } + + for t in threads { + thread.destroy(t) // join and free thread + } + fmt.println("Finished") + } +*/ Barrier :: struct { mutex: Mutex, cond: Cond, diff --git a/core/sync/sync2/primitives.odin b/core/sync/sync2/primitives.odin index d9e013664..6d056d439 100644 --- a/core/sync/sync2/primitives.odin +++ b/core/sync/sync2/primitives.odin @@ -29,12 +29,12 @@ mutex_try_lock :: proc(m: ^Mutex) -> bool { return _mutex_try_lock(m) } -// Example: -// -// if mutex_guard(&m) { -// ... -// } -// +/* +Example: + if mutex_guard(&m) { + ... + } +*/ @(deferred_in=mutex_unlock) mutex_guard :: proc(m: ^Mutex) -> bool { mutex_lock(m) @@ -80,25 +80,24 @@ rw_mutex_shared_unlock :: proc(rw: ^RW_Mutex) { rw_mutex_try_shared_lock :: proc(rw: ^RW_Mutex) -> bool { return _rw_mutex_try_shared_lock(rw) } - -// Example: -// -// if rw_mutex_guard(&m) { -// ... -// } -// +/* +Example: + if rw_mutex_guard(&m) { + ... + } +*/ @(deferred_in=rw_mutex_unlock) rw_mutex_guard :: proc(m: ^RW_Mutex) -> bool { rw_mutex_lock(m) return true } -// Example: -// -// if rw_mutex_shared_guard(&m) { -// ... -// } -// +/* +Example: + if rw_mutex_shared_guard(&m) { + ... + } +*/ @(deferred_in=rw_mutex_shared_unlock) rw_mutex_shared_guard :: proc(m: ^RW_Mutex) -> bool { rw_mutex_shared_lock(m) @@ -127,13 +126,12 @@ recursive_mutex_try_lock :: proc(m: ^Recursive_Mutex) -> bool { return _recursive_mutex_try_lock(m) } - -// Example: -// -// if recursive_mutex_guard(&m) { -// ... -// } -// +/* +Example: + if recursive_mutex_guard(&m) { + ... + } +*/ @(deferred_in=recursive_mutex_unlock) recursive_mutex_guard :: proc(m: ^Recursive_Mutex) -> bool { recursive_mutex_lock(m) diff --git a/core/sync/sync2/primitives_atomic.odin b/core/sync/sync2/primitives_atomic.odin index a13b73a99..5fc6fba85 100644 --- a/core/sync/sync2/primitives_atomic.odin +++ b/core/sync/sync2/primitives_atomic.odin @@ -82,13 +82,12 @@ atomic_mutex_try_lock :: proc(m: ^Atomic_Mutex) -> bool { return ok } - -// Example: -// -// if atomic_mutex_guard(&m) { -// ... -// } -// +/* +Example: + if atomic_mutex_guard(&m) { + ... + } +*/ @(deferred_in=atomic_mutex_unlock) atomic_mutex_guard :: proc(m: ^Atomic_Mutex) -> bool { atomic_mutex_lock(m) @@ -193,25 +192,24 @@ atomic_rw_mutex_try_shared_lock :: proc(rw: ^Atomic_RW_Mutex) -> bool { return false } - -// Example: -// -// if atomic_rw_mutex_guard(&m) { -// ... -// } -// +/* +Example: + if atomic_rw_mutex_guard(&m) { + ... + } +*/ @(deferred_in=atomic_rw_mutex_unlock) atomic_rw_mutex_guard :: proc(m: ^Atomic_RW_Mutex) -> bool { atomic_rw_mutex_lock(m) return true } -// Example: -// -// if atomic_rw_mutex_shared_guard(&m) { -// ... -// } -// +/* +Example: + if atomic_rw_mutex_shared_guard(&m) { + ... + } +*/ @(deferred_in=atomic_rw_mutex_shared_unlock) atomic_rw_mutex_shared_guard :: proc(m: ^Atomic_RW_Mutex) -> bool { atomic_rw_mutex_shared_lock(m) @@ -270,13 +268,12 @@ atomic_recursive_mutex_try_lock :: proc(m: ^Atomic_Recursive_Mutex) -> bool { return true } - -// Example: -// -// if atomic_recursive_mutex_guard(&m) { -// ... -// } -// +/* +Example: + if atomic_recursive_mutex_guard(&m) { + ... + } +*/ @(deferred_in=atomic_recursive_mutex_unlock) atomic_recursive_mutex_guard :: proc(m: ^Atomic_Recursive_Mutex) -> bool { atomic_recursive_mutex_lock(m) diff --git a/core/sync/sync2/sema_internal.odin b/core/sync/sync2/sema_internal.odin index 64fc4ed96..f4027e908 100644 --- a/core/sync/sync2/sema_internal.odin +++ b/core/sync/sync2/sema_internal.odin @@ -1,3 +1,4 @@ +//+private package sync2 import "core:time" diff --git a/core/sync/sync2/sync_util.odin b/core/sync/sync2/sync_util.odin index 853c3c685..013bf511a 100644 --- a/core/sync/sync2/sync_util.odin +++ b/core/sync/sync2/sync_util.odin @@ -1,12 +1,11 @@ package sync2 - -// Example: -// -// if guard(&m) { -// ... -// } -// +/* +Example: + if guard(&m) { + ... + } +*/ guard :: proc{ mutex_guard, rw_mutex_guard, @@ -17,13 +16,12 @@ guard :: proc{ atomic_recursive_mutex_guard, atomic_rw_mutex_guard, } - -// Example: -// -// if shared_guard(&m) { -// ... -// } -// +/* +Example: + if shared_guard(&m) { + ... + } +*/ shared_guard :: proc{ rw_mutex_shared_guard, atomic_rw_mutex_shared_guard, diff --git a/examples/all/all_vendor.odin b/examples/all/all_vendor.odin index 777c184f9..f94e092af 100644 --- a/examples/all/all_vendor.odin +++ b/examples/all/all_vendor.odin @@ -1,26 +1,47 @@ //+build windows package all -import glfw "vendor:glfw" -import gl "vendor:OpenGL" -import rl "vendor:raylib" -import PM "vendor:portmidi" + +import botan "vendor:botan" +import ENet "vendor:ENet" +import gl "vendor:OpenGL" +import glfw "vendor:glfw" +import microui "vendor:microui" +import miniaudio "vendor:miniaudio" +import PM "vendor:portmidi" +import rl "vendor:raylib" + import SDL "vendor:sdl2" -import IMG "vendor:sdl2/image" import SDLNet "vendor:sdl2/net" +import IMG "vendor:sdl2/image" import MIX "vendor:sdl2/mixer" import TTF "vendor:sdl2/ttf" -import vk "vendor:vulkan" -import ENet "vendor:ENet" -_ :: glfw +import stb_easy_font "vendor:stb/easy_font" +import stbi "vendor:stb/image" +import stbrp "vendor:stb/rect_pack" +import stbtt "vendor:stb/truetype" +import stb_vorbis "vendor:stb/vorbis" + +import vk "vendor:vulkan" + + +_ :: botan +_ :: ENet _ :: gl -_ :: rl +_ :: glfw +_ :: microui +_ :: miniaudio _ :: PM +_ :: rl _ :: SDL -_ :: IMG _ :: SDLNet +_ :: IMG _ :: MIX _ :: TTF -_ :: vk -_ :: ENet \ No newline at end of file +_ :: stb_easy_font +_ :: stbi +_ :: stbrp +_ :: stbtt +_ :: stb_vorbis +_ :: vk \ No newline at end of file diff --git a/src/check_type.cpp b/src/check_type.cpp index 5389252b3..a5a757f3e 100644 --- a/src/check_type.cpp +++ b/src/check_type.cpp @@ -109,11 +109,14 @@ void check_struct_fields(CheckerContext *ctx, Ast *node, Slice *fields } i32 field_src_index = 0; + i32 field_group_index = -1; for_array(i, params) { Ast *param = params[i]; if (param->kind != Ast_Field) { continue; } + field_group_index += 1; + ast_node(p, Field, param); Ast *type_expr = p->type; Type *type = nullptr; @@ -152,6 +155,7 @@ void check_struct_fields(CheckerContext *ctx, Ast *node, Slice *fields Entity *field = alloc_entity_field(ctx->scope, name_token, type, is_using, field_src_index); add_entity(ctx, ctx->scope, name, field); + field->Variable.field_group_index = field_group_index; array_add(&fields_array, field); String tag = p->tag.string; if (tag.len != 0 && !unquote_string(permanent_allocator(), &tag, 0, tag.text[0] == '`')) { @@ -1366,11 +1370,13 @@ Type *check_get_params(CheckerContext *ctx, Scope *scope, Ast *_params, bool *is isize variadic_index = -1; bool is_c_vararg = false; auto variables = array_make(permanent_allocator(), 0, variable_count); + i32 field_group_index = -1; for_array(i, params) { Ast *param = params[i]; if (param->kind != Ast_Field) { continue; } + field_group_index += 1; ast_node(p, Field, param); Ast *type_expr = unparen_expr(p->type); Type *type = nullptr; @@ -1671,9 +1677,11 @@ Type *check_get_params(CheckerContext *ctx, Scope *scope, Ast *_params, bool *is } param = alloc_entity_const_param(scope, name->Ident.token, type, poly_const, is_type_polymorphic(type)); + param->Constant.field_group_index = field_group_index; } else { param = alloc_entity_param(scope, name->Ident.token, type, is_using, true); param->Variable.param_value = param_value; + param->Variable.field_group_index = field_group_index; } } if (p->flags&FieldFlag_no_alias) { @@ -1767,7 +1775,10 @@ Type *check_get_results(CheckerContext *ctx, Scope *scope, Ast *_results) { } auto variables = array_make(permanent_allocator(), 0, variable_count); + i32 field_group_index = -1; for_array(i, results) { + field_group_index += 1; + ast_node(field, Field, results[i]); Ast *default_value = unparen_expr(field->default_value); ParameterValue param_value = {}; @@ -1798,6 +1809,7 @@ Type *check_get_results(CheckerContext *ctx, Scope *scope, Ast *_results) { token.string = str_lit(""); Entity *param = alloc_entity_param(scope, token, type, false, false); param->Variable.param_value = param_value; + param->Variable.field_group_index = -1; array_add(&variables, param); } else { for_array(j, field->names) { @@ -1821,6 +1833,7 @@ Type *check_get_results(CheckerContext *ctx, Scope *scope, Ast *_results) { Entity *param = alloc_entity_param(scope, token, type, false, false); param->flags |= EntityFlag_Result; param->Variable.param_value = param_value; + param->Variable.field_group_index = field_group_index; array_add(&variables, param); add_entity(ctx, scope, name, param); // NOTE(bill): Removes `declared but not used` when using -vet diff --git a/src/checker.cpp b/src/checker.cpp index ddb73d33e..63a697072 100644 --- a/src/checker.cpp +++ b/src/checker.cpp @@ -1253,7 +1253,7 @@ isize type_info_index(CheckerInfo *info, Type *type, bool error_on_failure) { // TODO(bill): This is O(n) and can be very slow for_array(i, info->type_info_map.entries){ auto *e = &info->type_info_map.entries[i]; - if (are_types_identical(e->key, type)) { + if (are_types_identical_unique_tuples(e->key, type)) { entry_index = e->value; // NOTE(bill): Add it to the search map map_set(&info->type_info_map, type, entry_index); @@ -1601,7 +1601,7 @@ void add_type_info_type_internal(CheckerContext *c, Type *t) { isize ti_index = -1; for_array(i, c->info->type_info_map.entries) { auto *e = &c->info->type_info_map.entries[i]; - if (are_types_identical(t, e->key)) { + if (are_types_identical_unique_tuples(t, e->key)) { // Duplicate entry ti_index = e->value; prev = true; @@ -3446,6 +3446,13 @@ void check_collect_value_decl(CheckerContext *c, Ast *decl) { } } + if (entity_visibility_kind == EntityVisiblity_Public && + (c->scope->flags&ScopeFlag_File) && + c->scope->file && + (c->scope->file->flags & AstFile_IsPrivate)) { + entity_visibility_kind = EntityVisiblity_PrivateToPackage; + } + if (entity_visibility_kind != EntityVisiblity_Public && !(c->scope->flags&ScopeFlag_File)) { error(decl, "Attribute 'private' is not allowed on a non file scope entity"); } diff --git a/src/docs.cpp b/src/docs.cpp index 8d65cb83a..3ea3cce1b 100644 --- a/src/docs.cpp +++ b/src/docs.cpp @@ -67,6 +67,14 @@ GB_COMPARE_PROC(cmp_ast_package_by_name) { #include "docs_format.cpp" #include "docs_writer.cpp" +void print_doc_line(i32 indent, String const &data) { + while (indent --> 0) { + gb_printf("\t"); + } + gb_file_write(gb_file_get_standard(gbFileStandard_Output), data.text, data.len); + gb_printf("\n"); +} + void print_doc_line(i32 indent, char const *fmt, ...) { while (indent --> 0) { gb_printf("\t"); @@ -86,6 +94,13 @@ void print_doc_line_no_newline(i32 indent, char const *fmt, ...) { gb_printf_va(fmt, va); va_end(va); } +void print_doc_line_no_newline(i32 indent, String const &data) { + while (indent --> 0) { + gb_printf("\t"); + } + gb_file_write(gb_file_get_standard(gbFileStandard_Output), data.text, data.len); +} + bool print_doc_comment_group_string(i32 indent, CommentGroup *g) { if (g == nullptr) { @@ -106,8 +121,9 @@ bool print_doc_comment_group_string(i32 indent, CommentGroup *g) { String comment = g->list[i].string; String original_comment = comment; - bool slash_slash = comment[1] == '/'; + bool slash_slash = false; if (comment[1] == '/') { + slash_slash = true; comment.text += 2; comment.len -= 2; } else if (comment[1] == '*') { @@ -131,7 +147,7 @@ bool print_doc_comment_group_string(i32 indent, CommentGroup *g) { } if (slash_slash) { - print_doc_line(indent, "%.*s", LIT(comment)); + print_doc_line(indent, comment); count += 1; } else { isize pos = 0; @@ -143,7 +159,7 @@ bool print_doc_comment_group_string(i32 indent, CommentGroup *g) { } } String line = substring(comment, pos, end); - pos = end+1; + pos = end; String trimmed_line = string_trim_whitespace(line); if (trimmed_line.len == 0) { if (count == 0) { @@ -159,7 +175,7 @@ bool print_doc_comment_group_string(i32 indent, CommentGroup *g) { line = substring(line, 2, line.len); } - print_doc_line(indent, "%.*s", LIT(line)); + print_doc_line(indent, line); count += 1; } } @@ -263,7 +279,7 @@ void print_doc_package(CheckerInfo *info, AstPackage *pkg) { } GB_ASSERT(type_expr != nullptr || init_expr != nullptr); - print_doc_line_no_newline(2, "%.*s", LIT(e->token.string)); + print_doc_line_no_newline(2, e->token.string); if (type_expr != nullptr) { gbString t = expr_to_string(type_expr); gb_printf(": %s ", t); @@ -298,7 +314,7 @@ void print_doc_package(CheckerInfo *info, AstPackage *pkg) { for_array(i, pkg->files) { AstFile *f = pkg->files[i]; String filename = remove_directory_from_path(f->fullpath); - print_doc_line(2, "%.*s", LIT(filename)); + print_doc_line(2, filename); } } diff --git a/src/docs_format.cpp b/src/docs_format.cpp index 5cfac4817..f47fd0945 100644 --- a/src/docs_format.cpp +++ b/src/docs_format.cpp @@ -15,7 +15,7 @@ struct OdinDocVersionType { #define OdinDocVersionType_Major 0 #define OdinDocVersionType_Minor 2 -#define OdinDocVersionType_Patch 2 +#define OdinDocVersionType_Patch 3 struct OdinDocHeaderBase { u8 magic[8]; @@ -172,6 +172,8 @@ enum OdinDocEntityFlag : u64 { OdinDocEntityFlag_Var_Thread_Local = 1ull<<40, OdinDocEntityFlag_Var_Static = 1ull<<41, + + OdinDocEntityFlag_Private = 1ull<<50, }; struct OdinDocEntity { @@ -185,6 +187,7 @@ struct OdinDocEntity { u32 reserved_for_init; OdinDocString comment; OdinDocString docs; + i32 field_group_index; OdinDocEntityIndex foreign_library; OdinDocString link_name; OdinDocArray attributes; diff --git a/src/docs_writer.cpp b/src/docs_writer.cpp index 56ad0561e..0a990cc37 100644 --- a/src/docs_writer.cpp +++ b/src/docs_writer.cpp @@ -292,8 +292,9 @@ bool odin_doc_append_comment_group_string(Array *buf, CommentGroup *g) { String comment = g->list[i].string; String original_comment = comment; - bool slash_slash = comment[1] == '/'; + bool slash_slash = false; if (comment[1] == '/') { + slash_slash = true; comment.text += 2; comment.len -= 2; } else if (comment[1] == '*') { @@ -330,7 +331,7 @@ bool odin_doc_append_comment_group_string(Array *buf, CommentGroup *g) { } } String line = substring(comment, pos, end); - pos = end+1; + pos = end; String trimmed_line = string_trim_whitespace(line); if (trimmed_line.len == 0) { if (count == 0) { @@ -482,7 +483,7 @@ OdinDocTypeIndex odin_doc_type(OdinDocWriter *w, Type *type) { for_array(i, w->type_cache.entries) { // NOTE(bill): THIS IS SLOW Type *other = w->type_cache.entries[i].key; - if (are_types_identical(type, other)) { + if (are_types_identical_unique_tuples(type, other)) { OdinDocTypeIndex index = w->type_cache.entries[i].value; map_set(&w->type_cache, type, index); return index; @@ -511,10 +512,16 @@ OdinDocTypeIndex odin_doc_type(OdinDocWriter *w, Type *type) { doc_type.entities = odin_doc_add_entity_as_slice(w, type->Named.type_name); break; case Type_Generic: - doc_type.kind = OdinDocType_Generic; - doc_type.name = odin_doc_write_string(w, type->Generic.name); - if (type->Generic.specialized) { - doc_type.types = odin_doc_type_as_slice(w, type->Generic.specialized); + { + String name = type->Generic.name; + if (type->Generic.entity) { + name = type->Generic.entity->token.string; + } + doc_type.kind = OdinDocType_Generic; + doc_type.name = odin_doc_write_string(w, name); + if (type->Generic.specialized) { + doc_type.types = odin_doc_type_as_slice(w, type->Generic.specialized); + } } break; case Type_Pointer: @@ -808,7 +815,8 @@ OdinDocEntityIndex odin_doc_add_entity(OdinDocWriter *w, Entity *e) { String link_name = {}; OdinDocEntityKind kind = OdinDocEntity_Invalid; - u32 flags = 0; + u64 flags = 0; + i32 field_group_index = -1; switch (e->kind) { case Entity_Invalid: kind = OdinDocEntity_Invalid; break; @@ -838,6 +846,10 @@ OdinDocEntityIndex odin_doc_add_entity(OdinDocWriter *w, Entity *e) { if (init_expr == nullptr) { init_expr = e->Variable.init_expr; } + field_group_index = e->Variable.field_group_index; + break; + case Entity_Constant: + field_group_index = e->Constant.field_group_index; break; case Entity_Procedure: if (e->Procedure.is_foreign) { flags |= OdinDocEntityFlag_Foreign; } @@ -854,6 +866,9 @@ OdinDocEntityIndex odin_doc_add_entity(OdinDocWriter *w, Entity *e) { if (e->flags & EntityFlag_NoAlias) { flags |= OdinDocEntityFlag_Param_NoAlias; } if (e->flags & EntityFlag_AnyInt) { flags |= OdinDocEntityFlag_Param_AnyInt; } } + if (e->scope && (e->scope->flags & (ScopeFlag_File|ScopeFlag_Pkg)) && !is_entity_exported(e)) { + flags |= OdinDocEntityFlag_Private; + } OdinDocString init_string = {}; if (init_expr) { @@ -882,6 +897,7 @@ OdinDocEntityIndex odin_doc_add_entity(OdinDocWriter *w, Entity *e) { doc_entity.init_string = init_string; doc_entity.comment = odin_doc_comment_group_string(w, comment); doc_entity.docs = odin_doc_comment_group_string(w, docs); + doc_entity.field_group_index = field_group_index; doc_entity.foreign_library = 0; // Set later doc_entity.link_name = odin_doc_write_string(w, link_name); if (e->decl_info != nullptr) { diff --git a/src/entity.cpp b/src/entity.cpp index b39ffc63a..05ee9a33e 100644 --- a/src/entity.cpp +++ b/src/entity.cpp @@ -160,10 +160,12 @@ struct Entity { ExactValue value; ParameterValue param_value; u32 flags; + i32 field_group_index; } Constant; struct { Ast *init_expr; // only used for some variables within procedure bodies i32 field_index; + i32 field_group_index; ParameterValue param_value; diff --git a/src/parser.cpp b/src/parser.cpp index 5bf43cee9..7e7146244 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -5412,6 +5412,15 @@ bool parse_file(Parser *p, AstFile *f) { if (f->package_token.kind != Token_package) { return false; } + if (docs != nullptr) { + TokenPos end = token_pos_end(docs->list[docs->list.count-1]); + if (end.line == f->package_token.pos.line || end.line+1 == f->package_token.pos.line) { + // Okay + } else { + docs = nullptr; + } + } + Token package_name = expect_token_after(f, Token_Ident, "package"); if (package_name.kind == Token_Ident) { if (package_name.string == "_") { diff --git a/src/types.cpp b/src/types.cpp index f621d4346..07951196a 100644 --- a/src/types.cpp +++ b/src/types.cpp @@ -2338,7 +2338,17 @@ Type *strip_type_aliasing(Type *x) { return x; } +bool are_types_identical_internal(Type *x, Type *y, bool check_tuple_names); + bool are_types_identical(Type *x, Type *y) { + return are_types_identical_internal(x, y, false); +} +bool are_types_identical_unique_tuples(Type *x, Type *y) { + return are_types_identical_internal(x, y, true); +} + + +bool are_types_identical_internal(Type *x, Type *y, bool check_tuple_names) { if (x == y) { return true; } @@ -2487,6 +2497,11 @@ bool are_types_identical(Type *x, Type *y) { if (xe->kind != ye->kind || !are_types_identical(xe->type, ye->type)) { return false; } + if (check_tuple_names) { + if (xe->token.string != ye->token.string) { + return false; + } + } if (xe->kind == Entity_Constant && !compare_exact_values(Token_CmpEq, xe->Constant.value, ye->Constant.value)) { // NOTE(bill): This is needed for polymorphic procedures return false; @@ -3933,7 +3948,7 @@ gbString write_type_to_string(gbString str, Type *type) { str = gb_string_appendc(str, " = "); str = write_exact_value_to_string(str, var->Constant.value); } else { - str = gb_string_appendc(str, "="); + str = gb_string_appendc(str, " := "); str = write_exact_value_to_string(str, var->Constant.value); } continue; @@ -3961,14 +3976,10 @@ gbString write_type_to_string(gbString str, Type *type) { str = gb_string_appendc(str, "typeid/"); str = write_type_to_string(str, var->type); } else { - if (var->kind == Entity_TypeName) { - str = gb_string_appendc(str, "$"); - str = gb_string_append_length(str, name.text, name.len); - str = gb_string_appendc(str, "="); - str = write_type_to_string(str, var->type); - } else { - str = gb_string_appendc(str, "typeid"); - } + str = gb_string_appendc(str, "$"); + str = gb_string_append_length(str, name.text, name.len); + str = gb_string_appendc(str, "="); + str = write_type_to_string(str, var->type); } } } diff --git a/tools/odin-html-docs/footer.txt.html b/tools/odin-html-docs/footer.txt.html new file mode 100644 index 000000000..ad73d2834 --- /dev/null +++ b/tools/odin-html-docs/footer.txt.html @@ -0,0 +1,45 @@ + + + + + + + + diff --git a/tools/odin-html-docs/header-lower.txt.html b/tools/odin-html-docs/header-lower.txt.html new file mode 100644 index 000000000..ae5dec861 --- /dev/null +++ b/tools/odin-html-docs/header-lower.txt.html @@ -0,0 +1,36 @@ + + + + + + + + + + + + + + + +
+ +
+
+
\ No newline at end of file diff --git a/tools/odin-html-docs/header.txt.html b/tools/odin-html-docs/header.txt.html new file mode 100644 index 000000000..4bb9036e2 --- /dev/null +++ b/tools/odin-html-docs/header.txt.html @@ -0,0 +1,6 @@ + + + + + + {0:s} \ No newline at end of file diff --git a/tools/odin-html-docs/odin_html_docs_main.odin b/tools/odin-html-docs/odin_html_docs_main.odin new file mode 100644 index 000000000..ec5103363 --- /dev/null +++ b/tools/odin-html-docs/odin_html_docs_main.odin @@ -0,0 +1,1474 @@ +package odin_html_docs + +import doc "core:odin/doc-format" +import "core:fmt" +import "core:io" +import "core:os" +import "core:strings" +import "core:path/slashpath" +import "core:sort" +import "core:slice" +import "core:time" + +GITHUB_LICENSE_URL :: "https://github.com/odin-lang/Odin/tree/master/LICENSE" +GITHUB_CORE_URL :: "https://github.com/odin-lang/Odin/tree/master/core" +GITHUB_VENDOR_URL :: "https://github.com/odin-lang/Odin/tree/master/vendor" +BASE_CORE_URL :: "/core" +BASE_VENDOR_URL :: "/vendor" + +header: ^doc.Header +files: []doc.File +pkgs: []doc.Pkg +entities: []doc.Entity +types: []doc.Type + +core_pkgs_to_use: map[string]^doc.Pkg // trimmed path +vendor_pkgs_to_use: map[string]^doc.Pkg // trimmed path +pkg_to_path: map[^doc.Pkg]string // trimmed path +pkg_to_collection: map[^doc.Pkg]^Collection + +Collection :: struct { + name: string, + pkgs_to_use: ^map[string]^doc.Pkg, + github_url: string, + base_url: string, + root: ^Dir_Node, +} + +array :: proc(a: $A/doc.Array($T)) -> []T { + return doc.from_array(header, a) +} +str :: proc(s: $A/doc.String) -> string { + return doc.from_string(header, s) +} + +errorf :: proc(format: string, args: ..any) -> ! { + fmt.eprintf("%s ", os.args[0]) + fmt.eprintf(format, ..args) + fmt.eprintln() + os.exit(1) +} + +base_type :: proc(t: doc.Type) -> doc.Type { + t := t + for { + if t.kind != .Named { + break + } + t = types[array(t.types)[0]] + } + 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 "" + } + n := max(int) + for str in strs { + n = min(n, len(str)) + } + + prefix := strs[0][:n] + for str in strs[1:] { + for len(prefix) != 0 && str[:len(prefix)] != prefix { + prefix = prefix[:len(prefix)-1] + } + if len(prefix) == 0 { + break + } + } + return prefix +} + +recursive_make_directory :: proc(path: string, prefix := "") { + head, _, tail := strings.partition(path, "/") + path_to_make := head + if prefix != "" { + path_to_make = fmt.tprintf("%s/%s", prefix, head) + } + os.make_directory(path_to_make, 0) + if tail != "" { + recursive_make_directory(tail, path_to_make) + } +} + + +write_html_header :: proc(w: io.Writer, title: string) { + fmt.wprintf(w, string(#load("header.txt.html")), title) + io.write(w, #load("header-lower.txt.html")) +} + +write_html_footer :: proc(w: io.Writer, include_directory_js: bool) { + fmt.wprintf(w, "\n") + + io.write(w, #load("footer.txt.html")) + + if false && include_directory_js { + io.write_string(w, ` +`) + } + + fmt.wprintf(w, "\n\n") +} + +main :: proc() { + if len(os.args) != 2 { + errorf("expected 1 .odin-doc file") + } + data, ok := os.read_entire_file(os.args[1]) + if !ok { + errorf("unable to read file:", os.args[1]) + } + err: doc.Reader_Error + header, err = doc.read_from_bytes(data) + switch err { + case .None: + case .Header_Too_Small: + errorf("file is too small for the file format") + case .Invalid_Magic: + errorf("invalid magic for the file format") + case .Data_Too_Small: + errorf("data is too small for the file format") + case .Invalid_Version: + errorf("invalid file format version") + } + files = array(header.files) + pkgs = array(header.pkgs) + entities = array(header.entities) + types = array(header.types) + + core_collection := &Collection{ + "Core", + &core_pkgs_to_use, + GITHUB_CORE_URL, + BASE_CORE_URL, + nil, + } + vendor_collection := &Collection{ + "Vendor", + &vendor_pkgs_to_use, + GITHUB_VENDOR_URL, + BASE_VENDOR_URL, + nil, + } + + { + fullpaths: [dynamic]string + defer delete(fullpaths) + + for pkg in pkgs[1:] { + append(&fullpaths, str(pkg.fullpath)) + } + path_prefix := common_prefix(fullpaths[:]) + + core_pkgs_to_use = make(map[string]^doc.Pkg) + vendor_pkgs_to_use = make(map[string]^doc.Pkg) + fullpath_loop: for fullpath, i in fullpaths { + path := strings.trim_prefix(fullpath, path_prefix) + pkg := &pkgs[i+1] + if len(array(pkg.entities)) == 0 { + continue fullpath_loop + } + + switch { + case strings.has_prefix(path, "core/"): + trimmed_path := strings.trim_prefix(path, "core/") + if strings.has_prefix(trimmed_path, "sys") { + continue fullpath_loop + } + + core_pkgs_to_use[trimmed_path] = pkg + case strings.has_prefix(path, "vendor/"): + trimmed_path := strings.trim_prefix(path, "vendor/") + if strings.contains(trimmed_path, "/bindings") { + continue fullpath_loop + } + vendor_pkgs_to_use[trimmed_path] = pkg + } + } + for path, pkg in core_pkgs_to_use { + pkg_to_path[pkg] = path + pkg_to_collection[pkg] = core_collection + } + for path, pkg in vendor_pkgs_to_use { + pkg_to_path[pkg] = path + pkg_to_collection[pkg] = vendor_collection + } + } + + b := strings.make_builder() + defer strings.destroy_builder(&b) + w := strings.to_writer(&b) + + { + strings.reset_builder(&b) + write_html_header(w, "Packages - pkg.odin-lang.org") + write_home_page(w) + write_html_footer(w, true) + os.write_entire_file("index.html", b.buf[:]) + } + + core_collection.root = generate_directory_tree(core_pkgs_to_use) + vendor_collection.root = generate_directory_tree(vendor_pkgs_to_use) + + generate_packages(&b, core_collection, "core") + generate_packages(&b, vendor_collection, "vendor") +} + +generate_packages :: proc(b: ^strings.Builder, collection: ^Collection, dir: string) { + w := strings.to_writer(b) + + { + strings.reset_builder(b) + write_html_header(w, fmt.tprintf("%s library - pkg.odin-lang.org", dir)) + write_collection_directory(w, collection) + write_html_footer(w, true) + os.make_directory(dir, 0) + os.write_entire_file(fmt.tprintf("%s/index.html", dir), b.buf[:]) + } + + for path, pkg in collection.pkgs_to_use { + strings.reset_builder(b) + write_html_header(w, fmt.tprintf("package %s - pkg.odin-lang.org", path)) + write_pkg(w, path, pkg, collection) + write_html_footer(w, false) + recursive_make_directory(path, dir) + os.write_entire_file(fmt.tprintf("%s/%s/index.html", dir, path), b.buf[:]) + } +} + + +write_home_sidebar :: proc(w: io.Writer) { + fmt.wprintln(w, ``) + fmt.wprintln(w, `
`) + defer fmt.wprintln(w, `
`) + + fmt.wprintln(w, ``) +} + +write_home_page :: proc(w: io.Writer) { + fmt.wprintln(w, `
`) + defer fmt.wprintln(w, `
`) + + write_home_sidebar(w) + + fmt.wprintln(w, `
`) + defer fmt.wprintln(w, `
`) + + fmt.wprintln(w, "
") + fmt.wprintln(w, "

Odin Packages

") + fmt.wprintln(w, "
") + fmt.wprintln(w, "
") + defer fmt.wprintln(w, "
") + + fmt.wprintln(w, `
`) + fmt.wprintln(w, `

Core Library Collection

`) + fmt.wprintln(w, `

Documentation for all the packages part of the core library collection.

`) + fmt.wprintln(w, `
`) + + fmt.wprintln(w, `
`) + fmt.wprintln(w, `

Vendor Library Collection

`) + fmt.wprintln(w, `

Documentation for all the packages part of the vendor library collection.

`) + fmt.wprintln(w, `
`) + + + +} + + + +Dir_Node :: struct { + dir: string, + path: string, + name: string, + pkg: ^doc.Pkg, + children: [dynamic]^Dir_Node, +} + +generate_directory_tree :: proc(pkgs_to_use: map[string]^doc.Pkg) -> (root: ^Dir_Node) { + sort_tree :: proc(node: ^Dir_Node) { + slice.sort_by_key(node.children[:], proc(node: ^Dir_Node) -> string {return node.name}) + for child in node.children { + sort_tree(child) + } + } + root = new(Dir_Node) + root.children = make([dynamic]^Dir_Node) + children := make([dynamic]^Dir_Node) + for path, pkg in pkgs_to_use { + dir, _, inner := strings.partition(path, "/") + if inner == "" { + node := new_clone(Dir_Node{ + dir = dir, + name = dir, + path = path, + pkg = pkg, + }) + append(&root.children, node) + } else { + node := new_clone(Dir_Node{ + dir = dir, + name = inner, + path = path, + pkg = pkg, + }) + append(&children, node) + } + } + child_loop: for child in children { + dir, _, inner := strings.partition(child.path, "/") + for node in root.children { + if node.dir == dir { + append(&node.children, child) + continue child_loop + } + } + parent := new_clone(Dir_Node{ + dir = dir, + name = dir, + path = dir, + pkg = nil, + }) + append(&root.children, parent) + append(&parent.children, child) + } + + sort_tree(root) + + return +} + +write_collection_directory :: proc(w: io.Writer, collection: ^Collection) { + get_line_doc :: proc(pkg: ^doc.Pkg) -> (line_doc: string, ok: bool) { + if pkg == nil { + return + } + line_doc, _, _ = strings.partition(str(pkg.docs), "\n") + line_doc = strings.trim_space(line_doc) + if line_doc == "" { + return + } + switch { + case strings.has_prefix(line_doc, "*"): + return "", false + case strings.has_prefix(line_doc, "Copyright"): + return "", false + } + return line_doc, true + } + + + fmt.wprintln(w, `
`) + defer fmt.wprintln(w, `
`) + + + write_home_sidebar(w) + + fmt.wprintln(w, `
`) + defer fmt.wprintln(w, `
`) + { + fmt.wprintln(w, `
`) + fmt.wprintln(w, `
`) + fmt.wprintf(w, "

%s Library Collection

\n", collection.name) + fmt.wprintln(w, "
    ") + fmt.wprintf(w, "
  • License: BSD-3-Clause
  • \n", GITHUB_LICENSE_URL) + fmt.wprintf(w, "
  • Repository: {0:s}
  • \n", collection.github_url) + fmt.wprintln(w, "
") + fmt.wprintln(w, "
") + fmt.wprintln(w, "
") + fmt.wprintln(w, `
`) + } + + fmt.wprintln(w, "
") + fmt.wprintln(w, `

Directories

`) + fmt.wprintln(w, "
") + + fmt.wprintln(w, "
") + fmt.wprintln(w, "\t") + fmt.wprintln(w, "\t\t") + + for dir in collection.root.children { + if len(dir.children) != 0 { + fmt.wprint(w, ``) + io.write_string(w, ``) + fmt.wprintf(w, "\n") + + for child in dir.children { + assert(child.pkg != nil) + fmt.wprintf(w, ``) + + line_doc, _, _ := strings.partition(str(child.pkg.docs), "\n") + line_doc = strings.trim_space(line_doc) + io.write_string(w, ``) + + fmt.wprintf(w, "") + fmt.wprintf(w, "\n") + } + } + + fmt.wprintln(w, "\t\t") + fmt.wprintln(w, "\t
`, dir.dir) + } else { + fmt.wprintf(w, `
`, dir.dir) + } + + if dir.pkg != nil { + fmt.wprintf(w, `%s`, collection.base_url, dir.path, dir.name) + } else { + fmt.wprintf(w, "%s", dir.name) + } + io.write_string(w, ``) + if line_doc, ok := get_line_doc(dir.pkg); ok { + write_doc_line(w, line_doc) + } else { + io.write_string(w, ` `) + } + io.write_string(w, `
`, str(child.pkg.name)) + fmt.wprintf(w, `%s`, collection.base_url, child.path, child.name) + io.write_string(w, ``) + if line_doc, ok := get_line_doc(child.pkg); ok { + write_doc_line(w, line_doc) + } else { + io.write_string(w, ` `) + } + io.write_string(w, `
") + fmt.wprintln(w, "
") +} + +is_entity_blank :: proc(e: doc.Entity_Index) -> bool { + name := str(entities[e].name) + return 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) { + write_param_entity :: proc(using writer: ^Type_Writer, e, next_entity: ^doc.Entity, flags: Write_Type_Flags, name_width := 0) { + name := str(e.name) + + write_padding :: proc(w: io.Writer, name: string, name_width: int) { + for _ in 0..`, BASE_CORE_URL) + io.write_string(w, init_string) + io.write_string(w, ``) + case strings.has_prefix(init_string, "context."): + io.write_string(w, name) + io.write_string(w, " := ") + fmt.wprintf(w, ``, BASE_CORE_URL) + io.write_string(w, init_string) + io.write_string(w, ``) + case: + the_type := types[e.type] + type_flags := flags - {.Is_Results} + if .Param_Ellipsis in e.flags { + type_flags += {.Variadic} + } + + #partial switch e.kind { + case .Constant: + assert(name != "") + io.write_byte(w, '$') + io.write_string(w, name) + if name != "" && init_string == "" && next_entity != nil && e.field_group_index >= 0 { + if e.field_group_index == next_entity.field_group_index && e.type == next_entity.type { + return + } + } + + generic_scope[name] = true + if !is_type_untyped(the_type) { + io.write_string(w, ": ") + write_padding(w, name, name_width) + 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 != "" && init_string == "" && next_entity != nil && e.field_group_index >= 0 { + if e.field_group_index == next_entity.field_group_index && e.type == next_entity.type { + io.write_string(w, name) + return + } + } + + if name != "" { + io.write_string(w, name) + io.write_string(w, ": ") + write_padding(w, name, name_width) + } + 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, ": ") + write_padding(w, name, name_width) + 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) + } + } + + if init_string != "" { + io.write_string(w, " = ") + io.write_string(w, init_string) + } + } + } + write_poly_params :: proc(using writer: ^Type_Writer, type: doc.Type, flags: Write_Type_Flags) { + if type.polymorphic_params != 0 { + io.write_byte(w, '(') + write_type(writer, types[type.polymorphic_params], flags+{.Poly_Names}) + 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 { + return + } + for _ in 0.. (name_width: int) { + for entity_index in type_entities { + e := &entities[entity_index] + name := str(e.name) + name_width = max(len(name), name_width) + } + return + } + + + type_entities := array(type.entities) + type_types := array(type.types) + switch type.kind { + case .Invalid: + // ignore + case .Basic: + type_flags := transmute(doc.Type_Flags_Basic)type.flags + if is_type_untyped(type) { + io.write_string(w, str(type.name)) + } else { + fmt.wprintf(w, `%s`, str(type.name)) + } + case .Named: + e := entities[type_entities[0]] + name := str(type.name) + tn_pkg := files[e.pos.file].pkg + collection: Collection // TODO determine this from package + + if tn_pkg != pkg { + fmt.wprintf(w, `%s.`, str(pkgs[tn_pkg].name)) + } + if .Private in e.flags { + io.write_string(w, name) + } else if n := strings.contains_rune(name, '('); n >= 0 { + fmt.wprintf(w, `{1:s}`, pkg_to_path[&pkgs[tn_pkg]], name[:n], collection.base_url) + io.write_string(w, name[n:]) + } else { + fmt.wprintf(w, `{1:s}`, pkg_to_path[&pkgs[tn_pkg]], name, collection.base_url) + } + case .Generic: + name := str(type.name) + if name not_in generic_scope { + io.write_byte(w, '$') + } + io.write_string(w, name) + if name not_in generic_scope && len(array(type.types)) == 1 { + io.write_byte(w, '/') + write_type(writer, types[type_types[0]], flags) + } + case .Pointer: + io.write_byte(w, '^') + write_type(writer, types[type_types[0]], flags) + case .Array: + assert(type.elem_count_len == 1) + io.write_byte(w, '[') + io.write_uint(w, uint(type.elem_counts[0])) + io.write_byte(w, ']') + write_type(writer, types[type_types[0]], flags) + case .Enumerated_Array: + io.write_byte(w, '[') + write_type(writer, types[type_types[0]], flags) + io.write_byte(w, ']') + write_type(writer, types[type_types[1]], flags) + case .Slice: + if .Variadic in flags { + io.write_string(w, "..") + } else { + io.write_string(w, "[]") + } + write_type(writer, types[type_types[0]], flags - {.Variadic}) + case .Dynamic_Array: + io.write_string(w, "[dynamic]") + write_type(writer, types[type_types[0]], flags) + case .Map: + io.write_string(w, "map[") + write_type(writer, types[type_types[0]], flags) + io.write_byte(w, ']') + write_type(writer, types[type_types[1]], flags) + case .Struct: + type_flags := transmute(doc.Type_Flags_Struct)type.flags + io.write_string(w, "struct") + 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 != "" { + io.write_string(w, " #align") + io.write_string(w, custom_align) + } + io.write_string(w, " {") + + tags := array(type.tags) + + if len(type_entities) != 0 { + do_newline(writer, flags) + indent += 1 + name_width := calc_name_width(type_entities) + + for entity_index, i in type_entities { + e := &entities[entity_index] + next_entity: ^doc.Entity = nil + if i+1 < len(type_entities) { + next_entity = &entities[type_entities[i+1]] + } + do_indent(writer, flags) + write_param_entity(writer, e, next_entity, flags, name_width) + + if tag := str(tags[i]); tag != "" { + io.write_byte(w, ' ') + io.write_quoted_string(w, tag) + } + + io.write_byte(w, ',') + do_newline(writer, flags) + } + indent -= 1 + do_indent(writer, flags) + } + io.write_string(w, "}") + case .Union: + type_flags := transmute(doc.Type_Flags_Union)type.flags + io.write_string(w, "union") + 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 != "" { + io.write_string(w, " #align") + io.write_string(w, custom_align) + } + io.write_string(w, " {") + if len(type_types) > 1 { + do_newline(writer, flags) + indent += 1 + for type_index in type_types { + do_indent(writer, flags) + write_type(writer, types[type_index], flags) + io.write_string(w, ", ") + do_newline(writer, flags) + } + indent -= 1 + do_indent(writer, flags) + } + io.write_string(w, "}") + case .Enum: + io.write_string(w, "enum") + if len(type_types) != 0 { + io.write_byte(w, ' ') + write_type(writer, types[type_types[0]], flags) + } + io.write_string(w, " {") + do_newline(writer, flags) + indent += 1 + + name_width := calc_name_width(type_entities) + + for entity_index in type_entities { + e := &entities[entity_index] + + name := str(e.name) + do_indent(writer, flags) + io.write_string(w, name) + + if init_string := str(e.init_string); init_string != "" { + for _ in 0.. 1 || !is_entity_blank(type_entities[0])) + if require_parens { io.write_byte(w, '(') } + for entity_index, i in type_entities { + if i > 0 { + io.write_string(w, ", ") + } + e := &entities[entity_index] + next_entity: ^doc.Entity = nil + if i+1 < len(type_entities) { + next_entity = &entities[type_entities[i+1]] + } + write_param_entity(writer, e, next_entity, flags) + } + if require_parens { io.write_byte(w, ')') } + + case .Proc: + type_flags := transmute(doc.Type_Flags_Proc)type.flags + io.write_string(w, "proc") + cc := str(type.calling_convention) + if cc != "" { + io.write_byte(w, ' ') + io.write_quoted_string(w, cc) + io.write_byte(w, ' ') + } + params := array(type.types)[0] + results := array(type.types)[1] + io.write_byte(w, '(') + write_type(writer, types[params], flags) + io.write_byte(w, ')') + if results != 0 { + assert(.Diverging not_in type_flags) + io.write_string(w, " -> ") + write_type(writer, types[results], flags+{.Is_Results}) + } + if .Diverging in type_flags { + io.write_string(w, " -> !") + } + if .Optional_Ok in type_flags { + io.write_string(w, " #optional_ok") + } + + case .Bit_Set: + type_flags := transmute(doc.Type_Flags_Bit_Set)type.flags + io.write_string(w, "bit_set[") + if .Op_Lt in type_flags { + io.write_uint(w, uint(type.elem_counts[0])) + io.write_string(w, "..<") + io.write_uint(w, uint(type.elem_counts[1])) + } else if .Op_Lt_Eq in type_flags { + io.write_uint(w, uint(type.elem_counts[0])) + io.write_string(w, "..=") + io.write_uint(w, uint(type.elem_counts[1])) + } else { + write_type(writer, types[type_types[0]], flags) + } + if .Underlying_Type in type_flags { + write_type(writer, types[type_types[1]], flags) + } + io.write_string(w, "]") + case .Simd_Vector: + io.write_string(w, "#simd[") + io.write_uint(w, uint(type.elem_counts[0])) + io.write_byte(w, ']') + case .SOA_Struct_Fixed: + io.write_string(w, "#soa[") + io.write_uint(w, uint(type.elem_counts[0])) + io.write_byte(w, ']') + case .SOA_Struct_Slice: + io.write_string(w, "#soa[]") + case .SOA_Struct_Dynamic: + io.write_string(w, "#soa[dynamic]") + case .Relative_Pointer: + io.write_string(w, "#relative(") + write_type(writer, types[type_types[1]], flags) + io.write_string(w, ") ") + write_type(writer, types[type_types[0]], flags) + case .Relative_Slice: + io.write_string(w, "#relative(") + write_type(writer, types[type_types[1]], flags) + io.write_string(w, ") ") + write_type(writer, types[type_types[0]], flags) + case .Multi_Pointer: + io.write_string(w, "[^]") + write_type(writer, types[type_types[0]], flags) + case .Matrix: + io.write_string(w, "matrix[") + io.write_uint(w, uint(type.elem_counts[0])) + io.write_string(w, ", ") + io.write_uint(w, uint(type.elem_counts[1])) + io.write_string(w, "]") + write_type(writer, types[type_types[0]], flags) + } +} + +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, "") + remaining := text[n+1:] + m := strings.index_byte(remaining, '`') + io.write_string(w, remaining[:m]) + io.write_string(w, "") + text = remaining[m+1:] + } else { + io.write_string(w, text) + return + } + } +} + +write_doc_sidebar :: proc(w: io.Writer) { + +} + +write_docs :: proc(w: io.Writer, pkg: ^doc.Pkg, docs: string) { + if docs == "" { + return + } + Block_Kind :: enum { + Paragraph, + Code, + } + Block :: struct { + kind: Block_Kind, + lines: []string, + } + + lines: [dynamic]string + it := docs + for line_ in strings.split_iterator(&it, "\n") { + line := strings.trim_right_space(line_) + append(&lines, line) + } + + curr_block_kind := Block_Kind.Paragraph + start := 0 + blocks: [dynamic]Block + + for line, i in lines { + text := strings.trim_space(line) + switch curr_block_kind { + case .Paragraph: + if strings.has_prefix(line, "\t") { + if i-start > 0 { + append(&blocks, Block{curr_block_kind, lines[start:i]}) + } + curr_block_kind, start = .Code, i + } else if text == "" { + if i-start > 0 { + append(&blocks, Block{curr_block_kind, lines[start:i]}) + } + start = i + } + case .Code: + if text == "" || strings.has_prefix(line, "\t") { + continue + } + + if i-start > 0 { + append(&blocks, Block{curr_block_kind, lines[start:i]}) + } + curr_block_kind, start = .Paragraph, i + } + } + if start < len(lines) { + if len(lines)-start > 0 { + append(&blocks, Block{curr_block_kind, lines[start:]}) + } + } + + for block in &blocks { + trim_amount := 0 + for trim_amount = 0; trim_amount < len(block.lines); trim_amount += 1 { + line := block.lines[trim_amount] + if strings.trim_space(line) != "" { + break + } + } + block.lines = block.lines[trim_amount:] + } + + for block, i in blocks { + if len(block.lines) == 0 { + continue + } + prev_line := "" + if i > 0 { + prev_lines := blocks[i-1].lines + if len(prev_lines) > 0 { + prev_line = prev_lines[len(prev_lines)-1] + } + } + prev_line = strings.trim_space(prev_line) + + lines := block.lines[:] + + end_line := block.lines[len(lines)-1] + if block.kind == .Paragraph && i+1 < len(blocks) { + if strings.has_prefix(end_line, "Example:") && blocks[i+1].kind == .Code { + lines = lines[:len(lines)-1] + } + } + + switch block.kind { + case .Paragraph: + io.write_string(w, "

") + for line, line_idx in lines { + if line_idx > 0 { + io.write_string(w, "\n") + } + io.write_string(w, line) + } + io.write_string(w, "

\n") + case .Code: + all_blank := len(lines) > 0 + for line in lines { + if strings.trim_space(line) != "" { + all_blank = false + } + } + if all_blank { + continue + } + + if strings.has_prefix(prev_line, "Example:") { + io.write_string(w, "
\n") + defer io.write_string(w, "
\n") + io.write_string(w, "Example:\n") + io.write_string(w, `
`)
+				for line in lines {
+					io.write_string(w, strings.trim_prefix(line, "\t"))
+					io.write_string(w, "\n")
+				}
+				io.write_string(w, "
\n") + } else { + io.write_string(w, "
")
+				for line in lines {
+					io.write_string(w, strings.trim_prefix(line, "\t"))
+					io.write_string(w, "\n")
+				}
+				io.write_string(w, "
\n") + } + } + } +} + +write_pkg_sidebar :: proc(w: io.Writer, curr_pkg: ^doc.Pkg, collection: ^Collection) { + + fmt.wprintln(w, ``) + fmt.wprintln(w, `
`) + defer fmt.wprintln(w, `
`) + + fmt.wprintf(w, "

%s Library

\n", collection.name) + + fmt.wprintln(w, `
    `) + defer fmt.wprintln(w, `
`) + + for dir in collection.root.children { + fmt.wprint(w, ``) + if dir.pkg == curr_pkg { + fmt.wprintf(w, `%s`, collection.base_url, dir.path, dir.name) + } else if dir.pkg != nil { + fmt.wprintf(w, `%s`, collection.base_url, dir.path, dir.name) + } else { + fmt.wprintf(w, "%s", dir.name) + } + if len(dir.children) != 0 { + fmt.wprintln(w, "
    ") + defer fmt.wprintln(w, "
\n") + for child in dir.children { + fmt.wprint(w, `
  • `) + defer fmt.wprintln(w, `
  • `) + if child.pkg == curr_pkg { + fmt.wprintf(w, `%s`, collection.base_url, child.path, child.name) + } else if child.pkg != nil { + fmt.wprintf(w, `%s`, collection.base_url, child.path, child.name) + } else { + fmt.wprintf(w, "%s", child.name) + } + } + } + } +} + +write_pkg :: proc(w: io.Writer, path: string, pkg: ^doc.Pkg, collection: ^Collection) { + fmt.wprintln(w, `
    `) + defer fmt.wprintln(w, `
    `) + + write_pkg_sidebar(w, pkg, collection) + + fmt.wprintln(w, `
    `) + + if false { // breadcrumbs + fmt.wprintln(w, `
    `) + defer fmt.wprintln(w, `
    `) + + fmt.wprintln(w, ``) + io.write_string(w, "
      \n") + defer io.write_string(w, "
    \n") + + fmt.wprintf(w, ``, collection.base_url) + + dirs := strings.split(path, "/") + for dir, i in dirs { + url := strings.join(dirs[:i+1], "/") + short_path := strings.join(dirs[1:i+1], "/") + + a_class := "breadcrumb-link" + is_curr := i+1 == len(dirs) + if is_curr { + io.write_string(w, `\n") + } + } + + + fmt.wprintf(w, "

    package core:%s

    \n", path) + overview_docs := strings.trim_space(str(pkg.docs)) + if overview_docs != "" { + fmt.wprintln(w, "

    Overview

    ") + fmt.wprintln(w, "
    ") + defer fmt.wprintln(w, "
    ") + + write_docs(w, pkg, overview_docs) + } + + fmt.wprintln(w, `

    Index

    `) + fmt.wprintln(w, `
    `) + pkg_procs: [dynamic]^doc.Entity + pkg_proc_groups: [dynamic]^doc.Entity + pkg_types: [dynamic]^doc.Entity + pkg_vars: [dynamic]^doc.Entity + pkg_consts: [dynamic]^doc.Entity + + for entity_index in array(pkg.entities) { + e := &entities[entity_index] + name := str(e.name) + if name == "" || name[0] == '_' { + continue + } + switch e.kind { + case .Invalid, .Import_Name, .Library_Name: + // ignore + case .Constant: append(&pkg_consts, e) + case .Variable: append(&pkg_vars, e) + case .Type_Name: append(&pkg_types, e) + case .Procedure: append(&pkg_procs, e) + case .Proc_Group: append(&pkg_proc_groups, e) + } + } + + entity_key :: proc(e: ^doc.Entity) -> string { + return str(e.name) + } + + slice.sort_by_key(pkg_procs[:], entity_key) + slice.sort_by_key(pkg_proc_groups[:], entity_key) + slice.sort_by_key(pkg_types[:], entity_key) + slice.sort_by_key(pkg_vars[:], entity_key) + slice.sort_by_key(pkg_consts[:], entity_key) + + write_index :: proc(w: io.Writer, name: string, entities: []^doc.Entity) { + fmt.wprintln(w, `
    `) + defer fmt.wprintln(w, `
    `) + + + fmt.wprintf(w, `
    `+"\n", name) + fmt.wprintf(w, ``+"\n", name) + io.write_string(w, name) + fmt.wprintln(w, ``) + defer fmt.wprintln(w, `
    `) + + if len(entities) == 0 { + io.write_string(w, "

    This section is empty.

    \n") + } else { + fmt.wprintln(w, "
      ") + for e in entities { + name := str(e.name) + fmt.wprintf(w, "
    • {0:s}
    • \n", name) + } + fmt.wprintln(w, "
    ") + } + } + + entity_ordering := [?]struct{name: string, entities: []^doc.Entity} { + {"Types", pkg_types[:]}, + {"Constants", pkg_consts[:]}, + {"Variables", pkg_vars[:]}, + {"Procedures", pkg_procs[:]}, + {"Procedure Groups", pkg_proc_groups[:]}, + } + + + for eo in entity_ordering { + write_index(w, eo.name, eo.entities) + } + + fmt.wprintln(w, "
    ") + + + 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, "@(") + name := str(attr.name) + value := str(attr.value) + io.write_string(w, name) + if value != "" { + io.write_string(w, "=") + io.write_string(w, value) + } + io.write_string(w, ")\n") + } + } + + pkg_index := files[e.pos.file].pkg + pkg := &pkgs[pkg_index] + writer := &Type_Writer{ + w = w, + pkg = pkg_index, + } + defer delete(writer.generic_scope) + collection := pkg_to_collection[pkg] + github_url := collection.github_url if collection != nil else GITHUB_CORE_URL + + name := str(e.name) + path := pkg_to_path[pkg] + filename := slashpath.base(str(files[e.pos.file].name)) + fmt.wprintf(w, "

    {0:s}", name) + fmt.wprintf(w, " ¶") + if e.pos.file != 0 && e.pos.line > 0 { + src_url := fmt.tprintf("%s/%s/%s#L%d", github_url, path, filename, e.pos.line) + fmt.wprintf(w, "", src_url) + } + fmt.wprintf(w, "

    \n") + fmt.wprintln(w, `
    `) + + switch e.kind { + case .Invalid, .Import_Name, .Library_Name: + // ignore + case .Constant: + fmt.wprint(w, `
    `)
    +			the_type := types[e.type]
    +
    +			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)
    +				write_type(writer, the_type, {.Allow_Indent})
    +				fmt.wprintf(w, " : ")
    +			}
    +
    +
    +			io.write_string(w, init_string)
    +			fmt.wprintln(w, "
    ") + case .Variable: + fmt.wprint(w, `
    `)
    +			write_attributes(w, e)
    +			fmt.wprintf(w, "%s: ", name)
    +			write_type(writer, types[e.type], {.Allow_Indent})
    +			init_string := str(e.init_string)
    +			if init_string != "" {
    +				io.write_string(w, " = ")
    +				io.write_string(w, "…")
    +			}
    +			fmt.wprintln(w, "
    ") + + case .Type_Name: + fmt.wprint(w, `
    `)
    +			fmt.wprintf(w, "%s :: ", name)
    +			the_type := types[e.type]
    +			type_to_print := the_type
    +			if the_type.kind == .Named && .Type_Alias not_in e.flags {
    +				if e.pos == entities[array(the_type.entities)[0]].pos {
    +					bt := base_type(the_type)
    +					#partial switch bt.kind {
    +					case .Struct, .Union, .Proc, .Enum:
    +						// Okay
    +					case:
    +						io.write_string(w, "distinct ")
    +					}
    +					type_to_print = bt
    +				}
    +			}
    +			write_type(writer, type_to_print, {.Allow_Indent})
    +			fmt.wprintln(w, "
    ") + case .Procedure: + fmt.wprint(w, `
    `)
    +			fmt.wprintf(w, "%s :: ", name)
    +			write_type(writer, types[e.type], nil)
    +			write_where_clauses(w, array(e.where_clauses))
    +			fmt.wprint(w, " {…}")
    +			fmt.wprintln(w, "
    ") + case .Proc_Group: + fmt.wprint(w, `
    `)
    +			fmt.wprintf(w, "%s :: proc{{\n", name)
    +			for entity_index in array(e.grouped_entities) {
    +				this_proc := &entities[entity_index]
    +				this_pkg := files[this_proc.pos.file].pkg
    +				io.write_byte(w, '\t')
    +				if this_pkg != pkg_index {
    +					fmt.wprintf(w, "%s.", str(pkgs[this_pkg].name))
    +				}
    +				pkg := &pkgs[this_pkg]
    +				collection := pkg_to_collection[pkg]
    +				name := str(this_proc.name)
    +				fmt.wprintf(w, ``, pkg_to_path[pkg], name, collection.base_url)
    +				io.write_string(w, name)
    +				io.write_string(w, ``)
    +				io.write_byte(w, ',')
    +				io.write_byte(w, '\n')
    +			}
    +			fmt.wprintln(w, "}")
    +			fmt.wprintln(w, "
    ") + + } + fmt.wprintln(w, `
    `) + + the_docs := strings.trim_space(str(e.docs)) + if the_docs != "" { + fmt.wprintln(w, `
    `) + fmt.wprintln(w, ` `) + write_docs(w, pkg, the_docs) + fmt.wprintln(w, `
    `) + } + } + write_entities :: proc(w: io.Writer, title: string, entities: []^doc.Entity) { + fmt.wprintf(w, "

    {0:s}

    \n", title) + fmt.wprintln(w, `
    `) + if len(entities) == 0 { + io.write_string(w, "

    This section is empty.

    \n") + } else { + for e in entities { + fmt.wprintln(w, `
    `) + write_entity(w, e) + fmt.wprintln(w, `
    `) + } + } + fmt.wprintln(w, "
    ") + } + + for eo in entity_ordering { + write_entities(w, eo.name, eo.entities) + } + + fmt.wprintln(w, `

    Source Files

    `) + fmt.wprintln(w, "
      ") + any_hidden := false + source_file_loop: for file_index in array(pkg.files) { + file := files[file_index] + filename := slashpath.base(str(file.name)) + switch { + case + strings.has_suffix(filename, "_windows.odin"), + strings.has_suffix(filename, "_darwin.odin"), + strings.has_suffix(filename, "_essence.odin"), + strings.has_suffix(filename, "_freebsd.odin"), + strings.has_suffix(filename, "_wasi.odin"), + strings.has_suffix(filename, "_js.odin"), + strings.has_suffix(filename, "_freestanding.odin"), + + strings.has_suffix(filename, "_amd64.odin"), + strings.has_suffix(filename, "_i386.odin"), + strings.has_suffix(filename, "_arch64.odin"), + strings.has_suffix(filename, "_wasm32.odin"), + strings.has_suffix(filename, "_wasm64.odin"), + false: + any_hidden = true + continue source_file_loop + } + fmt.wprintf(w, `
    • %s
    • `, collection.github_url, path, filename, filename) + fmt.wprintln(w) + } + if any_hidden { + fmt.wprintln(w, "
    • (hidden platform specific files)
    • ") + } + fmt.wprintln(w, "
    ") + + { + fmt.wprintln(w, `

    Generation Information

    `) + now := time.now() + fmt.wprintf(w, "

    Generated with odin version %s (vendor %q) %s_%s @ %v

    \n", ODIN_VERSION, ODIN_VENDOR, ODIN_OS, ODIN_ARCH, now) + } + + + + fmt.wprintln(w, `
    `) + { + write_link :: proc(w: io.Writer, id, text: string) { + fmt.wprintf(w, `
  • %s`, id, text) + } + + fmt.wprintln(w, ``) + } + +} \ No newline at end of file diff --git a/tools/odin-html-docs/style.css b/tools/odin-html-docs/style.css new file mode 100644 index 000000000..13db2f3c2 --- /dev/null +++ b/tools/odin-html-docs/style.css @@ -0,0 +1,163 @@ +/* doc directories */ + +table.directory { + /*border: 1px solid #ccc!important;*/ + table-layout: fixed; + border-collapse: collapse; +} + +header.collection-header ul { + margin-top: 1em; + margin-bottom: 0; + padding-left: 0.5em; + list-style-type: none; +} + +hr.collection-hr { + margin: 0; + padding: 0; +} + + +.doc-directory tr { + padding-left: 1em!important; + border-top: 1px solid #ccc!important; + border-bottom: 1px solid #ccc!important; +} + +.doc-directory td { + padding: 0.25em 0.5em; +} +.directory-child td { + padding-left: 2em!important; +} + +.directory-child td+td { + position: relative; + left: -1.5em!important; + padding-right: 0; +} + +.doc-directory tr[aria-expanded=true] td.pkg-name:before { + content: "\2193"; +} +.doc-directory tr[aria-expanded=false] td.pkg-name:before { + content: "\2192"!important; +} + +.doc-directory tr[aria-hidden=true] { + display: none; +} + + +/* doc page */ + +pre.doc-code { + white-space: pre-wrap; + word-break: keep-all; + word-wrap: break-word; + tab-size: 8; + background-color: #f8f8f8; + color: #202224; + border: 1px solid #c6c8ca; + border-radius: 0.25rem; + padding: 0.625rem; +} +pre.doc-code a { + font-family: Consolas,Liberation Mono,Menlo,monospace!important; + text-decoration: none; + color: #2179d8; + font-weight: 800; +} + +pre.doc-code a.code-procedure { + color: #047919; +} + +.pkg-line-doc { + color: #444; + width: 100%; +} + + +.doc-source { + display: inline; + float: right; +} + +.doc-source a { + text-decoration: none; + color: #666666; + font-size: 0.75em; +} +.doc-source a:hover { + text-decoration: underline; +} + +a > .a-hidden { + opacity: 0; +} +a:hover > .a-hidden { + opacity: 100; +} +section.documentation h3 { + font-size: calc(1.1rem + .2vw); +} + +.pkg-index h3 { + margin-top: 0 !important; + padding-top: 0 !important; +} + + +.documentation .pkg-entity { + padding-bottom: 0.75rem; + border-bottom: 1px solid #d0d0d0; +} + +details.doc-index > summary { + font-size: 1.75rem; +} +details.doc-index ul { + list-style-type: none; +} + +details.odin-doc-toggle > summary.hideme span:before { + content: "Expand description"; +} +details.odin-doc-toggle[open] > summary.hideme span:before { + content: "Close description"; + opacity: 0.8; +} + +details.odin-doc-toggle[open] > summary.hideme { + margin-bottom: 0.5em; +} + +details.code-example > summary { + font-weight: 700; +} + +@media only screen and (max-width: 991px) { + #pkg-sidebar { + display: none; + } +} + +#pkg-sidebar ul { + list-style-type: none; + padding: 0; +} + +#pkg-sidebar li:not(:last-child) { + margin-bottom: 0.25rem; +} + + +#pkg-sidebar li > ul { + padding-left: 1.25rem; +} + +#pkg-sidebar a.active { + font-style: italic; +} \ No newline at end of file