| 1 |
;;; ascend-mode.el, a mode for editing ASCEND code in emacs |
| 2 |
;;; |
| 3 |
;;; This file is part of the Ascend modeling library. |
| 4 |
;;; |
| 5 |
;;; Copyright (C) 1994,1995,1996,1997,1998 |
| 6 |
;;; |
| 7 |
;;; Carnegie Mellon University |
| 8 |
;;; |
| 9 |
;;; The Ascend modeling library is free software; you can redistribute it |
| 10 |
;;; and/or modify it under the terms of the GNU General Public License as |
| 11 |
;;; published by the Free Software Foundation; either version 2 of the |
| 12 |
;;; License, or (at your option) any later version. |
| 13 |
;;; |
| 14 |
;;; The Ascend Language Interpreter is distributed in hope that it will |
| 15 |
;;; be useful, but WITHOUT ANY WARRANTY; without even the implied |
| 16 |
;;; warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See |
| 17 |
;;; the GNU General Public License for more details. |
| 18 |
;;; |
| 19 |
;;; You should have received a copy of the GNU General Public License |
| 20 |
;;; along with the program; if not, write to the Free Software |
| 21 |
;;; Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139 USA. Check the |
| 22 |
;;; file named COPYING. |
| 23 |
;;; |
| 24 |
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; |
| 25 |
;;; |
| 26 |
;;; 1994 Aug 26 Mark Thomas <mthomas+@cmu.edu> |
| 27 |
;;; - Initial version created in Lucid Emacs 19.10 |
| 28 |
;;; |
| 29 |
;;; 1995 Sep 13 Mark Thomas <mthomas+@cmu.edu> |
| 30 |
;;; - Updated for use with XEmacs 19.12 |
| 31 |
;;; - Added functions to support Gnu Emacs Menus |
| 32 |
;;; |
| 33 |
;;; 1996 May 29 Mark Thomas <mthomas+@cmu.edu> |
| 34 |
;;; - Changing keywords: |
| 35 |
;;; * INITIALIZATION --> METHODS |
| 36 |
;;; * PROCEDURE --> METHOD |
| 37 |
;;; |
| 38 |
;;; 1997 Nov Mark Thomas <mthomas+@cmu.edu> |
| 39 |
;;; - Major rewrite to make ascend-mode compatible with the |
| 40 |
;;; latest releases of |
| 41 |
;;; * ASCEND IV (0.8) |
| 42 |
;;; * XEmacs (19.16) |
| 43 |
;;; * FSF Emacs (19.34) |
| 44 |
;;; - Most of the code was made more general to be easily |
| 45 |
;;; expandable as ASCEND IV grows |
| 46 |
;;; |
| 47 |
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; |
| 48 |
;;; |
| 49 |
;;; Make sure we get the 'cl package |
| 50 |
;;; |
| 51 |
(require 'cl) |
| 52 |
;;; |
| 53 |
;;; User Friendly Constants ------------------------------------------------- |
| 54 |
;;; |
| 55 |
|
| 56 |
(defvar ascend-block-indent-level 4 |
| 57 |
"*Indentation of ASCEND statements in a block with respect to the |
| 58 |
statement that starts the block.") |
| 59 |
|
| 60 |
(defvar ascend-continuation-indent-level 4 |
| 61 |
"*Indentation of ASCEND statement continuations with respect to |
| 62 |
statement start. CURRENTLY NOT SUPPORTED.") |
| 63 |
|
| 64 |
(defvar ascend-auto-newline nil |
| 65 |
"*If nonnil, a newline is automatically inserted when semicolon(;) |
| 66 |
is entered. Implies ascend-semicolon-auto-indent.") |
| 67 |
|
| 68 |
(defvar ascend-semicolon-auto-indent t |
| 69 |
"*If nonnil, entering a semicolon(;) will always indent the current |
| 70 |
line.") |
| 71 |
|
| 72 |
(defvar ascend-auto-add-end-statement t |
| 73 |
"*If nonnil, pressing RETURN \(or semicolon if ascend-auto-newline |
| 74 |
is nonnil\) on a line that starts a block will cause the matching end |
| 75 |
statement to be automatically added to the buffer.") |
| 76 |
|
| 77 |
(defvar ascend-tab-always-indent nil |
| 78 |
"*If nonnil, pressing TAB will always indent the current line; |
| 79 |
otherwise, TAB will only indent if in the left margin.") |
| 80 |
|
| 81 |
(defvar ascend-expand-abbrevs-in-comments nil |
| 82 |
"If nil, abbrevs are not expanded in comments, strings, and |
| 83 |
notes.") |
| 84 |
|
| 85 |
(defvar ascend-mode-hook nil |
| 86 |
"*Mode hook for ASCEND mode buffers.") |
| 87 |
|
| 88 |
;;; |
| 89 |
;;; Internal Variables ------------------------------------------------------ |
| 90 |
;;; |
| 91 |
|
| 92 |
(defvar ascend-abbrev-table nil |
| 93 |
"Abbrev table for use in ascend-mode buffers") |
| 94 |
|
| 95 |
(defvar ascend-mode-map nil |
| 96 |
"Keymap for use in ascend-mode buffers") |
| 97 |
|
| 98 |
(defvar ascend-mode-syntax-table nil |
| 99 |
"Syntax table for use in ascend-mode buffers") |
| 100 |
|
| 101 |
(defvar ascend-font-lock-keywords nil |
| 102 |
"ASCEND keywords for font-lock mode. |
| 103 |
See the documentation for font-lock-keywords.") |
| 104 |
|
| 105 |
(defvar ascend-menu nil |
| 106 |
"A menu for ASCEND mode buffers.") |
| 107 |
|
| 108 |
(defconst ascend-mode-version "1.14" |
| 109 |
"Version number for this release of ASCEND mode.") |
| 110 |
|
| 111 |
;;; |
| 112 |
;;; Do the actual dirty deed ------------------------------------------------ |
| 113 |
;;; |
| 114 |
|
| 115 |
;;; ABBREV TABLE |
| 116 |
;;; abbrev-list is a list of sub-lists; the car of each sublist is |
| 117 |
;;; the expansion text; the cdr are the strings that expand into |
| 118 |
;;; that text. |
| 119 |
(if (not ascend-abbrev-table) |
| 120 |
(let ((abbrev-list '(("ADD" "add") |
| 121 |
("ALIASES" "aliases" "alii" "al") |
| 122 |
("AND" "and") |
| 123 |
("ANY" "any") |
| 124 |
("ARE_ALIKE" "are_alike" "arealike" "aa") |
| 125 |
("ARE_THE_SAME" "are_the_same" "arethesame" "ats") |
| 126 |
("ARRAY" "array") |
| 127 |
("ATOM" "atom") |
| 128 |
("BREAK" "break") |
| 129 |
("CALL" "call") |
| 130 |
("CARD" "card") |
| 131 |
("CASE" "case") |
| 132 |
("CHOICE" "choice") |
| 133 |
("CONDITIONAL" "conditional") |
| 134 |
("CONSTANT" "constant" "const") |
| 135 |
("CONTINUE" "continue") |
| 136 |
("CREATE" "create") |
| 137 |
("DATA" "data") |
| 138 |
("DECREASING" "descreasing") |
| 139 |
("DEFAULT" "default") |
| 140 |
("DEFINITION" "definition") |
| 141 |
("DIMENSION" "dimension") |
| 142 |
("DIMENSIONLESS" "dimensionless") |
| 143 |
("DO" "do") |
| 144 |
("ELSE" "else") |
| 145 |
("END" "end") |
| 146 |
("EXTERNAL" "external") |
| 147 |
("FALSE" "false") |
| 148 |
("FALL_THROUGH" "fall_through" "fallthrough" "fall") |
| 149 |
("FOR" "for") |
| 150 |
("FROM" "from") |
| 151 |
("GLOBAL" "global") |
| 152 |
("IF" "if") |
| 153 |
("IMPORT" "import") |
| 154 |
("IN" "in") |
| 155 |
("INCREASING" "increasing") |
| 156 |
("INPUT" "input") |
| 157 |
("INTERACTIVE" "interactive") |
| 158 |
("INTERSECTION" "intersection") |
| 159 |
("IS_A" "is_a" "isa") |
| 160 |
("IS_REFINED_TO" "is_refined_to" "isrefinedto" "irt") |
| 161 |
("MAXIMIZE" "maximize" "max") |
| 162 |
("MAX_INTEGER" "max_integer" "maxinteger" "maxint") |
| 163 |
("MAX_REAL" "max_real" "maxreal") |
| 164 |
("METHOD" "method") |
| 165 |
("METHODS" "methods") |
| 166 |
("MINIMIZE" "minimize" "min") |
| 167 |
("MODEL" "model") |
| 168 |
("NOT" "not") |
| 169 |
("NOTES" "notes") |
| 170 |
("OF" "of") |
| 171 |
("OR" "or") |
| 172 |
("OTHERWISE" "otherwise") |
| 173 |
("OUTPUT" "output") |
| 174 |
("PROD" "prod") |
| 175 |
("PROVIDE" "provide") |
| 176 |
("REFINES" "refines") |
| 177 |
("REPLACE" "replace") |
| 178 |
("REQUIRE" "require") |
| 179 |
("RETURN" "return") |
| 180 |
("RUN" "run") |
| 181 |
("SATISFIED" "satisfied") |
| 182 |
("SELECT" "select") |
| 183 |
("SIZE" "size") |
| 184 |
("STOP" "stop") |
| 185 |
("SUCH_THAT" "such_that" "suchthat") |
| 186 |
("SUM" "sum") |
| 187 |
("SWITCH" "switch") |
| 188 |
("THEN" "then") |
| 189 |
("TRUE" "true") |
| 190 |
("UNION" "union") |
| 191 |
("UNITS" "units") |
| 192 |
("UNIVERSAL" "universal") |
| 193 |
("USE" "use") |
| 194 |
("VALUE" "value") |
| 195 |
("WHEN" "when") |
| 196 |
("WHERE" "where") |
| 197 |
("WHILE" "while") |
| 198 |
("WILL_BE" "will_be" "willbe" "wb") |
| 199 |
("WILL_BE_THE_SAME" "willbethesame" "wbts") |
| 200 |
("WILL_NOT_BE_THE_SAME" "willnotbethesame" "wnbts") |
| 201 |
("WITH" "with") |
| 202 |
("WITH_VALUE" "with_value" "withvalue" "wv")) |
| 203 |
)) |
| 204 |
(define-abbrev-table 'ascend-abbrev-table ()) |
| 205 |
(mapcar '(lambda (abbrev) |
| 206 |
(let ((expansion (car abbrev))) |
| 207 |
(mapcar '(lambda (name) |
| 208 |
(define-abbrev ascend-abbrev-table |
| 209 |
name |
| 210 |
expansion |
| 211 |
'ascend-undo-abbrev-in-comment)) |
| 212 |
(cdr abbrev)))) |
| 213 |
abbrev-list))) |
| 214 |
|
| 215 |
|
| 216 |
|
| 217 |
;;; MODE MAP |
| 218 |
;;; The ascend-mode-map is minimal. |
| 219 |
(if (not ascend-mode-map) |
| 220 |
(progn |
| 221 |
(setq ascend-mode-map (make-sparse-keymap)) |
| 222 |
(define-key ascend-mode-map "\t" 'electric-ascend-tab) |
| 223 |
(define-key ascend-mode-map "\C-m" 'electric-ascend-newline-indent) |
| 224 |
(define-key ascend-mode-map "\C-j" 'electric-ascend-newline) |
| 225 |
(define-key ascend-mode-map ";" 'electric-ascend-semicolon) |
| 226 |
(define-key ascend-mode-map "\M-\C-b" 'ascend-backward-block) |
| 227 |
(define-key ascend-mode-map "\M-\C-f" 'ascend-forward-block) |
| 228 |
(define-key ascend-mode-map "\M-\C-a" 'ascend-beginning-of-block) |
| 229 |
(define-key ascend-mode-map "\M-\C-e" 'ascend-end-of-block) |
| 230 |
(define-key ascend-mode-map "\M-\C-h" 'ascend-mark-block) |
| 231 |
)) |
| 232 |
|
| 233 |
|
| 234 |
|
| 235 |
;;; SYNTAX TABLE |
| 236 |
;;; For the syntax table, we have to make the math characters into |
| 237 |
;;; punctuation and define the comment characters. We make braces |
| 238 |
;;; into matching string delimiters when running under XEmacs |
| 239 |
(if (not ascend-mode-syntax-table) |
| 240 |
(progn |
| 241 |
(setq ascend-mode-syntax-table (make-syntax-table)) |
| 242 |
;; the following are the same as the (standard-syntax-table) |
| 243 |
;;(modify-syntax-entry ?\\ "\\" ascend-mode-syntax-table) |
| 244 |
;;(modify-syntax-entry ?\" "\"" ascend-mode-syntax-table) |
| 245 |
;;(modify-syntax-entry ?\t " " ascend-mode-syntax-table) |
| 246 |
;;(modify-syntax-entry ?\n " " ascend-mode-syntax-table) |
| 247 |
;;(modify-syntax-entry ?\r " " ascend-mode-syntax-table) |
| 248 |
;;(modify-syntax-entry ?\f " " ascend-mode-syntax-table) |
| 249 |
;;(modify-syntax-entry ?\v " " ascend-mode-syntax-table) |
| 250 |
;;(modify-syntax-entry ?\[ "(]" ascend-mode-syntax-table) |
| 251 |
;;(modify-syntax-entry ?\] ")[" ascend-mode-syntax-table) |
| 252 |
;;(modify-syntax-entry ?\{ "<}" ascend-mode-syntax-table) |
| 253 |
;;(modify-syntax-entry ?\} ">{" ascend-mode-syntax-table) |
| 254 |
;; |
| 255 |
;; the following differ from the (standard-syntax-table) |
| 256 |
(modify-syntax-entry ?\( "()1" ascend-mode-syntax-table) |
| 257 |
(modify-syntax-entry ?\) ")(4" ascend-mode-syntax-table) |
| 258 |
(modify-syntax-entry ?* ". 23" ascend-mode-syntax-table) |
| 259 |
(modify-syntax-entry ?+ "." ascend-mode-syntax-table) |
| 260 |
(modify-syntax-entry ?- "." ascend-mode-syntax-table) |
| 261 |
(modify-syntax-entry ?= "." ascend-mode-syntax-table) |
| 262 |
(modify-syntax-entry ?% "." ascend-mode-syntax-table) |
| 263 |
(modify-syntax-entry ?\/ "." ascend-mode-syntax-table) |
| 264 |
(modify-syntax-entry ?^ "." ascend-mode-syntax-table) |
| 265 |
(modify-syntax-entry ?< "." ascend-mode-syntax-table) |
| 266 |
(modify-syntax-entry ?> "." ascend-mode-syntax-table) |
| 267 |
(modify-syntax-entry ?& "." ascend-mode-syntax-table) |
| 268 |
(modify-syntax-entry ?| "." ascend-mode-syntax-table) |
| 269 |
(modify-syntax-entry ?. "." ascend-mode-syntax-table) |
| 270 |
(modify-syntax-entry ?, "." ascend-mode-syntax-table) |
| 271 |
;; treat _ as a word for abbrev mode and for search strings. If |
| 272 |
;; _ is not a word char, then when sitting on the b in foo_bar |
| 273 |
;; (looking-at "\\<") returns t |
| 274 |
(modify-syntax-entry ?_ "w" ascend-mode-syntax-table) |
| 275 |
(modify-syntax-entry ?' "\"" ascend-mode-syntax-table) |
| 276 |
;; treat braces as matching string delimters in XEmacs |
| 277 |
(if (string-match "XEmacs" emacs-version) |
| 278 |
(progn |
| 279 |
(modify-syntax-entry ?\{ "\"}" ascend-mode-syntax-table) |
| 280 |
(modify-syntax-entry ?\} "\"{" ascend-mode-syntax-table))) |
| 281 |
)) |
| 282 |
|
| 283 |
|
| 284 |
;;; FONT LOCK |
| 285 |
;;; The font lock stuff looks a lot worse than it is... |
| 286 |
(if ascend-font-lock-keywords |
| 287 |
() |
| 288 |
(let* ((keywords '("ADD" |
| 289 |
"ALIASES" |
| 290 |
"AND" |
| 291 |
"ANY" |
| 292 |
"ARE_ALIKE" |
| 293 |
"ARE_THE_SAME" |
| 294 |
"ARRAY" |
| 295 |
"ATOM" |
| 296 |
"BREAK" |
| 297 |
"CALL" |
| 298 |
"CARD" |
| 299 |
"CASE" |
| 300 |
"CHOICE" |
| 301 |
"CONDITIONAL" |
| 302 |
"CONSTANT" |
| 303 |
"CONTINUE" |
| 304 |
"CREATE" |
| 305 |
"DATA" |
| 306 |
"DECREASING" |
| 307 |
"DEFAULT" |
| 308 |
"DEFINITION" |
| 309 |
"DIMENSION" |
| 310 |
"DIMENSIONLESS" |
| 311 |
"DO" |
| 312 |
"ELSE" |
| 313 |
"END" |
| 314 |
"EXTERNAL" |
| 315 |
"FALSE" |
| 316 |
"FALL_THROUGH" |
| 317 |
"FOR" |
| 318 |
"FROM" |
| 319 |
"GLOBAL" |
| 320 |
"IF" |
| 321 |
"IMPORT" |
| 322 |
"IN" |
| 323 |
"INCREASING" |
| 324 |
"INPUT" |
| 325 |
"INTERACTIVE" |
| 326 |
"INTERSECTION" |
| 327 |
"IS_A" |
| 328 |
"IS_REFINED_TO" |
| 329 |
"MAXIMIZE" |
| 330 |
"MAX_INTEGER" |
| 331 |
"MAX_REAL" |
| 332 |
"METHOD" |
| 333 |
"METHODS" |
| 334 |
"MINIMIZE" |
| 335 |
"MODEL" |
| 336 |
"NOT" |
| 337 |
"NOTES" |
| 338 |
"OF" |
| 339 |
"OR" |
| 340 |
"OTHERWISE" |
| 341 |
"OUTPUT" |
| 342 |
"PROD" |
| 343 |
"PROVIDE" |
| 344 |
"REFINES" |
| 345 |
"REPLACE" |
| 346 |
"REQUIRE" |
| 347 |
"RETURN" |
| 348 |
"RUN" |
| 349 |
"SATISFIED" |
| 350 |
"SELECT" |
| 351 |
"SIZE" |
| 352 |
"STOP" |
| 353 |
"SUCH_THAT" |
| 354 |
"SUM" |
| 355 |
"SWITCH" |
| 356 |
"THEN" |
| 357 |
"TRUE" |
| 358 |
"UNION" |
| 359 |
"UNITS" |
| 360 |
"UNIVERSAL" |
| 361 |
"USE" |
| 362 |
"VALUE" |
| 363 |
"WHEN" |
| 364 |
"WHERE" |
| 365 |
"WHILE" |
| 366 |
"WILL_BE" |
| 367 |
"WILL_BE_THE_SAME" |
| 368 |
"WILL_NOT_BE_THE_SAME" |
| 369 |
"WITH" |
| 370 |
"WITH_VALUE")) |
| 371 |
(keyword-regexp |
| 372 |
(if (fboundp 'make-regexp) |
| 373 |
(concat "\\<" (make-regexp keywords t) "\\>") |
| 374 |
(concat "\\<\\(A\\(DD\\|LIASES\\|N[DY]\\|R\\(E_\\(ALIKE\\|" |
| 375 |
"THE_SAME\\)\\|RAY\\)\\|TOM\\)\\|BREAK\\|C\\(A\\(LL" |
| 376 |
"\\|RD\\|SE\\)\\|HOICE\\|ON\\(DITIONAL\\|STANT\\|" |
| 377 |
"TINUE\\)\\|REATE\\)\\|D\\(ATA\\|E\\(CREASING\\|F" |
| 378 |
"\\(AULT\\|INITION\\)\\)\\|IMENSION\\(\\|LESS\\)\\|O" |
| 379 |
"\\)\\|E\\(LSE\\|ND\\|XTERNAL\\)\\|F\\(AL\\(L_THROUGH" |
| 380 |
"\\|SE\\)\\|OR\\|ROM\\)\\|GLOBAL\\|I\\([FN]\\|MPORT" |
| 381 |
"\\|N\\(CREASING\\|PUT\\|TER\\(ACTIVE\\|SECTION\\)\\)" |
| 382 |
"\\|S_\\(A\\|REFINED_TO\\)\\)\\|M\\(AX\\(IMIZE\\|_" |
| 383 |
"\\(INTEGER\\|REAL\\)\\)\\|ETHODS?\\|INIMIZE\\|ODEL" |
| 384 |
"\\)\\|NOT\\(\\|ES\\)\\|O\\([FR]\\|THERWISE\\|UTPUT\\)" |
| 385 |
"\\|PRO\\(D\\|VIDE\\)\\|R\\(E\\(FINES\\|PLACE\\|QUIRE" |
| 386 |
"\\|TURN\\)\\|UN\\)\\|S\\(ATISFIED\\|ELECT\\|IZE\\|TOP" |
| 387 |
"\\|U\\(CH_THAT\\|M\\)\\|WITCH\\)\\|T\\(HEN\\|RUE\\)" |
| 388 |
"\\|U\\(NI\\(ON\\|TS\\|VERSAL\\)\\|SE\\)\\|VALUE\\|W" |
| 389 |
"\\(H\\(E\\(N\\|RE\\)\\|ILE\\)\\|I\\(LL_\\(BE\\(\\|" |
| 390 |
"_THE_SAME\\)\\|NOT_BE_THE_SAME\\)\\|TH\\(\\|_VALUE" |
| 391 |
"\\)\\)\\)\\)\\>"))) |
| 392 |
(method-regexp "\\<METHOD[ \t]+\\(\w+\\)") |
| 393 |
(type-regexp |
| 394 |
(concat "\\<\\(CONSTANT\\|DEFINITION\\|ATOM\\|MODEL\\)[ \t]+" |
| 395 |
"\\(\w+\\)")) |
| 396 |
) |
| 397 |
(setq ascend-font-lock-keywords |
| 398 |
(purecopy |
| 399 |
(list keyword-regexp |
| 400 |
(list method-regexp 1 'font-lock-function-name-face) |
| 401 |
(list type-regexp 2 'font-lock-type-face) |
| 402 |
))))) |
| 403 |
|
| 404 |
(put 'ascend-mode 'font-lock-defaults '(ascend-font-lock-keywords)) |
| 405 |
|
| 406 |
|
| 407 |
;;; MENU |
| 408 |
;;; The only tricky thing about the menu is using either |
| 409 |
;;; zmacs-regions in XEmacs or mark-active in FSF Emacs |
| 410 |
(if ascend-menu |
| 411 |
() |
| 412 |
(setq ascend-menu |
| 413 |
(list |
| 414 |
'["Goto Block Start" |
| 415 |
(ascend-beginning-of-block) |
| 416 |
t] |
| 417 |
'["Mark Current Block" |
| 418 |
(ascend-mark-block 1) |
| 419 |
t] |
| 420 |
"---" |
| 421 |
(vector |
| 422 |
"Comment Out Region" |
| 423 |
'comment-region |
| 424 |
(if (boundp 'mark-active) |
| 425 |
'(identity mark-active) |
| 426 |
'(or (not zmacs-regions) (mark)))) |
| 427 |
(vector |
| 428 |
"Indent Region" |
| 429 |
'indent-region |
| 430 |
(if (boundp 'mark-active) |
| 431 |
'(identity mark-active) |
| 432 |
'(or (not zmacs-regions) (mark)))) |
| 433 |
'["Indent Line" |
| 434 |
ascend-indent-line |
| 435 |
t] |
| 436 |
"---" |
| 437 |
'["Auto Newline On ;" |
| 438 |
(setq ascend-auto-newline (not ascend-auto-newline)) |
| 439 |
:style toggle |
| 440 |
:selected ascend-auto-newline] |
| 441 |
'["Auto Indent On ;" |
| 442 |
(setq ascend-semicolon-auto-indent |
| 443 |
(not ascend-semicolon-auto-indent)) |
| 444 |
:active (not ascend-auto-newline) |
| 445 |
:style toggle |
| 446 |
:selected ascend-semicolon-auto-indent] |
| 447 |
'["Auto Add Matching END" |
| 448 |
(setq ascend-auto-add-end-statement |
| 449 |
(not ascend-auto-add-end-statement)) |
| 450 |
:style toggle |
| 451 |
:selected ascend-auto-add-end-statement] |
| 452 |
'["Always Indent On TAB" |
| 453 |
(setq ascend-tab-always-indent (not ascend-tab-always-indent)) |
| 454 |
:style toggle |
| 455 |
:selected ascend-tab-always-indent] |
| 456 |
'["Auto Indent On RETURN" |
| 457 |
ascend-toggle-newline-linefeed |
| 458 |
:style toggle |
| 459 |
:selected (eq (key-binding "\C-m") 'electric-ascend-newline-indent)] |
| 460 |
'["Auto Expand Abbreviations" |
| 461 |
(setq abbrev-mode (not abbrev-mode)) |
| 462 |
:style toggle |
| 463 |
:selected abbrev-mode] |
| 464 |
'["Expand Abbrevs in Comments" |
| 465 |
(setq ascend-expand-abbrevs-in-comments |
| 466 |
(not ascend-expand-abbrevs-in-comments)) |
| 467 |
:active abbrev-mode |
| 468 |
:style toggle |
| 469 |
:selected ascend-expand-abbrevs-in-comments] |
| 470 |
'"---" |
| 471 |
'["Describe ASCEND mode" |
| 472 |
describe-mode |
| 473 |
t] |
| 474 |
))) |
| 475 |
|
| 476 |
|
| 477 |
;;; ASCEND MODE |
| 478 |
;;;###autoload |
| 479 |
(defun ascend-mode () |
| 480 |
"Major mode for editing ASCEND Code. |
| 481 |
TAB indents for ASCEND code. |
| 482 |
DELETE converts tabs to spaces as it moves back. |
| 483 |
\\{ascend-mode-map} |
| 484 |
Variables controlling indentation style: |
| 485 |
ascend-auto-newline (default nil) |
| 486 |
If nonnil, a newline is automatically inserted when |
| 487 |
semicolon(;) is entered. Implies ascend-semicolon-auto-indent. |
| 488 |
ascend-semicolon-auto-indent (default t) |
| 489 |
If nonnil, entering a semicolon(;) will always indent |
| 490 |
the current line. |
| 491 |
ascend-tab-always-indent (default nil) |
| 492 |
If nonnil, pressing TAB will always indent the current line; |
| 493 |
otherwise, TAB will only indent if in the left margin. |
| 494 |
ascend-block-indent-level (default 4) |
| 495 |
Indentation of ASCEND statements in an block with |
| 496 |
respect to the statement that starts the block. |
| 497 |
ascend-continuation-indent-level (default 4) |
| 498 |
Indentation of ASCEND statement continuations with respect |
| 499 |
to statement start. |
| 500 |
|
| 501 |
Comments delimited by (* .. *). The statement separator is the semicolon. |
| 502 |
|
| 503 |
Turning on ASCEND-mode calls the value of the variable ascend-mode-hook |
| 504 |
with no args, if that value is non-nil." |
| 505 |
(interactive) |
| 506 |
;; |
| 507 |
(kill-all-local-variables) |
| 508 |
(use-local-map ascend-mode-map) |
| 509 |
(setq major-mode 'ascend-mode) |
| 510 |
(setq mode-name "ASCEND") |
| 511 |
(setq local-abbrev-table ascend-abbrev-table) |
| 512 |
(set-syntax-table ascend-mode-syntax-table) |
| 513 |
;; |
| 514 |
(make-local-variable 'indent-line-function) |
| 515 |
(setq indent-line-function 'ascend-indent-line) |
| 516 |
;; |
| 517 |
(make-local-variable 'comment-start) |
| 518 |
(setq comment-start "(*") |
| 519 |
(make-local-variable 'comment-end) |
| 520 |
(setq comment-end "*)") |
| 521 |
(make-local-variable 'ascend-comment-start) |
| 522 |
(setq ascend-comment-start (regexp-quote comment-start)) |
| 523 |
(make-local-variable 'ascend-comment-end) |
| 524 |
(setq ascend-comment-end (regexp-quote comment-end)) |
| 525 |
(make-local-variable 'ascend-comment-start-end) |
| 526 |
(setq ascend-comment-start-end (concat ascend-comment-start "\\|" |
| 527 |
ascend-comment-end)) |
| 528 |
;; |
| 529 |
(make-local-variable 'ascend-notes-start) |
| 530 |
(setq ascend-notes-start "{") |
| 531 |
(make-local-variable 'ascend-notes-end) |
| 532 |
(setq ascend-notes-end "}") |
| 533 |
;; |
| 534 |
(make-local-variable 'ascend-block-start-regexp-list) |
| 535 |
(setq ascend-block-start-regexp-list '("FOR" |
| 536 |
"METHOD" |
| 537 |
"\\(UNIVERSAL[ \t]+\\)?MODEL" |
| 538 |
"\\(UNIVERSAL[ \t]+\\)?ATOM" |
| 539 |
"IF" |
| 540 |
"NOTES" |
| 541 |
"SELECT" |
| 542 |
"WHEN" |
| 543 |
"SWITCH" |
| 544 |
"WHILE" |
| 545 |
"DEFINITION" |
| 546 |
"UNITS" |
| 547 |
"CONDITIONAL" |
| 548 |
"DATA" |
| 549 |
)) |
| 550 |
(make-local-variable 'ascend-block-start-regexp) |
| 551 |
(setq ascend-block-start-regexp |
| 552 |
(concat "\\<\\(" |
| 553 |
(mapconcat 'identity ascend-block-start-regexp-list "\\|") |
| 554 |
"\\)\\>")) |
| 555 |
(make-local-variable 'ascend-block-end-regexp-list) |
| 556 |
(setq ascend-block-end-regexp-list '("END")) |
| 557 |
(make-local-variable 'ascend-block-end-regexp) |
| 558 |
(setq ascend-block-end-regexp |
| 559 |
(concat "\\<\\(" |
| 560 |
(mapconcat 'identity ascend-block-end-regexp-list "\\|") |
| 561 |
"\\)\\>")) |
| 562 |
(make-local-variable 'ascend-outdent-regexp-list) |
| 563 |
(setq ascend-outdent-regexp-list '("\\<CASE\\>" |
| 564 |
"\\<OTHERWISE\\>" |
| 565 |
")[ \t]*REFINES\\>" |
| 566 |
"\\(\)[ \t]*\\)?WHERE[ \t\n]*\(" |
| 567 |
"\\<ELSE\\>")) |
| 568 |
(make-local-variable 'ascend-outdent-regexp) |
| 569 |
(setq ascend-outdent-regexp |
| 570 |
(concat "\\(" |
| 571 |
(mapconcat 'identity ascend-outdent-regexp-list "\\|") |
| 572 |
"\\)")) |
| 573 |
(make-local-variable 'ascend-no-semi-regexp-list) |
| 574 |
(setq ascend-no-semi-regexp-list '("METHODS")) |
| 575 |
(make-local-variable 'ascend-no-semi-regexp) |
| 576 |
(setq ascend-no-semi-regexp |
| 577 |
(concat "\\<\\(" |
| 578 |
(mapconcat 'identity ascend-no-semi-regexp-list "\\|") |
| 579 |
"\\)\\>")) |
| 580 |
;; |
| 581 |
(ascend-create-menu) |
| 582 |
(if (null (string-match "XEmacs" emacs-version)) |
| 583 |
(progn |
| 584 |
;; Do the font magic for FSF Emacs |
| 585 |
(make-local-variable 'font-lock-keywords) |
| 586 |
(setq font-lock-keywords |
| 587 |
(append (list (list (car ascend-font-lock-keywords) |
| 588 |
'(0 font-lock-keyword-face))) |
| 589 |
(mapcar '(lambda (x) (list (car x) (cdr x))) |
| 590 |
(cdr ascend-font-lock-keywords)))))) |
| 591 |
(run-hooks 'ascend-mode-hook)) |
| 592 |
|
| 593 |
|
| 594 |
;;; |
| 595 |
;;; Electric functions ------------------------------------------------------ |
| 596 |
;;; |
| 597 |
|
| 598 |
(defun electric-ascend-newline (count) |
| 599 |
"Insert COUNT newlines." |
| 600 |
(interactive "P") |
| 601 |
(delete-horizontal-space) |
| 602 |
(ascend-indent-line) |
| 603 |
(if ascend-auto-add-end-statement |
| 604 |
(ascend-add-matching-end-block)) |
| 605 |
(newline (prefix-numeric-value count))) |
| 606 |
|
| 607 |
|
| 608 |
(defun electric-ascend-newline-indent (count) |
| 609 |
"Insert COUNT newlines then indent final line." |
| 610 |
(interactive "P") |
| 611 |
(electric-ascend-newline count) |
| 612 |
(indent-to (ascend-calculate-indentation))) |
| 613 |
|
| 614 |
|
| 615 |
(defun electric-ascend-tab (count) |
| 616 |
"Called when TAB is pressed. If COUNT is specified, |
| 617 |
insert COUNT tabs; if ascend-tab-always-indent is t, indent line; |
| 618 |
otherwise, only indent if before first character on line." |
| 619 |
(interactive "P") |
| 620 |
(cond (count |
| 621 |
(self-insert-command (prefix-numeric-value count))) |
| 622 |
(ascend-tab-always-indent |
| 623 |
(ascend-indent-line)) |
| 624 |
((ascend-point-in-left-margin-p) |
| 625 |
(ascend-indent-line)) |
| 626 |
((ascend-point-in-string-p) |
| 627 |
()) |
| 628 |
(t |
| 629 |
(self-insert-command 1)))) |
| 630 |
|
| 631 |
|
| 632 |
(defun electric-ascend-semicolon (count) |
| 633 |
"Called when semicolon(;) is pressed. If COUNT is |
| 634 |
specified, insert COUNT semicolons; otherwise insert a semicolon and |
| 635 |
correct line's indentation. If ascend-auto-newline is t, insert newline." |
| 636 |
(interactive "P") |
| 637 |
(cond (count |
| 638 |
(self-insert-command (prefix-numeric-value count))) |
| 639 |
((ascend-point-in-comment-p) |
| 640 |
(self-insert-command 1)) |
| 641 |
((ascend-point-in-string-p) |
| 642 |
()) |
| 643 |
(ascend-auto-newline |
| 644 |
(self-insert-command 1) |
| 645 |
(ascend-indent-line) |
| 646 |
(if ascend-auto-add-end-statement |
| 647 |
(ascend-add-matching-end-block)) |
| 648 |
(newline-and-indent)) |
| 649 |
(ascend-semicolon-auto-indent |
| 650 |
(self-insert-command 1) |
| 651 |
(ascend-indent-line)) |
| 652 |
(t |
| 653 |
(self-insert-command 1)))) |
| 654 |
|
| 655 |
|
| 656 |
;;; |
| 657 |
;;; Interactive functions --------------------------------------------------- |
| 658 |
;;; |
| 659 |
|
| 660 |
|
| 661 |
(defun ascend-toggle-newline-linefeed (arg) |
| 662 |
"Toggle the meanings of newline (C-m) and linefeed (C-j). |
| 663 |
By default in ASCEND-mode, NEWLINE runs 'electric-ascend-newline-indent |
| 664 |
---which inserts a newline and indents---and LINEFEED calls |
| 665 |
'electric-ascend-newline---which inserts a newline but does not indent). |
| 666 |
Calling this function without an argument toggles the meanings; if ARG is |
| 667 |
specified and is positive, newline is set to 'electric-ascend-newline; |
| 668 |
otherwise newline is set to 'electric-ascend-newline-indent." |
| 669 |
(interactive "P") |
| 670 |
(if (or (and (not arg) |
| 671 |
(eq (key-binding "\C-m") 'electric-ascend-newline-indent)) |
| 672 |
(< 0 (prefix-numeric-value arg))) |
| 673 |
(progn |
| 674 |
(define-key ascend-mode-map "\C-m" 'electric-ascend-newline) |
| 675 |
(define-key ascend-mode-map "\C-j" 'electric-ascend-newline-indent)) |
| 676 |
(define-key ascend-mode-map "\C-m" 'electric-ascend-newline-indent) |
| 677 |
(define-key ascend-mode-map "\C-j" 'electric-ascend-newline))) |
| 678 |
|
| 679 |
|
| 680 |
;;; |
| 681 |
;;; Indenting functions (interactive) --------------------------------------- |
| 682 |
;;; |
| 683 |
|
| 684 |
|
| 685 |
(defun ascend-calculate-indentation () |
| 686 |
"Calculate the indentation of the current ASCEND line without |
| 687 |
modifying the buffer." |
| 688 |
(let ((case-fold-search nil) |
| 689 |
tmp |
| 690 |
;; tmp is the point where the current comment starts--when we |
| 691 |
;; are in a comment; if we are not in a comment, it is nil |
| 692 |
) |
| 693 |
(save-excursion |
| 694 |
;; The next two lines put us on the first nonwhitespace character on |
| 695 |
;; the line---if we've been called from indent-line, then this |
| 696 |
;; should have already been done. |
| 697 |
(beginning-of-line) |
| 698 |
(skip-chars-forward " \t") |
| 699 |
(cond (;; handle comments |
| 700 |
(setq tmp (ascend-point-in-comment-p)) |
| 701 |
(let (;; count-stars is the number of asterisks that start the |
| 702 |
;; current line--ignoring any leading whitespace |
| 703 |
(count-stars (if (looking-at "\\*+") |
| 704 |
(apply '- (match-data)) |
| 705 |
0))) |
| 706 |
;; The next line searches for the previous nonblank line |
| 707 |
;; within the current comment; if there isn't one, it |
| 708 |
;; leaves point on the comment start character |
| 709 |
(re-search-backward "^[ \t]*\\S-" tmp 1) |
| 710 |
(if (looking-at ascend-comment-start) |
| 711 |
(progn |
| 712 |
;; There was no nonblank line--we are current looking |
| 713 |
;; at "(" followed be one or more "*"s. Set the |
| 714 |
;; indentation to the column after the last asterisk, |
| 715 |
;; except any asterisks that start the line we are |
| 716 |
;; indenting should fall under asterisks in the |
| 717 |
;; previous line. For example |
| 718 |
;; (****** |
| 719 |
;; ** foo |
| 720 |
(forward-char 1) ;; skip the "(" |
| 721 |
(skip-chars-forward "\\*") ;; skip the "*"s |
| 722 |
(max 0 (+ count-stars (current-column)))) |
| 723 |
(progn |
| 724 |
;; We found a nonblank line. Set the indentation to |
| 725 |
;; the column containing the first nonblank character. |
| 726 |
;; Ignore asterisks in the previous and current lines. |
| 727 |
(skip-syntax-forward " ") |
| 728 |
(current-column))))) |
| 729 |
(;; handle notes |
| 730 |
(ascend-point-in-note-p) |
| 731 |
(current-column)) |
| 732 |
(;; handle the end of a block |
| 733 |
(looking-at ascend-block-end-regexp) |
| 734 |
(if (setq tmp (ascend-get-matching-block-start)) |
| 735 |
(goto-char tmp)) |
| 736 |
(current-column)) |
| 737 |
(;; handle outdented keywords like ELSE or CASE |
| 738 |
(remove-if-not 'looking-at ascend-outdent-regexp-list) |
| 739 |
(if (setq tmp (ascend-pos-beginning-of-block 1)) |
| 740 |
(goto-char tmp)) |
| 741 |
(current-column)) |
| 742 |
(;; Lines that begin with a close-paren should indent to the |
| 743 |
;; column of the first nonblank character on the line |
| 744 |
;; containing the matching open-paren. |
| 745 |
(looking-at ")") |
| 746 |
(forward-char 1) |
| 747 |
(backward-sexp) |
| 748 |
(beginning-of-line) |
| 749 |
(skip-syntax-forward " ") |
| 750 |
(current-column)) |
| 751 |
(;; This statement puts us on the previous nonblank line. If |
| 752 |
;; this branch fires, then no previous nonblank line exists, |
| 753 |
;; and we should set the indentation to 0. |
| 754 |
(null (re-search-backward "^[ \t]*\\S-" nil t)) |
| 755 |
0) |
| 756 |
(;; Going backward may have put us in a comment; if so, go to |
| 757 |
;; the beginning of the comment and start the process all |
| 758 |
;; over again. |
| 759 |
(setq tmp (ascend-point-in-comment-p)) |
| 760 |
(goto-char tmp) |
| 761 |
(ascend-calculate-indentation)) |
| 762 |
(;; This statement puts us on the first nonblank character on |
| 763 |
;; the previous nonblank line. We then check to see if it is |
| 764 |
;; a block-start statement; if so, we need to indent by the |
| 765 |
;; block-indent-level. |
| 766 |
(and (progn |
| 767 |
(beginning-of-line) |
| 768 |
(skip-chars-forward " \t")) |
| 769 |
(looking-at ascend-block-start-regexp)) |
| 770 |
(+ (current-column) ascend-block-indent-level)) |
| 771 |
(;; check if it is an outdented statement |
| 772 |
(looking-at ascend-outdent-regexp) |
| 773 |
(+ (current-column) ascend-block-indent-level)) |
| 774 |
(;; just return the current column |
| 775 |
t |
| 776 |
(current-column)) |
| 777 |
;; The following will not get invoked because of the |
| 778 |
;; t on the previous condition. We need to do something |
| 779 |
;; here to get continuation lines to work. |
| 780 |
;;;;;;;;;;;;;;;;;;;;;;;;;; |
| 781 |
((looking-at ascend-comment-start) |
| 782 |
;; Code should line up with comments |
| 783 |
(looking-at ascend-comment-start) |
| 784 |
(current-column)) |
| 785 |
(;; This line does not require a semicolon, so it should be |
| 786 |
;; considered complete, and the next line should NOT be |
| 787 |
;; considered a continuation line. |
| 788 |
(remove-if-not 'looking-at ascend-no-semi-regexp-list) |
| 789 |
(current-column)) |
| 790 |
((save-excursion |
| 791 |
(end-of-line) |
| 792 |
(if (setq tmp (ascend-point-in-comment-p)) |
| 793 |
(goto-char tmp)) |
| 794 |
(skip-syntax-backward " ") |
| 795 |
(forward-char -1) |
| 796 |
(looking-at ";")) |
| 797 |
;; This line ends in a semicolon, so the next line should NOT |
| 798 |
;; be considered a continuation line. |
| 799 |
(current-column)) |
| 800 |
(t |
| 801 |
;; Consider the line to be a continuation line |
| 802 |
(+ (current-column) ascend-continuation-indent-level))) |
| 803 |
))) |
| 804 |
|
| 805 |
|
| 806 |
(defun ascend-indent-line () |
| 807 |
"Indent the current line relative to the current block." |
| 808 |
(interactive) |
| 809 |
(let ((m (point-marker))) |
| 810 |
(beginning-of-line) |
| 811 |
(if (ascend-point-in-note-p) |
| 812 |
nil |
| 813 |
(delete-horizontal-space) |
| 814 |
(indent-to (ascend-calculate-indentation))) |
| 815 |
(if (> m (point)) |
| 816 |
(goto-char m)))) |
| 817 |
|
| 818 |
|
| 819 |
;;; |
| 820 |
;;; Marking functions (interactive) ----------------------------------------- |
| 821 |
;;; |
| 822 |
|
| 823 |
|
| 824 |
(defun ascend-mark-block (count) |
| 825 |
"If point is inside a block, mark the current block by putting mark |
| 826 |
at the beginning and point at the end. If point is outside a block, |
| 827 |
mark the first complete block we find \(designated by the first END |
| 828 |
statement\). With argument COUNT, mark COUNT blocks outward or |
| 829 |
forward." |
| 830 |
(interactive "p") |
| 831 |
(if (re-search-forward ascend-block-end-regexp nil t count) |
| 832 |
;; if this fails, we haven't moved. if it succeeds, we are |
| 833 |
;; sitting just after END. Go backward one word to the start of |
| 834 |
;; END, then get the position where of the matching block start. |
| 835 |
;; Go to the end of the line, push the mark to the beginning of |
| 836 |
;; the block, and then activate the region. |
| 837 |
(let (beg-defun) |
| 838 |
(skip-syntax-backward "w_") |
| 839 |
(setq beg-defun (ascend-get-matching-block-start)) |
| 840 |
(end-of-line) |
| 841 |
(push-mark beg-defun nil t)))) |
| 842 |
|
| 843 |
|
| 844 |
;;; |
| 845 |
;;; Movement functions (interactive) --------------------------------------- |
| 846 |
;;; |
| 847 |
|
| 848 |
|
| 849 |
(defun ascend-backward-block (count) |
| 850 |
"Move backward to the next statement that begins an ASCEND block. |
| 851 |
With argument COUNT, move backward COUNT begin statements. Treats |
| 852 |
comments and NOTES as whitespace." |
| 853 |
(interactive "p") |
| 854 |
(goto-char (or (ascend-pos-block-backward count) (point-min)))) |
| 855 |
|
| 856 |
|
| 857 |
(defun ascend-forward-block (count) |
| 858 |
"Move forward to the next statement that ends an ASCEND block. With |
| 859 |
argument COUNT, move forward COUNT end statements. Treats comments |
| 860 |
and NOTES as whitespace." |
| 861 |
(interactive "p") |
| 862 |
(goto-char (or (ascend-pos-block-forward count) (point-min))) |
| 863 |
(end-of-line)) |
| 864 |
|
| 865 |
|
| 866 |
(defun ascend-beginning-of-block (count) |
| 867 |
"Go to the beginning of the current block. With argument COUNT, |
| 868 |
move outward COUNT blocks. Treats comments and NOTES as whitespace. |
| 869 |
|
| 870 |
This function differs from ascend-backward-block in that matching |
| 871 |
begin-block/end-block pairs are skipped, so that point moves to the |
| 872 |
beginning of the block that contains point, not to the beginning of |
| 873 |
the first begin-block statement we find." |
| 874 |
(interactive "p") |
| 875 |
(goto-char (or (ascend-pos-beginning-of-block count) (point-min)))) |
| 876 |
|
| 877 |
|
| 878 |
(defun ascend-end-of-block (count) |
| 879 |
"Go to the end of the current block. With argument COUNT, move |
| 880 |
outward COUNT blocks. Treats comments and NOTES as whitespace. |
| 881 |
|
| 882 |
This function differs from ascend-forward-block in that matching |
| 883 |
begin-block/end-block pairs are skipped, so that point moves to the |
| 884 |
end of the block that contains point, not to the end of the first |
| 885 |
end-block statement we find." |
| 886 |
(interactive "p") |
| 887 |
(goto-char (or (ascend-pos-end-of-block count) (point-max))) |
| 888 |
(end-of-line)) |
| 889 |
|
| 890 |
;;; |
| 891 |
;;; Is point here? ---------------------------------------------------------- |
| 892 |
;;; |
| 893 |
|
| 894 |
|
| 895 |
(defun ascend-point-in-comment-p () |
| 896 |
"If point is in an ASCEND comment, return the character |
| 897 |
position where the comment begins; otherwise return nil." |
| 898 |
;; Search backward for the first ascend-comment-start or |
| 899 |
;; ascend-comment-end expression we see; if we find a |
| 900 |
;; ascend-comment-start, we are in a comment and return point; |
| 901 |
;; otherwise, we are not in a comment and return nil. NOTE: Does |
| 902 |
;; not handle nested comments; does not handle "(*)" correctly; does |
| 903 |
;; not process ascend-comment-start/ascend-comment-end characters in |
| 904 |
;; symbols and notes correctly. |
| 905 |
(save-match-data |
| 906 |
(save-excursion |
| 907 |
(and (re-search-backward ascend-comment-start-end nil t) |
| 908 |
(looking-at ascend-comment-start) |
| 909 |
(point))))) |
| 910 |
|
| 911 |
|
| 912 |
;;;(defun ascend-point-in-nested-comment-p () |
| 913 |
;;; "Return t if point is in a nested ASCEND comment." |
| 914 |
;;; ;; Set c to zero; search backward for ascend-comment-start and |
| 915 |
;;; ;; ascend-comment-end expressions; increment/decrement c for each |
| 916 |
;;; ;; ascend-comment-start/-end; if c is > 0 when we reach the |
| 917 |
;;; ;; beginning of the buffer, we are in a comment. NOTE: Handles |
| 918 |
;;; ;; nested comments, but does not handle "(*)" correctly; does not |
| 919 |
;;; ;; process ascend-comment-start/ascend-comment-end characters in |
| 920 |
;;; ;; symbols and notes correctly. |
| 921 |
;;; (let ((c 0)) |
| 922 |
;;; (save-excursion |
| 923 |
;;; (while (re-search-backward ascend-comment-start-end |
| 924 |
;;; nil t) |
| 925 |
;;; (setq c (if (looking-at ascend-comment-start) (1+ c) (1- c))))) |
| 926 |
;;; (> c 0))) |
| 927 |
|
| 928 |
|
| 929 |
(defun ascend-point-in-note-p () |
| 930 |
"Return the position of the starting character if point |
| 931 |
is in an ASCEND notes block." |
| 932 |
(save-match-data |
| 933 |
(save-excursion |
| 934 |
(and (re-search-backward (concat ascend-notes-start "\\|" |
| 935 |
ascend-notes-end) |
| 936 |
nil t) |
| 937 |
(looking-at ascend-notes-start) |
| 938 |
(point))))) |
| 939 |
|
| 940 |
|
| 941 |
(defun ascend-point-in-string-p () |
| 942 |
"Return the position of the starting character if point |
| 943 |
is within an ASCEND string. |
| 944 |
Assumes strings never contain newlines." |
| 945 |
(save-match-data |
| 946 |
(save-excursion |
| 947 |
(let (;; parse from the beginning-of-line to point |
| 948 |
(p (point)) |
| 949 |
;; the character that begins the string |
| 950 |
c) |
| 951 |
(beginning-of-line) |
| 952 |
;; since {} are treated as string delimiters in XEmacs, we |
| 953 |
;; have to watch for them in the call to parse-partial-sexp. |
| 954 |
;; If we get \} as a close string character, pretend we are |
| 955 |
;; not in a string |
| 956 |
(if (and (setq c (nth 3 (parse-partial-sexp (point) p))) |
| 957 |
(null (eq c ?\}))) |
| 958 |
(progn |
| 959 |
(search-backward (char-to-string c)) |
| 960 |
(point))))))) |
| 961 |
|
| 962 |
|
| 963 |
(defun ascend-point-in-left-margin-p () |
| 964 |
"Return t if point is in left margin; the left margin is |
| 965 |
the whitespace between the left edge of the page and the start of text |
| 966 |
on the line." |
| 967 |
(save-excursion |
| 968 |
(skip-chars-backward " \t") |
| 969 |
(bolp))) |
| 970 |
|
| 971 |
|
| 972 |
;;; |
| 973 |
;;; Where is some text? ----------------------------------------------------- |
| 974 |
;;; |
| 975 |
|
| 976 |
|
| 977 |
(defun ascend-pos-block-backward (count) |
| 978 |
"Return the position of the next unprotected |
| 979 |
ascend-block-start-statement. Return nil if we do not find a block |
| 980 |
start statement." |
| 981 |
(save-excursion |
| 982 |
(let (;; match case inside of this function |
| 983 |
(case-fold-search nil) |
| 984 |
;; tmp holds the beginning of the current comment, string, |
| 985 |
;; or note |
| 986 |
tmp) |
| 987 |
(while (and (> count 0) |
| 988 |
(re-search-backward ascend-block-start-regexp nil t)) |
| 989 |
(cond (;; ignore strings, comments, and notes |
| 990 |
(setq tmp (or (ascend-point-in-string-p) |
| 991 |
(ascend-point-in-comment-p) |
| 992 |
(ascend-point-in-note-p))) |
| 993 |
(goto-char tmp)) |
| 994 |
(;; ignore block-starts that following block-ends: go |
| 995 |
;; backward one token to make sure we are not sitting |
| 996 |
;; on the "FOR" of an "END FOR" statement |
| 997 |
(progn (skip-syntax-backward " ") |
| 998 |
(skip-syntax-backward "w_") |
| 999 |
(looking-at ascend-block-end-regexp))) |
| 1000 |
(t |
| 1001 |
(setq count (1- count))))) |
| 1002 |
(match-beginning 0)))) |
| 1003 |
|
| 1004 |
|
| 1005 |
(defun ascend-pos-block-forward (count) |
| 1006 |
"Return the position of the next unprotected |
| 1007 |
ascend-block-end-statement. Return nil if we do not find a block end |
| 1008 |
statement." |
| 1009 |
(save-excursion |
| 1010 |
(let (;; match case inside of this function |
| 1011 |
(case-fold-search nil)) |
| 1012 |
(while (and (> count 0) |
| 1013 |
(re-search-forward ascend-block-end-regexp nil t)) |
| 1014 |
(if (not (or (ascend-point-in-string-p) |
| 1015 |
(ascend-point-in-comment-p) |
| 1016 |
(ascend-point-in-note-p))) |
| 1017 |
(setq count (1- count)))) |
| 1018 |
(match-beginning 0)))) |
| 1019 |
|
| 1020 |
|
| 1021 |
(defun ascend-pos-beginning-of-block (count) |
| 1022 |
"Return the position of the start of the block that currently |
| 1023 |
contains point. Return nil if we do not find a block-start statement. |
| 1024 |
Comments and notes are treated as whitespace. |
| 1025 |
|
| 1026 |
This function searches backward for ascend-block-start-regexp. This |
| 1027 |
function differs from ascend-pos-block-backward in that matching |
| 1028 |
block-end/block-start statements are ignored." |
| 1029 |
(let (;; match case inside of this function |
| 1030 |
(case-fold-search nil) |
| 1031 |
;; level is used to keep track of the begin-blocks and end-blocks; |
| 1032 |
;; it is initially count, indicating we are inside count |
| 1033 |
;; levels of nested blocks |
| 1034 |
(level count) |
| 1035 |
;; tmp is used for random values, such as the start of a comment, |
| 1036 |
;; the current value of point, etc. |
| 1037 |
tmp |
| 1038 |
;; the regexp to match the begins and ends of blocks |
| 1039 |
(regex (concat "\\(" ascend-block-start-regexp "\\|" |
| 1040 |
ascend-block-end-regexp "\\)"))) |
| 1041 |
(save-excursion |
| 1042 |
;; ;; move to the beginning of the line and see if we are looking |
| 1043 |
;; ;; at a block-start, if so adjust the level. we have to do |
| 1044 |
;; ;; this, otherwise sitting just after the END keyword will |
| 1045 |
;; ;; behave as if we're not in that block. |
| 1046 |
;; (skip-syntax-backward " ") |
| 1047 |
;; (beginning-of-line) |
| 1048 |
;; (skip-chars-forward " \t") |
| 1049 |
;; (if (and (looking-at ascend-block-start-regexp) |
| 1050 |
;; (not (or (ascend-point-in-string-p) |
| 1051 |
;; (ascend-point-in-comment-p) |
| 1052 |
;; (ascend-point-in-note-p)))) |
| 1053 |
;; (setq level (1- level))) |
| 1054 |
(while (and (> level 0) |
| 1055 |
(re-search-backward regex nil t)) |
| 1056 |
(cond (;; get out of the string, comment, or not |
| 1057 |
(setq tmp (or (ascend-point-in-string-p) |
| 1058 |
(ascend-point-in-comment-p) |
| 1059 |
(ascend-point-in-note-p))) |
| 1060 |
(goto-char tmp)) |
| 1061 |
(;; we're at block-end position; increase the level |
| 1062 |
(looking-at ascend-block-end-regexp) |
| 1063 |
(setq level (1+ level))) |
| 1064 |
;; at this point we know we are on a block-start. go |
| 1065 |
;; backward one token to make sure we are not sitting on the |
| 1066 |
;; "FOR" of an "END FOR" statement |
| 1067 |
((progn (setq tmp (- 0 (skip-syntax-backward " ") |
| 1068 |
(skip-syntax-backward "w_"))) |
| 1069 |
(looking-at ascend-block-end-regexp)) |
| 1070 |
(setq level (1+ level))) |
| 1071 |
(t |
| 1072 |
(forward-char tmp) |
| 1073 |
(setq level (1- level))))) |
| 1074 |
(if (zerop level) |
| 1075 |
(point) |
| 1076 |
nil)))) |
| 1077 |
|
| 1078 |
|
| 1079 |
(defun ascend-pos-end-of-block (count) |
| 1080 |
"Return the position of the end of the block that currently contains |
| 1081 |
point. Return nil if we do not find a block-end statement. Comments |
| 1082 |
and notes are treated as whitespace. |
| 1083 |
|
| 1084 |
This function searches forward for ascend-block-end-regexp. This |
| 1085 |
function differs from ascend-pos-block-forward in that matching |
| 1086 |
block-end/block-start statements are ignored." |
| 1087 |
(let (;; match case inside of this function |
| 1088 |
(case-fold-search nil) |
| 1089 |
;; level is used to keep track of the begin-blocks and end-blocks; |
| 1090 |
;; it is initially count, indicating we are inside coun |
| 1091 |
;; levels of nested blocks |
| 1092 |
(level count) |
| 1093 |
;; tmp is used for random values, such as the start of a comment, |
| 1094 |
;; the current value of point, etc. |
| 1095 |
tmp |
| 1096 |
;; the regexp to match the begins and ends of blocks |
| 1097 |
(regex (concat "\\(" ascend-block-start-regexp "\\|" |
| 1098 |
ascend-block-end-regexp "\\)"))) |
| 1099 |
(save-excursion |
| 1100 |
;; ;; move to the first non-whitespace character on the line and |
| 1101 |
;; ;; see if we are looking at a block-end, if so adjust the level. |
| 1102 |
;; ;; we have to do this, otherwise sitting just before a |
| 1103 |
;; ;; block-start keyword will behave as if we're not in that |
| 1104 |
;; ;; block. |
| 1105 |
;; (skip-syntax-forward " ") |
| 1106 |
;; (if (and (looking-at ascend-block-end-regexp) |
| 1107 |
;; (not (or (ascend-point-in-string-p) |
| 1108 |
;; (ascend-point-in-comment-p) |
| 1109 |
;; (ascend-point-in-note-p)))) |
| 1110 |
;; (setq level (1- level))) |
| 1111 |
(while (and (> level 0) |
| 1112 |
(re-search-forward regex nil t)) |
| 1113 |
;; save point: since we move backward below, we need to return |
| 1114 |
;; here before our next time through the loop so we don't |
| 1115 |
;; match the same regexp again |
| 1116 |
(setq tmp (point)) |
| 1117 |
;; we're at the end of the regexp, move to the front |
| 1118 |
(goto-char (match-beginning 0)) |
| 1119 |
(cond (;; if we are in a string, comment, or note, we need to |
| 1120 |
;; keep searching. unfortunately, there is no quick |
| 1121 |
;; way to jump out the end of a string, comment, or |
| 1122 |
;; note like there is for the beginning. Move to tmp |
| 1123 |
;; before we continue. |
| 1124 |
(or (ascend-point-in-string-p) |
| 1125 |
(ascend-point-in-comment-p) |
| 1126 |
(ascend-point-in-note-p)) |
| 1127 |
(goto-char tmp)) |
| 1128 |
(;; we're at block-end position; decrease the level and |
| 1129 |
;; move to the end of the line, otherwise, if sitting |
| 1130 |
;; on ``END FOR;'' we will match the FOR as a |
| 1131 |
;; block-start |
| 1132 |
(looking-at ascend-block-end-regexp) |
| 1133 |
(setq level (1- level)) |
| 1134 |
(end-of-line)) |
| 1135 |
(;; at this point we know we are on a block-start. go |
| 1136 |
;; backward one token to make sure we are not sitting |
| 1137 |
;; on the "FOR" of an "END FOR" statement. If we are |
| 1138 |
;; on END FOR, decrease the level and move to the end |
| 1139 |
;; of the line |
| 1140 |
(progn (skip-syntax-backward " ") |
| 1141 |
(skip-syntax-backward "w_") |
| 1142 |
(looking-at ascend-block-end-regexp)) |
| 1143 |
(setq level (1- level)) |
| 1144 |
(end-of-line)) |
| 1145 |
(;; we know we are on a block start that is really a |
| 1146 |
;; block start. increase the level and move to the end |
| 1147 |
;; of our orignial match |
| 1148 |
t |
| 1149 |
(setq level (1+ level)) |
| 1150 |
(goto-char tmp)))) |
| 1151 |
(if (zerop level) |
| 1152 |
(point) |
| 1153 |
nil)))) |
| 1154 |
|
| 1155 |
|
| 1156 |
(defun ascend-get-matching-block-start () |
| 1157 |
"Return the position of the start of the block which the |
| 1158 |
current token ends. Signals an error if the matching block |
| 1159 |
start is not found. |
| 1160 |
|
| 1161 |
This function expects to point to be sitting on an entry in the |
| 1162 |
ascend-block-end-regexp-list. To find the beginning of the current |
| 1163 |
block from inside the block, call ascend-pos-beginning-of-block." |
| 1164 |
(let (;; match case inside of this function |
| 1165 |
(case-fold-search nil) |
| 1166 |
;; end-line is the line number where we are; it is used |
| 1167 |
;; when signaling an error. |
| 1168 |
(end-line (1+ (count-lines (point-min) (point)))) |
| 1169 |
;; level is used to keep track of the begin-blocks and end-blocks; |
| 1170 |
;; it is initially 1, indicating we are inside a block |
| 1171 |
(level 1) |
| 1172 |
;; blocktype holds the token after the END |
| 1173 |
blocktype |
| 1174 |
;; tmp is used for random values, such as the start of a comment, |
| 1175 |
;; the current value of point, etc. |
| 1176 |
tmp) |
| 1177 |
(save-excursion |
| 1178 |
;; Signal an error if we are not where we expect to be |
| 1179 |
(if (null (looking-at ascend-block-end-regexp)) |
| 1180 |
(error "%s" "Not on an block-end-line")) |
| 1181 |
;; Skip over the block-end-regexp and any whitespace |
| 1182 |
(goto-char (match-end 0)) |
| 1183 |
(skip-syntax-forward " ") |
| 1184 |
;; The next token is the type of the block |
| 1185 |
(setq blocktype (buffer-substring (point) (progn |
| 1186 |
(skip-syntax-forward "w_") |
| 1187 |
(point)))) |
| 1188 |
;; Signal an error if no token follows END |
| 1189 |
(if (string= blocktype "") |
| 1190 |
(error "%s" "Missing token after END")) |
| 1191 |
;; See if we recognize the token after "END" |
| 1192 |
(if (string-match (concat "\\`" ascend-block-start-regexp "\\'") |
| 1193 |
blocktype) |
| 1194 |
(progn |
| 1195 |
;; The word after "END"---blocktype---is a block-start |
| 1196 |
;; keyword, so search backward for blocktype, adding a level |
| 1197 |
;; if it is preceeded by "END" and subtracting a level if not. |
| 1198 |
;; Return when the level is zero. |
| 1199 |
(beginning-of-line) |
| 1200 |
(while (and (> level 0) |
| 1201 |
(re-search-backward (concat "\\b" blocktype "\\b") |
| 1202 |
nil t)) |
| 1203 |
(if (setq tmp (or (ascend-point-in-string-p) |
| 1204 |
(ascend-point-in-comment-p) |
| 1205 |
(ascend-point-in-note-p))) |
| 1206 |
(goto-char tmp) |
| 1207 |
;(forward-char 1) |
| 1208 |
(setq tmp (point)) |
| 1209 |
(skip-syntax-backward " ") |
| 1210 |
(skip-syntax-backward "w_") |
| 1211 |
(if (looking-at ascend-block-end-regexp) |
| 1212 |
(setq level (1+ level)) |
| 1213 |
(setq level (1- level))))) |
| 1214 |
(if (zerop level) |
| 1215 |
tmp |
| 1216 |
(error "%s%s%s%d" "Cannot find beginning of " blocktype |
| 1217 |
" block that ends on line " end-line))) |
| 1218 |
(progn |
| 1219 |
;; We do not recognize the word after "END"---blocktype---as a |
| 1220 |
;; block-start, so look for a block-start followed by |
| 1221 |
;; blocktype. |
| 1222 |
(while (and (re-search-backward (concat ascend-block-start-regexp |
| 1223 |
"\\s-+" blocktype |
| 1224 |
"\\>") |
| 1225 |
nil t) |
| 1226 |
(setq tmp (or (ascend-point-in-string-p) |
| 1227 |
(ascend-point-in-comment-p) |
| 1228 |
(ascend-point-in-note-p)))) |
| 1229 |
(goto-char tmp)) |
| 1230 |
(if (looking-at (concat ascend-block-start-regexp "\\s-+" |
| 1231 |
blocktype "\\>")) |
| 1232 |
(point) |
| 1233 |
(error "%s%s%s%d" "Cannot find beginning of block " blocktype |
| 1234 |
" that ends on line " end-line))))))) |
| 1235 |
|
| 1236 |
|
| 1237 |
;;; |
| 1238 |
;;; Misc functions ---------------------------------------------------------- |
| 1239 |
;;; |
| 1240 |
|
| 1241 |
|
| 1242 |
(defun ascend-undo-abbrev-in-comment () |
| 1243 |
"If point is in an ascend comment, |
| 1244 |
undo the previous abbrev expansion." |
| 1245 |
(if (and (not ascend-expand-abbrevs-in-comments) |
| 1246 |
(or (ascend-point-in-string-p) |
| 1247 |
(ascend-point-in-comment-p) |
| 1248 |
(ascend-point-in-note-p))) |
| 1249 |
(unexpand-abbrev))) |
| 1250 |
|
| 1251 |
|
| 1252 |
(defun ascend-version () |
| 1253 |
"Print the version number of ASCEND mode in the minibuffer" |
| 1254 |
(interactive) |
| 1255 |
(message "ASCEND mode version %s Ident: $Id$" ascend-mode-version)) |
| 1256 |
|
| 1257 |
|
| 1258 |
(defun ascend-add-matching-end-block () |
| 1259 |
"Add the matching end" |
| 1260 |
(let ((case-fold-search nil) block indentation) |
| 1261 |
(save-excursion |
| 1262 |
(beginning-of-line) |
| 1263 |
(setq indentation (skip-chars-forward " \t")) |
| 1264 |
(if (or (not (looking-at ascend-block-start-regexp)) |
| 1265 |
(looking-at "\\<ELSE\\>")) |
| 1266 |
;; nothing to do |
| 1267 |
() |
| 1268 |
(if (looking-at "\\bUNIVERSAL\\b") |
| 1269 |
(progn |
| 1270 |
(skip-syntax-forward "w_") |
| 1271 |
(skip-syntax-forward " "))) |
| 1272 |
(if (looking-at "\\<\\(METHOD\\|ATOM\\|MODEL\\|DEFINITON\\)\\>") |
| 1273 |
(progn |
| 1274 |
(skip-syntax-forward "w_") |
| 1275 |
(skip-syntax-forward " "))) |
| 1276 |
(setq block (buffer-substring (point) (progn (skip-syntax-forward "w_") |
| 1277 |
(point)))) |
| 1278 |
(end-of-line) |
| 1279 |
(insert "\nEND " block ";") |
| 1280 |
(beginning-of-line) |
| 1281 |
(indent-to indentation))))) |
| 1282 |
|
| 1283 |
|
| 1284 |
;;; |
| 1285 |
;;; Menus ------------------------------------------------------------------- |
| 1286 |
;;; |
| 1287 |
|
| 1288 |
|
| 1289 |
(defun ascend-create-menu () |
| 1290 |
"Modify this buffer's menubar to include the ASCEND menu. |
| 1291 |
Won't create the menubar if one doesn't already exist." |
| 1292 |
(interactive) |
| 1293 |
(cond ((not ascend-menu) |
| 1294 |
;; do nothing |
| 1295 |
) |
| 1296 |
((and (string-match "XEmacs" emacs-version) current-menubar) |
| 1297 |
(set-buffer-menubar current-menubar) |
| 1298 |
(add-submenu nil (append '("ASCEND") ascend-menu))) |
| 1299 |
((and (string-match "Lucid" emacs-version) current-menubar) |
| 1300 |
(set-buffer-menubar current-menubar) |
| 1301 |
(add-menu nil "ASCEND" (copy-tree ascend-menu))) |
| 1302 |
((string-match "^[.0-9]+$" emacs-version) |
| 1303 |
(load "easymenu") |
| 1304 |
(easy-menu-define ascend ascend-mode-map "Ascend mode menu" |
| 1305 |
(cons "Ascend" ascend-menu))))) |
| 1306 |
|
| 1307 |
|
| 1308 |
;;; |
| 1309 |
;;; Done -------------------------------------------------------------------- |
| 1310 |
;;; |
| 1311 |
|
| 1312 |
(provide 'ascend-mode) |
| 1313 |
|
| 1314 |
;; ascend-mode.el ends here |