├── .github └── workflows │ └── ci.yml ├── Dscanner ├── libdparse │ └── src │ │ ├── dparse │ │ ├── ast.d │ │ ├── entities.d │ │ ├── formatter.d │ │ ├── lexer.d │ │ └── parser.d │ │ └── std │ │ └── experimental │ │ └── lexer.d └── src │ └── astprinter.d ├── Makefile ├── README.md ├── adrdox_docs ├── home.d ├── package.d ├── syntax.d └── tips.d ├── archive.d ├── cgi.d ├── color.d ├── comment.d ├── database.d ├── db.sql ├── doc2.d ├── dom.d ├── dub.json ├── jstex.d ├── jsvar.d ├── katex ├── KaTeX_AMS-Regular.ttf ├── KaTeX_AMS-Regular.woff ├── KaTeX_AMS-Regular.woff2 ├── KaTeX_Caligraphic-Bold.ttf ├── KaTeX_Caligraphic-Bold.woff ├── KaTeX_Caligraphic-Bold.woff2 ├── KaTeX_Caligraphic-Regular.ttf ├── KaTeX_Caligraphic-Regular.woff ├── KaTeX_Caligraphic-Regular.woff2 ├── KaTeX_Fraktur-Bold.ttf ├── KaTeX_Fraktur-Bold.woff ├── KaTeX_Fraktur-Bold.woff2 ├── KaTeX_Fraktur-Regular.ttf ├── KaTeX_Fraktur-Regular.woff ├── KaTeX_Fraktur-Regular.woff2 ├── KaTeX_Main-Bold.ttf ├── KaTeX_Main-Bold.woff ├── KaTeX_Main-Bold.woff2 ├── KaTeX_Main-BoldItalic.ttf ├── KaTeX_Main-BoldItalic.woff ├── KaTeX_Main-BoldItalic.woff2 ├── KaTeX_Main-Italic.ttf ├── KaTeX_Main-Italic.woff ├── KaTeX_Main-Italic.woff2 ├── KaTeX_Main-Regular.ttf ├── KaTeX_Main-Regular.woff ├── KaTeX_Main-Regular.woff2 ├── KaTeX_Math-BoldItalic.ttf ├── KaTeX_Math-BoldItalic.woff ├── KaTeX_Math-BoldItalic.woff2 ├── KaTeX_Math-Italic.ttf ├── KaTeX_Math-Italic.woff ├── KaTeX_Math-Italic.woff2 ├── KaTeX_SansSerif-Bold.ttf ├── KaTeX_SansSerif-Bold.woff ├── KaTeX_SansSerif-Bold.woff2 ├── KaTeX_SansSerif-Italic.ttf ├── KaTeX_SansSerif-Italic.woff ├── KaTeX_SansSerif-Italic.woff2 ├── KaTeX_SansSerif-Regular.ttf ├── KaTeX_SansSerif-Regular.woff ├── KaTeX_SansSerif-Regular.woff2 ├── KaTeX_Script-Regular.ttf ├── KaTeX_Script-Regular.woff ├── KaTeX_Script-Regular.woff2 ├── KaTeX_Size1-Regular.ttf ├── KaTeX_Size1-Regular.woff ├── KaTeX_Size1-Regular.woff2 ├── KaTeX_Size2-Regular.ttf ├── KaTeX_Size2-Regular.woff ├── KaTeX_Size2-Regular.woff2 ├── KaTeX_Size3-Regular.ttf ├── KaTeX_Size3-Regular.woff ├── KaTeX_Size3-Regular.woff2 ├── KaTeX_Size4-Regular.ttf ├── KaTeX_Size4-Regular.woff ├── KaTeX_Size4-Regular.woff2 ├── KaTeX_Typewriter-Regular.ttf ├── KaTeX_Typewriter-Regular.woff ├── KaTeX_Typewriter-Regular.woff2 ├── katex.min.css └── katex.min.js ├── latex.d ├── locate.d ├── postgres.d ├── script.d ├── script.js ├── search-docs.html ├── search-docs.js ├── skeleton-default.html ├── stemmer.d ├── style.css └── syntaxhighlighter.d /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | on: 3 | push: 4 | branches: 5 | - master 6 | tags: 7 | - 'v[0-9]+.[0-9]+.[0-9]+' 8 | pull_request: 9 | jobs: 10 | build: 11 | strategy: 12 | matrix: 13 | os: 14 | - ubuntu-latest 15 | - macos-latest 16 | - windows-latest 17 | dc: 18 | - dmd-latest 19 | - ldc-latest 20 | - dmd-beta 21 | - ldc-beta 22 | profile: 23 | - debug 24 | runs-on: ${{ matrix.os }} 25 | steps: 26 | - name: Setup D compiler 27 | uses: dlang-community/setup-dlang@v2 28 | with: 29 | compiler: ${{ matrix.dc }} 30 | - name: Checkout source 31 | uses: actions/checkout@v4 32 | - name: Build binary 33 | run: dub build -b ${{ matrix.profile }} 34 | 35 | build-release: 36 | if: startsWith(github.ref, 'refs/tags/v') 37 | needs: 38 | - build 39 | strategy: 40 | fail-fast: true 41 | matrix: 42 | include: 43 | - name: linux-i686 44 | os: ubuntu-latest 45 | arch: x86 46 | - name: linux-x86_64 47 | os: ubuntu-latest 48 | arch: x86_64 49 | - name: macos-x86_64 50 | os: macos-latest 51 | arch: x86_64 52 | - name: windows-i686 53 | os: windows-latest 54 | arch: x86 55 | - name: windows-x86_64 56 | os: windows-latest 57 | arch: x86_64 58 | runs-on: ${{ matrix.os }} 59 | steps: 60 | - if: matrix.name == 'linux-i686' 61 | run: | 62 | sudo dpkg --add-architecture i386 63 | sudo apt-get update 64 | sudo apt-get install -y gcc-multilib 65 | - name: Setup D compiler 66 | uses: dlang-community/setup-dlang@v2 67 | with: 68 | compiler: ldc-latest 69 | - name: Checkout source 70 | uses: actions/checkout@v4 71 | - name: Build binary release 72 | run: dub build -a ${{ matrix.arch }} -b release 73 | - name: Create dist 74 | uses: papeloto/action-zip@v1 75 | with: 76 | files: build README.md 77 | dest: adrdox_${{ matrix.name }}.zip 78 | - name: Upload dist 79 | uses: actions/upload-artifact@v2 80 | with: 81 | name: dist 82 | path: adrdox_${{ matrix.name }}.zip 83 | 84 | publish-release: 85 | if: startsWith(github.ref, 'refs/tags/v') 86 | needs: 87 | - build-release 88 | runs-on: ubuntu-latest 89 | steps: 90 | - name: Download dists 91 | uses: actions/download-artifact@v2 92 | with: 93 | name: dist 94 | - name: Create release 95 | uses: softprops/action-gh-release@v1 96 | env: 97 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 98 | with: 99 | files: '*.zip' 100 | #prerelease: true 101 | draft: true 102 | -------------------------------------------------------------------------------- /Dscanner/libdparse/src/std/experimental/lexer.d: -------------------------------------------------------------------------------- 1 | // Written in the D programming language 2 | 3 | /** 4 | * $(H2 Summary) 5 | * This module contains a range-based compile-time _lexer generator. 6 | * 7 | * $(H2 Overview) 8 | * The _lexer generator consists of a template mixin, $(LREF Lexer), along with 9 | * several helper templates for generating such things as token identifiers. 10 | * 11 | * To write a _lexer using this API: 12 | * $(OL 13 | * $(LI Create the string array constants for your language. 14 | * $(UL 15 | * $(LI $(LINK2 #.staticTokens, staticTokens)) 16 | * $(LI $(LINK2 #.dynamicTokens, dynamicTokens)) 17 | * $(LI $(LINK2 #.possibleDefaultTokens, possibleDefaultTokens)) 18 | * $(LI $(LINK2 #.tokenHandlers, tokenHandlers)) 19 | * )) 20 | * $(LI Create aliases for the various token and token identifier types 21 | * specific to your language. 22 | * $(UL 23 | * $(LI $(LREF TokenIdType)) 24 | * $(LI $(LREF tokenStringRepresentation)) 25 | * $(LI $(LREF TokenStructure)) 26 | * $(LI $(LREF TokenId)) 27 | * )) 28 | * $(LI Create a struct that mixes in the Lexer template mixin and 29 | * implements the necessary functions. 30 | * $(UL 31 | * $(LI $(LREF Lexer)) 32 | * )) 33 | * ) 34 | * Examples: 35 | * $(UL 36 | * $(LI A _lexer for D is available $(LINK2 https://github.com/Hackerpilot/Dscanner/blob/master/std/d/lexer.d, here).) 37 | * $(LI A _lexer for Lua is available $(LINK2 https://github.com/Hackerpilot/lexer-demo/blob/master/lualexer.d, here).) 38 | * $(LI A _lexer for JSON is available $(LINK2 https://github.com/Hackerpilot/lexer-demo/blob/master/jsonlexer.d, here).) 39 | * ) 40 | * $(DDOC_ANCHOR TemplateParameters) $(H2 Template Parameter Definitions) 41 | * $(DL 42 | * $(DT $(DDOC_ANCHOR defaultTokenFunction) $(B defaultTokenFunction) 43 | * $(DD A function that serves as the default token lexing function. For most 44 | * languages this will be the identifier lexing function.)) 45 | * $(DT $(DDOC_ANCHOR tokenSeparatingFunction) $(B tokenSeparatingFunction)) 46 | * $(DD A function that is able to determine if an identifier/keyword has come 47 | * to an end. This function must return bool and take a single size_t 48 | * argument representing the number of bytes to skip over before looking for 49 | * a separating character.) 50 | * $(DT $(DDOC_ANCHOR staticTokens) $(B staticTokens)) 51 | * $(DD A listing of the tokens whose exact value never changes and which cannot 52 | * possibly be a token handled by the default token lexing function. The 53 | * most common example of this kind of token is an operator such as 54 | * $(D_STRING "*"), or $(D_STRING "-") in a programming language.) 55 | * $(DT $(DDOC_ANCHOR dynamicTokens) $(B dynamicTokens)) 56 | * $(DD A listing of tokens whose value is variable, such as whitespace, 57 | * identifiers, number literals, and string literals.) 58 | * $(DT $(DDOC_ANCHOR possibleDefaultTokens) $(B possibleDefaultTokens)) 59 | * $(DD A listing of tokens that could posibly be one of the tokens handled by 60 | * the default token handling function. An common example of this is 61 | * a keyword such as $(D_STRING "for"), which looks like the beginning of 62 | * the identifier $(D_STRING "fortunate"). $(B tokenSeparatingFunction) is 63 | * called to determine if the character after the $(D_STRING 'r') separates 64 | * the identifier, indicating that the token is $(D_STRING "for"), or if 65 | * lexing should be turned over to the $(B defaultTokenFunction).) 66 | * $(DT $(DDOC_ANCHOR tokenHandlers) $(B tokenHandlers)) 67 | * $(DD A mapping of prefixes to custom token handling function names. The 68 | * generated _lexer will search for the even-index elements of this array, 69 | * and then call the function whose name is the element immedately after the 70 | * even-indexed element. This is used for lexing complex tokens whose prefix 71 | * is fixed.) 72 | * ) 73 | * 74 | * Here are some example constants for a simple calculator _lexer: 75 | * --- 76 | * // There are a near infinite number of valid number literals, so numbers are 77 | * // dynamic tokens. 78 | * enum string[] dynamicTokens = ["numberLiteral", "whitespace"]; 79 | * 80 | * // The operators are always the same, and cannot start a numberLiteral, so 81 | * // they are staticTokens 82 | * enum string[] staticTokens = ["-", "+", "*", "/"]; 83 | * 84 | * // In this simple example there are no keywords or other tokens that could 85 | * // look like dynamic tokens, so this is blank. 86 | * enum string[] possibleDefaultTokens = []; 87 | * 88 | * // If any whitespace character or digit is encountered, pass lexing over to 89 | * // our custom handler functions. These will be demonstrated in an example 90 | * // later on. 91 | * enum string[] tokenHandlers = [ 92 | * "0", "lexNumber", 93 | * "1", "lexNumber", 94 | * "2", "lexNumber", 95 | * "3", "lexNumber", 96 | * "4", "lexNumber", 97 | * "5", "lexNumber", 98 | * "6", "lexNumber", 99 | * "7", "lexNumber", 100 | * "8", "lexNumber", 101 | * "9", "lexNumber", 102 | * " ", "lexWhitespace", 103 | * "\n", "lexWhitespace", 104 | * "\t", "lexWhitespace", 105 | * "\r", "lexWhitespace" 106 | * ]; 107 | * --- 108 | * 109 | * Copyright: Brian Schott 2013 110 | * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt Boost, License 1.0) 111 | * Authors: Brian Schott, with ideas shamelessly stolen from Andrei Alexandrescu 112 | * Source: $(PHOBOSSRC std/experimental/_lexer.d) 113 | */ 114 | 115 | module std.experimental.lexer; 116 | 117 | /** 118 | * Template for determining the type used for a token type. 119 | * 120 | * Selects the smallest unsigned integral type that is able to hold the value 121 | * staticTokens.length + dynamicTokens.length + possibleDefaultTokens.length. 122 | * For example if there are 20 static tokens, 30 dynamic tokens, 123 | * and 10 possible default tokens, this template will alias itself to ubyte, 124 | * as 20 + 30 + 10 < $(D_KEYWORD ubyte).max. 125 | * Examples: 126 | * --- 127 | * // In our calculator example this means that IdType is an alias for ubyte. 128 | * alias IdType = TokenIdType!(staticTokens, dynamicTokens, possibleDefaultTokens); 129 | * --- 130 | */ 131 | template TokenIdType(alias staticTokens, alias dynamicTokens, 132 | alias possibleDefaultTokens) 133 | { 134 | immutable tokenCount = staticTokens.length + dynamicTokens.length 135 | + possibleDefaultTokens.length + 1; 136 | static if (tokenCount <= ubyte.max) 137 | alias TokenIdType = ubyte; 138 | else static if (tokenCount <= ushort.max) 139 | alias TokenIdType = ushort; 140 | else static if (tokenCount <= uint.max) 141 | alias TokenIdType = uint; 142 | else 143 | static assert (false, "The number of tokens must be less than uint.max"); 144 | } 145 | 146 | /** 147 | * Looks up the string representation of the given token type. 148 | * 149 | * This is the opposite of the function of the TokenId template. 150 | * Params: type = the token type identifier 151 | * Examples: 152 | * --- 153 | * alias str = tokenStringRepresentation(IdType, staticTokens, dynamicTokens, possibleDefaultTokens); 154 | * assert (str(tok!"*") == "*"); 155 | * --- 156 | * See_also: $(LREF TokenId) 157 | */ 158 | string tokenStringRepresentation(IdType, alias staticTokens, alias dynamicTokens, 159 | alias possibleDefaultTokens)(IdType type) @property 160 | { 161 | enum tokens = staticTokens ~ dynamicTokens ~ possibleDefaultTokens; 162 | 163 | if (type == 0) 164 | return "!ERROR!"; 165 | else if (type < tokens.length + 1) 166 | return tokens[type - 1]; 167 | else 168 | return null; 169 | } 170 | 171 | unittest 172 | { 173 | alias IdType = TokenIdType!(["foo"], ["bar"], ["doo"]); 174 | enum tok(string token) = TokenId!(IdType, ["foo"], ["bar"], ["doo"], token); 175 | alias str = tokenStringRepresentation!(IdType, ["foo"], ["bar"], ["doo"]); 176 | 177 | static assert (str(tok!"foo") == "foo"); 178 | static assert (str(tok!"bar") == "bar"); 179 | static assert (str(tok!"doo") == "doo"); 180 | } 181 | 182 | /** 183 | * Generates the token type identifier for the given symbol. 184 | * 185 | * There are two special cases: 186 | * $(UL 187 | * $(LI If symbol is $(D_STRING ""), then the token identifier will be 0) 188 | * $(LI If symbol is $(D_STRING "\0"), then the token identifier will be the maximum 189 | * valid token type identifier) 190 | * ) 191 | * In all cases this template will alias itself to a constant of type IdType. 192 | * This template will fail at compile time if $(D_PARAM symbol) is not one of 193 | * the staticTokens, dynamicTokens, or possibleDefaultTokens. 194 | * Examples: 195 | * --- 196 | * template tok(string symbol) 197 | * { 198 | * alias tok = TokenId!(IdType, staticTokens, dynamicTokens, 199 | * possibleDefaultTokens, symbol); 200 | * } 201 | * // num and plus are of type ubyte. 202 | * IdType plus = tok!"+"; 203 | * IdType num = tok!"numberLiteral"; 204 | * --- 205 | */ 206 | template TokenId(IdType, alias staticTokens, alias dynamicTokens, 207 | alias possibleDefaultTokens, string symbol) 208 | { 209 | enum tokens = staticTokens ~ dynamicTokens ~ possibleDefaultTokens; 210 | 211 | import std.algorithm; 212 | static if (symbol == "") 213 | { 214 | enum id = 0; 215 | alias TokenId = id; 216 | } 217 | else static if (symbol == "\0") 218 | { 219 | enum id = 1 + tokens.length; 220 | alias TokenId = id; 221 | } 222 | else 223 | { 224 | enum i = tokens.countUntil(symbol); 225 | static if (i != -1) 226 | { 227 | enum id = i + 1; 228 | static assert (id >= 0 && id < IdType.max, "Invalid token: " ~ symbol); 229 | alias TokenId = id; 230 | } 231 | else 232 | static assert (0, "Invalid token: " ~ symbol); 233 | } 234 | } 235 | 236 | /** 237 | * The token that is returned by the lexer. 238 | * Params: 239 | * IdType = The D type of the "type" token type field. 240 | * extraFields = A string containing D code for any extra fields that should 241 | * be included in the token structure body. This string is passed 242 | * directly to a mixin statement. 243 | * Examples: 244 | * --- 245 | * // No extra struct fields are desired in this example, so leave it blank. 246 | * alias Token = TokenStructure!(IdType, ""); 247 | * Token minusToken = Token(tok!"-"); 248 | * --- 249 | */ 250 | struct TokenStructure(IdType, string extraFields = "") 251 | { 252 | public pure nothrow @safe @nogc: 253 | 254 | bool opEquals(ref const typeof(this) other) const 255 | { 256 | return this.type == other.type && this.text == other.text; 257 | } 258 | 259 | /** 260 | * Returs: true if the token has the given type, false otherwise. 261 | */ 262 | bool opEquals(IdType type) const 263 | { 264 | return this.type == type; 265 | } 266 | 267 | /** 268 | * Constructs a token from a token type. 269 | * Params: type = the token type 270 | */ 271 | this(IdType type) 272 | { 273 | this.type = type; 274 | } 275 | 276 | /** 277 | * Constructs a token. 278 | * Params: 279 | * type = the token type 280 | * text = the text of the token, which may be null 281 | * line = the line number at which this token occurs 282 | * column = the column number at which this token occurs 283 | * index = the byte offset from the beginning of the input at which this 284 | * token occurs 285 | */ 286 | this(IdType type, string text, size_t line, size_t column, size_t index) 287 | { 288 | this.text = text; 289 | this.line = line; 290 | this.column = column; 291 | this.type = type; 292 | this.index = index; 293 | } 294 | 295 | /** 296 | * The _text of the token. 297 | */ 298 | string text; 299 | 300 | /** 301 | * The _line number at which this token occurs. 302 | */ 303 | size_t line; 304 | 305 | /** 306 | * The _column number at which this token occurs. This is measured in bytes 307 | * and may not be correct when tab characters are involved. 308 | */ 309 | size_t column; 310 | 311 | /** 312 | * The byte offset from the beginning of the input at which this token 313 | * occurs. 314 | */ 315 | size_t index; 316 | 317 | /** 318 | * The token type. 319 | */ 320 | IdType type; 321 | 322 | mixin (extraFields); 323 | } 324 | 325 | /** 326 | * The implementation of the _lexer is contained within this mixin template. 327 | * 328 | * To use it, this template should be mixed in to a struct that represents the 329 | * _lexer for your language. This struct should implement the following methods: 330 | * $(UL 331 | * $(LI popFront, which should call this mixin's _popFront() and 332 | * additionally perform any token filtering or shuffling you deem 333 | * necessary. For example, you can implement popFront to skip comment or 334 | * tokens.) 335 | * $(LI A function that serves as the default token lexing function. For 336 | * most languages this will be the identifier lexing function. This 337 | * should then be passed to the $(LREF Lexer) template mixin as the 338 | * $(LINK2 #.defaultTokenFunction defaultTokenFunction) template 339 | * parameter.) 340 | * $(LI A function that is able to determine if an identifier/keyword has 341 | * come to an end. This function must return $(D_KEYWORD bool) and take 342 | * a single $(D_KEYWORD size_t) argument representing the number of 343 | * bytes to skip over before looking for a separating character.) 344 | * $(LI Any functions referred to in the tokenHandlers template paramater. 345 | * These functions must be marked $(D_KEYWORD pure nothrow), take no 346 | * arguments, and return a token) 347 | * $(LI A constructor that initializes the range field as well as calls 348 | * popFront() exactly once (to initialize the _front field).) 349 | * ) 350 | * Params: 351 | * Token = $(LREF TokenStructure) 352 | * defaultTokenFunction = $(LINK2 #.defaultTokenFunction, defaultTokenFunction) 353 | * tokenSeparatingFunction = $(LINK2 #.tokenSeparatingFunction, tokenSeparatingFunction) 354 | * staticTokens = $(LINK2 #.staticTokens, staticTokens) 355 | * dynamicTokens = $(LINK2 #.dynamicTokens, dynamicTokens) 356 | * possibleDefaultTokens = $(LINK2 #.possibleDefaultTokens, possibleDefaultTokens) 357 | * tokenHandlers = $(LINK2 #.tokenHandlers, tokenHandlers) 358 | * Examples: 359 | * --- 360 | * struct CalculatorLexer 361 | * { 362 | * mixin Lexer!(IdType, Token, defaultTokenFunction, isSeparating, 363 | * staticTokens, dynamicTokens, possibleDefaultTokens, tokenHandlers); 364 | * 365 | * this (ubyte[] bytes) 366 | * { 367 | * this.range = LexerRange(bytes); 368 | * popFront(); 369 | * } 370 | * 371 | * void popFront() pure 372 | * { 373 | * _popFront(); 374 | * } 375 | * 376 | * Token lexNumber() pure nothrow @safe 377 | * { 378 | * // implementation goes here 379 | * } 380 | * 381 | * Token lexWhitespace() pure nothrow @safe 382 | * { 383 | * // implementation goes here 384 | * } 385 | * 386 | * Token defaultTokenFunction() pure nothrow @safe 387 | * { 388 | * // There is no default token in the example calculator language, so 389 | * // this is always an error. 390 | * range.popFront(); 391 | * return Token(tok!""); 392 | * } 393 | * 394 | * bool isSeparating(size_t offset) pure nothrow @safe 395 | * { 396 | * // For this example language, always return true. 397 | * return true; 398 | * } 399 | * } 400 | * --- 401 | */ 402 | mixin template Lexer(Token, alias defaultTokenFunction, 403 | alias tokenSeparatingFunction, alias staticTokens, alias dynamicTokens, 404 | alias possibleDefaultTokens, alias tokenHandlers) 405 | { 406 | private alias _IDType = typeof(Token.type); 407 | private enum _tok(string symbol) = TokenId!(_IDType, staticTokens, dynamicTokens, possibleDefaultTokens, symbol); 408 | 409 | static assert (tokenHandlers.length % 2 == 0, "Each pseudo-token must" 410 | ~ " have a corresponding handler function name."); 411 | 412 | static string generateMask(const ubyte[] arr) 413 | { 414 | ulong u; 415 | for (size_t i = 0; i < arr.length && i < 8; i++) 416 | { 417 | u |= (cast(ulong) arr[i]) << (i * 8); 418 | } 419 | 420 | return toHex(u); 421 | } 422 | 423 | private static string generateByteMask(size_t l) 424 | { 425 | return toHex(ulong.max >> ((8 - l) * 8)); 426 | } 427 | 428 | private static size_t calcSplitCount(size_t a, size_t b) pure nothrow 429 | { 430 | int i; 431 | while (true) 432 | { 433 | i++; 434 | a /= 2; 435 | if (a < b) 436 | break; 437 | } 438 | return i; 439 | } 440 | 441 | private static char[] getBeginningChars(string[] allTokens) 442 | { 443 | char[] beginningChars; 444 | for (size_t i = 0; i < allTokens.length; i++) 445 | { 446 | if (allTokens[i].length == 0) 447 | continue; 448 | beginningChars ~= allTokens[i][0]; 449 | size_t j = i + 1; 450 | while (j < allTokens.length && allTokens[i][0] == allTokens[j][0]) 451 | j++; 452 | i = j - 1; 453 | } 454 | return beginningChars; 455 | } 456 | 457 | private static string generateStatements() 458 | { 459 | import std.algorithm : sort; 460 | import std.range : stride; 461 | 462 | string[] pseudoTokens = array(tokenHandlers.stride(2)); 463 | string[] allTokens = array(sort(staticTokens ~ possibleDefaultTokens ~ pseudoTokens).uniq()); 464 | // Array consisting of a sorted list of the first characters of the 465 | // tokens. 466 | char[] beginningChars = getBeginningChars(allTokens); 467 | size_t i = calcSplitCount(beginningChars.length, 8); 468 | return generateStatementsStep(allTokens, pseudoTokens, beginningChars, i); 469 | } 470 | 471 | private static string generateStatementsStep(string[] allTokens, 472 | string[] pseudoTokens, char[] chars, size_t i, string indent = "") 473 | { 474 | string code; 475 | if (i > 0) 476 | { 477 | size_t p = chars.length / 2; 478 | code ~= indent ~ "if (f < "~toHex(chars[p])~") {\n"; 479 | code ~= generateStatementsStep(allTokens, pseudoTokens, chars[0 .. p], i - 1, indent ~ " "); 480 | code ~= indent ~ "}\n" ~ indent ~ "else\n" ~ indent ~ "{\n"; 481 | code ~= generateStatementsStep(allTokens, pseudoTokens, chars[p .. $], i - 1, indent ~ " "); 482 | code ~= indent ~ "}\n"; 483 | } 484 | else 485 | { 486 | code ~= indent ~ "switch (f)\n" ~ indent ~ "{\n"; 487 | foreach (char c; chars) 488 | { 489 | size_t begin; 490 | size_t end; 491 | for (size_t j = 0; j < allTokens.length; j++) 492 | { 493 | if (allTokens[j].length == 0 || allTokens[j][0] != c) 494 | continue; 495 | begin = j; 496 | end = j + 1; 497 | while (end < allTokens.length && allTokens[begin][0] == allTokens[end][0]) 498 | end++; 499 | break; 500 | } 501 | code ~= indent ~ "case "~toHex(c)~":\n"; 502 | code ~= printCase(allTokens[begin .. end], pseudoTokens, indent ~ " "); 503 | } 504 | code ~= indent ~ "default: goto _defaultTokenFunction;\n"; 505 | code ~= indent ~ "}\n"; 506 | } 507 | 508 | return code; 509 | } 510 | 511 | private static string printCase(string[] tokens, string[] pseudoTokens, string indent) 512 | { 513 | import std.array : array; 514 | import std.algorithm : countUntil; 515 | import std.conv : text; 516 | string[] sortedTokens = array(sort!"a.length > b.length"(tokens)); 517 | 518 | 519 | if (tokens.length == 1 && tokens[0].length == 1) 520 | { 521 | if (pseudoTokens.countUntil(tokens[0]) >= 0) 522 | { 523 | return indent ~ tokenHandlers[tokenHandlers.countUntil(tokens[0]) + 1] 524 | ~ "(token);\n" ~ indent ~ "return;\n"; 525 | } 526 | else if (staticTokens.countUntil(tokens[0]) >= 0) 527 | { 528 | return indent ~ "range.index++; range.column++;\n" 529 | ~ indent ~ "token= Token(_tok!\"" ~ escape(tokens[0]) ~ "\", null, line, column, index);\n" 530 | ~ indent ~ "return;"; 531 | } 532 | else if (pseudoTokens.countUntil(tokens[0]) >= 0) 533 | { 534 | return indent ~ tokenHandlers[tokenHandlers.countUntil(tokens[0]) + 1] 535 | ~ "(token);\n" ~ indent ~ "return;\n"; 536 | 537 | } 538 | } 539 | 540 | string code; 541 | 542 | bool insertTrailingGoto = true; 543 | foreach (i, token; sortedTokens) 544 | { 545 | immutable mask = generateMask(cast (const ubyte[]) token); 546 | if (token.length >= 8) 547 | code ~= indent ~ "if (frontBytes == " ~ mask ~ ")\n"; 548 | else if (token.length != 1) 549 | code ~= indent ~ "if ((frontBytes & " ~ generateByteMask(token.length) ~ ") == " ~ mask ~ ")\n"; 550 | if (token.length != 1) 551 | code ~= indent ~ "{\n"; 552 | if (pseudoTokens.countUntil(token) >= 0) 553 | { 554 | if (token.length <= 8) 555 | { 556 | code ~= indent ~ " " 557 | ~ tokenHandlers[tokenHandlers.countUntil(token) + 1] 558 | ~ "(token);\n"; 559 | code ~= indent ~ "return;\n"; 560 | } 561 | else 562 | { 563 | code ~= indent ~ " if (range.startsWith(cast (ubyte[]) \"" ~ escape(token) ~ "\")\n"; 564 | code ~= indent ~ " " 565 | ~ tokenHandlers[tokenHandlers.countUntil(token) + 1] 566 | ~ "();\n"; 567 | code ~= indent ~ "return;\n"; 568 | } 569 | } 570 | else if (staticTokens.countUntil(token) >= 0) 571 | { 572 | if (token.length <= 8) 573 | { 574 | insertTrailingGoto = false; 575 | code ~= indent ~ (token.length != 1 ? " " : "") ~ "range.index += " ~ text(token.length) ~ "; range.column += " ~ text(token.length) ~ ";\n"; 576 | code ~= indent ~ (token.length != 1 ? " " : "") ~ "token = Token(_tok!\"" ~ escape(token) ~ "\", null, line, column, index);\n"; 577 | code ~= indent ~ (token.length != 1 ? " " : "") ~ "return;\n"; 578 | } 579 | else 580 | { 581 | code ~= indent ~ " pragma(msg, \"long static tokens not supported\"); // " ~ escape(token) ~ "\n"; 582 | } 583 | } 584 | else 585 | { 586 | // possible default 587 | if (token.length <= 8) 588 | { 589 | code ~= indent ~ " if (tokenSeparatingFunction(" ~ text(token.length) ~ "))\n"; 590 | code ~= indent ~ " {\n"; 591 | code ~= indent ~ " range.index += " ~ text(token.length) ~ "; range.column += " ~ text(token.length) ~ ";\n"; 592 | code ~= indent ~ " token = Token(_tok!\"" ~ escape(token) ~ "\", null, line, column, index);\n"; 593 | code ~= indent ~ " return;\n"; 594 | code ~= indent ~ " }\n"; 595 | code ~= indent ~ " else\n"; 596 | code ~= indent ~ " goto _defaultTokenFunction;\n"; 597 | } 598 | else 599 | { 600 | code ~= indent ~ " if (range.startsWith(cast (ubyte[]) \"" ~ escape(token) ~"\") && isSeparating(" ~ text(token.length) ~ "))\n"; 601 | code ~= indent ~ " {\n"; 602 | code ~= indent ~ " range.index += " ~ text(token.length) ~ "; range.column += " ~ text(token.length) ~ ";\n"; 603 | code ~= indent ~ " token = Token(_tok!\"" ~ escape(token) ~ "\", null, line, column, index);\n"; 604 | code ~= indent ~ " return;\n"; 605 | code ~= indent ~ " }\n"; 606 | code ~= indent ~ " else\n"; 607 | code ~= indent ~ " goto _defaultTokenFunction;\n"; 608 | } 609 | } 610 | if (token.length != 1) 611 | { 612 | code ~= indent ~ "}\n"; 613 | } 614 | } 615 | if (insertTrailingGoto) 616 | code ~= indent ~ "goto _defaultTokenFunction;\n"; 617 | return code; 618 | } 619 | 620 | /** 621 | * Implements the range primitive _front. 622 | */ 623 | ref const(Token) front()() pure nothrow const @property @safe 624 | { 625 | return _front; 626 | } 627 | 628 | /** 629 | * Advances the lexer to the next token and stores the new current token in 630 | * the _front variable. 631 | */ 632 | void _popFront()() pure nothrow @safe 633 | { 634 | advance(_front); 635 | } 636 | 637 | /** 638 | * Implements the range primitive _empty. 639 | */ 640 | bool empty()() pure const nothrow @property @safe @nogc 641 | { 642 | return _front.type == _tok!"\0"; 643 | } 644 | 645 | static string escape(string input) pure @trusted 646 | { 647 | string retVal; 648 | foreach (ubyte c; cast(ubyte[]) input) 649 | { 650 | switch (c) 651 | { 652 | case '\\': retVal ~= `\\`; break; 653 | case '"': retVal ~= `\"`; break; 654 | case '\'': retVal ~= `\'`; break; 655 | case '\t': retVal ~= `\t`; break; 656 | case '\n': retVal ~= `\n`; break; 657 | case '\r': retVal ~= `\r`; break; 658 | default: retVal ~= c; break; 659 | } 660 | } 661 | return retVal; 662 | } 663 | 664 | enum tokenSearch = generateStatements(); 665 | 666 | static ulong getFront(const ubyte[] arr) pure nothrow @trusted 667 | { 668 | static union ByteArr { ulong l; ubyte[8] arr; } 669 | static assert(ByteArr.sizeof == ulong.sizeof); 670 | ByteArr b; 671 | b.l = ulong.max; 672 | b.arr[0 .. arr.length] = arr[]; 673 | return b.l; 674 | } 675 | 676 | void advance(ref Token token) pure nothrow @trusted 677 | { 678 | if (range.index >= range.bytes.length) 679 | { 680 | token.type = _tok!"\0"; 681 | return; 682 | } 683 | immutable size_t index = range.index; 684 | immutable size_t column = range.column; 685 | immutable size_t line = range.line; 686 | immutable ulong frontBytes = range.index + 8 <= range.bytes.length 687 | ? getFront(range.bytes[range.index .. range.index + 8]) 688 | : getFront(range.bytes[range.index .. $]); 689 | ubyte f = cast(ubyte) frontBytes; 690 | // pragma(msg, tokenSearch); 691 | mixin(tokenSearch); 692 | _defaultTokenFunction: 693 | defaultTokenFunction(token); 694 | } 695 | 696 | /** 697 | * The lexer input. 698 | */ 699 | LexerRange range; 700 | 701 | /** 702 | * The token that is currently at the front of the range. 703 | */ 704 | Token _front; 705 | } 706 | 707 | /** 708 | * Range structure that wraps the _lexer's input. 709 | */ 710 | struct LexerRange 711 | { 712 | // TODO: When D gets @forceinline the template inline hack (i.e 713 | // `void front()() { ... }` )should be removed. 714 | 715 | public nothrow pure @safe @nogc: 716 | /** 717 | * Params: 718 | * bytes = the _lexer input 719 | * index = the initial offset from the beginning of $(D_PARAM bytes) 720 | * column = the initial _column number 721 | * line = the initial _line number 722 | */ 723 | this(const(ubyte)[] bytes, size_t index = 0, size_t column = 1, size_t line = 1) 724 | { 725 | this.bytes = bytes; 726 | this.index = index; 727 | this.column = column; 728 | this.line = line; 729 | } 730 | 731 | /** 732 | * Returns: a mark at the current position that can then be used with slice. 733 | */ 734 | size_t mark()() const 735 | { 736 | return index; 737 | } 738 | 739 | /** 740 | * Sets the range to the given position. 741 | * Params: m = the position to seek to 742 | */ 743 | void seek()(size_t m) 744 | { 745 | index = m; 746 | } 747 | 748 | /** 749 | * Returs a slice of the input byte array between the given mark and the 750 | * current position. 751 | * Params m = the beginning index of the slice to return 752 | */ 753 | const(ubyte)[] slice()(size_t m) const 754 | { 755 | return bytes[m .. index]; 756 | } 757 | 758 | /** 759 | * Implements the range primitive _empty. 760 | */ 761 | bool empty()() const 762 | { 763 | return index >= bytes.length; 764 | } 765 | 766 | /** 767 | * Implements the range primitive _front. 768 | */ 769 | ubyte front()() const 770 | { 771 | return bytes[index]; 772 | } 773 | 774 | /** 775 | * Returns: the current item as well as the items $(D_PARAM p) items ahead. 776 | */ 777 | const(ubyte)[] peek(size_t p) const 778 | { 779 | return index + p + 1 > bytes.length 780 | ? bytes[index .. $] 781 | : bytes[index .. index + p + 1]; 782 | } 783 | 784 | /** 785 | * Returns: true if the range starts with the given byte sequence 786 | */ 787 | bool startsWith(const(ubyte[]) needle) const 788 | { 789 | if (needle.length + index >= bytes.length) 790 | return false; 791 | foreach (i; 0 .. needle.length) 792 | if (needle[i] != bytes[index + i]) 793 | return false; 794 | return true; 795 | } 796 | 797 | /** 798 | * 799 | */ 800 | ubyte peekAt()(size_t offset) const 801 | { 802 | return bytes[index + offset]; 803 | } 804 | 805 | /** 806 | * Returns: true if it is possible to peek $(D_PARAM p) bytes ahead. 807 | */ 808 | bool canPeek()(size_t p) const 809 | { 810 | return index + p < bytes.length; 811 | } 812 | 813 | /** 814 | * Implements the range primitive _popFront. 815 | */ 816 | void popFront()() 817 | { 818 | index++; 819 | column++; 820 | } 821 | 822 | /** 823 | * Implements the algorithm _popFrontN more efficiently. This function does 824 | * not detect or handle newlines. 825 | */ 826 | void popFrontN()(size_t n) 827 | { 828 | index += n; 829 | column += n; 830 | } 831 | 832 | /** 833 | * Increments the range's line number and resets the column counter. 834 | */ 835 | void incrementLine()(size_t i = 1) 836 | { 837 | column = 1; 838 | line += i; 839 | } 840 | 841 | /** 842 | * The input _bytes. 843 | */ 844 | const(ubyte)[] bytes; 845 | 846 | /** 847 | * The range's current position. 848 | */ 849 | size_t index; 850 | 851 | /** 852 | * The current _column number. 853 | */ 854 | size_t column; 855 | 856 | /** 857 | * The current _line number. 858 | */ 859 | size_t line; 860 | } 861 | 862 | nothrow @safe 863 | private void toHexInternal(char[] where, ubyte b) { 864 | if(b < 16) 865 | where[0] = '0'; 866 | else { 867 | ubyte t = (b & 0xf0) >> 4; 868 | if(t >= 10) 869 | where[0] = cast(char) ('A' + t - 10); 870 | else 871 | where[0] = cast(char) ('0' + t); 872 | b &= 0x0f; 873 | } 874 | if(b >= 10) 875 | where[1] = cast(char) ('A' + b - 10); 876 | else 877 | where[1] = cast(char) ('0' + b); 878 | } 879 | 880 | private void toHexInternal(char[] where, ulong b) { 881 | foreach(i; 0 .. 8) 882 | toHexInternal(where[i * 2 .. i * 2 + 2], cast(ubyte) (b >> ((7-i) * 8))); 883 | } 884 | 885 | string toHex(ulong w) { 886 | char[18] buffer; 887 | buffer[0] = '0'; 888 | buffer[1] = 'x'; 889 | toHexInternal(buffer[2 .. $], w); 890 | return buffer.idup; 891 | } 892 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | LIBDPARSE=Dscanner/libdparse/src/dparse/ast.d Dscanner/libdparse/src/dparse/formatter.d Dscanner/libdparse/src/dparse/parser.d Dscanner/libdparse/src/dparse/entities.d Dscanner/libdparse/src/dparse/lexer.d Dscanner/libdparse/src/std/experimental/lexer.d Dscanner/src/astprinter.d 2 | DMD?=dmd 3 | 4 | all: 5 | #$(DMD) diff.d terminal.d $(LIBDPARSE) 6 | $(DMD) -debug -m64 doc2.d latex.d jstex.d syntaxhighlighter.d comment.d stemmer.d dom.d script.d jsvar.d color.d archive.d -J. $(LIBDPARSE) -g # -version=std_parser_verbose 7 | # it may pull in script.d and jsvar.d later fyi 8 | # 9 | #$(DMD) -of/var/www/dpldocs.info/locate locate.d dom.d stemmer.d cgi -J. -version=fastcgi -m64 -debug 10 | pq: 11 | $(DMD) -m64 doc2.d latex.d jstex.d syntaxhighlighter.d comment.d stemmer.d dom.d script.d jsvar.d color.d archive.d -version=with_postgres database.d postgres.d -L-L/usr/local/pgsql/lib -L-lpq -J. $(LIBDPARSE) -g # -version=std_parser_verbose 12 | locate: 13 | $(DMD) -oflocate locate.d dom.d stemmer.d cgi -J. -version=scgi -m64 -debug postgres.d archive.d database.d -L-L/usr/local/pgsql/lib -g 14 | 15 | vps_locate: 16 | ldc2i -oflocate_vps -oq -O3 -m64 locate.d stemmer.d -J. -d-version=scgi -d-version=vps -g -L-L/usr/local/pgsql/lib -L-lpq 17 | ldc: 18 | echo "use make pq instead ldc is broken" 19 | echo ldc2 -oq -O3 --d-debug -m64 doc2.d latex.d jstex.d syntaxhighlighter.d comment.d archive.d stemmer.d dom.d color.d -J. $(LIBDPARSE) --d-version=with_postgres database.d postgres.d -L-L/usr/local/pgsql/lib -L-lpq -g # -version=std_parser_verbose 20 | 21 | http: 22 | $(DMD) -debug -ofserver -version=embedded_httpd -version=with_http_server -m64 doc2.d latex.d archive.d jstex.d cgi.d syntaxhighlighter.d comment.d stemmer.d dom.d script.d jsvar.d color.d -J. $(LIBDPARSE) -g # -version=std_parser_verbose 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | This is my documentation generator, the results of which can be seen on http://dpldocs.info/ 2 | 3 | You should be able to just clone it and run `make`, everything it needs is included. 4 | 5 | After you compile it, you can copy skeleton-default.html to skeleton.html and edit a few things inside it to your liking (or you can leave it and the generator will copy it automatically on first run). You may 6 | want to replace the contents of the `
blocks. 31 | ``` 32 | 33 | ```java 34 | public static void Main() { 35 | return "With some syntax highlighting." 36 | } 37 | ``` 38 | 39 | We also have `inline code`. 40 | 41 | $(TIP and various content boxes.) 42 | 43 | $(MATH \int \text{LaTeX} too! dx) 44 | ) 45 | 46 | 47 | $(H2 Document outline) 48 | 49 | Your comment consists of three parts: the first paragraph, which is meant to be a stand-alone summary which is shown out-of-context in search results, the synopsis, which is displayed "above the fold" - before the function prototype, member list, or automatically generated table of contents, and finally, the rest of the documentation. 50 | 51 | The fold is inserted at the first "\n\n\n" it finds in your comment (the first time it sees two blank lines: 52 | 53 | $(ADRDOX_SAMPLE 54 | 55 | This is the summary. It is shown in search results and 56 | at the top of your generated document. 57 | 58 | This is the synopsis, still displayed above the fold. 59 | 60 | So is this. 61 | 62 | 63 | The two blank lines above is the placeholder where the 64 | table of contents is inserted. This paragraph, and 65 | everything below it, is the bulk body of the page. 66 | 67 | Line breaks in the middle of a paragraph, except in code 68 | blocks, are ignored. You can format your comments however you like. 69 | ) 70 | 71 | $(H3 Symbol grouping) 72 | 73 | You can optionally group symbols together by defining groups in a special section in your module definition comment, then tagging the doc comments on the items. 74 | 75 | --- 76 | /++ 77 | This demos symbol grouping. 78 | 79 | Symbol_groups: 80 | 81 | group_name = 82 | Introductory and explanatory text for the group. It may 83 | include any kind of 84 | 85 | drawing = 86 | ## Drawing 87 | 88 | This library supports several drawing functions. You 89 | draw them all on a "surface" of sorts, derived from 90 | [Drawable]. 91 | +/ 92 | module test; 93 | 94 | /++ Group: group_name 95 | Introductory text 96 | 97 | and paragraphs like normal. 98 | 99 | 100 | This goes below the fold. 101 | +/ 102 | void foo() {} 103 | 104 | /++ 105 | This is in the [drawing] group. 106 | 107 | Group: drawing 108 | +/ 109 | interface Drawable { 110 | /// Group: group_name 111 | void whatever() {} 112 | } 113 | --- 114 | 115 | The `Symbol_groups:` section should only appear on the module commment. The `Group: name` line MUST be the first thing to appear in a comment, or be on the very last line of the comment. It can only appear once. Putting a function in multiple groups is not current supported. 116 | 117 | If there is no header at the start of the group definition, one will be automatically inserted based on the group name. 118 | 119 | For cross referencing purposes, the groups are considered pseudo-symbols at module scope. This means you can refer to them with the shortcut `[symbol]` syntax from anywhere in the module, or from outside the module if used with a fully-qualified name. 120 | 121 | However, for best results, it should not conflict with any real names in the module, nor with any [#footnotes|link references], which also introduce pseudo-symbols. If there is a conflict, the reference result is currently undefined (it may be any one of them, in no particular order). I will define that precedence order at some other time - so for now, avoid name conflicts! 122 | 123 | $(H2 Macros) 124 | 125 | adrdox inherits ddoc's macro syntax, but uses it differently than ddoc: it does not support user-defined macros, and sometimes uses them to bracket special syntax. 126 | 127 | Any time you see me show ddoc macro syntax, `$(NAME )`, be aware that you can also use `${NAME }`. For example, if you have unbalanced parenthesis inside the thing, you may prefer to use `${}`. 128 | 129 | ${ADRDOX_SAMPLE 130 | $(B this is bold) 131 | ${B so is this} 132 | ${I this has unbalanced paren :) } 133 | } 134 | 135 | $(H3 List of supported simple formatting macros) 136 | 137 | ${ADRDOX_SAMPLE 138 | $(B Bold text) 139 | $(I Italic text) 140 | $(HIGHLIGHT Highlighted text) 141 | $(SUPERSCRIPT Superscript) 142 | $(SUBSCRIPT Subscript) 143 | $(DIV HTML division, intended for using 144 | ID and CLASS for more 145 | flexible css customization) 146 | } 147 | 148 | Others may or may not work due to legacy compatibility, but I may remove them without notice so you should not use any not explicitly listed somewhere in this document. 149 | 150 | $(H2 Code snippets) 151 | 152 | $(H3 Inline code) 153 | 154 | Inline code can be marked with Markdown (and Ddoc) style ``code here ``, which will render as `code here`. Text inside the backticks suppress all other documentation generator processing - it will just be escaped for literal output. 155 | 156 | $(TIP If you need to display a literal ``, use the `$(BACKTICK)` macro or a doubled backtick: ````.) 157 | 158 | Code inside backticks may only span one line. If a line has an unmatched backtick, it is not processed as code. 159 | 160 | If you want syntax-highlighted inline D code, use `$(D d code here)`, such as `$(D if(a is true))` will result in $(D if(a is true)) - notice the syntax highlighting on the D keywords. 161 | 162 | $(H3 Block code) 163 | 164 | There are three syntaxes for code blocks: Markdown style $(BACKTICK)$(BACKTICK)$(BACKTICK), ddoc style ---, and a magic macro called `$(CONSOLE)`. 165 | 166 | All code blocks are outdented and leading and trailing blank lines are removed, but all other whitespace is left intact. This means you may indent it as much as you like inside your comments without breaking the output. 167 | 168 | $(H4 Markdown style - for generic code) 169 | 170 | The Markdown style block is meant to be used with generic code or preformatted text that is not D. 171 | 172 | $(ADRDOX_SAMPLE 173 | ``` 174 | Code here which preserves 175 | whitespace 176 | ``` 177 | ) 178 | 179 | You can optionally include a language name after the opening ticks and it will label and attempt syntax highlighting (the syntax highlighter is not as precise as the D highlighter, but often should be good enough): 180 | 181 | $(ADRDOX_SAMPLE 182 | ```javascript 183 | /* This is highlighted Javascript! */ 184 | window.onload = function() { 185 | var a = "hello, world!"; 186 | var b = 5; 187 | }; 188 | ``` 189 | 190 | ```c 191 | /* Highlighted C */ 192 | #include193 | typedef struct { 194 | int a; 195 | } test; 196 | ``` 197 | 198 | ```php 199 | 206 | ``` 207 | 208 | ```python 209 | # highlighted python 210 | class test: 211 | """ docstring """ 212 | def myfunc(): 213 | if True or 1 > 0: 214 | print "hello" 215 | else 216 | print test 217 | ``` 218 | 219 | ```html 220 | 221 | 222 | HTML & 223 | 224 | ``` 225 | 226 | ```css 227 | /* This also highlights */ 228 | span[data-test="foo"] > .bar { 229 | color: red; 230 | } 231 | ``` 232 | 233 | ```sdlang 234 | // dub.sdl can contain comments! 235 | name "somepackage" 236 | description "A little web service of mine." 237 | authors "Peter Parker" 238 | homepage "http://myproject.example.com" 239 | license "GPL-2.0" 240 | dependency "vibe-d" version="~>0.7.23" 241 | configuration "metro-app" { 242 | platforms "windows" 243 | targetType "executable" 244 | versions "MetroApp" 245 | libs "d3d11" 246 | } 247 | configuration "desktop-app" { 248 | platforms "windows" 249 | targetType "executable" 250 | versions "DesktopApp" 251 | libs "d3d9" 252 | } 253 | configuration "glut-app" { 254 | // works on any platform 255 | targetType "executable" 256 | versions "GlutApp" 257 | } 258 | ``` 259 | ) 260 | 261 | Currently supported languages for highlighting include: C, C++, Javascript, PHP, Java, C#, CSS, HTML, XML, Python, Ruby, [arsd.script|adrscript] and D. Though, for D, you should use ddoc style `---` delimiters to get the full-featured D highlighter instead of using the simpler one here. This simple highlighter aims for good enough to help visually on simple examples rather than being perfect on each target language. 262 | 263 | Use the language name in all lower case when tagging the language, like `php` or `c++`. 264 | 265 | $(TIP If you ever want to document the syntax of a Markdown code block itself, I added a magic $(BACKTICK)$(BACKTICK)$(BACKTICK){ code }$(BACKTICK)$(BACKTICK)$(BACKTICK) syntax. As long as the braces are nested, everything inside will be considered part of the literal code block, including other code blocks.) 266 | 267 | The generator MAY syntax highlight the language using `span` with class names, but might not (really depends on if I implement it). You may use the language as a target in CSS using the `data-language` attribute to customize the appearance. 268 | 269 | $(H4 Ddoc style - for D code) 270 | 271 | The ddoc style block only works with D code. It runs the sample through the D lexer, so it understands things like nested documentation comments and will properly skip them while syntax highlighting the output. 272 | 273 | $(ADRDOX_SAMPLE 274 | --- 275 | /** 276 | Ddoc style code blocks understand D deeply. 277 | 278 | --- 279 | if(example.nested) 280 | stillWorks!(); 281 | --- 282 | */ 283 | void main() {} 284 | --- 285 | ) 286 | 287 | Ddoc style code samples are special in one way: you can highlight code inside it by using `/* adrdox_highlight{ */ code here would be highlighted /* }adrdox_highlight */` comments in the sample. Note that it matches those strings $(I exactly), meaning you must use `/* */` comments and must have the same spacing. `/* adrdox_highlight{ */` turns it on, `/* }adrdox_highlight */` turns it off. Note that if you don't turn it off, you may cause invalid html to be generated (the implementation just opens and closes a `span` element right now). 288 | 289 | $(ADRDOX_SAMPLE 290 | --- 291 | // I will demo highlight below for the `main` function 292 | /* adrdox_highlight{ */void main() { 293 | 294 | }/* }adrdox_highlight */ 295 | // and now we are done. 296 | --- 297 | ) 298 | 299 | $(H4 Console macro - for console output) 300 | 301 | The `$(CONSOLE)` macro is for copy/pasting text out of your console, such as showing command lines or program output. You MAY nest macros inside it for additional formatting, and thus, you should escape any `$` followed by `(` in the text. 302 | 303 | $(ADRDOX_SAMPLE 304 | $(CONSOLE 305 | $ dmd hello.d 306 | $ ./hello 307 | Hello, $(B world)! 308 | ) 309 | ) 310 | 311 | Note that most special syntax works inside `$(CONSOLE)`, but Ddoc-style code samples, delimited with `---`, does not. This is because that breaks things more often than it is useful. 312 | 313 | In particular, using the `$(HIGHLIGHT)` macro inside CONSOLE may be helpful. 314 | 315 | $(H3 Documented unittests) 316 | 317 | $(SIDEBAR Why does it allow inline examples? I often write full examples that I want to present in the prose, but I also like the compile check the unittests provide. So to get best of both worlds, I had to do it myself.) 318 | 319 | I also implemented the feature from ddoc where unittests with a documentation comment are appended to the examples section of the previous documented declaration. They will appear in an `Examples` section (together with any others you manually write in `Examples:`), or inline in the documentation if you give them an `$(ID some_unique_name)` in the doc comment of the unittest, and write `$(EMBED_UNITTEST some_unique_name)` somewhere in your body text. Both the test and its associated comment will be moved to that location instead of being put in the examples section. 320 | 321 | If you have a line that must be in the test to be useful, but should not appear in the documentation, you can simply comment it: `// exclude from docs`. But the line must end with that exact string. 322 | 323 | --- 324 | /// The assert inside will not appear in the generated docs 325 | unittest { 326 | int a; 327 | assert(a == 2); // exclude from docs 328 | writeln(a); 329 | } 330 | --- 331 | 332 | $(H2 Cross-referencing) 333 | 334 | Many tasks of cross-referencing are done automatically. Inheritance and function signatures use semantic data from the D source to link themselves. URLs in the raw text, such as http://dpldocs.info/ are detected and hyperlinked automatically. Tables of contents are created, as needed, by scanning for headers. 335 | 336 | However, in your text, you may also want to reference names and links that are not automatically detected. 337 | 338 | $(SIDEBAR It does not attempt to pick out D symbol names automatically from the text, since this leads to a great many false positives. ddoc's attempt to do this failed miserably.) 339 | 340 | Since this is such a common task, I dedicated a short, special syntax to it: square brackets. Write a name or URL inside brackets and it will linkify it, as specifically as it can from the index built from semantic D data. For example: `[arsd.color]` will yield [arsd.color], a link to my color module. 341 | 342 | When documenting code, it will first try to detect a URL. If so, it treats it as a link. Next, it will try to look up the D identifier in the current scope. If it finds it, it will link to the most local variable, following the import graph. If all else fails, it will just assume it is a relative filename and link that way. 343 | 344 | $(NOTE 345 | If you want to load modules for name lookup, but not generate documentation for them, pass 346 | the file or the directory containing to `adrdox` with `--load`. 347 | ) 348 | 349 | In most cases, putting a D name inside brackets should link as you expect. 350 | 351 | You can also change the display name by putting a pipe after the link, followed by text: `[arsd.color|my color module]` gives [arsd.color|my color module]. 352 | 353 | Local sections can be referenced with `[#cross-referencing]`: [#cross-referencing]. 354 | 355 | $(H3 Markdown-style links) 356 | 357 | Markdown-style `[text](url)` links are also supported. There must be no space between the `]` and `(` and it must all appear on the same line. [My link here](http://dpldocs.info). Markdown-style links do $(B not) attempt name lookups like adrdox native `[links]`. 358 | 359 | $(H3 User-defined attribues) 360 | 361 | If you want a UDA to document its uses, you can add the magic macro `$(UDA_USES)` to it somewhere. This will list links to each symbol possessing the uda. 362 | 363 | --- 364 | /++ 365 | This is used on: 366 | 367 | $(UDA_USES) 368 | +/ 369 | enum MyUDA; 370 | 371 | @MyUDA void foo() {} 372 | --- 373 | 374 | (New 12 Dec 2021) You can also get a UDA's arguments as a source string with `$(UDA_STRING)`. 375 | 376 | --- 377 | struct MyUDA { string s; } 378 | 379 | /// The uda says $(UDA_STRING MyUDA). 380 | @MyUDA("cool") void foo() {} 381 | --- 382 | 383 | Please note it does NOT actually perform any evaluations - it is just string lookup on the left side of the parenthesis to find the string that is inside the parenthesis. This might change at a later date. 384 | 385 | $(H2 Paragraph detection) 386 | 387 | The generator will automatically handle paragraph tags by looking for blank lines and other separators. Just write and trust it to do the right thing. (If it doesn't, email me a bug report, please.) 388 | 389 | $(H2 Images) 390 | 391 | You can post images with `$(IMG source_url, alt text)`. The default CSS will put some reasonable size limits and margin on it. 392 | 393 | The image will typically be hosted elsewhere, `IMG` simply takes a URL (though it can be a data url, you need to manage that yourself too). 394 | 395 | FIXME: implement and document `$(LEFT )`, `$(RIGHT )`, and `$(CENTERED )`. 396 | 397 | You may also use inline `$(SVG )` or `$(RAW_HTML)`. FIXME 398 | 399 | Markdown-style `` images are also supported, iff there are no spaces between the symbols and all appear on the same line. . 400 | 401 | Note that if the parens are not there, it is normal![1] (code there: `normal![1]`) 402 | 403 | $(H2 Headers) 404 | 405 | You can use ddoc-style macros for headers: `$(H1 Name of header)`, `$(H2 Subheader)`, and so on through `$(H6)`. Linking will be added automatically by the generator. 406 | 407 | Custom ddoc sections (see below) are translated into ` ` headers. 408 | 409 | You can also use a markdown style `====` under a line to trigger a header. These will render as `
` if at top level, and `
` if under a custom ddoc section (FIXME: that details is not yet implemented). For this to work: 410 | 411 | $(LIST 412 | * The header must be preceded by a blank line 413 | * The `====` must be directly below the header 414 | * The `====` must be followed by a blank line 415 | * There must be at least 4 `=` on the line, and no other text (excluding whitespace). 416 | ) 417 | 418 | $(ADRDOX_SAMPLE 419 | 420 | This is some text preceding the header. 421 | 422 | This is the header 423 | ================== 424 | 425 | This is a paragraph under that header. 426 | ) 427 | 428 | Moreover, markdown style `## Header` are also supported. The number of `#` characters indicate the header level (1-6). Similar restrictions apply: 429 | 430 | $(LIST 431 | * The header must be preceded by and followed by a blank line 432 | * The `#` must be the first non-whitespace character on the line 433 | * There must be a space following the `#` characters. 434 | ) 435 | 436 | $(ADRDOX_SAMPLE 437 | 438 | # H1 439 | 440 | ## H2 441 | 442 | ### H3 443 | 444 | #not a header, missing space 445 | 446 | a # is not a header 447 | 448 | Nor is the following a header 449 | # because it is not preceded by a blank line 450 | ) 451 | 452 | $(H3 Ddoc sections) 453 | 454 | Most the Ddoc sections are supported too, and should be used where appropriate to document your code. I also added one called `diagnostics:`, where you can list common compile errors seen with the function. 455 | 456 | `Examples:` (or `Example:`) is special in that documented unit tests are appended here. 457 | 458 | You may define custom ddoc sections as long as they are all one word and includes at least one underscore in the name. They will be translated to `H3` headers, since they typically go under the `Detailed Description` H2-level header. 459 | 460 | Be sure to correctly nest headers - put H3 under H2, and H4 under H3, etc. Failure to do so may break your table of contents. 461 | 462 | $(ADRDOX_SAMPLE 463 | $(H2 A header) 464 | Some content 465 | $(H3 Another header) 466 | Some more content 467 | 468 | A_Ddoc_Style_Header: 469 | And some content 470 | ) 471 | 472 | 473 | $(H2 Content blocks) 474 | 475 | There are a few content blocks to add boxes to your documentation: `$(TIP)`, `$(NOTE)`, `$(WARNING)`, `$(PITFALL)`, and `$(SIDEBAR)`. Inside these, you may write any content. 476 | 477 | Use these boxes to make certain content stand out so the reader pays attention to something special (or, in the case of `SIDEBAR`, get out of the way so the reader can skip it). The intended semantics are: 478 | 479 | `$(TIP)` is a cool fact to help you make the most of the code. 480 | 481 | `$(NOTE)` is something the reader should be aware of, but they can get it wrong without major consequence. 482 | 483 | `$(WARNING)` is something they need to watch out for, such as potential crashes or memory leaks when using the function. 484 | 485 | `$(PITFALL)` is something that users very commonly get wrong and you want them to see it to avoid making the same mistake yet again. 486 | 487 | `$(SIDEBAR)` will be typically displayed outside the flow of the text. It should be used when you want to expand on some details, but it isn't something the user strictly needs to know. 488 | 489 | $(H2 Fancier Formatting) 490 | 491 | $(SIDEBAR 492 | $(H3 Why use macro syntax to bracket it instead of trying to detect like Markdown does?) 493 | 494 | Basically, I have to support at least some of ddoc macro syntax anyway for compatibility with existing documents like Phobos, so it is a convenient thing to simplify my parser. 495 | 496 | But, beyond that, it also gives me a chance to accept metadata, like class names to add to the HTML by putting them inside the block too. 497 | ) 498 | 499 | There are several magic macros that use domain-specific syntaxes for common formatting tasks, like lists and tables. The ddoc-style macro brackets the text, which is laid out in a particular way to make writing, reading, and editing the data most easy. 500 | 501 | 502 | $(H3 Blockquotes) 503 | 504 | Use the `$(BLOCKQUOTE)` macro to surround the quote. It will render as you expected. 505 | 506 | $(ADRDOX_SAMPLE 507 | $(BLOCKQUOTE 508 | This is a quote! You can write whatever you want in here. 509 | 510 | Including paragraphs, and other content. Unlike markdown, you 511 | do not need to write `>` or spaces or anything else before every 512 | line, instead you just wrap the whole thing in `$(BLOCKQUOTE)`. 513 | 514 | If it has unbalanced parenthesis, you can use `$(LPAREN)` or `$(RPAREN)` 515 | for them. 516 | ) 517 | ) 518 | 519 | $(H3 Lists) 520 | 521 | There are two types of list: `$(LIST)` and `$(NUMBERED_LIST)`. Both work the same way. The only difference is `$(LIST)` generates a `
` tag, while `$(NUMBERED_LIST)` generates a `
` tag. 522 | 523 | Inside the magic list macros, a `*` character at the beginning of a line will create a new list item. 524 | 525 | $(WARNING 526 | Make sure the leading `*` does not line up with your comment marker, or the preprocessor may strip it thinking it is a comment in the style of: 527 | 528 | --- 529 | /** 530 | * one of these 531 | */ 532 | --- 533 | 534 | Since the preprocessor runs before analyzing brackets, it won't know that the star was intentional. 535 | 536 | I recommend indenting your list stars by at least 4 spaces or one tab for best results. 537 | ) 538 | 539 | $(ADRDOX_SAMPLE 540 | $(LIST 541 | * List item 542 | * Another list item 543 | ) 544 | 545 | $(NUMBERED_LIST 546 | * One 547 | * Two 548 | * Three 549 | ) 550 | ) 551 | 552 | Text inside the list items is processed normally. You may nest lists, have paragraphs inside them, or anything else. 553 | 554 | $(TIP You can add a class name to the list element in the HTML by using the `$(CLASS)` magic macro before opening your first list item. Use this class, along with CSS, to apply custom style to the list and its items.) 555 | 556 | You may also use `$(RAW_HTML)` for full control of the output, or legacy Ddoc style `$(UL $(LI ...))` macros to form lists as well. 557 | 558 | $(H3 Tables) 559 | 560 | I support two table syntaxes: list tables (by row and by column, inspired by reStructuredText) and compact tables, with optional ASCII art (inspired by Markdown). 561 | 562 | $(H4 Compact Tables) 563 | 564 | A compact table consists of an optional one-line caption, a one-line header row, and any number of one-line data rows. 565 | 566 | Cells are separated with the `|` character. Empty cells at the beginning or end of the table are ignored, allowing you to draw an ASCII art border around the table if you like. 567 | 568 | The first row is always considered the header row. Columns without header text are also considered header columns. 569 | 570 | The minimal syntax to define a table is: 571 | 572 | $(ADRDOX_SAMPLE 573 | $(SMALL_TABLE 574 | Basic table caption (this line is optional) 575 | header 1|header 2 576 | data 1|data 2 577 | more data | more data 578 | ) 579 | ) 580 | 581 | $(TIP Since the ddoc-style macro bracketing the table must have balanced parenthesis, any unbalanced parenthesis character inside should be put inside a $(BACKTICK)code block$(BACKTICK). You can also put pipe characters inside code blocks: 582 | 583 | $(ADRDOX_SAMPLE 584 | $(SMALL_TABLE 585 | h1|h2 586 | `d1|with pipe`|d2 587 | ) 588 | ) 589 | ) 590 | 591 | ASCII art inside the compact table is allowed, but not required. Any line that consists only of the characters `+-=|` is assumed to be decorative and ignored by the parser. Empty lines are also ignored. White space around your cells are also ignored. 592 | 593 | The result is you can style it how you like. The following code will render the same way as the above table: 594 | 595 | $(ADRDOX_SAMPLE 596 | $(SMALL_TABLE 597 | Basic table caption (this line is optional) 598 | +-----------+-----------+ 599 | | header 1 | header 2 | 600 | +===========+===========+ 601 | | data 1 | data 2 | 602 | | more data | more data | 603 | +-----------+-----------+ 604 | ) 605 | ) 606 | 607 | $(H5 Two-dimensional tabular data) 608 | 609 | If a table has an empty upper-left cell, it is assumed to have two axes. Cells under the column with the empty header are also rendered as headers. 610 | 611 | Here is a two-dimensional table with and without the optional ascii art. 612 | 613 | $(ADRDOX_SAMPLE 614 | $(SMALL_TABLE 615 | 616 | XOR Truth Table 617 | +-----------+ 618 | | | 0 | 1 | 619 | +===|===|===+ 620 | | 0 | F | T | 621 | | 1 | T | F | 622 | +-----------+ 623 | ) 624 | 625 | $(SMALL_TABLE 626 | Alternative XOR 627 | ||0|1 628 | 0|F|T 629 | 1|T|F 630 | ) 631 | ) 632 | 633 | Notice that even without the ascii art, the outer pipe is necessary to indicate that an empty cell was intended in the upper left corner. 634 | 635 | $(TIP 636 | If you want to make a feature table, you can do it as a compact 637 | table with any entry for yes, and no data for no. 638 | 639 | $(ADRDOX_SAMPLE 640 | $(SMALL_TABLE 641 | Features 642 | || x | y 643 | a| * | 644 | b| | * 645 | c| * | * 646 | ) 647 | ) 648 | 649 | You can then style these with CSS rules like `td:empty` in lieu of adding a class to each element. The empty cell on the right did not require an extra `|` because all data rows are assumed to have equal number of cells as the header row. 650 | ) 651 | 652 | $(H4 Longer tables) 653 | 654 | I also support a list table format, inspired by restructuredText. 655 | 656 | $(ADRDOX_SAMPLE 657 | $(TABLE_ROWS 658 | Caption 659 | * + Header 1 660 | + Header 2 661 | * - Data 1 662 | - Data 2 663 | * - Data 1 664 | - Data 2 665 | ) 666 | ) 667 | 668 | In this format, the text before any `*` is the caption. Then, a leading `*` indicates a new row, a leading `+` starts a new table header, and a leading `-` starts a new table cell. The cells can be as long as you like. 669 | 670 | adrdox will also detect if you put a header on the left side of later rows, and format the table accordingly: 671 | 672 | $(ADRDOX_SAMPLE 673 | $(TABLE_ROWS 674 | Caption 675 | * + Header 1 676 | + Header 2 677 | + Header 3 678 | * + 2D Header 679 | - Data 1.2 680 | - Data 1.3 681 | * + Again 682 | - Data 1.2 683 | - Data 2.3 684 | ) 685 | ) 686 | 687 | 688 | 689 | $(H4 Formatting tables) 690 | 691 | To format tables, including aligning text inside a column, add a class name to the tag using the magic `$(CLASS name)` macro right inside the table backeting, then target that with CSS rules in your stylesheet. 692 | 693 | $(ADRDOX_SAMPLE 694 | $(RAW_HTML 695 | 700 | ) 701 | $(TABLE_ROWS 702 | $(CLASS my-yellow-table) 703 | Caption 704 | * + Header 1 705 | + Header 2 706 | * - Data 1 707 | - Data 2 708 | * - Data 1 709 | - Data 2 710 | ) 711 | ) 712 | 713 | 714 | $(H4 More advanced tables) 715 | 716 | To avoid complicating the syntax in more common cases, I do not attempt to support everything possible. Notably, most cases of colspan and rowspan cannot be expressed in any of my syntaxes. 717 | 718 | If you need something, and all else fails, you can always use the `$(RAW_HTML)` escape hatch and write the code yourself. 719 | 720 | $(H2 Mathematics) 721 | 722 | The doc generator can also render LaTeX formulas, if latex and dvipng is installed on your system. 723 | 724 | $(ADRDOX_SAMPLE 725 | $(MATH \int_{1}^{\pi} \cos(x) dx ) 726 | ) 727 | 728 | Note that generating these images is kinda slow. You must balance parenthesis inside the macro, and all the output images will be rendered inline, packed in the html file. 729 | 730 | If you can use a plain text or html character btw, you should. Don't break out MATH just for an $(INF) symbol, for example. 731 | 732 | $(H2 Ddoc Macro to HTML Tag reference) 733 | 734 | $(LIST 735 | * `$(IMG source_url, alt text)` 736 | * `$(B bold text)` 737 | * `$(I italic text)` 738 | * `$(U underlined text)` 739 | * `$(SUPERSCRIPT superscript text)` 740 | * `$(SUB subscript text)` 741 | ) 742 | 743 | $(H3 Adding ID and class attributes to HTML) 744 | 745 | You can add an ID or class attribute to an HTML tag by putting `$(ID id_here)` or `$(CLASS class_here)` inside a ddoc macro. It must be inside a `$(ddoc_style)` macro to be recognized. 746 | 747 | $(H2 Ddoc Sections) 748 | 749 | $(H3 List of supported DDoc Sections) 750 | 751 | $(LIST 752 | * `Examples:` or `Example:` gives usage examples. Documented unittests, if present and not embedded (see [#documented-unittests]), will also appear here. 753 | * `Bugs:` 754 | * `See_Also:` 755 | * `Returns:` 756 | * `Throws:` 757 | * `Deprecated:` 758 | * `Params:` uses a special `name = comment` syntax, just like ddoc, where only param names detected are printed. 759 | * `Macros:` are read, but ignored. 760 | ) 761 | 762 | Note that as an extension to ddoc, I also support doc comments on params as if it was written in the `Params:` section. 763 | 764 | $(H3 Meta subsections) 765 | 766 | The following sections, if present, will be grouped under the `Meta` header: 767 | 768 | $(LIST 769 | * `Authors:` or `Author:` 770 | * `Date` 771 | * `License:` 772 | * `Source:` 773 | * `History:` 774 | * `Credits:` 775 | * `Standards:` 776 | * `Copyright:` 777 | * `Version:` 778 | ) 779 | 780 | $(H3 Adrdox extension sections) 781 | 782 | $(LIST 783 | * `Diagnostics:` is a place to describe common errors you will see while trying to use the function, and explain how to fix them. 784 | * `Link_References:` does name=value. See [#footnotes]. 785 | $(COMMENT * `Adrdox_Meta:` intrduces metadata for the generator. See [#metadata] ) 786 | ) 787 | 788 | $(H3 Custom sections) 789 | 790 | If you start a line with `some_section:`, it will become a custom section in the document. It must have at least one underscore to be recognizes as a custom section. 791 | 792 | $(COMMENT 793 | $(H2 Metadata) 794 | 795 | FIXME: NOT IMPLEMENTED 796 | 797 | You can add metadata about your project to a `Adrdox_Meta:` section in the doc comment attached to the module declaration. These are inherited by submodules in your project as long as the package.d with the definition is loaded (see `--load` or passed as command line arg to be generated). 798 | 799 | It can define: 800 | $(LINK 801 | * Project name 802 | * Project logo image 803 | * Project homepage 804 | * Project color scheme: light or dark and accent color 805 | * Scripts for the project 806 | ) 807 | ) 808 | 809 | $(H2 Footnotes) 810 | 811 | adrdox supports footnotes[1] and popup notes[2], scoped to the declaration attached to the comment. The syntax is to simply write `[n]`, such as `[1]`, where you want it to be displayed, then later in the comment, write a `Link_References:` section at the end of your comment, like so: 812 | 813 | ``` 814 | Link_References: 815 | 1 = https://en.wikipedia.org/wiki/Footnote 816 | 2 = This note will popup inline. 817 | ``` 818 | 819 | Undefined footnote references output the plain text without modification, like [3]. Numeric footnotes can only be used locally, they must be used and defined inside the same comment. 820 | 821 | $(NOTE Text references must always be contained to a single line in the current implementation.) 822 | 823 | If you need something more complex than a single link or line of text, write a section for your notes inside your comment and use the `[n]` Link_References to link to it: 824 | 825 | --- 826 | /++ 827 | This huge complex function needs a complex footnote[1]. 828 | 829 | $(H2 Footnotes) 830 | 831 | $(DIV $(ID note-1) 832 | This can be arbitrarily complex. 833 | ) 834 | 835 | Link_References: 836 | 1 = [a_huge_complex_function#note-1] 837 | +/ 838 | void a_huge_complex_function() {} 839 | --- 840 | 841 | See that live [a_huge_complex_function|here]. 842 | 843 | You can also do custom links, images, or popup text via the shortcut `[reference]` syntax. You can define them with a symbol name in the Link_References section: 844 | 845 | ``` 846 | Link_References: 847 | dlang = http://dlang.org/ 848 | dlogo = $(IMG /d-logo.png, The D Logo) 849 | dmotto = Modern convenience. Modeling power. Native efficiency. 850 | ``` 851 | 852 | You can now reference those with `[dlang], [dlogo], and [dmotto]`, which will render thusly: [dlang], [dlogo], [dmotto]. Be aware that ONLY a single line of plain text, a single `$(IMG)`, or a single link (url or convenience reference, see below) are allowed in the `Link_References` section. 853 | 854 | $(NOTE 855 | Link references will override D name lookup. Be aware of name clashes that might 856 | break convenient access to in-scope symbol names. 857 | ) 858 | 859 | Like with other convenience links, you can change the displayed text by using a pipe character, like `[dlang|The D Website]`. It will continue to link to the same place or pop up the same text. If the name references an image, the text after the pipe will locally change the `alt` text on the image tag. 860 | 861 | Additionally, the pipe character can be used in the reference definition to change the default display text: 862 | 863 | ``` 864 | Link_References: 865 | input_range = [std.range.primitives.isInputRange|input range] 866 | ``` 867 | 868 | will always show "input range" when you write `[input_range]`, but can still be overridden by local text after the pipe, like `[input_range|an input range]`. Those will render: [input_range] and [input_range|an input range]. 869 | 870 | $(TIP 871 | Yes, you can define link references in terms of a D reference. It will look up the name using the normal scoping rules for the attached declaration. 872 | ) 873 | 874 | $(WARNING 875 | If you use a reference in a global reference definition, it will look up the name in the scope at the *usage point*. This may change in the future. 876 | ) 877 | 878 | Unrecognized refs are forwarded to regular lookups. 879 | 880 | While numeric link references are strictly scoped to the declaration of the attached comment, text link references are inherited by child declarations. Thus, you can define shortcuts at module scope and use them throughout the module. You can even define one in a package and use it throughout the package, without explicitly importing the `package.d` inside the module. Link references, however, are $(I not) imported like normal D symbols. They follow a strict parent->child inheritance. 881 | 882 | If you need a link reference to be used over and over across packages, you may also define global link references in a text file you pass to adrdox with the `--link-references` option. The format of this text file is as follows: 883 | 884 | ``` 885 | name = value 886 | othername = other value 887 | ``` 888 | 889 | Yes, the same as the `Link_References:` section inside a comment, but with no surrounding decoration. 890 | 891 | $(PITFALL Be especially careful when defining global textual link macros, because they will override normal name lookups when doing `[convenient]` cross references across the entire current documentation build set.) 892 | 893 | You may want to give unique, yet convenient names to common concepts used throughout your library and define them as Link_References for easy use. 894 | 895 | Link_References: 896 | 1 = http://dpldocs.info/ 897 | 2 = Popup notes are done as tags with title attributes. 898 | input_range = [std.range.primitives.isInputRange|input range] 899 | dlang = http://dlang.org/ 900 | dlogo = $(IMG /d-logo.png, The D Logo) 901 | dmotto = Modern convenience. Modeling power. Native efficiency. 902 | 903 | $(H2 Side-by-side comparisons) 904 | 905 | You might want to show two things side by side to emphasize how the user's existing knowledge can be shared. You can do that with the `$(SIDE_BY_SIDE $(COLUMN))` syntax: 906 | 907 | $(ADRDOX_SAMPLE 908 | $(SIDE_BY_SIDE 909 | $(COLUMN 910 | ```php 911 | 914 | ``` 915 | ) 916 | $(COLUMN 917 | --- 918 | import arsd.cgi; 919 | string foo = cgi.post["foo"]; 920 | --- 921 | ) 922 | ) 923 | ) 924 | 925 | Try to keep your columns as narrow as reasonable, so they can actually be read side-by-side! 926 | 927 | $(H2 Commenting stuff out in comments) 928 | 929 | The macro `$(COMMENT ...)` is removed from the generated document. You can use it to comment 930 | stuff out of your comment. Of course, you can also just use regular `/*` comments instead of 931 | `/**`. 932 | 933 | $(H2 Always Documenting Something) 934 | 935 | If you want something to always be documented, even if it is private, add `$(ALWAYS_DOCUMENT)` to its comment somewhere. 936 | 937 | $(H2 Never Documenting Something) 938 | 939 | If you want something to NEVER be documented, even if adrdox is run with --document-undocumented and other switches, add `$(NEVER_DOCUMENT)` to its comment somewhere. You should use this very rarely. 940 | 941 | $(H2 Documentable Constructs) 942 | 943 | adrdox allows documenting more language constructs than ddoc. It lets you document public imports, postblits, destructors, anonymous enums, and more. Try putting a doc comment on almost anything and see what happens! 944 | 945 | +/ 946 | module adrdox.syntax; 947 | 948 | /+ 949 | /// first 950 | struct A { 951 | /// second 952 | union { 953 | /// third 954 | int a; 955 | /// fourth 956 | int b; 957 | } 958 | } 959 | +/ 960 | 961 | 962 | /* 963 | 964 | $(H3 Code with output) 965 | 966 | The magic macro `$(CODE_WITH_OUTPUT)` is used to pair a block of code with a block of output, side-by-side. The first code block in the macro is considered the code, and the rest of the content is the output. 967 | 968 | As a special case, if the code is of the `adrdox` language, you do not need to provide output; it will render automatically. (I added that feature to make writing this document easer.) I might add other language filters too, probably by piping it out to some command line, if there's demand for it. 969 | 970 | I intend for this to be used to show syntax translations, but any time where a side-by-side view may be useful you can give it a try. 971 | 972 | */ 973 | /++ 974 | This huge complex function needs a complex footnote[1]. 975 | 976 | $(H2 Footnotes) 977 | 978 | $(DIV $(ID note-1) 979 | This can be arbitrarily complex. 980 | ) 981 | 982 | Link_References: 983 | 1 = [a_huge_complex_function#note-1] 984 | +/ 985 | void a_huge_complex_function() {} 986 | 987 | /// 988 | void test() {} 989 | -------------------------------------------------------------------------------- /adrdox_docs/tips.d: -------------------------------------------------------------------------------- 1 | // just docs: Tips on using adrdox 2 | /++ 3 | $(LIST 4 | * Always have a package.d for your package, even if it contains no declarations. 5 | * Always use a module declaration on all modules, and always put a comment on it. 6 | * Always put a ddoc comment on a public decl, even if it is an empty comment. 7 | * Want an index.html generated? Run adrdox on a `module index;` (this is a filthy hack but it works) 8 | ) 9 | 10 | adrdox will not descend into undocumented entities, so a missing doc comment on a top level 11 | declaration will make all effort in documenting inner items useless. If you do not document 12 | a module declaration, the whole module is skipped! 13 | +/ 14 | module adrdox.tips; 15 | -------------------------------------------------------------------------------- /db.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE dub_package ( 2 | id SERIAL, 3 | name TEXT NOT NULL, 4 | url_name TEXT NOT NULL, 5 | description TEXT NOT NULL, 6 | adrdox_cmdline_options TEXT NOT NULL, 7 | parent_id INTEGER NULL, -- for subpackages 8 | 9 | -- FIXME: adjustment score? 10 | 11 | PRIMARY KEY(id) 12 | ); 13 | CREATE INDEX dub_packages_by_name ON dub_package(name); 14 | 15 | CREATE TABLE package_version ( 16 | id SERIAL, 17 | dub_package_id INTEGER NOT NULL, 18 | version_tag TEXT NOT NULL, 19 | release_date TIMESTAMPTZ, 20 | is_latest BOOLEAN NOT NULL, 21 | 22 | FOREIGN KEY(dub_package_id) REFERENCES dub_package(id) ON DELETE CASCADE ON UPDATE CASCADE, 23 | 24 | PRIMARY KEY(id) 25 | ); 26 | 27 | CREATE TABLE d_symbols ( 28 | id SERIAL, 29 | package_version_id INTEGER NOT NULL, 30 | name TEXT NOT NULL, 31 | nesting_level INTEGER NOT NULL, -- 0 for module, 1 for top level in module, etc 32 | module_name TEXT NOT NULL, 33 | fully_qualified_name TEXT NOT NULL, 34 | url_name TEXT NOT NULL, -- can have a .1 and/or # in it btw. but should not have .html. 35 | summary TEXT NOT NULL, 36 | 37 | FOREIGN KEY(package_version_id) REFERENCES package_version(id) ON DELETE CASCADE ON UPDATE CASCADE, 38 | 39 | PRIMARY KEY(id) 40 | ); 41 | CREATE INDEX d_symbols_by_name ON d_symbols(name); 42 | CREATE INDEX d_symbols_by_fqn ON d_symbols(fully_qualified_name); 43 | CREATE INDEX d_symbols_by_module_name ON d_symbols(substring(fully_qualified_name, length(module_name) + 2)); 44 | 45 | create index d_symbols_by_lower on d_symbols (lower(name)); 46 | 47 | CREATE TABLE auto_generated_tags ( 48 | id SERIAL, 49 | tag TEXT NOT NULL, 50 | d_symbols_id INTEGER NOT NULL, 51 | score INTEGER NOT NULL, 52 | package_version_id INTEGER NOT NULL, 53 | 54 | FOREIGN KEY(d_symbols_id) REFERENCES d_symbols(id) ON DELETE CASCADE ON UPDATE CASCADE, 55 | FOREIGN KEY(package_version_id) REFERENCES package_version(id) ON DELETE CASCADE ON UPDATE CASCADE, 56 | 57 | PRIMARY KEY(id) 58 | ); 59 | CREATE INDEX auto_generated_tags_by_tag ON auto_generated_tags(tag); 60 | 61 | CREATE TABLE hand_written_tags ( 62 | id SERIAL, 63 | tag TEXT NOT NULL, 64 | d_symbol_fully_qualified_name TEXT NOT NULL, 65 | score INTEGER NOT NULL, 66 | 67 | PRIMARY KEY(id) 68 | ); 69 | CREATE INDEX hand_written_tags_by_tag ON hand_written_tags(tag); 70 | 71 | CREATE TABLE adrdox_schema ( 72 | schema_version INTEGER NOT NULL 73 | ); 74 | 75 | INSERT INTO adrdox_schema VALUES (1); 76 | -------------------------------------------------------------------------------- /dub.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "adrdox", 3 | "description": "A better alternative for documentation generation in D code", 4 | "authors": ["Adam D. Ruppe"], 5 | "homepage": "https://github.com/adamdruppe/adrdox", 6 | "license": "BSL-1.0", 7 | 8 | "copyFiles": ["skeleton-default.html", "script.js", "style.css", "search-docs.js", "search-docs.html", "katex"], 9 | "targetPath": "build", 10 | "targetType": "executable", 11 | "buildRequirements": ["allowWarnings"], 12 | 13 | "sourcePaths": ["Dscanner/libdparse/src"], 14 | "sourceFiles": ["doc2.d", "latex.d", "jstex.d", "cgi.d", "comment.d", "stemmer.d", "dom.d", "archive.d", "jsvar.d", "script.d", "color.d", "syntaxhighlighter.d", "Dscanner/src/astprinter.d"], 15 | "stringImportPaths": ["."], 16 | "libs-posix": [ "z" ] 17 | } 18 | -------------------------------------------------------------------------------- /jstex.d: -------------------------------------------------------------------------------- 1 | module adrdox.jstex; 2 | 3 | import arsd.dom; 4 | 5 | enum auto mathSpanCssClass = "raw-tex-math"; 6 | 7 | static immutable string[] filesForKaTeX = (){ 8 | string[] files = []; 9 | 10 | static foreach (ext; ["css", "js"]) { 11 | files ~= "katex.min." ~ ext; 12 | } 13 | static foreach (ext; ["ttf", "woff", "woff2"]) { 14 | static foreach (family; ["AMS-Regular", 15 | "Caligraphic-Bold", "Caligraphic-Regular", 16 | "Fraktur-Bold", "Fraktur-Regular", 17 | "Main-BoldItalic", "Main-Bold", "Main-Italic", "Main-Regular", 18 | "Math-BoldItalic", "Math-Italic", 19 | "SansSerif-Bold", "SansSerif-Italic", "SansSerif-Regular", 20 | "Script-Regular", 21 | "Size1-Regular", "Size2-Regular", "Size3-Regular", "Size4-Regular", 22 | "Typewriter-Regular"]) { 23 | files ~= "KaTeX_" ~ family ~ "." ~ ext; 24 | } 25 | } 26 | return files; 27 | }(); 28 | 29 | void prepareForKaTeX(Document document) { 30 | auto style = Element.make("link"); 31 | style.rel = "stylesheet"; 32 | style.href = "katex.min.css"; 33 | 34 | auto head = document.getFirstElementByTagName("head"); 35 | head.addChild(style); 36 | 37 | auto script = Element.make("script"); 38 | script.type = "text/javascript"; 39 | script.src = "katex.min.js"; 40 | script.attrs.onload = "document.querySelectorAll('span." ~ mathSpanCssClass ~ "').forEach(function(e){e.outerHTML=katex.renderToString(e.innerText)})"; 41 | 42 | auto body_ = document.getFirstElementByTagName("body"); 43 | body_.addChild(script); 44 | } 45 | 46 | Element mathToKaTeXHtml(string mathCode) { 47 | return Element.make("span", mathCode, mathSpanCssClass); 48 | } 49 | -------------------------------------------------------------------------------- /katex/KaTeX_AMS-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamdruppe/adrdox/9915e53ee6656b38167bfc4d12ef8710dd3654f2/katex/KaTeX_AMS-Regular.ttf -------------------------------------------------------------------------------- /katex/KaTeX_AMS-Regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamdruppe/adrdox/9915e53ee6656b38167bfc4d12ef8710dd3654f2/katex/KaTeX_AMS-Regular.woff -------------------------------------------------------------------------------- /katex/KaTeX_AMS-Regular.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamdruppe/adrdox/9915e53ee6656b38167bfc4d12ef8710dd3654f2/katex/KaTeX_AMS-Regular.woff2 -------------------------------------------------------------------------------- /katex/KaTeX_Caligraphic-Bold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamdruppe/adrdox/9915e53ee6656b38167bfc4d12ef8710dd3654f2/katex/KaTeX_Caligraphic-Bold.ttf -------------------------------------------------------------------------------- /katex/KaTeX_Caligraphic-Bold.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamdruppe/adrdox/9915e53ee6656b38167bfc4d12ef8710dd3654f2/katex/KaTeX_Caligraphic-Bold.woff -------------------------------------------------------------------------------- /katex/KaTeX_Caligraphic-Bold.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamdruppe/adrdox/9915e53ee6656b38167bfc4d12ef8710dd3654f2/katex/KaTeX_Caligraphic-Bold.woff2 -------------------------------------------------------------------------------- /katex/KaTeX_Caligraphic-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamdruppe/adrdox/9915e53ee6656b38167bfc4d12ef8710dd3654f2/katex/KaTeX_Caligraphic-Regular.ttf -------------------------------------------------------------------------------- /katex/KaTeX_Caligraphic-Regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamdruppe/adrdox/9915e53ee6656b38167bfc4d12ef8710dd3654f2/katex/KaTeX_Caligraphic-Regular.woff -------------------------------------------------------------------------------- /katex/KaTeX_Caligraphic-Regular.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamdruppe/adrdox/9915e53ee6656b38167bfc4d12ef8710dd3654f2/katex/KaTeX_Caligraphic-Regular.woff2 -------------------------------------------------------------------------------- /katex/KaTeX_Fraktur-Bold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamdruppe/adrdox/9915e53ee6656b38167bfc4d12ef8710dd3654f2/katex/KaTeX_Fraktur-Bold.ttf -------------------------------------------------------------------------------- /katex/KaTeX_Fraktur-Bold.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamdruppe/adrdox/9915e53ee6656b38167bfc4d12ef8710dd3654f2/katex/KaTeX_Fraktur-Bold.woff -------------------------------------------------------------------------------- /katex/KaTeX_Fraktur-Bold.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamdruppe/adrdox/9915e53ee6656b38167bfc4d12ef8710dd3654f2/katex/KaTeX_Fraktur-Bold.woff2 -------------------------------------------------------------------------------- /katex/KaTeX_Fraktur-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamdruppe/adrdox/9915e53ee6656b38167bfc4d12ef8710dd3654f2/katex/KaTeX_Fraktur-Regular.ttf -------------------------------------------------------------------------------- /katex/KaTeX_Fraktur-Regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamdruppe/adrdox/9915e53ee6656b38167bfc4d12ef8710dd3654f2/katex/KaTeX_Fraktur-Regular.woff -------------------------------------------------------------------------------- /katex/KaTeX_Fraktur-Regular.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamdruppe/adrdox/9915e53ee6656b38167bfc4d12ef8710dd3654f2/katex/KaTeX_Fraktur-Regular.woff2 -------------------------------------------------------------------------------- /katex/KaTeX_Main-Bold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamdruppe/adrdox/9915e53ee6656b38167bfc4d12ef8710dd3654f2/katex/KaTeX_Main-Bold.ttf -------------------------------------------------------------------------------- /katex/KaTeX_Main-Bold.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamdruppe/adrdox/9915e53ee6656b38167bfc4d12ef8710dd3654f2/katex/KaTeX_Main-Bold.woff -------------------------------------------------------------------------------- /katex/KaTeX_Main-Bold.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamdruppe/adrdox/9915e53ee6656b38167bfc4d12ef8710dd3654f2/katex/KaTeX_Main-Bold.woff2 -------------------------------------------------------------------------------- /katex/KaTeX_Main-BoldItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamdruppe/adrdox/9915e53ee6656b38167bfc4d12ef8710dd3654f2/katex/KaTeX_Main-BoldItalic.ttf -------------------------------------------------------------------------------- /katex/KaTeX_Main-BoldItalic.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamdruppe/adrdox/9915e53ee6656b38167bfc4d12ef8710dd3654f2/katex/KaTeX_Main-BoldItalic.woff -------------------------------------------------------------------------------- /katex/KaTeX_Main-BoldItalic.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamdruppe/adrdox/9915e53ee6656b38167bfc4d12ef8710dd3654f2/katex/KaTeX_Main-BoldItalic.woff2 -------------------------------------------------------------------------------- /katex/KaTeX_Main-Italic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamdruppe/adrdox/9915e53ee6656b38167bfc4d12ef8710dd3654f2/katex/KaTeX_Main-Italic.ttf -------------------------------------------------------------------------------- /katex/KaTeX_Main-Italic.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamdruppe/adrdox/9915e53ee6656b38167bfc4d12ef8710dd3654f2/katex/KaTeX_Main-Italic.woff -------------------------------------------------------------------------------- /katex/KaTeX_Main-Italic.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamdruppe/adrdox/9915e53ee6656b38167bfc4d12ef8710dd3654f2/katex/KaTeX_Main-Italic.woff2 -------------------------------------------------------------------------------- /katex/KaTeX_Main-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamdruppe/adrdox/9915e53ee6656b38167bfc4d12ef8710dd3654f2/katex/KaTeX_Main-Regular.ttf -------------------------------------------------------------------------------- /katex/KaTeX_Main-Regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamdruppe/adrdox/9915e53ee6656b38167bfc4d12ef8710dd3654f2/katex/KaTeX_Main-Regular.woff -------------------------------------------------------------------------------- /katex/KaTeX_Main-Regular.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamdruppe/adrdox/9915e53ee6656b38167bfc4d12ef8710dd3654f2/katex/KaTeX_Main-Regular.woff2 -------------------------------------------------------------------------------- /katex/KaTeX_Math-BoldItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamdruppe/adrdox/9915e53ee6656b38167bfc4d12ef8710dd3654f2/katex/KaTeX_Math-BoldItalic.ttf -------------------------------------------------------------------------------- /katex/KaTeX_Math-BoldItalic.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamdruppe/adrdox/9915e53ee6656b38167bfc4d12ef8710dd3654f2/katex/KaTeX_Math-BoldItalic.woff -------------------------------------------------------------------------------- /katex/KaTeX_Math-BoldItalic.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamdruppe/adrdox/9915e53ee6656b38167bfc4d12ef8710dd3654f2/katex/KaTeX_Math-BoldItalic.woff2 -------------------------------------------------------------------------------- /katex/KaTeX_Math-Italic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamdruppe/adrdox/9915e53ee6656b38167bfc4d12ef8710dd3654f2/katex/KaTeX_Math-Italic.ttf -------------------------------------------------------------------------------- /katex/KaTeX_Math-Italic.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamdruppe/adrdox/9915e53ee6656b38167bfc4d12ef8710dd3654f2/katex/KaTeX_Math-Italic.woff -------------------------------------------------------------------------------- /katex/KaTeX_Math-Italic.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamdruppe/adrdox/9915e53ee6656b38167bfc4d12ef8710dd3654f2/katex/KaTeX_Math-Italic.woff2 -------------------------------------------------------------------------------- /katex/KaTeX_SansSerif-Bold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamdruppe/adrdox/9915e53ee6656b38167bfc4d12ef8710dd3654f2/katex/KaTeX_SansSerif-Bold.ttf -------------------------------------------------------------------------------- /katex/KaTeX_SansSerif-Bold.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamdruppe/adrdox/9915e53ee6656b38167bfc4d12ef8710dd3654f2/katex/KaTeX_SansSerif-Bold.woff -------------------------------------------------------------------------------- /katex/KaTeX_SansSerif-Bold.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamdruppe/adrdox/9915e53ee6656b38167bfc4d12ef8710dd3654f2/katex/KaTeX_SansSerif-Bold.woff2 -------------------------------------------------------------------------------- /katex/KaTeX_SansSerif-Italic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamdruppe/adrdox/9915e53ee6656b38167bfc4d12ef8710dd3654f2/katex/KaTeX_SansSerif-Italic.ttf -------------------------------------------------------------------------------- /katex/KaTeX_SansSerif-Italic.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamdruppe/adrdox/9915e53ee6656b38167bfc4d12ef8710dd3654f2/katex/KaTeX_SansSerif-Italic.woff -------------------------------------------------------------------------------- /katex/KaTeX_SansSerif-Italic.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamdruppe/adrdox/9915e53ee6656b38167bfc4d12ef8710dd3654f2/katex/KaTeX_SansSerif-Italic.woff2 -------------------------------------------------------------------------------- /katex/KaTeX_SansSerif-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamdruppe/adrdox/9915e53ee6656b38167bfc4d12ef8710dd3654f2/katex/KaTeX_SansSerif-Regular.ttf -------------------------------------------------------------------------------- /katex/KaTeX_SansSerif-Regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamdruppe/adrdox/9915e53ee6656b38167bfc4d12ef8710dd3654f2/katex/KaTeX_SansSerif-Regular.woff -------------------------------------------------------------------------------- /katex/KaTeX_SansSerif-Regular.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamdruppe/adrdox/9915e53ee6656b38167bfc4d12ef8710dd3654f2/katex/KaTeX_SansSerif-Regular.woff2 -------------------------------------------------------------------------------- /katex/KaTeX_Script-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamdruppe/adrdox/9915e53ee6656b38167bfc4d12ef8710dd3654f2/katex/KaTeX_Script-Regular.ttf -------------------------------------------------------------------------------- /katex/KaTeX_Script-Regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamdruppe/adrdox/9915e53ee6656b38167bfc4d12ef8710dd3654f2/katex/KaTeX_Script-Regular.woff -------------------------------------------------------------------------------- /katex/KaTeX_Script-Regular.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamdruppe/adrdox/9915e53ee6656b38167bfc4d12ef8710dd3654f2/katex/KaTeX_Script-Regular.woff2 -------------------------------------------------------------------------------- /katex/KaTeX_Size1-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamdruppe/adrdox/9915e53ee6656b38167bfc4d12ef8710dd3654f2/katex/KaTeX_Size1-Regular.ttf -------------------------------------------------------------------------------- /katex/KaTeX_Size1-Regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamdruppe/adrdox/9915e53ee6656b38167bfc4d12ef8710dd3654f2/katex/KaTeX_Size1-Regular.woff -------------------------------------------------------------------------------- /katex/KaTeX_Size1-Regular.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamdruppe/adrdox/9915e53ee6656b38167bfc4d12ef8710dd3654f2/katex/KaTeX_Size1-Regular.woff2 -------------------------------------------------------------------------------- /katex/KaTeX_Size2-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamdruppe/adrdox/9915e53ee6656b38167bfc4d12ef8710dd3654f2/katex/KaTeX_Size2-Regular.ttf -------------------------------------------------------------------------------- /katex/KaTeX_Size2-Regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamdruppe/adrdox/9915e53ee6656b38167bfc4d12ef8710dd3654f2/katex/KaTeX_Size2-Regular.woff -------------------------------------------------------------------------------- /katex/KaTeX_Size2-Regular.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamdruppe/adrdox/9915e53ee6656b38167bfc4d12ef8710dd3654f2/katex/KaTeX_Size2-Regular.woff2 -------------------------------------------------------------------------------- /katex/KaTeX_Size3-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamdruppe/adrdox/9915e53ee6656b38167bfc4d12ef8710dd3654f2/katex/KaTeX_Size3-Regular.ttf -------------------------------------------------------------------------------- /katex/KaTeX_Size3-Regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamdruppe/adrdox/9915e53ee6656b38167bfc4d12ef8710dd3654f2/katex/KaTeX_Size3-Regular.woff -------------------------------------------------------------------------------- /katex/KaTeX_Size3-Regular.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamdruppe/adrdox/9915e53ee6656b38167bfc4d12ef8710dd3654f2/katex/KaTeX_Size3-Regular.woff2 -------------------------------------------------------------------------------- /katex/KaTeX_Size4-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamdruppe/adrdox/9915e53ee6656b38167bfc4d12ef8710dd3654f2/katex/KaTeX_Size4-Regular.ttf -------------------------------------------------------------------------------- /katex/KaTeX_Size4-Regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamdruppe/adrdox/9915e53ee6656b38167bfc4d12ef8710dd3654f2/katex/KaTeX_Size4-Regular.woff -------------------------------------------------------------------------------- /katex/KaTeX_Size4-Regular.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamdruppe/adrdox/9915e53ee6656b38167bfc4d12ef8710dd3654f2/katex/KaTeX_Size4-Regular.woff2 -------------------------------------------------------------------------------- /katex/KaTeX_Typewriter-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamdruppe/adrdox/9915e53ee6656b38167bfc4d12ef8710dd3654f2/katex/KaTeX_Typewriter-Regular.ttf -------------------------------------------------------------------------------- /katex/KaTeX_Typewriter-Regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamdruppe/adrdox/9915e53ee6656b38167bfc4d12ef8710dd3654f2/katex/KaTeX_Typewriter-Regular.woff -------------------------------------------------------------------------------- /katex/KaTeX_Typewriter-Regular.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamdruppe/adrdox/9915e53ee6656b38167bfc4d12ef8710dd3654f2/katex/KaTeX_Typewriter-Regular.woff2 -------------------------------------------------------------------------------- /katex/katex.min.css: -------------------------------------------------------------------------------- 1 | @font-face{font-family:KaTeX_AMS;src:url(KaTeX_AMS-Regular.woff2) format("woff2"),url(KaTeX_AMS-Regular.woff) format("woff"),url(KaTeX_AMS-Regular.ttf) format("truetype");font-weight:400;font-style:normal}@font-face{font-family:KaTeX_Caligraphic;src:url(KaTeX_Caligraphic-Bold.woff2) format("woff2"),url(KaTeX_Caligraphic-Bold.woff) format("woff"),url(KaTeX_Caligraphic-Bold.ttf) format("truetype");font-weight:700;font-style:normal}@font-face{font-family:KaTeX_Caligraphic;src:url(KaTeX_Caligraphic-Regular.woff2) format("woff2"),url(KaTeX_Caligraphic-Regular.woff) format("woff"),url(KaTeX_Caligraphic-Regular.ttf) format("truetype");font-weight:400;font-style:normal}@font-face{font-family:KaTeX_Fraktur;src:url(KaTeX_Fraktur-Bold.woff2) format("woff2"),url(KaTeX_Fraktur-Bold.woff) format("woff"),url(KaTeX_Fraktur-Bold.ttf) format("truetype");font-weight:700;font-style:normal}@font-face{font-family:KaTeX_Fraktur;src:url(KaTeX_Fraktur-Regular.woff2) format("woff2"),url(KaTeX_Fraktur-Regular.woff) format("woff"),url(KaTeX_Fraktur-Regular.ttf) format("truetype");font-weight:400;font-style:normal}@font-face{font-family:KaTeX_Main;src:url(KaTeX_Main-Bold.woff2) format("woff2"),url(KaTeX_Main-Bold.woff) format("woff"),url(KaTeX_Main-Bold.ttf) format("truetype");font-weight:700;font-style:normal}@font-face{font-family:KaTeX_Main;src:url(KaTeX_Main-BoldItalic.woff2) format("woff2"),url(KaTeX_Main-BoldItalic.woff) format("woff"),url(KaTeX_Main-BoldItalic.ttf) format("truetype");font-weight:700;font-style:italic}@font-face{font-family:KaTeX_Main;src:url(KaTeX_Main-Italic.woff2) format("woff2"),url(KaTeX_Main-Italic.woff) format("woff"),url(KaTeX_Main-Italic.ttf) format("truetype");font-weight:400;font-style:italic}@font-face{font-family:KaTeX_Main;src:url(KaTeX_Main-Regular.woff2) format("woff2"),url(KaTeX_Main-Regular.woff) format("woff"),url(KaTeX_Main-Regular.ttf) format("truetype");font-weight:400;font-style:normal}@font-face{font-family:KaTeX_Math;src:url(KaTeX_Math-BoldItalic.woff2) format("woff2"),url(KaTeX_Math-BoldItalic.woff) format("woff"),url(KaTeX_Math-BoldItalic.ttf) format("truetype");font-weight:700;font-style:italic}@font-face{font-family:KaTeX_Math;src:url(KaTeX_Math-Italic.woff2) format("woff2"),url(KaTeX_Math-Italic.woff) format("woff"),url(KaTeX_Math-Italic.ttf) format("truetype");font-weight:400;font-style:italic}@font-face{font-family:"KaTeX_SansSerif";src:url(KaTeX_SansSerif-Bold.woff2) format("woff2"),url(KaTeX_SansSerif-Bold.woff) format("woff"),url(KaTeX_SansSerif-Bold.ttf) format("truetype");font-weight:700;font-style:normal}@font-face{font-family:"KaTeX_SansSerif";src:url(KaTeX_SansSerif-Italic.woff2) format("woff2"),url(KaTeX_SansSerif-Italic.woff) format("woff"),url(KaTeX_SansSerif-Italic.ttf) format("truetype");font-weight:400;font-style:italic}@font-face{font-family:"KaTeX_SansSerif";src:url(KaTeX_SansSerif-Regular.woff2) format("woff2"),url(KaTeX_SansSerif-Regular.woff) format("woff"),url(KaTeX_SansSerif-Regular.ttf) format("truetype");font-weight:400;font-style:normal}@font-face{font-family:KaTeX_Script;src:url(KaTeX_Script-Regular.woff2) format("woff2"),url(KaTeX_Script-Regular.woff) format("woff"),url(KaTeX_Script-Regular.ttf) format("truetype");font-weight:400;font-style:normal}@font-face{font-family:KaTeX_Size1;src:url(KaTeX_Size1-Regular.woff2) format("woff2"),url(KaTeX_Size1-Regular.woff) format("woff"),url(KaTeX_Size1-Regular.ttf) format("truetype");font-weight:400;font-style:normal}@font-face{font-family:KaTeX_Size2;src:url(KaTeX_Size2-Regular.woff2) format("woff2"),url(KaTeX_Size2-Regular.woff) format("woff"),url(KaTeX_Size2-Regular.ttf) format("truetype");font-weight:400;font-style:normal}@font-face{font-family:KaTeX_Size3;src:url(KaTeX_Size3-Regular.woff2) format("woff2"),url(KaTeX_Size3-Regular.woff) format("woff"),url(KaTeX_Size3-Regular.ttf) format("truetype");font-weight:400;font-style:normal}@font-face{font-family:KaTeX_Size4;src:url(KaTeX_Size4-Regular.woff2) format("woff2"),url(KaTeX_Size4-Regular.woff) format("woff"),url(KaTeX_Size4-Regular.ttf) format("truetype");font-weight:400;font-style:normal}@font-face{font-family:KaTeX_Typewriter;src:url(KaTeX_Typewriter-Regular.woff2) format("woff2"),url(KaTeX_Typewriter-Regular.woff) format("woff"),url(KaTeX_Typewriter-Regular.ttf) format("truetype");font-weight:400;font-style:normal}.katex{font:normal 1.21em KaTeX_Main,Times New Roman,serif;line-height:1.2;text-indent:0;text-rendering:auto;border-color:currentColor}.katex *{-ms-high-contrast-adjust:none!important}.katex .katex-version:after{content:"0.12.0"}.katex .katex-mathml{position:absolute;clip:rect(1px,1px,1px,1px);padding:0;border:0;height:1px;width:1px;overflow:hidden}.katex .katex-html>.newline{display:block}.katex .base{position:relative;white-space:nowrap;width:min-content}.katex .base,.katex .strut{display:inline-block}.katex .textbf{font-weight:700}.katex .textit{font-style:italic}.katex .textrm{font-family:KaTeX_Main}.katex .textsf{font-family:KaTeX_SansSerif}.katex .texttt{font-family:KaTeX_Typewriter}.katex .mathnormal{font-family:KaTeX_Math;font-style:italic}.katex .mathit{font-family:KaTeX_Main;font-style:italic}.katex .mathrm{font-style:normal}.katex .mathbf{font-family:KaTeX_Main;font-weight:700}.katex .boldsymbol{font-family:KaTeX_Math;font-weight:700;font-style:italic}.katex .amsrm,.katex .mathbb,.katex .textbb{font-family:KaTeX_AMS}.katex .mathcal{font-family:KaTeX_Caligraphic}.katex .mathfrak,.katex .textfrak{font-family:KaTeX_Fraktur}.katex .mathtt{font-family:KaTeX_Typewriter}.katex .mathscr,.katex .textscr{font-family:KaTeX_Script}.katex .mathsf,.katex .textsf{font-family:KaTeX_SansSerif}.katex .mathboldsf,.katex .textboldsf{font-family:KaTeX_SansSerif;font-weight:700}.katex .mathitsf,.katex .textitsf{font-family:KaTeX_SansSerif;font-style:italic}.katex .mainrm{font-family:KaTeX_Main;font-style:normal}.katex .vlist-t{display:inline-table;table-layout:fixed;border-collapse:collapse}.katex .vlist-r{display:table-row}.katex .vlist{display:table-cell;vertical-align:bottom;position:relative}.katex .vlist>span{display:block;height:0;position:relative}.katex .vlist>span>span{display:inline-block}.katex .vlist>span>.pstrut{overflow:hidden;width:0}.katex .vlist-t2{margin-right:-2px}.katex .vlist-s{display:table-cell;vertical-align:bottom;font-size:1px;width:2px;min-width:2px}.katex .vbox{-ms-flex-direction:column;flex-direction:column;align-items:baseline}.katex .hbox,.katex .vbox{display:-ms-inline-flexbox;display:inline-flex}.katex .hbox{-ms-flex-direction:row;flex-direction:row;width:100%}.katex .thinbox{display:inline-flex;flex-direction:row;width:0;max-width:0}.katex .msupsub{text-align:left}.katex .mfrac>span>span{text-align:center}.katex .mfrac .frac-line{display:inline-block;width:100%;border-bottom-style:solid}.katex .hdashline,.katex .hline,.katex .mfrac .frac-line,.katex .overline .overline-line,.katex .rule,.katex .underline .underline-line{min-height:1px}.katex .mspace{display:inline-block}.katex .clap,.katex .llap,.katex .rlap{width:0;position:relative}.katex .clap>.inner,.katex .llap>.inner,.katex .rlap>.inner{position:absolute}.katex .clap>.fix,.katex .llap>.fix,.katex .rlap>.fix{display:inline-block}.katex .llap>.inner{right:0}.katex .clap>.inner,.katex .rlap>.inner{left:0}.katex .clap>.inner>span{margin-left:-50%;margin-right:50%}.katex .rule{display:inline-block;border:0 solid;position:relative}.katex .hline,.katex .overline .overline-line,.katex .underline .underline-line{display:inline-block;width:100%;border-bottom-style:solid}.katex .hdashline{display:inline-block;width:100%;border-bottom-style:dashed}.katex .sqrt>.root{margin-left:.27777778em;margin-right:-.55555556em}.katex .fontsize-ensurer.reset-size1.size1,.katex .sizing.reset-size1.size1{font-size:1em}.katex .fontsize-ensurer.reset-size1.size2,.katex .sizing.reset-size1.size2{font-size:1.2em}.katex .fontsize-ensurer.reset-size1.size3,.katex .sizing.reset-size1.size3{font-size:1.4em}.katex .fontsize-ensurer.reset-size1.size4,.katex .sizing.reset-size1.size4{font-size:1.6em}.katex .fontsize-ensurer.reset-size1.size5,.katex .sizing.reset-size1.size5{font-size:1.8em}.katex .fontsize-ensurer.reset-size1.size6,.katex .sizing.reset-size1.size6{font-size:2em}.katex .fontsize-ensurer.reset-size1.size7,.katex .sizing.reset-size1.size7{font-size:2.4em}.katex .fontsize-ensurer.reset-size1.size8,.katex .sizing.reset-size1.size8{font-size:2.88em}.katex .fontsize-ensurer.reset-size1.size9,.katex .sizing.reset-size1.size9{font-size:3.456em}.katex .fontsize-ensurer.reset-size1.size10,.katex .sizing.reset-size1.size10{font-size:4.148em}.katex .fontsize-ensurer.reset-size1.size11,.katex .sizing.reset-size1.size11{font-size:4.976em}.katex .fontsize-ensurer.reset-size2.size1,.katex .sizing.reset-size2.size1{font-size:.83333333em}.katex .fontsize-ensurer.reset-size2.size2,.katex .sizing.reset-size2.size2{font-size:1em}.katex .fontsize-ensurer.reset-size2.size3,.katex .sizing.reset-size2.size3{font-size:1.16666667em}.katex .fontsize-ensurer.reset-size2.size4,.katex .sizing.reset-size2.size4{font-size:1.33333333em}.katex .fontsize-ensurer.reset-size2.size5,.katex .sizing.reset-size2.size5{font-size:1.5em}.katex .fontsize-ensurer.reset-size2.size6,.katex .sizing.reset-size2.size6{font-size:1.66666667em}.katex .fontsize-ensurer.reset-size2.size7,.katex .sizing.reset-size2.size7{font-size:2em}.katex .fontsize-ensurer.reset-size2.size8,.katex .sizing.reset-size2.size8{font-size:2.4em}.katex .fontsize-ensurer.reset-size2.size9,.katex .sizing.reset-size2.size9{font-size:2.88em}.katex .fontsize-ensurer.reset-size2.size10,.katex .sizing.reset-size2.size10{font-size:3.45666667em}.katex .fontsize-ensurer.reset-size2.size11,.katex .sizing.reset-size2.size11{font-size:4.14666667em}.katex .fontsize-ensurer.reset-size3.size1,.katex .sizing.reset-size3.size1{font-size:.71428571em}.katex .fontsize-ensurer.reset-size3.size2,.katex .sizing.reset-size3.size2{font-size:.85714286em}.katex .fontsize-ensurer.reset-size3.size3,.katex .sizing.reset-size3.size3{font-size:1em}.katex .fontsize-ensurer.reset-size3.size4,.katex .sizing.reset-size3.size4{font-size:1.14285714em}.katex .fontsize-ensurer.reset-size3.size5,.katex .sizing.reset-size3.size5{font-size:1.28571429em}.katex .fontsize-ensurer.reset-size3.size6,.katex .sizing.reset-size3.size6{font-size:1.42857143em}.katex .fontsize-ensurer.reset-size3.size7,.katex .sizing.reset-size3.size7{font-size:1.71428571em}.katex .fontsize-ensurer.reset-size3.size8,.katex .sizing.reset-size3.size8{font-size:2.05714286em}.katex .fontsize-ensurer.reset-size3.size9,.katex .sizing.reset-size3.size9{font-size:2.46857143em}.katex .fontsize-ensurer.reset-size3.size10,.katex .sizing.reset-size3.size10{font-size:2.96285714em}.katex .fontsize-ensurer.reset-size3.size11,.katex .sizing.reset-size3.size11{font-size:3.55428571em}.katex .fontsize-ensurer.reset-size4.size1,.katex .sizing.reset-size4.size1{font-size:.625em}.katex .fontsize-ensurer.reset-size4.size2,.katex .sizing.reset-size4.size2{font-size:.75em}.katex .fontsize-ensurer.reset-size4.size3,.katex .sizing.reset-size4.size3{font-size:.875em}.katex .fontsize-ensurer.reset-size4.size4,.katex .sizing.reset-size4.size4{font-size:1em}.katex .fontsize-ensurer.reset-size4.size5,.katex .sizing.reset-size4.size5{font-size:1.125em}.katex .fontsize-ensurer.reset-size4.size6,.katex .sizing.reset-size4.size6{font-size:1.25em}.katex .fontsize-ensurer.reset-size4.size7,.katex .sizing.reset-size4.size7{font-size:1.5em}.katex .fontsize-ensurer.reset-size4.size8,.katex .sizing.reset-size4.size8{font-size:1.8em}.katex .fontsize-ensurer.reset-size4.size9,.katex .sizing.reset-size4.size9{font-size:2.16em}.katex .fontsize-ensurer.reset-size4.size10,.katex .sizing.reset-size4.size10{font-size:2.5925em}.katex .fontsize-ensurer.reset-size4.size11,.katex .sizing.reset-size4.size11{font-size:3.11em}.katex .fontsize-ensurer.reset-size5.size1,.katex .sizing.reset-size5.size1{font-size:.55555556em}.katex .fontsize-ensurer.reset-size5.size2,.katex .sizing.reset-size5.size2{font-size:.66666667em}.katex .fontsize-ensurer.reset-size5.size3,.katex .sizing.reset-size5.size3{font-size:.77777778em}.katex .fontsize-ensurer.reset-size5.size4,.katex .sizing.reset-size5.size4{font-size:.88888889em}.katex .fontsize-ensurer.reset-size5.size5,.katex .sizing.reset-size5.size5{font-size:1em}.katex .fontsize-ensurer.reset-size5.size6,.katex .sizing.reset-size5.size6{font-size:1.11111111em}.katex .fontsize-ensurer.reset-size5.size7,.katex .sizing.reset-size5.size7{font-size:1.33333333em}.katex .fontsize-ensurer.reset-size5.size8,.katex .sizing.reset-size5.size8{font-size:1.6em}.katex .fontsize-ensurer.reset-size5.size9,.katex .sizing.reset-size5.size9{font-size:1.92em}.katex .fontsize-ensurer.reset-size5.size10,.katex .sizing.reset-size5.size10{font-size:2.30444444em}.katex .fontsize-ensurer.reset-size5.size11,.katex .sizing.reset-size5.size11{font-size:2.76444444em}.katex .fontsize-ensurer.reset-size6.size1,.katex .sizing.reset-size6.size1{font-size:.5em}.katex .fontsize-ensurer.reset-size6.size2,.katex .sizing.reset-size6.size2{font-size:.6em}.katex .fontsize-ensurer.reset-size6.size3,.katex .sizing.reset-size6.size3{font-size:.7em}.katex .fontsize-ensurer.reset-size6.size4,.katex .sizing.reset-size6.size4{font-size:.8em}.katex .fontsize-ensurer.reset-size6.size5,.katex .sizing.reset-size6.size5{font-size:.9em}.katex .fontsize-ensurer.reset-size6.size6,.katex .sizing.reset-size6.size6{font-size:1em}.katex .fontsize-ensurer.reset-size6.size7,.katex .sizing.reset-size6.size7{font-size:1.2em}.katex .fontsize-ensurer.reset-size6.size8,.katex .sizing.reset-size6.size8{font-size:1.44em}.katex .fontsize-ensurer.reset-size6.size9,.katex .sizing.reset-size6.size9{font-size:1.728em}.katex .fontsize-ensurer.reset-size6.size10,.katex .sizing.reset-size6.size10{font-size:2.074em}.katex .fontsize-ensurer.reset-size6.size11,.katex .sizing.reset-size6.size11{font-size:2.488em}.katex .fontsize-ensurer.reset-size7.size1,.katex .sizing.reset-size7.size1{font-size:.41666667em}.katex .fontsize-ensurer.reset-size7.size2,.katex .sizing.reset-size7.size2{font-size:.5em}.katex .fontsize-ensurer.reset-size7.size3,.katex .sizing.reset-size7.size3{font-size:.58333333em}.katex .fontsize-ensurer.reset-size7.size4,.katex .sizing.reset-size7.size4{font-size:.66666667em}.katex .fontsize-ensurer.reset-size7.size5,.katex .sizing.reset-size7.size5{font-size:.75em}.katex .fontsize-ensurer.reset-size7.size6,.katex .sizing.reset-size7.size6{font-size:.83333333em}.katex .fontsize-ensurer.reset-size7.size7,.katex .sizing.reset-size7.size7{font-size:1em}.katex .fontsize-ensurer.reset-size7.size8,.katex .sizing.reset-size7.size8{font-size:1.2em}.katex .fontsize-ensurer.reset-size7.size9,.katex .sizing.reset-size7.size9{font-size:1.44em}.katex .fontsize-ensurer.reset-size7.size10,.katex .sizing.reset-size7.size10{font-size:1.72833333em}.katex .fontsize-ensurer.reset-size7.size11,.katex .sizing.reset-size7.size11{font-size:2.07333333em}.katex .fontsize-ensurer.reset-size8.size1,.katex .sizing.reset-size8.size1{font-size:.34722222em}.katex .fontsize-ensurer.reset-size8.size2,.katex .sizing.reset-size8.size2{font-size:.41666667em}.katex .fontsize-ensurer.reset-size8.size3,.katex .sizing.reset-size8.size3{font-size:.48611111em}.katex .fontsize-ensurer.reset-size8.size4,.katex .sizing.reset-size8.size4{font-size:.55555556em}.katex .fontsize-ensurer.reset-size8.size5,.katex .sizing.reset-size8.size5{font-size:.625em}.katex .fontsize-ensurer.reset-size8.size6,.katex .sizing.reset-size8.size6{font-size:.69444444em}.katex .fontsize-ensurer.reset-size8.size7,.katex .sizing.reset-size8.size7{font-size:.83333333em}.katex .fontsize-ensurer.reset-size8.size8,.katex .sizing.reset-size8.size8{font-size:1em}.katex .fontsize-ensurer.reset-size8.size9,.katex .sizing.reset-size8.size9{font-size:1.2em}.katex .fontsize-ensurer.reset-size8.size10,.katex .sizing.reset-size8.size10{font-size:1.44027778em}.katex .fontsize-ensurer.reset-size8.size11,.katex .sizing.reset-size8.size11{font-size:1.72777778em}.katex .fontsize-ensurer.reset-size9.size1,.katex .sizing.reset-size9.size1{font-size:.28935185em}.katex .fontsize-ensurer.reset-size9.size2,.katex .sizing.reset-size9.size2{font-size:.34722222em}.katex .fontsize-ensurer.reset-size9.size3,.katex .sizing.reset-size9.size3{font-size:.40509259em}.katex .fontsize-ensurer.reset-size9.size4,.katex .sizing.reset-size9.size4{font-size:.46296296em}.katex .fontsize-ensurer.reset-size9.size5,.katex .sizing.reset-size9.size5{font-size:.52083333em}.katex .fontsize-ensurer.reset-size9.size6,.katex .sizing.reset-size9.size6{font-size:.5787037em}.katex .fontsize-ensurer.reset-size9.size7,.katex .sizing.reset-size9.size7{font-size:.69444444em}.katex .fontsize-ensurer.reset-size9.size8,.katex .sizing.reset-size9.size8{font-size:.83333333em}.katex .fontsize-ensurer.reset-size9.size9,.katex .sizing.reset-size9.size9{font-size:1em}.katex .fontsize-ensurer.reset-size9.size10,.katex .sizing.reset-size9.size10{font-size:1.20023148em}.katex .fontsize-ensurer.reset-size9.size11,.katex .sizing.reset-size9.size11{font-size:1.43981481em}.katex .fontsize-ensurer.reset-size10.size1,.katex .sizing.reset-size10.size1{font-size:.24108004em}.katex .fontsize-ensurer.reset-size10.size2,.katex .sizing.reset-size10.size2{font-size:.28929605em}.katex .fontsize-ensurer.reset-size10.size3,.katex .sizing.reset-size10.size3{font-size:.33751205em}.katex .fontsize-ensurer.reset-size10.size4,.katex .sizing.reset-size10.size4{font-size:.38572806em}.katex .fontsize-ensurer.reset-size10.size5,.katex .sizing.reset-size10.size5{font-size:.43394407em}.katex .fontsize-ensurer.reset-size10.size6,.katex .sizing.reset-size10.size6{font-size:.48216008em}.katex .fontsize-ensurer.reset-size10.size7,.katex .sizing.reset-size10.size7{font-size:.57859209em}.katex .fontsize-ensurer.reset-size10.size8,.katex .sizing.reset-size10.size8{font-size:.69431051em}.katex .fontsize-ensurer.reset-size10.size9,.katex .sizing.reset-size10.size9{font-size:.83317261em}.katex .fontsize-ensurer.reset-size10.size10,.katex .sizing.reset-size10.size10{font-size:1em}.katex .fontsize-ensurer.reset-size10.size11,.katex .sizing.reset-size10.size11{font-size:1.19961427em}.katex .fontsize-ensurer.reset-size11.size1,.katex .sizing.reset-size11.size1{font-size:.20096463em}.katex .fontsize-ensurer.reset-size11.size2,.katex .sizing.reset-size11.size2{font-size:.24115756em}.katex .fontsize-ensurer.reset-size11.size3,.katex .sizing.reset-size11.size3{font-size:.28135048em}.katex .fontsize-ensurer.reset-size11.size4,.katex .sizing.reset-size11.size4{font-size:.32154341em}.katex .fontsize-ensurer.reset-size11.size5,.katex .sizing.reset-size11.size5{font-size:.36173633em}.katex .fontsize-ensurer.reset-size11.size6,.katex .sizing.reset-size11.size6{font-size:.40192926em}.katex .fontsize-ensurer.reset-size11.size7,.katex .sizing.reset-size11.size7{font-size:.48231511em}.katex .fontsize-ensurer.reset-size11.size8,.katex .sizing.reset-size11.size8{font-size:.57877814em}.katex .fontsize-ensurer.reset-size11.size9,.katex .sizing.reset-size11.size9{font-size:.69453376em}.katex .fontsize-ensurer.reset-size11.size10,.katex .sizing.reset-size11.size10{font-size:.83360129em}.katex .fontsize-ensurer.reset-size11.size11,.katex .sizing.reset-size11.size11{font-size:1em}.katex .delimsizing.size1{font-family:KaTeX_Size1}.katex .delimsizing.size2{font-family:KaTeX_Size2}.katex .delimsizing.size3{font-family:KaTeX_Size3}.katex .delimsizing.size4{font-family:KaTeX_Size4}.katex .delimsizing.mult .delim-size1>span{font-family:KaTeX_Size1}.katex .delimsizing.mult .delim-size4>span{font-family:KaTeX_Size4}.katex .nulldelimiter{display:inline-block;width:.12em}.katex .delimcenter,.katex .op-symbol{position:relative}.katex .op-symbol.small-op{font-family:KaTeX_Size1}.katex .op-symbol.large-op{font-family:KaTeX_Size2}.katex .op-limits>.vlist-t{text-align:center}.katex .accent>.vlist-t{text-align:center}.katex .accent .accent-body{position:relative}.katex .accent .accent-body:not(.accent-full){width:0}.katex .overlay{display:block}.katex .mtable .vertical-separator{display:inline-block;min-width:1px}.katex .mtable .arraycolsep{display:inline-block}.katex .mtable .col-align-c>.vlist-t{text-align:center}.katex .mtable .col-align-l>.vlist-t{text-align:left}.katex .mtable .col-align-r>.vlist-t{text-align:right}.katex .svg-align{text-align:left}.katex svg{display:block;position:absolute;width:100%;height:inherit;fill:currentColor;stroke:currentColor;fill-rule:nonzero;fill-opacity:1;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1}.katex svg path{stroke:none}.katex img{border-style:none;min-width:0;min-height:0;max-width:none;max-height:none}.katex .stretchy{width:100%;display:block;position:relative;overflow:hidden}.katex .stretchy:after,.katex .stretchy:before{content:""}.katex .hide-tail{width:100%;position:relative;overflow:hidden}.katex .halfarrow-left{position:absolute;left:0;width:50.2%;overflow:hidden}.katex .halfarrow-right{position:absolute;right:0;width:50.2%;overflow:hidden}.katex .brace-left{position:absolute;left:0;width:25.1%;overflow:hidden}.katex .brace-center{position:absolute;left:25%;width:50%;overflow:hidden}.katex .brace-right{position:absolute;right:0;width:25.1%;overflow:hidden}.katex .x-arrow-pad{padding:0 .5em}.katex .mover,.katex .munder,.katex .x-arrow{text-align:center}.katex .boxpad{padding:0 .3em}.katex .fbox,.katex .fcolorbox{box-sizing:border-box;border:.04em solid}.katex .cancel-pad{padding:0 .2em}.katex .cancel-lap{margin-left:-.2em;margin-right:-.2em}.katex .sout{border-bottom-style:solid;border-bottom-width:.08em}.katex-display{display:block;margin:1em 0;text-align:center}.katex-display>.katex{display:block;text-align:center;white-space:nowrap}.katex-display>.katex>.katex-html{display:block;position:relative}.katex-display>.katex>.katex-html>.tag{position:absolute;right:0}.katex-display.leqno>.katex>.katex-html>.tag{left:0;right:auto}.katex-display.fleqn>.katex{text-align:left;padding-left:2em} 2 | -------------------------------------------------------------------------------- /latex.d: -------------------------------------------------------------------------------- 1 | module adrdox.latex; 2 | 3 | import std.process; 4 | import std.file; 5 | 6 | import arsd.dom; 7 | 8 | string makeDataUrl(string mimeType, in void[] data) { 9 | import std.base64; 10 | auto data64 = Base64.encode(cast(const(ubyte[])) data); 11 | return "data:" ~ mimeType ~ ";base64," ~ cast(string)(data64); 12 | } 13 | 14 | // requires latex and dvipng to be installed on your system already, it just 15 | // calls out to them in the shell 16 | Element mathToImgHtml(string mathCode) { 17 | 18 | string dir = tempDir; 19 | 20 | // FIXME: this should prolly be unique or somethign 21 | string filebase = "./adrdox"; 22 | 23 | std.file.write(dir ~ "/" ~ filebase ~ ".latex", 24 | `\documentclass{article} 25 | \usepackage{amsmath} 26 | \usepackage{amsfonts} 27 | \usepackage{amssymb} 28 | \pagestyle{empty} 29 | \begin{document} 30 | $ `~mathCode~` $ 31 | \end{document}` 32 | ); 33 | 34 | auto tpl = executeShell( 35 | "latex -interaction=nonstopmode " ~ filebase ~ ".latex" 36 | ~ " && " ~ 37 | "dvipng -T tight -D 200 -o "~filebase~".png -bg Transparent "~filebase~".dvi -z 9", 38 | null, Config.none, size_t.max, dir 39 | ); 40 | 41 | if(tpl.status != 0) 42 | return null; 43 | 44 | 45 | auto prefix = dir ~ "/" ~ filebase; 46 | if(exists(prefix ~ ".aux")) 47 | remove(prefix ~ ".aux"); 48 | if(exists(prefix ~ ".dvi")) 49 | remove(prefix ~ ".dvi"); 50 | if(exists(prefix ~ ".latex")) 51 | remove(prefix ~ ".latex"); 52 | if(exists(prefix ~ ".log")) 53 | remove(prefix ~ ".log"); 54 | 55 | if(exists(prefix ~ ".png")) { 56 | auto file = read(prefix ~ ".png"); 57 | remove(prefix ~ ".png"); 58 | 59 | auto img = Element.make("img"); 60 | img.alt = mathCode; 61 | img.src = makeDataUrl("image/png", file); 62 | img.className = "rendered-math"; 63 | return img; 64 | } 65 | 66 | return null; 67 | 68 | } 69 | -------------------------------------------------------------------------------- /locate.d: -------------------------------------------------------------------------------- 1 | // FIXME: add +proj and -proj to adjust project results 2 | 3 | // dmdi -g -debug -version=vps locate stemmer.d -oflocate_vps -version=scgi 4 | 5 | // my local config assumes this will be on port 9653 6 | 7 | module adrdox.locate; 8 | 9 | import arsd.postgres; 10 | 11 | import ps = PorterStemmer; 12 | import arsd.cgi; 13 | import arsd.dom; 14 | import std.stdio; 15 | import std.file; 16 | import std.conv : to; 17 | import std.algorithm : sort; 18 | import std.string : toLower, replace, split; 19 | 20 | PostgreSql db_; 21 | 22 | PostgreSql db() { 23 | if(db_ is null) { 24 | db_ = new PostgreSql("dbname=adrdox"); 25 | } 26 | return db_; 27 | } 28 | 29 | string lastDottedPiece(string term) { 30 | auto idx = term.lastIndexOf("."); 31 | auto piece = term[idx + 1 .. $]; 32 | if(piece == "this") { 33 | if(idx == -1) 34 | throw new Exception("dont search for this.this"); 35 | else 36 | return lastDottedPiece(term[0 .. idx]); 37 | } else { 38 | return piece; 39 | } 40 | } 41 | 42 | TermElement[] resultsByTerm(string term) { 43 | if(term == "this") 44 | return null; 45 | TermElement[] ret; 46 | foreach(row; db.query("SELECT d_symbols.id, score FROM hand_written_tags INNER JOIN d_symbols ON d_symbol_fully_qualified_name = fully_qualified_name WHERE tag ILIKE ? ORDER BY score DESC", term)) 47 | ret ~= TermElement(to!int(row[0]), to!int(row[1])); 48 | 49 | foreach(row; db.query(" 50 | SELECT 51 | d_symbols.id, fully_qualified_name 52 | FROM 53 | d_symbols 54 | INNER JOIN 55 | package_version ON package_version_id = package_version.id 56 | WHERE 57 | is_latest = true 58 | AND 59 | ( 60 | ( 61 | fully_qualified_name = ? 62 | OR 63 | ( 64 | lower(name) = lower(?) 65 | AND 66 | fully_qualified_name ilike ? 67 | ) 68 | ) 69 | ) 70 | ", term, term.lastDottedPiece, "%" ~ term)) { 71 | ret ~= TermElement(to!int(row[0]), row[1] == term ? 50 : 25); 72 | } 73 | 74 | /+ 75 | -- OR 76 | -- substring(fully_qualified_name, length(module_name) + 2) = ? 77 | +/ 78 | 79 | version(none) 80 | foreach(row; db.query(" 81 | SELECT 82 | d_symbols_id, score 83 | FROM 84 | auto_generated_tags 85 | INNER JOIN 86 | package_version ON package_version_id = package_version.id 87 | WHERE 88 | tag ILIKE ? 89 | AND 90 | is_latest = true 91 | ORDER BY 92 | score + (case (dub_package_id = 6 or dub_package_id = 9) when true then 5 else 0 end) DESC 93 | ", term)) 94 | ret ~= TermElement(to!int(row[0]), to!int(row[1])); 95 | return ret; 96 | } 97 | 98 | DeclElement getDecl(int i) { 99 | foreach(row; db.query(" 100 | SELECT 101 | d_symbols.*, 102 | dub_package.url_name AS package_subdomain 103 | FROM 104 | d_symbols 105 | INNER JOIN 106 | package_version ON package_version.id = d_symbols.package_version_id 107 | INNER JOIN 108 | dub_package ON dub_package.id = package_version.dub_package_id 109 | WHERE 110 | d_symbols.id = ? 111 | AND 112 | is_latest = true 113 | ", i)) { 114 | return DeclElement(row["fully_qualified_name"], row["summary"], row["url_name"], row["id"].to!int, "", 0, row["package_subdomain"]); 115 | } 116 | return DeclElement.init; 117 | } 118 | 119 | static struct TermElement { 120 | int declId; 121 | int score; 122 | } 123 | 124 | static struct DeclElement { 125 | string name; 126 | string description; // actually HTML 127 | string link; 128 | int id; 129 | string type; 130 | int parent; 131 | string packageName; 132 | } 133 | 134 | static struct Magic { 135 | int declId; 136 | int score; 137 | DeclElement decl; 138 | } 139 | 140 | int getProjectAdjustment(DeclElement details, string preferredProject) { 141 | int projectAdjustment; 142 | if(preferredProject.length) { 143 | if(preferredProject == details.packageName) 144 | projectAdjustment = 150; 145 | } 146 | if(details.packageName == "phobos" || details.packageName == "druntime") 147 | projectAdjustment += 50; 148 | if(details.packageName == "arsd-official") 149 | projectAdjustment += 30; 150 | 151 | return projectAdjustment; 152 | } 153 | 154 | Magic[] getPossibilities(string search, string preferredProject) { 155 | int[int] declScores; 156 | 157 | int[int] declHits; 158 | 159 | // ps.PorterStemmer s; 160 | 161 | auto terms = search.split(" "); 162 | // filter empty terms 163 | for(int i = 0; i < terms.length; i++) { 164 | if(terms[i].strip.length == 0) { 165 | terms[i] = terms[$-1]; 166 | terms = terms[0 .. $-1]; 167 | i--; 168 | } 169 | } 170 | 171 | void addHit(TermElement item, size_t idx) { 172 | if(idx == 0) { 173 | declScores[item.declId] += item.score; 174 | return; 175 | } 176 | if(item.declId in declScores) { 177 | //declScores[item.declId] += 25; // hit both terms 178 | declScores[item.declId] += item.score; 179 | } else { 180 | // only hit one term... 181 | declScores[item.declId] += item.score / 2; 182 | } 183 | } 184 | 185 | // On each term, we want to check for exact match and fuzzy match / natural language match. 186 | // FIXME: if something matches both it should be really strong. see time_t vs "time_t std.datetime" 187 | foreach(idx, term; terms) { 188 | assert(term.length > 0); 189 | 190 | foreach(item; resultsByTerm(term)) { 191 | addHit(item, idx); 192 | declHits[item.declId] |= 1 << idx; 193 | } 194 | /+ 195 | auto st = s.stem(term.toLower).idup; 196 | if(st != l) 197 | foreach(item; resultsByTerm(st)) { 198 | addHit(item, idx); 199 | declHits[item.declId] |= 1 << idx; 200 | } 201 | +/ 202 | } 203 | 204 | Magic[] magic; 205 | 206 | string[string] fqns; 207 | 208 | foreach(decl, score; declScores) { 209 | auto hits = declHits[decl]; 210 | foreach(idx, term; terms) { 211 | if(!(hits & (1 << idx))) 212 | score /= 2; 213 | } 214 | auto details = getDecl(decl); 215 | /+ 216 | if(details.name in fqns) 217 | continue; 218 | fqns[details.name] = details.name; 219 | +/ 220 | int projectAdjustment = getProjectAdjustment(details, preferredProject); 221 | magic ~= Magic(decl, score + projectAdjustment, details); 222 | } 223 | 224 | if(magic.length == 0) { 225 | foreach(term; terms) { 226 | if(term.length == 0) continue; 227 | term = term.toLower(); 228 | foreach(row; db.query("select * from d_symbols inner join package_version on package_version.id = package_version_id where is_latest = true and lower(name) = ? LIMIT 50", term)) { 229 | string name = row[1]; 230 | int id = row[0].to!int; 231 | /+ 232 | import std.algorithm; 233 | name = name.toLower; 234 | auto dist = cast(int) levenshteinDistance(name, term); 235 | if(dist <= 2) { 236 | +/ 237 | int dist = 0; 238 | { 239 | auto details = getDecl(id); 240 | int projectAdjustment = getProjectAdjustment(details, preferredProject); 241 | magic ~= Magic(id, projectAdjustment + (3 - dist), details); 242 | } 243 | } 244 | } 245 | } 246 | 247 | // boosts based on topography 248 | foreach(ref item; magic) { 249 | auto decl = item.decl; 250 | if(decl.type == "module") { 251 | // if it is a module, give it moar points 252 | item.score += 8; 253 | continue; 254 | } 255 | if(getDecl(decl.id).type == "module") { 256 | item.score += 5; 257 | } 258 | } 259 | 260 | return magic; 261 | } 262 | 263 | import std.uri; 264 | 265 | void searcher(Cgi cgi) { 266 | 267 | auto search = cgi.request("q", cgi.request("searchTerm", cgi.queryString)); 268 | 269 | version(vps) { 270 | string path = cgi.requestUri; 271 | 272 | auto q = path.indexOf("?"); 273 | if(q != -1) { 274 | path = path[0 .. q]; 275 | } 276 | 277 | if(path.length && path[0] == '/') 278 | path = path[1 .. $]; 279 | 280 | if(path.length == 0 && search.length == 0) { 281 | import std.file; 282 | 283 | cgi.write(std.file.read("/dpldocs-build/search-home.html"), true); 284 | return; 285 | } 286 | 287 | 288 | if(path == "script.js") { 289 | import std.file; 290 | cgi.setResponseContentType("text/javascript"); 291 | cgi.write(std.file.read("/dpldocs-build/script.js"), true); 292 | return; 293 | 294 | } 295 | 296 | if(path == "style.css") { 297 | import std.file; 298 | cgi.setResponseContentType("text/css"); 299 | cgi.write(std.file.read("/dpldocs-build/style.css"), true); 300 | return; 301 | } 302 | } else { 303 | string path = cgi.requestUri; 304 | 305 | auto q = path.indexOf("?"); 306 | if(q != -1) { 307 | path = path[0 .. q]; 308 | } 309 | 310 | if(path.length && path[0] == '/') 311 | path = path[1 .. $]; 312 | 313 | 314 | } 315 | 316 | alias searchTerm = search; 317 | 318 | if(search.length == 0 && path.length) 319 | search = path; 320 | 321 | if(search.length == 0) { 322 | cgi.setResponseLocation("/"); 323 | return; 324 | } 325 | auto parts = search.split(" "); 326 | switch(parts[0].toLower()) { 327 | case "auto-ref-return-function-prototype": 328 | cgi.setResponseLocation("http://dlang.org/spec/function.html#auto-ref-functions"); 329 | return; 330 | case "auto-function-return-prototype": 331 | cgi.setResponseLocation("http://dlang.org/spec/function.html#auto-functions"); 332 | return; 333 | case "ref-function-return-prototype": 334 | cgi.setResponseLocation("http://dlang.org/spec/function.html#ref-functions"); 335 | return; 336 | case "bugzilla": 337 | auto url = "http://d.puremagic.com/issues/"; 338 | if(parts.length > 1) 339 | url ~= "show_bug.cgi?id=" ~ parts[1]; 340 | cgi.setResponseLocation(url); 341 | return; 342 | case "dip": 343 | auto url = "http://wiki.dlang.org/DIPs"; 344 | if(parts.length > 1) 345 | url = "http://wiki.dlang.org/DIP" ~ parts[1]; 346 | cgi.setResponseLocation(url); 347 | return; 348 | case "wiki": 349 | auto url = "http://wiki.dlang.org/"; 350 | if(parts.length > 1) 351 | url ~= "search="~std.uri.encodeComponent(join(parts[1..$], " 352 | "))~"&go=Go&title=Special%3ASearch"; 353 | cgi.setResponseLocation(url); 354 | return; 355 | case "faqs": 356 | case "faq": 357 | cgi.setResponseLocation("http://wiki.dlang.org/FAQs"); 358 | return; 359 | case "template-alias-parameter": 360 | cgi.setResponseLocation("https://dlang.org/spec/template.html#aliasparameters"); 361 | return; 362 | case "is-expression": 363 | cgi.setResponseLocation("https://dlang.org/spec/expression.html#IsExpression"); 364 | return; 365 | case "typeof-expression": 366 | cgi.setResponseLocation("https://dlang.org/spec/declaration.html#Typeof"); 367 | return; 368 | case "oldwiki": 369 | auto url = "http://prowiki.org/wiki4d/wiki.cgi"; 370 | if(parts.length > 1) 371 | url ~= "?formpage=Search&id=Search&search=" ~ std.uri. 372 | encodeComponent(join(parts[1..$], " ")); 373 | cgi.setResponseLocation(url); 374 | return; 375 | default: 376 | // just continue 377 | version(vps) { } else { 378 | /+ 379 | if(std.file.exists("/var/www/dpldocs.info/experimental-docs/" ~ searchTerm ~ ".1.html")) { 380 | cgi.setResponseLocation("/experimental-docs/" ~ searchTerm ~ ".1.html"); 381 | return; 382 | } 383 | if(std.file.exists("/var/www/dpldocs.info/experimental-docs/" ~ searchTerm ~ ".html")) { 384 | cgi.setResponseLocation("/experimental-docs/" ~ searchTerm ~ ".html"); 385 | return; 386 | } 387 | +/ 388 | // redirect to vps 389 | if("local" !in cgi.get) 390 | cgi.setResponseLocation("//search.dpldocs.info/?q=" ~ std.uri.encodeComponent(searchTerm)); 391 | } 392 | } 393 | 394 | 395 | Magic[] magic = getPossibilities(search, cgi.request("project")); 396 | 397 | sort!((a, b) => a.score > b.score)(magic); 398 | 399 | // adjustments based on previously showing results 400 | { 401 | bool[int] alreadyPresent; 402 | foreach(ref item; magic) { 403 | auto decl = item.decl; 404 | if(decl.parent in alreadyPresent) 405 | item.score -= 8; 406 | alreadyPresent[decl.id] = true; 407 | } 408 | } 409 | 410 | auto document = new Document(); 411 | version(vps) { 412 | import std.file; 413 | document.parseUtf8(readText("/dpldocs-build/skeleton.html"), true, true); 414 | document.title = "Dub Documentation Search"; 415 | } else 416 | document.parseUtf8(import("skeleton.html"), true, true); 417 | document.title = "Search Results"; 418 | 419 | auto form = document.requireElementById!Form("search"); 420 | form.setValue("searchTerm", search); 421 | 422 | version(vps) { 423 | // intentionally blank 424 | } else { 425 | auto l = document.requireSelector("link"); 426 | l.href = "/experimental-docs/" ~ l.href; 427 | l = document.requireSelector("script[src]"); 428 | l.src = "/experimental-docs/" ~ l.src; 429 | } 430 | 431 | auto pc = document.requireSelector("#page-content"); 432 | pc.addChild("h1", "Search Results"); 433 | auto ml = pc.addChild("dl"); 434 | ml.className = "member-list"; 435 | 436 | string getFqn(DeclElement i) { 437 | string n; 438 | while(true) { 439 | if(n) n = "." ~ n; 440 | n = i.name ~ n; 441 | if(i.type == "module") 442 | break; 443 | if(i.parent == 0) 444 | break; 445 | i = getDecl(i.parent); 446 | if(i.id == 0) 447 | break; 448 | } 449 | return n; 450 | } 451 | 452 | bool[string] alreadyPresent; 453 | int count = 0; 454 | foreach(idx, item; magic) { 455 | auto decl = item.decl; 456 | if(decl.id == 0) continue; // should never happen 457 | version(vps) 458 | auto link = "//"~decl.packageName~".dpldocs.info/" ~ decl.link; 459 | else 460 | auto link = "//dpldocs.info/experimental-docs/" ~ decl.link; 461 | if(decl.link.length && decl.link[0] == '/') 462 | link = decl.link; 463 | auto fqn = getFqn(decl); 464 | if(fqn in alreadyPresent) 465 | continue; 466 | alreadyPresent[fqn] = true; 467 | auto dt = ml.addChild("dt"); 468 | dt.addClass("search-result"); 469 | dt.addChild("span", decl.packageName).addClass("project-name"); 470 | dt.addChild("br"); 471 | dt.addChild("a", fqn.replace(".", ".\u200B"), link); 472 | dt.dataset.score = to!string(item.score); 473 | auto html = decl.description; 474 | //auto d = new Document(html); 475 | //writeln(d.root.innerText.replace("\n", " ")); 476 | //writeln(); 477 | 478 | // FIXME fix relative links from here 479 | ml.addChild("dd", Html(html)); 480 | foreach(a; ml.querySelectorAll("a[href]")) { 481 | auto uri = Uri(a.href).basedOn(Uri("//" ~ decl.packageName~".dpldocs.info/")); 482 | a.href = uri.toString; 483 | } 484 | count++; 485 | 486 | if(count >= 20) 487 | break; 488 | } 489 | 490 | cgi.write(document.toString, true); 491 | } 492 | 493 | mixin GenericMain!(searcher); 494 | 495 | -------------------------------------------------------------------------------- /postgres.d: -------------------------------------------------------------------------------- 1 | /// Uses libpq implement the [arsd.database.Database] interface. 2 | module arsd.postgres; 3 | pragma(lib, "pq"); 4 | 5 | public import arsd.database; 6 | 7 | import std.string; 8 | import std.exception; 9 | 10 | // remember to CREATE DATABASE name WITH ENCODING 'utf8' 11 | // 12 | // http://www.postgresql.org/docs/8.0/static/libpq-exec.html 13 | // ExecParams, PQPrepare, PQExecPrepared 14 | // 15 | // SQL: `DEALLOCATE name` is how to dealloc a prepared statement. 16 | 17 | /++ 18 | The PostgreSql implementation of the [Database] interface. 19 | 20 | You should construct this class, but then use it through the 21 | interface functions. 22 | 23 | --- 24 | auto db = new PostgreSql("dbname=name"); 25 | foreach(row; db.query("SELECT id, data FROM table_name")) 26 | writeln(row[0], " = ", row[1]); 27 | --- 28 | +/ 29 | class PostgreSql : Database { 30 | /// dbname = name is probably the most common connection string 31 | this(string connectionString) { 32 | this.connectionString = connectionString; 33 | conn = PQconnectdb(toStringz(connectionString)); 34 | if(conn is null) 35 | throw new DatabaseException("Unable to allocate PG connection object"); 36 | if(PQstatus(conn) != CONNECTION_OK) 37 | throw new DatabaseException(error()); 38 | query("SET NAMES 'utf8'"); // D does everything with utf8 39 | } 40 | 41 | string connectionString; 42 | 43 | ~this() { 44 | PQfinish(conn); 45 | } 46 | 47 | string sysTimeToValue(SysTime s) { 48 | return "'" ~ escape(s.toISOExtString()) ~ "'::timestamptz"; 49 | } 50 | 51 | /** 52 | Prepared statement support 53 | 54 | This will be added to the Database interface eventually in some form, 55 | but first I need to implement it for all my providers. 56 | 57 | The common function of those 4 will be what I put in the interface. 58 | */ 59 | 60 | ResultSet executePreparedStatement(T...)(string name, T args) { 61 | char*[args.length] argsStrings; 62 | 63 | foreach(idx, arg; args) { 64 | // FIXME: optimize to remove allocations here 65 | static if(!is(typeof(arg) == typeof(null))) 66 | argsStrings[idx] = toStringz(to!string(arg)); 67 | // else make it null 68 | } 69 | 70 | auto res = PQexecPrepared(conn, toStringz(name), argsStrings.length, argStrings.ptr, 0, null, 0); 71 | 72 | int ress = PQresultStatus(res); 73 | if(ress != PGRES_TUPLES_OK 74 | && ress != PGRES_COMMAND_OK) 75 | throw new DatabaseException(error()); 76 | 77 | return new PostgresResult(res); 78 | 79 | } 80 | 81 | /// 82 | override void startTransaction() { 83 | query("START TRANSACTION"); 84 | } 85 | 86 | ResultSet queryImpl(string sql, Variant[] args...) { 87 | sql = escapedVariants(this, sql, args); 88 | 89 | bool first_retry = true; 90 | 91 | retry: 92 | 93 | auto res = PQexec(conn, toStringz(sql)); 94 | int ress = PQresultStatus(res); 95 | // https://www.postgresql.org/docs/current/libpq-exec.html 96 | // FIXME: PQresultErrorField can get a lot more info in a more structured way 97 | if(ress != PGRES_TUPLES_OK 98 | && ress != PGRES_COMMAND_OK) 99 | { 100 | if(first_retry && error() == "no connection to the server\n") { 101 | first_retry = false; 102 | // try to reconnect... 103 | PQfinish(conn); 104 | conn = PQconnectdb(toStringz(connectionString)); 105 | if(conn is null) 106 | throw new DatabaseException("Unable to allocate PG connection object"); 107 | if(PQstatus(conn) != CONNECTION_OK) 108 | throw new DatabaseException(error()); 109 | goto retry; 110 | } 111 | throw new DatabaseException(error()); 112 | } 113 | 114 | return new PostgresResult(res); 115 | } 116 | 117 | string escape(string sqlData) { 118 | char* buffer = (new char[sqlData.length * 2 + 1]).ptr; 119 | ulong size = PQescapeString (buffer, sqlData.ptr, sqlData.length); 120 | 121 | string ret = assumeUnique(buffer[0.. cast(size_t) size]); 122 | 123 | return ret; 124 | } 125 | 126 | 127 | /// 128 | string error() { 129 | return copyCString(PQerrorMessage(conn)); 130 | } 131 | 132 | private: 133 | PGconn* conn; 134 | } 135 | 136 | /// 137 | class PostgresResult : ResultSet { 138 | // name for associative array to result index 139 | int getFieldIndex(string field) { 140 | if(mapping is null) 141 | makeFieldMapping(); 142 | field = field.toLower; 143 | if(field in mapping) 144 | return mapping[field]; 145 | else throw new Exception("no mapping " ~ field); 146 | } 147 | 148 | 149 | string[] fieldNames() { 150 | if(mapping is null) 151 | makeFieldMapping(); 152 | return columnNames; 153 | } 154 | 155 | // this is a range that can offer other ranges to access it 156 | bool empty() { 157 | return position == numRows; 158 | } 159 | 160 | Row front() { 161 | return row; 162 | } 163 | 164 | void popFront() { 165 | position++; 166 | if(position < numRows) 167 | fetchNext(); 168 | } 169 | 170 | override size_t length() { 171 | return numRows; 172 | } 173 | 174 | this(PGresult* res) { 175 | this.res = res; 176 | numFields = PQnfields(res); 177 | numRows = PQntuples(res); 178 | 179 | if(numRows) 180 | fetchNext(); 181 | } 182 | 183 | ~this() { 184 | PQclear(res); 185 | } 186 | 187 | private: 188 | PGresult* res; 189 | int[string] mapping; 190 | string[] columnNames; 191 | int numFields; 192 | 193 | int position; 194 | 195 | int numRows; 196 | 197 | Row row; 198 | 199 | void fetchNext() { 200 | Row r; 201 | r.resultSet = this; 202 | string[] row; 203 | 204 | for(int i = 0; i < numFields; i++) { 205 | string a; 206 | 207 | if(PQgetisnull(res, position, i)) 208 | a = null; 209 | else { 210 | a = copyCString(PQgetvalue(res, position, i), PQgetlength(res, position, i)); 211 | 212 | } 213 | row ~= a; 214 | } 215 | 216 | r.row = row; 217 | this.row = r; 218 | } 219 | 220 | void makeFieldMapping() { 221 | for(int i = 0; i < numFields; i++) { 222 | string a = copyCString(PQfname(res, i)); 223 | 224 | columnNames ~= a; 225 | mapping[a] = i; 226 | } 227 | 228 | } 229 | } 230 | 231 | string copyCString(const char* c, int actualLength = -1) { 232 | const(char)* a = c; 233 | if(a is null) 234 | return null; 235 | 236 | string ret; 237 | if(actualLength == -1) 238 | while(*a) { 239 | ret ~= *a; 240 | a++; 241 | } 242 | else { 243 | ret = a[0..actualLength].idup; 244 | } 245 | 246 | return ret; 247 | } 248 | 249 | extern(C) { 250 | struct PGconn {}; 251 | struct PGresult {}; 252 | 253 | void PQfinish(PGconn*); 254 | PGconn* PQconnectdb(const char*); 255 | 256 | int PQstatus(PGconn*); // FIXME check return value 257 | 258 | const (char*) PQerrorMessage(PGconn*); 259 | 260 | PGresult* PQexec(PGconn*, const char*); 261 | void PQclear(PGresult*); 262 | 263 | PGresult* PQprepare(PGconn*, const char* stmtName, const char* query, int nParams, const void* paramTypes); 264 | 265 | PGresult* PQexecPrepared(PGconn*, const char* stmtName, int nParams, const char** paramValues, const int* paramLengths, const int* paramFormats, int resultFormat); 266 | 267 | int PQresultStatus(PGresult*); // FIXME check return value 268 | 269 | int PQnfields(PGresult*); // number of fields in a result 270 | const(char*) PQfname(PGresult*, int); // name of field 271 | 272 | int PQntuples(PGresult*); // number of rows in result 273 | const(char*) PQgetvalue(PGresult*, int row, int column); 274 | 275 | size_t PQescapeString (char *to, const char *from, size_t length); 276 | 277 | enum int CONNECTION_OK = 0; 278 | enum int PGRES_COMMAND_OK = 1; 279 | enum int PGRES_TUPLES_OK = 2; 280 | 281 | int PQgetlength(const PGresult *res, 282 | int row_number, 283 | int column_number); 284 | int PQgetisnull(const PGresult *res, 285 | int row_number, 286 | int column_number); 287 | 288 | 289 | } 290 | 291 | /* 292 | import std.stdio; 293 | void main() { 294 | auto db = new PostgreSql("dbname = test"); 295 | 296 | db.query("INSERT INTO users (id, name) values (?, ?)", 30, "hello mang"); 297 | 298 | foreach(line; db.query("SELECT * FROM users")) { 299 | writeln(line[0], line["name"]); 300 | } 301 | } 302 | */ 303 | -------------------------------------------------------------------------------- /script.js: -------------------------------------------------------------------------------- 1 | window.addEventListener("load", function() { 2 | 3 | var lineWrappers = document.querySelectorAll(".with-line-wrappers"); 4 | for(var i = 0; i < lineWrappers.length; i++) { 5 | var l = lineWrappers[i]; 6 | 7 | var codeblock = document.createElement("div"); 8 | codeblock.className = "codeblock"; 9 | l.parentNode.insertBefore(codeblock, l); 10 | 11 | var header = document.createElement("header"); 12 | codeblock.appendChild(header); 13 | codeblock.appendChild(l); 14 | 15 | var btn = document.createElement("button"); 16 | btn.setAttribute("type", "button"); 17 | var canCopyToClipboard = document.queryCommandSupported("copy"); 18 | btn.addEventListener("click", (function(l) { return function() { 19 | document.body.classList.add("hide-line-numbers"); 20 | window.getSelection().selectAllChildren(l); 21 | if(canCopyToClipboard) 22 | if(!document.execCommand("copy")) { 23 | alert("copy failed, try ctrl+c manually"); 24 | } 25 | };})(l)); 26 | btn.textContent = canCopyToClipboard ? "Copy to Clipboard" : "Select All"; 27 | header.appendChild(btn); 28 | 29 | var btn = document.createElement("button"); 30 | btn.setAttribute("type", "button"); 31 | btn.addEventListener("click", function() { 32 | document.body.classList.toggle("hide-line-numbers"); 33 | }); 34 | btn.textContent = "Toggle Line Numbers"; 35 | header.appendChild(btn); 36 | } 37 | 38 | /* // still sucks in firefox! 39 | document.addEventListener("copy", function(event) { 40 | document.body.classList.add("hide-line-numbers"); 41 | }); 42 | */ 43 | 44 | document.body.addEventListener("mouseover", function(event) { 45 | if(event.target.hasAttribute("data-ident")) { 46 | var all = document.querySelectorAll("[data-ident=\""+event.target.getAttribute("data-ident")+"\"]"); 47 | for(var i = 0; i < all.length; i++) 48 | all[i].className += " active"; 49 | } 50 | }); 51 | document.body.addEventListener("mouseout", function(event) { 52 | if(event.target.hasAttribute("data-ident")) { 53 | var all = document.querySelectorAll("[data-ident=\""+event.target.getAttribute("data-ident")+"\"]"); 54 | for(var i = 0; i < all.length; i++) 55 | all[i].className = all[i].className.replace(" active", ""); 56 | } 57 | }); 58 | /* 59 | document.body.addEventListener("dblclick", function(event) { 60 | if(event.target.hasAttribute("data-ident")) { 61 | location.href = "/" + event.target.getAttribute("data-ident"); 62 | } 63 | }); 64 | */ 65 | 66 | var sn = document.getElementById("source-navigation"); 67 | if(sn) { 68 | sn.addEventListener("click", function(event) { 69 | if(event.target.tagName != "A" || event.target.className == "docs") 70 | return true; 71 | if(event.target.nextSibling) { 72 | var s = event.target.nextSibling; 73 | if(s.style.display == "" || s.style.display == "none" || s.className.indexOf("search-hit") != -1) { 74 | s.style.display = "block"; 75 | var items = s.getElementsByTagName("ul"); 76 | var i; 77 | for(i = 0; i < items.length; i++) 78 | items[i].style.display = ""; 79 | items = s.getElementsByTagName("li"); 80 | for(i = 0; i < items.length; i++) 81 | items[i].style.display = ""; 82 | } else 83 | s.style.display = ""; 84 | } 85 | 86 | //var id = event.target.href.substring(event.target.href.indexOf("#") + 1); 87 | //sn.style.marginTop = (document.getElementById(id).offsetTop - event.target.offsetTop + 16) + "px"; 88 | }); 89 | 90 | var search = document.createElement("input"); 91 | search.setAttribute("type", "search"); 92 | function searchHelper() { 93 | var regex = new RegExp(search.value, "i"); 94 | var items = document.querySelectorAll("#source-navigation a[href^=\"#\"]"); 95 | var stxt = search.value; 96 | for(var i = 0; i < items.length; i++) { 97 | var a = items[i]; 98 | if(stxt.length && regex.test(a.textContent)) { 99 | var p = a.parentNode; 100 | while(p.tagName != "DIV") { 101 | if(p.tagName == "LI") 102 | p.style.display = "list-item"; 103 | else 104 | p.style.display = "block"; 105 | p.className += " search-hit"; 106 | p = p.parentNode; 107 | } 108 | } else { 109 | var p = a.parentNode; 110 | if(stxt.length == 0) { 111 | p.style.display = ""; 112 | while(p.tagName != "DIV") { 113 | p.style.display = ""; 114 | p = p.parentNode; 115 | } 116 | } else 117 | p.style.display = "none"; 118 | p.className = p.className.replace(" search-hit", ""); 119 | } 120 | } 121 | } 122 | search.addEventListener("keyup", searchHelper); 123 | sn.insertBefore(search, sn.firstChild); 124 | } 125 | 126 | /* 127 | function updateDynamicStyle() { 128 | var thing = document.getElementById("page-content"); 129 | var newStyle = document.getElementById("dynamic-style"); 130 | if(!newStyle) { 131 | newStyle = document.createElement("style"); 132 | newStyle.setAttribute("id", "dynamic-style"); 133 | newStyle.type = "text/css"; 134 | document.head.appendChild(newStyle); 135 | } 136 | 137 | var maxContentWidth = window.innerWidth; 138 | // 800 is the threshold for putting nav vertically 139 | if(maxContentWidth < 800) 140 | maxContentWidth = 800; 141 | else 142 | maxContentWidth = 143 | document.body.offsetWidth - 144 | document.getElementById("page-nav").offsetWidth - 145 | document.getElementById("page-nav").offsetLeft - 146 | 64; 147 | 148 | // sanity check lol 149 | if(maxContentWidth < 800) 150 | maxContentWidth = 800; 151 | 152 | newStyle.innerHTML = ".member-list:not(.constructors) dt .simplified-prototype:hover { width: " + (thing.offsetWidth - 32) + "px; } #page-content pre.d_code, #page-content .overload-option, #page-content .member-list dt { max-width: " + (maxContentWidth) + "px; }"; 153 | } 154 | 155 | updateDynamicStyle(); 156 | 157 | window.onresize = updateDynamicStyle; 158 | */ 159 | 160 | // Disable line numbers in IE because the copy/paste with them sucks - it includes all line numbers 161 | // in the middle making it too hard to use. Copy/paste is more important than line displays. 162 | if (navigator.userAgent.indexOf('MSIE') !== -1 || navigator.appVersion.indexOf('Trident/') > 0) { 163 | var items = document.querySelectorAll(".with-line-wrappers"); 164 | for(var a = 0; a < items.length; a++) 165 | items[a].className = items[a].className.replace("with-line-wrappers", ""); 166 | } 167 | 168 | // Keybind to focus search bar on '?' keydown. 169 | document.addEventListener("keydown", (event) => { 170 | if (event.key == "?") { 171 | var searchBox = document.getElementsByName("searchTerm")[0]; 172 | // Hack so the '?' doesn't auto-populate in the search bar. 173 | this.setTimeout(() => { 174 | searchBox.focus(); 175 | }, 100); 176 | } 177 | }); 178 | 179 | 180 | }); 181 | -------------------------------------------------------------------------------- /search-docs.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 19 | 20 | 21 | 26 | 27 | -------------------------------------------------------------------------------- /search-docs.js: -------------------------------------------------------------------------------- 1 | /* 2 | This is the source for offline web search; it will be embedded 3 | in a generated search page along with the search index xml. 4 | 5 | You will almost certainly want to gzip this when delivering it! 6 | Also be sure it has the proper cache headers to give a remotely 7 | acceptable performance result. Configure the web server to do 8 | both these. When storing it for offline usage, you might just 9 | leave it unzipped though for convenience of use without a web server. 10 | 11 | Tip to the end user: you might want to open this page and keep it 12 | open in a reused tab. 13 | 14 | The file generated should be the skeleton.html with the search 15 | index in a 9 | 10 | 12 | 13 | 14 | 15 |
16 |28 |17 | Documentation 18 | 21 |22 | 23 | 27 |29 |34 | 35 | 36 |30 |31 | 33 |