: This version runs on xhtml from pod2html
:
: TODO
:
:
Order name search results by left-most occurence
:
Confirm XQDoc syntax
:
Related articles
:
Related books
:
:
:
: Features
:
:
Search by NAME POD section
:
Smarter search ordering, alphabetical
:
Link sub names on sub search
:
Include current methods in module header
:
Breadcrumb Package Name
:
Related deli.cio.us tags
:
: @author Tony Stubblebine
: @version 0.1
:)
(: ******************************************************* :)
(: * SEARCH ACTIONS :)
(: ******************************************************* :)
(:~
: Search and list modules based on the NAME section
: @param $term search term
: @return xhtml module listing
:)
define function name_search( $term as xs:string ) as element()
{
for $i in input()
where cts:contains($i/html//package, $term)
order by ($i//package)[1]
return
module_listing($i, $term)
}
define function module_listing( $i as node(), $term as xs:string)
as element()
{
let $package := $i//package
let $suffix := substring-after(text {
($i/html//h1[cts:contains(./a, "NAME")]/following-sibling::p)[1]}, " ")
return
}
define function starts_with_search( $term as xs:string ) as element()
{
for $i in input()
let $package := ($i//package)[1]
where starts-with($package, concat($term, "::"))
or $package = $term
and not( contains(substring-after($package, concat($term, "::")), "::") )
order by $package
return module_listing($i, $term)
}
(:~
: Search and list modules based on definied subroutines. Inherited subroutines
: aren't included in the search.
: @params $term search term
: @return xhtml module listing
:)
define function subroutine_search( $term as xs:string ) as element()
{
(: List all matching subroutines :)
for $sub in input()/html//sub[cts:contains(., $term)]
let $package := $sub/ancestor::html/head/package
let $anchor := lookup_sub_anchor($package, $sub)
order by $package
return
}
(: ******************************************************* :)
(: * DISPLAY COMPONENTS :)
(: ******************************************************* :)
(:~
: Show a single package.
: @params $package
: @return xhtml
:)
define function show_package ($package as xs:string) as element()
{
(: TODO: pick the most relevant :)
for $p in (input()/html[@id = $package])[1]
return $p
}
(:~
: Display a search form
: @return xhtml
:)
define function search_form () as element()
{
let $type := xdmp:get-request-field('type', 'name')
let $term := xdmp:get-request-field('term', '')
return
}
(:~
: Create the HTML for the header of a module. Includes parent classes and
: modules.
: TODO: get rid of $x param.
: @params $x insertion point in the document.
: @params $orig original document
:)
define function module_header ($x as element()?, $orig as node()) as element()
{
if (exists($orig/html/head//package)) then
else ""
}
(:~
: Display all the packages in the inheritance tree and all of the methods in
: those packages.
: TODO: Weed out overridden methods.
: @params $package starting package name.
: @return xhtml section.
:)
define function base_list ($package as xs:string) as element()
{
for $p in input()/html[@id = $package]//parent
where string-length($p) > 1
and $p != $package
return method_list($p),
for $p in input()/html[@id = $package]//parent
where string-length($p) > 1
and $p != $package
return base_list($p)
}
(:~
: Display a package and its methods.
: @params $package package name
: @return xhtml package/method section
:)
define function method_list ($package as xs:string) as element()
{
{subs_list(text {$package})}
}
(:~
: List all public subroutines in a package with links to documentation if
: possible.
: @params $package name
: @return xhtml section
:)
define function subs_list ($package as xs:string)
{
for $sub in input()/html[@id = $package]//sub
where not (starts-with($sub, "_"))
order by $sub
return
let $anchor := lookup_sub_anchor($package, $sub) return
if (string-length($anchor) > 1) then
{text {$sub}}()
else {text {$sub}}()
}
(: ******************************************************* :)
(: * LOOKUP FUNCTIONS :)
(: ******************************************************* :)
(:~
: Find the anchor tag name for a subroutine in a package.
: @params $package package name
: @params $sub_name subroutine name
: @return text anchor name
:)
define function lookup_sub_anchor ($package as xs:string, $sub_name as xs:string)
as xs:string
{
(:
TODO - replace contains with starts with sub_name or starts with
item_subname
:)
if (string-length((input()/html[@id = $package]//a[cts:contains(@name, $sub_name)])[1]) > 1)
then
(input()/html[@id = $package]//a[cts:contains(@name, $sub_name)]
)[1]/@name cast as xs:string
else ""
}
(: ******************************************************* :)
(: * DISPATCH FUNCTIONS :)
(: ******************************************************* :)
(:~
: Process an A tag and passthru the contents
: @params $x current node (an A element)
: @params $orig current document
: @return xhtml
:)
define function pass_a ($x as node(), $orig as node()) as element()*
{
if ( matches($x/@href, "^http") ) then
{$x/@*}{passthru($x, $orig)}
else if ( matches($x/@href, "^\w[\w\d:]+")) then
{passthru($x, $orig)}
else
{$x/@*}{passthru($x, $orig)}
}
(:~
: Recursively process stored HTML for display.
: @params $x current element
: @params $orig original document
: @returns xhtml
:)
define function passthru ($x as element()?, $orig as node()) as element()
{
for $z in $x/node() return dispatch($z, $orig)
}
define function package_breadcrumb ($name as xs:string, $prefix as xs:string)
as element()
{
if (contains($name, "::")) then
let $head := substring-before($name, "::")
let $tail := substring-after($name, "::")
let $new_pref := if (string-length($prefix) > 0) then
concat($prefix, "::", $head)
else $head
return
{$head}::{package_breadcrumb($tail, $new_pref)}
else
let $base := if (string-length($prefix) > 0) then
concat($prefix, "::", $name)
else $name
return
{$name}
}
define function pass_ul ($x as element()?, $orig as node()) as element()
{
if ($x = ($orig//ul)[1]) then
{module_header($x, $orig)}
else passthru($x, $orig)
}
(:~
: Dispatch individual elements for custom processing.
: @params $x current element
: @params $orig original document
: @returns xhtml
:)
define function dispatch ($x as node(), $orig as node()) as element()*
{
if (empty($x)) then "" else
typeswitch($x)
case text() return text {$x}
case element (head) return ""
case element (body) return passthru($x, $orig) (: module_header($x, $orig) :)
case element (p) return
{passthru($x, $orig)}
case element (ul) return pass_ul($x, $orig) (:
{passthru($x, $orig)}
:)
case element (li) return
{passthru($x, $orig)}
case element (hr) return
case element (pre) return
{passthru($x, $orig)}
case element (h1) return
{passthru($x, $orig)}
case element (h2) return
{passthru($x, $orig)}
case element (h3) return
{passthru($x, $orig)}
case element (h4) return
{passthru($x, $orig)}
case element (b) return {passthru($x, $orig)}
case element (i) return {passthru($x, $orig)}
case element (strong) return {passthru($x, $orig)}
case element (code) return {passthru($x, $orig)}
case element (dt) return
{passthru($x, $orig)}
case element (dd) return
{passthru($x, $orig)}
case element (br) return
case element (a) return pass_a($x, $orig)
case element (emphasis) return {passthru($x, $orig)}
case element (package) return ""
case element (subroutines) return ""
case element (sub) return ""
case element (source_file) return ""
case element (parent) return ""
default return passthru($x, $orig)
}
define function process_request () as element()
{
let $term := xdmp:get-request-field('term', '')
let $type := xdmp:get-request-field('type', 'name')
let $package := xdmp:get-request-field('package', '')
return
{
if ($package) then show_package($package)
else if ($type = "sub") then subroutine_search($term)
else if ($type = "starts-with") then starts_with_search($term)
else if (string-length($term) > 0) then name_search($term)
else get_intro()
}
}
define function get_intro() as element()
{
What is this?
This is a prototype CPAN search built with XQuery on a MarkLogic XML repository. The XML is actually XHTML generated by running pod2html on all the modules that are installed on my server. That XML is decorated by grepping subroutine and parent classes out of the module. The code.
New Features
Method signature and inheritance structure - what methods are available to call on the module, what module the method is defined in (often a parent class), links to the method documented (if available).
Package breadcrumb - browse up the namespace by clicking on the package name
Related links - just dumb package name to deli.cio.us tag mapping right now
Alphabetical Search Results - it's not contextual but it's predictable.
Subroutine Search - Know the subroutine name but can't remember the module? This is for you.
In the Works
Related Books - list any books that mention the module that you're looking at.
All of CPAN - put all CPAN pod into the DB. This would be a good time to add download links.
Related Articles - list articles that mention the module you're looking at.
Different results order - thinking about ordering search results by left-most appearance in segments of a NAME block (segments being the pieces of the package name and the description that usually follows).
Used By - Count the number of modules that use the current module. A measure of popularity.
Polish - Cleaner HTML. A lot of PRE tags could be combined. Repeated method names in the method signature. Little things.
Versions - POD for each version
}
define function display_page( $body as node(), $sidebar as node() ) as element()
{
POD Search
POD Search
{ search_form() }
{ $body }
{ $sidebar }
}
define function delicious_box ($tag as xs:string) as element()
{
let $url := input()/delicious[tag = lower-case($tag)]//url[@type = "tag"]
return
}
}
define function get_delicious($package as xs:string) as element()
{
if (contains($package, "::")) then
let $head := substring-before($package, "::")
let $tail := get_delicious(substring-after($package, "::"))
return
{delicious_box($head)}{$tail}
else
{delicious_box($package)}
}
define function get_safari($package as xs:string) as element()
{
{
if (count(input()/safari[.//package = $package]//book) > 0) then
Safari Book Excerpts
{
for $b in (input()/safari[.//package = $package]//book)[1 to 5]
return
}
define function get_sidebar ($result as element()) as element()
{
let $term := xdmp:get-request-field('term', '')
return
if (string-length(($result//package)[1]) > 1) then