luv

Workshop wiki

source.lisp

wiki/source.lisp

system luv-wiki · 24 definitions · on GitHub

Browsing the system's source from the wiki.

A source-file is one Lisp file read by the Eclector client: its text, top-level nodes, and definitions. Each becomes a page under source/ drawn entirely as dexp boxes, with every top-level form anchored by its line and a table of its definitions on top, and a sidebar of every system's files beside it; source.html lists the files by system. Symbols that name a definition anywhere in the corpus link to it, "Referenced from" summaries and lisp: links point into these pages, and #ID mentions in code point back at the wiki.

in-package#:luv.wiki
defclasssource-file
pathname:initarg:pathname:accessorsource-file-pathname
relative-path:initarg:relative-path:accessorsource-file-relative-path:documentation

The path relative to the repository root, e.g. "luvcraft/mesher.lisp".

system-name:initarg:system-name:accessorsource-file-system-name
text:initarg:text:accessorsource-file-text
nodes:initarg:nodes:accessorsource-file-nodes
line-starts:initarg:line-starts:accessorsource-file-line-starts
definitions:initarg:definitions:initform'
:accessorsource-file-definitions
:documentation

One Lisp source file of the system as read by luv-wiki.

defmethodprint-object
stream
print-unreadable-object
filestream:typet
formatstream"~A"
source-file-relative-pathfile
defunread-source-file
pathname&keyrelative-pathsystem-name

Read the file at pathname into a source-file with its definitions.

let*
text
uiop:read-file-stringpathname
nodes
read-lisp-stringtext:name
namestringpathname
make-instance'source-file:pathnamepathname:relative-path
orrelative-path
file-namestringpathname
:system-namesystem-name:texttext:nodesnodes:line-startsline-starts:definitions
defunsource-file-package
file

The package named by the file's first IN-PACKAGE form, as written.

dolist
node
source-file-nodesfile
when
let
children
element-childrennode
when
and
symbol-node-name
firstchildren
string-equal
symbol-node-name
firstchildren
"in-package"
secondchildren
return
node-text
secondchildren
defunsource-page-name
file

The site-relative page for file, e.g. "source/luvcraft/mesher.lisp.html".

concatenate'string"source/"
source-file-relative-pathfile
".html"
defunpage-prefix-for
page-name

The relative prefix that leads from page-name back to the site root.

with-output-to-string
out
looprepeat
count#\/page-name
do
write-string"../"out
defundefinition-source-file
andsite
find
definition-pathnamedefinition
site-source-filessite
:key#'source-file-pathname:test#'equal
defundefinition-page-href

The href of definition's line in its source page, or NIL when the site has no page for its file.

let
whenfile
formatnil"~A~A#L~D"*page-prefix*
definition-linedefinition

Rendering

defunrender-definition-entry
definition&keyhref
name-pt

One line of a definitions index: kind, name (or only the method signature when name-p is false, under its generic), with file:line as the tooltip.

let
specializers
definition-specializersdefinition

Trailing T specializers say nothing; drop them.

loopwhile
andspecializers
string=
car
lastspecializers
"t"
do
setfspecializers
butlastspecializers
spinneret:with-html
:li:class
ifname-p"definition-entry""definition-entry method-entry"
:span:class
formatnil"kind kind-~A"
definition-kinddefinition
definition-kinddefinition
:a.name:hrefhref:title
formatnil"~A:~D"
definition-linedefinition
ifname-p
definition-namedefinition
spinneret:html""
when
or
definition-qualifiersdefinition
specializers
:span.signature
formatnil"~{~A ~}~@[(~{~A~^ ~})~]"
definition-qualifiersdefinition
specializers
defungroup-methods
definitions

definitions in order, but with each method attached to the generic (or first method) of the same name: a list of (definition . methods).

let
groups'
index
make-hash-table:test'equalp
dolist
definitiondefinitions
let
name
definition-namedefinition
cond
string=
definition-kinddefinition
"defgeneric"
let
existing
gethashnameindex
if
andexisting
string=
definition-kind
carexisting
"defmethod"

Methods came first: the generic takes over the group.

setf
carexisting
definition
cdrexisting
cons
carexisting
cdrexisting
let
group
setf
gethashnameindex
group
pushgroupgroups
string=
definition-kinddefinition
"defmethod"
let
existing
gethashnameindex
ifexisting
pushdefinition
cdrexisting
let
group
setf
gethashnameindex
group
pushgroupgroups
t
pushgroups
mapcar
lambda
group
cons
cargroup
reverse
cdrgroup
nreversegroups
defunrender-source-toc
file&key
prefix""

The definitions of file as a scannable list linking to their lines, methods grouped under their generic.

let
definitions
source-file-definitionsfile
whendefinitions
spinneret:with-html
:nav.definitions
:ul
loopfor
head.methods
in
group-methodsdefinitions
do
render-definition-entryhead:href
formatnil"~A#L~D"prefix
definition-linehead
dolist
methodmethods
render-definition-entrymethod:href
formatnil"~A#L~D"prefix
definition-linemethod
:name-pnil
defunrender-file-path
path

path as a directory part in the muted face and the file name emphasized.

let
slash
position#\/path:from-endt
spinneret:with-html
whenslash
:span.directory
subseqpath0
:span.file
subseqpath
defunrender-source-sidebar
current&optional

The systems and their files as a compact list beside a source page: one DETAILS per system, the one holding current open and current marked.

