diff --git a/README.md b/README.md
index 06bd3c2..43dcd59 100644
--- a/README.md
+++ b/README.md
@@ -59,7 +59,7 @@ UCE pages now use explicit request handlers instead of implicit globals:
Useful related runtime patterns:
-- `render_file(String file_name)` or `render_file(String file_name, Request& context)` to invoke another page
+- `unit_render(String file_name)` or `unit_render(String file_name, Request& context)` to invoke another page
- `context.cfg` for request-local structured configuration
- `context.call` for invocation or message-local structured input
- `context.connection` for broker-owned per-WebSocket-connection state shared across `WS(Request& context)` calls
@@ -101,7 +101,7 @@ Use `= ... ?>` by default for user-visible text. Use `` only for tru
UCE includes a native component layer built on top of ordinary `.uce` files:
- `component(name[, props[, context]])`
-- `render_component(name[, props[, context]])`
+- `component_render(name[, props[, context]])`
- `component_exists(name)`
- `component_resolve(name)`
@@ -121,11 +121,11 @@ When you want returned component markup inside a literal block, prefer:
>
```
-because `= ... ?>` HTML-escapes the returned markup. For direct output from C++ code, use `render_component(...)`.
+because `= ... ?>` HTML-escapes the returned markup. For direct output from C++ code, use `component_render(...)`.
Components expose `COMPONENT(Request& context)` as their default entrypoint and may expose additional named handlers with `COMPONENT:NAME(Request& context)`.
-The component helpers call only `COMPONENT...` handlers. A file meant purely for component use can define `COMPONENT()` without defining `RENDER()`, which keeps direct page entry and component entry cleanly separated. Inside a component file, `component(":NAME", props, context)` and `render_component(":NAME", props, context)` target another named component handler in that same file.
+The component helpers call only `COMPONENT...` handlers. A file meant purely for component use can define `COMPONENT()` without defining `RENDER()`, which keeps direct page entry and component entry cleanly separated. Inside a component file, `component(":NAME", props, context)` and `component_render(":NAME", props, context)` target another named component handler in that same file.
## WebSockets
@@ -254,6 +254,10 @@ SESSION_PATH=/tmp/uce/sessions
FCGI_SOCKET_PATH=/run/uce.sock
FCGI_PORT=9993
+PRECOMPILE_FILES_IN=
+SITE_DIRECTORY=site
+PROACTIVE_COMPILE_CHECK_INTERVAL=60
+
WORKER_COUNT=4
MAX_MEMORY=16777216
SESSION_TIME=2592000
@@ -273,6 +277,14 @@ If you want WebSocket support through nginx, also make sure the built-in HTTP li
HTTP_PORT=8080
```
+Proactive compilation settings:
+
+- `SITE_DIRECTORY=site` tells the runtime which tree to scan on startup for `.uce` files when `PRECOMPILE_FILES_IN` is left empty.
+- `PRECOMPILE_FILES_IN=` can override that startup scan root with a different absolute or runtime-relative directory.
+- `PROACTIVE_COMPILE_CHECK_INTERVAL=60` controls how often the low-priority background compiler rechecks known `.uce` files for stale or missing shared objects.
+
+The runtime keeps a shared known-file registry under `BIN_DIRECTORY` and updates it as request handling discovers new `.uce` files, so proactive recompiles are not limited to the initial startup scan.
+
Recommended deployment notes:
- keep `HTTP_PORT` bound to localhost only at the firewall or by network policy; nginx should be the public entry point
diff --git a/etc/uce/settings.cfg b/etc/uce/settings.cfg
index 4422746..12c3cb6 100644
--- a/etc/uce/settings.cfg
+++ b/etc/uce/settings.cfg
@@ -7,9 +7,16 @@ SESSION_PATH=/tmp/uce/sessions
FCGI_SOCKET_PATH=/run/uce.sock
FCGI_PORT=9993
-# PRECOMPILE .uce FILES
+# OPTIONAL PROACTIVE COMPILE ROOT
+# Leave empty to scan SITE_DIRECTORY relative to the runtime root.
PRECOMPILE_FILES_IN=
+# PUBLIC SITE DIRECTORY USED FOR STARTUP SCAN WHEN PRECOMPILE_FILES_IN IS EMPTY
+SITE_DIRECTORY=site
+
+# PERIODIC KNOWN-.uce RECHECK INTERVAL IN SECONDS
+PROACTIVE_COMPILE_CHECK_INTERVAL=60
+
# SPAWN WORKERS
WORKER_COUNT=4
diff --git a/site/doc/areas/ob.txt b/site/doc/areas/ob.txt
index a394486..8d691d5 100644
--- a/site/doc/areas/ob.txt
+++ b/site/doc/areas/ob.txt
@@ -1,7 +1,7 @@
Output / Invocation Functions
1_RENDER
-call_file
+unit_call
component
component_exists
component_resolve
@@ -11,5 +11,6 @@ ob_get
ob_get_close
ob_start
print
-render_component
-render_file
+component_render
+unit_load
+unit_render
diff --git a/site/doc/areas/runtime.txt b/site/doc/areas/runtime.txt
new file mode 100644
index 0000000..8aa69b9
--- /dev/null
+++ b/site/doc/areas/runtime.txt
@@ -0,0 +1,4 @@
+Runtime
+unit_info
+units_list
+unit_compile
diff --git a/site/doc/areas/session.txt b/site/doc/areas/session.txt
index f19efc8..a226ecd 100644
--- a/site/doc/areas/session.txt
+++ b/site/doc/areas/session.txt
@@ -1,5 +1,5 @@
Sessions
-make_session_id
+session_id_create
session_destroy
session_start
diff --git a/site/doc/areas/sys.txt b/site/doc/areas/sys.txt
index 0313987..3e7ca52 100644
--- a/site/doc/areas/sys.txt
+++ b/site/doc/areas/sys.txt
@@ -8,11 +8,11 @@ file_exists
file_get_contents
file_mtime
file_put_contents
-get_cwd
+cwd_get
ls
mkdir
path_join
-set_cwd
+cwd_set
shell_escape
shell_exec
-unlink
+file_unlink
diff --git a/site/doc/areas/time.txt b/site/doc/areas/time.txt
index 432117d..b085287 100644
--- a/site/doc/areas/time.txt
+++ b/site/doc/areas/time.txt
@@ -1,7 +1,7 @@
Time and Date Functions
-microtime
+time_precise
time
-date
-gmdate
-parse_time
+time_format_local
+time_format_utc
+time_parse
diff --git a/site/doc/areas/uri.txt b/site/doc/areas/uri.txt
index adebcc9..cb9154e 100644
--- a/site/doc/areas/uri.txt
+++ b/site/doc/areas/uri.txt
@@ -1,7 +1,7 @@
URI Functions
encode_query
-make_session_id
+session_id_create
parse_query
uri_decode
uri_encode
diff --git a/site/doc/index.uce b/site/doc/index.uce
index 3ff7c8a..c7c9871 100644
--- a/site/doc/index.uce
+++ b/site/doc/index.uce
@@ -7,15 +7,15 @@ void render_see_section(String name)
{
if(idx == 0)
{
- <>
+ }
+ ?>
}
?>
diff --git a/site/doc/pages/0_context.txt b/site/doc/pages/0_context.txt
index d10b143..920e185 100644
--- a/site/doc/pages/0_context.txt
+++ b/site/doc/pages/0_context.txt
@@ -64,9 +64,8 @@ Whether the request should be logged
f64 stats.time_start
f64 stats.time_end
-:render_file(String file_name, [Request& context])
+:unit_render(String file_name, [Request& context])
Invokes another UCE file using the current or supplied request context
:see
>types
-
diff --git a/site/doc/pages/1_COMPONENT.txt b/site/doc/pages/1_COMPONENT.txt
index 39b3832..b447480 100644
--- a/site/doc/pages/1_COMPONENT.txt
+++ b/site/doc/pages/1_COMPONENT.txt
@@ -4,7 +4,7 @@ COMPONENT(Request& context)
:desc
Defines the default component entrypoint for the current `.uce` file.
-`component()` and `render_component()` invoke `COMPONENT(Request& context)` by default. Named component entrypoints use `COMPONENT:NAME(Request& context)`.
+`component()` and `component_render()` invoke `COMPONENT(Request& context)` by default. Named component entrypoints use `COMPONENT:NAME(Request& context)`.
This keeps page rendering and component rendering separate:
@@ -16,7 +16,7 @@ A file intended only for component reuse can define `COMPONENT()` without defini
Inside component handlers, props arrive through `context.call`.
-When you call `component(":NAME", props, context)` or `render_component(":NAME", props, context)`, the runtime resolves `:NAME` against the current `.uce` file instead of requiring the file name again.
+When you call `component(":NAME", props, context)` or `component_render(":NAME", props, context)`, the runtime resolves `:NAME` against the current `.uce` file instead of requiring the file name again.
Examples:
`COMPONENT(Request& context)`
@@ -31,6 +31,6 @@ Examples:
:see
>component
->render_component
+>component_render
>1_RENDER
>1_WS
diff --git a/site/doc/pages/1_RENDER.txt b/site/doc/pages/1_RENDER.txt
index 1628652..3fcd182 100644
--- a/site/doc/pages/1_RENDER.txt
+++ b/site/doc/pages/1_RENDER.txt
@@ -14,7 +14,7 @@ The request environment is passed explicitly through `context`, including params
For a normal direct page request, `context.call` starts empty.
-If the page is invoked from another UCE file via `render_file(file_name, context)`, the callee receives that same `context`.
+If the page is invoked from another UCE file via `unit_render(file_name, context)`, the callee receives that same `context`.
Pages intended to serve WebSocket traffic may expose both `RENDER(Request& context)` and `WS(Request& context)`. Files may also define `COMPONENT()` handlers when they intentionally need both page and component behavior in one unit. In that case `RENDER(Request& context)` serves the direct HTTP response, `WS(Request& context)` handles subsequent WebSocket messages, and `COMPONENT()` remains available only through the component helpers.
diff --git a/site/doc/pages/1_preprocessor.txt b/site/doc/pages/1_preprocessor.txt
index 3116e44..9609554 100644
--- a/site/doc/pages/1_preprocessor.txt
+++ b/site/doc/pages/1_preprocessor.txt
@@ -74,7 +74,7 @@ The loaded file is resolved relative to the current source file unless the path
- `#load` is recognized only when the current line starts with `#load ` at column 1.
- `EXPORT` harvesting likewise only triggers when the current line starts with `EXPORT` at column 1 and is followed by whitespace.
- Relative `#load` paths are expanded against the including unit's source directory.
-- `render_file()` and `call_file()` are runtime APIs; `#load` is a compile-time include/composition feature.
+- `unit_render()` and `unit_call()` are runtime APIs; `#load` is a compile-time include/composition feature.
:Limitations
- This pass is character-wise, not a full parser.
@@ -92,7 +92,7 @@ The loaded file is resolved relative to the current source file unless the path
:see
load
-render_file
-call_file
+unit_render
+unit_call
0_context
1_COMPONENT
diff --git a/site/doc/pages/DTree.txt b/site/doc/pages/DTree.txt
index 985d286..10787b8 100644
--- a/site/doc/pages/DTree.txt
+++ b/site/doc/pages/DTree.txt
@@ -12,7 +12,7 @@ Common uses include:
`json_decode()` / `json_encode()`
`context.var`
`context.call`
-`call_file()` return values
+`unit_call()` return values
Useful methods include:
`to_string()`
diff --git a/site/doc/pages/component.txt b/site/doc/pages/component.txt
index f0f1199..e95fa75 100644
--- a/site/doc/pages/component.txt
+++ b/site/doc/pages/component.txt
@@ -8,7 +8,7 @@ Renders another `.uce` file as a component and returns the captured output as a
Component props are passed in `context.call`.
-Because `= ... ?>` HTML-escapes its value, embed component markup with ``, `print(component(...))`, or use `render_component(...)` for direct output.
+Because `= ... ?>` HTML-escapes its value, embed component markup with ``, `print(component(...))`, or use `component_render(...)` for direct output.
When `name` contains a colon, such as `components/card:BODY`, the part after the colon selects a named component handler exported from the component file through `COMPONENT:BODY(Request& context)`.
diff --git a/site/doc/pages/render_component.txt b/site/doc/pages/component_render.txt
similarity index 75%
rename from site/doc/pages/render_component.txt
rename to site/doc/pages/component_render.txt
index 9c6fb8d..fa1a7f4 100644
--- a/site/doc/pages/render_component.txt
+++ b/site/doc/pages/component_render.txt
@@ -1,5 +1,5 @@
:sig
-void render_component(String name, [DTree props], [Request& context])
+void component_render(String name, [DTree props], [Request& context])
:desc
Renders another `.uce` file as a component and writes the result directly to the current output buffer.
@@ -10,12 +10,12 @@ Component props are passed through `context.call`, and `name:COMPONENTFUNC` may
When `name` starts with `:`, the runtime resolves that named handler against the current `.uce` file.
-Use `render_component()` when you want to write component output directly from C++ code instead of capturing it as a `String`.
+Use `component_render()` when you want to write component output directly from C++ code instead of capturing it as a `String`.
Example:
`DTree props;`
`props["body"] = "Hello";`
-`render_component("components/card:BODY", props, context);`
+`component_render("components/card:BODY", props, context);`
:see
>ob
diff --git a/site/doc/pages/get_cwd.txt b/site/doc/pages/cwd_get.txt
similarity index 87%
rename from site/doc/pages/get_cwd.txt
rename to site/doc/pages/cwd_get.txt
index 3c68e43..94508e2 100644
--- a/site/doc/pages/get_cwd.txt
+++ b/site/doc/pages/cwd_get.txt
@@ -1,5 +1,5 @@
:sig
-String get_cwd()
+String cwd_get()
:params
return value : the current working directory
diff --git a/site/doc/pages/set_cwd.txt b/site/doc/pages/cwd_set.txt
similarity index 78%
rename from site/doc/pages/set_cwd.txt
rename to site/doc/pages/cwd_set.txt
index 755aca1..c4c856d 100644
--- a/site/doc/pages/set_cwd.txt
+++ b/site/doc/pages/cwd_set.txt
@@ -1,5 +1,5 @@
:sig
-void set_cwd(String path)
+void cwd_set(String path)
:params
path : the new working directory
diff --git a/site/doc/pages/file_unlink.txt b/site/doc/pages/file_unlink.txt
new file mode 100644
index 0000000..446485a
--- /dev/null
+++ b/site/doc/pages/file_unlink.txt
@@ -0,0 +1,11 @@
+:sig
+void file_unlink(String file_name)
+
+:params
+file_name : name of the file
+
+:desc
+Deletes the file identified by `file_name`.
+
+:see
+>sys
diff --git a/site/doc/pages/markdown_to_ast.txt b/site/doc/pages/markdown_to_ast.txt
index d772b54..b67e27b 100644
--- a/site/doc/pages/markdown_to_ast.txt
+++ b/site/doc/pages/markdown_to_ast.txt
@@ -72,6 +72,6 @@ The parser preserves directive data needed by those hooks.
:see
markdown_to_html
component
-render_component
+component_render
json_encode
DTree
diff --git a/site/doc/pages/markdown_to_html.txt b/site/doc/pages/markdown_to_html.txt
index 090b80c..04a153f 100644
--- a/site/doc/pages/markdown_to_html.txt
+++ b/site/doc/pages/markdown_to_html.txt
@@ -95,6 +95,6 @@ This makes directive components a good fit for alerts, callouts, cards, embeds,
:see
markdown_to_ast
component
-render_component
+component_render
json_decode
String
diff --git a/site/doc/pages/make_session_id.txt b/site/doc/pages/session_id_create.txt
similarity index 58%
rename from site/doc/pages/make_session_id.txt
rename to site/doc/pages/session_id_create.txt
index 755a06c..fbb6616 100644
--- a/site/doc/pages/make_session_id.txt
+++ b/site/doc/pages/session_id_create.txt
@@ -1,11 +1,11 @@
:sig
-String make_session_id()
+String session_id_create()
:params
return value : a new session ID
:desc
-Creates a session ID
+Creates a session ID.
:see
>session
diff --git a/site/doc/pages/kill.txt b/site/doc/pages/task_kill.txt
similarity index 81%
rename from site/doc/pages/kill.txt
rename to site/doc/pages/task_kill.txt
index 0094a4f..a4ed75b 100644
--- a/site/doc/pages/kill.txt
+++ b/site/doc/pages/task_kill.txt
@@ -1,5 +1,5 @@
:sig
-s64 kill(pid_t pid, s64 sig)
+int task_kill(pid_t pid, int sig = 0)
:params
pid : PID of the process
@@ -7,7 +7,7 @@ sig : signal number
return value : 0 if signal was sent, -1 otherwise
:desc
-This is the standard POSIX kill() function, provided here for reference.
+Wraps the standard POSIX `kill()` function.
Possible signal numbers are: SIGABND, SIGABRT, SIGALRM, SIGBUS, SIGFPE, SIGHUP, SIGILL, SIGINT, SIGKILL, SIGPIPE, SIGPOLL, SIGPROF, SIGQUIT, SIGSEGV, SIGSYS, SIGTERM, SIGTRAP, SIGURG, SIGUSR1, SIGUSR2, SIGVTALRM, SIGXCPU, SIGXFSZ, SIGCHLD, SIGIO, SIGIOERR, SIGWINCH, SIGSTOP, SIGTSTP, SIGTTIN, SIGTTOU, SIGCONT.
diff --git a/site/doc/pages/date.txt b/site/doc/pages/time_format_local.txt
similarity index 97%
rename from site/doc/pages/date.txt
rename to site/doc/pages/time_format_local.txt
index 7e0c918..d7f57ed 100644
--- a/site/doc/pages/date.txt
+++ b/site/doc/pages/time_format_local.txt
@@ -1,5 +1,5 @@
:sig
-String date(String format = "", u64 timestamp = 0)
+String time_format_local(String format = "", u64 timestamp = 0)
:params
format : formatting string specifying the date format
diff --git a/site/doc/pages/gmdate.txt b/site/doc/pages/time_format_utc.txt
similarity index 98%
rename from site/doc/pages/gmdate.txt
rename to site/doc/pages/time_format_utc.txt
index e29f761..7a870e6 100644
--- a/site/doc/pages/gmdate.txt
+++ b/site/doc/pages/time_format_utc.txt
@@ -1,5 +1,5 @@
:sig
-String gmdate(String format = "", u64 timestamp = 0)
+String time_format_utc(String format = "", u64 timestamp = 0)
:params
format : formatting string specifying the date format
diff --git a/site/doc/pages/parse_time.txt b/site/doc/pages/time_parse.txt
similarity index 86%
rename from site/doc/pages/parse_time.txt
rename to site/doc/pages/time_parse.txt
index a32f7cb..e0c2e06 100644
--- a/site/doc/pages/parse_time.txt
+++ b/site/doc/pages/time_parse.txt
@@ -1,5 +1,5 @@
:sig
-u64 parse_time(String time_string)
+u64 time_parse(String time_string)
:params
time_string : a string containing a date and/or time in text form
diff --git a/site/doc/pages/microtime.txt b/site/doc/pages/time_precise.txt
similarity index 89%
rename from site/doc/pages/microtime.txt
rename to site/doc/pages/time_precise.txt
index 9c42f29..c640f85 100644
--- a/site/doc/pages/microtime.txt
+++ b/site/doc/pages/time_precise.txt
@@ -1,5 +1,5 @@
:sig
-f64 microtime()
+f64 time_precise()
:params
return value : current Unix timestamp
diff --git a/site/doc/pages/call_file.txt b/site/doc/pages/unit_call.txt
similarity index 60%
rename from site/doc/pages/call_file.txt
rename to site/doc/pages/unit_call.txt
index 7e83b01..3e60a6f 100644
--- a/site/doc/pages/call_file.txt
+++ b/site/doc/pages/unit_call.txt
@@ -1,5 +1,5 @@
:sig
-DTree* call_file(String file_name, String function_name, DTree* call_param = null)
+DTree* unit_call(String file_name, String function_name, DTree* call_param = null)
:params
file_name : UCE file to load and execute
@@ -8,16 +8,17 @@ call_param : optional, call parameter
return value : DTree* returned from function
:desc
-Calls a function inside a UCE file.
+Calls an exported function inside a UCE file.
:Example
// export a function
-EXPORT void test_func()
+EXPORT DTree* test_func(DTree* call_param)
{
print("HELLO FROM TEST FUNCTION");
+ return(0);
}
// use that function in another file
-call_file("call_file_funcs.uce", "test_func");
+unit_call("call_file_funcs.uce", "test_func");
:see
>ob
diff --git a/site/doc/pages/unit_compile.txt b/site/doc/pages/unit_compile.txt
new file mode 100644
index 0000000..ca1ecd8
--- /dev/null
+++ b/site/doc/pages/unit_compile.txt
@@ -0,0 +1,15 @@
+:sig
+bool unit_compile(String path = "")
+
+:params
+path : optional UCE unit path. If empty, recompiles the current executing unit.
+return value : `true` when the unit was compiled and loaded successfully
+
+:desc
+Triggers a manual recompile of a UCE compilation unit.
+
+If `path` is relative, it is resolved relative to the current executing unit. Successful manual compiles also refresh the in-memory metadata for that unit.
+
+:see
+unit_info
+units_list
diff --git a/site/doc/pages/unit_info.txt b/site/doc/pages/unit_info.txt
new file mode 100644
index 0000000..d200a9e
--- /dev/null
+++ b/site/doc/pages/unit_info.txt
@@ -0,0 +1,27 @@
+:sig
+DTree unit_info(String path = "")
+
+:params
+path : optional UCE unit path. If empty, uses the current executing unit.
+return value : metadata tree for the resolved unit, or an empty tree if the unit cannot be resolved
+
+:desc
+Returns runtime metadata for a UCE compilation unit.
+
+The returned tree includes:
+
+- normalized paths and generated artifact paths
+- compile status, compile error status, runtime error status, and a combined `error_status`
+- request and invocation counters
+- best, worst, last, and average render time
+- compile counters plus best, worst, last, and average compile time
+- file mtimes, stale status, load status, and exported API declarations
+
+If `path` is relative, it is resolved relative to the current executing unit.
+
+Because unit metadata lives in process memory alongside `SharedUnit`, request and timing counters reflect the current runtime process, not an aggregate across every worker process.
+
+:see
+units_list
+unit_compile
+0_context
diff --git a/site/doc/pages/unit_load.txt b/site/doc/pages/unit_load.txt
new file mode 100644
index 0000000..4b20426
--- /dev/null
+++ b/site/doc/pages/unit_load.txt
@@ -0,0 +1,16 @@
+:sig
+SharedUnit* unit_load(String file_name)
+
+:params
+file_name : UCE file to load
+return value : loaded shared unit, or `null` if the unit could not be loaded
+
+:desc
+Loads a UCE compilation unit and returns its in-memory `SharedUnit` record.
+
+This is a low-level runtime helper. Most application code should prefer `unit_render()`, `unit_call()`, `component()`, or `component_render()`.
+
+:see
+unit_render
+unit_call
+load
diff --git a/site/doc/pages/render_file.txt b/site/doc/pages/unit_render.txt
similarity index 68%
rename from site/doc/pages/render_file.txt
rename to site/doc/pages/unit_render.txt
index 66c5fe7..53c6d3c 100644
--- a/site/doc/pages/render_file.txt
+++ b/site/doc/pages/unit_render.txt
@@ -1,6 +1,6 @@
:sig
-void render_file(String file_name)
-void render_file(String file_name, Request& context)
+void unit_render(String file_name)
+void unit_render(String file_name, Request& context)
:params
file_name : UCE file to load and execute
@@ -13,10 +13,10 @@ If `context` is omitted, the current active request context is used.
:Example
// call a common page template
-render_file("page-template.uce");
+unit_render("page-template.uce");
// explicitly pass a request context
-render_file("page-template.uce", context);
+unit_render("page-template.uce", context);
:see
>ob
diff --git a/site/doc/pages/units_list.txt b/site/doc/pages/units_list.txt
new file mode 100644
index 0000000..560dd5c
--- /dev/null
+++ b/site/doc/pages/units_list.txt
@@ -0,0 +1,11 @@
+:sig
+std::vector units_list()
+
+:desc
+Returns the normalized paths of all known `.uce` units.
+
+This includes the shared known-unit registry plus any units already loaded in the current runtime process.
+
+:see
+unit_info
+unit_compile
diff --git a/site/doc/pages/unlink.txt b/site/doc/pages/unlink.txt
deleted file mode 100644
index 5d80d53..0000000
--- a/site/doc/pages/unlink.txt
+++ /dev/null
@@ -1,11 +0,0 @@
-:sig
-void unlink(String file_name)
-
-:params
-file_name : name of the file
-
-:desc
-Deletes the file identified by 'file_name'.
-
-:see
->sys
diff --git a/site/doc/search.uce b/site/doc/search.uce
new file mode 100644
index 0000000..35128df
--- /dev/null
+++ b/site/doc/search.uce
@@ -0,0 +1,67 @@
+
+// Returns whether haystack contains needle (case-sensitive, both should be pre-lowercased)
+bool str_contains(String haystack, String needle)
+{
+ return replace(haystack, needle, "\x01") != haystack;
+}
+
+RENDER(Request& context)
+{
+ String q = to_lower(trim(context.get["q"]));
+
+ if(q.length() < 2)
+ {
+ return;
+ }
+
+ u32 count = 0;
+
+ for(auto file_name : ls("pages/"))
+ {
+ String ft = nibble(file_name, ".");
+ String name = ft;
+ String badge = "";
+
+ if(ft.substr(0, 2) == "0_")
+ {
+ nibble(name, "_");
+ badge = "struct";
+ }
+ else if(ft.substr(0, 2) == "1_")
+ {
+ nibble(name, "_");
+ badge = "directive";
+ }
+
+ String content = file_get_contents("pages/" + ft + ".txt");
+
+ if(str_contains(to_lower(name), q) || str_contains(to_lower(content), q))
+ {
+ // Find first content line that contains the query for a snippet
+ String snippet = "";
+ for(auto line : split(content, "\n"))
+ {
+ String t = trim(line);
+ if(t.length() > 4 && t.substr(0, 1) != ":" && str_contains(to_lower(t), q))
+ {
+ snippet = t;
+ if(snippet.length() > 100)
+ snippet = snippet.substr(0, 100) + "…";
+ break;
+ }
+ }
+
+ <>