PageRenderTime 65ms CodeModel.GetById 22ms RepoModel.GetById 1ms app.codeStats 0ms

/nmcobol-mode.el

https://github.com/emacsmirror/nmcobol-mode
Emacs Lisp | 2162 lines | 1677 code | 166 blank | 319 comment | 78 complexity | 794f099dbe49df543a16b2714595bad2 MD5 | raw file
  1. ;;; nmcobol-mode.el --- For use with Tandem Cobol only - Sorry.
  2. ;; Copyright (C) 2006, 2007 Free Software Foundation, Inc.
  3. ;; Author: Rick Bielawski <rbielaws@i1.net>
  4. ;; Keywords: languages, COBOL, Tandem, Guardian, NSK
  5. ;; Maintainer: Rick Bielawski <rbielaws@i1.net>
  6. ;; This file is free software; you can redistribute it and/or modify
  7. ;; it under the terms of the GNU General Public License as published by
  8. ;; the Free Software Foundation; either version 2, or (at your option)
  9. ;; any later version.
  10. ;; This file is distributed in the hope that it will be useful,
  11. ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. ;; GNU General Public License for more details.
  14. ;; You should have received a copy of the GNU General Public License
  15. ;; along with GNU Emacs; see the file COPYING. If not, write to the
  16. ;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
  17. ;; Boston, MA 02110-1301, USA.
  18. ;;; Commentary:
  19. ;; NMCOBOL -- COmmon Business Oriented Language - in ?TANDEM line format.
  20. ;; This is currently a work-in-progress. You are welcome to try it and
  21. ;; submit changes or report bugs but there is no point trying to report
  22. ;; lack of features unless you are including patches that address such
  23. ;; shortcomings.
  24. ;;
  25. ;; Since Tandem's line format is not standard this mode is not likely to
  26. ;; suit the needs of most Cobol progammers. Moreover, Tandem Cobol has
  27. ;; extensions that standard Cobol doesn't have and may not implement
  28. ;; features required or generally available by other Cobol compilers.
  29. ;; I'm happy to add submitted patches to support other compilers only if
  30. ;; such patches don't interfere with support of Tandem Cobol.
  31. ;;; Installing:
  32. ;; Before you can use nmcobol-mode, emacs needs to be able to find it. Place
  33. ;; the nmcobol-mode.el file in a directory on the load-path; typically the
  34. ;; .../site-lisp or perhaps .../lisp/progmods directory. Usually you would
  35. ;; also want to byte compile nmcobol-mode.el but this is not required. To do
  36. ;; this, visit the nmcobol-mode.el file, type: M-x emacs-lisp-byte-compile <ret>
  37. ;; There should be no warnings or errors during byte compilation.
  38. ;;
  39. ;; There are 4 basic ways to use NMCOBOL-MODE on a file. The first method
  40. ;; manually selects nmcobol-mode as the editing mode. The other 3 cause emacs
  41. ;; to recognize automatically that you want to visit the file using
  42. ;; nmcobol-mode.
  43. ;;
  44. ;; Pick one:
  45. ;; 1. While visiting a file, type: M-x nmcobol-mode <ret>
  46. ;; 2. Put the string -*-nmcobol-*- in a comment on the first line of the file.
  47. ;; Save the file and close it. Now any time you open it nmcobol-mode starts.
  48. ;; 3. Create an association between a particular file naming convention and
  49. ;; nmcobol-mode. This is done by adding an association to auto-mode-alist.
  50. ;; For example:
  51. ;; (setq auto-mode-alist
  52. ;; (append
  53. ;; '(("\\.cob\\'" . nmcobol-mode) ;extension of .cob means nmcobol-mode
  54. ;; ("\\([\\/]\\|^\\)[^.]+$" . nmcobol-mode)) ;so does no extension at all.
  55. ;; auto-mode-alist))
  56. ;; 4. Advise set-auto-mode to look at the buffer contents upon loading.
  57. ;; For an example see: http://www.emacswiki.org/cgi-bin/wiki/TandemNskSupport
  58. ;;
  59. ;; The above all tell emacs that you want to use nmcobol-mode but you must load
  60. ;; nmcobol-mode before you can use it. There are 2 methods of telling emacs to
  61. ;; load the nmcobol-mode routines. The first unconditionally loads nmcobol-mode
  62. ;; definitions immediately. The second tells emacs to automatically load
  63. ;; nmcobol-mode only when you try to use it. Add one of the following lines to
  64. ;; your .emacs file.
  65. ;;
  66. ;;(require 'nmcobol-mode) ; Unconditional load
  67. ;;(autoload 'nmcobol-mode "nmcobol-mode" "Major mode for Tandem NMCOBOL files." t nil)
  68. ;;
  69. ;;; Getting eldoc to work in nmcobol-mode:
  70. ;; Open a file containing variable declarations for which you want
  71. ;; help permanently loaded. For example: $SYSTEM.COPYLIBS.COBOLLIB.
  72. ;; The buffer must be in nmcobol-mode or ddl-mode.
  73. ;; Use: M-x nmcobol-mode <ret> if necessary.
  74. ;; Then: M-x nmcobol-eldoc-make-list <ret>
  75. ;; You should now be in a specially formatted buffer containing a list of
  76. ;; the variables declared and their corresponding help strings. You can
  77. ;; modify the strings themselves if desired but do not alter anything
  78. ;; else. Repeat these steps to add more help entries to the file.
  79. ;; Save the file somewhere on your search list like your site-lisp
  80. ;; directory. Example:
  81. ;; C-x C-w ~/../site-lisp/extdecs-help.el <ret>
  82. ;; Now add the file you just saved to the list of nmcobol-mode eldoc help
  83. ;; files to be loaded.
  84. ;; M-x customize-option <ret> nmcobol-eldoc-def-files <ret>
  85. ;; Save the customization for future sessions.
  86. ;; Once you have your help entries defined see help for eldoc-mode for
  87. ;; turing on the mode. Putting (eldoc-mode t) in .emacs might do it.
  88. ;;; History:
  89. ;; 2006-11-07 RGB Started writing this mode using my cobol-mode as skeleton.
  90. ;; 2006-12-18 RGB Added eldoc support.
  91. ;; 2006-12-26 RGB Added nmcobol-addup-pics.
  92. ;; 2006-12-27 RGB Added thing-at-point support for Guardian style 'filename.
  93. ;; 2006-12-29 RGB Added an NMCobol menu and nmcobol-customize-options.
  94. ;; 2007-01-03 RGB Added movement by sentences.
  95. ;; 2007-01-04 RGB Fixed movement by sentences, added paragraphs.
  96. ;; 2007-01-04 RGB Make COUNT argument in movement by functions optional.
  97. ;; 2007-01-09 RGB Added eldoc for variables including a buffer-local obarray.
  98. ;; 2007-01-10 RGB Added Cobol specific support for anchored-transpose.
  99. ;; 2007-01-11 RGB Fixed 66 level handling in paragraph movement and eldoc-scan.
  100. ;; 2007-01-28 RGB Sentence/Paragraph movement now saves match data.
  101. ;; 2007-01-31 RGB Started adding automatic indentation.
  102. ;; 2007-02-01 RGB Made * electric.
  103. ;; 2007-02-05 RGB Auto-indent now operates on regions
  104. ;; 2007-02-07 RGB Fixed customization of nmcobol-keywords-case &
  105. ;; nmcobol-imenu-menubar. Fixed nmcobol-pic-string-regexp
  106. ;; not recognizing 'ZZZ-.' syntax. Fixed
  107. ;; nmcobol-char-maybe-comment which broke numeric
  108. ;; prefix behavior. Doc-string updates.
  109. ;; 2007-02-12 RGB Indentation now handles all the block statements properly.
  110. ;; 2007-02-13 RGB The beginning of secondary indentation started. This
  111. ;; handles alignment of things like picture clauses, TO
  112. ;; statements etc.
  113. ;; 2007-02-19 RGB imenu now ignores xxxx-exit paragraphs. Started trying
  114. ;; to implement paren matching on end-<verb> keywords.
  115. ;; 2007-02-22 RGB nmcobol-get-block-type now ignores keywords in strings.
  116. ;;; Code:
  117. (defgroup nmcobol nil
  118. "Major mode for editing NMCOBOL source files in Emacs.
  119. While in nmcobol-mode use C-h m for a description of the mode's features."
  120. :prefix 'nmcobol-
  121. :group 'languages)
  122. ;;; SYNTAX TABLE
  123. (defvar nmcobol-mode-syntax-table
  124. (let ((st (make-syntax-table)))
  125. (modify-syntax-entry ?\n " " st)
  126. (modify-syntax-entry ?\! "." st)
  127. (modify-syntax-entry ?\" "." st) ; wiki ?" bug workaround comment
  128. (modify-syntax-entry ?\# "w" st)
  129. (modify-syntax-entry ?\$ "w" st)
  130. (modify-syntax-entry ?\% "'" st)
  131. (modify-syntax-entry ?\& "'" st)
  132. (modify-syntax-entry ?\' "." st)
  133. (modify-syntax-entry ?\( "()" st)
  134. (modify-syntax-entry ?\) ")(" st)
  135. (modify-syntax-entry ?\* "." st)
  136. (modify-syntax-entry ?\+ "." st)
  137. (modify-syntax-entry ?\, "." st)
  138. (modify-syntax-entry ?\- "w" st)
  139. (modify-syntax-entry ?\. "." st)
  140. (modify-syntax-entry ?\/ "." st)
  141. (modify-syntax-entry ?\: "." st)
  142. (modify-syntax-entry ?\; "." st)
  143. (modify-syntax-entry ?\< "." st)
  144. (modify-syntax-entry ?\= "." st)
  145. (modify-syntax-entry ?\> "." st)
  146. (modify-syntax-entry ?\? "." st)
  147. (modify-syntax-entry ?\@ "." st)
  148. (modify-syntax-entry ?\[ "(]" st)
  149. (modify-syntax-entry ?\\ "." st)
  150. (modify-syntax-entry ?\] ")[" st)
  151. (modify-syntax-entry ?^ "w" st)
  152. (modify-syntax-entry ?\_ "w" st)
  153. (modify-syntax-entry ?\{ "(}" st)
  154. (modify-syntax-entry ?\| "." st)
  155. (modify-syntax-entry ?\} "){" st)
  156. st)
  157. "Syntax table for `nmcobol-mode'.")
  158. ;;; KEY MAP
  159. (defvar nmcobol-skeleton-map
  160. (let ((map (make-sparse-keymap)))
  161. (define-key map [?i] 'nmcobol-if-skel)
  162. (define-key map [?p] 'nmcobol-paragraph-skel)
  163. map)
  164. "Keymap for `nmcobol-mode'.")
  165. (defvar nmcobol-eldoc-map
  166. (let ((map (make-sparse-keymap)))
  167. (define-key map [?m] 'nmcobol-eldoc-make-list)
  168. (define-key map [?s] 'nmcobol-eldoc-scan-buffer)
  169. (define-key map [?v] 'nmcobol-eldoc-visit-file)
  170. (define-key map [?w] 'nmcobol-eldoc-where-def)
  171. map)
  172. "Keymap for `nmcobol-mode'.")
  173. (defvar nmcobol-mode-map
  174. (let ((map (make-sparse-keymap)))
  175. (define-key map [tab] 'indent-according-to-mode)
  176. (define-key map [?\C-c ?\C-c] 'column-marker-here)
  177. (define-key map [?\C-c ?\C-e] nmcobol-eldoc-map)
  178. (define-key map [?\C-c ?\C-f] 'auto-fill-mode)
  179. (define-key map [?\C-c ?\C-o] 'nmcobol-customize-options)
  180. (define-key map [?\C-c ?\C-r] 'popup-ruler)
  181. (define-key map [?\C-c ?\C-s] nmcobol-skeleton-map)
  182. (define-key map [?\C-c ?=] 'nmcobol-addup-pics)
  183. (define-key map [?\C-c return] 'comment-indent-new-line)
  184. (define-key map [?*] 'nmcobol-char-maybe-comment)
  185. (define-key map [?/] 'nmcobol-char-maybe-comment)
  186. map)
  187. "Keymap for `nmcobol-mode'.")
  188. (defun nmcobol-setup-menu ()
  189. "Adds a menu of NMCOBOL specific functions to the menu bar."
  190. (define-key (current-local-map) [menu-bar nmcobol-menu]
  191. (cons "NMCobol" (make-sparse-keymap "NMCobol")))
  192. (define-key (current-local-map) [menu-bar nmcobol-menu customize]
  193. '(menu-item "Customize" nmcobol-customize-options
  194. :key-sequence [?\C-c ?\C-o]
  195. :help "Customize nmcobol-mode options"))
  196. (define-key (current-local-map) [menu-bar nmcobol-menu comment-eol]
  197. '(menu-item "Comment EOL" comment-indent-new-line
  198. :key-sequence [?\C-c return]
  199. :help "Continues comment on new line"))
  200. (if (featurep 'column-marker)
  201. (define-key (current-local-map) [menu-bar nmcobol-menu column]
  202. '(menu-item "Column Marker" column-marker-here
  203. :key-sequence [?\C-c ?\C-c]
  204. :help "Puts column marker at current column (C-u removes)")))
  205. (define-key (current-local-map) [menu-bar nmcobol-menu ruler]
  206. '(menu-item "Ruler" popup-ruler
  207. :key-sequence [?\C-c ?\C-r]
  208. :help "Inserts temporary ruler"))
  209. (define-key (current-local-map) [menu-bar nmcobol-menu eldoc-show]
  210. '(menu-item "Eldoc Where" nmcobol-eldoc-where-def
  211. :key-sequence [?\C-c ?\C-e ?w]
  212. :help "Shows Where function at point is defined"))
  213. (define-key (current-local-map) [menu-bar nmcobol-menu eldoc-visit]
  214. '(menu-item "Eldoc Visit" nmcobol-eldoc-visit-file
  215. :key-sequence [?\C-c ?\C-e ?v]
  216. :help "Visits file defining function at point"))
  217. (define-key (current-local-map) [menu-bar nmcobol-menu eldoc-update]
  218. '(menu-item "Eldoc Scan" nmcobol-eldoc-scan-buffer
  219. :key-sequence [?\C-c ?\C-e ?s]
  220. :help "Updates buffer-local eldoc entries (in memory)"))
  221. (define-key (current-local-map) [menu-bar nmcobol-menu eldoc-create]
  222. '(menu-item "Eldoc Make List" nmcobol-eldoc-make-list
  223. :key-sequence [?\C-c ?\C-e ?m]
  224. :help "Puts current file eldoc entries in a file."))
  225. (define-key (current-local-map) [menu-bar nmcobol-menu skeletons]
  226. (cons "Skeletons" (make-sparse-keymap "Skeletons")))
  227. (define-key (current-local-map) [menu-bar nmcobol-menu skeletons if]
  228. '(menu-item "If Then" nmcobol-if-skel
  229. :key-sequence [?\C-c ?\C-s ?i]
  230. :help "Inserts an If/Then statement"))
  231. (define-key (current-local-map) [menu-bar nmcobol-menu skeletons paragraph]
  232. '(menu-item "New Paragraph" nmcobol-paragraph-skel
  233. :key-sequence [?\C-c ?\C-s ?p]
  234. :help "Inserts comment bars for new paragraph name")))
  235. ;; All keyword lists get sorted so new words can be anywhere within the
  236. ;; appropriate list. The keywords are currently only used for highlighting but
  237. ;; more uses such as abbrev-mode are in progress.
  238. (defvar nmcobol-keywords-directives ;font-lock-preprocessor-face
  239. '( "ANSI" "BLANK" "CALL-SHARED" "CANCEL"
  240. "CHECK" "CODE" "COLUMNS" "COMPACT"
  241. "COMPILE" "CONSULT" "CROSSREF" "DIAGNOSE-74"
  242. "DIAGNOSE-85" "DIAGNOSEALL" "ENDIF" "ENDUNIT"
  243. "ENV" "ERRORFILE" "ERRORS" "FIPS"
  244. "FMAP" "HEADING" "HEAP" "HIGHPIN"
  245. "HIGHREQUESTERS" "ICODE" "IF" "IFNOT"
  246. "INNERLIST" "INSPECT" "LARGEDATA" "LD"
  247. "LESS-CODE" "LIBRARY" "LINES" "LIST"
  248. "LMAP" "MAIN" "MAP" "NLD"
  249. "NOBLANK" "NOCANCEL" "NOCODE" "NOCOMPACT"
  250. "NOCONSULT" "NOCROSSREF" "NODIAGNOSE-74" "NODIAGNOSE-85"
  251. "NODIAGNOSEALL" "NOFIPS" "NOICODE" "NOINNERLIST"
  252. "NOINSPECT" "NOLIST" "NOLMAP" "NOMAP"
  253. "NON-SHARED" "NONSTOP" "NOPORT" "NOSAVEABEND"
  254. "NOSEARCH" "NOSHOWCOPY" "NOSHOWFILE" "NOSQL"
  255. "NOSUPPRESS" "NOSYMBOLS" "NOTRAP2" "NOTRAP2-74"
  256. "NOWARN" "OPTIMIZE" "PERFORM-TRACE" "PORT"
  257. "RESETTOG" "RUNNABLE" "RUNNAMED" "SAVE"
  258. "SAVEABEND" "SEARCH" "SECTION" "SETTOG"
  259. "SHARED" "SHOWCOPY" "SHOWFILE" "SOURCE"
  260. "SQL" "SQLMEM" "SUBSET" "SUBTYPE"
  261. "SUPPRESS" "SYMBOLS" "SYNTAX" "TANDEM"
  262. "TRAP2" "TRAP2-74" "UL" "WARN")
  263. "List of NMCOBOL compiler directives.
  264. Used to create the `font-lock-keywords' table.")
  265. (defvar nmcobol-keywords-imperatives
  266. '( "ACCEPT" "DISPLAY" "MULTIPLY" "STOP"
  267. "ADD" "DIVIDE" "OPEN" "STRING"
  268. "ALTER" "ENTER" "PERFORM" "SUBTRACT"
  269. "CALL" "EXIT" "READ" "UNLOCKFILE"
  270. "CANCEL" "GO TO" "RELEASE" "UNLOCKRECORD"
  271. "INITIALIZE" "REWRITE" "UNSTRING" "CLOSE"
  272. "INSPECT" "SET" "WRITE" "COMPUTE"
  273. "LOCKFILE" "SORT" "CONTINUE" "MERGE"
  274. "START" "DELETE" "MOVE")
  275. "List of NMCOBOL keywords identifying an imperative statement.
  276. Used by indentation routines in their determination of such."
  277. )
  278. (defvar nmcobol-block-keywords
  279. '("ADD" "COMPUTE" "DELETE" "DIVIDE"
  280. "EVALUATE" "IF" "MULTIPLY" "PERFORM"
  281. "READ" "REWRITE" "SEARCH" "START"
  282. "STRING" "SUBTRACT" "UNSTRING" "WRITE"
  283. )
  284. "A list of words that should have open paren syntax - conditionally.
  285. `nmcobol-get-block-type' determines if they actually require the
  286. END-<word> statement." )
  287. (defvar nmcobol-keywords-statements ;font-lock-keyword-face
  288. '("ACCEPT" "ADD" "ALTER" "CALL"
  289. "CANCEL" "CHECKPOINT" "CLOSE" "COMPUTE"
  290. "CONTINUE" "COPY" "DELETE" "DISPLAY"
  291. "DIVIDE" "ELSE" "END" "END-ADD"
  292. "END-COMPUTE" "END-DELETE" "END-DIVIDE" "END-EVALUATE"
  293. "END-IF" "END-MULTIPLY" "END-OF-PAGE" "END-PERFORM"
  294. "END-READ" "END-RECEIVE" "END-RETURN" "END-REWRITE"
  295. "END-SEARCH" "END-START" "END-STRING" "END-SUBTRACT"
  296. "END-UNSTRING" "END-WRITE" "ENTER COBOL" "ENTER"
  297. "EVALUATE" "EXIT" "FD" "FILE"
  298. "GO TO" "IF" "INITIALIZE" "INSPECT"
  299. "LOCKFILE" "MERGE" "MOVE" "MULTIPLY"
  300. "OPEN" "PERFORM" "READ" "RELEASE"
  301. "REPLACE" "RETURN" "REWRITE" "SD"
  302. "SEARCH" "SELECT" "SET" "SORT"
  303. "START" "STARTBACKUP" "STOP" "STRING"
  304. "SUBTRACT" "THEN" "UNLOCKFILE" "UNLOCKRECORD"
  305. "UNSTRING" "USE" "WHEN" "WRITE" )
  306. "List of NMCOBOL statement keywords.
  307. Used to create the `font-lock-keywords' table.")
  308. (defvar nmcobol-keywords-deprecated ;font-lock-warning-face
  309. '( "STARTBACKUP" "CHECKPOINT")
  310. "List of NMCOBOL keywords and Builtin functions now deprecated.
  311. Used to create the `font-lock-keywords' table")
  312. (defvar nmcobol-keywords-reserved ;font-lock-type-face
  313. '( "ACCEPT" "ACCESS" "ADD"
  314. "ADDRESS" "ADVANCING" "AFTER"
  315. "ALL" "ALPHABET" "ALPHABETIC"
  316. "ALPHABETIC-LOWER" "ALPHABETIC-UPPER" "ALPHANUMERIC"
  317. "ALPHANUMERIC-EDITED" "ALSO" "ALTER"
  318. "ALTERNATE" "AND" "ANY"
  319. "APPROXIMATE" "AREA" "AREAS"
  320. "ASCENDING" "ASSIGN" "AT"
  321. "AUTHOR" "BEFORE" "BINARY"
  322. "BLANK" "BLOCK" "BOTTOM"
  323. "BY" "CALL" "CANCEL"
  324. "CD" "CF" "CH"
  325. "CHARACTER" "CHARACTERS" "CHARACTER-SET"
  326. "CHECKPOINT" "CLASS" "CLOCK-UNITS"
  327. "CLOSE" "COBOL" "CODE"
  328. "CODE-SET" "COLLATING" "COLUMN"
  329. "COMMA" "COMMON" "COMMUNICATION"
  330. "COMP" "COMP-3" "COMP-5"
  331. "COMPUTATIONAL" "COMPUTATIONAL-3" "COMPUTATIONAL-5"
  332. "COMPUTE" "CONFIGURATION" "CONTAINS"
  333. "CONTENT" "CONTINUE" "CONTROL"
  334. "CONTROLS" "CONVERTING" "COPY"
  335. "CORR" "CORRESPONDING" "COUNT"
  336. "CURRENCY" "DATA" "DATE"
  337. "DATE-COMPILED" "DATE-WRITTEN" "DAY"
  338. "DAY-OF-WEEK" "DE" "DEBUG-CONTENTS"
  339. "DEBUG-ITEM" "DEBUG-LINE" "DEBUG-SUB-2"
  340. "DEBUG-SUB-3" "DEBUGGING" "DECIMAL-POINT"
  341. "DECLARATIVES" "DEBUG-NAME" "DEBUG-SUB-1"
  342. "DELETE" "DELIMITED" "DELIMITER"
  343. "DEPENDING" "DESCENDING" "DESTINATION"
  344. "DETAIL" "DISABLE" "DISPLAY"
  345. "DIVIDE" "DIVISION" "DOWN"
  346. "DUPLICATES" "DYNAMIC" "EGI"
  347. "ELSE" "EMI" "ENABLE"
  348. "END" "END-ADD" "END-COMPUTE"
  349. "END-DELETE" "END-DIVIDE" "END-EVALUATE"
  350. "END-IF" "END-MULTIPLY" "END-OF-PAGE"
  351. "END-PERFORM" "END-READ" "END-RECEIVE"
  352. "END-RETURN" "END-REWRITE" "END-SEARCH"
  353. "END-START" "END-STRING" "END-SUBTRACT"
  354. "END-UNSTRING" "END-WRITE" "ENTER"
  355. "EOP" "EQUAL" "ERROR"
  356. "ESI" "EVALUATE" "EVERY"
  357. "EXCEPTION" "EXCLUSIVE" "EXIT"
  358. "EXTEND" "EXTENDED-STORAGE" "EXTERNAL"
  359. "FALSE" "FD" "FILE"
  360. "FILE-CONTROL" "FILLER" "FINAL"
  361. "FIRST" "FOOTING" "FOR"
  362. "FROM" "FUNCTION" "GENERATE"
  363. "GENERIC" "GIVING" "GLOBAL"
  364. "GO" "GREATER" "GROUP"
  365. "GUARDIAN-ERR" "HEADING" "HIGH-VALUE"
  366. "HIGH-VALUES" "I-O" "I-O-CONTROL"
  367. "IDENTIFICATION" "IF" "IN"
  368. "INDEX" "INDEXED" "INDICATE"
  369. "INITIAL" "INITIALIZE" "INITIATE"
  370. "INPUT" "INPUT-OUTPUT" "INSPECT"
  371. "INSTALLATION" "INTO" "INVALID"
  372. "IS" "JUST" "JUSTIFIED"
  373. "KEY" "LABEL" "LAST"
  374. "LEADING" "LEFT" "LENGTH"
  375. "LESS" "LIMIT" "LIMITS"
  376. "LINAGE" "LINAGE-COUNTER" "LINE"
  377. "LINE-COUNTER" "LINKAGE" "LOCK"
  378. "LOCKFILE" "LOW-VALUE" "LOW-VALUES"
  379. "MEMORY" "MERGE" "MESSAGE"
  380. "MODE" "MODULES" "MOVE"
  381. "MULTIPLE" "MULTIPLY" "NATIVE"
  382. "NEGATIVE" "NEXT" "NO"
  383. "NOT" "NULL" "NULLS"
  384. "NUMBER" "NUMERIC" "NUMERIC-EDITED"
  385. "OBJECT-COMPUTER" "OCCURS" "OF"
  386. "OFF" "OMITTED" "ON"
  387. "OPEN" "OPTIONAL" "OR"
  388. "ORDER" "ORGANIZATION" "OTHER"
  389. "OUTPUT" "OVERFLOW" "PACKED-DECIMAL"
  390. "PADDING" "PAGE" "PAGE-COUNTER"
  391. "PERFORM" "PF" "PH"
  392. "PIC" "PICTURE" "PLUS"
  393. "POINTER" "POSITION" "POSITIVE"
  394. "PRINTING" "PROCEDURE" "PROCEDURES"
  395. "PROCEED" "PROGRAM" "PROGRAM-ID"
  396. "PROGRAM-STATUS" "PROGRAM-STATUS-1" "PROGRAM-STATUS-2"
  397. "PROMPT" "PROTECTED" "PURGE"
  398. "QUEUE" "QUOTE" "QUOTES"
  399. "RANDOM" "RD" "READ"
  400. "RECEIVE" "RECEIVE-CONTROL" "RECORD"
  401. "RECORDS" "REDEFINES" "REEL"
  402. "REFERENCE" "REFERENCES" "RELATIVE"
  403. "RELEASE" "REMAINDER" "REMOVAL"
  404. "RENAMES" "REPLACE" "REPLACING"
  405. "REPLY" "REPORT" "REPORTING"
  406. "REPORTS" "RERUN" "RESERVE"
  407. "RESET" "RETURN" "REVERSED"
  408. "REWIND" "REWRITE" "RF"
  409. "RH" "RIGHT" "ROUNDED"
  410. "RUN" "SAME" "SD"
  411. "SEARCH" "SECTION" "SECURITY"
  412. "SEGMENT" "SEGMENT-LIMIT" "SELECT"
  413. "SEND" "SENTENCE" "SEPARATE"
  414. "SEQUENCE" "SEQUENTIAL" "SET"
  415. "SHARED" "SIGN" "SIZE"
  416. "SORT" "SORT-MERGE" "SOURCE"
  417. "SOURCE-COMPUTER" "SPACE" "SPACES"
  418. "SPECIAL-NAMES" "STANDARD" "STANDARD-1"
  419. "STANDARD-2" "START" "STARTBACKUP"
  420. "STATUS" "STOP" "STRING"
  421. "SUB-QUEUE-1" "SUB-QUEUE-2" "SUB-QUEUE-3"
  422. "SUBTRACT" "SUM" "SUPPRESS"
  423. "SYMBOLIC" "SYNC" "SYNCDEPTH"
  424. "SYNCHRONIZED" "TABLE" "TAL"
  425. "TALLYING" "TAPE" "TERMINAL"
  426. "TERMINATE" "TEST" "TEXT"
  427. "THAN" "THEN" "THROUGH"
  428. "THRU" "TIME" "TIMES"
  429. "TO" "TOP" "TRAILING"
  430. "TRUE" "TYPE" "UNIT"
  431. "UNLOCK" "UNLOCKFILE" "UNLOCKRECORD"
  432. "UNSTRING" "UNTIL" "UP"
  433. "UPON" "USAGE" "USE"
  434. "USING" "VALUE" "VALUES"
  435. "VARYING" "WHEN" "WITH"
  436. "WORDS" "WORKING-STORAGE" "WRITE"
  437. "ZERO" "ZEROES")
  438. "List of NMCOBOL keywords reserved only in certain language contexts.
  439. Used to create the `font-lock-keywords' table.")
  440. (defvar nmcobol-keywords-std-fcns ;font-lock-keyword-face
  441. '( "ACOS" "ANNUITY" "ASIN"
  442. "ATAN" "CHAR" "COS"
  443. "CURRENT-DATE" "DATE-OF-INTEGER" "DAY-OF-INTEGER"
  444. "FACTORIAL" "INTEGER" "INTEGER-OF-DATE"
  445. "INTEGER-OF-DAY" "INTEGER-PART" "LENGTH"
  446. "LOG" "LOG10" "LOWER-CASE"
  447. "MAX" "MEAN" "MEDIAN"
  448. "MIDRANGE" "MIN" "MOD"
  449. "NUMVAL" "NUMVAL-C" "ORD"
  450. "ORD-MAX" "ORD-MIN" "PRESENT-VALUE"
  451. "RANDOM" "RANGE" "REM"
  452. "REVERSE" "SIN" "SQRT"
  453. "STANDARD-DEVIATION" "SUM" "TAN"
  454. "UPPER-CASE" "VARIANCE" "WHEN-COMPILED")
  455. "List of NMCOBOL standard functions.
  456. Used to create the `font-lock-keywords' table.")
  457. (defvar nmcobol-keywords-privileged ;font-lock-warning-face
  458. '( "END-EXEC" "EXEC")
  459. "List of NMCOBOL privileged functions.
  460. Used to create the `font-lock-keywords' table.")
  461. (defvar nmcobol-keywords-builtin ;font-lock-variable-name-face
  462. '( "#IN" "#OUT"
  463. "#TERM" "#TEMP"
  464. "#DYNAMIC" "COBOL85^ARMTRAP"
  465. "COBOL85^COMPLETION" "COBOL_COMPLETION_"
  466. "COBOL_CONTROL_" "COBOL_GETENV_"
  467. "COBOL_PUTENV_" "COBOL85^RETURN^SORT^ERRORS"
  468. "COBOL_RETURN_SORT_ERRORS_" "COBOL85^REWIND^SEQUENTIAL"
  469. "COBOL_REWIND_SEQUENTIAL_" "COBOL85^SET^SORT^PARAM^TEXT"
  470. "COBOL_SET_SORT_PARAM_TEXT_" "COBOL85^SET^SORT^PARAM^VALUE"
  471. "COBOL_SET_SORT_PARAM_VALUE_" "COBOL_SET_MAX_RECORD_"
  472. "COBOL_SETMODE_" "COBOL85^SPECIAL^OPEN"
  473. "COBOL_SPECIAL_OPEN_" "COBOLASSIGN"
  474. "COBOL_ASSIGN_" "COBOLFILEINFO"
  475. "COBOL_FILE_INFO_" "COBOLSPOOLOPEN"
  476. "CREATEPROCESS" "ALTERPARAMTEXT"
  477. "CHECKLOGICALNAME" "CHECKMESSAGE"
  478. "DELETEASSIGN" "DELETEPARAM"
  479. "DELETESTARTUP" "GETASSIGNTEXT"
  480. "GETASSIGNVALUE" "GETBACKUPCPU"
  481. "GETPARAMTEXT" "GETSTARTUPTEXT"
  482. "PUTASSIGNTEXT" "PUTASSIGNVALUE"
  483. "PUTPARAMTEXT" "PUTSTARTUPTEXT")
  484. "List of NMCOBOL privileged builtin functions.
  485. Used to create the `font-lock-keywords' table.")
  486. (defcustom nmcobol-block-always-keywords
  487. '("ELSE" "EVALUATE" "IF" "SEARCH" "THEN"
  488. "WHEN" )
  489. "List of keywords that always require and END-<word>. Used in paren matching."
  490. :type '(repeat (string :tag "word"))
  491. :group 'nmcobol)
  492. (defcustom nmcobol-keyword-section-names-regexp
  493. "^\\s-\\{1,3\\}\\(\\w+\\)\\s-+\\(division\\(\\s-+using\\s-+[^.\n]+\\)?\\|section\\) *\\."
  494. "Defines a regexp that finds the names of divisions & sections.
  495. Used to create the `font-lock-keywords' table."
  496. :type 'regexp
  497. :group 'nmcobol)
  498. (defcustom nmcobol-keyword-fcn-names-regexp
  499. "^\\s-\\{1,3\\}\\(\\w+\\)\\s-*\\."
  500. "Defines a regexp that finds the names of paragraphs.
  501. Used by `font-lock-keywords'. See also `nmcobol-imenu-fcn-names-regexp'"
  502. :type 'regexp
  503. :group 'nmcobol)
  504. ;;; Build keyword regexp from keyword lists
  505. (defvar nmcobol-keywords-imperatives-regexp ()
  506. "regexp matching `nmcobol-keywords-imperatives'")
  507. (defvar nmcobol-keywords-statements-regexp ()
  508. "regexp matching `nmcobol-keywords-statements'")
  509. (defvar nmcobol-keywords-reserved-regexp ()
  510. "regexp matching `nmcobol-keywords-reserved'")
  511. (defvar nmcobol-block-always-regexp ()
  512. "regexp matching `nmcobol-block-always-keywords'")
  513. (defvar nmcobol-block-begin-regexp ()
  514. "regexp matching `nmcobol-block-keywords'")
  515. (defvar nmcobol-block-end-regexp ()
  516. "regexp matching \"END-\" + `nmcobol-block-keywords'")
  517. (defvar nmcobol-non-cobol-regexp
  518. "^[*/?]"
  519. "Expression describing comment and compiler directive lines.")
  520. (defun nmcobol-setup-regexp-vars ()
  521. "Rebuilds regexp variables from keyword lists."
  522. (setq nmcobol-keywords-imperatives-regexp
  523. (concat (regexp-opt nmcobol-keywords-imperatives t) "\\(\\s-\\|\\.\\)")
  524. nmcobol-keywords-statements-regexp
  525. (concat "^\\([ \t]+\\)"
  526. (regexp-opt nmcobol-keywords-statements t) "\\(\\s-\\|\\.\\)")
  527. nmcobol-keywords-reserved-regexp ;not used!
  528. (nmcobol-keyword-anywhere-regexp nmcobol-keywords-reserved)
  529. nmcobol-block-always-regexp
  530. (concat (regexp-opt nmcobol-block-always-keywords t) "\\(\\s-\\|\\.\\)")
  531. nmcobol-block-begin-regexp
  532. (regexp-opt nmcobol-block-keywords t)
  533. nmcobol-block-end-regexp
  534. (concat "END-" (regexp-opt nmcobol-block-keywords t))))
  535. ;;; Font lock (highlighting)
  536. (defcustom nmcobol-font-lock-always t
  537. "`nmcobol-mode' makes sure `font-lock-mode' is on for nmcobol-mode buffers.
  538. Some things don't work if it's off so insuring it's on is the default."
  539. :type 'boolean
  540. :group 'nmcobol)
  541. (defcustom nmcobol-primecode-warning t
  542. "Highlight instances of ]a ]d and ]e in column 1 with a warning face.
  543. This alerts you that submission of this file to RMS/PrimeCode will fail
  544. due to invalid contents. nil disables this warning."
  545. :type 'boolean
  546. :group 'nmcobol)
  547. (defun nmcobol-keyword-special-regexp ( word-list )
  548. "Returns a regexp that finds any of the words in WORD-LIST.
  549. But only if the keyword is surrounded by non-word chars."
  550. (concat "\\W"(regexp-opt word-list t)"\\W"))
  551. (defun nmcobol-keyword-anywhere-regexp ( word-list )
  552. "Returns a regexp that finds any of the words in WORD-LIST.
  553. But only if the keyword is surrounded by non-word chars."
  554. (concat "\\b"(regexp-opt word-list t)"\\b"))
  555. ;; The next 4 def's work tightly together and, as coded, cannot be reused for
  556. ;; additional purposes.
  557. (defvar nmcobol-keyword-on-directive-line-regexp () "Internal use only.")
  558. (defun nmcobol-keyword-on-directive-line-regexp ( word-list )
  559. "Returns a function to find WORD-LIST only if line starts with ?"
  560. (setq nmcobol-keyword-on-directive-line-regexp
  561. (concat "\\b"(regexp-opt word-list t)"\\b"))
  562. 'nmcobol-font-lock-directive-line)
  563. (defvar nmcobol-amid-font-lock-excursion nil
  564. "Used by `nmcobol-font-lock-directive-line'. When a line starting with
  565. ? in column 1 is detected this variable holds the context needed to
  566. continue searching for more keywords. If nil a line starting with ?
  567. should be searched for.")
  568. (make-variable-buffer-local 'nmcobol-amid-font-lock-excursion)
  569. (defun nmcobol-font-lock-directive-line ( search-limit )
  570. "This function finds keywords only in lines starting with ?. Valid
  571. keywords are described by `nmcobol-keyword-on-directive-line-regexp'.
  572. First a line beginning with ? is searched for. Once found, point is
  573. moved to the beginning of that area and limit is set to the end.
  574. Keywords are searched for within that range. If found, context is saved
  575. in nmcobol-amid-font-lock-excursion and the match-data is returned. If
  576. not found, another line starting with ? is searched for. If saved
  577. context exists when this function is called then another keyword is
  578. searched for in the previously narrowed region. If none is found the
  579. next region is searched for."
  580. (let ((looking t))
  581. (while
  582. (and looking
  583. (or nmcobol-amid-font-lock-excursion
  584. (when (re-search-forward "^\\?.+\n" search-limit t)
  585. (setq nmcobol-amid-font-lock-excursion (point))
  586. (goto-char (match-beginning 0)))))
  587. (if (re-search-forward nmcobol-keyword-on-directive-line-regexp
  588. nmcobol-amid-font-lock-excursion t)
  589. (setq looking nil)
  590. (goto-char nmcobol-amid-font-lock-excursion)
  591. (setq nmcobol-amid-font-lock-excursion nil)))
  592. (not looking)))
  593. (defvar nmcobol-find-syntactic--state ()
  594. "Used by `nmcobol-find-syntactic-keywords' to find multiple syntactic
  595. elements which all must be anchored to the beginning of a line.
  596. nil = No searches on this line yet. skip line if it's a directive.
  597. 0 = look for sequence number in col 1-6 (removed)
  598. 1 = sequence/label area checked. look at body.
  599. 2 = body not a comment, any trailing comment marked, check for strings
  600. marker = terminated string found check for more.")
  601. (make-variable-buffer-local 'nmcobol-find-syntactic--state)
  602. (defun nmcobol-find-syntactic-keywords ( search-limit )
  603. "Used by `font-lock-syntactic-keywords' to find comments and strings.
  604. Returns t if either a comment or string is found, nil if neither is found.
  605. match-data 1&2 are set for comments, 3&4 are set for a normal string, 5&6 are
  606. set for eol-terminated strings. Where the match pair mark the start character
  607. and end character respectively. Point is moved to the next line during this
  608. function only after the last search completes for the current line. A state
  609. machine, controlled by `nmcobol-find-syntactic--state' sequences the searches."
  610. (let ((found nil)
  611. (save (point)))
  612. (while (and (< (point) search-limit)
  613. (not found))
  614. (cond
  615. ;; no comments or quotes? in compiler directives
  616. ((or (null nmcobol-find-syntactic--state)
  617. (equal nmcobol-find-syntactic--state (make-marker)))
  618. (if (looking-at "^\\?")
  619. (forward-line 1) ;do this state on next line
  620. (setq nmcobol-find-syntactic--state 1) ;do next state on this line
  621. ))
  622. ;; see if entire line is a comment
  623. ((= 1 nmcobol-find-syntactic--state)
  624. (if (not (looking-at "^\\(?:*\\|/\\)"))
  625. (setq nmcobol-find-syntactic--state 2) ;goto next state
  626. ;; else set match data and point to next line
  627. (looking-at "\\(.\\).*\\(\n\\|\\'\\)") ;setup match-data
  628. (forward-line 1) ;next iteration looks at next line
  629. (setq nmcobol-find-syntactic--state ()
  630. found t)))
  631. ;; look for strings only within columns 8-72 inclusive
  632. ((= 2 nmcobol-find-syntactic--state)
  633. (if (looking-at "^[-d D][^\"\n]\\{0,130\\}\"")
  634. (let* ((open-quote (list (copy-marker (1- (match-end 0)))
  635. (copy-marker (match-end 0))))
  636. (leol (copy-marker (line-end-position)))
  637. close-quote)
  638. (setq found t)
  639. (goto-char (cadr open-quote))
  640. (if (search-forward "\"" leol t)
  641. (progn ; normally ending string
  642. (setq close-quote (match-data)
  643. nmcobol-find-syntactic--state (cadr close-quote))
  644. (beginning-of-line)
  645. (set-match-data
  646. `(,(car open-quote) ,(cadr close-quote)
  647. nil nil nil nil ;match-string 1&2 not found
  648. ,@open-quote ,@close-quote)) ;3&4 are normal string
  649. )
  650. ;; implicit string end
  651. (forward-line 1) ;next iteration looks at next line
  652. (setq close-quote (list (copy-marker (1- leol)) leol)
  653. nmcobol-find-syntactic--state ())
  654. (set-match-data
  655. `(,(car open-quote) ,(cadr close-quote)
  656. nil nil nil nil ;match-string 1&2 not found
  657. nil nil nil nil ;match-string 3&4 not found
  658. ,@open-quote ,@close-quote)) ;5&6 unterminated string
  659. ))
  660. ;; no string was found. Start new analysis on next line
  661. (forward-line 1)
  662. (setq nmcobol-find-syntactic--state ())))
  663. ;; a string has been found look for another after it
  664. ((markerp nmcobol-find-syntactic--state)
  665. (let ((leol (copy-marker (line-end-position)))
  666. open-quote close-quote)
  667. (goto-char nmcobol-find-syntactic--state)
  668. (if (search-forward "\"" leol t)
  669. (progn
  670. (setq open-quote (match-data)
  671. found t)
  672. (if (search-forward "\"" leol t)
  673. (progn ; normally ending string
  674. (beginning-of-line) ;next iteration starts here again
  675. (setq close-quote (match-data)
  676. nmcobol-find-syntactic--state (cadr close-quote))
  677. (set-match-data
  678. `(,(car open-quote) ,(cadr close-quote)
  679. nil nil nil nil ;match-string 1&2 not found
  680. ,@open-quote ,@close-quote)) ;3&4 normal string
  681. )
  682. ;; implicit string end
  683. (forward-line 1) ;next iteration looks at next line
  684. (setq close-quote (list (copy-marker (1- leol)) leol)
  685. nmcobol-find-syntactic--state ())
  686. (set-match-data
  687. `(,(car open-quote) ,(cadr close-quote)
  688. nil nil nil nil ;match-string 1&2 not found
  689. nil nil nil nil ;match-string 3&4 not found
  690. ,@open-quote ,@close-quote)) ;5&6 unterminated string
  691. ))
  692. (forward-line 1)
  693. (setq nmcobol-find-syntactic--state ()))))))
  694. ;; Point should not return forward of search-limit
  695. (and (> (point) search-limit) (goto-char search-limit))
  696. ;; point shouldn't move if nothing was found.
  697. (prog1 found (or found (goto-char save)))))
  698. (defvar nmcobol-static-font-lock-keywords
  699. ;; font-lock-keywords is a symbol or list of symbols yielding the keywords to
  700. ;; be fontified. Keywords are listed here using either (MATCHER . FACENAME)
  701. ;; or (MATCHER . (MATCH FACENAME)) syntax. Other options are available but
  702. ;; not used here. For simplicity, all regexp's were designed so MATCH would
  703. ;; be 1. Nothing forced this but to me it makes debug/maintenance easier.
  704. `(("^\\([^ ?Dd*/-]\\)" 1 font-lock-warning-face)
  705. ("^\\([?Dd-]\\)" 1 font-lock-builtin-face)
  706. (,nmcobol-keyword-section-names-regexp
  707. 1 font-lock-function-name-face)
  708. (,(nmcobol-keyword-on-directive-line-regexp nmcobol-keywords-directives)
  709. 1 font-lock-preprocessor-face)
  710. (,(nmcobol-keyword-anywhere-regexp nmcobol-keywords-builtin)
  711. 1 font-lock-variable-name-face)
  712. (,(nmcobol-keyword-special-regexp nmcobol-keywords-statements)
  713. 1 font-lock-keyword-face)
  714. (,(nmcobol-keyword-anywhere-regexp nmcobol-keywords-std-fcns)
  715. 1 font-lock-keyword-face)
  716. (,(nmcobol-keyword-anywhere-regexp (append nmcobol-keywords-deprecated
  717. nmcobol-keywords-privileged))
  718. 1 font-lock-warning-face)
  719. (,(nmcobol-keyword-anywhere-regexp nmcobol-keywords-reserved)
  720. 1 font-lock-type-face)
  721. (,nmcobol-keyword-fcn-names-regexp
  722. 1 font-lock-function-name-face)))
  723. (defvar nmcobol-font-lock-keywords ())
  724. (defun nmcobol-build-font-lock-keywords ()
  725. "Creates `font-lock-keywords' based on current customize settings."
  726. (append nmcobol-static-font-lock-keywords
  727. `(,(when nmcobol-primecode-warning
  728. ;; ]a ]d or ]e cannot appear in col 1-2 if using PrimeCode.
  729. '("^\\][ade]" . font-lock-warning-face)))))
  730. (defvar nmcobol-this-paren-type ()
  731. "Used internally by `nmcobol-font-lock-syntactic-keywords'"
  732. )
  733. (defvar nmcobol-block-symbol-prefix "nmcobol-blk-typ-"
  734. "Paren matching symbols are built using this prefix.
  735. You probably don't want to change it "
  736. )
  737. (defcustom nmcobol-else-gets-paren-syntax t
  738. "When `show-paren-mode' is ON and this is nil, IF always matches END-IF only.
  739. When this variable is non-nil, IF can match ELSE and ELSE matches END-IF."
  740. :type 'boolean
  741. :group 'nmcobol)
  742. (defun nmcobol-find-paren-words (lim)
  743. "Function to set paren syntactic properties for Cobol's block keywords.
  744. Returns t and sets both `nmcobol-this-paren-type' and appropriate match-data
  745. when keywords requiring paren syntax are seen within range of point & lim."
  746. (let ((looking t) ;looping continues until lim or found (not looking)
  747. open-type ;type of paren needed to match this open
  748. close-type ;type of paren needed to match this close
  749. open-loc
  750. close-loc
  751. word)
  752. ;; walk thru each statement
  753. (while (and looking
  754. (re-search-forward nmcobol-keywords-statements-regexp
  755. lim 'move-anyway))
  756. ;; Save the word and it's location in case paren class is to be applied
  757. (setq word (match-string-no-properties 2)
  758. open-loc (match-beginning 2)
  759. close-loc (1- (match-end 2)))
  760. ;; Look for the different types of paren class we might apply
  761. (cond
  762. ;; The ELSE keyword can be ignored or treated as both open and close
  763. ((and nmcobol-else-gets-paren-syntax (string= "ELSE" word))
  764. ;; The leading E of Else will match the I of IF and the trailing
  765. ;; E should match the F of END-IF. When no ELSE is present the I
  766. ;; of IF will match the F of END-IF.
  767. (setq looking nil
  768. word open-loc
  769. open-loc close-loc
  770. close-loc word
  771. close-type (setq open-type
  772. (intern
  773. (concat nmcobol-block-symbol-prefix "IF")))))
  774. ;; close paren keywords always have close paren syntax
  775. ((string-match nmcobol-block-end-regexp word)
  776. (setq close-type
  777. (intern
  778. (concat nmcobol-block-symbol-prefix
  779. (upcase (match-string-no-properties 1 word))))
  780. looking nil))
  781. ;; 'possible' open paren keywords. More checking needed.
  782. ((string-match nmcobol-block-begin-regexp word)
  783. (let ((next (point)))
  784. (if (re-search-forward nmcobol-keywords-statements-regexp
  785. lim 'move-anyway)
  786. (setq next (match-beginning 0)))
  787. (goto-char open-loc)
  788. (if (setq open-type (nmcobol-get-block-type next))
  789. (setq looking nil))
  790. (goto-char (1+ close-loc))))))
  791. ;; Build the response data
  792. (if looking
  793. ;; nil is returned if no paren syntax to apply
  794. (set-match-data ())
  795. ;; both the match data and the paren type data must be set
  796. (set-match-data
  797. (append (list open-loc (1+ close-loc))
  798. (if open-type
  799. (list open-loc (1+ open-loc))
  800. (list () ()))
  801. (if close-type
  802. (list close-loc (1+ close-loc)))))
  803. (setq nmcobol-this-paren-type
  804. (cons
  805. ;; declare the open-paren type
  806. (cons 4 open-type)
  807. ;; declare the close-paren type
  808. (cons 5 close-type)))
  809. ;; t must be returned iff match-data should be acted upon
  810. t)))
  811. (defvar nmcobol-font-lock-syntactic-keywords
  812. `(
  813. ;; nmcobol-find-syntactic-keywords returns matches 1&2 for comments, 3&4
  814. ;; for strings. 5&6 for eol terminated strings. I must use "|"(15)
  815. ;; rather than "\""(7) for eol terminated strings because the begin
  816. ;; and end characters must be the same when "\""(7) is used.
  817. (nmcobol-find-syntactic-keywords (1 "<" t t) (2 ">" t t)
  818. (3 "\"" t t) (4 "\"" t t)
  819. (5 "|" t t) (6 "|" t t))
  820. ;; nmcobol-find-paren-words returns match 1 for open paren syntax and
  821. ;; match 2 for close paren syntax. The car of nmcobol-this-paren-type
  822. ;; is used for open paren type and the cdr for close type. The types
  823. ;; must be configurable due to the large number of them.
  824. (nmcobol-find-paren-words (1 (car nmcobol-this-paren-type) t t)
  825. (2 (cdr nmcobol-this-paren-type) t t)
  826. )
  827. )
  828. "A list of regexp's or functions. Used to add syntax-table properties to
  829. characters that can't be set by the syntax-table alone.")
  830. ;(defun nmcobol-start-of-statement-hook ()
  831. ; "Used as `font-lock-extend-region-functions' hook which see."
  832. ; ;; WARNING - any malfunction in this can cause emacs to enter a loop
  833. ; ;; that C-g will not break out of. Your session (and all edits) will
  834. ; ;; be toast.
  835. ; ;; There seems to be no good way to suppress the warning caused by
  836. ; ;; accessing font-lock-beg. It's required but apparently only
  837. ; ;; defined within a let statement.
  838. ; (goto-char font-lock-beg)
  839. ; (unless (bolp) (beginning-of-line))
  840. ; (unless (or (looking-at nmcobol-non-cobol-regexp)
  841. ; (looking-at nmcobol-keywords-statements-regexp))
  842. ; (let* ((lim (progn (nmcobol-backward-sentence 0)(point))))
  843. ; (goto-char font-lock-beg)
  844. ; (re-search-backward
  845. ; nmcobol-keywords-statements-regexp lim 'move-anyway)
  846. ; ;; If I reached the beginning of the sentence, there may be
  847. ; ;; comments and blank lines: they don't need refontification.
  848. ; (when (= (point) lim)
  849. ; (while (or (looking-at "^[ \t]*$")
  850. ; (looking-at nmcobol-non-cobol-regexp))
  851. ; (forward-line)))))
  852. ; (unless (eq (point) font-lock-beg)
  853. ; (setq font-lock-beg (point))))
  854. (defun nmcobol-start-of-statement ()
  855. "Used as the syntax-begin function of `font-lock-defaults' which see."
  856. ;; WARNING - any malfunction in this can cause emacs to enter a loop
  857. ;; that C-g will not break out of. Your session (and all edits) will
  858. ;; be toast.
  859. (unless (bolp) (beginning-of-line))
  860. (unless (or (looking-at nmcobol-non-cobol-regexp)
  861. (looking-at nmcobol-keywords-statements-regexp))
  862. (let* ((start (point))
  863. (lim (progn (nmcobol-backward-sentence 0)(point))))
  864. (goto-char start)
  865. (re-search-backward
  866. nmcobol-keywords-statements-regexp lim 'move-anyway)
  867. ;; If I reached the beginning of the sentence, there may be
  868. ;; comments and blank lines: they don't need refontification.
  869. (when (= (point) lim)
  870. (while (and (< (point) start)
  871. (or (looking-at "^[ \t]*$")
  872. (looking-at nmcobol-non-cobol-regexp)))
  873. (forward-line))
  874. (if (> (point) start)
  875. (goto-char start))))))
  876. (defun nmcobol-setup-font-lock ()
  877. "Sets up the buffer local value for font-lock-defaults and optionally
  878. turns on font-lock-mode"
  879. ;; I use font-lock-syntactic-keywords to set some properties and I
  880. ;; don't want them ignored.
  881. (set (make-local-variable 'parse-sexp-lookup-properties) t)
  882. ;; I really can't imagine anyone wanting this off.
  883. (set (make-local-variable 'parse-sexp-ignore-comments) t)
  884. ;; This allows column markers to be different in separate buffers.
  885. (set (make-local-variable 'nmcobol-font-lock-keywords)
  886. (nmcobol-build-font-lock-keywords))
  887. ;; make sure the parsing state is reset
  888. (setq nmcobol-find-syntactic--state ())
  889. ; (setq font-lock-extend-region-functions
  890. ; (append font-lock-extend-region-functions
  891. ; '(nmcobol-start-of-statement-hook)))
  892. ;; This is where all the font-lock stuff actually gets set up. Once
  893. ;; font-lock-defaults has it's value, setting font-lock-mode true should
  894. ;; cause all your syntax highlighting dreams to come true.
  895. (setq font-lock-defaults
  896. ;; The first value is all the keyword expressions.
  897. '(nmcobol-font-lock-keywords
  898. ;; keywords-only means no strings or comments get fontified
  899. nil
  900. ;; case-fold (ignore case)
  901. t
  902. ;; syntax-alist. See also imenu-syntax-alist
  903. ;; This can't override syntax applied by font-lock-syntactic-keywords
  904. ;; It only overrides nmcobol-mode-syntax-table.
  905. nil
  906. ;; syntax-begin - function to move outside syntactic block
  907. ;; This doesn't work for some reason. I'm using the hook
  908. ;; font-lock-extend-region-functions for this.
  909. nil ; nmcobol-start-of-statement
  910. ;; font-lock-syntactic-keywords
  911. ;; takes (matcher (match syntax override lexmatch) ...)...
  912. (font-lock-syntactic-keywords . nmcobol-font-lock-syntactic-keywords)))
  913. ; font lock is turned on by default in this mode. Use customize to disable.
  914. (when nmcobol-font-lock-always (font-lock-mode t)))
  915. ;;; Static Column Markers
  916. (defcustom nmcobol-column-marker-1 79
  917. "*Turns on column-marker-1 (which see) at the specified column.
  918. Use of this feature requires the column-marker.el package be loaded or on
  919. the search list."
  920. :type 'integer
  921. :group 'nmcobol)
  922. (make-variable-buffer-local 'nmcobol-column-marker-1)
  923. (defcustom nmcobol-column-marker-2 0
  924. "*Turns on column-marker-2 (which see) at the specified column.
  925. Use of this feature requires the column-marker.el package."
  926. :type 'integer
  927. :group 'nmcobol)
  928. (make-variable-buffer-local 'nmcobol-column-marker-2)
  929. (defun nmcobol-setup-column-markers ()
  930. "Turns on column markers if configured and available.
  931. See `nmcobol-column-marker-1' and `nmcobol-column-marker-2' "
  932. (if (not (condition-case () (require 'column-marker) (error nil)))
  933. (if (not (and (zerop nmcobol-column-marker-1)
  934. (zerop nmcobol-column-marker-2)))
  935. (message "column markers are configured but %s"
  936. " column-marker feature not available."))
  937. (setq indent-tabs-mode nil) ;documented as buffer local
  938. (column-marker-1 nmcobol-column-marker-1)
  939. (column-marker-2 nmcobol-column-marker-2)))
  940. ;;; Imenu & Which-function
  941. (defcustom nmcobol-imenu-menubar t
  942. "If not nil, `imenu-add-to-menubar' is called during mode initialization.
  943. This adds a [Menu name] menu to your menu bar. By default the menu
  944. contains a list of all procedures, sections and pages in your program.
  945. You can go directly to any item on the menu by selecting it. You can
  946. control what appears on this menu by modifying
  947. `nmcobol-imenu-expression-alist'. You must turn imenu on for this to
  948. work. See `imenu' in the Emacs reference manual for more information.
  949. Personally I recommend customizing `imenu-sort-function' to sort by
  950. name."
  951. :type '(choice :tag "Menu Name"
  952. (string :tag "Menu Name")
  953. (const :tag "Index" t)
  954. (const :tag "None" nil))
  955. :group 'nmcobol)
  956. (defvar nmcobol-imenu-syntax-alist ()
  957. "Overrides to `nmcobol-mode-syntax-table' used during `imenu-generic-expression' search."
  958. ;;AFAIK there are no character adjustments needed during imenu search.
  959. )
  960. (defcustom nmcobol-imenu-fcn-names-regexp
  961. "^[ \t]\\{1,3\\}\\(\\w+\\([^T]\\|[^I]T\\|[^X]IT\\|[^E]XIT\\|[^-]EXIT\\)\\)[ \t]*\\."
  962. "Defines a regexp that finds the names of paragraphs.
  963. Used to build the `imenu' index."
  964. :type 'regexp
  965. :group 'nmcobol)
  966. (defcustom nmcobol-imenu-expression-alist
  967. `((nil ,nmcobol-imenu-fcn-names-regexp 1)
  968. ("Divisions/Sections" ,nmcobol-keyword-section-names-regexp 1)
  969. ("?Sections" "^\\?section\\s-+\\(\\w+\\)\\b" 1)
  970. ("?Pages" "^\\?page\\s-+\"\\s-*\\(.+?\\)\"" 1))
  971. "A list of regular expressions for creating an `imenu' index.
  972. Each element has the form (list-name regexp num).
  973. Where list-name is the name of the submenu under which items matching regexp
  974. are found and num is the expression index defining the label to use for the
  975. submenu entry. When num = 0 the entire matching regexp text appears under
  976. list-name. When list-name is nil the matching entries appear in the root
  977. imenu list rather than in a submenu. See also `nmcobol-imenu-menubar'"
  978. :type '(repeat (list (choice :tag "Submenu Name" string (const nil))
  979. regexp (integer :tag "Regexp index")))
  980. :group 'nmcobol)
  981. (defcustom nmcobol-display-which-function t
  982. "This option turns `which-func' on for all `nmcobol-mode' buffers.
  983. `which-func' is a package that causes the current function, section or
  984. page to be displayed on the mode line. `which-func' uses `imenu'. Also
  985. see `nmcobol-imenu-expression-alist' for more information."
  986. :type 'boolean
  987. :group 'nmcobol)
  988. (defun nmcobol-setup-imenu ()
  989. "Installs nmcobol-imenu-generic-expression & nmcobol-imenu-syntax-alist."
  990. ;; imenu doc says these 3 are buffer-local by default
  991. (setq imenu-generic-expression nmcobol-imenu-expression-alist)
  992. (setq imenu-syntax-alist nmcobol-imenu-syntax-alist)
  993. (setq imenu-case-fold-search t) ;NMCOBOL are never case sensitive
  994. (when nmcobol-imenu-menubar
  995. (if (condition-case ()
  996. (progn (require 'imenu) t)
  997. (error nil))
  998. (imenu-add-menubar-index)
  999. (message "nmcobol-imenu-menubar is set but imenu feature not available.")))
  1000. (when nmcobol-display-which-function
  1001. (if (condition-case ()
  1002. (progn (require 'which-func) t)
  1003. (error nil))
  1004. (which-function-mode t)
  1005. (message "nmcobol-display-which-function set but which-func not available"))))
  1006. ;;; Adaptive-fill / auto-fill (needs much work but it's a start)
  1007. (defcustom nmcobol-restrict-auto-fill t
  1008. "When not nil a buffer local value for `fill-nobreak-predicate' is created
  1009. to prevent code from being accidentally realligned. The function uses syntax
  1010. highlighting to detect comments so `font-lock-mode' must be enabled to work."
  1011. :type 'boolean
  1012. :group 'nmcobol)
  1013. (defun nmcobol-setup-adaptive-fill ()
  1014. "Sets up the NMCOBOL-MODE adaptive-fill variables. DOESN'T WORK!"
  1015. ;; All of this section is left over from TAL mode !needs attention!
  1016. (set (make-local-variable 'fill-individual-varying-indent)
  1017. nil)
  1018. (set (make-local-variable 'auto-fill-inhibit-regexp)
  1019. "\\s-*[^*/]")
  1020. (set (make-local-variable 'comment-use-syntax)
  1021. t)
  1022. (set (make-local-variable 'comment-start)
  1023. "*")
  1024. (set (make-local-variable 'comment-end)
  1025. "")
  1026. (set (make-local-variable 'comment-padding)
  1027. " ")
  1028. (set (make-local-variable 'comment-start-skip)
  1029. "\\(\\s<\\|*\\)\\s-*")
  1030. (set (make-local-variable 'sentence-end)
  1031. "\\(;\\|\\.[ \t\n\f]\\)")
  1032. (set (make-local-variable 'paragraph-start)
  1033. "\\( +[0-9]\\| +\\w+\\( +\\(division\\(\\s-+using\\s-+[^.\n]+\\)?\\|section\\)\\) *\\.\\)")
  1034. (set (make-local-variable 'paragraph-separate)
  1035. "\n")
  1036. (set (make-local-variable 'adaptive-fill-regexp)
  1037. "^\\s-*\\(!\\|--\\)[~%^&()_#[*|;:-=+]*\\s-*")
  1038. (set (make-local-variable 'adaptive-fill-first-line-regexp)
  1039. adaptive-fill-regexp)
  1040. (when nmcobol-restrict-auto-fill
  1041. ; This is supposed to restrict auto-fill to comments only
  1042. (fset (make-local-variable 'fill-nobreak-predicate)
  1043. (lambda ()
  1044. (not (eq (get-text-property (point) 'face)
  1045. 'font-lock-comment-face))))))
  1046. ;;; Indentation
  1047. (defcustom nmcobol-righthand-keyword-column nil
  1048. "The column to which `nmcobol-secondary-indent-words' are indented.
  1049. 38 is the default for DDL output so that value is mirrored here.
  1050. Setting this to nil prevents secondary indentation."
  1051. :type '(choice (integer :tag "Indent column")
  1052. (const :tag "Don't indent" nil))
  1053. :group 'nmcobol)
  1054. (make-variable-buffer-local 'nmcobol-righthand-keyword-column)
  1055. (defcustom nmcobol-secondary-indent-words
  1056. '("BY" "DELIMITED" "FROM" "GIVING" "IS" "OF" "PIC"
  1057. "SHARED" "TO" "USING" "VALUE" "WITH")
  1058. "List of keywords to be indented to `nmcobol-righthand-keyword-column' col."
  1059. :type '(repeat (string :tag "word"))
  1060. :group 'nmcobol
  1061. )
  1062. (defun nmcobol-char-maybe-comment (arg)
  1063. "Removes leading white space if character is preceeded only by white space.
  1064. The behavior can be circumvented with C-u.
  1065. A numeric prefix does not prevent the removal of white space."
  1066. (interactive "*P")
  1067. (if (and (looking-back "^[ \t]*")
  1068. (or (null arg)
  1069. (numberp arg)))
  1070. (progn
  1071. (replace-match "")
  1072. (self-insert-command (prefix-numeric-value arg))
  1073. (if (null arg)(indent-relative t)))
  1074. (self-insert-command (if (numberp arg)(prefix-numeric-value arg) 1))))
  1075. (defun nmcobol-indent-region (&optional beg end)
  1076. "Indents all lines even partly within the selected region.
  1077. If BEG/END are nil, indent the current line."
  1078. (interactive)
  1079. (if (and (null beg) transient-mark-mode mark-active)
  1080. (setq beg (min (point)(mark))
  1081. end (max (point)(mark))
  1082. deactivate-mark t))
  1083. (if (equal beg end)
  1084. (nmcobol-indent-line)
  1085. (setq end (set-marker (make-marker) end))
  1086. (goto-char beg)
  1087. (while (< (point) end)
  1088. (nmcobol-indent-line t)
  1089. (forward-line))
  1090. (goto-char end)
  1091. (set-marker end nil)))
  1092. (defun nmcobol-re-search-for-keyword (regexp bound)
  1093. "Like re-search-forward but only program lines are examined.
  1094. Matches within quoted strings are also ignored.
  1095. Obviously REGEXP can't span lines."
  1096. (while (and (> bound (point))
  1097. (or (looking-at nmcobol-non-cobol-regexp)
  1098. (not (re-search-forward regexp (line-end-position) t))
  1099. (eq (get-text-property (match-beginning 0) 'face)
  1100. 'font-lock-string-face)))
  1101. (forward-line))
  1102. (< (point) bound))
  1103. (defun nmcobol-get-block-type (next)
  1104. "Returns a value indicating the type of block at point. Nil if none.
  1105. NEXT is the buffer position of the next COBOL statement. The search for
  1106. keywords that determine if this statement starts a block, ends at NEXT.
  1107. Point is left where the search ended."
  1108. (if (looking-at nmcobol-block-always-regexp)
  1109. (intern (concat nmcobol-block-symbol-prefix
  1110. (match-string-no-properties 1)))
  1111. (let ((loc (point))
  1112. sym)
  1113. (cond
  1114. ((looking-at "PERFORM\\s-+")
  1115. (goto-char (match-end 0))
  1116. (if (or (looking-at nmcobol-keywords-imperatives-regexp)
  1117. (looking-at "\\(\\s_\\|\\w\\)+\\s-+TIMES\\_>")
  1118. (looking-at "\\(WITH\\|TEST\\|UNTIL\\|VARYING\\)\\_>"))
  1119. (setq sym (intern (concat nmcobol-block-symbol-prefix
  1120. "PERFORM")))))
  1121. ((looking-at "\\(ADD\\|COMPUTE\\|DIVIDE\\|MULTIPLY\\|SUBTRACT\\)\\_>")
  1122. (setq sym (intern (concat nmcobol-block-symbol-prefix
  1123. (match-string-no-properties 0))))
  1124. (goto-char (match-end 0))
  1125. ;; Look for the ON ERROR clause
  1126. (unless (nmcobol-re-search-for-keyword "\\s-ERROR\\_>" next)
  1127. (setq sym ())))
  1128. ((looking-at "\\(READ\\|START\\|DELETE\\|DELETE\\|REWRITE\\)\\_>")
  1129. (setq sym (intern (concat nmcobol-block-symbol-prefix
  1130. (match-string-no-properties 0))))
  1131. (goto-char (match-end 0))
  1132. ;; Look for the [NOT] AT END or INVALID KEY clause
  1133. (unless (nmcobol-re-search-for-keyword "\\s-\\(END\\|INVALID\\)\\_>"
  1134. next)
  1135. (setq sym ())))
  1136. ((looking-at "\\(UNSTRING\\|STRING\\)\\_>")
  1137. (setq sym (intern (concat nmcobol-block-symbol-prefix
  1138. (match-string-no-properties 0))))
  1139. (goto-char (match-end 0))
  1140. ;; Look for the [NOT] ON OVERFLOW clause
  1141. (unless (nmcobol-re-search-for-keyword "\\s-OVERFLOW\\_>" next)
  1142. (setq sym ())))
  1143. ((looking-at "WRITE\\_>")
  1144. (setq sym (intern (concat nmcobol-block-symbol-prefix
  1145. (match-string-no-properties 0))))
  1146. (goto-char (match-end 0))
  1147. ;; Look for EOP, END-OF-PAGE, or [NOT] INVALID KEY clauses
  1148. (unless (nmcobol-re-search-for-keyword
  1149. "\\s-\\(EOP\\|END-OF-PAGE\\|INVALID\\)\\_>" next)
  1150. (setq sym ()))))
  1151. (goto-char loc)
  1152. sym)))
  1153. (defun nmcobol-indent-line (&optional batch-mode)
  1154. "Indents the current NMCOBOL line appropriately for it's context.
  1155. BATCH-MODE is used by `nmcobol-indent-region' to prevent buffer
  1156. modifications that only consist of blanks in otherwise empty lines
  1157. and to prevent arithmetic operations involving * or / from being
  1158. converted to comment lines accidentally."
  1159. (save-match-data
  1160. (let* (nmcobol-sentence-includes-comments
  1161. (loc (point-marker)) ;marker of starting cursor
  1162. (col (current-column)) ;cursor's starting column
  1163. (bol (progn (beginning-of-line)(point))) ;beginning of line loc
  1164. (cur (prog2 (skip-chars-forward " \t") ;current indent amount
  1165. (current-column)
  1166. (goto-char bol))))
  1167. ;; I'm at the beginning of the line to indent - do it
  1168. (cond
  1169. ;; Don't move lines starting like this
  1170. ((looking-at "[-?/*dD]")
  1171. ;(message "dont move")
  1172. (goto-char loc)
  1173. (set-marker loc nil))
  1174. ;; Non-code lines all start in column 0
  1175. ((and (not batch-mode)(looking-at "\\([ \t]+\\)[?/*]"))
  1176. ;(message "col 0")
  1177. (replace-match "" nil nil nil 1)
  1178. (goto-char loc)
  1179. (set-marker loc nil))
  1180. ;; Blank lines start where prior line left off or column 1 if none
  1181. ((looking-at "[ \t]*\n")
  1182. ;(message "relative")
  1183. (set-marker loc nil)
  1184. (if batch-mode () ;don't indent blank lines if flag non-nil
  1185. (let ((indent (save-excursion
  1186. (if (re-search-backward "^[ \t]+[^ \t\n]" nil t)
  1187. (skip-chars-forward " \t" (line-end-position))
  1188. 1))))
  1189. (end-of-line)
  1190. (let ((cc (- indent (current-column))))
  1191. (if (> 0 cc) ;Move backward needed
  1192. (backward-delete-char-untabify (- cc))
  1193. (indent-to indent))))))
  1194. ;; Period only lines are like new sentences within a paragraph
  1195. ((looking-at "[ \t]*\\.")
  1196. ;(message "period")
  1197. (goto-char loc)
  1198. (set-marker loc nil)
  1199. (unless (= 4 cur)
  1200. (replace-match " ." nil nil nil 0)))
  1201. ;; This is the messy part - find out what kind of line this is
  1202. (t
  1203. (let* ((typ (nmcobol-beginning-of-sentence)) ; Get sentence type
  1204. (sos (point)) ; start of sentence loc
  1205. (indent 4) ; default sentence start
  1206. extra ; add to indent in special cases
  1207. prior-typ)
  1208. (if (= bol (point))
  1209. ;; This is the beginning of a new sentence of type typ
  1210. (cond
  1211. ((numberp typ) ;it's a numbered sentence
  1212. ;(message "number")
  1213. (if (or (= 0 typ)(= 1 typ)(= 66 typ)(= 77 typ))
  1214. ;; These numbers are top level
  1215. (setq indent 1)
  1216. ;; Other numbers are relative to prior sentence.
  1217. (nmcobol-backward-sentence 1)
  1218. (setq prior-typ (nmcobol-beginning-of-sentence)
  1219. indent (if (looking-at "[ \t]+")
  1220. (prog2 (goto-char (match-end 0))
  1221. (current-column)
  1222. (beginning-of-line))
  1223. 0)
  1224. extra (if (looking-at "[ \t]+[0-9]\\{1,2\\}[ \t]+")
  1225. (prog2 (goto-char (match-end 0))
  1226. (- (current-column) indent)
  1227. (beginning-of-line))))
  1228. (cond
  1229. ;; Error case. Prior statement isn't numbered
  1230. ((not (numberp prior-typ))
  1231. ;(message "number - prior non")
  1232. (setq indent (+ indent 3)))
  1233. ;; Prior statement is same level. Use same indent.
  1234. ((= prior-typ typ)
  1235. ;(message "number - prior same")
  1236. )
  1237. ;; Current is subordinate to prior, indent deeper
  1238. ((> typ prior-typ)
  1239. ;(message "number - prior superior")
  1240. (setq indent (+ indent (or extra 3))))
  1241. ;; Prior statement is inferior to current
  1242. (t
  1243. ;(message "number - search for superior")
  1244. ;; find equal or superior statement
  1245. (while (and (= 0 (nmcobol-backward-sentence 1))
  1246. (setq prior-typ
  1247. (nmcobol-beginning-of-sentence))
  1248. (numberp prior-typ)
  1249. (< typ prior-typ)))
  1250. ;; That statement's indentation is what I'll use
  1251. (beginning-of-line) ; can't necessarily assume this.
  1252. (setq indent (if (looking-at "[ \t]+")
  1253. (prog2 (goto-char (match-end 0))
  1254. (current-column)
  1255. (beginning-of-line))
  1256. 0)
  1257. extra (if (looking-at "[ \t]+[0-9]\\{1,2\\}[ \t]+")
  1258. (prog2 (goto-char (match-end 0))
  1259. (- (current-column) indent)
  1260. (beginning-of-line))))
  1261. (unless (and (numberp prior-typ)
  1262. (= prior-typ typ))
  1263. (setq indent (+ (or extra 3) indent)))))))
  1264. ;; Beginning of a special but non-numbered sentence.
  1265. ((stringp typ)
  1266. ;(message "special statement")
  1267. (setq indent 1))
  1268. (t ;typ is nil - start of regular sentence
  1269. ;(message "Normal sentence start")
  1270. (setq indent 4)) ;should already be set to this...?
  1271. )
  1272. ;; Context is amid sentence. See if it's amid a statement.
  1273. (goto-char bol)
  1274. (if (looking-at nmcobol-keywords-statements-regexp)
  1275. ;; Lines starting with a statement keyword indent like the
  1276. ;; last statement unless the last statement begins a block
  1277. ;; or the current statement ends a block.
  1278. (progn
  1279. ;; Get the type of statement being indented.
  1280. (setq typ (upcase (match-string-no-properties 2)))
  1281. ;(message "amid - new statement")
  1282. ;; find start of last statement & get it's indentation
  1283. (re-search-backward
  1284. nmcobol-keywords-statements-regexp sos 'move-anyway)
  1285. (setq prior-typ (upcase (or (match-string-no-properties 2)
  1286. "")))
  1287. (skip-chars-forward " \t")
  1288. (setq indent (current-column))
  1289. ;; if prior statement is a block begin, indent from it
  1290. (if (setq extra (nmcobol-get-block-type bol))
  1291. (setq indent (+ indent 3)))
  1292. ;; if current line is a block end, indentation decreases
  1293. ;; unless it ends a statement that didn't require an end.
  1294. (when
  1295. (or
  1296. (string-match nmcobol-block-end-regexp typ)
  1297. (string-match "\\(WHEN\\|ELSE\\|THEN\\)" typ))
  1298. (unless (and
  1299. (not extra)
  1300. (string= prior-typ
  1301. (upcase
  1302. (match-string-no-properties 1 typ))))
  1303. (setq indent (- indent 3)))))
  1304. ;; line must be continuation of some statement
  1305. ;(message "amid a statement")
  1306. (if (re-search-backward
  1307. nmcobol-keywords-statements-regexp sos 'move-anyway)
  1308. (progn (goto-char (match-end 1))
  1309. (setq prior-typ (match-string-no-properties 2)
  1310. indent (current-column)))
  1311. (skip-chars-forward " \t" (line-end-position))
  1312. (setq prior-typ ""
  1313. indent (current-column)))
  1314. (setq indent (+ 6 indent)))
  1315. (setq typ nil))
  1316. ;(message "typ = %s Cur = %s, new = %s" typ cur indent)
  1317. ;; Unless it's a special type (typ) it shouldn't be indenting
  1318. ;; less than 4 so flag an error by removing all indentation
  1319. (if (and (not typ)
  1320. (< indent 4))
  1321. (setq indent 0))
  1322. (unless (= indent cur) ;current line is already indented correctly
  1323. (goto-char bol)
  1324. (looking-at "[ \t]*")
  1325. (replace-match (make-string indent ? ) nil nil nil 0))
  1326. (goto-char loc)
  1327. (set-marker loc nil)
  1328. (if (and (< 0 col)(> indent (current-column)))
  1329. (move-to-column indent)))))))
  1330. (nmcobol-secondary-indent nmcobol-righthand-keyword-column nil))
  1331. (defun nmcobol-secondary-indent (to-col right)
  1332. "Indents `nmcobol-secondary-indent-words' on the current line to TO-COL.
  1333. RIGHT indicates if the left or right hand side of the word is indented to
  1334. that column. nil means align the left hand side, any other value
  1335. indicates the right side. If TO-COL is unreasonable (not between 20 and
  1336. 120) the routine does nothing. Comment and compiler directive lines are
  1337. always ignored."
  1338. (when (and to-col (> to-col 20)(< to-col 120))
  1339. (let ((start (point-marker))
  1340. (end (line-end-position))
  1341. (regexp (concat "\\_<"
  1342. (regexp-opt nmcobol-secondary-indent-words t)
  1343. "\\_>"))
  1344. col)
  1345. (beginning-of-line)
  1346. (when (and (not (looking-at nmcobol-non-cobol-regexp))
  1347. (re-search-forward regexp end t)
  1348. (not (eq 'font-lock-string-face
  1349. (get-text-property (point) 'face))))
  1350. (if right
  1351. (progn
  1352. (setq col (- to-col (current-column)))
  1353. (goto-char (match-beginning 1)))
  1354. (goto-char (match-beginning 1))
  1355. (setq col (- to-col (current-column))))
  1356. (unless (looking-back "^\\s-+")
  1357. (cond
  1358. ((< 0 col)
  1359. (insert (make-string col ? )))
  1360. ((> 0 col)
  1361. (while (and (> 0 col)
  1362. (looking-back "\\s-\\s-"))
  1363. (backward-delete-char-untabify 1)
  1364. (setq col (1+ col)))))))
  1365. (goto-char start))))
  1366. (defun nmcobol-setup-indent ()
  1367. "Sets default indentation or sets up nmcobol-indent if available."
  1368. ; (if (condition-case ()
  1369. ; (progn (require 'nmcobol-indent) t)
  1370. ; (error nil))
  1371. (setq indent-line-function 'nmcobol-indent-region
  1372. indent-region-function 'nmcobol-indent-region))
  1373. ; (set (make-local-variable 'indent-line-function) 'indent-relative-maybe)))
  1374. ;;; Language Skeletons -- Feel free to add more of your own!
  1375. (defcustom nmcobol-keywords-case 'upper
  1376. "*Indicates if keywords in skeletons should be all UPPER CASE, all lower
  1377. case or Camel Case (First Char Upper & Rest Lower)."
  1378. :type '(choice :tag "Skeleton Case"
  1379. (const :tag "ALL CAPS" upper)
  1380. (const :tag "all small" lower)
  1381. (const :tag "Camel Case" camel)
  1382. (const :tag "DON'T Change" ()))
  1383. :group 'nmcobol)
  1384. (defun nmcobol-setup-skel ()
  1385. "Configures skeleton.el functions for the NMCOBOL environemnt."
  1386. (set (make-local-variable 'skeleton-transformation) 'nmcobol-skel-transform)
  1387. ;; This prevents abbrevs from expanding within skeletons
  1388. (setq skeleton-further-elements '((abbrev-mode nil))))
  1389. (defun nmcobol-skel-transform ( element )
  1390. "Called by `skeleton-insert'. Gives ELEMENT `nmcobol-keywords-case' capitalization."
  1391. ;; This should be made more complex to only change the case of certain words
  1392. ;; so the user can create skeletons containing items that should not be
  1393. ;; affected by nmcobol-keywords-case. There are 3 obvious ways. 1) use the
  1394. ;; keywords tables above. 2) add a customize to ignore words. 3) add a
  1395. ;; customize to specify specific words to be affected. Preferences?
  1396. (if (stringp element)
  1397. (cond
  1398. ((eq nmcobol-keywords-case 'upper) (upcase element))
  1399. ((eq nmcobol-keywords-case 'lower) (downcase element))
  1400. ((eq nmcobol-keywords-case 'camel) (capitalize element))
  1401. ( t element ))
  1402. element))
  1403. (defun nmcobol-set-line-syntax ()
  1404. "Applies font-lock-syntactic-keywords to current line.
  1405. Used to set properties necessary for proper indentation."
  1406. (if font-lock-mode
  1407. (save-excursion ; next stmt moves point.
  1408. (font-lock-fontify-syntactic-keywords-region
  1409. (line-beginning-position) (line-end-position))
  1410. () ;any result is inserted into buffer
  1411. )))
  1412. (define-skeleton nmcobol-if-skel
  1413. "This is an example skeleton."
  1414. nil
  1415. " IF" > " " - \n
  1416. _ | \n
  1417. " END-IF" (nmcobol-set-line-syntax) > \n)
  1418. (define-skeleton nmcobol-paragraph-skel
  1419. "Inserts comment bars to surround a new paragraph name."
  1420. nil \n
  1421. "********************************" > \n
  1422. " 0." > -1 _ "." \n
  1423. "********************************" > \n \n
  1424. "." > \n \n)
  1425. ;;; Abbrev support
  1426. (defcustom nmcobol-abbrev-mode t
  1427. "Sets the default value for `abbrev-mode' upon entry into `nmcobol-mode'."
  1428. :type 'boolean
  1429. :group 'nmcobol)
  1430. (defvar nmcobol-mode-abbrev-table-list
  1431. '(("$i" "" nmcobol-if-skel)
  1432. ("$p" "" nmcobol-paragraph-skel))
  1433. "List of pre-defined `nmcobol-mode' abbrev definitions.
  1434. Use \\[list-abbrevs] to see all defined abbrevs.")
  1435. (defvar nmcobol-mode-abbrev-table)
  1436. (defun nmcobol-setup-abbrevs ()
  1437. "Installs the `nmcobol-mode-abbrev-table' as `local-abbrev-table'"
  1438. (define-abbrev-table
  1439. 'nmcobol-mode-abbrev-table
  1440. nmcobol-mode-abbrev-table-list)
  1441. (setq local-abbrev-table nmcobol-mode-abbrev-table)
  1442. (setq skeleton-further-elements '((abbrev-mode nil)))
  1443. (abbrev-mode nmcobol-abbrev-mode) ;Setting is documented as buffer local
  1444. )
  1445. ;;; Eldoc support
  1446. (defcustom nmcobol-eldoc-def-files ()
  1447. "List of files containing function help strings used by `eldoc-mode'.
  1448. These are the strings eldoc-mode displays as help for functions near point.
  1449. The format of the file must be exactly as follows or who knows what happens.
  1450. (set (intern \"<fcn-name1>\" nmcobol-eldoc-obarray) <helper string1>)
  1451. (set (intern \"<fcn-name2>\" nmcobol-eldoc-obarray) <helper string2>)
  1452. ...
  1453. Where <fcn-name> is the name of the function to which <helper string> applies.
  1454. <helper-string> is the string to display when point is near <fcn-name>.
  1455. Alternatively <helper-string> can be a list where the first element is
  1456. the help string mentioned above and the second element is a string
  1457. containing the filename of the file where <fcn-name> is defined.
  1458. For example '(\"Help string here\" \"source\\of_help.here\")
  1459. When present `nmcobol-eldoc-where-def' and `nmcobol-eldoc-visit-file'
  1460. use it."
  1461. :type '(repeat string)
  1462. :group 'nmcobol)
  1463. (defvar nmcobol-eldoc-obarray ()
  1464. "Global Keywords & variables and their associated help strings stored here.")
  1465. (defvar nmcobol-eldoc-local-obarray ()
  1466. "Local variables and their associated help strings stored here.")
  1467. (make-variable-buffer-local 'nmcobol-eldoc-local-obarray)
  1468. (defcustom nmcobol-eldoc-highlight-face 'match
  1469. "Face used to highlight a variable name when displayed by eldoc."
  1470. :type 'face
  1471. :group 'nmcobol)
  1472. (defun nmcobol-eldoc-context (&optional type)
  1473. "If TYPE is nil or 'doc return the doc string for the symbol near point
  1474. or nil if none. If TYPE is 'file return the file where the symbol was
  1475. defined or nil. If TYPE is 'word return the buffer keyword being looked up."
  1476. (let* ((word (thing-at-point 'symbol))
  1477. name symbol value)
  1478. (when word
  1479. (setq word (upcase word)
  1480. name (concat (save-excursion
  1481. (beginning-of-line)
  1482. (char-to-string (char-after))) word)
  1483. symbol (or (intern-soft name nmcobol-eldoc-local-obarray)
  1484. (intern-soft name nmcobol-eldoc-obarray)))
  1485. (when symbol
  1486. (setq value (symbol-value symbol))
  1487. (cond
  1488. ((eq type 'word) (setq value (downcase word)))
  1489. ((eq type 'file) (setq value (if (listp value)(cadr value))))
  1490. (type (setq value nil))
  1491. (t (unless (stringp value) (setq value (car value))))))
  1492. value)))
  1493. (defun nmcobol-eldoc-function ()
  1494. "Returns a documentation string appropriate for the current context or nil."
  1495. (let* ((value (nmcobol-eldoc-context))
  1496. word)
  1497. (when (and value (setq word (nmcobol-eldoc-context 'word)))
  1498. ;; highlight symbol in the help string
  1499. (if (string-match (concat "\\(?:^\\|\\.\\)" (regexp-opt (list word) t)
  1500. "\\( \\|\\.\\)") value)
  1501. (put-text-property (match-beginning 1) (match-end 1)
  1502. 'face nmcobol-eldoc-highlight-face
  1503. value)))
  1504. value))
  1505. (defun nmcobol-eldoc-where-def ()
  1506. "Displays the filename from which current eldoc string was extracted."
  1507. (interactive)
  1508. (let* ((fname (nmcobol-eldoc-context 'file)))
  1509. (if fname
  1510. (message "Symbol defined in %s" fname)
  1511. (if (nmcobol-eldoc-context 'word)
  1512. (message "Filename not available")
  1513. (message "No eldoc info for context")))))
  1514. (defun nmcobol-eldoc-visit-file ()
  1515. "Visits the file from which current eldoc string was extracted."
  1516. (interactive)
  1517. (let* ((fname (nmcobol-eldoc-context 'file))
  1518. (word (nmcobol-eldoc-context 'word)))
  1519. (if fname
  1520. (progn (find-file fname)
  1521. (message "Searching for %s" word)
  1522. (goto-char (point-min))
  1523. (re-search-forward (concat " " word "[ ;.(\t\n)]")))
  1524. (if word
  1525. (message "Filename not available")
  1526. (message "No eldoc info for context")))))
  1527. (defun nmcobol-setup-eldoc ()
  1528. "Loads the function documentation for use with eldoc."
  1529. (set (make-local-variable 'eldoc-documentation-function)
  1530. 'nmcobol-eldoc-function)
  1531. (unless (vectorp nmcobol-eldoc-obarray)
  1532. (setq nmcobol-eldoc-obarray (make-vector 41 0))
  1533. (condition-case var (mapc 'load nmcobol-eldoc-def-files)
  1534. (error (message "*** ERROR *** %s" var))))
  1535. (nmcobol-eldoc-scan-buffer))
  1536. (defvar nmcobol-eldoc-definition-regexp
  1537. (concat "^\\(?:"
  1538. "\\?SECTION\\|"
  1539. " +\\([0-9][0-9]\\)\\|"
  1540. " \\{0,3\\}\\(def\\(?:inition\\)?\\|rec\\(?:ord\\)?\\)\\) +\\(\\w+\\)"
  1541. "\\(?: +\\(\\w.+$\\)\\| *\\.\\| *\\,\\)")
  1542. "Expression describing variable and 88 level declarations for eldoc.
  1543. Used by `nmcobol-eldoc-make-list' in creating `eldoc' entries.
  1544. Match strings are:
  1545. 1 = numeric-level (if any)
  1546. 2 = non numeric level (if any)
  1547. 3 = data name (unless 1 and 2 both nil then this is nil too)
  1548. 4 = picture, value, occurs etc doc clause (if any, else nil)")
  1549. (defun nmcobol-eldoc-next-level ()
  1550. "Returns level number with match-data set for next data or nil."
  1551. (and (re-search-forward nmcobol-eldoc-definition-regexp () t)
  1552. (match-string-no-properties 3)
  1553. (if (match-string-no-properties 1)
  1554. (let ((num (string-to-number (match-string-no-properties 1))))
  1555. (if (or (= 66 num)(= 77 num)) 1 num))
  1556. 1)))
  1557. (defun nmcobol-eldoc-write-entry (buf file-name context last-level)
  1558. "Creates the eldoc entry in BUF using current match-data.
  1559. Returns `nmcobol-eldoc-next-level' data when it returns a level <= LEVEL."
  1560. (let* ((name (upcase (match-string-no-properties 3)))
  1561. (doc (match-string-no-properties 4))
  1562. (next-context (concat context name "."))
  1563. (result (concat context name " " doc))
  1564. (level (nmcobol-eldoc-next-level)))
  1565. (when (and (or doc context)
  1566. (not (string= "FILLER" name)))
  1567. (if (and level (= 88 level)(/= 88 last-level))
  1568. (setq result (concat result " +88")))
  1569. (set (intern (concat " " name) nmcobol-eldoc-local-obarray) result)
  1570. (setq result (prin1-to-string result))
  1571. (if buf
  1572. (with-current-buffer buf
  1573. (if file-name
  1574. (insert (concat "(set (intern \" " name
  1575. "\" nmcobol-eldoc-obarray) '(" result " \""
  1576. file-name "\"))\n"))
  1577. (insert (concat "(set (intern \" " name
  1578. "\" nmcobol-eldoc-obarray) " result ")\n"))))))
  1579. (while (and level (> level last-level))
  1580. (setq level (nmcobol-eldoc-write-entry
  1581. buf file-name next-context level)))
  1582. level))
  1583. (defun nmcobol-eldoc-make-list ()
  1584. "Creates a buffer of eldoc help strings for variables in this buffer.
  1585. Run this while visiting a COBOL/NMCOBOL source or DDL source file to
  1586. create a buffer of eldoc help entries. Save this buffer somewhere on
  1587. your search path and see `nmcobol-eldoc-def-files'. See also
  1588. `nmcobol-eldoc-scan-buffer'.
  1589. The optional arguments are for recursive calls only."
  1590. (interactive)
  1591. (if (not (or (eq 'nmcobol-mode major-mode) (eq 'ddl-mode major-mode)))
  1592. (error "Buffer is not ddl or nmcobol-mode! Can't extract help text."))
  1593. (let ((new-buf (get-buffer-create "nmcobol-eldoc-list.el"))
  1594. (this-buf (buffer-file-name))
  1595. level)
  1596. (setq nmcobol-eldoc-local-obarray (make-vector 41 0))
  1597. (with-current-buffer new-buf
  1598. (goto-char (point-max))
  1599. (if this-buf (insert (concat ";; from " this-buf "\n"))))
  1600. (save-excursion
  1601. (message "Processing eldoc entries.")
  1602. (goto-char (point-min))
  1603. (setq level (nmcobol-eldoc-next-level))
  1604. (while level
  1605. (setq level (nmcobol-eldoc-write-entry new-buf this-buf () level))))
  1606. (switch-to-buffer new-buf)
  1607. (emacs-lisp-mode)))
  1608. (defun nmcobol-eldoc-scan-buffer ()
  1609. "Rescans the current buffer, updating in-memory eldoc entries."
  1610. (interactive)
  1611. (setq nmcobol-eldoc-local-obarray (make-vector 41 0))
  1612. (message "Updating in-memory eldoc entries.")
  1613. (let (level)
  1614. (save-excursion
  1615. (goto-char (point-min))
  1616. (setq level (nmcobol-eldoc-next-level))
  1617. (while level
  1618. (setq level (nmcobol-eldoc-write-entry () () () level)))))
  1619. (message "nmcobol-eldoc-scan-buffer complete"))
  1620. ;;; (thing-at-point 'filename) support
  1621. (defun nmcobol-setup-thing-at-point ()
  1622. "Makes a buffer local version of `thing-at-point-file-name-chars'"
  1623. (defvar thing-at-point-file-name-chars)
  1624. (set (make-local-variable
  1625. 'thing-at-point-file-name-chars) "[:alnum:]=\\$.?*#_^-"))
  1626. ;;; anchored-transpose support
  1627. (eval-when-compile (require 'anchored-transpose))
  1628. (defun nmcobol-setup-anchored-transpose ()
  1629. "By default a trailing period is not included in text begin swapped."
  1630. (set (make-local-variable 'anchored-transpose-fuzzy-r1beg) "[\t ]+")
  1631. (set (make-local-variable 'anchored-transpose-fuzzy-r1end) "\\s *[.!?]?\\s *")
  1632. (set (make-local-variable 'anchored-transpose-fuzzy-r2beg) "[\t ]+")
  1633. (set (make-local-variable 'anchored-transpose-fuzzy-r2end) "\\s *[.!?]?\\s *"))
  1634. ;;; Movement by ...
  1635. (defcustom nmcobol-sentence-includes-comments t
  1636. "Movement by sentences includes immediately adjacent comments if non-nil"
  1637. :type 'boolean
  1638. :group 'nmcobol)
  1639. (defvar nmcobol-sentence-end-token
  1640. "\\.\\s-"
  1641. "Cobol's sentence end token is a period and one whitespace character.")
  1642. (defvar nmcobol-not-a-paragraph-regexp
  1643. "\\(e\\(?:nd-if\\|xit\\)\\) *\\."
  1644. "Match stmts confused as paragraph names by `nmcobol-num-or-paragraph-regexp'")
  1645. (defvar nmcobol-num-or-paragraph-regexp
  1646. (concat "\\(?:\\([0-8][0-9]\\)[ \n]"
  1647. "\\|\\(\\(?:f\\|s\\)d\\)[ \n]"
  1648. "\\|\\(\\w+\\)"
  1649. "\\(?: +\\(section\\|division\\)[ .\n]"
  1650. "\\| *\\.\\)\\)")
  1651. "Expression to find numeric or paragraph boundaries. Match strings are:
  1652. 1 - numeric data level,
  1653. 2 - string FD or SD,
  1654. 3 - string paragraph name,
  1655. 4 - string SECTION or DIVISION.")
  1656. (defun nmcobol-beginning-of-sentence ()
  1657. "Moves to the canonical start of a Cobol sentence.
  1658. Either beginning-of-line containing the first keyword or the
  1659. beginning of a comment section prior to the keyword depending on
  1660. the flag `nmcobol-sentence-includes-comments'
  1661. Returns the type of sentence we're at the start of.
  1662. If a numbered data definition the numeric level is returned.
  1663. If an FD or SD sentence the string FD or SD is returned.
  1664. If a section or division statement the corresponding string is returned.
  1665. If the statement is a paragraph name the name is returned.
  1666. Otherwise nil is returned."
  1667. (save-match-data
  1668. ;; Make room for backward sentence to see the period
  1669. (if (looking-back "\\.") (forward-char))
  1670. (nmcobol-backward-sentence 1 t)
  1671. ;; Make room for next search to see a potential leading space char.
  1672. (if (and (looking-at "\\S-")(not (bobp)))(backward-char))
  1673. ;; I should now be at the end of the prior sentence.
  1674. (let ((looking t))
  1675. ;; look for a word character
  1676. (while
  1677. (and looking
  1678. (re-search-forward " \\(\\w\\|\\s(\\|\\s)\\)" () 'move-anyway))
  1679. (backward-char)
  1680. (if (save-excursion (beginning-of-line)
  1681. (not (looking-at nmcobol-non-cobol-regexp)))
  1682. (setq looking nil)
  1683. (beginning-of-line)
  1684. (while (looking-at nmcobol-non-cobol-regexp)
  1685. (forward-line 1))))
  1686. (if (looking-back "^ +" (line-beginning-position))
  1687. (let ((type (if (looking-at nmcobol-num-or-paragraph-regexp)
  1688. (or (match-string-no-properties 2)
  1689. (match-string-no-properties 4)
  1690. (match-string-no-properties 3)
  1691. (string-to-number
  1692. (match-string-no-properties 1))))))
  1693. (if (looking-at nmcobol-not-a-paragraph-regexp)
  1694. (setq type nil))
  1695. (beginning-of-line)
  1696. (when (and nmcobol-sentence-includes-comments (not (bobp)))
  1697. (while (and (= 0 (forward-line -1))
  1698. (looking-at nmcobol-non-cobol-regexp)))
  1699. (forward-line))
  1700. type)))))
  1701. (defun nmcobol-forward-sentence (count)
  1702. "Move forward past COUNT Cobol sentence end tokens.
  1703. COUNT must be a positive number. Called by advice on forward-sentence.
  1704. See also `nmcobol-sentence-end-token'. Returns 0 if successful, N if
  1705. movement stopped prior to COUNT sentence end tokens were found."
  1706. (save-match-data
  1707. (while (and (< 0 count)
  1708. (re-search-forward nmcobol-sentence-end-token () 'move-anyway))
  1709. (backward-char)
  1710. (if (save-excursion (beginning-of-line)
  1711. (looking-at nmcobol-non-cobol-regexp))
  1712. (while (progn
  1713. (forward-line 1)
  1714. (looking-at nmcobol-non-cobol-regexp)))
  1715. (setq count (1- count)))
  1716. (forward-char)))
  1717. count)
  1718. (defun nmcobol-backward-sentence (count &optional recursive)
  1719. "Move backward over COUNT Cobol periods then forward outside sentence.
  1720. COUNT must be a positive number. See `nmcobol-sentence-end-token' for
  1721. the definition of where outside a sentence is. RECURSIVE is used
  1722. internally to see if point is within or between sentences. Called by
  1723. advice on `forward-sentence'."
  1724. (save-match-data
  1725. (unless recursive
  1726. (let ((prev (point)))
  1727. (nmcobol-beginning-of-sentence)
  1728. (if (< (point) prev)
  1729. (if (= 0 count)(set-match-data (list (point)(point))))
  1730. (setq count (1+ count)))))
  1731. (while (and (< 0 count)
  1732. (re-search-backward nmcobol-sentence-end-token () 'move-anyway))
  1733. (if (save-excursion (beginning-of-line)
  1734. (not (looking-at nmcobol-non-cobol-regexp)))
  1735. (setq count (1- count))
  1736. (beginning-of-line)
  1737. (while (looking-at nmcobol-non-cobol-regexp)
  1738. (forward-line -1))
  1739. (forward-line)))
  1740. (if (= 0 count)
  1741. (goto-char (match-end 0))
  1742. (setq count (1- count))))
  1743. count)
  1744. (defadvice forward-sentence
  1745. (around nmcobol-ad-forward-sentence activate)
  1746. "In nmcobol-mode, moves by Cobol sentences."
  1747. (if (eq major-mode 'nmcobol-mode)
  1748. (if (and (not (eq t nmcobol-sentence-includes-comments))
  1749. (save-excursion (beginning-of-line)
  1750. (save-match-data
  1751. (looking-at nmcobol-non-cobol-regexp))))
  1752. ad-do-it
  1753. (if (or (unless (ad-get-arg 0) (ad-set-arg 0 1) nil)
  1754. (= 0 (setq ad-return-value (ad-get-arg 0)))
  1755. (= 0 (setq ad-return-value
  1756. (cond
  1757. ((< 0 (ad-get-arg 0))
  1758. (nmcobol-forward-sentence (ad-get-arg 0)))
  1759. ((> 0 (ad-get-arg 0))
  1760. (nmcobol-backward-sentence (- (ad-get-arg 0))))))))
  1761. (if (= 0 ad-return-value)
  1762. (nmcobol-beginning-of-sentence))))
  1763. ad-do-it))
  1764. (defun nmcobol-forward-paragraph (count)
  1765. "Moves forward by data levels or paragraphs; COUNT times.
  1766. Returns 0 if successful or COUNT minus the number of paragraphs moved."
  1767. (let ((context (nmcobol-beginning-of-sentence)))
  1768. (while (and (< 0 count)
  1769. (not (eobp))
  1770. (setq count (1- count)
  1771. context (nmcobol-next-paragraph context)))))
  1772. count)
  1773. (defun nmcobol-backward-paragraph (count)
  1774. "Moves backward by data levels or paragraphs; COUNT times.
  1775. Returns 0 if successful or COUNT minus the number of paragraphs moved."
  1776. (let ((prev (point))
  1777. (context (nmcobol-beginning-of-sentence)))
  1778. (if (and (< (point) prev)
  1779. context)
  1780. (if (< 0 count) (setq count (1- count))))
  1781. (while (and (< 0 count)
  1782. (not (bobp))
  1783. (setq count (1- count)
  1784. context (nmcobol-prior-paragraph context)))))
  1785. count)
  1786. (defun nmcobol-next-paragraph (context)
  1787. "Moves forward to the next data level or paragraph marker based on CONTEXT.
  1788. Returns the new context where nil means `eobp' reached before next paragraph.
  1789. See `nmcobol-beginning-of-sentence' for possible context values."
  1790. (if (numberp context)
  1791. ;; Numbered paragraphs are handled based on data level
  1792. (let ((next-context (and (= 0 (nmcobol-forward-sentence 1))
  1793. (nmcobol-beginning-of-sentence))))
  1794. (if (numberp next-context)
  1795. (cond
  1796. ;; anything besides another 77 terminates a 77
  1797. ((= context 77)
  1798. (while (and (numberp next-context)
  1799. (= next-context 77))
  1800. (setq next-context
  1801. (and (= 0 (nmcobol-forward-sentence 1))
  1802. (nmcobol-beginning-of-sentence)))))
  1803. ;; when next number is same as current look for a lower numbered
  1804. ;; level, a 77, or unnumbered, to terminate 'paragraph'.
  1805. ((= next-context context)
  1806. (while (and
  1807. (numberp (setq next-context
  1808. (and (= 0 (nmcobol-forward-sentence 1))
  1809. (nmcobol-beginning-of-sentence))))
  1810. (>= next-context context)
  1811. (/= next-context 77)
  1812. (/= next-context 66))))
  1813. ;; When next number is higher than current, look for same or lower
  1814. ;; numbered level, a 77, or unnumbered, to terminate 'paragraph'.
  1815. ((and (> next-context context)
  1816. (/= next-context 77)
  1817. (/= next-context 66))
  1818. (while (and
  1819. (numberp (setq next-context
  1820. (and (= 0 (nmcobol-forward-sentence 1))
  1821. (nmcobol-beginning-of-sentence))))
  1822. (> next-context context)
  1823. (/= next-context 77)
  1824. (/= next-context 66))))
  1825. ;; If next-context = 77 or is < context then we're already done.
  1826. )
  1827. ;; if next-context isn't numbered then we're already done
  1828. )
  1829. next-context)
  1830. ;; Normal paragraph movement
  1831. (while (and (= 0 (nmcobol-forward-sentence 1))
  1832. (not (setq context (nmcobol-beginning-of-sentence)))))
  1833. context))
  1834. (defun nmcobol-prior-paragraph (context)
  1835. "Moves backward to the next data level or paragraph marker based on CONTEXT.
  1836. Returns the new context where nil means `bobp' reached before a paragraph start.
  1837. See `nmcobol-beginning-of-sentence' for possible context values."
  1838. (if (numberp context)
  1839. ;; Numbered paragraphs are handled based on data level
  1840. (let ((next-context (and (= 0 (nmcobol-backward-sentence 1))
  1841. (nmcobol-beginning-of-sentence))))
  1842. (if (numberp next-context)
  1843. (cond
  1844. ;; anything besides another 77 terminates a 77
  1845. ((= context 77)
  1846. (while (and (numberp next-context)
  1847. (= next-context 77))
  1848. (setq next-context
  1849. (and (= 0 (nmcobol-backward-sentence 1))
  1850. (nmcobol-beginning-of-sentence)))))
  1851. ;; when next number is same as current look for a lower numbered
  1852. ;; level, a 77, or unnumbered, to terminate 'paragraph'.
  1853. ((= next-context context)
  1854. (while (and
  1855. (numberp (setq next-context
  1856. (and (= 0 (nmcobol-backward-sentence 1))
  1857. (nmcobol-beginning-of-sentence))))
  1858. (>= next-context context)
  1859. (/= next-context 77)
  1860. (/= next-context 66))))
  1861. ;; When next number is higher than current, look for same or lower
  1862. ;; numbered level, a 77, or unnumbered, to terminate 'paragraph'.
  1863. ((and (> next-context context)
  1864. (/= next-context 77)
  1865. (/= next-context 66))
  1866. (while (and
  1867. (numberp (setq next-context
  1868. (and (= 0 (nmcobol-backward-sentence 1))
  1869. (nmcobol-beginning-of-sentence))))
  1870. (> next-context context)
  1871. (/= next-context 77)
  1872. (/= next-context 66))))
  1873. ;; If next-context = 77 or is < context then we're already done.
  1874. )
  1875. ;; if next-context isn't numbered then we're already done
  1876. )
  1877. next-context)
  1878. ;; Normal paragraph movement
  1879. (while (and (= 0 (nmcobol-backward-sentence 1))
  1880. (not (stringp (setq context (nmcobol-beginning-of-sentence)))))
  1881. (setq context nil))
  1882. context))
  1883. (defadvice forward-paragraph
  1884. (around nmcobol-ad-forward-paragraph activate)
  1885. "In nmcobol-mode, moves by numbered data levels or Cobol paragraphs.
  1886. Uses `nmcobol-beginning-of-sentence' in determining how to move."
  1887. (if (eq major-mode 'nmcobol-mode)
  1888. (if (and (not (eq t nmcobol-sentence-includes-comments))
  1889. (save-excursion (beginning-of-line)
  1890. (looking-at nmcobol-non-cobol-regexp)))
  1891. ad-do-it
  1892. (setq ad-return-value
  1893. (if (or (unless (ad-get-arg 0) (ad-set-arg 0 1) nil)
  1894. (< 0 (ad-get-arg 0)))
  1895. (nmcobol-forward-paragraph (ad-get-arg 0))
  1896. (nmcobol-backward-paragraph (- (ad-get-arg 0))))))
  1897. ad-do-it))
  1898. ;; User programming utility functions
  1899. (defcustom nmcobol-pic-string-regexp
  1900. (concat "pic\\(?:ture\\)?\\s-+\\([-abnpsvxz0-9.,()/+crd*$]*"
  1901. "[-abnpvxz0-9()/+crd*$]+\\)\\(\\.?[ \t]\\|\\.?$\\)")
  1902. "Used by `nmcobol-addup-pics' to find picture strings."
  1903. :type 'regexp
  1904. :group 'nmcobol)
  1905. (defun nmcobol-pic-string-length(string)
  1906. "Used internally. Returns the length a picture string represents."
  1907. (let ((len (length string))
  1908. (off 0))
  1909. (while (string-match "[abnpxz90*$](\\([0-9]+\\))" string off)
  1910. (setq len (+ (string-to-number (match-string-no-properties 1 string))
  1911. (- len (length (match-string-no-properties 0 string))))
  1912. off (match-end 0)))
  1913. len))
  1914. (defun nmcobol-addup-pics (begin end)
  1915. "Adds the length represented by all picture strings in the region.
  1916. Eventually I'd like for it to account for REDEFINES. It DOESN'T now."
  1917. (interactive "r")
  1918. (if mark-active
  1919. (save-excursion
  1920. (goto-char begin)
  1921. (let ((sum 0))
  1922. (while (re-search-forward nmcobol-pic-string-regexp end t)
  1923. (setq sum (+ sum (nmcobol-pic-string-length
  1924. (match-string-no-properties 1)))))
  1925. (message "PIC[TURE] clauses add up to %d" sum)))
  1926. (message "Select a region to operate upon.")))
  1927. ;;; Miscellaneous
  1928. (defun nmcobol-setup-paren-mode ()
  1929. "Adds functions to make show-paren-mode recognize END-<statement> verbs."
  1930. (require 'paren)
  1931. ; (if (boundp show-paren-decide-dir-function)
  1932. ; (set (make-local-variable 'show-paren-decide-dir-function)
  1933. ; 'nmcobol-show-paren-decide-dir))
  1934. ; (if (boundp show-paren-scan-function)
  1935. ; (set (make-local-variable 'show-paren-scan-function)
  1936. ; 'nmcobol-show-paren-scan))
  1937. (show-paren-mode 1))
  1938. (defun nmcobol-customize-options ()
  1939. "Invokes (customize-group 'nmcobol)"
  1940. (interactive)
  1941. (customize-group 'nmcobol))
  1942. (defcustom nmcobol-mode-hook nil
  1943. "Standard mode hook. Run after entering NMCobol mode."
  1944. :type 'hook
  1945. :group 'nmcobol)
  1946. ;;;###autoload
  1947. (defun nmcobol-mode ()
  1948. "A major mode for editing ?TANDEM format COBOL program source files.
  1949. See `cobol-mode' for ?ANSI format COBOL program source files.
  1950. Customization options are available via
  1951. \\[customize-group] <ret> NMCOBOL <ret>
  1952. This mode provides NMCOBOL specific support for such packages as:
  1953. `abbrev-mode' `adaptive-fill-mode' `anchored-transpose'
  1954. `auto-fill-mode' `eldoc-mode' `filladapt-mode'
  1955. `font-lock-mode' `imenu' `indent-line-function'
  1956. `show-paren-mode' `skeleton-insert' `thing-at-point'
  1957. `which-function'
  1958. (thing-at-point 'filename) will return a Guardian filename, define or
  1959. template if present at point.
  1960. ** Note ** Some things won't work correctly if `font-lock-mode' is off.
  1961. nmcobol-mode implements the following \\[execute-extended-command] ... commands
  1962. `nmcobol-mode' Activates this mode for the current buffer
  1963. `nmcobol-if-skel' Inserts an if/then statement skeleton
  1964. \\{nmcobol-mode-map}
  1965. Use \\[describe-bindings] to see ALL key bindings.
  1966. Some settings I like:
  1967. Turn on `skeleton-pair-insert-maybe' for (), [] and \"\"
  1968. Turn on `imenu' and set `imenu-sort-function' to imenu--sort-by-name
  1969. Turn on `recentf-mode'. You might need `recentf-auto-cleanup' = 'never
  1970. Set `column-marker-1' to 79 so you can tell what TEDIT users can't see.
  1971. Load `popup-ruler' for a TEDIT F9 type ruler on steroids.
  1972. I find `transient-mark-mode' totally indespensible.
  1973. CUA mode has some really great rectangle functions."
  1974. (interactive)
  1975. (kill-all-local-variables)
  1976. (set (make-local-variable 'major-mode) 'nmcobol-mode)
  1977. (set (make-local-variable 'mode-name) "NMCOBOL")
  1978. (set (make-local-variable 'make-backup-files) nil) ;necessary for now
  1979. (set-syntax-table nmcobol-mode-syntax-table)
  1980. (nmcobol-setup-regexp-vars)
  1981. (use-local-map nmcobol-mode-map)
  1982. (nmcobol-setup-font-lock)
  1983. (nmcobol-setup-adaptive-fill)
  1984. (nmcobol-setup-abbrevs)
  1985. (nmcobol-setup-imenu)
  1986. (nmcobol-setup-eldoc)
  1987. (nmcobol-setup-indent)
  1988. (nmcobol-setup-skel)
  1989. (nmcobol-setup-column-markers)
  1990. (nmcobol-setup-thing-at-point)
  1991. (nmcobol-setup-anchored-transpose)
  1992. (nmcobol-setup-paren-mode)
  1993. (nmcobol-setup-menu)
  1994. (run-hooks 'nmcobol-mode-hook))
  1995. (provide 'nmcobol-mode)
  1996. ;;; nmcobol-mode.el ends here