git.fiddlerwoaroof.com
Raw Blame History
;;; -*- Mode: LISP; Syntax: COMMON-LISP; Package: LW-ADD-ONS; Base: 10 -*-
;;; $Header: /usr/local/cvsrep/lw-add-ons/documentation.lisp,v 1.20 2015/05/29 18:23:24 edi Exp $

;;; Copyright (c) 2005-2015, Dr. Edmund Weitz.  All rights reserved.

;;; Redistribution and use in source and binary forms, with or without
;;; modification, are permitted provided that the following conditions
;;; are met:

;;;   * Redistributions of source code must retain the above copyright
;;;     notice, this list of conditions and the following disclaimer.

;;;   * Redistributions in binary form must reproduce the above
;;;     copyright notice, this list of conditions and the following
;;;     disclaimer in the documentation and/or other materials
;;;     provided with the distribution.

;;; THIS SOFTWARE IS PROVIDED BY THE AUTHOR 'AS IS' AND ANY EXPRESSED
;;; OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
;;; WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
;;; ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
;;; DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
;;; DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
;;; GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
;;; INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
;;; WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
;;; NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
;;; SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

(in-package :lw-add-ons)

(defun manual-dir (&optional relative-path)
  "Returns a namestring for the LW browsable documentation
directory, optionally appending the string RELATIVE-PATH."
  (namestring (sys:lispworks-dir
               (format nil
                       #+:lispworks8
                       "manual/html-m/~A"
                       #+(or :lispworks6.1 :lispworks7)
                       "manual/online/~A"
                       #-(or :lispworks6.1 :lispworks7 :lispworks8)
                       "manual/online/web/~A"
                       (or relative-path "")))))

(defun remove-backslashes (string)
  "Returns STRING with backslashes replaced with slashes."
  (regex-replace-all "\\\\" string "/"))

(defun make-file-url (pathspec)
  "Accepts a pathname designator and returns a corresponding file
URL."
  (format nil "file:///~A"
          (remove-backslashes (namestring pathspec))))

(defun doc-entry (entry)
  "Returns the URL for the entry ENTRY."
  (or (ignore-errors* (do-hyperdoc-lookup entry))
      (gethash entry *doc-hash*)))

(defun add-doc-entry (entry link)
  "Sets the URL for the entry ENTRY to be LINK."
  (setf (gethash entry *doc-hash*) link))

(defun add-clhs-entry (entry link)
  "Accepts a CLHS entry and the name of the corresponding HTML
file \(maybe with fragment part) and creates the right *DOC-HASH*
entry."
  (let ((clhs-prefix
          (load-time-value
            (make-file-url (manual-dir "CLHS/Body/")))))
    (add-doc-entry entry (format nil "~A~A" clhs-prefix link))))

(defun collect-lw-links ()
  "Puts entries for all LW-specific functions into *DOC-HASH*
using functionality from the LW-DOC module."
  (lw-doc:parse-files)
  (let ((lw-doc:*link-prefix*
          (make-file-url (manual-dir))))
    (loop for (symbol nil) being the hash-keys of lw-doc::*link-table*
          using (hash-value ((shortcut nil link) . nil))
          do (add-doc-entry symbol (lw-doc::make-link shortcut link)))))

(defun collect-clhs-links ()
  "Puts all CLHS `standard' entries into *DOC-HASH* using the
`Map_Sym.txt' file."
  (with-open-file (map (merge-pathnames "Map_Sym.txt"
                                        (manual-dir "CLHS/Data/")))
    (loop for symbol-line = (read-line map nil nil)
          for link-line = (read-line map nil nil)
          while (and symbol-line link-line)
          do (add-clhs-entry symbol-line (subseq link-line 8)))))

(defun collect-clhs-add-on-links ()
  "Adds additional CLHS entries as defined in *CLHS-ADD-ONS*."
  (loop for (entry link) in *clhs-add-ons*
        do (add-clhs-entry entry link)))

(defun collect-mop-links ()
  "Adds MOP entries as defined by the fragments in *MOP-LINKS*."
  (let ((mop-url (make-file-url *mop-page*)))
    (loop for (entry link) in *mop-links*
          do (add-doc-entry entry (format nil "~A~A" mop-url link)))))

(defun setup-doc-entries ()
  "Empties *DOC-HASH* and then \(re-)fills it as described above.
Finally sets up *DOC-HASH-ENTRIES* as well."
  (clrhash *doc-hash*)
  (collect-mop-links)
  (collect-lw-links)
  (collect-clhs-links)
  (collect-clhs-add-on-links)
  (setq *doc-hash-entries*
        (loop for key being the hash-keys of *doc-hash*
              collect key)))

;; now do it
(setup-doc-entries)

(defun complete-doc-entry (string parse-inf)
  "Completion function used by \"Meta Documentation\" command."
  (declare (ignore parse-inf))
  (editor::complete-string string *doc-entries*
                           :ignore-case t))

(defun hyperdoc-lookup-function-and-base-uri (package)
  "If PACKAGE is a package with Hyperdoc support the lookup function
and the base URI are returned as two values."
  (let ((lookup-symbol (find-symbol "HYPERDOC-LOOKUP" package))
        (base-uri-symbol (find-symbol "*HYPERDOC-BASE-URI*" package)))
    (when (and lookup-symbol
               base-uri-symbol
               (fboundp lookup-symbol)
               (boundp base-uri-symbol))
      (values (symbol-function lookup-symbol)
              (symbol-value base-uri-symbol)))))

(defmethod do-hyperdoc-lookup ((symbol symbol))
  "Checks if SYMBOL has an associated Hyperdoc URI and returns it."
  (let ((package (symbol-package symbol)))
    (multiple-value-bind (lookup-function base-uri)
        (hyperdoc-lookup-function-and-base-uri package)
      (when-let (partial-uri (and lookup-function
                                  (or (funcall lookup-function symbol 'function)
                                      (funcall lookup-function symbol 'variable))))
        (string-append base-uri partial-uri)))))

(defmethod do-hyperdoc-lookup ((string string))
  "Applies DO-HYPERDOC-LOOKUP to all external symbols named STRING in
all packages with Hyperdoc support."
  (loop for package in (list-all-packages)
        for is-candidate = (hyperdoc-lookup-function-and-base-uri package)
        for (symbol status) = (multiple-value-list
                               (and is-candidate
                                    (find-symbol (string-upcase string) package)))
        for uri = (and symbol (eq status :external)
                       (do-hyperdoc-lookup symbol))
        when uri do (return uri)))

(defun collect-hyperdoc-entries ()
  "Collects a list of \(downcased) symbol names of all external
symbols in all packages with Hyperdoc support."
  (loop for package in (list-all-packages)
        when (hyperdoc-lookup-function-and-base-uri package)
        nconc (loop for symbol being the external-symbols of package
                    when (do-hyperdoc-lookup symbol)
                    collect (string-downcase (symbol-name symbol)))))