Advertisement
Guest User

csharp-codedoc-insertion-magic

a guest
May 7th, 2012
312
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Lisp 11.41 KB | None | 0 0
  1. ;; ==================================================================
  2. ;; C# code-doc insertion magic
  3. ;; ==================================================================
  4. ;;
  5. ;; In Visual Studio, if you type three slashes, it immediately expands into
  6. ;; an inline code-documentation fragment.  The following method does the
  7. ;; same thing.
  8. ;;
  9. ;; This is the kind of thing that could be handled by YASnippet or
  10. ;; another similarly flexible snippet framework. But I don't want to
  11. ;; introduce a dependency on yasnippet to csharp-mode. So the capability
  12. ;; must live within csharp-mode itself.
  13.  
  14. (defun csharp-maybe-insert-codedoc (arg)
  15.  
  16.   "Insert an xml code documentation template as appropriate, when
  17. typing slashes.  This fn gets bound to / (the slash key), in
  18. csharp-mode.  If the slash being inserted is not the third
  19. consecutive slash, the slash is inserted as normal.  If it is the
  20. third consecutive slash, then a xml code documentation template
  21. may be inserted in some cases. For example,
  22.  
  23.  a <summary> template is inserted if the prior line is empty,
  24.        or contains only an open curly brace;
  25.  a <remarks> template is inserted if the prior word
  26.        closes the <summary> element;
  27.  a <returns> template is inserted if the prior word
  28.        closes the <remarks> element;
  29.  an <example> template is inserted if the prior word closes
  30.        the <returns> element;
  31.  a <para> template is inserted if the prior word closes
  32.        a <para> element.
  33.  
  34. In all other cases the slash is inserted as normal.
  35.  
  36. If you want the default cc-mode behavior, which implies no automatic
  37. insertion of xml code documentation templates, then use this in
  38. your `csharp-mode-hook' function:
  39.  
  40.     (local-set-key (kbd \"/\") 'c-electric-slash)
  41.  
  42. "
  43.   (interactive "*p")
  44.   ;;(message "csharp-maybe-insert-codedoc")
  45.   (let (
  46.         (cur-point (point))
  47.         (char last-command-event)
  48.         (cb0 (char-before (- (point) 0)))
  49.         (cb1 (char-before (- (point) 1)))
  50.         is-first-non-whitespace
  51.         did-auto-insert
  52.         )
  53.  
  54.     ;; check if two prior chars were slash, in other words,
  55.     ;; check if this is the third slash in a row.
  56.     (if (and (= char ?/) cb0 (= ?/ cb0) cb1 (= ?/ cb1))
  57.  
  58.         (progn
  59.           ;;(message "yes - this is the third consecutive slash")
  60.           (setq is-first-non-whitespace
  61.                 (save-excursion
  62.                   (back-to-indentation)
  63.                   (= cur-point (+ (point) 2))))
  64.  
  65.           (if is-first-non-whitespace
  66.               ;; This is a 3-slash sequence.  It is the first non-whitespace text
  67.               ;; on the line. Now we need to examine the surrounding context
  68.               ;; in order to determine which xml cod doc template to insert.
  69.               (let (word-back char0 char1
  70.                               word-fore char-0 char-1
  71.                               text-to-insert         ;; text to insert in lieu of slash
  72.                               fn-to-call     ;; func to call after inserting text
  73.                               (preceding-line-is-empty (or
  74.                                                         (= (line-number-at-pos) 1)
  75.                                                         (save-excursion
  76.                                                           (forward-line -1)
  77.                                                           (beginning-of-line)
  78.                                                           (looking-at "[ \t]*$\\|[ \t]*{[ \t]*$"))))
  79.                               (flavor 0) ;; used only for diagnostic purposes
  80.                               )
  81.  
  82.                 ;;(message "starting a 3-slash comment")
  83.                 ;; get the prior word, and the 2 chars preceding it.
  84.                 (backward-word)
  85.  
  86.                 (setq word-back (thing-at-point 'word)
  87.                       char0 (char-before (- (point) 0))
  88.                       char1 (char-before (- (point) 1)))
  89.  
  90.                 ;; restore prior position
  91.                 (goto-char cur-point)
  92.  
  93.                 ;; get the following word, and the 2 chars preceding it.
  94.                 (forward-word)
  95.                 (backward-word)
  96.                 (setq word-fore (thing-at-point 'word)
  97.                       char-0 (char-before (- (point) 0))
  98.                       char-1 (char-before (- (point) 1)))
  99.  
  100.                 ;; restore prior position again
  101.                 (goto-char cur-point)
  102.  
  103.                 (cond
  104.                  ;; The preceding line is empty, or all whitespace, or
  105.                  ;; contains only an open-curly.  In this case, insert a
  106.                  ;; summary element pair.
  107.                  (preceding-line-is-empty
  108.                   (setq text-to-insert  "/ <summary>\n///   \n/// </summary>"
  109.                         flavor 1) )
  110.  
  111.                  ;; The preceding word closed a summary element.  In this case,
  112.                  ;; if the forward word does not open a remarks element, then
  113.                  ;; insert a remarks element.
  114.                  ((and (string-equal word-back "summary") (eq char0 ?/)  (eq char1 ?<))
  115.                   (if (not (and (string-equal word-fore "remarks") (eq char-0 ?<)))
  116.                       (setq text-to-insert "/ <remarks>\n///   <para>\n///     \n///   </para>\n/// </remarks>"
  117.                             flavor 2)))
  118.  
  119.                  ;; The preceding word closed the remarks section.  In this case,
  120.                  ;; insert an example element.
  121.                  ((and (string-equal word-back "remarks")  (eq char0 ?/)  (eq char1 ?<))
  122.                   (setq text-to-insert "/ <example>\n///   \n/// </example>"
  123.                         flavor 3))
  124.  
  125.                  ;; The preceding word closed the example section.  In this
  126.                  ;; case, insert an returns element.  This isn't always
  127.                  ;; correct, because sometimes the xml code doc is attached to
  128.                  ;; a class or a property, neither of which has a return
  129.                  ;; value. A more intelligent implementation would inspect the
  130.                  ;; syntax state and only inject a returns element if
  131.                  ;; appropriate.
  132.                  ((and (string-equal word-back "example")  (eq char0 ?/)  (eq char1 ?<))
  133.                   (setq text-to-insert "/ <returns></returns>"
  134.                         fn-to-call (lambda ()
  135.                                      (backward-word)
  136.                                      (backward-char)
  137.                                      (backward-char)
  138.                                      (c-indent-line-or-region)
  139.                                      )
  140.                         flavor 4))
  141.  
  142.                  ;; The preceding word opened the remarks section, or it
  143.                  ;; closed a para section. In this case, insert a para
  144.                  ;; element, using appropriate indentation with respect to the
  145.                  ;; prior tag.
  146.                  ((or
  147.                    (and (string-equal word-back "remarks")  (eq char0 ?<)  (or (eq char1 32) (eq char1 9)))
  148.                    (and (string-equal word-back "para")     (eq char0 ?/)  (eq char1 ?<)))
  149.  
  150.                   (let (prior-point spacer)
  151.                     (save-excursion
  152.                       (backward-word)
  153.                       (backward-char)
  154.                       (backward-char)
  155.                       (setq prior-point (point))
  156.                       (skip-chars-backward "\t ")
  157.                       (setq spacer (buffer-substring (point) prior-point))
  158.                       ;;(message (format "pt(%d) prior(%d) spacer(%s)" (point) prior-point spacer))
  159.                       )
  160.  
  161.                     (if (string-equal word-back "remarks")
  162.                         (setq spacer (concat spacer "   ")))
  163.  
  164.                     (setq text-to-insert (format "/%s<para>\n///%s  \n///%s</para>"
  165.                                                  spacer spacer spacer)
  166.                           flavor 6)))
  167.  
  168.                  ;; The preceding word opened a para element.  In this case, if
  169.                  ;; the forward word does not close the para element, then
  170.                  ;; close the para element.
  171.                  ;; --
  172.                  ;; This is a nice idea but flawed.  Suppose I have a para element with some
  173.                  ;; text in it. If I position the cursor at the first line, then type 3 slashes,
  174.                  ;; I get a close-element, and that would be inappropriate.  Not sure I can
  175.                  ;; easily solve that problem, so the best thing might be to simply punt, and
  176.                  ;; require people to close their own elements.
  177.                  ;;
  178.                  ;;              ( (and (string-equal word-back "para")  (eq char0 60)  (or (eq char1 32) (eq char1 9)))
  179.                  ;;                (if (not (and (string-equal word-fore "para") (eq char-0 47) (eq char-1 60) ))
  180.                  ;;                    (setq text-to-insert "/   \n/// </para>\n///"
  181.                  ;;                          fn-to-call (lambda ()
  182.                  ;;                                       (previous-line)
  183.                  ;;                                       (end-of-line)
  184.                  ;;                                       )
  185.                  ;;                          flavor 7) )
  186.                  ;;                )
  187.  
  188.                  ;; the default case - do nothing
  189.                  (t nil))
  190.  
  191.                 (if text-to-insert
  192.                     (progn
  193.                       ;;(message (format "inserting special text (f(%d))" flavor))
  194.  
  195.                       ;; set the flag, that we actually inserted text
  196.                       (setq did-auto-insert t)
  197.  
  198.                       ;; save point of beginning of insertion
  199.                       (setq cur-point (point))
  200.  
  201.                       ;; actually insert the text
  202.                       (insert text-to-insert)
  203.  
  204.                       ;; indent the inserted string, and re-position point, either through
  205.                       ;; the case-specific fn, or via the default progn.
  206.                       (if fn-to-call
  207.                           (funcall fn-to-call)
  208.  
  209.                         (let ((newline-count 0) (pos 0) ix)
  210.  
  211.                           ;; count the number of newlines in the inserted string
  212.                           (while (string-match "\n" text-to-insert pos)
  213.                             (setq pos (match-end 0)
  214.                                   newline-count (+ newline-count 1) )
  215.                             )
  216.  
  217.                           ;; indent what we just inserted
  218.                           (c-indent-region cur-point (point) t)
  219.  
  220.                           ;; move up n/2 lines. This assumes that the
  221.                           ;; inserted text is ~symmetric about the halfway point.
  222.                           ;; The assumption holds if the xml code doc uses a
  223.                           ;; begin-elt and end-elt on a new line all by themselves,
  224.                           ;; and a blank line in between them where the point should be.
  225.                           ;; A more intelligent implementation would use a specific
  226.                           ;; marker string, like @@DOT, to note the desired point.
  227.                           (forward-line (- 0 (/ newline-count 2)))
  228.                           (end-of-line)))))))))
  229.  
  230.     (if (not did-auto-insert)
  231.         (self-insert-command (prefix-numeric-value arg)))))
  232.  
  233. ;; ==================================================================
  234. ;; end of c# code-doc insertion magic
  235. ;; ==================================================================
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement