;; chickadee chicken-doc server ;; Copyright (c) 2010 Jim Ursetto. All Rights Reserved. ;; License: BSD. (module chickadee (chickadee-start-server cdoc-uri chickadee-uri incremental-search-uri chickadee-css-files chickadee-js-files maximum-match-results maximum-match-signatures incremental-search incremental-search-delay cache-nodes-for cache-static-content-for last-modified ajax-log %chickadee:debug-incremental-search-latency ) (import scheme chicken) (import tcp data-structures srfi-1) (use spiffy-request-vars html-tags html-utils chicken-doc) (use spiffy) (use matchable) (use (only uri-generic uri-encode-string)) (use uri-common) (use intarweb) ;(load "chicken-doc-html.scm") (use chicken-doc-html) (use doctype) (use regex) (import irregex) (use (only srfi-13 string-index string-concatenate)) (use (only posix seconds->string seconds->utc-time utc-time->seconds)) (use srfi-18) ;;; Pages (define (input-form) (
class: "lookup" action: (cdoc-page-path) method: 'get ( id: "searchbox" class: (string-append "text incsearch { " "url: \"" (uri->string (incremental-search-uri)) "\"," "delay: " (number->string (incremental-search-delay)) " }") type: "text" name: "q" autocomplete: "off" ;; apparently readonly in DOM autocorrect: "off" autocapitalize: "off" ;; iphone/ipad ) ( class: "button" type: "submit" id: "query-name" name: "query-name" value: "Lookup") ( class: "button" type: "submit" id: "query-regex" name: "query-regex" value: "Regex"))) (define (format-id x) (match (match-nodes x) ((n1) (redirect-to (path->href (node-path n1)))) (() ;; Should we return 404 here? This is not a real resource (node-page #f "" (

"No node found matching identifier " ( (htmlize x))))) (nodes (match-page nodes x)))) (define (format-re x) (match-page (match-nodes (irregex x)) x)) (define (format-path-re x) (match-page (match-node-paths/re (irregex x)) x)) (define (match-page nodes match-text) (let ((max-results (maximum-match-results)) (result-length (length nodes))) (cache-for (cache-nodes-for) ;? (lambda () (last-modified-at (max (repository-modification-time (current-repository)) (last-modified)) (lambda () (node-page (string-append "query " match-text " (" (if (> result-length max-results) (string-append (number->string max-results) " of ") "") (number->string result-length) " matches)") "" ;contents (if (= result-length 0) "" (tree->string (list "" ( (" (") acc))))) "
"path") ( "signature")) (let loop ((sigs (maximum-match-signatures)) (results max-results) (nodes nodes) (acc '())) (if (or (null? nodes) (<= results 0)) (reverse acc) (let ((n (car nodes))) (loop (- sigs 1) (- results 1) (cdr nodes) (cons (list "
class: "match-path" (title-path n)) ( class: "match-sig" ( href: (path->href (node-path n)) (if (<= sigs 0) "-" ( convert-to-entities?: #t (node-signature n))))) "
")))))))))) (define (contents-list n) (let ((p (map ->string (node-path n))) (ids (node-child-ids n))) (if (null? ids) "" (tree->string `("

Contents

\n" "\n" ))))) (define (format-path p) (let ((n (handle-exceptions e #f (lookup-node (string-split p))))) (if n (cache-for ;; NB We send cache-control even with 304s. (cache-nodes-for) (lambda () (last-modified-at ;; Node modification time may also be more fine-grained, ;; but some generated HTML may depend on the entire repository ;; anyway--and we usually update the whole repo at once. (max (repository-modification-time (current-repository)) (last-modified)) (lambda () (if (null? (node-path n)) (node-page #f (contents-list n) (root-page)) (node-page (title-path n) (contents-list n) (chicken-doc-sxml->html (node-sxml n) path->href))))))) (node-not-found p (

"No node found at path " ( (htmlize p))))))) (define (path->href p) ; FIXME: use uri-relative-to, etc (string-append (chickadee-page-path) "/" (string-intersperse (map (lambda (x) (uri-encode-string (->string x))) p) "/"))) (define (title-path n) (let loop ((p (node-path n)) (f '()) (r '())) (if (null? p) (tree->string (reverse r)) (let* ((id (->string (car p))) (f (append f (list id))) (n (lookup-node f))) (loop (cdr p) f (cons (list "href f) "\">" (quote-html id) "" (if (null? (cdr p)) '() " » ")) r)))))) (define (query p) (let ((q (string-split p))) (cond ((null? q) (redirect-to (cdoc-page-path))) ((null? (cdr q)) (format-id p)) (else (redirect-to (path->href q)) ;; (format-path p) )))) ; API defect (define (incremental-search-handler _) (with-request-vars* $ (q) ;; FIXME: doesn't skip 0 or #f incremental-search (let ((M (vector->list ((if (string-index q #\space) match-paths/prefix match-ids/prefix) q (incremental-search))))) (let ((body (if (null? M) "" (let ((plen (string-length q))) (tree->string `("

")))))) ;; Latency pause for debugging (let ((pause (%chickadee:debug-incremental-search-latency))) (if (> pause 0) (thread-sleep! (/ pause 1000)))) ;; Send last-modified headers? May not be worth it. (cache-privately-for ; `private` has no effect on nginx proxy cache (cache-nodes-for) (lambda () ;; (send-response ;; body: body) (parameterize ((access-log (ajax-log))) ; Logging is extremely slow (send-response body: body)))))))) (define (root-page) (++ (

"Search Chicken documentation") (input-form) (

"Enter a documentation node name or path in the search box above." (