reduce-mode.el 97 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560
  1. ;;; reduce-mode.el --- Major mode to edit REDUCE computer-algebra code
  2. ;; Copyright (C) 1998-2001, 2012, 2017-2019 Francis J. Wright
  3. ;; Author: Francis J. Wright <https://sourceforge.net/u/fjwright>
  4. ;; Created: late 1992
  5. ;; Version: $Id$
  6. ;; Keywords: languages
  7. ;; Homepage: https://reduce-algebra.sourceforge.io/reduce-ide
  8. ;; Package-Version: 1.55
  9. ;; This file is not part of GNU Emacs.
  10. ;; This program is free software: you can redistribute it and/or
  11. ;; modify it under the terms of the GNU General Public License as
  12. ;; published by the Free Software Foundation, either version 3 of
  13. ;; the License, or (at your option) any later version.
  14. ;; This program is distributed in the hope that it will be useful,
  15. ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
  16. ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  17. ;; GNU General Public License for more details.
  18. ;; You should have received a copy of the GNU General Public License
  19. ;; along with this program. If not, see <http://www.gnu.org/licenses/>.
  20. ;; Contributions by Rainer Schoepf flagged ; RS
  21. ;; Schoepf@goofy.zdv.Uni-Mainz.DE
  22. ;; Contributions by Thomas Sturm flagged ; TS
  23. ;; sturm@redlog.eu
  24. ;;; Commentary:
  25. ;; REDUCE Mode is a major mode for editing source code for the REDUCE
  26. ;; computer algebra system, which is Open Source and available from
  27. ;; <https://sourceforge.net/projects/reduce-algebra>.
  28. ;; The latest version of REDUCE Mode is available from
  29. ;; <https://sourceforge.net/p/reduce-algebra/code/HEAD/tree/trunk/generic/emacs>.
  30. ;; Full documentation covering the installation and use of REDUCE mode
  31. ;; is provided by a texinfo source file called `reduce-ide.texinfo'.
  32. ;; From this are (or can be) derived the info file `reduce-ide.info',
  33. ;; the HTML file `reduce-ide.html' and the PDF file `reduce-ide.pdf'.
  34. ;; The info file can be browsed using the independent info browsing
  35. ;; program called `info', or installed into the Emacs info browser.
  36. ;;; Usage:
  37. ;; To install in GNU Emacs 24+, download this file to any convenient
  38. ;; directory and run the Emacs command `package-install-file' on it.
  39. ;; Brief manual installation instructions follow.
  40. ;; Byte-compile this file, put it somewhere in your `load-path', and
  41. ;; put the following in your `.emacs' file:
  42. ;; (autoload 'reduce-mode "reduce-mode" "Major mode for REDUCE code editing" t)
  43. ;; To run REDUCE Mode automatically on files with extension ".red" or
  44. ;; ".tst" put the following (after `autoload') in your `.emacs' file:
  45. ;;;###autoload
  46. (add-to-list 'auto-mode-alist '("\\.\\(red\\|tst\\)\\'" . reduce-mode))
  47. ;; To make REDUCE Mode customization always available put the
  48. ;; following (after `autoload') in your `.emacs' file:
  49. ;;;###autoload
  50. (defgroup reduce nil
  51. "Support for editing and running REDUCE code."
  52. :tag "REDUCE" :group 'languages :load "reduce-mode")
  53. ;; To turn on only REDUCE font-lock mode by default include
  54. ;; (add-hook 'reduce-mode-hook 'turn-on-font-lock)
  55. ;; or to turn on all supported font-lock modes by default include
  56. ;; (global-font-lock-mode 1)
  57. ;;; To do:
  58. ;; BUGS
  59. ;; ====
  60. ;; ! should not be an escape IN STRINGS (motion by sexp, font-lock)
  61. ;; reduce-backward-statement does too much searching!
  62. ;; Enhancements
  63. ;; ============
  64. ;; more flexible intelligent indentation, rationalize the code
  65. ;; make skipping comment statements configurable (?)
  66. ;; add RLisp88 support (?)
  67. ;; more structure templates (?) -- while, repeat
  68. ;; faster font-lock (function rather than just regexps)?
  69. ;;; Code:
  70. (defconst reduce-mode-version
  71. ;; Extract version from `package-version' in file header:
  72. (eval-when-compile
  73. (require 'lisp-mnt)
  74. (save-excursion (lm-header "package-version")))
  75. "Version information for REDUCE Mode.")
  76. ;; (message "Loading reduce-mode") ; TEMPORARY!
  77. (eval-when-compile ; keep compiler happy!
  78. (require 'timer))
  79. ;; Customizable user options:
  80. (defgroup reduce nil
  81. "Support for editing and running REDUCE code."
  82. :tag "REDUCE"
  83. :group 'languages)
  84. (defgroup reduce-interface nil
  85. "Interface options for editing and running REDUCE code."
  86. :tag "REDUCE Interface"
  87. :group 'reduce)
  88. (defgroup reduce-format-display nil
  89. "Format and display options for editing and running REDUCE code."
  90. :tag "REDUCE Format & Display"
  91. :group 'reduce)
  92. (defcustom reduce-mode-load-hook nil
  93. "*List of functions to be called when REDUCE mode is loaded.
  94. E.g. `require-reduce-run' to automatically load `reduce-run'.
  95. It can be used to customize global features of REDUCE mode such as its
  96. key map, i.e. it is a good place to put keybindings."
  97. :type 'hook
  98. :options '(require-reduce-run)
  99. :group 'reduce)
  100. (defcustom reduce-mode-hook nil
  101. "*List of functions to be called when REDUCE mode is entered.
  102. E.g. `turn-on-font-lock' to turn on font-lock mode locally.
  103. It can be used to customize buffer-local features of REDUCE mode."
  104. :type 'hook
  105. :group 'reduce)
  106. ;; Interface:
  107. (defcustom reduce-imenu-generic-expression ; EXPERIMENTAL!
  108. '((nil "^\\([^%\n]+\\(ic\\|ro\\) \\)?\\s *procedure \\(\\w\\(\\w\\|\\s_\\|!.\\)*\\)" 3)
  109. ("Operators" "^\\([^%\n]+ic \\)?\\s *operator \\(\\w\\(\\w\\|\\s_\\|!.\\)*\\)" 2))
  110. "*Imenu support for procedure definitions and operator declarations.
  111. An alist with elements of the form (MENU-TITLE REGEXP INDEX) --
  112. see the documentation for `imenu-generic-expression'."
  113. :type '(repeat (list (choice (const nil) string) regexp integer))
  114. :group 'reduce-interface)
  115. (defcustom reduce-imenu nil
  116. "*If non-nil REDUCE mode automatically calls `imenu-add-to-menubar'.
  117. This adds a Contents menu to the menubar. Default is nil."
  118. :type 'boolean
  119. :group 'reduce-interface)
  120. (defcustom reduce-imenu-title "Procs/Ops"
  121. "*The title to use if REDUCE mode adds a proc/op menu to the menubar.
  122. Default is \"Procs/Ops\"."
  123. :type 'string
  124. :group 'reduce-interface)
  125. (defcustom reduce-max-up-tries 2
  126. "*Repeats of reduce-forward/backward-statement to move up block or group."
  127. :type 'integer
  128. :group 'reduce-interface)
  129. (defcustom reduce-completion-alist
  130. '(("algebraic ")
  131. ("algebraic procedure ")
  132. ("ap" . "algebraic procedure ")
  133. ("begin" . reduce-insert-block)
  134. ("clearrules ")
  135. ("collect ")
  136. ("comment ")
  137. ("endmodule")
  138. ("factorize(")
  139. ("first ")
  140. ("for all ")
  141. ("for each ")
  142. ("freeof ")
  143. ("gensym()")
  144. ("ift" . reduce-expand-if-then)
  145. ("if...then" . reduce-expand-if-then)
  146. ("ife" . reduce-expand-if-then-else)
  147. ("if...then...else" . reduce-expand-if-then-else)
  148. ("impart ")
  149. ("infinity")
  150. ("integer ")
  151. ("length ")
  152. ("linear ")
  153. ("load_package ")
  154. ("member ")
  155. ("module ")
  156. ("operator ")
  157. ("order ")
  158. ("procedure ")
  159. ("product ")
  160. ("quotient(")
  161. ("remainder(")
  162. ("repart ")
  163. ("repeat ")
  164. ("repeat until ")
  165. ("resultant(")
  166. ("return ")
  167. ("reverse ")
  168. ("scalar ")
  169. ("second ")
  170. ("smember(")
  171. ("such that ")
  172. ("st" . "such that ")
  173. ("symbolic ")
  174. ("symbolic operator ")
  175. ("sop" . "symbolic operator ")
  176. ("symbolic procedure ")
  177. ("sp" . "symbolic procedure ")
  178. ("third ")
  179. ("until ")
  180. ("where ")
  181. ("while ")
  182. ("while do ")
  183. ("write ")
  184. ("<<" . reduce-insert-group)
  185. )
  186. "Alist of REDUCE symbols to be completed by `reduce-complete-symbol'.
  187. Optional `cdr' is a replacement string or nullary function (for structures)."
  188. :type '(repeat (cons string (choice (const nil) string function)))
  189. :group 'reduce-interface)
  190. ;; Formatting:
  191. (defcustom reduce-indentation 3
  192. "*Depth of successive indentations in REDUCE code."
  193. :type 'integer
  194. :group 'reduce-format-display)
  195. (defcustom reduce-indent-line-conservative nil ; TS
  196. "*If non-nil, `reduce-indent-line' will not successively indent."
  197. :type 'boolean
  198. :group 'reduce-format-display)
  199. (defcustom reduce-comment-region-string "%% "
  200. "*String inserted by \\[reduce-comment-region] at start of each line."
  201. :version "1.21" ; Name was reduce-comment-region up to version 1555!
  202. :type 'string
  203. :group 'reduce-format-display)
  204. (defcustom reduce-auto-indent-mode t
  205. "*If non-nil then conditionally re-indent the current line.
  206. This will happen after `reduce-auto-indent-delay' seconds of idle
  207. time if the text just typed matches `reduce-auto-indent-regex'."
  208. :set (lambda (symbol value)
  209. (reduce-auto-indent-mode (or value 0)))
  210. :initialize 'custom-initialize-default
  211. :type 'boolean
  212. :group 'reduce-format-display)
  213. (defcustom reduce-auto-indent-delay 0.125
  214. "*Time in seconds to delay before maybe re-indenting current line."
  215. :type 'number
  216. :group 'reduce-format-display)
  217. (defcustom reduce-auto-indent-regexp "\\(else\\|end\\|>>\\)\\="
  218. "*Auto indent current line if text just typed matches this regexp.
  219. It should end with \\=\\=. The default value is \"\\(else\\|end\\|>>\\)\\=\\=\"."
  220. :type 'regexp
  221. :group 'reduce-format-display)
  222. ;; Display:
  223. (defcustom reduce-show-delim-mode-on show-paren-mode
  224. "If non-nil then turn on `reduce-show-delim-mode' initially.
  225. Since `reduce-show-delim-mode' is a buffer-local minor mode, it
  226. can also be turned on and off in each buffer independently.
  227. Defaults to the value of `show-paren-mode'."
  228. :package-version '(reduce-mode . "1.54")
  229. :type 'boolean
  230. :group 'reduce-format-display)
  231. (defcustom reduce-show-proc-mode nil
  232. "*If non-nil then display current procedure name in mode line.
  233. Update after `reduce-show-proc-delay' seconds of Emacs idle time."
  234. :set (lambda (symbol value)
  235. (reduce-show-proc-mode (or value 0)))
  236. :initialize 'custom-initialize-default
  237. :type 'boolean
  238. :group 'reduce-format-display)
  239. (defcustom reduce-show-proc-delay 0.125
  240. "*Time in seconds to delay before showing the current procedure name."
  241. :type 'number
  242. :group 'reduce-format-display)
  243. ;; External variables:
  244. ;; Due to improvements of byte compilation around 2003 the compiler
  245. ;; would complain about `make-local-var' on these later on. I left
  246. ;; unchanged another (too late) declaration for `which-func-mode' below,
  247. ;; which appears not to disturb. TS
  248. (defvar which-func-mode)
  249. (defvar which-func-format)
  250. (defvar imenu-space-replacement)
  251. ;; Internal variables:
  252. (defvar reduce-imenu-done nil
  253. "Buffer-local: set to true if `reduce-imenu-add-to-menubar' has been called.")
  254. (make-variable-buffer-local 'reduce-imenu-done)
  255. (defvar reduce-mode-map nil
  256. "Keymap for REDUCE mode.")
  257. (defvar reduce-mode-syntax-table nil
  258. "Syntax table for REDUCE mode.")
  259. (defconst reduce-font-lock-keywords
  260. '(
  261. reduce-font-lock-keywords-0 ; Default = nil
  262. reduce-font-lock-keywords-1 ; Algebraic
  263. reduce-font-lock-keywords-2 ; Symbolic
  264. reduce-font-lock-keywords-3 ; Full = t
  265. )
  266. "A list of symbols corresponding to increasing fontification.
  267. Each is assigned a `font-lock-keywords' value for REDUCE mode.")
  268. (defconst reduce-font-lock-syntactic-keywords
  269. ;; ((MATCHER SUBEXP SYNTAX OVERRIDE LAXMATCH) ... )
  270. ;; where SYNTAX = (SYNTAX-CODE . MATCHING-CHAR)
  271. ;; If this proves unreliable, try
  272. ;; '(("\".*\\(!\\)\"" 1 (1 . nil)))
  273. ;; i.e. only mark ! at end of a string as punctuation.
  274. ;; But this may be slow!
  275. '(("[^'\(]\\(!\\)\"" 1 (1 . nil)))
  276. "Mark ! followed by \" as having punctuation syntax (syntax-code 1)
  277. unless preceded by ' or (, for correct syntax highlighing of strings.")
  278. ;;;; *****************
  279. ;;;; REDUCE major mode
  280. ;;;; *****************
  281. ;;; Automatically pre-define reduce mode to autoload if available
  282. ;;; when building Emacs (unlikely ever to be done!):
  283. (declare-function reduce-show-delim-mode "reduce-delim" ())
  284. ;;;###autoload
  285. (defun reduce-mode ()
  286. "Major mode for editing REDUCE source code -- part of REDUCE IDE.
  287. Author: Francis Wright <http://sourceforge.net/users/fjwright>
  288. Version: see `reduce-mode-version'
  289. Comments, suggestions, bug reports, etc. are welcome.
  290. Full texinfo documentation is provided in the file `reduce-ide.texinfo'.
  291. Commands are aware of REDUCE syntax, and syntax-directed commands
  292. ignore comments, strings and character case. Standard indentation and
  293. comment commands are supported. Modelled primarily on Lisp mode;
  294. comment commands follow Lisp conventions.
  295. `<< ... >>' and `begin ... end' are treated as bracketed or
  296. ``symbolic'' expressions for motion, delimiter matching, etc.
  297. The command `reduce-indent-line' (`\\[reduce-indent-line]') indents in a fixed style (mine!).
  298. If re-run immediately after itself or `reindent-then-newline-and-indent'
  299. \(`\\[reindent-then-newline-and-indent]') or `newline-and-indent' (`\\[newline-and-indent]') it indents further.
  300. The indentation increment is the value of the variable `reduce-indentation'.
  301. Structure template commands are provided to insert and indent
  302. if-then (`\\[reduce-insert-if-then]'), block (`\\[reduce-insert-block]') and group (`\\[reduce-insert-group]') constructs,
  303. the latter optionally on a single line.
  304. The command `reduce-complete-symbol' (`\\[reduce-complete-symbol]') performs REDUCE
  305. keyword/phrase/structure completion.
  306. Text highlighting is supported via the command `font-lock-mode', and
  307. the style of highlighting may be controlled by setting
  308. `font-lock-maximum-decoration' to one of:
  309. 0, nil : basic keyword highlighting -- the default;
  310. 1 : algebraic-mode highlighting;
  311. 2 : symbolic-mode highlighting;
  312. 3, t : full highlighting -- of almost everything!
  313. Highlighting may also be controlled using the REDUCE menu.
  314. Delete converts tabs to spaces as it moves back.
  315. Blank lines separate paragraphs. Percent signs start comments.
  316. REDUCE mode defines the following local key bindings:
  317. \\{reduce-mode-map}
  318. Entry to this mode calls the value of `reduce-mode-hook' if non-nil."
  319. (interactive)
  320. (kill-all-local-variables)
  321. (use-local-map reduce-mode-map)
  322. (setq major-mode 'reduce-mode)
  323. (setq mode-name "REDUCE")
  324. (reduce-mode-variables)
  325. ;; Set up font-lock mode - variables automatically buffer-local:
  326. (setq font-lock-defaults
  327. ;; reduce-font-lock-keywords evaluates to a list of symbols!
  328. (list reduce-font-lock-keywords ; KEYWORDS
  329. nil ; KEYWORDS-ONLY
  330. t ; CASE-FOLD
  331. nil ; SYNTAX-ALIST
  332. (cons ; (VARIABLE . VALUE) ...
  333. 'font-lock-syntactic-keywords ; obsolete since 24.1! Use
  334. ; syntax-propertize-function
  335. ; instead!
  336. reduce-font-lock-syntactic-keywords)
  337. ))
  338. (reduce-font-lock-level) ; for font-lock menu
  339. (setq font-lock-multiline t) ; for comment statements
  340. ;; Additional support for comment statements:
  341. (add-to-list 'font-lock-extend-region-functions
  342. #'reduce-font-lock-extend-region-for-comment-statement)
  343. ;; Make all parsing respect the syntax property set by the above
  344. ;; font-lock option (which is essential to parse "...!"):
  345. (set (make-local-variable 'parse-sexp-lookup-properties) t)
  346. ;; Optionally turn on REDUCE minor modes:
  347. (when reduce-show-delim-mode-on
  348. (require 'reduce-delim)
  349. (reduce-show-delim-mode))
  350. (if reduce-auto-indent-mode (reduce-auto-indent-mode t))
  351. ;; For reduce-show-proc-mode:
  352. (set (make-local-variable 'which-func-mode) nil)
  353. (set (make-local-variable 'which-func-format) 'reduce-show-proc-string)
  354. (if reduce-show-proc-mode (reduce-show-proc-mode t))
  355. ;; This seems to be obsolete in Emacs 26!
  356. ;; Experimental support for outline minor mode (cf. lisp-mode.el)
  357. ;; `outline-regexp' must match `heading' from beginning of line;
  358. ;; length of match determines level:
  359. ;; (set (make-local-variable 'outline-regexp) "[^ \t\n]")
  360. ;; Imenu support:
  361. (set (make-local-variable 'imenu-generic-expression)
  362. ;; `make-local-variable' in case imenu not yet loaded!
  363. reduce-imenu-generic-expression)
  364. (set (make-local-variable 'imenu-space-replacement) " ")
  365. ;; Necessary to avoid re-installing the same imenu:
  366. (setq reduce-imenu-done nil)
  367. (if reduce-imenu (reduce-imenu-add-to-menubar))
  368. ;; ChangeLog support:
  369. (set (make-local-variable 'add-log-current-defun-function)
  370. 'reduce-current-proc)
  371. (run-hooks 'reduce-mode-hook))
  372. (defun reduce-mode-variables ()
  373. "Define REDUCE mode local variables."
  374. (set-syntax-table reduce-mode-syntax-table)
  375. ;; (set (make-local-variable 'paragraph-start)
  376. ;; (concat "^$\\|" page-delimiter))
  377. (set (make-local-variable 'paragraph-separate)
  378. ;; paragraph-start)
  379. (concat paragraph-start "\\|^%")) ; RS
  380. ;; so that comments at beginning of a line do not disturb reformatting.
  381. (set (make-local-variable 'paragraph-ignore-fill-prefix) t)
  382. (set (make-local-variable 'indent-line-function) 'reduce-indent-line)
  383. (set (make-local-variable 'comment-start) "% ")
  384. (set (make-local-variable 'comment-start-skip)
  385. "\\(^\\|[^!]\\)%+ *") ; "%+ *" but not !%
  386. (set (make-local-variable 'comment-column) 40)
  387. (set (make-local-variable 'comment-indent-function)
  388. 'reduce-comment-indent)
  389. ;; (setq fill-prefix "% ") ; buffer local
  390. (set (make-local-variable 'parse-sexp-ignore-comments) t) ; RS
  391. )
  392. (defun reduce-imenu-add-to-menubar (&optional redraw)
  393. "Add \"Contents\" menu to menubar; if REDRAW force update."
  394. (interactive)
  395. (if reduce-imenu-done
  396. ;; This is PRIMARILY to avoid a bug in imenu-add-to-menubar that
  397. ;; causes it to corrupt the menu bar if it is run more than once
  398. ;; in the same buffer.
  399. ()
  400. (setq reduce-imenu-done t)
  401. (imenu-add-to-menubar reduce-imenu-title)
  402. (if redraw (force-mode-line-update))))
  403. ;;;; **********************
  404. ;;;; Keyboard and menu maps
  405. ;;;; **********************
  406. (if reduce-mode-map ()
  407. (let ((map (make-sparse-keymap)))
  408. ;; (define-key map ">" 'reduce-self-insert-and-blink-matching-group-open)
  409. ;; (define-key map "\t" 'reduce-indent-line)
  410. (define-key map "\n" 'reindent-then-newline-and-indent)
  411. (define-key map "\C-c\t" 'reduce-unindent-line) ; default
  412. (define-key map [(shift tab)] 'reduce-unindent-line) ; backtab
  413. (define-key map "\177" 'backward-delete-char-untabify) ; DEL
  414. (define-key map "\C-c\C-n" 'reduce-forward-statement)
  415. (define-key map "\C-c\C-p" 'reduce-backward-statement)
  416. (define-key map "\C-c\C-d" 'reduce-down-block-or-group)
  417. (define-key map "\C-c\C-u" 'reduce-up-block-or-group)
  418. (define-key map "\C-c\C-k" 'reduce-kill-statement)
  419. (define-key map "\e\C-f" 'reduce-forward-sexp)
  420. (define-key map "\e\C-b" 'reduce-backward-sexp)
  421. (define-key map "\e\C-e" 'reduce-forward-procedure)
  422. (define-key map "\e\C-a" 'reduce-backward-procedure)
  423. (define-key map "\e\C-h" 'reduce-mark-procedure)
  424. (define-key map "\C-xnd" 'reduce-narrow-to-procedure)
  425. (define-key map "\C-ck" 'reduce-kill-procedure)
  426. ;; (define-key map "\e;" 'reduce-indent-comment) ; via global map
  427. (define-key map "\e\C-\\" 'reduce-indent-region)
  428. (define-key map "\e\C-q" 'reduce-indent-procedure)
  429. (define-key map "\C-c;" 'reduce-comment-region)
  430. (define-key map "\C-c:" 'reduce-comment-procedure)
  431. (define-key map "\eq" 'reduce-fill-comment)
  432. (define-key map "\C-ci" 'reduce-insert-if-then)
  433. (define-key map "\C-cb" 'reduce-insert-block)
  434. (define-key map "\C-c<" 'reduce-insert-group)
  435. (define-key map "\e\C-l" 'reduce-reposition-window)
  436. (define-key map "\e\t" 'reduce-complete-symbol)
  437. (setq reduce-mode-map map)))
  438. ;; REDUCE-mode menu bar and pop-up menu
  439. (easy-menu-define ; (symbol maps doc menu)
  440. reduce-mode-menu
  441. reduce-mode-map
  442. "REDUCE Mode Menu"
  443. `("REDUCE"
  444. ["Indent Line" indent-for-tab-command :active t
  445. :help "Re-indent the current line"]
  446. ["Unindent Line" reduce-unindent-line :active t
  447. :help "Unindent the current line by one indentation step"]
  448. ["Kill Statement" reduce-kill-statement :active t
  449. :help "Kill to the end of the current statement"]
  450. ["Fill Comment" reduce-fill-comment :active t
  451. :help "Fill the current comment"]
  452. ["(Un)Comment Region" reduce-comment-region :active mark-active
  453. :help "Toggle the commenting of the current region"]
  454. ;; "-- PROCEDURES --" ; not good in ntemacs
  455. "--"
  456. ["Forward Procedure" reduce-forward-procedure :active t
  457. :help "Move forward to the nearest end of a procedure"]
  458. ["Backward Procedure" reduce-backward-procedure :active t
  459. :help "Move backward to the nearest start of a procedure"]
  460. ["Indent Procedure" reduce-indent-procedure :active t
  461. :help "Re-indent the current procedure"]
  462. ["Mark Procedure" reduce-mark-procedure :active t
  463. :help "Mark the current procedure"]
  464. ["Reposition Window" reduce-reposition-window :active t
  465. :help "Scroll to show the current procedure optimally"]
  466. ["Narrow To Procedure" reduce-narrow-to-procedure :active t
  467. :help "Narrow the buffer to the current procedure"]
  468. ["(Un)Comment Proc" reduce-comment-procedure :active t
  469. :help "Toggle the commenting of the current procedure"]
  470. ["Kill Procedure" reduce-kill-procedure :active t
  471. :help "Kill the current procedure"]
  472. "--"
  473. ("Show / Find / Tag"
  474. ["Show Current Proc" reduce-show-proc-mode
  475. :style toggle :selected reduce-show-proc-mode :active t
  476. :help "Toggle display of the current procedure name"]
  477. ["Make Proc/Op Menu" (reduce-imenu-add-to-menubar t) :active (not reduce-imenu-done)
  478. :help "Show an imenu of procedures and operators"]
  479. "--"
  480. ["Find Tag..." xref-find-definitions :active t
  481. :help "Find a procedure definition using a tag file"]
  482. ["New TAGS Table..." visit-tags-table :active t
  483. :help "Select a new tag file"]
  484. "--"
  485. ["Tag Directory..." reduce-tagify-dir :active t
  486. :help "Tag REDUCE files in this directory"]
  487. ["Tag Dir & Subdirs..." reduce-tagify-dir-recursively :active t
  488. :help "Tag all REDUCE files under this directory"]
  489. )
  490. ;; "-- TEMPLATES --" ; not good in ntemacs
  491. "--"
  492. ["Insert If-Then" reduce-insert-if-then :active t
  493. :help "Insert an `if-then' template"]
  494. ["Insert Block" reduce-insert-block :active t
  495. :help "Insert a `block' template"]
  496. ["Insert Group" reduce-insert-group :active t
  497. :help "Insert a `group' template"]
  498. "--"
  499. ["Indent Region" reduce-indent-region :active mark-active
  500. :help "Re-indent the current region"]
  501. ["Indent Buffer" (reduce-indent-region (point-min) (point-max))
  502. :keys "C-u M-C-\\" :active t
  503. :help "Re-indent the current buffer"]
  504. "--"
  505. ["Command Mini Help" (apropos-command "reduce") :active t
  506. :help "Show a REDUCE Mode command summary"]
  507. ["Customize..." (customize-group 'reduce) :active t
  508. :help "Customize REDUCE Mode"]
  509. ["Show Version" reduce-mode-show-version :active t
  510. :help "Show the REDUCE Mode version"]
  511. ;; This seems to be obsolete in Emacs 26!
  512. ;; ["Outline" outline-minor-mode
  513. ;; :style toggle :selected outline-minor-mode :active t
  514. ;; :help "Toggle outline minor mode"]
  515. ["Update ChangeLog" add-change-log-entry-other-window :active t
  516. :help "Add change log entry other window"]
  517. ))
  518. (defun reduce-mode-show-version ()
  519. "Echo version information for REDUCE Major Mode."
  520. (interactive)
  521. (message "REDUCE Major Mode version: %s" reduce-mode-version))
  522. ;;;; ************
  523. ;;;; Syntax table
  524. ;;;; ************
  525. (if reduce-mode-syntax-table ()
  526. (let ((table (make-syntax-table)))
  527. (modify-syntax-entry ?_ "_" table)
  528. (modify-syntax-entry ?! "/" table) ; single character quote
  529. (modify-syntax-entry ?\\ "." table)
  530. (modify-syntax-entry ?{ "(}" table)
  531. (modify-syntax-entry ?} "){" table)
  532. (modify-syntax-entry ?\( "()" table)
  533. (modify-syntax-entry ?\) ")(" table)
  534. (modify-syntax-entry ?\[ "(]" table)
  535. (modify-syntax-entry ?\] ")[" table)
  536. (modify-syntax-entry ?< "." table)
  537. (modify-syntax-entry ?> "." table)
  538. (modify-syntax-entry ?* "." table)
  539. (modify-syntax-entry ?/ "." table)
  540. (modify-syntax-entry ?+ "." table)
  541. (modify-syntax-entry ?- "." table)
  542. (modify-syntax-entry ?= "." table)
  543. (modify-syntax-entry ?% "<" table)
  544. (modify-syntax-entry ?\n ">" table)
  545. (modify-syntax-entry ?& "." table)
  546. (modify-syntax-entry ?| "." table)
  547. (modify-syntax-entry ?' "'" table)
  548. (modify-syntax-entry ?\" "\"" table)
  549. (modify-syntax-entry ?$ "." table) ; RS
  550. (setq reduce-mode-syntax-table table))
  551. )
  552. ;;;; ********************
  553. ;;;; Indentation commands
  554. ;;;; ********************
  555. (defun reduce-indent-line (&optional arg)
  556. "Indent current line; if ARG indent whole statement rigidly.
  557. Indents to fixed style determined by current and previous non-blank line.
  558. Subsequent consecutive calls indent additionally by `reduce-indentation'
  559. unless `reduce-indent-line-conservative' is non-nil. With argument,
  560. indent any additional lines of the same statement rigidly together with
  561. this one."
  562. (interactive "*P") ; error if buffer read-only
  563. (let ((start-marker (point-marker))
  564. (indentation (progn (back-to-indentation) (current-column)))
  565. new-indent)
  566. (if (and (memq this-command
  567. '(reduce-indent-line indent-for-tab-command))
  568. (memq last-command
  569. (list 'reduce-indent-line 'indent-for-tab-command
  570. 'newline-and-indent
  571. 'reindent-then-newline-and-indent))
  572. (not reduce-indent-line-conservative)) ; TS
  573. (indent-to 0 reduce-indentation)
  574. (if (< (setq new-indent (reduce-calculate-indent)) indentation)
  575. (delete-horizontal-space))
  576. (indent-to new-indent))
  577. (if arg
  578. (save-excursion
  579. (setq indentation (- (current-column) indentation))
  580. (indent-rigidly
  581. (point) (progn (reduce-forward-statement 1) (point)) indentation)
  582. ))
  583. (if (< (point) start-marker) (goto-char start-marker))
  584. (set-marker start-marker nil)
  585. ))
  586. (defun reduce-calculate-indent ()
  587. "Return appropriate indentation for current line as REDUCE code."
  588. (let ((case-fold-search t))
  589. (or (reduce-calculate-indent-proc)
  590. (reduce-calculate-indent-this)
  591. (reduce-calculate-indent-prev))))
  592. (defconst procedure-regexp "\\(?:^\\|\\s-+\\|[;$]\\)procedure\\s-+[![:alpha:]]"
  593. "Regexp for use in a SEARCH to find a procedure header.")
  594. (defsubst looking-at-procedure ()
  595. "Return t if text after point matches the start of a procedure."
  596. (looking-at ".*\\<procedure\\s-+[![:alpha:]]"))
  597. (defun reduce-calculate-indent-proc ()
  598. ;; "Handle comment lines, or if immediately following a procedure body
  599. ;; then return 0, otherwise return nil."
  600. "Return 0 if immediately following procedure body, else return nil."
  601. (save-excursion
  602. (beginning-of-line)
  603. (cond
  604. ;; ((looking-at "[ \t]*%")
  605. ;; (back-to-indentation) (reduce-comment-indent))
  606. ;; ((and (re-search-backward "[;$][ \t\n]*\n" nil t) ; ))
  607. ((progn
  608. ;; Find previous line that is neither blank nor a comment:
  609. (while (and (= (forward-line -1) 0)
  610. (looking-at "[ \t\f]*[%\n]")) )
  611. ;; Does it end with a separator?
  612. (and (looking-at ".*[;$][ \t]*[%\n]")
  613. ;; Is it the end of a procedure?
  614. (progn (end-of-line)
  615. (= (reduce-backward-statement 2) 0))
  616. (looking-at-procedure)))
  617. 0)
  618. )))
  619. (defun reduce-calculate-indent-this ()
  620. "Handle current line BEGINNING with a special token.
  621. For an opening token (`begin' or `<<') normally return the indentation of
  622. the previous non-blank line; for an intermediate token (`then' or `else')
  623. return the indentation of the beginning of the statement; for a
  624. closing token (`end' or `>>') return the indentation of the beginning
  625. of the construct; otherwise return nil."
  626. (save-excursion
  627. (back-to-indentation)
  628. (cond
  629. ;; *** Opening tokens *** :
  630. ((looking-at "[({ \t]*\\(\\<begin\\>\\|<<\\)")
  631. ;; Find previous non-blank line:
  632. (let ((closed (looking-at ".*\\(\\<end\\>\\|>>\\)")))
  633. (skip-syntax-backward " >") ; whitespace, endcomment
  634. (if (looking-at "[;$]")
  635. (reduce-backward-statement 1)
  636. (back-to-indentation))
  637. (if (or (looking-at-procedure)
  638. (and
  639. (or closed ; single-line construct
  640. (looking-at "\\w+[ \t]*:=")) ; assignment
  641. (not (looking-at ".*[;$]")))) ; not completed
  642. (+ (current-column) reduce-indentation)
  643. (current-column))
  644. ))
  645. ((looking-at "\\w+[ \t]*:[^=]") ; label
  646. ;; Indent to beginning of enclosing block:
  647. (reduce-backward-block) (current-column))
  648. ;; *** Intermediate tokens *** :
  649. ((looking-at "\\<then\\>\\|\\<else\\>")
  650. (reduce-find-matching-if) (current-indentation))
  651. ;; *** Closing tokens *** :
  652. ((looking-at "\\<end\\>")
  653. (reduce-backward-block) (current-indentation))
  654. ((looking-at ">>")
  655. (reduce-backward-group) (current-indentation))
  656. ;; ((looking-at "#\\<endif\\>")
  657. ;; (reduce-backward-group) 0)
  658. ((looking-at "#\\(\\<define\\>\\|\\<if\\>\\|\\<\\elif\\>\\|\\<\\else\\>\\|\\<endif\\>\\)")
  659. 0))))
  660. (defun reduce-find-matching-if ()
  661. "Find the `if' matching a `then' or `else'."
  662. ;; Must skip groups, blocks and brackets.
  663. ;; Detects a missing `if' as early as possible as an unrecoverable error.
  664. (let ((pattern "\\<\\(if\\|else\\|end\\|begin\\)\\>\\|>>\\|\\s)\\|<<\\|\\s(\\|[^!][;$]"))
  665. (or (and
  666. (reduce-re-search-backward pattern)
  667. (cond
  668. ((looking-at "if")) ; found it -- return t
  669. ((looking-at "else") ; nested conditional
  670. (reduce-find-matching-if) (reduce-find-matching-if))
  671. ((= (following-char) ?>) ; end of group
  672. (reduce-backward-group) (reduce-find-matching-if))
  673. ((looking-at "end") ; end of block
  674. (reduce-backward-block) (reduce-find-matching-if))
  675. ((= (char-syntax (following-char)) ?\) )
  676. (forward-char) (backward-list) ; skip balanced brackets
  677. (reduce-find-matching-if))))
  678. ;; begin, <<, opening bracket, `;', `$' or beginning of buffer
  679. (error "`if' matching `then' or `else' not found"))
  680. ))
  681. (defun reduce-calculate-indent-prev ()
  682. "Return indentation based on previous non-blank line."
  683. ;; Should comments be ignored, esp. if they begin the line?
  684. ;; e.g. they may indicate a commented-out region!
  685. (save-excursion
  686. (beginning-of-line)
  687. (if (bobp)
  688. 0 ; no previous line
  689. ;; Find previous line that is neither blank nor a comment
  690. ;; beginning in the first column (which may represent
  691. ;; commented-out code):
  692. (while (and (= (forward-line -1) 0)
  693. (looking-at "%\\|[ \t\f]*$")) )
  694. (if (and (bobp) (looking-at "[ \t\f]*$"))
  695. 0 ; no previous non-blank line
  696. (back-to-indentation)
  697. ;; Point is now at first text in the previous non-blank line.
  698. (let ((previous-indentation (current-column))
  699. extra-indentation)
  700. ;; Skip any label:
  701. (when (looking-at "^\\(\\w+[ \t]*:\\)[^=]") ; label
  702. (goto-char (match-end 1))
  703. (skip-chars-forward "[ \t]")
  704. (if (eolp) ; label alone on line
  705. (setq extra-indentation reduce-indentation)
  706. (setq previous-indentation (current-column))))
  707. ;; Point is now at start of statement text in the previous
  708. ;; non-blank line.
  709. (or extra-indentation
  710. (setq extra-indentation
  711. (cond
  712. ;; *** Tokens at beginning of the line *** :
  713. ((looking-at "%") 0) ; % comment (HANDLE THIS FIRST!)
  714. ; ((looking-at "\\w+[ \t]*:[^=]") ; label
  715. ; (if (looking-at ".*\\<if\\>") ; what else?
  716. ; (* 2 reduce-indentation)
  717. ; reduce-indentation))
  718. ;; *** Tokens anywhere in the line *** :
  719. ((or (looking-at-procedure)
  720. (and (looking-at ".*\\<begin\\>")
  721. (not (looking-at ".*\\<end\\>")))
  722. (and (looking-at ".*<<") (not (looking-at ".*>>"))))
  723. (if (looking-at ".*,[ \t]*[%\n]") ; line ends with ,
  724. (* 2 reduce-indentation)
  725. reduce-indentation))
  726. ;; *** Tokens at the end of the (logical) line *** :
  727. ((looking-at ".*\\<\\(if\\|for\\|do\\|collect\\|join\\|sum\\product\\)\\>[ \t]*[%\n]")
  728. reduce-indentation)
  729. ;; Otherwise, extra indentation undefined
  730. )))
  731. (cond
  732. ((looking-at "#\\<endif\\>")
  733. (current-indentation))
  734. ((looking-at "#\\(\\<define\\>\\|\\<if\\>\\|\\<\\elif\\>\\|\\<\\else\\>\\)")
  735. (current-indentation))
  736. ;; If extra indentation determined then use it ...
  737. (extra-indentation (+ previous-indentation extra-indentation))
  738. ;; If beginning new statement or comma-separated element
  739. ;; then indent to previous statement or element
  740. ;; unless it is a first argument ...
  741. ((reduce-calculate-indent-prev1))
  742. ; This produces very odd results if the group is preceded by indented code:
  743. ; ((and (looking-at ".*<<") (not (looking-at ".*>>")))
  744. ; (reduce-backward-statement 1)
  745. ; (back-to-indentation)
  746. ; (+ (current-column) reduce-indentation))
  747. ;; If continuing `if' then indent relative to the `if' ...
  748. ; ((looking-at ".*\\(\\<then\\>\\|\\<else\\>\\)[ \t]*[%\n]")
  749. ; (if (looking-at ".*\\<if\\>")
  750. ; ()
  751. ; (goto-char (match-beginning 1))
  752. ; (reduce-find-matching-if))
  753. ; (+ (current-indentation) reduce-indentation))
  754. ;; ... but the `if' must be embedded ...
  755. ((looking-at ".+\\<if\\>.*\\(\\<then\\>\\|\\<else\\>\\)[ \t]*[%\n]")
  756. (goto-char (match-beginning 1))
  757. (reduce-find-matching-if)
  758. (+ (current-indentation) reduce-indentation))
  759. ;; Otherwise continuing previous line, so ...
  760. (t (+ previous-indentation reduce-indentation))
  761. ))))))
  762. (defun reduce-calculate-indent-prev1 ()
  763. "Sub-function of `reduce-calculate-indent-prev'.
  764. If beginning new statement or comma-separated element or
  765. sub-expression ending with `+', `-', `or' or `and' then indent to
  766. previous statement or element unless it is a first argument ..."
  767. (if (looking-at ".*\\(\\([,+-]\\|\\<or\\|\\<and\\)\\|[\;$]\\)[ \t]*[%\n]")
  768. (let* ((second_arg (match-string 2))
  769. (open (or second_arg
  770. (not (looking-at
  771. ".*\\(\\<end\\>\\|>>\\)[\;$][ \t]*[%\n]")))))
  772. (end-of-line)
  773. (reduce-backward-statement 1)
  774. (if second_arg
  775. (setq second_arg
  776. (save-excursion
  777. (reduce-re-search-backward "[^ \t\f\n]")
  778. (not (looking-at "\\(,\\|\\s(\\)[ \t]*[%\n]"))
  779. )))
  780. (back-to-indentation)
  781. (if (or second_arg
  782. (and open
  783. (looking-at
  784. ;; ... procedure / begin, << / label
  785. ".*\\<procedure\\>\
  786. \\|\\<begin\\>\\|<<\
  787. \\|\\w+[ \t]*:[^=]")) ; ???
  788. (looking-at "\\w+[ \t]*:[^=]")) ; label
  789. (+ (current-column) reduce-indentation)
  790. (current-column)))))
  791. (defun reduce-unindent-line (arg)
  792. "Unindent current line; if ARG indent whole statement rigidly.
  793. Delete `reduce-indentation' spaces from beginning of line.
  794. With argument, unindent any additional lines of the same statement
  795. rigidly along with this one."
  796. (interactive "*P") ; error if buffer read-only
  797. (let ((start-marker (point-marker))
  798. (indentation (progn (back-to-indentation) (current-column))))
  799. (if (bolp)
  800. ()
  801. (backward-delete-char-untabify reduce-indentation)
  802. (if arg
  803. (save-excursion
  804. (setq indentation (- (current-column) indentation))
  805. (indent-rigidly
  806. (point) (progn (reduce-forward-statement 1) (point)) indentation)
  807. ))
  808. (if (< (point) start-marker) (goto-char start-marker))
  809. (set-marker start-marker nil)
  810. )))
  811. (defun reduce-comment-indent ()
  812. "Value of `comment-indent-function'."
  813. ;; Called only by indent-for-comment and
  814. ;; (hence) indent-new-comment-line.
  815. (if (looking-at "%%%")
  816. (current-column)
  817. (if (looking-at "%%")
  818. (reduce-calculate-indent)
  819. (skip-chars-backward " \t")
  820. ;; (bolp) needed by indent-new-comment-line:
  821. (max (if (bolp) 0 (1+ (current-column))) comment-column)
  822. )))
  823. (defun reduce-indent-procedure (arg)
  824. "Indent this and following ARG procedures.
  825. Indent the procedure (and trailing white space) ending after point.
  826. With arg, indent the following arg procedures including this one."
  827. (interactive "*p") ; error if buffer read-only
  828. (save-excursion
  829. (if (reduce-mark-procedure arg)
  830. ;; Leaves mark at end of procedure, point at start.
  831. (reduce-indent-region (point) (mark))
  832. )))
  833. (defun reduce-indent-region (beg-region end-region)
  834. "Interactively indent region; otherwise BEG-REGION to END-REGION.
  835. Interactively with prefix arg, indent the whole buffer."
  836. ;; (interactive "*r") ; error if buffer read-only
  837. (interactive
  838. (if current-prefix-arg
  839. (list (point-min) (point-max))
  840. (list (region-beginning) (region-end))))
  841. ;; Indent lines between beg-region and end-region
  842. ;; and return point to where it started.
  843. ;; This version is not very efficient.
  844. (message "Indenting ...")
  845. (let ((end-region-mark (make-marker)) (save-point (point-marker)))
  846. ;; Must use markers so that they move with the text.
  847. (set-marker end-region-mark end-region)
  848. (goto-char beg-region)
  849. (while (< (point) end-region-mark)
  850. (reduce-indent-line)
  851. ;; Strip trailing white space from lines
  852. (end-of-line) (delete-horizontal-space)
  853. (forward-line))
  854. (goto-char save-point)
  855. (set-marker end-region-mark nil)
  856. (set-marker save-point nil))
  857. (message "Indenting ... done"))
  858. ;;;; ******************************************************
  859. ;;;; Support for automatic re-indentation of specific lines
  860. ;;;; ******************************************************
  861. (defvar reduce-auto-indent-idle-timer nil)
  862. (defun reduce-auto-indent-mode (&optional arg)
  863. "Toggle REDUCE Auto Indent mode.
  864. With prefix ARG, turn mode on if and only if ARG is positive.
  865. Returns the new status of REDUCE Auto Indent mode (non-nil means on).
  866. When REDUCE Auto Indent mode is enabled, after
  867. `reduce-auto-indent-delay' seconds of Emacs idle time re-indent the
  868. current line if the text just typed matches `reduce-auto-indent-regexp'."
  869. (interactive "P")
  870. (let ((on-p (if arg
  871. (> (prefix-numeric-value arg) 0)
  872. (not reduce-auto-indent-mode))))
  873. (if reduce-auto-indent-idle-timer
  874. (cancel-timer reduce-auto-indent-idle-timer))
  875. (if on-p
  876. (setq reduce-auto-indent-idle-timer
  877. (run-with-idle-timer reduce-auto-indent-delay t
  878. 'reduce-auto-indent-function)))
  879. (setq reduce-auto-indent-mode on-p)
  880. (reduce-auto-indent-function)))
  881. (defun reduce-auto-indent-function ()
  882. "Re-indent current line if match with `reduce-auto-indent-regexp' just typed."
  883. (and (eq major-mode 'reduce-mode)
  884. (eq last-command 'self-insert-command)
  885. (save-excursion
  886. (save-match-data
  887. (if (re-search-backward reduce-auto-indent-regexp nil t)
  888. (reduce-indent-line))
  889. ))))
  890. ;;;; ******************************
  891. ;;;; Operations based on procedures
  892. ;;;; ******************************
  893. (defun reduce-backward-procedure (arg)
  894. "Move backward to next start of procedure. With ARG, do it ARG times."
  895. (interactive "p")
  896. (let ((case-fold-search t) (count arg))
  897. (while (and (> count 0) (reduce-re-search-backward procedure-regexp))
  898. (setq count (1- count)))
  899. (if (= count arg)
  900. ()
  901. ;; (reduce-backward-statement 1) ; overkill? Instead ...
  902. ;; Find preceding "%", ";", "$", "(" or beginning of buffer:
  903. (while (progn (skip-chars-backward "^%;$(")
  904. (and (not (bobp))
  905. (not (backward-char 1))
  906. (= (preceding-char) ?!))))
  907. ;; If in %-comment then skip to its end:
  908. (if (reduce-back-to-percent-comment-start) (end-of-line))
  909. ;; Find actual start of procedure statement:
  910. (if (reduce-re-search-forward "[a-zA-Z]") (backward-char 1))
  911. )))
  912. (defun reduce-forward-procedure (arg)
  913. "Move forward to next end of procedure. With ARG, do it ARG times."
  914. (interactive "p")
  915. (let ((case-fold-search t) (start (point)) count)
  916. ;; Move to end of procedure starting before point:
  917. (if (reduce-re-search-backward procedure-regexp)
  918. (reduce-forward-statement 2))
  919. ;; Now move forward by arg or arg-1 procedures
  920. ;; or stay put if at least one move not possible
  921. (unless (<= (point) start)
  922. (setq arg (1- arg)) (setq start (point)))
  923. (setq count arg)
  924. (while (and (> count 0) (reduce-re-search-forward procedure-regexp))
  925. (setq count (1- count)))
  926. (if (< count arg)
  927. (reduce-forward-statement 2)
  928. (goto-char start)))
  929. ;; Skip white space and any following eol:
  930. (skip-chars-forward " \t")
  931. (if (= (following-char) ?\n) (forward-char)))
  932. (defun reduce-mark-procedure (arg)
  933. "Mark this and following ARG procedures.
  934. Put mark after next end of procedure, point at start of that procedure.
  935. Procedure ends AFTER any trailing white space.
  936. Return t is successful, nil otherwise."
  937. ;; Could be more efficient by hacking reduce-forward-procedure!
  938. (interactive "p")
  939. (let ((start (point)) transient-mark-mode)
  940. ;; Region must stay active, even if transient-mark-mode is on.
  941. (reduce-forward-procedure arg)
  942. (if (= (point) start)
  943. nil
  944. (skip-chars-forward " \t\n") ; skip trailing white space
  945. (push-mark start t) ; save original position QUIETLY
  946. (push-mark) ; mark end of procedure
  947. (reduce-backward-procedure arg)
  948. t)
  949. ))
  950. (defun reduce-kill-procedure ()
  951. "Kill the procedure (and trailing white space) ending after point."
  952. (interactive "*") ; error if buffer read-only
  953. (if (reduce-mark-procedure 1)
  954. (kill-region (region-beginning) (region-end))))
  955. (defun reduce-narrow-to-procedure (arg)
  956. ;; Based on `narrow-to-defun' in `lisp.el'.
  957. "Narrow to this and following ARG procedures.
  958. Make text outside current procedure invisible.
  959. The procedure visible is the one that contains point or follows point."
  960. (interactive "p")
  961. (save-excursion
  962. (widen)
  963. (reduce-forward-procedure arg)
  964. (let ((end (point)))
  965. (reduce-backward-procedure arg)
  966. (narrow-to-region (point) end))))
  967. ;;;; ******************************
  968. ;;;; Operations based on statements
  969. ;;;; ******************************
  970. (defvar reduce-up-tries 1
  971. "Repeat count of reduce-forward/backward-statement at end of block or group.")
  972. (defun reduce-up-block-or-group-maybe (found start)
  973. "Move over `<<', `begin', `>>' or `end' (including end-of-file marker)
  974. after reduce-max-up-tries repeated interactive attempts."
  975. (if (and found (= (point) start) (eq this-command last-command))
  976. (if (< reduce-up-tries reduce-max-up-tries)
  977. (setq reduce-up-tries (1+ reduce-up-tries))
  978. (setq reduce-up-tries 1)
  979. (goto-char found)
  980. (if (eq this-command 'reduce-forward-statement)
  981. ;; End of file marker needs special treatment:
  982. (progn
  983. (reduce-re-search-forward "[;$]" 'move)
  984. (if (reduce-re-search-forward "[^ \t\f\n]") (goto-char found)))
  985. ))
  986. (setq reduce-up-tries 1)))
  987. (defvar reduce-forward-statement-found nil
  988. "Free variable bound in `reduce-forward-statement'")
  989. ;; Consider replacing with lexical binding.
  990. (defun reduce-forward-statement (arg)
  991. "Move forward to end of statement. With ARG, do it ARG times.
  992. If looking at the end of a block or group, or the end-of-file marker,
  993. move over it after `reduce-max-up-tries' consecutive interactive tries."
  994. (interactive "p")
  995. (let ((case-fold-search t)
  996. (pattern "[;$]\\|>>\\|\\<end\\>\\|<<\\|\\<begin\\>\\|\\s(\\|\\s)")
  997. (start (point))
  998. reduce-forward-statement-found)
  999. ;; Skip an immediate closing bracket:
  1000. (if (looking-at "[ \t\n]*\\s)") (goto-char (match-end 0)))
  1001. (while (and (> arg 0) (reduce-forward-statement1 pattern))
  1002. (setq arg (1- arg)))
  1003. ;; Never move backwards:
  1004. (if (< (point) start) (goto-char start))
  1005. ;; Move over >> or end on repeated interactive attempt:
  1006. (reduce-up-block-or-group-maybe reduce-forward-statement-found start)))
  1007. (defun reduce-forward-statement1 (pattern)
  1008. "Move forward to next statement end and return t if successful."
  1009. (if (looking-at "[;$]")
  1010. ;; (forward-char 1)
  1011. (not (forward-char 1))
  1012. (if (reduce-re-search-forward pattern)
  1013. (cond
  1014. ((= (preceding-char) ?>)
  1015. (setq reduce-forward-statement-found (point))
  1016. (backward-char 2) (skip-chars-backward " \t\n") t)
  1017. ((memq (preceding-char) '(?d ?D))
  1018. (setq reduce-forward-statement-found (point))
  1019. (backward-char 3) (skip-chars-backward " \t\n") t)
  1020. ((= (preceding-char) ?<)
  1021. (reduce-forward-group) (reduce-forward-statement1 pattern))
  1022. ((memq (preceding-char) '(?n ?N))
  1023. (reduce-forward-block) (reduce-forward-statement1 pattern))
  1024. ((= (char-syntax (preceding-char)) ?\( )
  1025. (backward-char) (forward-list) ; skip balanced brackets
  1026. (reduce-forward-statement1 pattern))
  1027. ((= (char-syntax (preceding-char)) ?\) )
  1028. (if (save-excursion ; quoted list does not
  1029. (backward-list) ; contain REDUCE statements
  1030. (skip-chars-backward " \t\n")
  1031. (= (preceding-char) ?'))
  1032. (reduce-forward-statement1 pattern)
  1033. (backward-char) (skip-chars-backward " \t\n") t))
  1034. (t t))
  1035. )))
  1036. (defun reduce-backward-statement (arg)
  1037. "Move backward to start of statement. With ARG, do it ARG times.
  1038. If looking at the beginning of a block or group move over it after
  1039. `reduce-max-up-tries' consecutive interactive tries.
  1040. The end-of-file marker is treated as a statement.
  1041. Returns the count of statements left to move."
  1042. ;; Return count used by reduce-calculate-indent-proc.
  1043. (interactive "p")
  1044. (let ((case-fold-search t)
  1045. (pattern "[;$]\\|<<\\|\\<begin\\>\\|>>\\|\\<end\\>\\|\\s)\\|\\s(")
  1046. (start (point)) (found t)
  1047. ;; Check whether after end of file marker, ``end''.
  1048. ;; Assume it starts at the beginning of the line.
  1049. (not-eof (save-excursion
  1050. (or (reduce-re-search-forward "[^ \t\f\n]")
  1051. (not (progn
  1052. (reduce-re-search-backward "[^ \t\f\n]")
  1053. (beginning-of-line)
  1054. (looking-at "\\<end\\>")))
  1055. ))))
  1056. (if (and (reduce-re-search-backward "[^ \t\f\n]")
  1057. (not (or (memq (following-char) '(?\; ?$))
  1058. ;; Skip an immediate opening bracket:
  1059. (= (char-syntax (following-char)) ?\( ))))
  1060. (forward-char 1))
  1061. (while (and (> arg 0) found)
  1062. (setq found (reduce-backward-statement1 pattern not-eof))
  1063. (setq arg (1- arg)))
  1064. (if found
  1065. (cond ((= (following-char) ?<)
  1066. (setq found (point)) (forward-char 2))
  1067. ((memq (following-char) '(?b ?B))
  1068. (setq found (point)) (forward-char 5))
  1069. (t (forward-char 1))
  1070. ))
  1071. ;; Move to actual start of statement:
  1072. (reduce-re-search-forward "[^ \t\f\n]") (backward-char 1)
  1073. ;; Never move forwards:
  1074. (if (> (point) start) (goto-char start))
  1075. ;; Move over << or begin on repeated interactive attempt:
  1076. (reduce-up-block-or-group-maybe found start)
  1077. arg
  1078. ))
  1079. (defun reduce-backward-statement1 (pattern not-eof)
  1080. "Move backward to next statement beginning.
  1081. Return t if successful, nil if reaches beginning of buffer."
  1082. (if (reduce-re-search-backward pattern 'move)
  1083. (cond
  1084. ((= (following-char) ?>) ; end of group
  1085. (reduce-backward-group) (reduce-backward-statement1 pattern not-eof))
  1086. ((memq (following-char) '(?e ?E)) ; end of block (or file)
  1087. (if not-eof
  1088. (progn (reduce-backward-block) (setq not-eof nil)))
  1089. (reduce-backward-statement1 pattern not-eof))
  1090. ((= (char-syntax (following-char)) ?\) )
  1091. (forward-char) (backward-list) ; skip balanced brackets
  1092. (reduce-backward-statement1 pattern not-eof))
  1093. ((= (char-syntax (following-char)) ?\( )
  1094. (forward-char) (skip-chars-forward " \t\n") (backward-char) t)
  1095. (t t))
  1096. ))
  1097. (defun reduce-kill-statement (&optional arg)
  1098. "Kill the rest of the current statement or ARG statements from point.
  1099. If no nonblanks kill thru newline.
  1100. With prefix argument, kill that many statements from point.
  1101. Negative arguments kill complete statements backwards, cf. `kill-line'."
  1102. ;; Based on kill-line in simple.el
  1103. (interactive "*P") ; error if buffer read-only
  1104. (kill-region (point)
  1105. (progn
  1106. (if (and (null arg) (looking-at "[ \t]*$"))
  1107. (forward-line 1)
  1108. (setq arg (prefix-numeric-value arg))
  1109. (if (> arg 0)
  1110. (progn
  1111. (reduce-forward-statement arg)
  1112. (skip-chars-forward " \t")) ; 2 Oct 1994
  1113. (reduce-backward-statement (- 1 arg))))
  1114. (point))))
  1115. ;;;; ************************
  1116. ;;;; Moving by block or group
  1117. ;;;; ************************
  1118. (defun reduce-up-block-or-group (arg)
  1119. "Move backwards up one level of block or group; if ARG move forwards.
  1120. Move to beginning of nearest unpaired `begin' or `<<'.
  1121. A universal argument means move forwards
  1122. to end of nearest unpaired `end' or `>>'.
  1123. With a numeric argument, do it that many times, where a
  1124. negative argument means move forward instead of backward."
  1125. (interactive "P")
  1126. (let ((case-fold-search t))
  1127. (setq arg (reduce-prefix-numeric-value arg))
  1128. (while (and (not (= arg 0)) (reduce-up-block-or-group1 arg))
  1129. (setq arg (if (> arg 0) (1- arg) (1+ arg)))
  1130. )))
  1131. (defun reduce-up-block-or-group1 (arg)
  1132. "Sub-function of `reduce-up-block-or-group'."
  1133. (let ((start (point)))
  1134. (if (or
  1135. (and (> arg 0) (reduce-backward-block-or-group))
  1136. (and (< arg 0) (reduce-forward-block-or-group)))
  1137. t
  1138. (goto-char start) nil)))
  1139. (defun reduce-backward-block-or-group ()
  1140. "Move backward to beginning of block or group containing point."
  1141. (if (reduce-re-search-backward "\\<begin\\>\\|<<\\|\\<end\\>\\|>>")
  1142. (cond ((= (following-char) ?>)
  1143. (reduce-backward-group)
  1144. (reduce-backward-block-or-group))
  1145. ((memq (following-char) '(?e ?E))
  1146. (reduce-backward-block)
  1147. (reduce-backward-block-or-group))
  1148. (t t)
  1149. )))
  1150. (defun reduce-forward-block-or-group ()
  1151. "Move forward to end of block or group containing point."
  1152. (if (reduce-re-search-forward "\\<end\\>\\|>>\\|\\<begin\\>\\|<<")
  1153. (cond ((= (preceding-char) ?<)
  1154. (reduce-forward-group)
  1155. (reduce-forward-block-or-group))
  1156. ((memq (preceding-char) '(?n ?N))
  1157. (reduce-forward-block)
  1158. (reduce-forward-block-or-group))
  1159. (t t)
  1160. )))
  1161. (defun reduce-down-block-or-group (arg)
  1162. "Move forward down one level of block or group; if ARG move backwards.
  1163. Move to end of nearest unpaired `begin' or `<<'.
  1164. A universal argument means move backward
  1165. to beginning of nearest unpaired `end' or `>>'.
  1166. With a numeric argument, do it that many times, where a
  1167. negative argument means move backward instead of forward."
  1168. (interactive "P")
  1169. (let ((case-fold-search t))
  1170. (setq arg (reduce-prefix-numeric-value arg))
  1171. (while (and (not (= arg 0)) (reduce-down-block-or-group1 arg))
  1172. (setq arg (if (> arg 0) (1- arg) (1+ arg)))
  1173. )))
  1174. (defun reduce-down-block-or-group1 (arg)
  1175. "Sub-function of `reduce-down-block-or-group'."
  1176. (let ((start (point)))
  1177. (if
  1178. (if (> arg 0)
  1179. (and
  1180. (reduce-re-search-forward "<<\\|\\<begin\\>\\|>>\\|\\<end\\>")
  1181. (memq (preceding-char) '(?< ?n ?N)))
  1182. (and
  1183. (reduce-re-search-backward ">>\\|\\<end\\>\\|<<\\|\\<begin\\>")
  1184. (memq (following-char) '(?> ?e ?E)))
  1185. )
  1186. t
  1187. (goto-char start) nil)
  1188. ))
  1189. (defun reduce-prefix-numeric-value (arg)
  1190. "Interpret universal ARG as -1, otherwise apply `prefix-numeric-value'."
  1191. (if (and arg (listp arg)) -1 (prefix-numeric-value arg)))
  1192. (defun reduce-forward-block ()
  1193. "Move forwards to end of block containing point.
  1194. Return t if successful; otherwise move as far as possible and return nil."
  1195. (let (return)
  1196. (while (and (setq return (reduce-re-search-forward
  1197. "[^'\(]\\<end\\>\\|\\([^'\(]\\<begin\\>\\)" 'move))
  1198. (match-beginning 1))
  1199. (reduce-forward-block))
  1200. return))
  1201. ;; ***** Should reduce-backward-block also skip white space,which it
  1202. ;; ***** seems to do? This is a problem for reduce-show-delim-mode.
  1203. (defun reduce-backward-block ()
  1204. "Move backwards to start of block containing point.
  1205. Return t if successful; otherwise move as far as possible and return nil."
  1206. (let (return)
  1207. (while (and (setq return (reduce-re-search-backward
  1208. "[^'\(]\\<begin\\>\\|\\([^'\(]\\<end\\>\\)" 'move))
  1209. (match-beginning 1))
  1210. (reduce-backward-block))
  1211. return))
  1212. (defun reduce-forward-group ()
  1213. "Move forwards to end of group containing point.
  1214. Return t if successful; otherwise move as far as possible and return nil."
  1215. (let (return)
  1216. (while (and (setq return (reduce-re-search-forward ">>\\|<<" 'move))
  1217. (= (preceding-char) ?<))
  1218. (reduce-forward-group))
  1219. return))
  1220. (defun reduce-backward-group ()
  1221. "Move backwards to start of group containing point.
  1222. Return t if successful; otherwise move as far as possible and return nil."
  1223. (let (return)
  1224. (while (and (setq return (reduce-re-search-backward "<<\\|>>" 'move))
  1225. (= (following-char) ?>))
  1226. (reduce-backward-group))
  1227. return))
  1228. ;;;; *****************************************************************
  1229. ;;;; Searching for syntactic elements ignoring comments, strings, etc.
  1230. ;;;; *****************************************************************
  1231. (defun reduce-re-search-forward (regexp &optional MOVE)
  1232. "Syntactic search forwards for REGEXP; if no match and MOVE then move to end.
  1233. Skip comments, strings, escaped tokens, and quoted tokens other than `('.
  1234. Return t if match found, nil otherwise."
  1235. (let ((start (point))
  1236. (pattern (concat regexp "\\|%\\|\\<comment\\>"))
  1237. (move (if MOVE 'move t)))
  1238. (if (reduce-re-search-forward1 pattern move)
  1239. t
  1240. (if (not MOVE) (goto-char start))
  1241. nil)
  1242. ))
  1243. (defun reduce-re-search-forward1 (pattern move)
  1244. "Skip strings."
  1245. (if (reduce-re-search-forward2 pattern move)
  1246. (if (reduce-in-string) ; try again!
  1247. (reduce-re-search-forward1 pattern move)
  1248. t)
  1249. nil))
  1250. (defun reduce-re-search-forward2 (pattern move)
  1251. "Skip escaped, quoted or commented text."
  1252. (if (re-search-forward pattern nil move)
  1253. (let ((match-data (match-data))
  1254. before)
  1255. (if (> (match-beginning 0) 0)
  1256. (setq before (char-after (1- (match-beginning 0)))))
  1257. (cond
  1258. ((and before
  1259. (or (= before ?!) ; skip escaped text
  1260. (and (= before ?') ; skip quoted text except '(...)
  1261. (not (= (char-after (match-beginning 0)) ?\( )))))
  1262. (reduce-re-search-forward2 pattern move)) ; search again
  1263. ((= (preceding-char) ?%) ; skip % comment
  1264. (forward-line 1)
  1265. (reduce-re-search-forward2 pattern move)) ; search again
  1266. ((string-match "^comment$"
  1267. ;; otherwise might fortuitously match only
  1268. ;; the beginning of the string "comment"
  1269. (buffer-substring
  1270. (match-beginning 0) (match-end 0)) )
  1271. (re-search-forward "[^!][;$]" nil move) ; 'move ???
  1272. (reduce-re-search-forward2 pattern move)) ; search again
  1273. (t (store-match-data match-data) t))
  1274. )))
  1275. (defun reduce-re-search-backward (regexp &optional MOVE)
  1276. "Syntactic search backwards for REGEXP else if MOVE then move to start.
  1277. Skip REDUCE comments and strings. Return t if match found, nil otherwise."
  1278. (let ((start (point))
  1279. (move (if MOVE 'move t)))
  1280. (if (reduce-re-search-backward1 regexp move)
  1281. t
  1282. (if (not MOVE) (goto-char start))
  1283. nil)
  1284. ))
  1285. (defun reduce-re-search-backward1 (regexp move)
  1286. "Sub-function of `reduce-re-search-backward'.
  1287. Skip strings backwards."
  1288. (if (reduce-re-search-backward2 regexp move)
  1289. (if (reduce-in-string) ; try again!
  1290. (reduce-re-search-backward1 regexp move)
  1291. t)
  1292. nil))
  1293. (defun reduce-re-search-backward2 (regexp move)
  1294. "Skip escaped, quoted or commented text backwards."
  1295. (if (re-search-backward regexp nil move)
  1296. (let ((match-data (match-data)))
  1297. (if (or (= (preceding-char) ?!) ; escaped
  1298. (and (= (preceding-char) ?') ; quoted (maybe)
  1299. (not (= (char-after (- (point) 2)) ?!)))
  1300. (reduce-back-to-comment-start)) ; in comment
  1301. (reduce-re-search-backward2 regexp move) ; search again
  1302. ;; Restore finally accepted match data:
  1303. (store-match-data match-data)
  1304. t)
  1305. )))
  1306. (defun reduce-back-to-comment-start ()
  1307. "If point is in a comment then move to its start and return t.
  1308. Otherwise do not move and return nil."
  1309. (or
  1310. ;; Check whether in % comment:
  1311. (reduce-back-to-percent-comment-start)
  1312. ;; Check whether in comment statement:
  1313. (let ((start (point)) posn
  1314. (pattern "[^!][;$]\\|\\<comment\\>"))
  1315. (cond
  1316. ((setq posn (reduce-back-to-comment-statement-start pattern))
  1317. ;; in comment statement -- go to its true beginning
  1318. (goto-char posn) t)
  1319. (t (goto-char start) nil)) ; not in comment statement
  1320. )))
  1321. (defun reduce-back-to-comment-statement-start (pattern)
  1322. "Move backwards to the nearest `comment' keyword or separator.
  1323. If it is `comment' then return its start position; otherwise return nil."
  1324. (while (and (re-search-backward pattern nil 'move)
  1325. (reduce-back-to-percent-comment-start)))
  1326. (if (looking-at "comment") (point)))
  1327. (defun reduce-back-to-percent-comment-start ()
  1328. "If point is in a percent comment then move to its start and return t.
  1329. Otherwise do not move and return nil."
  1330. ;;; (re-search-backward
  1331. ;;; "^%\\|[^!]%" (save-excursion (beginning-of-line) (point)) t)
  1332. ;; Note that a % may appear at the end of, or alone on, a line!
  1333. (let ((start (point)))
  1334. (beginning-of-line)
  1335. (prog1
  1336. (re-search-forward "^%\\|[^!]%" (1+ start) 'move)
  1337. (backward-char)
  1338. )))
  1339. (defun reduce-in-string ()
  1340. "Return t if point is within a string, assuming no multi-line strings."
  1341. (let ((start (point)) (in-string nil))
  1342. (beginning-of-line)
  1343. (while (< (point) start)
  1344. (if (= (following-char) ?\")
  1345. (if in-string
  1346. ;; Cannot include a \" within a string
  1347. (setq in-string nil) ; found end of string
  1348. (if (not(= (preceding-char) ?!))
  1349. (setq in-string t)) ; found beginning of string
  1350. ))
  1351. (forward-char 1))
  1352. in-string))
  1353. ;;;; ****************
  1354. ;;;; Comment commands
  1355. ;;;; ****************
  1356. (defun reduce-comment-region (beg-region end-region arg)
  1357. "Comment/uncomment every line in region, from BEG-REGION to END-REGION.
  1358. With interactive ARG, comment if non-negative, uncomment if null
  1359. or negative (cf. minor modes).
  1360. Put `reduce-comment-region-string' at the beginning of every line in the region.
  1361. First two args specify the region boundaries, third arg is interactive."
  1362. ;; Taken almost directly from fortran.el
  1363. ;; by Michael D. Prange (prange@erl.mit.edu).
  1364. (interactive "*r\nP") ; error if buffer read-only
  1365. (let ((end-region-mark (make-marker)) (save-point (point-marker)))
  1366. (set-marker end-region-mark end-region)
  1367. (goto-char beg-region)
  1368. (beginning-of-line)
  1369. (if (if arg
  1370. (< (reduce-prefix-numeric-value arg) 0)
  1371. (looking-at "%")) ; FJW
  1372. ;; Uncomment the region:
  1373. (let ((com "%+ ?"))
  1374. (if (looking-at com)
  1375. (delete-region (point) (match-end 0)))
  1376. (while (and (= (forward-line 1) 0)
  1377. (< (point) end-region-mark))
  1378. (if (looking-at com)
  1379. (delete-region (point) (match-end 0)))))
  1380. ;; Comment the region:
  1381. (progn (insert reduce-comment-region-string)
  1382. (while (and (= (forward-line 1) 0)
  1383. (< (point) end-region-mark))
  1384. (insert reduce-comment-region-string)))
  1385. )
  1386. (goto-char save-point)
  1387. (set-marker end-region-mark nil)
  1388. (set-marker save-point nil)))
  1389. (defun reduce-comment-procedure (arg)
  1390. "Comment/uncomment every line of this procedure.
  1391. This procedure is the one that ends after point.
  1392. With interactive arg, if non-negative comment out procedure, if null
  1393. or negative uncomment all consecutive commented-out lines containing
  1394. or following point (cf. minor modes)."
  1395. (interactive "*P") ; error if buffer read-only
  1396. (save-excursion
  1397. (beginning-of-line)
  1398. (if (if arg
  1399. (< (reduce-prefix-numeric-value arg) 0)
  1400. (looking-at "%"))
  1401. (let (start) ; uncomment lines
  1402. (if (looking-at "%") ; necessary ???
  1403. (if (re-search-backward "^[^%]" nil t) (forward-line 1))
  1404. (re-search-forward "^%" nil t))
  1405. (setq start (point))
  1406. (re-search-forward "^[^%]" nil t)
  1407. (reduce-comment-region start (point) -1)) ; UNCOMMENT
  1408. (if (reduce-mark-procedure 1) ; comment out procedure
  1409. (progn ; first back up to real
  1410. (exchange-point-and-mark) ; end of procedure
  1411. (skip-chars-backward " \t\n")
  1412. (reduce-comment-region (region-beginning) (region-end) nil))))
  1413. ))
  1414. (defun reduce-fill-comment (justify)
  1415. "Fill %-comment or comment statement paragraph at or after point.
  1416. If JUSTIFY is non-nil (interactively, with prefix argument), justify as well."
  1417. (interactive "*P")
  1418. (save-excursion
  1419. (let (first)
  1420. ;; If in empty line then move to start of next non-empty line:
  1421. (beginning-of-line)
  1422. (while (and (looking-at "[ \t]*$")
  1423. (= (forward-line) 0)
  1424. (setq first (point))))
  1425. ;; Is point within a comment statement?
  1426. (if (or (and (looking-at "[ \t]*comment")
  1427. (setq first (point)))
  1428. ;; (See `reduce-font-lock-extend-region-for-comment-statement'.)
  1429. (save-excursion
  1430. (and (re-search-backward "\\(comment\\)\\|\\(;\\)" nil t)
  1431. (match-beginning 1)
  1432. (setq first (point)))))
  1433. ;; Yes -- use normal text-mode fill, but only within the
  1434. ;; comment statement, which might be within code:
  1435. (save-restriction
  1436. (narrow-to-region first (save-excursion (search-forward ";")))
  1437. (fill-paragraph justify))
  1438. ;;No...
  1439. ;; If point is in a %-comment then find its prefix and fill it:
  1440. (if (looking-at "[ \t]*%")
  1441. (let (fill-prefix last)
  1442. ;; Code modified from `set-fill-prefix' in fill.el.
  1443. (setq fill-prefix (buffer-substring
  1444. (point)
  1445. (progn (skip-chars-forward " \t%") (point))))
  1446. (if (equal fill-prefix "")
  1447. (setq fill-prefix nil))
  1448. ;; Find the last line of the comment:
  1449. (while (and (= (forward-line) 0)
  1450. (looking-at "[ \t]*%")))
  1451. (setq last (point))
  1452. ;; Move to the first line of the comment:
  1453. (if first
  1454. (goto-char first)
  1455. (while (and (= (forward-line -1) 0)
  1456. (looking-at "[ \t]*%")) )
  1457. ;; Might have reached BOB, so ...
  1458. (if (not (looking-at "[ \t]*%"))
  1459. (forward-line)))
  1460. ;; Fill region as one paragraph: break lines to fit fill-column.
  1461. (fill-region-as-paragraph (point) last justify)))))))
  1462. ;;;; ***************************
  1463. ;;;; Structure template commands
  1464. ;;;; ***************************
  1465. (defun reduce-insert-if-then (&optional else)
  1466. "Insert `if ... then'; if ELSE then include `else'.
  1467. Position point after `if'.
  1468. With argument include a correctly indented `else' on a second line."
  1469. (interactive "*P") ; error if buffer read-only
  1470. (insert "if ")
  1471. (let ((finish (point)))
  1472. (insert " then ")
  1473. (if else
  1474. (progn
  1475. (newline)
  1476. (insert "else ")
  1477. (reduce-indent-line)
  1478. ))
  1479. (goto-char finish)
  1480. ))
  1481. (defun reduce-insert-block (&optional nosplit)
  1482. "Insert and indent `begin ... end' block; if NOSPLIT then on same line.
  1483. Position point inside.
  1484. With argument put `begin' and `end' on the same line
  1485. \(see `reduce-insert-block-or-group')."
  1486. (interactive "*P") ; error if buffer read-only
  1487. (reduce-insert-block-or-group "begin" "end" t nosplit))
  1488. (defun reduce-insert-group (&optional nosplit)
  1489. "Insert and indent `<< >>' group; if NOSPLIT then on same line.
  1490. Position point inside.
  1491. With argument put `<<' and `>>' on the same line
  1492. \(see `reduce-insert-block-or-group')."
  1493. (interactive "*P") ; error if buffer read-only
  1494. (reduce-insert-block-or-group "<<" ">>" nil nosplit))
  1495. (defun reduce-insert-block-or-group (open close block nosplit)
  1496. "Insert and indent `open ... close' structure and position point inside.
  1497. If the mark is transient and active then enclose the region; otherwise
  1498. if point is not at the end of the line then enclose the rest of the line.
  1499. Leave the mark at the insertion point in the body of a block.
  1500. If `nosplit' is true then put `open' and `close' on the same line."
  1501. (let ((region-beginning (and transient-mark-mode mark-active
  1502. (region-beginning)))
  1503. (region-end (and transient-mark-mode mark-active
  1504. (copy-marker (region-end))))
  1505. finish-marker)
  1506. (if region-beginning (goto-char region-beginning))
  1507. (insert open)
  1508. (if block (progn
  1509. (insert " scalar ")
  1510. (setq finish-marker (point-marker))
  1511. (insert ";")))
  1512. (if (looking-at "[ \t]*$") ()
  1513. (if nosplit (insert " ") (newline-and-indent)))
  1514. (if region-end
  1515. (progn ; better to indent rigidly?
  1516. (reduce-indent-region (point) region-end)
  1517. (goto-char region-end)
  1518. (if (bolp) (backward-char))
  1519. (set-marker region-end nil) )
  1520. (if (looking-at "[ \t]*$") ()
  1521. ;; (reduce-forward-statement 1)
  1522. (end-of-line)
  1523. (setq region-end t)) )
  1524. (if region-end ()
  1525. (reduce-indent-line)
  1526. (if nosplit (insert " ") (newline-and-indent)) )
  1527. (if block (push-mark) (setq finish-marker (point-marker)))
  1528. (if nosplit (insert " ") (newline))
  1529. (insert close)
  1530. (if (looking-at "[ \t]*else")
  1531. (just-one-space)
  1532. (insert ";")
  1533. (if (looking-at "[ \t]*$") ()
  1534. (insert " ")) )
  1535. (reduce-indent-line) ; necessary AFTER inserting close
  1536. (goto-char finish-marker)
  1537. (set-marker finish-marker nil)
  1538. ))
  1539. ;; If an expansion function interprets an argument then it means that
  1540. ;; the expansion should be kept on one line. The following are
  1541. ;; provided solely to ignore any argument:
  1542. (defun reduce-expand-if-then (&optional arg)
  1543. "Insert `if ... then' and position point inside, ignoring ARG."
  1544. (reduce-insert-if-then))
  1545. (defun reduce-expand-if-then-else (&optional arg)
  1546. "Insert `if ... then ... else' and position point after `if', ignoring ARG."
  1547. (reduce-insert-if-then 'else))
  1548. ;;;; **********************************
  1549. ;;;; Balanced structure (sexp) commands
  1550. ;;;; **********************************
  1551. (defun reduce-forward-sexp (&optional arg)
  1552. "Move forward across one, or ARG, balanced expression(s).
  1553. With argument, do it that many times."
  1554. (interactive "p")
  1555. (let ((case-fold-search t))
  1556. (skip-chars-forward " \t\n;$")
  1557. (cond
  1558. ((= (char-syntax (following-char)) ?\( ) (forward-sexp))
  1559. ((looking-at "<<") (forward-char 2) (reduce-forward-group))
  1560. ((looking-at "begin") (forward-char 5) (reduce-forward-block))
  1561. ((looking-at ">>") (forward-char 2))
  1562. (t (forward-sexp))
  1563. ))
  1564. (if (and arg (> arg 1)) (reduce-forward-sexp (1- arg)))
  1565. )
  1566. (defun reduce-backward-sexp (&optional arg)
  1567. "Move backward across one, or ARG, balanced expression(s).
  1568. With argument, do it that many times."
  1569. (interactive "p")
  1570. (skip-chars-backward " \t\n;$")
  1571. (if (= (char-syntax (preceding-char)) ?\) )
  1572. (backward-sexp)
  1573. (let ((case-fold-search t) (start (point)))
  1574. (skip-chars-backward ">>end<<")
  1575. (cond
  1576. ((looking-at ">>") (reduce-backward-group))
  1577. ((looking-at "end") (reduce-backward-block))
  1578. ((looking-at "<<"))
  1579. (t (goto-char start) (backward-sexp))
  1580. )
  1581. ))
  1582. (if (and arg (> arg 1)) (reduce-backward-sexp (1- arg)))
  1583. )
  1584. ;;;; *************************************
  1585. ;;;; Support for matching group delimiters
  1586. ;;;; *************************************
  1587. (defun reduce-self-insert-and-blink-matching-group-open ()
  1588. "Insert character and then blink matching group opening construct."
  1589. ;; Based on blink-matching-open in simple.el
  1590. ;; but cannot use syntax table for composite tokens like << ... >>
  1591. (interactive "*") ; error if buffer read-only
  1592. ;; (insert last-command-char)
  1593. (insert ?>)
  1594. (and (= (char-after (- (point) 2)) ?>)
  1595. blink-matching-paren
  1596. (save-excursion
  1597. (save-restriction
  1598. (if blink-matching-paren-distance
  1599. (narrow-to-region
  1600. (max (point-min)
  1601. (- (point) blink-matching-paren-distance))
  1602. (point)))
  1603. (backward-char 2)
  1604. (reduce-backward-group)
  1605. )
  1606. (if (looking-at "<<")
  1607. (blink-point)
  1608. (message "Matching << not found"))
  1609. ;; [within blink-matching-paren-distance]
  1610. )
  1611. ))
  1612. (defun blink-point ()
  1613. "Blink the position of point."
  1614. ;; Based closely on blink-matching-open in simple.el
  1615. (if (pos-visible-in-window-p)
  1616. (sit-for 1)
  1617. (let ((blinkpos (point)))
  1618. (message
  1619. "Matches %s"
  1620. (if (save-excursion
  1621. (skip-chars-backward " \t")
  1622. (not (bolp)))
  1623. (buffer-substring (progn (beginning-of-line) (point))
  1624. (+ blinkpos 2))
  1625. (buffer-substring blinkpos
  1626. (progn
  1627. (forward-char 1)
  1628. (skip-chars-forward "\n \t")
  1629. (end-of-line)
  1630. (point)))))
  1631. )))
  1632. ;;;; *****************************
  1633. ;;;; Support for reposition-window
  1634. ;;;; *****************************
  1635. ;; The next two functions should probably be built into
  1636. ;; reduce-forward/backward-procedure:
  1637. (defun reduce-beginning-of-defun (&optional arg)
  1638. (if (null arg) (setq arg 1))
  1639. (if (> arg 0)
  1640. (reduce-backward-procedure arg)
  1641. (reduce-forward-procedure (- 1 arg))
  1642. (reduce-backward-procedure 1)))
  1643. (defun reduce-end-of-defun (&optional arg)
  1644. (if (null arg) (setq arg 1))
  1645. (if (> arg 0)
  1646. (reduce-forward-procedure arg)
  1647. (reduce-backward-procedure (- 1 arg))
  1648. (reduce-forward-procedure 1)))
  1649. (defun reduce-reposition-window ()
  1650. "See `reposition-window' for details."
  1651. (interactive)
  1652. (let ((beginning-of-defun (symbol-function 'beginning-of-defun))
  1653. (end-of-defun (symbol-function 'end-of-defun)))
  1654. (fset 'beginning-of-defun 'reduce-beginning-of-defun)
  1655. (fset 'end-of-defun 'reduce-end-of-defun)
  1656. (condition-case nil
  1657. (reposition-window)
  1658. (error (message "Error trapped in reposition-window")))
  1659. (fset 'beginning-of-defun beginning-of-defun)
  1660. (fset 'end-of-defun end-of-defun)
  1661. ))
  1662. ;;;; ******************************************************
  1663. ;;;; Support for REDUCE keyword/phrase/structure completion
  1664. ;;;; ******************************************************
  1665. (defun reduce-complete-symbol (arg)
  1666. "Perform completion on REDUCE symbol preceding point or region.
  1667. Do this only if mark is transient and active.
  1668. Compare that symbol against the elements of `reduce-completion-alist'.
  1669. If a perfect match (only) has a cdr then delete the match and insert
  1670. the cdr if it is a string or call it if it is a (nullary) function,
  1671. passing on any prefix argument (in raw form)."
  1672. ;; Based on lisp-complete-symbol in lisp.el
  1673. (interactive "*P") ; error if buffer read-only
  1674. (let* ((end (progn
  1675. (cond ((and transient-mark-mode mark-active)
  1676. (if (= (point) (region-beginning))
  1677. ()
  1678. (exchange-point-and-mark)
  1679. (skip-syntax-backward " "))))
  1680. (point)))
  1681. (beg (unwind-protect
  1682. (save-excursion
  1683. (reduce-backward-sexp)
  1684. ;; (while (= (char-syntax (following-char)) ?\')
  1685. ;; (forward-char 1))
  1686. (skip-syntax-forward "\'")
  1687. (point))
  1688. ))
  1689. (pattern (buffer-substring-no-properties beg end))
  1690. (completion (try-completion pattern reduce-completion-alist)))
  1691. (cond ((eq completion t) ; perfect match
  1692. (let ((fn (cdr (assoc pattern reduce-completion-alist))))
  1693. (if fn
  1694. (cond ((stringp fn) (delete-region beg end) (insert fn))
  1695. ((fboundp fn) (delete-region beg end) (funcall fn arg))
  1696. (t (error "Completion for \"%s\" not a string or function" pattern)))
  1697. )))
  1698. ((null completion)
  1699. (message "Can't find completion for \"%s\"" pattern)
  1700. (ding))
  1701. ((not (string= pattern completion))
  1702. (delete-region beg end)
  1703. (insert completion)
  1704. (if (fboundp (cdr (assoc completion reduce-completion-alist)))
  1705. (setq deactivate-mark nil))) ; for beg -> begin -> ...
  1706. (t
  1707. (message "Making completion list...")
  1708. (let ((list (all-completions pattern reduce-completion-alist)))
  1709. (with-output-to-temp-buffer "*Completions*"
  1710. (display-completion-list list)))
  1711. (message "Making completion list...%s" "done")))))
  1712. ;;;; ****************************************************
  1713. ;;;; Support font-lock-mode for highlighting keywords and
  1714. ;;;; "object" names (based on code by Rainer Schoepf).
  1715. ;;;; ****************************************************
  1716. ;; Note that Font Lock Mode is documented in the ELisp manual under
  1717. ;; Major and Minor Modes. Fontification is performed syntactically
  1718. ;; (e.g. comments) and THEN by keyword.
  1719. (defconst reduce-identifier-regexp
  1720. "\\(?:[a-z]\\|!.\\)\
  1721. \\(?:\\w\\|\\s_\\|!.\\)*"
  1722. ;; NB: digits have word syntax
  1723. "Regular expression matching a REDUCE identifier.")
  1724. (defconst reduce-infix-regexp
  1725. "where\\|when\\|or\\|and\\|member\\|memq\\|neq\\|eq")
  1726. (defconst reduce-keyword-regexp
  1727. (mapconcat 'identity
  1728. (list
  1729. "begin" "return" "module" "end\\(?:module\\)?"
  1730. "if" "then" "else"
  1731. "while" "do" "repeat" "until"
  1732. "collect" "join" "conc" "sum" "product"
  1733. "for\\(?:\\s-*\\(?:all\\|each\\)\\)?" "step"
  1734. "in" "on" "off" "write"
  1735. "let" "clearrules"
  1736. "clear" "pause"
  1737. "assert_install" "assert_install_all"
  1738. "assert_uninstall" "assert_uninstall_all"
  1739. "assert"
  1740. ;; Lisp keywords used frequently in REDUCE:
  1741. "lambda" "function"
  1742. ;; "put" "flag" "remprop" "remflag"
  1743. reduce-infix-regexp)
  1744. "\\|")
  1745. "Regular expression matching a REDUCE keyword.")
  1746. ;(defvar reduce-reserved-variable-regexp
  1747. ; "e\\|i\\|infinity\\|nil\\|pi\\|t")
  1748. (defconst font-lock-default-face 'font-lock-default-face
  1749. "A copy of the default face for use by REDUCE Font Lock mode.")
  1750. (copy-face 'default 'font-lock-default-face)
  1751. ;; Assertion and preprocessor rules based on code by Thomas Sturm.
  1752. ;; A good test file for all assertion rules is "redlog/cl/clqe.red".
  1753. ;; A constant with a name of the form `font-lock-rule' becomes an
  1754. ;; element of the list assigned by `reduce-mode' to
  1755. ;; `font-lock-keywords', which directly controls search-based
  1756. ;; fontification, whereas a constant with a name of the form
  1757. ;; `font-lock-rules' (plural) below becomes appended to or spliced
  1758. ;; into the list assigned to `font-lock-keywords'.
  1759. (defconst reduce-font-lock-asserted-type-rule
  1760. `("procedure"
  1761. ;; anchored-highlighter to handle the rest of the statement:
  1762. ,(concat "[^!]:\\s-*\\(" reduce-identifier-regexp "\\)") nil nil
  1763. (1 font-lock-type-face t))
  1764. "A rule specifying how to highlight types of procedure
  1765. arguments and return values.")
  1766. (defconst reduce-font-lock-assert-declare/struct-rules
  1767. `((,(concat
  1768. "\\(declare\\)\\s-+"
  1769. "\\(" reduce-identifier-regexp "\\)\\s-*:")
  1770. (1 font-lock-keyword-face)
  1771. (2 font-lock-function-name-face)
  1772. ;; anchored-highlighter to handle the rest of the statement:
  1773. (,reduce-identifier-regexp nil nil (0 font-lock-type-face)))
  1774. (,(concat
  1775. "\\(struct\\)\\s-+"
  1776. "\\(" reduce-identifier-regexp "\\)"
  1777. ;; optionally followed by...
  1778. "\\(?:\\s-+\\(\\(?:checked\\|asserted\\)\\s-+by\\)\\s-+"
  1779. "\\(" reduce-identifier-regexp "\\)\\)?")
  1780. (1 font-lock-keyword-face)
  1781. (2 font-lock-type-face)
  1782. (3 font-lock-keyword-face t)
  1783. (4 font-lock-function-name-face)))
  1784. "Rules specifying how to highlight `declare' and `struct'
  1785. statements, as used in `redlog'.")
  1786. (defconst reduce-font-lock-preprocessor-rules
  1787. `((,(concat
  1788. "\\(#define\\)\\s-+"
  1789. "\\(" reduce-identifier-regexp "\\)\\s-+"
  1790. "\\(" reduce-identifier-regexp "\\)")
  1791. (1 font-lock-preprocessor-face)
  1792. (2 font-lock-variable-name-face)
  1793. (3 font-lock-variable-name-face))
  1794. ("\\(#if\\)\\s-+\\(.*\\)"
  1795. (1 font-lock-preprocessor-face)
  1796. (2 font-lock-default-face))
  1797. ("\\(#elif\\)\\s-+\\(.*\\)"
  1798. (1 font-lock-preprocessor-face)
  1799. (2 font-lock-default-face))
  1800. ("\\(#else\\)"
  1801. (1 font-lock-preprocessor-face))
  1802. ("\\(#endif\\)"
  1803. (1 font-lock-preprocessor-face)))
  1804. "Rules specifying how to highlight preprocessor #-directives.")
  1805. (defconst reduce-font-lock-keywords-0
  1806. `("reduce-font-lock-keywords-0" ; TEMPORARY label for debugging
  1807. (reduce-font-lock-match-comment-statement
  1808. . (1 font-lock-comment-face t))
  1809. ;; Main keywords:
  1810. (,(concat
  1811. ;; Ignore !keyword, _keyword, 'keyword, #keyword:
  1812. "\\(?:^\\|[^!_'#]\\)"
  1813. "\\<\\(" reduce-keyword-regexp "\\)\\>"
  1814. ;; Ignore composite identifiers:
  1815. "[^!_#]")
  1816. (1 font-lock-keyword-face)
  1817. ;; Handle consecutive keywords:
  1818. (,(concat "\\<\\(" reduce-keyword-regexp "\\)\\>[^!_#]")
  1819. nil nil (1 font-lock-keyword-face)))
  1820. ;; Group delimiters and references:
  1821. "<<\\|>>\\|\\<\\go\\(?:\\s-*to\\)?\\>"
  1822. ;; ????? Handle goto label and label : specially?
  1823. ;; Procedure declarations:
  1824. (,(concat "\\<\\(procedure\\)\\s-+"
  1825. "\\(" reduce-identifier-regexp "\\)")
  1826. (1 font-lock-keyword-face)
  1827. (2 font-lock-function-name-face))
  1828. ;; Type declarations:
  1829. ("\\(?:^\\|[^_]\\)\\<\\(algebraic\\|symbolic\\|lisp\\|operator\\|scalar\\|integer\\|real\\|linear\\)\\>[^!_]"
  1830. (1 font-lock-type-face))
  1831. ,reduce-font-lock-asserted-type-rule
  1832. ,@reduce-font-lock-assert-declare/struct-rules
  1833. ,@reduce-font-lock-preprocessor-rules)
  1834. "Default minimal REDUCE fontification rules.")
  1835. (defconst reduce-font-lock-keywords-basic
  1836. (list
  1837. '(reduce-font-lock-match-comment-statement
  1838. . (1 font-lock-comment-face t))
  1839. ;; Main keywords:
  1840. (list (concat
  1841. ;; Ignore quoted keywords and composite identifiers:
  1842. ;; "\\(^[^!_']?\\|[^!][^!_']\\)"
  1843. "\\(^[^!_'#]?\\|[^!#][^!_'#]\\)"
  1844. "\\<\\(\\(" reduce-keyword-regexp "\\)"
  1845. ;; Handle consecutive keywords:
  1846. "\\(\\s +\\(" reduce-keyword-regexp "\\)\\)*"
  1847. "\\)\\>"
  1848. ;; Ignore composite identifiers:
  1849. ;; "[^!_]"
  1850. "[^!_#]"
  1851. )
  1852. '(2 font-lock-keyword-face))
  1853. ;; Group delimiters: OK
  1854. '("<<\\|>>" . font-lock-keyword-face)
  1855. ;; Procedure declarations:
  1856. (list (concat "\\<\\(procedure\\)\\s +"
  1857. "\\(" reduce-identifier-regexp "\\)" "\\s *(?")
  1858. '(1 font-lock-keyword-face)
  1859. ;; This will probably cause highlighting within comments, see above:
  1860. ;; '(2 font-lock-function-name-face t)
  1861. '(2 font-lock-function-name-face) ; no highlighting in comments; TS
  1862. ;; Anchored matches (single line only!):
  1863. (list (concat "\\s *"
  1864. "\\(" reduce-identifier-regexp "\\)"
  1865. "\\s *\\([\);$].*\\|\\s.\\)"
  1866. ; Stop after `)', `;' or `$'
  1867. )
  1868. nil nil
  1869. '(1 font-lock-variable-name-face)))
  1870. ;; Type declarations:
  1871. (list "\\<\\(operator\\|scalar\\|integer\\|real\\)\\s "
  1872. '(1 font-lock-type-face)
  1873. ;; Anchored matches (single line only!):
  1874. (list (concat "\\s *"
  1875. "\\(" reduce-identifier-regexp "\\)"
  1876. "\\s *\\s."
  1877. )
  1878. nil nil
  1879. '(1 font-lock-variable-name-face)))
  1880. ;; References -- goto and labels:
  1881. (list (concat "\\<\\(go\\(\\s *to\\)?\\)\\s +"
  1882. "\\(" reduce-identifier-regexp "\\)")
  1883. '(1 font-lock-keyword-face)
  1884. '(3 font-lock-constant-face)) ; was font-lock-reference-face
  1885. (cons (concat "^\\s *\\(" reduce-identifier-regexp "\\)\\s *:[^=]")
  1886. '(1 font-lock-constant-face)) ; was font-lock-reference-face
  1887. )
  1888. "Basic REDUCE fontification sub-rules.")
  1889. (defconst reduce-font-lock-keywords-algebraic
  1890. (append (list
  1891. ;; More type declarations:
  1892. (list "\\<\\(array\\|matrix\\)\\s "
  1893. '(1 font-lock-type-face)
  1894. ;; Anchored matches (single line only!):
  1895. (list (concat "\\s *"
  1896. "\\(" reduce-identifier-regexp "\\)"
  1897. "\\s *\\(([^\)]*)\\s *\\)?\\s."
  1898. )
  1899. nil nil
  1900. '(1 font-lock-variable-name-face))
  1901. )
  1902. reduce-font-lock-asserted-type-rule)
  1903. reduce-font-lock-preprocessor-rules)
  1904. "More algebraic-mode REDUCE fontification sub-rules.")
  1905. (defconst reduce-font-lock-keywords-symbolic
  1906. (append (list
  1907. ;; References -- module:
  1908. (list (concat "\\<\\(module\\)\\s +"
  1909. "\\(" reduce-identifier-regexp "\\)")
  1910. '(1 font-lock-keyword-face)
  1911. '(2 font-lock-constant-face)) ; was font-lock-reference-face
  1912. ;; Type declarations:
  1913. '("\\<\\(fluid\\|global\\)\\>\\s *'(\\(.*\\))"
  1914. (1 font-lock-type-face)
  1915. (2 font-lock-variable-name-face))
  1916. (cons (concat
  1917. ;; Ignore quoted keywords and composite identifiers:
  1918. "\\(^[^!_']?\\|[^!][^!_']\\)"
  1919. "\\<\\(newtok\\|precedence\\|switch\\|share\\|"
  1920. "algebraic\\|symbolic\\|f?expr\\|s?macro\\|asserted\\|inline\\)\\>"
  1921. ;; Ignore composite identifiers:
  1922. "[^!_]"
  1923. )
  1924. '(2 font-lock-type-face))
  1925. reduce-font-lock-asserted-type-rule)
  1926. reduce-font-lock-assert-declare/struct-rules
  1927. reduce-font-lock-preprocessor-rules)
  1928. "More symbolic-mode REDUCE fontification sub-rules.")
  1929. (defconst reduce-font-lock-keywords-full
  1930. (list
  1931. ;; Gaudier fontification
  1932. ;; =====================
  1933. ;; More type declarations:
  1934. (list "\\<\\(array\\|matrix\\)\\s "
  1935. '(1 font-lock-type-face)
  1936. ;; Anchored matches (single line only!):
  1937. (list (concat "\\s *"
  1938. "\\(" reduce-identifier-regexp "\\)"
  1939. "\\s *\\(([^\)]*)\\s *\\)?\\s."
  1940. )
  1941. nil nil
  1942. '(1 font-lock-variable-name-face))
  1943. )
  1944. ;; Set *ALL* quoted identifiers in the default face:
  1945. (cons (concat
  1946. "'\\("
  1947. ;; All (multi-line) quoted lists (nested to 2 levels):
  1948. "(\\([^)]*([^)]*[^!])\\)*[^)]*[^!])"
  1949. "\\|" reduce-identifier-regexp ; includes keywords!
  1950. "\\)")
  1951. '(0 font-lock-default-face keep)) ; not already highlighted
  1952. ;; Highlight variable invocations:
  1953. ;; ( var), var PUNCTUATION, var EOL, var KEYWORD, var INFIX )
  1954. (list (concat
  1955. "\\(" reduce-identifier-regexp "\\)"
  1956. "\\s *\\("
  1957. "\\s\)\\|\\s.\\|$\\|"
  1958. "\\s \\<\\(" reduce-keyword-regexp
  1959. "\\|\\(" reduce-infix-regexp "\\)\\)\\>"
  1960. "\\)")
  1961. '(1 font-lock-variable-name-face)
  1962. '(4 font-lock-default-face nil t))
  1963. ;;; Should force ALL infix ops into right font!
  1964. ;; Highlight function calls:
  1965. ;; ( fn(), fn{}, fn"", fn'data, fn var, fn ! )
  1966. (cons (concat
  1967. "\\(\\(" reduce-identifier-regexp "\\)"
  1968. ;; Handle unparenthesized compositions:
  1969. "\\(\\s +\\(" reduce-identifier-regexp "\\)\\)*\\)"
  1970. "\\s *\\(\\s\(\\|[\"']\\|\\s \\(\\w\\|!\\)\\)"
  1971. )
  1972. ;; Must keep already fontified keywords in order to
  1973. ;; highlight functions immediately following keywords
  1974. ;; and avoid mis-highlighting variables:
  1975. '(1 font-lock-function-name-face keep))
  1976. )
  1977. "Full maximal REDUCE fontification sub-rules.")
  1978. (defconst reduce-font-lock-keywords-1
  1979. `("reduce-font-lock-keywords-1" ; TEMPORARY label for debugging
  1980. ,@reduce-font-lock-keywords-basic
  1981. ,@reduce-font-lock-keywords-algebraic)
  1982. "Standard algebraic-mode REDUCE fontification rules.")
  1983. (defconst reduce-font-lock-keywords-2
  1984. `("reduce-font-lock-keywords-2" ; TEMPORARY label for debugging
  1985. ,@reduce-font-lock-keywords-basic
  1986. ,@reduce-font-lock-keywords-symbolic)
  1987. "Standard symbolic-mode REDUCE fontification rules.")
  1988. (defconst reduce-font-lock-keywords-3
  1989. `("reduce-font-lock-keywords-3" ; TEMPORARY label for debugging
  1990. ,@reduce-font-lock-keywords-basic
  1991. ,@reduce-font-lock-keywords-algebraic
  1992. ,@reduce-font-lock-keywords-symbolic
  1993. ,@reduce-font-lock-keywords-full)
  1994. "Full REDUCE fontification rules.")
  1995. ;; Support functions for comment statements. Being normally
  1996. ;; multi-line, they require the support of the function
  1997. ;; `reduce-font-lock-extend-region-for-comment-statement'.
  1998. (defun reduce-font-lock-match-comment-statement (limit)
  1999. "Search for a comment statement between point and LIMIT.
  2000. If successful, return non-nil and set the match data to describe
  2001. the match; otherwise return nil."
  2002. ;; Fontification will call this function repeatedly with the same
  2003. ;; limit, and with point where the previous invocation left it,
  2004. ;; until it fails. On failure, there is no need to reset point in
  2005. ;; any particular way.
  2006. (when
  2007. (search-forward-regexp "\\(\\<comment\\>[^;$]*\\)[;$]" limit t)
  2008. ;; If successful, check that "comment" is preceded by beginning of
  2009. ;; buffer or a terminator, possibly with white space and/or %
  2010. ;; comments in between:
  2011. (save-excursion
  2012. (goto-char (match-beginning 0))
  2013. (save-match-data
  2014. (looking-back "\\(?:\\`\\|[;$]\\)\
  2015. \\(?:\\s-*\\(?:%.*\\)?\n\\)*\\s-*" nil)))))
  2016. (defvar font-lock-beg)
  2017. (defvar font-lock-end)
  2018. (defun reduce-font-lock-extend-region-for-comment-statement ()
  2019. "Extend font-lock region if necessary to include all of any
  2020. comment statements that it intersects, and if so return non-nil.
  2021. This function is prepended to `font-lock-extend-region-functions'."
  2022. (let (new-beg new-end)
  2023. (goto-char font-lock-beg)
  2024. ;; Is font-lock-beg within a comment?
  2025. (save-excursion
  2026. (if (and (re-search-backward "\\(comment\\)\\|\\([;$]\\)" nil t)
  2027. (match-beginning 1))
  2028. (setq new-beg (point))))
  2029. (when (or new-beg
  2030. ;; Or does a comment start in the font-lock region?
  2031. (search-forward "comment" font-lock-end t))
  2032. ;; If either of the above then...
  2033. (search-forward-regexp "[;$]" nil 1) ; if un-terminated move to EOB
  2034. ;; Do multiple comments start in the font-lock region?
  2035. (while (and (< (point) font-lock-end)
  2036. (search-forward "comment" font-lock-end t))
  2037. (search-forward-regexp "[;$]" nil 1)) ; if un-terminated move to EOB
  2038. (if (> (point) font-lock-end)
  2039. (setq new-end (point))))
  2040. ;; Temporary message for testing:
  2041. ;; (message "reduce-font-lock-extend-region-for-comment-statement: %s --> %s, %s --> %s"
  2042. ;; font-lock-beg new-beg font-lock-end new-end)
  2043. ;; Return non-nil if font-lock region adjusted:
  2044. (or (if new-beg (setq font-lock-beg new-beg))
  2045. (if new-end (setq font-lock-end new-end)))))
  2046. ;; Provide a REDUCE font-lock menu, based originally on
  2047. ;; font-lock-menu.el by Simon Marshall <simon@gnu.ai.mit.edu>.
  2048. (defconst reduce-font-lock-level-max
  2049. (1- (length reduce-font-lock-keywords))
  2050. "Maximum REDUCE font-lock level.")
  2051. (defvar reduce-font-lock-level)
  2052. (defun reduce-font-lock-level ()
  2053. "Establish the buffer-local variable `reduce-font-lock-level'.
  2054. It is used only to control the font-lock menu and is set for each
  2055. new buffer from the value of `font-lock-maximum-decoration',
  2056. which must be done in `reduce-mode'."
  2057. (set (make-local-variable 'reduce-font-lock-level)
  2058. ;; The value of `font-lock-maximum-decoration' may be an alist,
  2059. ;; non-negative integer, t (meaning max) or nil (meaning 0).
  2060. (let (level)
  2061. (if (consp font-lock-maximum-decoration) ; alist
  2062. (if (setq level (or (assoc 'reduce-mode font-lock-maximum-decoration)
  2063. (assoc t font-lock-maximum-decoration)))
  2064. (setq level (cdr level)))
  2065. (setq level font-lock-maximum-decoration)) ; not alist
  2066. ;; level = integer, t or nil
  2067. (cond ((numberp level)
  2068. (cond ((< level 0) 0)
  2069. ((> level reduce-font-lock-level-max)
  2070. reduce-font-lock-level-max)
  2071. (t level)))
  2072. ((eq level t) reduce-font-lock-level-max) ; t means max
  2073. (t 0))))) ; nil means 0
  2074. (defconst reduce-font-lock-submenu
  2075. '("Syntax Highlighting"
  2076. ["In Current Buffer" font-lock-mode
  2077. :style toggle :selected font-lock-mode :active t]
  2078. ["Highlight Buffer" font-lock-fontify-buffer t]
  2079. ;; ["Toggle `!' Syntax" reduce-font-lock-toggle-escape t]
  2080. ["Maximum (3)" (reduce-font-lock-change 3)
  2081. :style radio :selected (eq reduce-font-lock-level 3) :active t]
  2082. ["Symbolic (2)" (reduce-font-lock-change 2)
  2083. :style radio :selected (eq reduce-font-lock-level 2) :active t]
  2084. ["Algebraic (1)" (reduce-font-lock-change 1)
  2085. :style radio :selected (eq reduce-font-lock-level 1) :active t]
  2086. ["Minimum (0)" (reduce-font-lock-change 0)
  2087. :style radio :selected (eq reduce-font-lock-level 0) :active t]))
  2088. (easy-menu-define ; (symbol maps doc menu)
  2089. reduce-fontification-submenu
  2090. nil
  2091. "REDUCE Fontification Submenu"
  2092. reduce-font-lock-submenu)
  2093. (define-key-after (lookup-key reduce-mode-map [menu-bar REDUCE])
  2094. [Fontification] (cons "Syntax Highlighting" reduce-fontification-submenu)
  2095. t) ; was 'Make\ Proc\ Menu
  2096. (defconst reduce-font-lock-level-names
  2097. '("minimum" "algebraic" "symbolic" "maximum"))
  2098. (defun reduce-font-lock-change (level)
  2099. "Re-fontify at the specified LEVEL."
  2100. ;; Do messages need to be saved in the messages buffer?
  2101. ;; If interactive then needs to be more robust.
  2102. ;; (interactive)
  2103. (let ((name (nth level reduce-font-lock-level-names)))
  2104. (if (eq reduce-font-lock-level level)
  2105. (message "REDUCE Font Lock decoration unchanged (level %d : %s)."
  2106. level name)
  2107. (let ((font-lock-maximum-decoration level))
  2108. (font-lock-refresh-defaults))
  2109. (setq reduce-font-lock-level level)
  2110. (message "REDUCE Font Lock decoration set to level %d : %s."
  2111. level name))))
  2112. ;; (let ((name (nth (1- level) reduce-font-lock-level-names))
  2113. ;; (keywords (eval (nth (1- level) (car font-lock-defaults)))))
  2114. ;; ;; `font-lock-defaults' is used in order to support both
  2115. ;; ;; reduce-mode and reduce-run with the same code!
  2116. ;; (setq keywords (font-lock-compile-keywords keywords)) ; Emacs 20 only!
  2117. ;; (if (and font-lock-mode (equal font-lock-keywords keywords))
  2118. ;; (message "REDUCE Font Lock decoration unchanged (level %d : %s)."
  2119. ;; level name)
  2120. ;; (font-lock-mode 0)
  2121. ;; (font-lock-set-defaults)
  2122. ;; (setq font-lock-keywords keywords)
  2123. ;; (font-lock-mode 1)
  2124. ;; (setq reduce-font-lock-level level)
  2125. ;; (message "REDUCE Font Lock decoration set to level %d : %s."
  2126. ;; level name))))
  2127. (defun reduce-font-lock-toggle-escape (&optional arg)
  2128. "Toggle `!' escape syntax for REDUCE Font Lock mode (only) and re-fontify.
  2129. With arg, clear `!' escape syntax if arg >= 0 and set it if arg < 0.
  2130. For example,
  2131. \(add-hook 'reduce-mode-hook
  2132. (function (lambda () (reduce-font-lock-toggle-escape 1))))
  2133. will turn off the default font-lock escape syntax for `!'."
  2134. (interactive "P")
  2135. (require 'font-lock)
  2136. (let ((reset font-lock-syntax-table))
  2137. (font-lock-mode 0)
  2138. (font-lock-set-defaults) ; resets font-lock-syntax-table
  2139. (if arg (setq reset (< (prefix-numeric-value arg) 0)))
  2140. (if reset
  2141. ;; `!' syntax has been reset to `escape', so do nothing:
  2142. () ;; (setq font-lock-syntax-table nil) ; default
  2143. ;; Set `!' syntax to punctuation:
  2144. (setq font-lock-syntax-table
  2145. (copy-syntax-table reduce-mode-syntax-table))
  2146. (modify-syntax-entry ?! "." font-lock-syntax-table)) ; punctuation
  2147. (font-lock-mode 1)
  2148. ;; Display message so it is not overwritten by font-lock messages:
  2149. (message
  2150. (if font-lock-syntax-table
  2151. "REDUCE Font Lock syntax (only) for `!' set to `punctuation'."
  2152. "REDUCE Font Lock syntax table reset."))))
  2153. ;;;; **********************************************************
  2154. ;;;; Support for displaying current procedure name in mode line
  2155. ;;;; **********************************************************
  2156. (defvar reduce-show-proc-idle-timer nil)
  2157. (defvar reduce-show-proc-string nil)
  2158. (defvar which-func-mode)
  2159. (defun reduce-show-proc-mode (&optional arg)
  2160. "Toggle REDUCE Show Proc mode.
  2161. With prefix ARG, turn REDUCE Show Proc mode on if and only if ARG is positive.
  2162. Returns the new status of REDUCE Show Proc mode (non-nil means on).
  2163. When REDUCE Show Proc mode is enabled, display current procedure name
  2164. in mode line after `reduce-show-proc-delay' seconds of Emacs idle time."
  2165. (interactive "P")
  2166. (let ((on-p (if arg
  2167. (> (prefix-numeric-value arg) 0)
  2168. (not reduce-show-proc-mode))))
  2169. (if reduce-show-proc-idle-timer
  2170. (cancel-timer reduce-show-proc-idle-timer))
  2171. (if on-p
  2172. (setq reduce-show-proc-idle-timer
  2173. (run-with-idle-timer reduce-show-proc-delay t
  2174. 'reduce-show-proc-function)))
  2175. (setq reduce-show-proc-mode on-p
  2176. which-func-mode on-p)
  2177. (reduce-show-proc-function)))
  2178. (defconst reduce-show-proc-regexp
  2179. (car reduce-imenu-generic-expression))
  2180. (defun reduce-current-proc ()
  2181. "Return name of procedure definition point is in, or nil."
  2182. ;; Used by reduce-show-proc-mode and ChangeLog support
  2183. (let ((start (point)) procname)
  2184. (end-of-line)
  2185. (save-match-data
  2186. (when (re-search-backward
  2187. (nth 1 reduce-show-proc-regexp) nil t)
  2188. (setq procname
  2189. (match-string (nth 2 reduce-show-proc-regexp)))
  2190. (reduce-forward-procedure 1)
  2191. (if (<= (point) start) ; not in procedure
  2192. (setq procname nil))))
  2193. (goto-char start)
  2194. procname))
  2195. (defun reduce-show-proc-function ()
  2196. "Display current procedure name in mode line."
  2197. (when (eq major-mode 'reduce-mode)
  2198. (setq reduce-show-proc-string
  2199. (concat "[" (or (reduce-current-proc) "") "]"))
  2200. (force-mode-line-update)))
  2201. ;;;; *****************************************
  2202. ;;;; Support for tagging procedure definitions
  2203. ;;;; *****************************************
  2204. (defcustom reduce-etags-directory invocation-directory
  2205. "Directory containing the etags program, or nil if it is in path.
  2206. If non-nil the string must end with /."
  2207. :package-version '(reduce-mode . "1.54")
  2208. :type '(choice (directory :tag "Etags program directory")
  2209. (const :tag "Etags is in exec path" nil))
  2210. :group 'reduce-interface)
  2211. (defun reduce-tagify-dir (dir)
  2212. "Generate a REDUCE TAGS file for `*.red' files in directory DIR.
  2213. TAGS goes in DIR, which by default is the current directory."
  2214. (interactive
  2215. (list (read-directory-name
  2216. "Tag files in dir: " ; PROMPT
  2217. nil ; DIR (default cwd)
  2218. nil ; DEFAULT-DIRNAME
  2219. t))) ; MUSTMATCH
  2220. (setq dir (directory-file-name (expand-file-name dir)))
  2221. (reduce--tagify
  2222. dir (directory-files dir nil "\\.red\\'")
  2223. (message "Tagging files `%s/*.red'..." dir)))
  2224. (defun reduce--tagify (dir files msg)
  2225. "Generate a REDUCE TAGS file in directory DIR for specified FILES.
  2226. FILES must be a list of filenames, which can be relative to DIR.
  2227. MSG is the message displayed when the tagging process started."
  2228. (let* ((default-directory dir)
  2229. (value
  2230. (apply
  2231. #'call-process ; creates a synchronous process
  2232. (concat reduce-etags-directory "etags") ; program
  2233. nil ; infile
  2234. "*rtags-log*" ; destination
  2235. nil ; display
  2236. "--lang=none" ; args ...
  2237. "--regex=/[^%]*procedure[ \\t]+\\([^ \\t\(;$]+\\)/\\1/i"
  2238. files))) ; LIST of filenames
  2239. (if (eq value 0)
  2240. (message "%sdone" msg)
  2241. (message "etags failed with status: %s" value))))
  2242. (defun reduce-tagify-dir-recursively (dir)
  2243. "Generate a REDUCE TAGS file for all `*.red' files under directory DIR.
  2244. TAGS goes in DIR, which by default is the current directory."
  2245. (interactive
  2246. (list (read-directory-name
  2247. "Tag all files under dir: " ; PROMPT
  2248. nil ; DIR (default cwd)
  2249. nil ; DEFAULT-DIRNAME
  2250. t))) ; MUSTMATCH
  2251. (setq dir (directory-file-name (expand-file-name dir)))
  2252. (let ((reduce--tagify-root dir))
  2253. ;; reduce--tagify-root required by `reduce--directory-files-recursively'.
  2254. (reduce--tagify
  2255. dir (reduce--directory-files-recursively dir)
  2256. (message "Tagging all files `%s/...*.red'..." dir))))
  2257. (defvar reduce--tagify-root)
  2258. (defun reduce--directory-files-recursively (dir)
  2259. "Return a list of all `*.red' files under DIR.
  2260. This function works recursively. Files are returned in \"depth first\"
  2261. order, and files from each directory are sorted in alphabetical order.
  2262. Each file name appears in the returned list relative to directory
  2263. `reduce--tagify-root', assumed to be bound locally in the caller."
  2264. ;; Modelled on `directory-files-recursively'.
  2265. (let (result
  2266. files
  2267. ;; When DIR is "/", remote file names like "/method:" could
  2268. ;; also be offered. We shall suppress them.
  2269. (tramp-mode (and tramp-mode (file-remote-p (expand-file-name dir)))))
  2270. (dolist (file (sort (file-name-all-completions "" dir) 'string<))
  2271. (unless (member file '("./" "../"))
  2272. (if (directory-name-p file)
  2273. (let* ((leaf (substring file 0 -1))
  2274. (full-file (expand-file-name leaf dir)))
  2275. (setq result
  2276. (nconc result (reduce--directory-files-recursively
  2277. full-file))))
  2278. (when (string-match "\\.red\\'" file)
  2279. (push (file-relative-name
  2280. (expand-file-name file dir)
  2281. reduce--tagify-root)
  2282. files)))))
  2283. (nconc result (nreverse files))))
  2284. ;;;; **********************************************************************
  2285. ;;; Load Hook
  2286. (defun require-reduce-run ()
  2287. "Require the library `reduce-run'. Useful on `reduce-mode-load-hook'."
  2288. (require 'reduce-run))
  2289. (provide 'reduce-mode)
  2290. (run-hooks 'reduce-mode-load-hook)
  2291. ;;; reduce-mode.el ends here