Commits

Paul Sexton committed f614283

Added emacs lisp code allowing combination of org mode and lisp mode in a single Emacs buffer! Can edit doc
strings in place using all the goodness of org.
Most doc strings are recognised, but they can also be explicitly flagged by including ### as the first three
characters in the string.

  • Participants
  • Parent commits c1f60e0

Comments (0)

Files changed (2)

 ;;;;   Currently [[hs:sym]]  --> HyperSpecRoot/sym but this will not work as
 ;;;;   hyperspec pages are unfortunately not named according to the symbol they
 ;;;;   describe.
+;;;; * table of slots at beginning of each class def
+;;;; * reverse auto-doc:
+;;;;   CLOD annotates org file to 'mark up' doc strings for each
+;;;;   type of entity.
+;;;;   CLOD can then READ an org file and LOAD docstrings into entities.
+;;;;   Workflow:
+;;;;   Write initial docs in lisp (optional)
+;;;;   Run CLOD, produce org file
+;;;;   Edit org file
+;;;;   Later, after changes in source:
+;;;;   Suck org file back into clod, then re-export to org again. The org
+;;;;   file will contain updated doc string markers.
 ;;;;
-;;;; todo table of slots at beginning of each class def
 ;;;;
 ;;;; Generate documentation for this package with:
 ;;;; (clod:document-package :clod "~/lisp/clod/doc/clod-doc.org"
   'Description'. However, if you don't want this to happen, but rather want
   the docstring to define its own heading names, make sure that the very first
   thing in the docstring is a heading (straight after the opening quote).
+  (Note for mmm-mode users (see below): if the docstring starts with '###'
+  to signal that it is in fact a docstring, CLOD will skip the hashes before
+  looking to see if the string starts with a heading.)
+  So '=\"###* Arguments ...\"=' will work in that case.
 - Some symbol names used by common lisp can conflict with the markup used
   by org mode. For example, =*global-variable*=: asterisks are interpreted
   by org mode as signifying bold text. CLOD catches these in headings and
   semicolons =;;;= are assumed to be example lisp source code. The first 3
   semicolons are removed and the rest of the line is syntax highlighted.
 
-* Example
+* Combining org mode and common lisp mode in a single Emacs buffer
+  
+You can use org mode markup within docstrings, but you can't see the effects of
+the markup until you export the documentation to org using CLOD. You also don't
+get access to org's support for automatic formatting of bulleted lists as you
+write, or the fantastic support for writing tables, or hyperlinks that you can
+click with the mouse, or ....
 
-Here is the docstring for [[document-package]].
+What if you could use all the goodness of Org, while editing docstrings in your
+lisp source code? You can. This section explains how.
+
+1. Download and install nXhtml, an emacs package that contains code allowing
+   multiple major modes to be active in a single buffer.
+   http://ourcomments.org/cgi-bin/emacsw32-dl-latest.pl
+2. Add the code in `mmm-clod.el' to your .emacs file. Make sure you change
+   the mmm-mode directory to the directory where you installed mmm-mode.
+3. Restart emacs. Load a lisp source file. All documentation strings should
+   appear with a coloured background, and when you move the cursor inside them,
+   you will see 'Lisp[Org]' on the modeline. 
+4. If not everything is highlighting correctly, or if you write a new docstring
+   and org does not activate within it, press control-` to 'refresh' mmm-mode.
+
+Not everything works: expanding and collapsing headings fails, and
+clicking the mouse elsewhere within the doc string often causes problems. But
+overall the two modes work together surprisingly well.
+
+MMM-mode recognises the following things as doc strings:
+1. Any string that emacs fontifies using 'font-lock-doc-face'. (in other words,
+   font-lock mode must be active.)
+2. Any string inside the form '=(:documentation STRING)='.
+3. Finally, any string whose first three characters are '###'. Since lines 
+   beginning with a hash are interpreted as comments by org mode, these
+   characters will disappear when you export your document to HTML or other
+   formats.
+
+* Example docstring
+
+Here is the docstring for [[document-package]]. It illustrates the use of
+headings, bulleted lists, definition lists, =code=, *bold* and /italic/
+markup, hyperlinks to other definitions, and syntax highlighting of lisp source
+code examples.
 
 : * Arguments
 : - PKG :: A package name or package object.
 :   sections, just like functions and generic functions. Most people put
 :   'method' documentation in the docstrings of their generic functions, but
 :   if you set docstrings for individual methods then set this to nil.
-: - =STYLE-SHEET= specifies the name of a Cascading Style Sheet (.CSS) file
+: - =STYLE-SHEET= specifies the name of a /Cascading Style Sheet/ (.CSS) file
 :   which will be used as the style for the document if you export it
 :   to HTML from org mode.
 :
          (for line = (read-line in))
          (for str =
               (regex-replace-all
+               ;; Regex matches 'heading' lines
                (format nil "^([~A]+)([ \t]+)(.*)$"
                        (quote-meta-chars
                         (format nil "~C" *heading-char*)))
                                       :initial-element *heading-char*)
                          match3))
                :simple-calls t))