spinneret:with-html
:aside.source-nav:aria-label"Source files"
:p.source-nav-title
:a:href
concatenate'string*page-prefix*"source.html"
"Source"
dolist
entry
site-systemssite
let
files
system-entry-filesentry
whenfiles
:details.source-system:open
and
membercurrentfiles
t
:summary
:span.system
short-system-name
system-entry-nameentry
:span.count
formatnil"~D"
lengthfiles
:ul
dolist
filefiles
:li:class
if
eqfilecurrent
"current"nil
:a:href
concatenate'string*page-prefix*
render-file-path
source-file-relative-pathfile
defunrender-source-page
file

Emit the page for file: its definitions table and every top-level form as dexp boxes, each anchored by its starting line, with the sidebar of all files beside it on wide screens.

let*
*page-kind*"source-file"
*page-definition-cards*
make-hash-table:test'eq
line-starts
source-file-line-startsfile
system-name
source-file-system-namefile
title
source-file-relative-pathfile
render-page-frametitle
lambda
spinneret:with-html
:article.source-body
:h1.source-titletitle
:p.source-meta
whensystem-name
spinneret:html"system "
:codesystem-name
spinneret:html" · "
formatnil"~D definition~:P · "
length
source-file-definitionsfile
:a:href
concatenate'string
site-source-url*site*
source-file-relative-pathfile
"on GitHub"
let
:div.lisp.source
dolist
node
source-file-nodesfile
let
:div.toplevel:id
render-figure-cards
loopfordefinitionin
source-file-definitionsfile
append
definition-mentionsdefinition
:body-class"wide source-page":crumbs
append
list
cons"Source""source.html"
whensystem-name
list
conssystem-name
concatenate'string"source.html#"
system-anchorsystem-name
list
constitlenil
:right
lambda
spinneret:with-html
:a:href
concatenate'string
site-source-url*site*
title
file-namestring
source-file-pathnamefile
defvar*file-cards*nil"While the source index renders: files whose definitions get a card."
defunfile-card-id
file
formatnil"file-~A"
substitute-if#\-
lambda
c
memberc'
#\/#\.
source-file-relative-pathfile
defunrender-file-entry
file

A file's relative path linking to its page, the file name emphasized, and a count button that shows the definitions in a popover.

progn
pushfile*file-cards*
spinneret:with-html
:span.file-entry
:a.path:href
render-file-path
source-file-relative-pathfile
:button.count:type"button":data-card:title"definitions"
formatnil"~D"
length
source-file-definitionsfile
defunrender-file-cards

Hidden cards holding each listed file's definitions, for the popover.

spinneret:with-html
:div.figure-cards:hiddent
dolist
file
:div.figure-card.file-card:id
:a.card-title:href
source-file-relative-pathfile
defungraph-systems

The systems worth drawing: not test systems and not the aggregate root system that merely depends on everything.

remove-if
lambda
entry
let
name
system-entry-nameentry
or
search"/test"name
string=name"luv"
site-systemssite
defunshort-system-name
name

name without the leading luv/ that nearly every system shares.

if
starts-with"luv/"name
subseqname4
name
defunsystem-anchor
name

The id of name's row in the source index: the name itself, slashes and all, since luv-wiki-site and luv-wiki are different systems.

concatenate'string"system-"name
defuntransitive-reduction
edges

edges is a list of (from . to). Return the edges not implied by a longer path, so a layered drawing shows only the essential dependencies.

let
successors
make-hash-table:test'equal
loopfor
from.to
inedgesdo
pushto
gethashfromsuccessors
labels
reaches-p
fromto&optionalseen

Is there a path FROM -> ... -> TO of length >= 2?

some
lambda
next
and
not
membernextseen:test#'equal
or
and
not
equalnextto
memberto
gethashnextsuccessors
:test#'equal
and
not
equalnextto
reaches-pnextto
consnextseen
gethashfromsuccessors
remove-if
lambda
edge
reaches-p
caredge
cdredge
edges
defunrender-system-graph

The systems and their essential dependencies as a Mermaid flowchart, fundamentals at the top.

let*
names
mapcar#'system-entry-nameentries
edges
loopforentryinentriesappend
loopfordependencyin
system-entry-depends-onentry
when
memberdependencynames:test#'string=
collect
consdependency
system-entry-nameentry
flet
node
name
substitute#\_#\/name
spinneret:with-html
:pre.mermaid.system-graph
with-output-to-string
out
formatout"%%{init: {\"flowchart\": {\"nodeSpacing\": 14, \"rankSpacing\": 30, \"curve\": \"basis\"}}}%%~%"
formatout"flowchart TB~%"
dolist
namenames
formatout" ~A[\"~A\"]~%"
nodename
loopfor
from.to
inedgesdo
formatout" ~A --> ~A~%"
nodefrom
defunrender-source-index

Emit source.html: the dependency graph of the systems, then one dense table in dependency order — system, description, files — where a file's count opens its definitions in a popover.

let
render-page-frame"Source"
lambda
spinneret:with-html
:h1"Source"
:p.lede

The systems of luv and of the wiki that renders it, fundamentals first. A file's count opens its definitions; symbols in the pages link to their definitions and

:code"#ID"
" mentions link to figures."
:p.graph-note

Dependencies between the systems, essential edges only (test systems and the aggregate

:code"luv"
" left out); names drop the "
:code"luv/"
" prefix."
:table.systems
:thead
:tr
:th"system"
:th"description"
:th"files"
:tbody
dolist
entry
site-systemssite
:tr:id
system-anchor
system-entry-nameentry
:td.system-name
system-entry-nameentry
:td.system-description
or
system-entry-descriptionentry
""
:td.system-files
dolist
file
system-entry-filesentry
:body-class"wide source-index"