nroff-mode.el 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341
  1. ;;; nroff-mode.el --- GNU Emacs major mode for editing nroff source
  2. ;; Copyright (C) 1985-1986, 1994-1995, 1997, 2001-2012
  3. ;; Free Software Foundation, Inc.
  4. ;; Maintainer: FSF
  5. ;; Keywords: wp
  6. ;; This file is part of GNU Emacs.
  7. ;; GNU Emacs is free software: you can redistribute it and/or modify
  8. ;; it under the terms of the GNU General Public License as published by
  9. ;; the Free Software Foundation, either version 3 of the License, or
  10. ;; (at your option) any later version.
  11. ;; GNU Emacs is distributed in the hope that it will be useful,
  12. ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. ;; GNU General Public License for more details.
  15. ;; You should have received a copy of the GNU General Public License
  16. ;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
  17. ;;; Commentary:
  18. ;; This package is a major mode for editing nroff source code. It knows
  19. ;; about various nroff constructs, ms, mm, and me macros, and will fill
  20. ;; and indent paragraphs properly in their presence. It also includes
  21. ;; a command to count text lines (excluding nroff constructs), a command
  22. ;; to center a line, and movement commands that know how to skip macros.
  23. ;; Paragraph filling and line-counting currently don't respect comments,
  24. ;; as they should.
  25. ;;; Code:
  26. (defgroup nroff nil
  27. "Nroff mode."
  28. :link '(custom-group-link :tag "Font Lock Faces group" font-lock-faces)
  29. :group 'wp
  30. :prefix "nroff-")
  31. (defcustom nroff-electric-mode nil
  32. "Non-nil means automatically closing requests when you insert an open."
  33. :group 'nroff
  34. :type 'boolean)
  35. (defvar nroff-mode-map
  36. (let ((map (make-sparse-keymap))
  37. (menu-map (make-sparse-keymap)))
  38. (define-key map "\t" 'tab-to-tab-stop)
  39. (define-key map "\es" 'center-line)
  40. (define-key map "\e?" 'nroff-count-text-lines)
  41. (define-key map "\n" 'nroff-electric-newline)
  42. (define-key map "\en" 'nroff-forward-text-line)
  43. (define-key map "\ep" 'nroff-backward-text-line)
  44. (define-key map "\C-c\C-c" 'nroff-view)
  45. (define-key map [menu-bar nroff-mode] (cons "Nroff" menu-map))
  46. (define-key menu-map [nn]
  47. '(menu-item "Newline" nroff-electric-newline
  48. :help "Insert newline for nroff mode; special if nroff-electric mode"))
  49. (define-key menu-map [nc]
  50. '(menu-item "Count text lines" nroff-count-text-lines
  51. :help "Count lines in region, except for nroff request lines."))
  52. (define-key menu-map [nf]
  53. '(menu-item "Forward text line" nroff-forward-text-line
  54. :help "Go forward one nroff text line, skipping lines of nroff requests"))
  55. (define-key menu-map [nb]
  56. '(menu-item "Backward text line" nroff-backward-text-line
  57. :help "Go backward one nroff text line, skipping lines of nroff requests"))
  58. (define-key menu-map [ne]
  59. '(menu-item "Electric newline mode"
  60. nroff-electric-mode
  61. :help "Auto insert closing requests if necessary"
  62. :button (:toggle . nroff-electric-mode)))
  63. (define-key menu-map [npm]
  64. '(menu-item "Preview as man page" nroff-view
  65. :help "Run man on this file."))
  66. map)
  67. "Major mode keymap for `nroff-mode'.")
  68. (defvar nroff-mode-syntax-table
  69. (let ((st (copy-syntax-table text-mode-syntax-table)))
  70. ;; " isn't given string quote syntax in text-mode but it
  71. ;; (arguably) should be for use round nroff arguments (with ` and
  72. ;; ' used otherwise).
  73. (modify-syntax-entry ?\" "\" 2" st)
  74. ;; Comments are delimited by \" and newline.
  75. ;; And in groff also \# to newline.
  76. (modify-syntax-entry ?# ". 2" st)
  77. (modify-syntax-entry ?\\ "\\ 1" st)
  78. (modify-syntax-entry ?\n ">" st)
  79. st)
  80. "Syntax table used while in `nroff-mode'.")
  81. (defvar nroff-imenu-expression
  82. ;; man headers:
  83. '((nil "^\\.SH \"?\\([^\"\n]*\\)\"?$" 1)))
  84. (defcustom nroff-font-lock-keywords
  85. (list
  86. ;; Directives are . or ' at start of line, followed by
  87. ;; optional whitespace, then command (which my be longer than
  88. ;; 2 characters in groff). Perhaps the arguments should be
  89. ;; fontified as well.
  90. "^[.']\\s-*\\sw+"
  91. ;; There are numerous groff escapes; the following get things
  92. ;; like \-, \(em (standard troff) and \f[bar] (groff
  93. ;; variants). This won't currently do groff's \A'foo' and
  94. ;; the like properly. One might expect it to highlight an escape's
  95. ;; arguments in common cases, like \f.
  96. (concat "\\\\" ; backslash
  97. "\\(" ; followed by various possibilities
  98. (mapconcat 'identity
  99. '("[f*n]*\\[.+?]" ; some groff extensions
  100. "(.." ; two chars after (
  101. "[^(\"#]" ; single char escape
  102. ) "\\|")
  103. "\\)")
  104. )
  105. "Font-lock highlighting control in `nroff-mode'."
  106. :group 'nroff
  107. :type '(repeat regexp))
  108. (defcustom nroff-mode-hook nil
  109. "Hook run by function `nroff-mode'."
  110. :type 'hook
  111. :group 'nroff)
  112. ;;;###autoload
  113. (define-derived-mode nroff-mode text-mode "Nroff"
  114. "Major mode for editing text intended for nroff to format.
  115. \\{nroff-mode-map}
  116. Turning on Nroff mode runs `text-mode-hook', then `nroff-mode-hook'.
  117. Also, try `nroff-electric-mode', for automatically inserting
  118. closing requests for requests that are used in matched pairs."
  119. (set (make-local-variable 'font-lock-defaults)
  120. ;; SYNTAX-BEGIN is set to backward-paragraph to avoid slow-down
  121. ;; near the end of large buffers due to searching to buffer's
  122. ;; beginning.
  123. '(nroff-font-lock-keywords nil t nil backward-paragraph))
  124. (set (make-local-variable 'outline-regexp) "\\.H[ ]+[1-7]+ ")
  125. (set (make-local-variable 'outline-level) 'nroff-outline-level)
  126. ;; now define a bunch of variables for use by commands in this mode
  127. (set (make-local-variable 'page-delimiter) "^\\.\\(bp\\|SK\\|OP\\)")
  128. (set (make-local-variable 'paragraph-start)
  129. (concat "[.']\\|" paragraph-start))
  130. (set (make-local-variable 'paragraph-separate)
  131. (concat "[.']\\|" paragraph-separate))
  132. ;; Don't auto-fill directive lines starting . or ' since they normally
  133. ;; have to be one line. But do auto-fill comments .\" .\# and '''.
  134. ;; Comment directives (those starting . or ') are [.'][ \t]*\\[#"]
  135. ;; or ''', and this regexp is everything except those. So [.']
  136. ;; followed by not backslash and not ' or followed by backslash but
  137. ;; then not # or "
  138. (set (make-local-variable 'auto-fill-inhibit-regexp)
  139. "[.'][ \t]*\\([^ \t\\']\\|\\\\[^#\"]\\)")
  140. ;; comment syntax added by mit-erl!gildea 18 Apr 86
  141. (set (make-local-variable 'comment-start) "\\\" ")
  142. (set (make-local-variable 'comment-start-skip) "\\\\[\"#][ \t]*")
  143. (set (make-local-variable 'comment-column) 24)
  144. (set (make-local-variable 'comment-indent-function) 'nroff-comment-indent)
  145. (set (make-local-variable 'comment-insert-comment-function)
  146. 'nroff-insert-comment-function)
  147. (set (make-local-variable 'imenu-generic-expression) nroff-imenu-expression))
  148. (defun nroff-outline-level ()
  149. (save-excursion
  150. (looking-at outline-regexp)
  151. (skip-chars-forward ".H ")
  152. (string-to-number (buffer-substring (point) (+ 1 (point))))))
  153. ;; Compute how much to indent a comment in nroff/troff source.
  154. ;; By mit-erl!gildea April 86
  155. (defun nroff-comment-indent ()
  156. "Compute indent for an nroff/troff comment.
  157. Puts a full-stop before comments on a line by themselves."
  158. (let ((pt (point)))
  159. (unwind-protect
  160. (progn
  161. (skip-chars-backward " \t")
  162. (if (bolp)
  163. (progn
  164. ;; FIXME delete-horizontal-space?
  165. (setq pt (1+ pt))
  166. (insert ?.)
  167. 1)
  168. (if (save-excursion
  169. (backward-char 1)
  170. (looking-at "^[.']"))
  171. 1
  172. (max comment-column
  173. (* 8 (/ (+ (current-column)
  174. 9) 8)))))) ; add 9 to ensure at least two blanks
  175. (goto-char pt))))
  176. ;; http://lists.gnu.org/archive/html/emacs-devel/2007-10/msg01869.html
  177. (defun nroff-insert-comment-function ()
  178. "Function for `comment-insert-comment-function' in `nroff-mode'."
  179. (indent-to (nroff-comment-indent))
  180. (insert comment-start))
  181. (defun nroff-count-text-lines (start end &optional print)
  182. "Count lines in region, except for nroff request lines.
  183. All lines not starting with a period are counted up.
  184. Interactively, print result in echo area.
  185. Noninteractively, return number of non-request lines from START to END."
  186. (interactive "r\np")
  187. (if print
  188. (message "Region has %d text lines" (nroff-count-text-lines start end))
  189. (save-excursion
  190. (save-restriction
  191. (narrow-to-region start end)
  192. (goto-char (point-min))
  193. (- (buffer-size) (nroff-forward-text-line (buffer-size)))))))
  194. (defun nroff-forward-text-line (&optional cnt)
  195. "Go forward one nroff text line, skipping lines of nroff requests.
  196. An argument is a repeat count; if negative, move backward."
  197. (interactive "p")
  198. (if (not cnt) (setq cnt 1))
  199. (while (and (> cnt 0) (not (eobp)))
  200. (forward-line 1)
  201. (while (and (not (eobp)) (looking-at "[.']."))
  202. (forward-line 1))
  203. (setq cnt (- cnt 1)))
  204. (while (and (< cnt 0) (not (bobp)))
  205. (forward-line -1)
  206. (while (and (not (bobp))
  207. (looking-at "[.']."))
  208. (forward-line -1))
  209. (setq cnt (+ cnt 1)))
  210. cnt)
  211. (defun nroff-backward-text-line (&optional cnt)
  212. "Go backward one nroff text line, skipping lines of nroff requests.
  213. An argument is a repeat count; negative means move forward."
  214. (interactive "p")
  215. (nroff-forward-text-line (- cnt)))
  216. (defconst nroff-brace-table
  217. '((".(b" . ".)b")
  218. (".(l" . ".)l")
  219. (".(q" . ".)q")
  220. (".(c" . ".)c")
  221. (".(x" . ".)x")
  222. (".(z" . ".)z")
  223. (".(d" . ".)d")
  224. (".(f" . ".)f")
  225. (".LG" . ".NL")
  226. (".SM" . ".NL")
  227. (".LD" . ".DE")
  228. (".CD" . ".DE")
  229. (".BD" . ".DE")
  230. (".DS" . ".DE")
  231. (".DF" . ".DE")
  232. (".FS" . ".FE")
  233. (".KS" . ".KE")
  234. (".KF" . ".KE")
  235. (".LB" . ".LE")
  236. (".AL" . ".LE")
  237. (".BL" . ".LE")
  238. (".DL" . ".LE")
  239. (".ML" . ".LE")
  240. (".RL" . ".LE")
  241. (".VL" . ".LE")
  242. (".RS" . ".RE")
  243. (".TS" . ".TE")
  244. (".EQ" . ".EN")
  245. (".PS" . ".PE")
  246. (".BS" . ".BE")
  247. (".G1" . ".G2") ; grap
  248. (".na" . ".ad b")
  249. (".nf" . ".fi")
  250. (".de" . "..")))
  251. (defun nroff-electric-newline (arg)
  252. "Insert newline for nroff mode; special if nroff-electric mode.
  253. In `nroff-electric-mode', if ending a line containing an nroff opening request,
  254. automatically inserts the matching closing request after point."
  255. (interactive "P")
  256. (let ((completion (save-excursion
  257. (beginning-of-line)
  258. (and (null arg)
  259. nroff-electric-mode
  260. (<= (point) (- (point-max) 3))
  261. (cdr (assoc (buffer-substring (point)
  262. (+ 3 (point)))
  263. nroff-brace-table)))))
  264. (needs-nl (not (looking-at "[ \t]*$"))))
  265. (if (null completion)
  266. (newline (prefix-numeric-value arg))
  267. (save-excursion
  268. (insert "\n\n" completion)
  269. (if needs-nl (insert "\n")))
  270. (forward-char 1))))
  271. (define-minor-mode nroff-electric-mode
  272. "Toggle automatic nroff request pairing (Nroff Electric mode).
  273. With a prefix argument ARG, enable Nroff Electric mode if ARG is
  274. positive, and disable it otherwise. If called from Lisp, enable
  275. the mode if ARG is omitted or nil.
  276. Nroff Electric mode is a buffer-local minor mode, for use with
  277. `nroff-mode'. When enabled, Emacs checks for an nroff request at
  278. the beginning of the line, and inserts the matching closing
  279. request if necessary. This command toggles that mode (off->on,
  280. on->off), with an argument, turns it on if arg is positive,
  281. otherwise off."
  282. :lighter " Electric"
  283. (or (derived-mode-p 'nroff-mode) (error "Must be in nroff mode")))
  284. (declare-function Man-getpage-in-background "man" (topic))
  285. (defun nroff-view ()
  286. "Run man on this file."
  287. (interactive)
  288. (require 'man)
  289. (let* ((file (buffer-file-name))
  290. (viewbuf (get-buffer (concat "*Man " file "*"))))
  291. (unless file
  292. (error "Buffer is not associated with any file"))
  293. (and (buffer-modified-p)
  294. (y-or-n-p (format "Save buffer %s first? " (buffer-name)))
  295. (save-buffer))
  296. (if viewbuf
  297. (kill-buffer viewbuf))
  298. (Man-getpage-in-background file)))
  299. ;; Old names that were not namespace clean.
  300. (define-obsolete-function-alias 'count-text-lines 'nroff-count-text-lines "22.1")
  301. (define-obsolete-function-alias 'forward-text-line 'nroff-forward-text-line "22.1")
  302. (define-obsolete-function-alias 'backward-text-line 'nroff-backward-text-line "22.1")
  303. (define-obsolete-function-alias 'electric-nroff-newline 'nroff-electric-newline "22.1")
  304. (define-obsolete-function-alias 'electric-nroff-mode 'nroff-electric-mode "22.1")
  305. (provide 'nroff-mode)
  306. ;;; nroff-mode.el ends here