├── .gitignore
├── test
├── full
│ ├── mlkit.png
│ ├── sml.pkg
│ └── Makefile
├── sigs.sml
├── about.md
└── Makefile
├── PARSE_ARG.sig
├── sigdoc.mlb
├── sml.pkg
├── Makefile
├── LICENSE
├── jslib
├── style.css
└── jquery-ui.css
├── ParseArg.sml
├── README.md
└── sigdoc.sml
/.gitignore:
--------------------------------------------------------------------------------
1 | MLB
2 | sigdoc
--------------------------------------------------------------------------------
/test/full/mlkit.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/diku-dk/sigdoc/HEAD/test/full/mlkit.png
--------------------------------------------------------------------------------
/PARSE_ARG.sig:
--------------------------------------------------------------------------------
1 | signature PARSE_ARG = sig
2 | datatype t = Nullary of string * (unit -> unit)
3 | | Unary of string * (string -> unit)
4 | | Multi of string * (string list -> unit)
5 | val run : t list -> string list
6 | end
7 |
--------------------------------------------------------------------------------
/sigdoc.mlb:
--------------------------------------------------------------------------------
1 | local
2 | $(SML_LIB)/basis/basis.mlb
3 | lib/github.com/diku-dk/sml-regexp/regexp.mlb
4 | lib/github.com/diku-dk/sml-sort/listsort.mlb
5 | lib/github.com/diku-dk/sml-cstring/cstring.mlb
6 | lib/github.com/diku-dk/sml-setmap/string_map.mlb
7 | PARSE_ARG.sig
8 | ParseArg.sml
9 | in sigdoc.sml
10 | end
11 |
--------------------------------------------------------------------------------
/sml.pkg:
--------------------------------------------------------------------------------
1 | require {
2 | github.com/diku-dk/sml-cstring 0.1.0 #261ac586ddad1d9976a34f86bd12214940799fa7
3 | github.com/diku-dk/sml-setmap 0.1.1 #28f95bb08ecd19f719967c945f63f594b3c9fbc6
4 | github.com/diku-dk/sml-regexp 0.1.0 #f0d0891e746255d701cd9b17e8a6a6b76799c4f9
5 | github.com/diku-dk/sml-sort 0.2.0 #2cb5a201e9ad26e124b66aaafa57352c49f1405a
6 | }
7 |
--------------------------------------------------------------------------------
/Makefile:
--------------------------------------------------------------------------------
1 |
2 | MLCOMP ?= mlkit
3 | SMLPKG ?= smlpkg
4 |
5 | all: sigdoc
6 |
7 | sigdoc: sigdoc.mlb sigdoc.sml Makefile lib
8 | $(MLCOMP) -output sigdoc sigdoc.mlb
9 |
10 | install:
11 | cp -p sigdoc $(DESTDIR)/bin/
12 |
13 | clean:
14 | rm -rf *~ MLB run sigdoc *.html
15 | $(MAKE) -C test clean
16 | $(MAKE) -C test/full clean
17 |
18 | realclean:
19 | $(MAKE) clean
20 | rm -rf lib
21 |
22 | lib: sml.pkg
23 | $(SMLPKG) sync
24 |
--------------------------------------------------------------------------------
/test/sigs.sml:
--------------------------------------------------------------------------------
1 |
2 | (** A signature *)
3 |
4 | signature A = sig
5 | val a : int
6 | end
7 |
8 | (**
9 |
10 | [a] an integer.
11 |
12 | *)
13 |
14 | val hi = 45 (* a top-level binding *)
15 |
16 | structure K : A =
17 | struct val a = 6 end
18 |
19 | (** Another signature *)
20 |
21 | signature B = sig
22 | val b : int
23 | end
24 |
25 | (**
26 |
27 | [b] another integer.
28 |
29 | *)
30 |
31 | structure J : A =
32 | K
33 |
34 | structure S : B =
35 | struct val b = 34 end
36 |
--------------------------------------------------------------------------------
/test/about.md:
--------------------------------------------------------------------------------
1 | This documentation tool provides documentation for the basis library
2 | subset implemented by the [MLKit](http://elsman.com/mlkit) and
3 | available packages, which may be fetched with the
4 | [smlpkg](http://github.com/diku-dk/smlpkg) package manager.
5 |
6 | Some of the documentation specified for modules in the basis library
7 | is borrowed from the [Standard ML Basis
8 | Library](https://smlfamily.github.io/Basis/index.html)
9 | documentation. A physical book specifying the Standard ML Basis
10 | Library is also available [1].
11 |
12 | [1] Gansner, E., & Reppy, J. (Eds.). (2004). The Standard ML Basis
13 | Library. Cambridge: Cambridge University
14 | Press. [10.1017/CBO9780511546846](http://doi.org/10.1017/CBO9780511546846).
15 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2014 Martin Elsman
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
23 |
--------------------------------------------------------------------------------
/test/full/sml.pkg:
--------------------------------------------------------------------------------
1 | require {
2 | github.com/diku-dk/sml-uref 0.0.2 #6881e4ab970febf55d4e684753dfd173b2fb033d
3 | github.com/diku-dk/sml-pickle 0.1.0 #e9a405deb48865cc5ca1cea6fd8e79fbf9fc8851
4 | github.com/diku-dk/sml-setmap 0.1.2 #8549d0d64a4b63b1e7fba3e545c5cac89c2ff447
5 | github.com/diku-dk/sml-sort 0.2.1 #9efbabfd57e2db2514a07e69156d6857efe1a99f
6 | github.com/diku-dk/sml-sobol 0.1.1 #23584dec7d165bf977c55d5aad1056fb585f642d
7 | github.com/diku-dk/sml-random 0.1.1 #0a0f79c857207f948e89e18b320f162e30e1fdaf
8 | github.com/diku-dk/sml-md5 0.1.1 #438d4a6ca20fd6a29c4dd45f247fe073432318d2
9 | github.com/diku-dk/sml-sha256 0.1.0 #e11278f732df62a4ce3e0a7c17e173feb90b3d65
10 | github.com/diku-dk/sml-base64 0.1.1 #575a413424c0a9ba8d3ea4397cb5116dd522cfdc
11 | github.com/diku-dk/sml-hashtable 0.1.1 #2e98df6c57effdbc8c47fa1335e4e543ab412cc2
12 | github.com/diku-dk/sml-cstring 0.1.0 #261ac586ddad1d9976a34f86bd12214940799fa7
13 | github.com/diku-dk/sml-json 0.1.1 #724043b6d05791a62f91ce999e9ebbed22dd227d
14 | github.com/diku-dk/sml-parse 0.1.5 #5693148ec0e4cbba1a1664170d9814f3b63267d9
15 | github.com/diku-dk/sml-regexp 0.1.1 #b91e46945d68c3cb72b6bb002d562a1f57ecc01e
16 | }
17 |
--------------------------------------------------------------------------------
/jslib/style.css:
--------------------------------------------------------------------------------
1 | body {
2 | font-family: "Trebuchet MS", "Helvetica", "Arial", "Verdana", "sans-serif";
3 | }
4 |
5 | .button1 {
6 | background-color: white;
7 | border: none;
8 | padding: 0px;
9 | text-align: bottom;
10 | text-decoration: none;
11 | display: inline-block;
12 | font-size: 16px;
13 | margin: 0px 2px;
14 | cursor: pointer;
15 | border-radius: 10px;
16 | bottom: -5px;
17 | vertical-align:bottom;
18 | position:relative;
19 | }
20 |
21 | .button1:active {
22 | transform: translateY(2px);
23 | }
24 |
25 | .tooltip {
26 | position: relative;
27 | display: inline-block;
28 | }
29 |
30 | .tooltip .tooltiptext {
31 | visibility: hidden;
32 | width: 140px;
33 | background-color: #888;
34 | color: #fff;
35 | text-align: center;
36 | border-radius: 6px;
37 | padding: 4px 0;
38 | position: absolute;
39 | z-index: 1;
40 | bottom: 125%;
41 | left: 50%;
42 | margin-left: -70px;
43 | opacity: 0;
44 | transition: opacity 0.3s;
45 | }
46 |
47 | .tooltip .tooltiptext::after {
48 | content: "";
49 | position: absolute;
50 | top: 100%;
51 | left: 50%;
52 | margin-left: -5px;
53 | border-width: 5px;
54 | border-style: solid;
55 | border-color: #555 transparent transparent transparent;
56 | }
57 |
58 | .tooltip:hover .tooltiptext {
59 | visibility: visible;
60 | opacity: 1;
61 | }
62 |
63 | .vbadge{
64 | background: #11bb44;
65 | font-family: Verdana, sans-serif;
66 | font-style: normal;
67 | padding: 6px 10px 2px 10px;
68 | font-size: 10px;
69 | color: #ffffff;
70 | border-radius: 3px;
71 | line-height: 0;
72 | vertical-align: center;
73 | }
74 |
--------------------------------------------------------------------------------
/test/Makefile:
--------------------------------------------------------------------------------
1 |
2 | SIGDOC ?= ../sigdoc
3 |
4 | BASIS ?= ~/gits/mlkit/basis
5 |
6 | BASISFILES0 = GENERAL.sig General.sml OPTION.sig Option.sml LIST.sig List.sml LIST_PAIR.sml \
7 | ListPair.sml VECTOR.sig Vector.sml VECTOR_SLICE.sml VectorSlice.sml ARRAY.sig \
8 | Array.sml ARRAY_SLICE.sml ArraySlice.sml ARRAY2.sig Array2.sml MONO_VECTOR.sml \
9 | MONO_ARRAY.sml ByteTable.sml MONO_VECTOR_SLICE.sml MONO_ARRAY_SLICE.sml ByteSlice.sml \
10 | MONO_ARRAY2.sml STRING_CVT.sml StringCvt.sml Char.sml String.sml CHAR.sig \
11 | STRING.sig SUBSTRING.sig Substring.sml TEXT.sig Text.sml BOOL.sig Bool.sml \
12 | WORD.sig Word.sml Word64.sml Word63.sml Word32.sml Word31.sml Word8.sml Word16.sml \
13 | PACK_WORD.sml Pack32Little.sml Pack32Big.sml BYTE.sig Byte.sml INTEGER.sml Int.sml \
14 | Int32.sml Int31.sml Int63.sml Int64.sml MATH.sig Math.sml REAL.sig Real.sml \
15 | PACK_REAL.sml PackRealLittle.sml PackRealBig.sml RealArrayVector.sml INT_INF.sml \
16 | IntInf.sml IO.sml TIME.sig Time.sml OS_PATH.sml Path.sml \
17 | OS_FILE_SYS.sml FileSys.sml OS_PROCESS.sml Process.sml OS_IO.sml OS.sml \
18 | COMMAND_LINE.sml CommandLine.sml DATE.sig Date.sml TIMER.sig Timer.sml \
19 | NET_HOST_DB.sig NetHostDB.sml SOCKET.sig Socket.sml INET_SOCK.sig BIT_FLAGS.sml \
20 | POSIX_IO.sml POSIX_PROCESS.sml POSIX_PROCENV.sml POSIX_FILE_SYS.sml POSIX_SIGNAL.sml \
21 | POSIX_ERROR.sml POSIX_SYS_DB.sml POSIX_TTY.sml POSIX.sig Posix.sml io/stream-io.sig \
22 | io/stream-io.sml io/imperative-io.sig io/imperative-io.sml io/text-stream-io.sig \
23 | io/text-io.sig io/text-io.sml io/bin-stream-io.sig io/bin-io.sig io/io-close.sml \
24 | UNIX.sig Unix.sml io/prim-io.sig io/text-prim-io.sml io/bin-prim-io.sml
25 |
26 | BASISFILES:=$(BASISFILES0:%=$(BASIS)/%)
27 |
28 | .PHONY: test
29 | test: about0.html
30 | $(SIGDOC) -about about0.html -libpath ../jslib ../lib/github.com/diku-dk/*/*.{sig,sml} ../lib/github.com/diku-dk/*/*/*.{sig,sml} $(BASISFILES)
31 |
32 | about0.html: about.md
33 | pandoc -o $@ $<
34 |
35 | test0:
36 | $(SIGDOC) -libpath ../jslib $(BASISFILES)
37 |
38 | .PHONY: clean
39 | clean:
40 | rm -rf *~ *.html generated_tags.js
41 |
--------------------------------------------------------------------------------
/test/full/Makefile:
--------------------------------------------------------------------------------
1 | SMLPKG ?= smlpkg
2 |
3 | SIGDOC ?= ../../sigdoc
4 |
5 | BASIS ?= ~/gits/mlkit/basis
6 |
7 | BASISFILES0 = GENERAL.sig General.sml OPTION.sig Option.sml LIST.sig List.sml LIST_PAIR.sml \
8 | ListPair.sml VECTOR.sig Vector.sml VECTOR_SLICE.sml VectorSlice.sml ARRAY.sig \
9 | Array.sml ARRAY_SLICE.sml ArraySlice.sml ARRAY2.sig Array2.sml MONO_VECTOR.sml \
10 | MONO_ARRAY.sml ByteTable.sml MONO_VECTOR_SLICE.sml MONO_ARRAY_SLICE.sml ByteSlice.sml \
11 | MONO_ARRAY2.sml STRING_CVT.sml StringCvt.sml Char.sml String.sml CHAR.sig \
12 | STRING.sig SUBSTRING.sig Substring.sml TEXT.sig Text.sml BOOL.sig Bool.sml \
13 | WORD.sig Word.sml Word64.sml Word63.sml Word32.sml Word31.sml Word8.sml Word16.sml \
14 | PACK_WORD.sml Pack32Little.sml Pack32Big.sml BYTE.sig Byte.sml INTEGER.sml Int.sml \
15 | Int32.sml Int31.sml Int63.sml Int64.sml MATH.sig Math.sml REAL.sig Real.sml IEEE_REAL.sig IEEEReal.sml \
16 | PACK_REAL.sml PackRealLittle.sml PackRealBig.sml RealArrayVector.sml INT_INF.sml \
17 | IntInf.sml IO.sml TIME.sig Time.sml OS_PATH.sml Path.sml \
18 | OS_FILE_SYS.sml FileSys.sml OS_PROCESS.sml Process.sml OS_IO.sml OS.sml \
19 | COMMAND_LINE.sml CommandLine.sml DATE.sig Date.sml TIMER.sig Timer.sml \
20 | NET_HOST_DB.sig NetHostDB.sml SOCKET.sig Socket.sml INET_SOCK.sig BIT_FLAGS.sml \
21 | POSIX_IO.sml POSIX_PROCESS.sml POSIX_PROCENV.sml POSIX_FILE_SYS.sml POSIX_SIGNAL.sml \
22 | POSIX_ERROR.sml POSIX_SYS_DB.sml POSIX_TTY.sml POSIX.sig Posix.sml io/stream-io.sig \
23 | io/stream-io.sml io/imperative-io.sig io/imperative-io.sml io/text-stream-io.sig \
24 | io/text-io.sig io/text-io.sml io/bin-stream-io.sig io/bin-io.sig io/io-close.sml \
25 | UNIX.sig Unix.sml io/prim-io.sig io/text-prim-io.sml io/bin-prim-io.sml
26 |
27 | BASISFILES:=$(BASISFILES0:%=$(BASIS)/%)
28 |
29 | LOGO = '
'
30 |
31 | .PHONY: test
32 | test: about0.html lib jslib
33 | $(SIGDOC) -pkg sml.pkg -logo $(LOGO) -about about0.html -libpath jslib lib/github.com/diku-dk/*/*.{sig,sml} lib/github.com/diku-dk/*/*/*.{sig,sml} $(BASISFILES)
34 |
35 | jslib:
36 | cp -pa ../../jslib .
37 |
38 | about0.html: ../about.md
39 | pandoc -o $@ $<
40 |
41 | lib: sml.pkg
42 | $(SMLPKG) sync
43 |
44 | .PHONY: clean
45 | clean:
46 | rm -rf *~ *.html generated_tags.js jslib
47 |
48 | .PHONY: realclean
49 | realclean:
50 | $(MAKE) clean
51 | rm -rf lib
52 |
--------------------------------------------------------------------------------
/ParseArg.sml:
--------------------------------------------------------------------------------
1 | structure ParseArg : PARSE_ARG = struct
2 | exception Exit of string list
3 | datatype t = Nullary of string * (unit -> unit)
4 | | Unary of string * (string -> unit)
5 | | Multi of string * (string list -> unit)
6 | fun isFlag x = (String.sub(x,0) = #"-") handle _ => false
7 | fun read_args xs =
8 | let fun loop nil a = (rev a, nil)
9 | | loop (x::xs) a =
10 | if isFlag x then (rev a, x::xs)
11 | else loop xs (x::a)
12 | in loop xs nil
13 | end
14 | fun getNullary x (Nullary(y,f)::ts) =
15 | if x = y then SOME f
16 | else getNullary x ts
17 | | getNullary x (t::ts) = getNullary x ts
18 | | getNullary x nil = NONE
19 | fun getUnary x (Unary(y,f)::ts) =
20 | if x = y then SOME f
21 | else getUnary x ts
22 | | getUnary x (t::ts) = getUnary x ts
23 | | getUnary x nil = NONE
24 | fun getMulti x (Multi(y,f)::ts) =
25 | if x = y then SOME f
26 | else getMulti x ts
27 | | getMulti x (t::ts) = getMulti x ts
28 | | getMulti x nil = NONE
29 |
30 | fun err s = (print (s ^ "\n"); raise Fail "ParseArg error")
31 | fun unknown x = err ("Unknown argument '" ^ x ^ "'")
32 | fun run ts =
33 | let
34 | fun loop nil = []
35 | | loop (x::xs) =
36 | if isFlag x then
37 | let val (args, xs) = read_args xs
38 | val () =
39 | case args of
40 | nil =>
41 | (case getNullary x ts of
42 | SOME f => f()
43 | | NONE => unknown x)
44 | | [a] =>
45 | (case getUnary x ts of
46 | SOME f => f a
47 | | NONE =>
48 | (case getMulti x ts of
49 | SOME f => f [a]
50 | | NONE =>
51 | (case getNullary x ts of
52 | SOME f => (f(); raise Exit(a::xs))
53 | | NONE => unknown x)))
54 | | a::aa =>
55 | (case getMulti x ts of
56 | SOME f => f args
57 | | NONE =>
58 | (case getUnary x ts of
59 | SOME f => (f a; raise Exit(aa@xs))
60 | | NONE =>
61 | (case getNullary x ts of
62 | SOME f => (f(); raise Exit(args@xs))
63 | | NONE => unknown x)))
64 | in loop xs
65 | end
66 | else raise Exit(x::xs)
67 | in loop (CommandLine.arguments()) handle Exit ys => ys
68 | end
69 | end
70 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | ## sigdoc
2 |
3 | _A signature documentation tool for Standard ML_
4 |
5 | Sigdoc is a command-line tool (named `sigdoc`) for generating HTML
6 | documentation for Standard ML signatures. It takes as arguments a set
7 | of signature files, containing single signatures, and a set of
8 | implementation files. Sigdoc scans the signature files to obtain the
9 | set of signature identifiers. Then, for each signature identifier, the
10 | tool generates a documentation page with documentation for each
11 | specified identifier. Also, the tool scans the implementation files
12 | for structure identifiers, identifying implementations of the
13 | signatures and generates a documentation page with an index over
14 | structure identifiers (with links to the signature documentation).
15 |
16 | The generated HTML documentation embeds an auto-completing search
17 | field that depends on the jquery library placed in the `jslib/`
18 | subdirectory. For the generated code to work, copy the content of the
19 | `jslib/` directory into the target directory for the documentation.
20 |
21 | Based on the file paths, the tool also tries to identify if the
22 | signature or structure stem from an `smlpkg` package, and if so, a link to the package is embedded in the generated HTML documentation.
23 |
24 | The result of running `sigdoc` on a series of (signature and
25 | implementation) files, is a set of files written to the working
26 | directory.
27 |
28 | Signatures within files must conform to the following snippet
29 | structure:
30 |
31 | (** Header
32 |
33 | General comment, which may be multi-line or empty...
34 |
35 | *)
36 |
37 | signature A = sig
38 | type a
39 | ...
40 | val b : a -> unit
41 | end
42 |
43 | (**
44 |
45 | [type a] is a type.
46 |
47 | [b a] returns unit.
48 |
49 | [Discussion]
50 |
51 | This last entry can be omitted. Also, above it is
52 | possible to include text-blocks using indentation and
53 | it is possible to refer to exception specifications,
54 | structure specifications, include specifications, and
55 | datatype specifications.
56 | *)
57 |
58 | Structure declarations within files are documented if they take one of
59 | the following forms:
60 |
61 | (** SigDoc *)
62 | structure A : B ...
63 |
64 | (** SigDoc *)
65 | structure A :> B ...
66 |
67 | In both cases `B` must be an identified signature identifier. A
68 | structure declaration that occur first in a file (perhaps after a
69 | single comment) is also documented provided the ascribed signature
70 | identifier identifies a signature in the provided files.
71 |
72 | ### Usage
73 |
74 | bash-3.2$ ./sigdoc
75 | Usage: sigdoc [-libpath p] [-about f] [-logo s]
76 | [-pkg f] FILES
77 |
78 | -libpath p : specify the path to the js-library and
79 | style files, relative to the working
80 | directory.
81 | -about f : specify a file with HTML to embed in an
82 | About tab.
83 | -logo s : specify html that presents a logo.
84 | -pkg f : specify path to smlpkg package file to
85 | read package versions from.
86 | FILES include .sml and .sig files, which may contain
87 | signatures and structures.
88 | For further information, please consult the Sigdoc
89 | documentation at http://github.com/diku-dk/sigdoc
90 |
--------------------------------------------------------------------------------
/jslib/jquery-ui.css:
--------------------------------------------------------------------------------
1 | /*! jQuery UI - v1.10.3 - 2013-05-03
2 | * http://jqueryui.com
3 | * Includes: jquery.ui.core.css, jquery.ui.accordion.css, jquery.ui.autocomplete.css, jquery.ui.button.css, jquery.ui.datepicker.css, jquery.ui.dialog.css, jquery.ui.menu.css, jquery.ui.progressbar.css, jquery.ui.resizable.css, jquery.ui.selectable.css, jquery.ui.slider.css, jquery.ui.spinner.css, jquery.ui.tabs.css, jquery.ui.tooltip.css
4 | * To view and modify this theme, visit http://jqueryui.com/themeroller/?ffDefault=Verdana%2CArial%2Csans-serif&fwDefault=normal&fsDefault=1.1em&cornerRadius=4px&bgColorHeader=cccccc&bgTextureHeader=highlight_soft&bgImgOpacityHeader=75&borderColorHeader=aaaaaa&fcHeader=222222&iconColorHeader=222222&bgColorContent=ffffff&bgTextureContent=flat&bgImgOpacityContent=75&borderColorContent=aaaaaa&fcContent=222222&iconColorContent=222222&bgColorDefault=e6e6e6&bgTextureDefault=glass&bgImgOpacityDefault=75&borderColorDefault=d3d3d3&fcDefault=555555&iconColorDefault=888888&bgColorHover=dadada&bgTextureHover=glass&bgImgOpacityHover=75&borderColorHover=999999&fcHover=212121&iconColorHover=454545&bgColorActive=ffffff&bgTextureActive=glass&bgImgOpacityActive=65&borderColorActive=aaaaaa&fcActive=212121&iconColorActive=454545&bgColorHighlight=fbf9ee&bgTextureHighlight=glass&bgImgOpacityHighlight=55&borderColorHighlight=fcefa1&fcHighlight=363636&iconColorHighlight=2e83ff&bgColorError=fef1ec&bgTextureError=glass&bgImgOpacityError=95&borderColorError=cd0a0a&fcError=cd0a0a&iconColorError=cd0a0a&bgColorOverlay=aaaaaa&bgTextureOverlay=flat&bgImgOpacityOverlay=0&opacityOverlay=30&bgColorShadow=aaaaaa&bgTextureShadow=flat&bgImgOpacityShadow=0&opacityShadow=30&thicknessShadow=8px&offsetTopShadow=-8px&offsetLeftShadow=-8px&cornerRadiusShadow=8px
5 | * Copyright 2013 jQuery Foundation and other contributors Licensed MIT */
6 |
7 | /* Layout helpers
8 | ----------------------------------*/
9 | .ui-helper-hidden {
10 | display: none;
11 | }
12 | .ui-helper-hidden-accessible {
13 | border: 0;
14 | clip: rect(0 0 0 0);
15 | height: 1px;
16 | margin: -1px;
17 | overflow: hidden;
18 | padding: 0;
19 | position: absolute;
20 | width: 1px;
21 | }
22 | .ui-helper-reset {
23 | margin: 0;
24 | padding: 0;
25 | border: 0;
26 | outline: 0;
27 | line-height: 1.3;
28 | text-decoration: none;
29 | font-size: 100%;
30 | list-style: none;
31 | }
32 | .ui-helper-clearfix:before,
33 | .ui-helper-clearfix:after {
34 | content: "";
35 | display: table;
36 | border-collapse: collapse;
37 | }
38 | .ui-helper-clearfix:after {
39 | clear: both;
40 | }
41 | .ui-helper-clearfix {
42 | min-height: 0; /* support: IE7 */
43 | }
44 | .ui-helper-zfix {
45 | width: 100%;
46 | height: 100%;
47 | top: 0;
48 | left: 0;
49 | position: absolute;
50 | opacity: 0;
51 | filter:Alpha(Opacity=0);
52 | }
53 |
54 | .ui-front {
55 | z-index: 100;
56 | }
57 |
58 |
59 | /* Interaction Cues
60 | ----------------------------------*/
61 | .ui-state-disabled {
62 | cursor: default !important;
63 | }
64 |
65 |
66 | /* Icons
67 | ----------------------------------*/
68 |
69 | /* states and images */
70 | .ui-icon {
71 | display: block;
72 | text-indent: -99999px;
73 | overflow: hidden;
74 | background-repeat: no-repeat;
75 | }
76 |
77 |
78 | /* Misc visuals
79 | ----------------------------------*/
80 |
81 | /* Overlays */
82 | .ui-widget-overlay {
83 | position: fixed;
84 | top: 0;
85 | left: 0;
86 | width: 100%;
87 | height: 100%;
88 | }
89 | .ui-accordion .ui-accordion-header {
90 | display: block;
91 | cursor: pointer;
92 | position: relative;
93 | margin-top: 2px;
94 | padding: .5em .5em .5em .7em;
95 | min-height: 0; /* support: IE7 */
96 | }
97 | .ui-accordion .ui-accordion-icons {
98 | padding-left: 2.2em;
99 | }
100 | .ui-accordion .ui-accordion-noicons {
101 | padding-left: .7em;
102 | }
103 | .ui-accordion .ui-accordion-icons .ui-accordion-icons {
104 | padding-left: 2.2em;
105 | }
106 | .ui-accordion .ui-accordion-header .ui-accordion-header-icon {
107 | position: absolute;
108 | left: .5em;
109 | top: 50%;
110 | margin-top: -8px;
111 | }
112 | .ui-accordion .ui-accordion-content {
113 | padding: 1em 2.2em;
114 | border-top: 0;
115 | overflow: auto;
116 | }
117 | .ui-autocomplete {
118 | position: absolute;
119 | top: 0;
120 | left: 0;
121 | cursor: default;
122 | }
123 | .ui-button {
124 | display: inline-block;
125 | position: relative;
126 | padding: 0;
127 | line-height: normal;
128 | margin-right: .1em;
129 | cursor: pointer;
130 | vertical-align: middle;
131 | text-align: center;
132 | overflow: visible; /* removes extra width in IE */
133 | }
134 | .ui-button,
135 | .ui-button:link,
136 | .ui-button:visited,
137 | .ui-button:hover,
138 | .ui-button:active {
139 | text-decoration: none;
140 | }
141 | /* to make room for the icon, a width needs to be set here */
142 | .ui-button-icon-only {
143 | width: 2.2em;
144 | }
145 | /* button elements seem to need a little more width */
146 | button.ui-button-icon-only {
147 | width: 2.4em;
148 | }
149 | .ui-button-icons-only {
150 | width: 3.4em;
151 | }
152 | button.ui-button-icons-only {
153 | width: 3.7em;
154 | }
155 |
156 | /* button text element */
157 | .ui-button .ui-button-text {
158 | display: block;
159 | line-height: normal;
160 | }
161 | .ui-button-text-only .ui-button-text {
162 | padding: .4em 1em;
163 | }
164 | .ui-button-icon-only .ui-button-text,
165 | .ui-button-icons-only .ui-button-text {
166 | padding: .4em;
167 | text-indent: -9999999px;
168 | }
169 | .ui-button-text-icon-primary .ui-button-text,
170 | .ui-button-text-icons .ui-button-text {
171 | padding: .4em 1em .4em 2.1em;
172 | }
173 | .ui-button-text-icon-secondary .ui-button-text,
174 | .ui-button-text-icons .ui-button-text {
175 | padding: .4em 2.1em .4em 1em;
176 | }
177 | .ui-button-text-icons .ui-button-text {
178 | padding-left: 2.1em;
179 | padding-right: 2.1em;
180 | }
181 | /* no icon support for input elements, provide padding by default */
182 | input.ui-button {
183 | padding: .4em 1em;
184 | }
185 |
186 | /* button icon element(s) */
187 | .ui-button-icon-only .ui-icon,
188 | .ui-button-text-icon-primary .ui-icon,
189 | .ui-button-text-icon-secondary .ui-icon,
190 | .ui-button-text-icons .ui-icon,
191 | .ui-button-icons-only .ui-icon {
192 | position: absolute;
193 | top: 50%;
194 | margin-top: -8px;
195 | }
196 | .ui-button-icon-only .ui-icon {
197 | left: 50%;
198 | margin-left: -8px;
199 | }
200 | .ui-button-text-icon-primary .ui-button-icon-primary,
201 | .ui-button-text-icons .ui-button-icon-primary,
202 | .ui-button-icons-only .ui-button-icon-primary {
203 | left: .5em;
204 | }
205 | .ui-button-text-icon-secondary .ui-button-icon-secondary,
206 | .ui-button-text-icons .ui-button-icon-secondary,
207 | .ui-button-icons-only .ui-button-icon-secondary {
208 | right: .5em;
209 | }
210 |
211 | /* button sets */
212 | .ui-buttonset {
213 | margin-right: 7px;
214 | }
215 | .ui-buttonset .ui-button {
216 | margin-left: 0;
217 | margin-right: -.3em;
218 | }
219 |
220 | /* workarounds */
221 | /* reset extra padding in Firefox, see h5bp.com/l */
222 | input.ui-button::-moz-focus-inner,
223 | button.ui-button::-moz-focus-inner {
224 | border: 0;
225 | padding: 0;
226 | }
227 | .ui-datepicker {
228 | width: 17em;
229 | padding: .2em .2em 0;
230 | display: none;
231 | }
232 | .ui-datepicker .ui-datepicker-header {
233 | position: relative;
234 | padding: .2em 0;
235 | }
236 | .ui-datepicker .ui-datepicker-prev,
237 | .ui-datepicker .ui-datepicker-next {
238 | position: absolute;
239 | top: 2px;
240 | width: 1.8em;
241 | height: 1.8em;
242 | }
243 | .ui-datepicker .ui-datepicker-prev-hover,
244 | .ui-datepicker .ui-datepicker-next-hover {
245 | top: 1px;
246 | }
247 | .ui-datepicker .ui-datepicker-prev {
248 | left: 2px;
249 | }
250 | .ui-datepicker .ui-datepicker-next {
251 | right: 2px;
252 | }
253 | .ui-datepicker .ui-datepicker-prev-hover {
254 | left: 1px;
255 | }
256 | .ui-datepicker .ui-datepicker-next-hover {
257 | right: 1px;
258 | }
259 | .ui-datepicker .ui-datepicker-prev span,
260 | .ui-datepicker .ui-datepicker-next span {
261 | display: block;
262 | position: absolute;
263 | left: 50%;
264 | margin-left: -8px;
265 | top: 50%;
266 | margin-top: -8px;
267 | }
268 | .ui-datepicker .ui-datepicker-title {
269 | margin: 0 2.3em;
270 | line-height: 1.8em;
271 | text-align: center;
272 | }
273 | .ui-datepicker .ui-datepicker-title select {
274 | font-size: 1em;
275 | margin: 1px 0;
276 | }
277 | .ui-datepicker select.ui-datepicker-month-year {
278 | width: 100%;
279 | }
280 | .ui-datepicker select.ui-datepicker-month,
281 | .ui-datepicker select.ui-datepicker-year {
282 | width: 49%;
283 | }
284 | .ui-datepicker table {
285 | width: 100%;
286 | font-size: .9em;
287 | border-collapse: collapse;
288 | margin: 0 0 .4em;
289 | }
290 | .ui-datepicker th {
291 | padding: .7em .3em;
292 | text-align: center;
293 | font-weight: bold;
294 | border: 0;
295 | }
296 | .ui-datepicker td {
297 | border: 0;
298 | padding: 1px;
299 | }
300 | .ui-datepicker td span,
301 | .ui-datepicker td a {
302 | display: block;
303 | padding: .2em;
304 | text-align: right;
305 | text-decoration: none;
306 | }
307 | .ui-datepicker .ui-datepicker-buttonpane {
308 | background-image: none;
309 | margin: .7em 0 0 0;
310 | padding: 0 .2em;
311 | border-left: 0;
312 | border-right: 0;
313 | border-bottom: 0;
314 | }
315 | .ui-datepicker .ui-datepicker-buttonpane button {
316 | float: right;
317 | margin: .5em .2em .4em;
318 | cursor: pointer;
319 | padding: .2em .6em .3em .6em;
320 | width: auto;
321 | overflow: visible;
322 | }
323 | .ui-datepicker .ui-datepicker-buttonpane button.ui-datepicker-current {
324 | float: left;
325 | }
326 |
327 | /* with multiple calendars */
328 | .ui-datepicker.ui-datepicker-multi {
329 | width: auto;
330 | }
331 | .ui-datepicker-multi .ui-datepicker-group {
332 | float: left;
333 | }
334 | .ui-datepicker-multi .ui-datepicker-group table {
335 | width: 95%;
336 | margin: 0 auto .4em;
337 | }
338 | .ui-datepicker-multi-2 .ui-datepicker-group {
339 | width: 50%;
340 | }
341 | .ui-datepicker-multi-3 .ui-datepicker-group {
342 | width: 33.3%;
343 | }
344 | .ui-datepicker-multi-4 .ui-datepicker-group {
345 | width: 25%;
346 | }
347 | .ui-datepicker-multi .ui-datepicker-group-last .ui-datepicker-header,
348 | .ui-datepicker-multi .ui-datepicker-group-middle .ui-datepicker-header {
349 | border-left-width: 0;
350 | }
351 | .ui-datepicker-multi .ui-datepicker-buttonpane {
352 | clear: left;
353 | }
354 | .ui-datepicker-row-break {
355 | clear: both;
356 | width: 100%;
357 | font-size: 0;
358 | }
359 |
360 | /* RTL support */
361 | .ui-datepicker-rtl {
362 | direction: rtl;
363 | }
364 | .ui-datepicker-rtl .ui-datepicker-prev {
365 | right: 2px;
366 | left: auto;
367 | }
368 | .ui-datepicker-rtl .ui-datepicker-next {
369 | left: 2px;
370 | right: auto;
371 | }
372 | .ui-datepicker-rtl .ui-datepicker-prev:hover {
373 | right: 1px;
374 | left: auto;
375 | }
376 | .ui-datepicker-rtl .ui-datepicker-next:hover {
377 | left: 1px;
378 | right: auto;
379 | }
380 | .ui-datepicker-rtl .ui-datepicker-buttonpane {
381 | clear: right;
382 | }
383 | .ui-datepicker-rtl .ui-datepicker-buttonpane button {
384 | float: left;
385 | }
386 | .ui-datepicker-rtl .ui-datepicker-buttonpane button.ui-datepicker-current,
387 | .ui-datepicker-rtl .ui-datepicker-group {
388 | float: right;
389 | }
390 | .ui-datepicker-rtl .ui-datepicker-group-last .ui-datepicker-header,
391 | .ui-datepicker-rtl .ui-datepicker-group-middle .ui-datepicker-header {
392 | border-right-width: 0;
393 | border-left-width: 1px;
394 | }
395 | .ui-dialog {
396 | position: absolute;
397 | top: 0;
398 | left: 0;
399 | padding: .2em;
400 | outline: 0;
401 | }
402 | .ui-dialog .ui-dialog-titlebar {
403 | padding: .4em 1em;
404 | position: relative;
405 | }
406 | .ui-dialog .ui-dialog-title {
407 | float: left;
408 | margin: .1em 0;
409 | white-space: nowrap;
410 | width: 90%;
411 | overflow: hidden;
412 | text-overflow: ellipsis;
413 | }
414 | .ui-dialog .ui-dialog-titlebar-close {
415 | position: absolute;
416 | right: .3em;
417 | top: 50%;
418 | width: 21px;
419 | margin: -10px 0 0 0;
420 | padding: 1px;
421 | height: 20px;
422 | }
423 | .ui-dialog .ui-dialog-content {
424 | position: relative;
425 | border: 0;
426 | padding: .5em 1em;
427 | background: none;
428 | overflow: auto;
429 | }
430 | .ui-dialog .ui-dialog-buttonpane {
431 | text-align: left;
432 | border-width: 1px 0 0 0;
433 | background-image: none;
434 | margin-top: .5em;
435 | padding: .3em 1em .5em .4em;
436 | }
437 | .ui-dialog .ui-dialog-buttonpane .ui-dialog-buttonset {
438 | float: right;
439 | }
440 | .ui-dialog .ui-dialog-buttonpane button {
441 | margin: .5em .4em .5em 0;
442 | cursor: pointer;
443 | }
444 | .ui-dialog .ui-resizable-se {
445 | width: 12px;
446 | height: 12px;
447 | right: -5px;
448 | bottom: -5px;
449 | background-position: 16px 16px;
450 | }
451 | .ui-draggable .ui-dialog-titlebar {
452 | cursor: move;
453 | }
454 | .ui-menu {
455 | list-style: none;
456 | padding: 2px;
457 | margin: 0;
458 | display: block;
459 | outline: none;
460 | }
461 | .ui-menu .ui-menu {
462 | margin-top: -3px;
463 | position: absolute;
464 | }
465 | .ui-menu .ui-menu-item {
466 | margin: 0;
467 | padding: 0;
468 | width: 100%;
469 | /* support: IE10, see #8844 */
470 | list-style-image: url(data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7);
471 | }
472 | .ui-menu .ui-menu-divider {
473 | margin: 5px -2px 5px -2px;
474 | height: 0;
475 | font-size: 0;
476 | line-height: 0;
477 | border-width: 1px 0 0 0;
478 | }
479 | .ui-menu .ui-menu-item a {
480 | text-decoration: none;
481 | display: block;
482 | padding: 2px .4em;
483 | line-height: 1.5;
484 | min-height: 0; /* support: IE7 */
485 | font-weight: normal;
486 | }
487 | .ui-menu .ui-menu-item a.ui-state-focus,
488 | .ui-menu .ui-menu-item a.ui-state-active {
489 | font-weight: normal;
490 | margin: -1px;
491 | }
492 |
493 | .ui-menu .ui-state-disabled {
494 | font-weight: normal;
495 | margin: .4em 0 .2em;
496 | line-height: 1.5;
497 | }
498 | .ui-menu .ui-state-disabled a {
499 | cursor: default;
500 | }
501 |
502 | /* icon support */
503 | .ui-menu-icons {
504 | position: relative;
505 | }
506 | .ui-menu-icons .ui-menu-item a {
507 | position: relative;
508 | padding-left: 2em;
509 | }
510 |
511 | /* left-aligned */
512 | .ui-menu .ui-icon {
513 | position: absolute;
514 | top: .2em;
515 | left: .2em;
516 | }
517 |
518 | /* right-aligned */
519 | .ui-menu .ui-menu-icon {
520 | position: static;
521 | float: right;
522 | }
523 | .ui-progressbar {
524 | height: 2em;
525 | text-align: left;
526 | overflow: hidden;
527 | }
528 | .ui-progressbar .ui-progressbar-value {
529 | margin: -1px;
530 | height: 100%;
531 | }
532 | .ui-progressbar .ui-progressbar-overlay {
533 | background: url("images/animated-overlay.gif");
534 | height: 100%;
535 | filter: alpha(opacity=25);
536 | opacity: 0.25;
537 | }
538 | .ui-progressbar-indeterminate .ui-progressbar-value {
539 | background-image: none;
540 | }
541 | .ui-resizable {
542 | position: relative;
543 | }
544 | .ui-resizable-handle {
545 | position: absolute;
546 | font-size: 0.1px;
547 | display: block;
548 | }
549 | .ui-resizable-disabled .ui-resizable-handle,
550 | .ui-resizable-autohide .ui-resizable-handle {
551 | display: none;
552 | }
553 | .ui-resizable-n {
554 | cursor: n-resize;
555 | height: 7px;
556 | width: 100%;
557 | top: -5px;
558 | left: 0;
559 | }
560 | .ui-resizable-s {
561 | cursor: s-resize;
562 | height: 7px;
563 | width: 100%;
564 | bottom: -5px;
565 | left: 0;
566 | }
567 | .ui-resizable-e {
568 | cursor: e-resize;
569 | width: 7px;
570 | right: -5px;
571 | top: 0;
572 | height: 100%;
573 | }
574 | .ui-resizable-w {
575 | cursor: w-resize;
576 | width: 7px;
577 | left: -5px;
578 | top: 0;
579 | height: 100%;
580 | }
581 | .ui-resizable-se {
582 | cursor: se-resize;
583 | width: 12px;
584 | height: 12px;
585 | right: 1px;
586 | bottom: 1px;
587 | }
588 | .ui-resizable-sw {
589 | cursor: sw-resize;
590 | width: 9px;
591 | height: 9px;
592 | left: -5px;
593 | bottom: -5px;
594 | }
595 | .ui-resizable-nw {
596 | cursor: nw-resize;
597 | width: 9px;
598 | height: 9px;
599 | left: -5px;
600 | top: -5px;
601 | }
602 | .ui-resizable-ne {
603 | cursor: ne-resize;
604 | width: 9px;
605 | height: 9px;
606 | right: -5px;
607 | top: -5px;
608 | }
609 | .ui-selectable-helper {
610 | position: absolute;
611 | z-index: 100;
612 | border: 1px dotted black;
613 | }
614 | .ui-slider {
615 | position: relative;
616 | text-align: left;
617 | }
618 | .ui-slider .ui-slider-handle {
619 | position: absolute;
620 | z-index: 2;
621 | width: 1.2em;
622 | height: 1.2em;
623 | cursor: default;
624 | }
625 | .ui-slider .ui-slider-range {
626 | position: absolute;
627 | z-index: 1;
628 | font-size: .7em;
629 | display: block;
630 | border: 0;
631 | background-position: 0 0;
632 | }
633 |
634 | /* For IE8 - See #6727 */
635 | .ui-slider.ui-state-disabled .ui-slider-handle,
636 | .ui-slider.ui-state-disabled .ui-slider-range {
637 | filter: inherit;
638 | }
639 |
640 | .ui-slider-horizontal {
641 | height: .8em;
642 | }
643 | .ui-slider-horizontal .ui-slider-handle {
644 | top: -.3em;
645 | margin-left: -.6em;
646 | }
647 | .ui-slider-horizontal .ui-slider-range {
648 | top: 0;
649 | height: 100%;
650 | }
651 | .ui-slider-horizontal .ui-slider-range-min {
652 | left: 0;
653 | }
654 | .ui-slider-horizontal .ui-slider-range-max {
655 | right: 0;
656 | }
657 |
658 | .ui-slider-vertical {
659 | width: .8em;
660 | height: 100px;
661 | }
662 | .ui-slider-vertical .ui-slider-handle {
663 | left: -.3em;
664 | margin-left: 0;
665 | margin-bottom: -.6em;
666 | }
667 | .ui-slider-vertical .ui-slider-range {
668 | left: 0;
669 | width: 100%;
670 | }
671 | .ui-slider-vertical .ui-slider-range-min {
672 | bottom: 0;
673 | }
674 | .ui-slider-vertical .ui-slider-range-max {
675 | top: 0;
676 | }
677 | .ui-spinner {
678 | position: relative;
679 | display: inline-block;
680 | overflow: hidden;
681 | padding: 0;
682 | vertical-align: middle;
683 | }
684 | .ui-spinner-input {
685 | border: none;
686 | background: none;
687 | color: inherit;
688 | padding: 0;
689 | margin: .2em 0;
690 | vertical-align: middle;
691 | margin-left: .4em;
692 | margin-right: 22px;
693 | }
694 | .ui-spinner-button {
695 | width: 16px;
696 | height: 50%;
697 | font-size: .5em;
698 | padding: 0;
699 | margin: 0;
700 | text-align: center;
701 | position: absolute;
702 | cursor: default;
703 | display: block;
704 | overflow: hidden;
705 | right: 0;
706 | }
707 | /* more specificity required here to overide default borders */
708 | .ui-spinner a.ui-spinner-button {
709 | border-top: none;
710 | border-bottom: none;
711 | border-right: none;
712 | }
713 | /* vertical centre icon */
714 | .ui-spinner .ui-icon {
715 | position: absolute;
716 | margin-top: -8px;
717 | top: 50%;
718 | left: 0;
719 | }
720 | .ui-spinner-up {
721 | top: 0;
722 | }
723 | .ui-spinner-down {
724 | bottom: 0;
725 | }
726 |
727 | /* TR overrides */
728 | .ui-spinner .ui-icon-triangle-1-s {
729 | /* need to fix icons sprite */
730 | background-position: -65px -16px;
731 | }
732 | .ui-tabs {
733 | position: relative;/* position: relative prevents IE scroll bug (element with position: relative inside container with overflow: auto appear as "fixed") */
734 | padding: .2em;
735 | }
736 | .ui-tabs .ui-tabs-nav {
737 | margin: 0;
738 | padding: .2em .2em 0;
739 | }
740 | .ui-tabs .ui-tabs-nav li {
741 | list-style: none;
742 | float: left;
743 | position: relative;
744 | top: 0;
745 | margin: 1px .2em 0 0;
746 | border-bottom-width: 0;
747 | padding: 0;
748 | white-space: nowrap;
749 | }
750 | .ui-tabs .ui-tabs-nav li a {
751 | float: left;
752 | padding: .5em 1em;
753 | text-decoration: none;
754 | }
755 | .ui-tabs .ui-tabs-nav li.ui-tabs-active {
756 | margin-bottom: -1px;
757 | padding-bottom: 1px;
758 | }
759 | .ui-tabs .ui-tabs-nav li.ui-tabs-active a,
760 | .ui-tabs .ui-tabs-nav li.ui-state-disabled a,
761 | .ui-tabs .ui-tabs-nav li.ui-tabs-loading a {
762 | cursor: text;
763 | }
764 | .ui-tabs .ui-tabs-nav li a, /* first selector in group seems obsolete, but required to overcome bug in Opera applying cursor: text overall if defined elsewhere... */
765 | .ui-tabs-collapsible .ui-tabs-nav li.ui-tabs-active a {
766 | cursor: pointer;
767 | }
768 | .ui-tabs .ui-tabs-panel {
769 | display: block;
770 | border-width: 0;
771 | padding: 1em 1.4em;
772 | background: none;
773 | }
774 | .ui-tooltip {
775 | padding: 8px;
776 | position: absolute;
777 | z-index: 9999;
778 | max-width: 300px;
779 | -webkit-box-shadow: 0 0 5px #aaa;
780 | box-shadow: 0 0 5px #aaa;
781 | }
782 | body .ui-tooltip {
783 | border-width: 2px;
784 | }
785 |
786 | /* Component containers
787 | ----------------------------------*/
788 | .ui-widget {
789 | font-family: Verdana,Arial,sans-serif;
790 | font-size: 1.1em;
791 | }
792 | .ui-widget .ui-widget {
793 | font-size: 1em;
794 | }
795 | .ui-widget input,
796 | .ui-widget select,
797 | .ui-widget textarea,
798 | .ui-widget button {
799 | font-family: Verdana,Arial,sans-serif;
800 | font-size: 1em;
801 | }
802 | .ui-widget-content {
803 | border: 1px solid #aaaaaa;
804 | background: #ffffff url(images/ui-bg_flat_75_ffffff_40x100.png) 50% 50% repeat-x;
805 | color: #222222;
806 | }
807 | .ui-widget-content a {
808 | color: #222222;
809 | }
810 | .ui-widget-header {
811 | border: 1px solid #aaaaaa;
812 | background: #cccccc url(images/ui-bg_highlight-soft_75_cccccc_1x100.png) 50% 50% repeat-x;
813 | color: #222222;
814 | font-weight: bold;
815 | }
816 | .ui-widget-header a {
817 | color: #222222;
818 | }
819 |
820 | /* Interaction states
821 | ----------------------------------*/
822 | .ui-state-default,
823 | .ui-widget-content .ui-state-default,
824 | .ui-widget-header .ui-state-default {
825 | border: 1px solid #d3d3d3;
826 | background: #e6e6e6 url(images/ui-bg_glass_75_e6e6e6_1x400.png) 50% 50% repeat-x;
827 | font-weight: normal;
828 | color: #555555;
829 | }
830 | .ui-state-default a,
831 | .ui-state-default a:link,
832 | .ui-state-default a:visited {
833 | color: #555555;
834 | text-decoration: none;
835 | }
836 | .ui-state-hover,
837 | .ui-widget-content .ui-state-hover,
838 | .ui-widget-header .ui-state-hover,
839 | .ui-state-focus,
840 | .ui-widget-content .ui-state-focus,
841 | .ui-widget-header .ui-state-focus {
842 | border: 1px solid #999999;
843 | background: #dadada url(images/ui-bg_glass_75_dadada_1x400.png) 50% 50% repeat-x;
844 | font-weight: normal;
845 | color: #212121;
846 | }
847 | .ui-state-hover a,
848 | .ui-state-hover a:hover,
849 | .ui-state-hover a:link,
850 | .ui-state-hover a:visited {
851 | color: #212121;
852 | text-decoration: none;
853 | }
854 | .ui-state-active,
855 | .ui-widget-content .ui-state-active,
856 | .ui-widget-header .ui-state-active {
857 | border: 1px solid #aaaaaa;
858 | background: #ffffff url(images/ui-bg_glass_65_ffffff_1x400.png) 50% 50% repeat-x;
859 | font-weight: normal;
860 | color: #212121;
861 | }
862 | .ui-state-active a,
863 | .ui-state-active a:link,
864 | .ui-state-active a:visited {
865 | color: #212121;
866 | text-decoration: none;
867 | }
868 |
869 | /* Interaction Cues
870 | ----------------------------------*/
871 | .ui-state-highlight,
872 | .ui-widget-content .ui-state-highlight,
873 | .ui-widget-header .ui-state-highlight {
874 | border: 1px solid #fcefa1;
875 | background: #fbf9ee url(images/ui-bg_glass_55_fbf9ee_1x400.png) 50% 50% repeat-x;
876 | color: #363636;
877 | }
878 | .ui-state-highlight a,
879 | .ui-widget-content .ui-state-highlight a,
880 | .ui-widget-header .ui-state-highlight a {
881 | color: #363636;
882 | }
883 | .ui-state-error,
884 | .ui-widget-content .ui-state-error,
885 | .ui-widget-header .ui-state-error {
886 | border: 1px solid #cd0a0a;
887 | background: #fef1ec url(images/ui-bg_glass_95_fef1ec_1x400.png) 50% 50% repeat-x;
888 | color: #cd0a0a;
889 | }
890 | .ui-state-error a,
891 | .ui-widget-content .ui-state-error a,
892 | .ui-widget-header .ui-state-error a {
893 | color: #cd0a0a;
894 | }
895 | .ui-state-error-text,
896 | .ui-widget-content .ui-state-error-text,
897 | .ui-widget-header .ui-state-error-text {
898 | color: #cd0a0a;
899 | }
900 | .ui-priority-primary,
901 | .ui-widget-content .ui-priority-primary,
902 | .ui-widget-header .ui-priority-primary {
903 | font-weight: bold;
904 | }
905 | .ui-priority-secondary,
906 | .ui-widget-content .ui-priority-secondary,
907 | .ui-widget-header .ui-priority-secondary {
908 | opacity: .7;
909 | filter:Alpha(Opacity=70);
910 | font-weight: normal;
911 | }
912 | .ui-state-disabled,
913 | .ui-widget-content .ui-state-disabled,
914 | .ui-widget-header .ui-state-disabled {
915 | opacity: .35;
916 | filter:Alpha(Opacity=35);
917 | background-image: none;
918 | }
919 | .ui-state-disabled .ui-icon {
920 | filter:Alpha(Opacity=35); /* For IE8 - See #6059 */
921 | }
922 |
923 | /* Icons
924 | ----------------------------------*/
925 |
926 | /* states and images */
927 | .ui-icon {
928 | width: 16px;
929 | height: 16px;
930 | }
931 | .ui-icon,
932 | .ui-widget-content .ui-icon {
933 | background-image: url(images/ui-icons_222222_256x240.png);
934 | }
935 | .ui-widget-header .ui-icon {
936 | background-image: url(images/ui-icons_222222_256x240.png);
937 | }
938 | .ui-state-default .ui-icon {
939 | background-image: url(images/ui-icons_888888_256x240.png);
940 | }
941 | .ui-state-hover .ui-icon,
942 | .ui-state-focus .ui-icon {
943 | background-image: url(images/ui-icons_454545_256x240.png);
944 | }
945 | .ui-state-active .ui-icon {
946 | background-image: url(images/ui-icons_454545_256x240.png);
947 | }
948 | .ui-state-highlight .ui-icon {
949 | background-image: url(images/ui-icons_2e83ff_256x240.png);
950 | }
951 | .ui-state-error .ui-icon,
952 | .ui-state-error-text .ui-icon {
953 | background-image: url(images/ui-icons_cd0a0a_256x240.png);
954 | }
955 |
956 | /* positioning */
957 | .ui-icon-blank { background-position: 16px 16px; }
958 | .ui-icon-carat-1-n { background-position: 0 0; }
959 | .ui-icon-carat-1-ne { background-position: -16px 0; }
960 | .ui-icon-carat-1-e { background-position: -32px 0; }
961 | .ui-icon-carat-1-se { background-position: -48px 0; }
962 | .ui-icon-carat-1-s { background-position: -64px 0; }
963 | .ui-icon-carat-1-sw { background-position: -80px 0; }
964 | .ui-icon-carat-1-w { background-position: -96px 0; }
965 | .ui-icon-carat-1-nw { background-position: -112px 0; }
966 | .ui-icon-carat-2-n-s { background-position: -128px 0; }
967 | .ui-icon-carat-2-e-w { background-position: -144px 0; }
968 | .ui-icon-triangle-1-n { background-position: 0 -16px; }
969 | .ui-icon-triangle-1-ne { background-position: -16px -16px; }
970 | .ui-icon-triangle-1-e { background-position: -32px -16px; }
971 | .ui-icon-triangle-1-se { background-position: -48px -16px; }
972 | .ui-icon-triangle-1-s { background-position: -64px -16px; }
973 | .ui-icon-triangle-1-sw { background-position: -80px -16px; }
974 | .ui-icon-triangle-1-w { background-position: -96px -16px; }
975 | .ui-icon-triangle-1-nw { background-position: -112px -16px; }
976 | .ui-icon-triangle-2-n-s { background-position: -128px -16px; }
977 | .ui-icon-triangle-2-e-w { background-position: -144px -16px; }
978 | .ui-icon-arrow-1-n { background-position: 0 -32px; }
979 | .ui-icon-arrow-1-ne { background-position: -16px -32px; }
980 | .ui-icon-arrow-1-e { background-position: -32px -32px; }
981 | .ui-icon-arrow-1-se { background-position: -48px -32px; }
982 | .ui-icon-arrow-1-s { background-position: -64px -32px; }
983 | .ui-icon-arrow-1-sw { background-position: -80px -32px; }
984 | .ui-icon-arrow-1-w { background-position: -96px -32px; }
985 | .ui-icon-arrow-1-nw { background-position: -112px -32px; }
986 | .ui-icon-arrow-2-n-s { background-position: -128px -32px; }
987 | .ui-icon-arrow-2-ne-sw { background-position: -144px -32px; }
988 | .ui-icon-arrow-2-e-w { background-position: -160px -32px; }
989 | .ui-icon-arrow-2-se-nw { background-position: -176px -32px; }
990 | .ui-icon-arrowstop-1-n { background-position: -192px -32px; }
991 | .ui-icon-arrowstop-1-e { background-position: -208px -32px; }
992 | .ui-icon-arrowstop-1-s { background-position: -224px -32px; }
993 | .ui-icon-arrowstop-1-w { background-position: -240px -32px; }
994 | .ui-icon-arrowthick-1-n { background-position: 0 -48px; }
995 | .ui-icon-arrowthick-1-ne { background-position: -16px -48px; }
996 | .ui-icon-arrowthick-1-e { background-position: -32px -48px; }
997 | .ui-icon-arrowthick-1-se { background-position: -48px -48px; }
998 | .ui-icon-arrowthick-1-s { background-position: -64px -48px; }
999 | .ui-icon-arrowthick-1-sw { background-position: -80px -48px; }
1000 | .ui-icon-arrowthick-1-w { background-position: -96px -48px; }
1001 | .ui-icon-arrowthick-1-nw { background-position: -112px -48px; }
1002 | .ui-icon-arrowthick-2-n-s { background-position: -128px -48px; }
1003 | .ui-icon-arrowthick-2-ne-sw { background-position: -144px -48px; }
1004 | .ui-icon-arrowthick-2-e-w { background-position: -160px -48px; }
1005 | .ui-icon-arrowthick-2-se-nw { background-position: -176px -48px; }
1006 | .ui-icon-arrowthickstop-1-n { background-position: -192px -48px; }
1007 | .ui-icon-arrowthickstop-1-e { background-position: -208px -48px; }
1008 | .ui-icon-arrowthickstop-1-s { background-position: -224px -48px; }
1009 | .ui-icon-arrowthickstop-1-w { background-position: -240px -48px; }
1010 | .ui-icon-arrowreturnthick-1-w { background-position: 0 -64px; }
1011 | .ui-icon-arrowreturnthick-1-n { background-position: -16px -64px; }
1012 | .ui-icon-arrowreturnthick-1-e { background-position: -32px -64px; }
1013 | .ui-icon-arrowreturnthick-1-s { background-position: -48px -64px; }
1014 | .ui-icon-arrowreturn-1-w { background-position: -64px -64px; }
1015 | .ui-icon-arrowreturn-1-n { background-position: -80px -64px; }
1016 | .ui-icon-arrowreturn-1-e { background-position: -96px -64px; }
1017 | .ui-icon-arrowreturn-1-s { background-position: -112px -64px; }
1018 | .ui-icon-arrowrefresh-1-w { background-position: -128px -64px; }
1019 | .ui-icon-arrowrefresh-1-n { background-position: -144px -64px; }
1020 | .ui-icon-arrowrefresh-1-e { background-position: -160px -64px; }
1021 | .ui-icon-arrowrefresh-1-s { background-position: -176px -64px; }
1022 | .ui-icon-arrow-4 { background-position: 0 -80px; }
1023 | .ui-icon-arrow-4-diag { background-position: -16px -80px; }
1024 | .ui-icon-extlink { background-position: -32px -80px; }
1025 | .ui-icon-newwin { background-position: -48px -80px; }
1026 | .ui-icon-refresh { background-position: -64px -80px; }
1027 | .ui-icon-shuffle { background-position: -80px -80px; }
1028 | .ui-icon-transfer-e-w { background-position: -96px -80px; }
1029 | .ui-icon-transferthick-e-w { background-position: -112px -80px; }
1030 | .ui-icon-folder-collapsed { background-position: 0 -96px; }
1031 | .ui-icon-folder-open { background-position: -16px -96px; }
1032 | .ui-icon-document { background-position: -32px -96px; }
1033 | .ui-icon-document-b { background-position: -48px -96px; }
1034 | .ui-icon-note { background-position: -64px -96px; }
1035 | .ui-icon-mail-closed { background-position: -80px -96px; }
1036 | .ui-icon-mail-open { background-position: -96px -96px; }
1037 | .ui-icon-suitcase { background-position: -112px -96px; }
1038 | .ui-icon-comment { background-position: -128px -96px; }
1039 | .ui-icon-person { background-position: -144px -96px; }
1040 | .ui-icon-print { background-position: -160px -96px; }
1041 | .ui-icon-trash { background-position: -176px -96px; }
1042 | .ui-icon-locked { background-position: -192px -96px; }
1043 | .ui-icon-unlocked { background-position: -208px -96px; }
1044 | .ui-icon-bookmark { background-position: -224px -96px; }
1045 | .ui-icon-tag { background-position: -240px -96px; }
1046 | .ui-icon-home { background-position: 0 -112px; }
1047 | .ui-icon-flag { background-position: -16px -112px; }
1048 | .ui-icon-calendar { background-position: -32px -112px; }
1049 | .ui-icon-cart { background-position: -48px -112px; }
1050 | .ui-icon-pencil { background-position: -64px -112px; }
1051 | .ui-icon-clock { background-position: -80px -112px; }
1052 | .ui-icon-disk { background-position: -96px -112px; }
1053 | .ui-icon-calculator { background-position: -112px -112px; }
1054 | .ui-icon-zoomin { background-position: -128px -112px; }
1055 | .ui-icon-zoomout { background-position: -144px -112px; }
1056 | .ui-icon-search { background-position: -160px -112px; }
1057 | .ui-icon-wrench { background-position: -176px -112px; }
1058 | .ui-icon-gear { background-position: -192px -112px; }
1059 | .ui-icon-heart { background-position: -208px -112px; }
1060 | .ui-icon-star { background-position: -224px -112px; }
1061 | .ui-icon-link { background-position: -240px -112px; }
1062 | .ui-icon-cancel { background-position: 0 -128px; }
1063 | .ui-icon-plus { background-position: -16px -128px; }
1064 | .ui-icon-plusthick { background-position: -32px -128px; }
1065 | .ui-icon-minus { background-position: -48px -128px; }
1066 | .ui-icon-minusthick { background-position: -64px -128px; }
1067 | .ui-icon-close { background-position: -80px -128px; }
1068 | .ui-icon-closethick { background-position: -96px -128px; }
1069 | .ui-icon-key { background-position: -112px -128px; }
1070 | .ui-icon-lightbulb { background-position: -128px -128px; }
1071 | .ui-icon-scissors { background-position: -144px -128px; }
1072 | .ui-icon-clipboard { background-position: -160px -128px; }
1073 | .ui-icon-copy { background-position: -176px -128px; }
1074 | .ui-icon-contact { background-position: -192px -128px; }
1075 | .ui-icon-image { background-position: -208px -128px; }
1076 | .ui-icon-video { background-position: -224px -128px; }
1077 | .ui-icon-script { background-position: -240px -128px; }
1078 | .ui-icon-alert { background-position: 0 -144px; }
1079 | .ui-icon-info { background-position: -16px -144px; }
1080 | .ui-icon-notice { background-position: -32px -144px; }
1081 | .ui-icon-help { background-position: -48px -144px; }
1082 | .ui-icon-check { background-position: -64px -144px; }
1083 | .ui-icon-bullet { background-position: -80px -144px; }
1084 | .ui-icon-radio-on { background-position: -96px -144px; }
1085 | .ui-icon-radio-off { background-position: -112px -144px; }
1086 | .ui-icon-pin-w { background-position: -128px -144px; }
1087 | .ui-icon-pin-s { background-position: -144px -144px; }
1088 | .ui-icon-play { background-position: 0 -160px; }
1089 | .ui-icon-pause { background-position: -16px -160px; }
1090 | .ui-icon-seek-next { background-position: -32px -160px; }
1091 | .ui-icon-seek-prev { background-position: -48px -160px; }
1092 | .ui-icon-seek-end { background-position: -64px -160px; }
1093 | .ui-icon-seek-start { background-position: -80px -160px; }
1094 | /* ui-icon-seek-first is deprecated, use ui-icon-seek-start instead */
1095 | .ui-icon-seek-first { background-position: -80px -160px; }
1096 | .ui-icon-stop { background-position: -96px -160px; }
1097 | .ui-icon-eject { background-position: -112px -160px; }
1098 | .ui-icon-volume-off { background-position: -128px -160px; }
1099 | .ui-icon-volume-on { background-position: -144px -160px; }
1100 | .ui-icon-power { background-position: 0 -176px; }
1101 | .ui-icon-signal-diag { background-position: -16px -176px; }
1102 | .ui-icon-signal { background-position: -32px -176px; }
1103 | .ui-icon-battery-0 { background-position: -48px -176px; }
1104 | .ui-icon-battery-1 { background-position: -64px -176px; }
1105 | .ui-icon-battery-2 { background-position: -80px -176px; }
1106 | .ui-icon-battery-3 { background-position: -96px -176px; }
1107 | .ui-icon-circle-plus { background-position: 0 -192px; }
1108 | .ui-icon-circle-minus { background-position: -16px -192px; }
1109 | .ui-icon-circle-close { background-position: -32px -192px; }
1110 | .ui-icon-circle-triangle-e { background-position: -48px -192px; }
1111 | .ui-icon-circle-triangle-s { background-position: -64px -192px; }
1112 | .ui-icon-circle-triangle-w { background-position: -80px -192px; }
1113 | .ui-icon-circle-triangle-n { background-position: -96px -192px; }
1114 | .ui-icon-circle-arrow-e { background-position: -112px -192px; }
1115 | .ui-icon-circle-arrow-s { background-position: -128px -192px; }
1116 | .ui-icon-circle-arrow-w { background-position: -144px -192px; }
1117 | .ui-icon-circle-arrow-n { background-position: -160px -192px; }
1118 | .ui-icon-circle-zoomin { background-position: -176px -192px; }
1119 | .ui-icon-circle-zoomout { background-position: -192px -192px; }
1120 | .ui-icon-circle-check { background-position: -208px -192px; }
1121 | .ui-icon-circlesmall-plus { background-position: 0 -208px; }
1122 | .ui-icon-circlesmall-minus { background-position: -16px -208px; }
1123 | .ui-icon-circlesmall-close { background-position: -32px -208px; }
1124 | .ui-icon-squaresmall-plus { background-position: -48px -208px; }
1125 | .ui-icon-squaresmall-minus { background-position: -64px -208px; }
1126 | .ui-icon-squaresmall-close { background-position: -80px -208px; }
1127 | .ui-icon-grip-dotted-vertical { background-position: 0 -224px; }
1128 | .ui-icon-grip-dotted-horizontal { background-position: -16px -224px; }
1129 | .ui-icon-grip-solid-vertical { background-position: -32px -224px; }
1130 | .ui-icon-grip-solid-horizontal { background-position: -48px -224px; }
1131 | .ui-icon-gripsmall-diagonal-se { background-position: -64px -224px; }
1132 | .ui-icon-grip-diagonal-se { background-position: -80px -224px; }
1133 |
1134 |
1135 | /* Misc visuals
1136 | ----------------------------------*/
1137 |
1138 | /* Corner radius */
1139 | .ui-corner-all,
1140 | .ui-corner-top,
1141 | .ui-corner-left,
1142 | .ui-corner-tl {
1143 | border-top-left-radius: 4px;
1144 | }
1145 | .ui-corner-all,
1146 | .ui-corner-top,
1147 | .ui-corner-right,
1148 | .ui-corner-tr {
1149 | border-top-right-radius: 4px;
1150 | }
1151 | .ui-corner-all,
1152 | .ui-corner-bottom,
1153 | .ui-corner-left,
1154 | .ui-corner-bl {
1155 | border-bottom-left-radius: 4px;
1156 | }
1157 | .ui-corner-all,
1158 | .ui-corner-bottom,
1159 | .ui-corner-right,
1160 | .ui-corner-br {
1161 | border-bottom-right-radius: 4px;
1162 | }
1163 |
1164 | /* Overlays */
1165 | .ui-widget-overlay {
1166 | background: #aaaaaa url(images/ui-bg_flat_0_aaaaaa_40x100.png) 50% 50% repeat-x;
1167 | opacity: .3;
1168 | filter: Alpha(Opacity=30);
1169 | }
1170 | .ui-widget-shadow {
1171 | margin: -8px 0 0 -8px;
1172 | padding: 8px;
1173 | background: #aaaaaa url(images/ui-bg_flat_0_aaaaaa_40x100.png) 50% 50% repeat-x;
1174 | opacity: .3;
1175 | filter: Alpha(Opacity=30);
1176 | border-radius: 8px;
1177 | }
1178 |
--------------------------------------------------------------------------------
/sigdoc.sml:
--------------------------------------------------------------------------------
1 | (* Utilities *)
2 |
3 | val sigdoc_url = "http://github.com/diku-dk/sigdoc"
4 |
5 | fun println s = print (s ^"\n")
6 |
7 | fun readFile f =
8 | let val () = print ("Reading file: " ^ f ^ "\n")
9 | val is = TextIO.openIn f
10 | in let val s = TextIO.inputAll is
11 | in TextIO.closeIn is; s
12 | end handle ? => (TextIO.closeIn is; raise ?)
13 | end
14 |
15 | structure Map = struct
16 | open StringMap
17 | fun argsForWhich (m:'a map) (p : 'a -> bool) : string list =
18 | Fold (fn ((s,v),a) => if p v then s::a else a) nil m
19 | end
20 |
21 | (* Catenable strings *)
22 | structure CS = CString
23 | infix 6 & (* as ^ *)
24 | val op & = CS.&
25 | val op $ = CS.$
26 |
27 | (* Lexing of sml tokens *)
28 | signature LEX = sig
29 | type id = string
30 | type excon = id
31 | type tycon = id
32 | type strid = id
33 | type sigid = id
34 | datatype t = ID of id
35 | | SEP of string
36 | | SPACE of string
37 | | COMMENT of string
38 | | KW of string
39 | | LINK of {href:string,elem:string}
40 | val lex : string -> t list
41 | val defs : t list -> id list * excon list * tycon list * (strid*sigid) list
42 | val conv : (strid -> sigid option) -> t list -> t list
43 | val isKw : string -> bool
44 | val pp : t list -> CS.t
45 | end
46 |
47 | fun htmlencode s : string =
48 | let fun enc #"<" = "<"
49 | | enc #">" = ">"
50 | | enc #"&" = "&"
51 | | enc #"\"" = """
52 | | enc c = String.str c
53 | in String.translate enc s
54 | end
55 |
56 | fun taga t a e = $("<" ^ t ^ a ^ ">") & e & $("" ^ t ^ ">")
57 | fun tag t e = taga t "" e
58 | fun taga0 t a = taga t a ($"")
59 | fun tag0 t = $("<" ^ t ^ " />")
60 | fun tdwl w e = taga "td" (" width='" ^ Int.toString w ^ "%' align='left'") e
61 |
62 | fun encode id =
63 | let val chars = "#%&/<>-_+'~^:!@$=?|"
64 | fun i_to_s i = if i < 10 then "0" ^ Int.toString i else Int.toString i
65 | fun f c = case CharVector.findi (fn (_,e) => c = e) chars of
66 | SOME (i,_) => "$" ^ i_to_s i
67 | | NONE => String.str c
68 | in String.translate f id
69 | end
70 |
71 | fun plingencode id =
72 | String.translate (fn #"'" => "\\'" | c => String.str c) id
73 |
74 | fun init_space s =
75 | Char.isSpace(String.sub(s,0))
76 | handle _ => false
77 |
78 | fun remove_init_ws s =
79 | let fun loop n =
80 | if Char.isSpace(String.sub(s,n)) then loop (n+1)
81 | else n
82 | in String.extract(s,loop 0,NONE)
83 | end handle _ => ""
84 |
85 | structure Lex : LEX =
86 | struct
87 | type id = string
88 | type excon = id
89 | type tycon = id
90 | type strid = id
91 | type sigid = id
92 | datatype t = ID of id
93 | | SEP of string
94 | | SPACE of string
95 | | COMMENT of string
96 | | KW of string
97 | | LINK of {href:string,elem:string}
98 |
99 | fun sOf (ID s) = $ s
100 | | sOf (SEP s) = $ s
101 | | sOf (SPACE s) = $ s
102 | | sOf (COMMENT s) = $ s
103 | | sOf (KW s) = tag "b" ($s)
104 | | sOf (LINK {href,elem}) = taga "a" (" href='" ^ href ^ "'") ($elem)
105 |
106 | (* signature level key words *)
107 | fun isKw s =
108 | case s of
109 | "and" => true
110 | | "eqtype" => true
111 | | "end" => true
112 | | "exception" => true
113 | | "sharing" => true
114 | | "sig" => true
115 | | "signature" => true
116 | | "structure" => true
117 | | "type" => true
118 | | "datatype" => true
119 | | "val" => true
120 | | "include" => true
121 | | "where" => true
122 | | _ => false
123 |
124 | fun spaces t =
125 | case t of
126 | SPACE s :: rest =>
127 | (case spaces rest of
128 | NONE => SOME(s,rest)
129 | | SOME (sp,r) => SOME(s^sp,r))
130 | | _ => NONE
131 |
132 | fun valId t =
133 | case t of
134 | ID "val" :: t =>
135 | (case spaces t of
136 | SOME(space, ID id :: rest) => SOME (SPACE space,id,rest)
137 | | _ => NONE)
138 | | _ => NONE
139 |
140 | fun structureSigid t =
141 | case t of
142 | ID "structure" :: t =>
143 | (case spaces t of
144 | SOME(space, ID strid :: rest) =>
145 | (case spaces rest of
146 | SOME(space2, ID symb :: rest2) =>
147 | if symb = ":" orelse symb = ":>" then
148 | (case spaces rest2 of
149 | SOME(space3, ID sigid :: rest) =>
150 | if sigid <> "sig" then
151 | SOME (SPACE space, strid, SPACE space2, ID ":", SPACE space3, sigid, rest)
152 | else NONE
153 | | _ => NONE)
154 | else NONE
155 | | _ => NONE)
156 | | _ => NONE)
157 | | _ => NONE
158 |
159 | fun includeSigid t =
160 | case t of
161 | ID "include" :: t =>
162 | (case spaces t of
163 | SOME(sp, ID sigid :: rest) => SOME(SPACE sp,sigid,rest)
164 | | _ => NONE)
165 | | _ => NONE
166 |
167 | fun longid id =
168 | case String.fields (fn c => c = #".") id of
169 | [id1,id2] => SOME (id1,id2)
170 | | _ => NONE
171 |
172 | fun conv lookupStructure t =
173 | let fun conv0 t =
174 | case valId t of
175 | SOME(sp,id,rest) =>
176 | KW "val" :: sp :: LINK{href="#" ^ encode id,
177 | elem=id} :: conv0 rest
178 | | NONE =>
179 | case structureSigid t of
180 | SOME (sp,strid,sp2,colon,sp3,sigid,rest) =>
181 | (KW "structure" :: sp :: ID strid :: sp2 :: colon :: sp3 ::
182 | LINK{href=sigid ^ ".sml.html",
183 | elem=sigid} :: conv0 rest)
184 | | NONE =>
185 | case includeSigid t of
186 | SOME(sp,sigid,rest) =>
187 | (KW "include" :: sp ::
188 | LINK{href=sigid ^ ".sml.html",
189 | elem=sigid} :: conv0 rest)
190 | | NONE =>
191 | case t of
192 | nil => nil
193 | | ID id :: rest =>
194 | if isKw id then KW id :: conv0 rest
195 | else (case longid id of
196 | SOME(id1,id2) =>
197 | (case lookupStructure id1 of
198 | SOME sigid =>
199 | let val file = sigid ^ ".sml.html"
200 | in LINK{href=file,elem=id1} :: ID "." :: ID id2 :: conv0 rest
201 | end
202 | | NONE =>
203 | ID id1 :: ID "." :: ID id2 :: conv0 rest)
204 | | NONE => ID id :: conv0 rest)
205 | | e :: rest => e :: conv0 rest
206 | in conv0 t
207 | end
208 |
209 | fun pp ts = CS.concat(map sOf ts)
210 |
211 | type pos = int
212 | type level = int
213 |
214 | type stream = string * pos
215 |
216 | fun getc (s,p) =
217 | if String.size s > p then
218 | SOME(String.sub(s,p),(s,p+1))
219 | else NONE
220 |
221 | fun pr_error_pos (s,p) =
222 | "Error at position " ^ Int.toString p ^ ": " ^ String.extract(s,p,NONE)
223 |
224 | fun read_chars (f:char->bool) (is:stream) (C:string -> t) =
225 | let fun read is a =
226 | case getc is of
227 | SOME(c,is') =>
228 | if f c then
229 | read is' (c::a)
230 | else if a <> [] then
231 | SOME(C(String.implode(rev a)),is)
232 | else NONE
233 | | NONE =>
234 | if a <> [] then
235 | SOME(C(String.implode(rev a)),is)
236 | else NONE
237 | in read is []
238 | end
239 |
240 | fun read_id is =
241 | let fun isIdChar c =
242 | Char.isAlpha c orelse Char.isDigit c orelse c = #"'" orelse c = #"_" orelse c = #"."
243 | in read_chars isIdChar is ID
244 | end
245 |
246 | fun read_symb is =
247 | let val symbolChars = "@$/*-+<>!#%?^~:|=&"
248 | fun isSymbolChar c = CharVector.exists (fn c' => c=c') symbolChars
249 | in read_chars isSymbolChar is ID
250 | end
251 |
252 | fun read_sep is =
253 | let val sepChars = ",;{}[]()"
254 | fun isSepChar c = CharVector.exists (fn c' => c=c') sepChars
255 | in case getc is of
256 | SOME(c,is') =>
257 | if isSepChar c then SOME(SEP(String.str c),is')
258 | else NONE
259 | | NONE => NONE
260 | end
261 |
262 | fun read_space is =
263 | read_chars Char.isSpace is SPACE
264 |
265 | fun read_comment is =
266 | let fun read lev is a =
267 | case getc is of
268 | SOME(#"(",is1) =>
269 | (case getc is1 of
270 | SOME(#"*",is2) =>
271 | read (lev+1) is2 (#"*":: #"("::a)
272 | | _ => read lev is1 (#"("::a))
273 | | SOME(#"*",is1) =>
274 | (case getc is1 of
275 | SOME(#")",is2) =>
276 | let val a = #")":: #"*"::a
277 | in if lev=1 then
278 | SOME(COMMENT(implode(rev a)),is2)
279 | else read (lev-1) is2 a
280 | end
281 | | _ => read lev is1 (#"*"::a))
282 | | SOME(c,is1) => read lev is1 (c::a)
283 | | NONE => raise Fail "immature end of comment"
284 | in case getc is of
285 | SOME(#"(",is1) =>
286 | (case getc is1 of
287 | SOME(#"*",is2) =>
288 | read 1 is2 [#"*", #"("]
289 | | _ => NONE)
290 | | _ => NONE
291 | end
292 |
293 | fun eos (s,i) = i >= String.size s
294 |
295 | fun lex s =
296 | let fun read (is:stream) (a:t list) : t list =
297 | case read_space is of
298 | SOME(t,is) => read is (t::a)
299 | | NONE =>
300 | case read_comment is of
301 | SOME(t,is) => read is (t::a)
302 | | NONE =>
303 | case read_sep is of
304 | SOME(t,is) => read is (t::a)
305 | | NONE =>
306 | case read_symb is of
307 | SOME(t,is) => read is (t::a)
308 | | NONE =>
309 | case read_id is of
310 | SOME(t,is) => read is (t::a)
311 | | NONE =>
312 | if eos is then rev a
313 | else raise Fail ("Error reading string: " ^ pr_error_pos is)
314 | in read (s,0) nil
315 | end
316 |
317 | fun defs ts =
318 | let fun tyvar id =
319 | case explode id of
320 | #"'" :: _ => true
321 | | _ => false
322 | fun eat_tyvars ts =
323 | case ts of
324 | ID id :: SPACE _ :: ts' =>
325 | if tyvar id then ts' else ts
326 | | _ => ts
327 | fun typespec "type" = true
328 | | typespec "eqtype" = true
329 | | typespec "datatype" = true
330 | | typespec _ = false
331 |
332 | fun loop (acc as (ids,tycons,excons,strs)) ts =
333 | case ts of
334 | ID "val" :: SPACE _ :: ID id :: ts =>
335 | loop (id::ids,tycons,excons,strs) ts
336 | | ID "exception" :: SPACE _ :: ID excon :: ts =>
337 | loop (ids,tycons,excon::excons,strs) ts
338 | | ID "structure" :: SPACE _ :: ID strid ::
339 | SPACE _ :: ID ":" :: SPACE _ :: ID sigid :: ts =>
340 | loop (ids,tycons,excons,(strid,sigid)::strs) ts
341 | | ID "structure" :: SPACE _ :: ID strid ::
342 | SPACE _ :: ID ":>" :: SPACE _ :: ID sigid :: ts =>
343 | loop (ids,tycons,excons,(strid,sigid)::strs) ts
344 | | ID kw :: SPACE _ :: ts =>
345 | if typespec kw then
346 | (case eat_tyvars ts of
347 | ID tycon :: ts =>
348 | loop (ids,tycon::tycons,excons,strs) ts
349 | | _ => loop acc ts)
350 | else loop acc ts
351 | | _ :: ts => loop acc ts
352 | | nil => acc
353 | in
354 | loop ([],[],[],[]) ts
355 | end
356 | end
357 |
358 | structure R = RegExp
359 |
360 | type sigid = string
361 | type strid = string
362 | type id = string
363 |
364 | datatype origin = ORIGIN_BASIS
365 | | ORIGIN_PKG of string * string
366 | | ORIGIN_NONE
367 |
368 | type sigmap = {short_comment:string, long_comment:string,
369 | src:string, origin:origin, comments:string} Map.map (* dom=sigid *)
370 |
371 | fun pkg_id p =
372 | let val re = RegExp.fromString ".*github.com/.*/(.*)"
373 | in case RegExp.extract re p of
374 | SOME [id] => id
375 | | _ => p
376 | end
377 |
378 | fun find_origin pkgvmap s =
379 | let val re_pkg = RegExp.fromString ".*(github.com/.*/.*)/.*"
380 | val re_bas = RegExp.fromString ".*basis.*"
381 | in case RegExp.extract re_pkg s of
382 | SOME [p] =>
383 | (case Map.lookup pkgvmap p of
384 | SOME v => ORIGIN_PKG (p,v)
385 | | NONE => ORIGIN_PKG (p,""))
386 | | _ => if RegExp.match re_bas s then ORIGIN_BASIS
387 | else ORIGIN_NONE
388 | end
389 |
390 | exception SigFormatError of string
391 | fun read_sig pkgvmap (f:string) (s:string) : (sigmap * string) option =
392 | let val re = ".*\\(\\*\\*(.*)\\*\\).*(signature ([0-9a-zA-Z_]+) .*end[\n ]+(where .*)?)[\n ]*\\(\\*\\*(.*)\\*\\)(.*)"
393 | fun doit (c,sigid,src,cs,rest) =
394 | let val origin = find_origin pkgvmap f
395 | val (shortc,longc) =
396 | case R.extract (R.fromString "([^\n]*)\n[ ]*\n(.*)") c of
397 | SOME [shortc,longc] => (shortc,longc)
398 | | _ => if CharVector.exists (fn c => c = #"\n") c then ("",c) else (c,"")
399 | val m = Map.singleton(sigid, {short_comment=shortc, long_comment=longc,
400 | src=src, comments=cs, origin=origin})
401 | in SOME (m,rest)
402 | end
403 | in case R.extract (R.fromString re) s of
404 | SOME [c,sigid,src,cs,rest] => doit(c,sigid,src,cs,rest)
405 | | SOME [c,sigid,whe,src,cs,rest] => doit(c,sigid,src,cs,rest)
406 | | SOME ss => (List.app (fn s => print(s ^ "\n")) ss;
407 | raise Fail "read_sig wrong format 0")
408 | | NONE =>
409 | case R.extract (R.fromString ".*signature ([0-9a-zA-Z_]+)[ \n]*=.*") s of
410 | SOME [sigid] =>
411 | ( print("Warning: Signature binding for '" ^ sigid ^ "' not on the form " ^
412 | "'(**...*)...signature XXX =...sig...end...(**...*)'\n")
413 | ; NONE)
414 | | SOME _ => raise Fail "read_sig wrong format 1"
415 | | NONE => NONE
416 | end
417 |
418 | fun read_sigs pkgvmap (f:string) : sigmap =
419 | let fun loop s =
420 | case read_sig pkgvmap f s of
421 | SOME(m,rest) => Map.plus(m,loop rest)
422 | | NONE => Map.empty
423 | val s = readFile f
424 | in loop s
425 | end
426 |
427 | type strmap = {sigid:sigid,short_comment:string,origin:origin} Map.map (* StrId -> SigId *)
428 |
429 | fun read_impl pkgvmap (sigmap:sigmap) (f:string) : strmap =
430 | let val origin = find_origin pkgvmap f
431 | fun start acc s =
432 | let fun doit (c,strid,sigid,rest) =
433 | let val c = case R.extract (R.fromString "\\(\\*(\\*)?(.*)\\*\\)") c of
434 | SOME [_,c] => c
435 | | _ => ""
436 | val acc = case Map.lookup sigmap sigid of
437 | NONE => (print ("Skipping " ^ strid ^ " : " ^ sigid ^
438 | ", as " ^ sigid ^ " is not known.\n");
439 | acc)
440 | | SOME _ => Map.add(strid,{sigid=sigid,short_comment=c,origin=origin},acc)
441 | in cont acc rest
442 | end
443 | in case R.extract (R.fromString "[ \n]*(\\(\\*\\*?[^*]*\\*\\))?[ \n]*structure ([0-9a-zA-Z_]+) :>? ([0-9a-zA-Z_]+)[\n ]+(.*)") s of
444 | SOME [c,strid,sigid,rest] => doit (c,strid,sigid,rest)
445 | | SOME [strid,sigid,rest] => doit ("",strid,sigid,rest)
446 | | SOME ss => (List.app (fn s => print(s ^ "\n")) ss;
447 | raise Fail "read_impl wrong format 1")
448 | | NONE => cont acc s
449 | end
450 | and cont acc s =
451 | case R.extract (R.fromString ".*\\(\\*\\* SigDoc \\*\\)[ \n]*structure ([0-9a-zA-Z_]+) :>? ([0-9a-zA-Z_]+)[\n ]+(.*)") s of
452 | SOME [strid,sigid,rest] =>
453 | let val acc = case Map.lookup sigmap sigid of
454 | NONE => (print ("Skipping " ^ strid ^ " : " ^ sigid ^
455 | ", as " ^ sigid ^ " is not known.\n");
456 | acc)
457 | | SOME _ => Map.add(strid,{sigid=sigid,short_comment="",origin=origin},acc)
458 | in cont acc rest
459 | end
460 | | SOME _ => raise Fail "cont"
461 | | NONE => acc
462 | val m = start Map.empty (readFile f)
463 | in if Map.isEmpty m then print ("No structures to document in " ^ f ^ "\n") else ()
464 | ; m
465 | end
466 |
467 | fun match_id nil h = NONE
468 | | match_id (id::ids) h =
469 | if h = id orelse String.isPrefix (id ^ " ") h orelse
470 | String.isPrefix (id ^ "(") h orelse
471 | String.isSubstring (" " ^ id ^ " ") h then
472 | SOME id
473 | else match_id ids h
474 |
475 | fun pp_comments (ids,tycons,excons,strs) s =
476 | let (*val () = print ("ids = " ^ Int.toString (length ids) ^"\n")*)
477 | (*(*(*
478 | fun loop cs s =
479 | case R.extract (R.fromString "(.*)\\n\\[([\\-0-9a-zA-Z_:<>=&%#@|'!*/$(), ]+)\\](.*)") s of
480 | SOME [rest,h,b] =>
481 | loop ((h,b)::cs) rest
482 | | SOME [h,b] => ((h,b)::cs)
483 | | SOME ss => (List.app (fn s => print(s ^ "\n")) ss;
484 | raise Fail "pp_comments wrong format 1")
485 | | NONE => cs
486 | *)
487 | val lines = String.fields (fn #"\n" => true | _ => false) s
488 | val lines = map (fn s => s ^ "\n") lines
489 | fun read_head s =
490 | (case CharVector.findi (fn (_,#"]") => true | _ => false) s of
491 | SOME (i,_) =>
492 | (String.extract(s,1,SOME (i-1)),
493 | String.extract(s,i+1,NONE) handle _ => "")
494 | | NONE => raise Fail "read_head")
495 | handle _ => raise Fail ("read_head2:" ^ s)
496 | fun only_ws s = CharVector.all Char.isSpace s
497 | fun eat_wss (s::ss) = if only_ws s then eat_wss ss
498 | else s::ss
499 | | eat_wss nil = nil
500 | fun finalize a cs =
501 | case eat_wss(rev a) of
502 | l0::ls =>
503 | let val (h,r) = read_head l0
504 | in (h, concat (r::ls))::cs
505 | end
506 | | nil => cs
507 | fun loop cs a (l::ls) =
508 | if String.isPrefix "[" l then
509 | loop (finalize a cs) [l] ls
510 | else loop cs (l::a) ls
511 | | loop cs a nil = rev(finalize a cs)
512 | val cs = loop [] [] lines
513 | fun layout_head h = if CS.toString h = "Discussion"
514 | then tag "i" h
515 | else ($"[") & tag "tt" h & ($"]")
516 | fun layout_body b =
517 | let
518 | val b = remove_init_ws b
519 | val lines = String.fields (fn #"\n" => true | _ => false) b
520 | fun next_init_space ls =
521 | (init_space(hd ls))
522 | handle _ => false
523 | fun loop nil true acc = "\n"::acc
524 | | loop nil false acc = acc
525 | | loop (l0::ls) ispre acc =
526 | let val l = l0 ^ "\n"
527 | in
528 | if ispre then
529 | if init_space l0 then
530 | if next_init_space ls then
531 | loop ls true (l::acc)
532 | else loop ls true (l::acc) (* was acc *)
533 | else loop ls false (l::"\n"::acc)
534 | else if only_ws l then
535 | loop ls false acc
536 | else if init_space l then
537 | loop ls true (l::"
\n"::acc)
538 | else loop ls false (l::acc)
539 | end
540 | in $(concat(rev(loop lines false nil)))
541 | end
542 | in tag "dl"
543 | (CS.concat
544 | (map (fn (h,b) =>
545 | let
546 | val h2 = htmlencode h
547 | val h3 =
548 | case match_id ids h of
549 | SOME id =>
550 | let val name = encode id
551 | in taga "a" (" name='" ^ name ^ "'") ($h2)
552 | end
553 | | NONE => $ h2
554 | val b = htmlencode b
555 | in if only_ws b then tag "dt" (tag "b" (layout_head h3))
556 | else tag "dt" (tag "b" (layout_head h3)) & ($" ")
557 | & tag "dd" (layout_body b) & tag0 "br"
558 | end) cs))
559 | end
560 |
561 | val libpath : string ref = ref ""
562 | val about_html : string ref = ref ""
563 | val logo_html : string ref = ref ""
564 | val pkgfile : string ref = ref ""
565 |
566 | fun load_about () =
567 | case !about_html of
568 | "" => NONE
569 | | f => SOME (readFile f)
570 | handle _ => raise Fail ("Failed to read file " ^ f)
571 |
572 | fun page h idx b =
573 | let val str_idx_html = "str_idx.html"
574 | val sig_idx_html = "sig_idx.html"
575 | val id_idx_html = "id_idx.html"
576 | val pkg_idx_html = "pkg_idx.html"
577 | val generated_tags_js = "generated_tags.js"
578 |
579 | val style_css = OS.Path.concat(!libpath,"style.css")
580 | val jquery_ui_css = OS.Path.concat(!libpath,"jquery-ui.css")
581 | val jquery_1_9_1_js = OS.Path.concat(!libpath,"jquery-1.9.1.js")
582 | val jquery_ui_js = OS.Path.concat(!libpath,"jquery-ui.js")
583 |
584 | val str_idx_link = taga "a" (" href='" ^ str_idx_html ^ "'") ($"Structures")
585 | val sig_idx_link = taga "a" (" href='" ^ sig_idx_html ^ "'") ($"Signatures")
586 | val id_idx_link = taga "a" (" href='" ^ id_idx_html ^ "'") ($"Identifiers")
587 | val pkg_idx_link = taga "a" (" href='" ^ pkg_idx_html ^ "'") ($"Packages")
588 | val search = taga0 "input" " id='tags' placeholder='Search' style='width:100%; margin-right:20px;'"
589 | val head =
590 | tag "head"
591 | (taga0 "link" (" rel='stylesheet' href='" ^ jquery_ui_css ^ "'") &
592 | taga0 "script" (" src='" ^ jquery_1_9_1_js ^ "'") &
593 | taga0 "script" (" src='" ^ jquery_ui_js ^ "'") &
594 | taga0 "script" (" src='" ^ generated_tags_js ^ "'") &
595 | taga0 "link" (" rel='stylesheet' href='" ^ style_css ^ "'") &
596 | tag "script" ($"function copyText(text) { console.log('hi'); try { navigator.clipboard.writeText(text); console.log('Text copied!'); } catch (err) { console.error('error copying text', err); } }") &
597 | tag "script"
598 | ($ "$(function() { \
599 | \ $( '#tags' ).autocomplete({\
600 | \ source: availableTags,\
601 | \ select: function(event,ui){ window.location = ui.item.value; }\
602 | \ });\
603 | \});")
604 | )
605 | fun tr e = tag "tr" e
606 | fun tdr e = taga "td" " align='right'" e
607 | fun tdc e = taga "td" " align='center'" e
608 | fun td e = tag "td" e
609 |
610 | val about_link =
611 | if !about_html = ""
612 | then $""
613 | else tdr (taga "a" (" href='about.html'") ($"About"))
614 |
615 | val logo =
616 | if !logo_html = ""
617 | then $""
618 | else td ($(!logo_html))
619 | in
620 | tag "html"
621 | (tag "head" head &
622 | tag "body"
623 | (taga "table" " width='100%'"
624 | (tr (logo & td ($"Library Documentation")
625 | & tdc str_idx_link
626 | & tdc sig_idx_link
627 | & tdc id_idx_link
628 | & tdc pkg_idx_link
629 | & about_link)) &
630 | taga "p" " style='width:100%'" search &
631 | tag "h4" h &
632 | tag "p" idx &
633 | b &
634 | tag0 "hr" &
635 | tag "i" ($"Generated by " & taga "a" (" href='" ^ sigdoc_url ^ "'") ($"SigDoc"))))
636 | end
637 |
638 | fun strs_for_sigid sigid (strmap:strmap) =
639 | Map.argsForWhich strmap (fn {sigid=x,...} => sigid=x)
640 |
641 | fun pp_version v =
642 | $" " & taga "span" " class='vbadge'" ($("v" ^ v))
643 |
644 | fun pp_origin origin =
645 | case origin of
646 | ORIGIN_BASIS => tag "i" ($ "(basis)")
647 | | ORIGIN_PKG (p,v) =>
648 | let val version = case v of
649 | "" => $""
650 | | _ => pp_version v
651 | in tag "i"
652 | ($"(pkg " &
653 | taga "a" (" href='http://" ^ p ^ "'") ($p) &
654 | version & $")")
655 | end
656 | | ORIGIN_NONE => tag "i" ($"(none)")
657 |
658 | fun pp strmap (sigid, {short_comment,long_comment,src,comments,origin}) =
659 | let val ts = Lex.lex src
660 | val ids = Lex.defs ts
661 | val idmap = Map.singleton(sigid,ids)
662 | val ts = Lex.conv (fn x =>
663 | case Map.lookup strmap x of
664 | SOME {sigid,...} => SOME sigid
665 | | NONE => NONE) ts
666 | val src2 = Lex.pp ts
667 | open Lex
668 | fun layout_struct x =
669 | tag "b" ($("structure " ^ x ^ " : " ^ sigid ^ " ") & pp_origin origin & ($"\n"))
670 | val space = $" "
671 | val comments =
672 | pp_comments ids comments
673 | handle Fail s => (print ("Warning: Failed to print comments for " ^ sigid ^ " - " ^ s ^ "\n"); $"")
674 | val structures =
675 | let val strs = strs_for_sigid sigid strmap
676 | in case strs of
677 | nil => $""
678 | | _ => tag "pre" (CS.concat (map layout_struct strs))
679 | & tag0 "hr"
680 | end
681 | val output =
682 | page
683 | ($"Signature " & tag "code" ($sigid) & space & tag "tt" (pp_origin origin))
684 | ($(htmlencode short_comment))
685 | ($(htmlencode long_comment) &
686 | tag0 "hr" &
687 | structures &
688 | tag "pre" src2 &
689 | tag0 "hr" &
690 | comments)
691 | in (output, idmap)
692 | end
693 |
694 | fun writeFile f a =
695 | let val () = print ("Writing file: " ^ f ^ "\n")
696 | val os = TextIO.openOut f
697 | in (TextIO.output(os,a);
698 | TextIO.closeOut os)
699 | handle ? => (TextIO.closeOut os; raise ?)
700 | end
701 |
702 | fun gen_idx {head: string,
703 | lines: 'a list,
704 | line_id: 'a -> string,
705 | line_entry: 'a -> CS.t,
706 | line_bodies: 'a -> CS.t list,
707 | sep: string} =
708 | let
709 | fun section ch part =
710 | if CS.toString part = "" then $""
711 | else
712 | let val h =
713 | if Char.isAlpha ch then String.str (Char.toUpper ch)
714 | else "Symbols"
715 | val h = taga "a" (" name='"^h^"'") ($h)
716 | in tag "h4" h & taga "table" " width='100%'" part
717 | end
718 | val (ch,ch_acc,acc,chs) =
719 | foldl (fn (line,(ch,ch_acc,acc,chs)) =>
720 | let
721 | val bodies = line_bodies line
722 | val id = line_id line
723 | val ch2 = String.sub(id,0)
724 | val e = line_entry line
725 | val n = List.length bodies
726 | val bodies = List.map (tdwl (75 div n)) bodies
727 | val entry = tag "tr" (tdwl 20 (tag "tt" e) & tdwl 5 ($sep) & (CS.concat bodies))
728 | in if not(Char.isAlpha ch2) orelse ch=ch2 then
729 | (ch,ch_acc & entry,acc,chs)
730 | else (ch2,entry,acc & section ch ch_acc,ch2::chs)
731 | end)
732 | (#"*",$"",$"",nil) lines
733 | val cs = acc & section ch ch_acc
734 | val idx =
735 | CS.concatWith " | "
736 | (map (fn c =>
737 | let val h = String.str (Char.toUpper c)
738 | in taga "a" (" href='#"^h^"'") ($h)
739 | end) (rev chs))
740 | in page ($head) idx cs
741 | end
742 |
743 | fun gen_sig_idx (sigmap:sigmap, strmap) =
744 | let val sigs = Map.list sigmap
745 | val sigs = ListSort.sort (fn (x,y) => String.compare (#1 x,#1 y)) sigs
746 | val im = List.map (fn (s,{short_comment,...}) =>
747 | let val t = s ^ ".sml.html"
748 | val strs = map (fn s => tag "tt" ($s)) (strs_for_sigid s strmap)
749 | val strs = CS.concatWith ", " strs
750 | in (s, taga "a" (" href='" ^ t ^ "'")
751 | (tag "tt" ($s)), [strs, tag "i" ($short_comment)])
752 | end) sigs
753 | val cs = gen_idx {head="Signatures",
754 | lines=im,
755 | line_id= #1,
756 | line_entry= #2,
757 | line_bodies= #3,
758 | sep=" "}
759 | in writeFile "sig_idx.html" (CS.toString cs)
760 | end
761 |
762 | fun pp_sigid s sigid =
763 | let val t = sigid ^ ".sml.html"
764 | in taga "a" (" href='" ^ t ^ "'") (tag "tt" ($s))
765 | end
766 |
767 | fun gen_str_idx (sigmap:sigmap, strmap) =
768 | let val strs = Map.list strmap
769 | val strs = ListSort.sort (fn ((x,y),(x1,y1)) => String.compare(x,x1)) strs
770 | val im = List.map (fn (strid,{sigid,short_comment,origin}) =>
771 | let val c =
772 | if short_comment <> "" then short_comment
773 | else
774 | case Map.lookup sigmap sigid of
775 | SOME {short_comment,...} => short_comment
776 | | NONE => ""
777 | in (strid, tag "div" ($strid), [pp_sigid sigid sigid, tag "i" ($c)])
778 | end) strs
779 | val cs = gen_idx {head="Structures",
780 | lines=im,
781 | line_id= #1,
782 | line_entry = #2,
783 | line_bodies= #3,
784 | sep=":"}
785 | in writeFile "str_idx.html" (CS.toString cs)
786 | end
787 |
788 | type pkgversion = string
789 |
790 | fun read_pkgfile () : pkgversion Map.map =
791 | case !pkgfile of
792 | "" => Map.empty
793 | | pkgf => let val re = RegExp.fromString ".*([^ ]+)[ \n]+([.0-9]+)[ \n]+(.*)"
794 | fun loop s m =
795 | case RegExp.extract re s of
796 | SOME [p, v, rest] => loop rest (Map.add(p,v,m))
797 | | _ => m
798 | in loop (readFile pkgf) Map.empty
799 | end
800 |
801 | type pkgmap = {full:string, sigs:unit Map.map, impls:string Map.map, version:string} Map.map
802 |
803 | fun copyText s = taga "span" " class='tooltip'" (taga "span" " class='tooltiptext'" ($"Copy Package URL") & taga "button" (" class='button1' onclick=\"copyText('http://" ^ s ^ "')\"") ($"❐"))
804 |
805 | fun gen_pkg_idx (sigmap:sigmap, strmap:strmap, pkgvmap:pkgversion Map.map) =
806 | let
807 | fun insert_sig (p,sigid,m) =
808 | let val id = pkg_id p
809 | in case Map.lookup m id of
810 | SOME {full,sigs,impls} =>
811 | Map.add(id,{full=full,sigs=Map.add(sigid,(),sigs),impls=impls},m)
812 | | NONE => Map.add(id,{full=p,sigs=Map.singleton(sigid,()),impls=Map.empty},m)
813 | end
814 | fun insert_str (p,strid,sigid,m) =
815 | let val id = pkg_id p
816 | in case Map.lookup m id of
817 | SOME {full,sigs,impls} =>
818 | Map.add(id,{full=full,sigs=sigs,impls=Map.add(strid,sigid,impls)},m)
819 | | NONE => Map.add(id,{full=p,sigs=Map.empty,impls=Map.singleton(strid,sigid)},m)
820 | end
821 |
822 | val pkgmap = Map.Fold (fn ((sigid,{origin,...}),a) =>
823 | case origin of
824 | ORIGIN_PKG (p,_) => insert_sig(p,sigid,a)
825 | | ORIGIN_BASIS => insert_sig("basis",sigid,a)
826 | | ORIGIN_NONE => insert_sig("unknown",sigid,a)) Map.empty sigmap
827 | val pkgmap = Map.Fold (fn ((strid,{origin,sigid,...}),a) =>
828 | case origin of
829 | ORIGIN_PKG (p,_) => insert_str(p,strid,sigid,a)
830 | | ORIGIN_BASIS => insert_str("basis",strid,sigid,a)
831 | | ORIGIN_NONE => insert_str("unknown",strid,sigid,a)) pkgmap strmap
832 |
833 | val im = List.map (fn (p,{full,sigs,impls}) =>
834 | let val sigs = List.foldr (fn (s,a) => pp_sigid s s & ($" ") & a) ($"") (Map.dom sigs)
835 | val strs = List.foldr (fn ((s,sigid),a) => pp_sigid s sigid & ($" ") & a) ($"") (Map.list impls)
836 | val link = if p = "basis" then $"Basis Library"
837 | else taga "a" (" href='http://" ^ full ^ "' title='" ^ full ^ "'")
838 | (tag "tt" ($p)) & $" " & copyText full
839 | val info =
840 | if p = "basis" then $""
841 | else
842 | let val pkgversion =
843 | case Map.lookup pkgvmap full of
844 | SOME v => pp_version v
845 | | NONE => $""
846 | val badge = "https://" ^ full ^ "/workflows/CI/badge.svg"
847 | in
848 | pkgversion & $" " &
849 | taga "a" (" href='https://" ^ full ^ "/actions'")
850 | (taga0 "img" (" style='vertical-align:bottom' src='"
851 | ^ badge ^ "'"))
852 | end
853 | in (p, link,
854 | [info, $"Signatures: " & sigs,
855 | $"Structures: " & strs]
856 | )
857 | end) (Map.list pkgmap)
858 | val cs = gen_idx {head="Packages",
859 | lines=im,
860 | line_id= #1,
861 | line_entry= #2,
862 | line_bodies= #3,
863 | sep=" "}
864 | in writeFile "pkg_idx.html" (CS.toString cs)
865 | end
866 |
867 | fun gen_id_idx (idmap, sigmap, strmap) =
868 | let val im = Map.Fold (fn ((sigid, (ids,_,_,_)),a) =>
869 | let val strs = strs_for_sigid sigid strmap
870 | in map (fn id => (id,sigid,strs)) ids @ a
871 | end) nil idmap
872 | val im = ListSort.sort (fn((id,_,_),(id2,_,_)) => String.compare (id,id2)) im
873 | fun compact nil a = rev a
874 | | compact ((id,sigid,strs)::rest) nil =
875 | compact rest [(id,[(sigid,strs)])]
876 | | compact ((id,sigid,strs)::rest) (acc as ((id2,args)::acc2)) =
877 | if id = id2
878 | then compact rest ((id,(sigid,strs)::args)::acc2)
879 | else compact rest ((id,[(sigid,strs)])::acc)
880 | val im = compact im nil
881 | fun layout_impls (id, nil) = $""
882 | | layout_impls (id, (sigid,strs)::rest) =
883 | List.foldl (fn (s,a) => a & taga "a" (" href='" ^ sigid ^ ".sml.html'") (tag "tt" ($s))
884 | & (tag "tt" ($("." ^ id))) & ($" "))
885 | ($"") strs & layout_impls (id, rest)
886 | val cs =
887 | gen_idx {head="Identifiers",
888 | lines=im,
889 | line_id= #1,
890 | line_entry = $ o #1,
891 | line_bodies=fn x => [layout_impls x],
892 | sep=" "}
893 | fun qq s = "'" ^ s ^ "'"
894 | fun pair e1 e2 = "{label:" ^ e1 ^ ",value:" ^ e2 ^ "}"
895 | fun prtag id strid sigid = pair (qq (plingencode(strid ^ "." ^ id))) (qq(sigid ^ ".sml.html"))
896 | fun tags nil = $""
897 | | tags ((id, nil)::rest) = tags rest
898 | | tags ([(id, [(sigid,[strid])])]) = $(prtag id strid sigid)
899 | | tags ((id, (sigid,nil)::es)::rest) = tags ((id,es)::rest)
900 | | tags ((id, (sigid,strid::strids)::es)::rest) =
901 | $(prtag id strid sigid) & $"," & tags ((id,(sigid,strids)::es)::rest)
902 | val alltags = $"var availableTags = [" & tags im & $"];"
903 | in writeFile "id_idx.html" (CS.toString cs)
904 | ; writeFile "generated_tags.js" (CS.toString alltags)
905 | end
906 |
907 | fun gen (files:string list) =
908 | (* Look in all files for signature snippets on the form
909 |
910 | "(** Header\nLong comment... *) signature A = sig end (** defs... *)"
911 |
912 | * Look in all files for structure snippets on the form
913 |
914 | 1. "(** SigDoc *) structure A : B"
915 | 2. "(** SigDoc *) structure A :> B"
916 | 3. "$(* *) structure A :> B" (* first occurence in file *)
917 | *)
918 | let
919 | val () = case load_about () of
920 | SOME about =>
921 | let val p = CS.toString (page ($"About") ($"") ($about))
922 | in writeFile "about.html" p
923 | end
924 | | NONE => ()
925 | val pkgvmap = read_pkgfile ()
926 | val sigmap : sigmap =
927 | foldl (fn (x,a) =>
928 | let val m = read_sigs pkgvmap x
929 | in Map.plus(a,m)
930 | end handle SigFormatError s =>
931 | (print ("Skipping file: " ^ s ^ "\n");
932 | a)
933 | ) Map.empty files
934 | val strmap : strmap =
935 | foldl (fn (x,a) => Map.plus(a,read_impl pkgvmap sigmap x)) Map.empty files
936 | fun out (arg as (s,a), idmap) =
937 | let val (cstr, idmap2) = pp strmap arg
938 | val str = CS.toString cstr
939 | val file = s ^ ".sml.html"
940 | in writeFile file str;
941 | Map.plus(idmap,idmap2)
942 | end
943 | val idmap = Map.Fold out Map.empty sigmap
944 | in
945 | gen_sig_idx (sigmap, strmap);
946 | gen_str_idx (sigmap, strmap);
947 | gen_pkg_idx (sigmap, strmap, pkgvmap);
948 | gen_id_idx (idmap, sigmap, strmap)
949 | end handle Fail s => (println ("** Error: " ^ s); OS.Process.exit OS.Process.failure)
950 |
951 | fun help () =
952 | (print "Usage: sigdoc [-libpath p] [-about f] [-logo s]\n\
953 | \ [-pkg f] FILES\n\n";
954 | print " -libpath p : specify the path to the js-library and\n\
955 | \ style files, relative to the working\n\
956 | \ directory.\n";
957 | print " -about f : specify a file with HTML to embed in an\n\
958 | \ About tab.\n";
959 | print " -logo s : specify html that presents a logo.\n";
960 | print " -pkg f : specify path to smlpkg package file to\n\
961 | \ read package versions from.\n";
962 | print "FILES include .sml and .sig files, which may contain\n\
963 | \signatures and structures.\n";
964 | print "For further information, please consult the Sigdoc\n";
965 | print ("documentation at " ^ sigdoc_url ^ "\n"))
966 |
967 | fun reg r xs = r := xs
968 |
969 | val () = case ParseArg.run [ParseArg.Unary("-libpath", reg libpath),
970 | ParseArg.Unary("-about", reg about_html),
971 | ParseArg.Unary("-logo", reg logo_html),
972 | ParseArg.Unary("-pkg", reg pkgfile)] of
973 | [] => help()
974 | | files => gen files
975 |
--------------------------------------------------------------------------------