+         ;; Regex matches first part of "[[...][...]]" long-form
+         ;; hyperlinks. Ensures first part of hyperlink is
+         ;; html-safe.
          (setf str (regex-replace-all "\\[\\[([^]]*)\\]\\["
                                       str
                                       (lambda (match match1 &rest matches)
                                         (format nil "[[~A]["
                                                 (html-safe-string match1)))
                                       :simple-calls t))
+         ;; Regex matches all [[...]] short-form hyperlinks.
+         ;; Ensures html safety.
          (setf str (regex-replace-all "\\[\\[([^]]*)\\]\\]"
                                       str
                                       (lambda (match match1 &rest matches)
 
 (defun write-docstring-section (title docstr)
   "Writes the documentation string DOCSTR within its own subsection."
+  (cond
+    ((string-starts-with? docstr (format nil "###~C" #\newline))
+      (setf docstr (subseq docstr 4)))
+    ((string-starts-with? docstr (format nil "###~C~C" #\return #\newline))
+      (setf docstr (subseq docstr 5)))
+    ((string-starts-with? docstr (format nil "###" #\return #\newline))
+      (setf docstr (subseq docstr 3))))
   (if (and (stringp docstr)
            (eql 0 (search (format nil "~C " *heading-char*) docstr)))
       (let ((*heading-level* (1+ *heading-level*)))
+;;;;  -*- mode: emacs-lisp; coding: iso-latin-1-unix -*-
+
+;;; This code can be added to your .emacs file to allow editing of docstrings
+;;; using org mode, WITHIN lisp mode.
+;;; See the CLOD documentation for details.
+
+;;; Change this to the directory whwere nXhtml is installed.
+(add-to-list 'load-path (mkfilename *user-emacs-root* "/mmm-mode"))
+
+(require 'mmm-auto)
+(setq mmm-global-mode 'maybe)
+(mmm-add-mode-ext-class 'lisp-mode  nil  'org-submode)
+(mmm-add-mode-ext-class 'slime-mode  nil  'org-submode)
+;; The above, using major mode symbols, didn't seem to work for me so I
+;; also added this line (regexp uses same format as major-mode-alist):
+(mmm-add-mode-ext-class nil  "\\.lisp$"  'org-submode)
+(mmm-add-mode-ext-class nil  "\\.asd$"  'org-submode)
+(setq mmm-submode-decoration-level 2)
+
+;; This prevents transient loss of fontification on first
+;; calling `mmm-ify-by-class'
+(defadvice mmm-ify-by-class (after refontify-after-mmm activate compile)
+  (font-lock-fontify-buffer))
+
+;; Control-backquote "refreshes" MMM-mode in current buffer.
+(global-set-key [?\C-`] (lambda () (interactive) (mmm-ify-by-class 'org-submode)))
+
+;; And the definition of 'org-submode
+(mmm-add-group 'org-submode
+               '((org-submode1
+                  :submode org-mode
+                  :face mmm-declaration-submode-face
+                  :front "\""
+                  :back "[^\\]\""
+                  :back-offset 1
+                  :front-verify check-docstring-match
+                  :end-not-begin t)
+                 (org-submode-2
+                  :submode org-mode
+                  :face mmm-declaration-submode-face
+                  ;; Match '(:documentation "...")' docstrings
+                  :front "(:documentation[\t\n ]+\""
+                  :back "[^\\]\""
+                  :back-offset 1
+                  :end-not-begin t)))
+
+(defun face-at (pos)
+  (save-excursion
+    (goto-char pos)
+    (face-at-point)))
+
+
+(defun check-docstring-match ()
+  (interactive)
+  (let ((beg (match-beginning 0))
+        (end (match-end 0)))
+  (cond
+   ;; Docstring if emacs has fontified it in 'docstring' face
+   ((eql (face-at end) 'font-lock-doc-face)
+    t)
+   ;; Docstring if the first three characters after the opening
+   ;; quote are "###"
+   ((string= (buffer-substring end (+ 3 end)) "###")
+    t)
+   (t
+    nil))))
+