diff --git a/ChangeLog b/ChangeLog new file mode 100755 index 0000000..17e52a7 --- /dev/null +++ b/ChangeLog @@ -0,0 +1,49 @@ +04/25/01: Release of modelica-mode version 1.4.1 + +04/25/01: modelica-mode.el, br-mdc.el + - don't define read-only or intangible property for hidden annotations + - introduce constants mdc-class-keyword and mdc-class-modifier-keyword + formerly defined in br-mdc.el + - updates and fixes in mdc-indent-line, mdc-calculate-indent, + mdc-statement-start, mdc-short-class-definition, mdc-forward-begin + - add "." to word characters in mdc-mode-syntax-table + (useful for treating names and numbers as words, + as opposed to pure idents and integers) + +Former history of modelica-mode.el: + 04/24/01: + - update function mdc-forward-begin for Modelica 1.4 + (used e.g. by mdc-forward-statement and mdc-show-annotation) + 04/03/01: release of version 1.4.0 for Modelica 1.4 + - hide/show of annotations + (initial effort made by Mike Tiller) + - bug fix in mdc-within-comment + - mdc-within-string exploits result of font-lock-mode + - extended font-lock support for Modelica 1.4 + - new move commands + - bug fix in mdc-statement-start for first statement of block + 12/03/97: release of version 1.06 + - corrected mdc-indent-for-docstring + 12/02/97: release of version 1.05 + - initialize comment syntax as proposed by + David Kagedal + - extended commands for writing comments and documentation strings + - commands for moving point according to syntax + - provide menu bar using easymenu + 11/27/97: release of version 1.01 + 11/25/97: + - treatment of balanced expressions in mdc-statement-start + - make '_' belonging to syntax class word constituent + - new mdc-within-string + 11/18/97: release of version 1.0 for Modelica 1.0 + 11/18/97: + - support for "if" in conditional equations + - add "initial", "terminal", "switch" to font-lock-function-name-face + 10/30/97: first release of version 1.0b for Modelica 1.0 + +Former history of mdc-browse.el: + 04/12/01: release of version 1.1 + - synched up with OO-Browser 4.07 and Modelica 1.4 (partly) + 12/04/97: release of version 1.01 + - support for Action Key clicking on classes via mdc-to-definition + 12/03/97: release of version 1.0 diff --git a/README b/README new file mode 100755 index 0000000..418b9f4 --- /dev/null +++ b/README @@ -0,0 +1,139 @@ +This directory contains extensions for Emacs supporting Modelica. +Modelica is a unified object-oriented language for physical systems modeling +(see http://www.Modelica.org). + +Emacs lisp code +=============== + modelica-mode.el -- major mode for editing Modelica files + + mdc-browse.el -- Modelica extension for the OO-Browser + br-mdc.el (see http://sourceforge.net/projects/oo-browser) + +Installation +============ + +See the files modelica-mode.el and mdc-browse.el for installation +instuctions. + +At least put the files + modelica-mode.el + mdc-browse.el + br-mdc.el +to an Emacs lisp directory, e.g. ~/elisp + +and add the following lines to your file ~/.emacs + +;; Modelica mode +(setq load-path (cons "~/elisp" load-path)) +(autoload 'modelica-mode "modelica-mode" "Modelica Editing Mode" t) +(setq auto-mode-alist (cons '("\.mo$" . modelica-mode) auto-mode-alist)) + +;; Enable Modelica browsing +(autoload 'mdc-browse "mdc-browse" "Modelica Class Browsing" t) +(autoload 'br-mdc "br-mdc" "Modelica Class Browsing" t) + +(defvar br-env-lang-avector + '[ + ("C++/C" . "c++-") + ("Eiffel" . "eif-") + ("Info" . "info-") + ("Java" . "java-") + ("Lisp" . "clos-") + ("Modelica" . "mdc-") + ("Obj-C" . "objc-") + ("Python" . "python-") + ] + "Association vector of elements of OO-Browser languages.") + +;; Autostart OO-Browser (the installation is assumed under ~/oo-browser) +(setq load-path (append + '("~/oo-browser/" + "~/oo-browser/hypb/") + load-path)) +(load "br-start") +(global-set-key "\C-c\C-o" 'oo-browser) + + +Modelica mode for Emacs +======================= + +The aim is to provide basic support as known from many programming +languages under Emacs. This includes proper indentation, automated +closing of code blocks, movement by statements and code blocks, +support for writing comments, and syntax highlighting. + + +Tutorial introduction into OO-Browser for Modelica +================================================== + +The following steps should be helpful to start investigating +OO-Browser for Modelica without the need to read its documentation +before. Please follow the installation instructions given above first. + +Source files to browse can be placed in multiple directories, which +are grouped into library directories (for stable code to be re-used) +and system directories (for code under development). For now we will +just work with one system directory. Copy Modelica code examples there. + +Now start Emacs and open one of the files in your directory. + +-> select the menu item Create-or-Load-Env> + +It is prompted for the environment name. +-> Choose a name, e.g. hello-world. + +It is prompted for the file to store class information in. +-> confirm the default name OOBR + +Now you must choose a language to work with. +-> select Modelica + +Finally specify system and library directories +(specify the current directory "." as the only system directory) + +Hit RET to specify the code directories for the Environment +-> Return + +Top-level system-specific code dir #1 (RET to end): +-> . Return + +Top-level system-specific code dir #2 (RET to end): +-> Return + +Top-level reusable code library dir #1 (RET to end): +-> Return + +Now OO-Browser is ready to scan your Modelica files + +Build Environment 'hello-world now? (y or n) +-> y + +Build Environment in the background? (y or n) +-> n + +Now all Modelica code files in the specified directories are scanned. +Afterwards a list with all obtained classes appears. Some basic +operations are: + +- show ancestors and descendants of a class, + e.g. click on a listed class name to show its definition, + hit the "a" key to show ancestors + (hit the "x" key to return to the list of all classes + if ancestors were found) + hit the "d" key to show descendants + +- obtain information about classes + e.g. hit the "i" key to obtain the class location and documentation + +- view and edit class implementations + e.g. hit the "v" key to view the implementation of a class + hit the "e" key to edit the implementation of a class + +- view class definition by clicking the Action Key + (shift-left mouse button or shift-middle mouse button) + over a class name in any buffer showing a Modelica file + + +Have fun! + +Ruediger Franke diff --git a/br-mdc.el b/br-mdc.el new file mode 100755 index 0000000..3ae91c2 --- /dev/null +++ b/br-mdc.el @@ -0,0 +1,780 @@ +;;!emacs +;; +;; FILE: br-mdc.el +;; (derived from Bob Weiner's br-java.el) +;; SUMMARY: Support routines for Modelica inheritance browsing. +;; USAGE: GNU Emacs Lisp Library +;; KEYWORDS: continuous systems modeling, oop, tools +;; +;; AUTHOR: Ruediger Franke +;; ORG: Modelica Design Group +;; +;; ORIG-DATE: 20-Nov-97 +;; LAST-MOD: 25-Apr-01 +;; +;; Copyright (C) 1997--2001 Free Software Foundation, Inc. +;; See the file BR-COPY for license information. +;; +;; This file is intended for use with OO-Browser. +;; + +(defconst br-mdc-version "1.2.1") + +;; Synched up with OO-Browser 4.07 + +;; DESCRIPTION: +;; see file mdc-browse.el +;; +;; Note: +;; 1.) This version does not look into classes referenced in extends +;; or import clauses if the referenced classes are definid in +;; different files. +;; That is why the completion of names does not fully work! +;; The problem can be circumvented by +;; a) not using import name.* notation +;; b) explicitly stating outer classes in extends clauses, e.g. +;; package A +;; class B +;; end B; +;; end A; +;; class C +;; extends A; // o.k. +;; extends B; // B is not found inside A in a different file! +;; extends A.B; // o.k. +;; end C; +;; 2.) This version may give wrong results if a class of the same +;; relative name is defined multiple times in the same file. +;; +;; DESCRIP-END. + +;;; HISTORY: see ChangeLog + +;;; ************************************************************************ +;;; Other required Elisp libraries +;;; ************************************************************************ + +(mapcar 'require '(br-lib hasht modelica-mode)) + +;;; ************************************************************************ +;;; User visible variables +;;; ************************************************************************ + +(defvar mdc-default-classes + '("Boolean" "Integer" "Real" "String") + "*List of default Modelica class names handled by OO-Browser.") + +(defvar mdc-lib-search-dirs nil + "List of directories below which Modelica library source files are found. +Subdirectories of Library source are also searched. A Library is a stable +group of classes.") + +(defvar mdc-sys-search-dirs nil + "List of directories below which Modelica system source files are found. +Subdirectories of System source are also searched. A System class is one +that is not yet reusable and is likely to change before release.") + +(defconst mdc-narrow-view-to-class nil + "*Non-nil means narrow buffer to just the matching class definition when displayed.") + +;;; ************************************************************************ +;;; Internal functions, required by OO Browser +;;; ************************************************************************ + +(defun mdc-get-classes-from-source (filename &optional skip-tags + skip-tags-cleanup) + "Scans FILENAME and returns cons of class list with parents-class alist. +Handles multiple inheritance. Assumes file existence and readability have +already been checked. + With optional SKIP-TAGS non-nil, does not compute and store lookup tags +for member definitions. If SKIP-TAGS is nil, normally a cleanup +function is called after scanning the members. SKIP-TAGS-CLEANUP +non-nil suppresses this action." + ;; initialize hash table of class names + (mdc-create-class-names-htable) + ;; load file into a buffer and call mdc-obtain-class-info + (let ((classes nil) (parents nil) + (package (mdc-obtain-package-info filename))) + ;; get classes from file + (mdc-get-file-buffer filename) + (save-excursion + (save-restriction + (widen) + (goto-char (point-min)) + (mdc-obtain-class-info (point-max) t package nil))) + (mdc-release-file-buffer) + ;; delete hash table of class names to free stored data + (mdc-delete-class-names-htable) + ;; return classes and parents + (cons classes (delq nil parents)))) + +(defun mdc-class-definition-regexp (class &optional regexp-flag) + "Return regexp to uniquely match the definition of CLASS name. +Optional REGEXP-FLAG non-nil means CLASS has already been quoted for use in a +regular expression." + (setq class (mdc-narrow-name class)) + (concat mdc-class-name-before + (if regexp-flag + class + (regexp-quote class)) + mdc-identifier-after)) + +(defun mdc-get-parents-from-source (filename class-name) + "Scan source in FILENAME and return list of parents of CLASS-NAME. +Assume file existence has already been checked." + (cond ((null class-name) nil) + ((equal filename br-null-path) nil) + (t (car (car (br-rassoc + class-name + (cdr (mdc-get-classes-from-source filename t)))))))) + +(defun mdc-select-path (paths-htable-elt &optional feature-p) + "Select proper pathname from PATHS-HTABLE-ELT based upon value of optional FEATURE-P. +Selection is between path of class definition and path for features associated +with the class." + (let ((elt (cdr paths-htable-elt))) + (if (consp elt) + (if feature-p (cdr elt) (car elt)) + ;; Both paths are the same. + elt))) + +(defun mdc-set-case (type) + "Return string TYPE identifier for use as a class name." + type) + +(defun mdc-set-case-type (class-name) + "Return string CLASS-NAME for use as a type identifier." + class-name) + +(defun mdc-to-class-end () + "Assuming point is at start of class, move to start of line after end of class." + (interactive) + (let (class) + (looking-at mdc-class-def-regexp) + (setq class (buffer-substring + (match-beginning mdc-class-def-name-grpn) + (match-end mdc-class-def-name-grpn))) + (goto-char (match-end 0)) + (goto-char (mdc-class-end-pos class)) + (forward-line 1))) + +(defun mdc-to-comments-begin () + "Generally this function should skip back from current point past + any preceding blank lines and comments. This is not done for Modelica + as the documentation string starts behind the class name." +; (forward-comment (- (buffer-size))) +; (skip-chars-forward " \t\n\r") + (beginning-of-line)) + +(defun mdc-store-class-info (class) + "Lookup Modelica doc string for class or method/function" + (setq mdc-docstring (mdc-lookup-docstring class))) + +(defun mdc-insert-class-info () + "Use the info facility to display Modelica doc strings" + (interactive) + (insert mdc-docstring)) + +(defun mdc-to-definition (&optional other-win) + "If point is over a class name, move to its definition. + With OTHER-WIN non-nil, show it in another window." + (interactive) + (let (end class) + ;; store current name in class + (save-excursion + (re-search-forward (concat "[^" mdc-identifier-chars "]")) + (goto-char (match-beginning 0)) + (setq end (point)) + (re-search-backward (concat "[^" mdc-identifier-chars "]")) + (goto-char (match-end 0)) + (setq class (buffer-substring (point) end))) + ;; obtain normalized class name + (mdc-create-class-names-htable) + (mdc-import-classes-from-source (buffer-file-name)) + (setq class (mdc-normalize-class-name class)) + (mdc-delete-class-names-htable) + ;; check for class + (cond + ((br-check-for-class class other-win)) + (t (beep) + (message + (concat "(OO-Browser): Can't find class definition for \"" class "\".")) + nil)))) + +;;; ************************************************************************ +;;; Private functions +;;; ************************************************************************ + +(defun mdc-obtain-package-info (filename) + "Obtain and return package name recursively defined in package.mo files. + Furthermore the names of *.mo files are stored." + (let (dirname updirname package packagefilename) + (setq dirname (file-name-directory filename)) + (setq updirname (file-name-directory (directory-file-name dirname))) + (setq packagefilename + (car (directory-files dirname t "^package.mo$"))) + (if packagefilename + (progn + ;; first obtain package info from parent directory + (setq package + (mdc-obtain-package-info (directory-file-name dirname))) + ;; append local package name + (if (equal (file-relative-name filename dirname) "package.mo") + ;; nothing to do if filename is package.mo + () + ;; obtain class info from file package.mo in this directory + (mdc-get-file-buffer packagefilename) + (save-excursion + (save-restriction + (widen) + (goto-char (point-min)) + (mdc-obtain-class-info (point-max) nil package nil))) + (mdc-release-file-buffer) + ;; obtain package name from directory name + (setq package + (concat package (if package ".") + (file-relative-name + (directory-file-name dirname) updirname))) + ;; store names of subdirectories and *.mo files + ;; (i.e. additional class names defined in this package) + (let (class file (file-list (directory-files dirname t))) + (while file-list + (setq file (car file-list)) + (setq file-list (cdr file-list)) + (if (equal file filename) + ;; don't store name given as calling argument + () + ;; obtain class name + ;; first check for .mo extension + (if (equal (file-name-extension file) "mo") + (setq class + (file-name-sans-extension + (file-relative-name file dirname))) + ;; furthermore check for directory containing package.mo + (if (and (file-directory-p file) + (directory-files file t "^package.mo$")) + (setq class + (file-relative-name + (directory-file-name file) dirname)) + (setq class nil))) + ;; store class name + (if (or (not class) + (equal class ".") + (equal class "..") + (equal class "package")) + ;; don't store + () + (mdc-store-class-name + (concat package (if package ".") class))))))))) + ;; return obtained package name + package)) + +(defun mdc-obtain-class-info (obtain-end-pos &optional obtain-parents + class defined-in-file) + "Scan current buffer up to OBTAIN-END-POS for class information. +Calls itself recursively to obtain information for a local CLASS. +The point is assumed to be after a match of mdc-class-def-regexp. +Class information is stored in dynamically bounded variable +'mdc-class-names-htable', as well as in 'classes' and 'parents' +if OBTAIN-PARENTS, DEFINED-IN-FILE, and CLASS are non nil." + (let (parent (parent-list nil) parent-cons + save-point local-class (class-represent class)) + ;; store class name so that it will be known for local classes + (if (and class defined-in-file) + (progn + (if (member class mdc-default-classes) + (setq class-represent (concat "\[" class "\]"))) + (mdc-store-class-name class-represent))) + ;; skip optional docstring + ;; (this is required as mdc-within-string is limited to single lines) + (forward-comment (buffer-size)) + (while (looking-at "\"") + (forward-char 1) + (if (looking-at "\"") + (forward-char 1) + (re-search-forward "\"")) + (forward-comment (buffer-size))) + ;; treat short class definition + (if (looking-at mdc-short-definition-regexp) + (progn + (setq parent (buffer-substring + (match-beginning mdc-short-definition-grpn) + (match-end mdc-short-definition-grpn))) + ;; skip possible class_modification for parent + (goto-char (match-end 0)) + (forward-comment (buffer-size)) + (if (looking-at "(") + (progn (forward-sexp) + (setq save-point (point)))) + ;; store parent for normalization of class names + ;; (append a "." to class to avoid replacement for e.g. + ;; type MyReal = Real; MyReal r, but replace e.g. + ;; package SIunits = Modelica.SIunits; SIunits.Temp_K T, + ;; see mdc-normalize-class-name) + (setq parent (mdc-normalize-class-name parent)) + (mdc-store-class-name-as parent (concat (mdc-narrow-name class) ".")) + ;; store parent for inheritance browsing + (if (and class defined-in-file obtain-parents) + (setq parent-list (cons parent parent-list)))) + ;; else obtain parent-list from extends_clauses + (while (re-search-forward + (concat mdc-identifier-before + "\\(annotation\\|block\\|c\\(lass\\|onnector\\)\\|" + "extends\\|function\\|import\\|model\\|package\\|" + "record\\|type\\)" + mdc-identifier-after) + obtain-end-pos t) + (setq save-point (point)) + (goto-char (match-beginning 0)) + (cond + ;; treat comments and strings + ((or (mdc-within-comment) + (mdc-within-string)) + ()) + ;; skip annotations + ((looking-at "annotation") + (goto-char (match-end 0)) + (forward-comment (buffer-size)) + (forward-sexp) + (setq save-point (point))) + ;; import other model files + ;; (is not valid anymore in Modelica 1.4) + ((looking-at mdc-import-file-regexp) + (mdc-import-classes-from-source + (buffer-substring + (match-beginning mdc-import-file-grpn) + (match-end mdc-import-file-grpn)))) + ;; import class name + ((looking-at mdc-import-regexp) + (let ((name (buffer-substring + (match-beginning mdc-import-name-grpn) + (match-end mdc-import-name-grpn)))) + (goto-char (match-end 0)) + (forward-comment (buffer-size)) + (if (looking-at mdc-short-definition-regexp) + ;; statement has form "import A = B.C.D" + (let ((new-name name)) + (setq name (buffer-substring + (match-beginning mdc-short-definition-grpn) + (match-end mdc-short-definition-grpn))) + (mdc-store-class-name-as name new-name)) + ;; statement has form "import B.C.D" + (mdc-store-class-name name)))) + ;; obtain parent information + ((looking-at mdc-parent-regexp) + (setq parent (buffer-substring + (match-beginning mdc-parent-name-grpn) + (match-end mdc-parent-name-grpn))) + ;; skip possible class_modification for parent + (goto-char (match-end 0)) + (forward-comment (buffer-size)) + (if (looking-at "(") + (progn (forward-sexp) + (setq save-point (point)))) + ;; store parent for normalization of class names + (setq parent (mdc-normalize-class-name parent)) + (mdc-store-class-name parent) + ;; store parent for inheritance browsing + (if (and class defined-in-file obtain-parents) + (setq parent-list (cons parent parent-list)))) + ;; call mdc-obtain-class-info recursively for local classes + ((looking-at mdc-class-def-regexp) + (setq local-class (buffer-substring + (match-beginning mdc-class-def-name-grpn) + (match-end mdc-class-def-name-grpn))) + (goto-char (match-end 0)) + ;; backup current class names htable so that locally defined + ;; classes do not overwrite global ones for further analysis + ;; (not yet used as extends and import are not fully supported) +; (mdc-backup-class-names) + (mdc-obtain-class-info + (mdc-class-end-pos local-class) + obtain-parents + (concat class (if class ".") local-class) t) + (setq save-point (point)) +; (mdc-restore-class-names) + )) + (goto-char save-point))) + ;; store class and parent information for browsing + (if (and class defined-in-file obtain-parents) + (setq parent-cons (cons parent-list class-represent) + classes (cons class-represent classes) + parents (cons parent-cons parents))))) + +(defun mdc-create-class-names-htable () + (setq mdc-class-names-htable (hash-make 7)) + (mapcar + (function + (lambda (class) + (hash-add (concat "\[" class "\]") (mdc-narrow-name class) + mdc-class-names-htable))) + mdc-default-classes)) + +(defun mdc-delete-class-names-htable () + (setq mdc-class-names-htable nil)) + +(defun mdc-backup-class-names () + (setq mdc-class-names-htable-backup (hash-copy mdc-class-names-htable))) + +(defun mdc-restore-class-names () + (setq mdc-class-names-htable mdc-class-names-htable-backup)) + +(defun mdc-lookup-docstring (class) + "Looks up the doc string for CLASS." + (let (filename line docstring pos + (class-regexp (mdc-class-definition-regexp class))) + (setq filename (br-class-path class)) + (if (not filename) + (error (format "(mdc-lookup-docstring): Entry \"%s\" may be referenced but it is not defined in the Environment." class))) + ;; obtain class documentation string + (mdc-get-file-buffer filename) + (condition-case nil + (save-excursion + (save-restriction + (widen) + (goto-char (point-min)) + (while (progn + (re-search-forward class-regexp) + (setq docstring + (buffer-substring + (match-beginning 0) + (match-end 0))) + (or (mdc-within-comment) + (mdc-within-string)))) + (setq docstring + (concat docstring + "\n(defined in " filename ")\n\n")) + ;; append doc string + (forward-comment (buffer-size)) + (if (looking-at "\"\\([^\"]*[^\\]\\)\"") + (setq docstring + (concat docstring + (buffer-substring + (match-beginning 1) + (match-end 1))))))) + (error nil)) + (mdc-release-file-buffer) + (if docstring + docstring + (concat class " definition not found in Environment.")))) + +(defun mdc-class-end-pos (name) + "Return position after class end; +Assumes point to be after a match of mdc-class-def-regexp." + (setq name (mdc-narrow-name name)) + (save-excursion + (if (looking-at mdc-short-definition-regexp) + (while (progn + (re-search-forward ";") + (or (mdc-within-comment) + (mdc-within-string)))) + (condition-case nil + (while + (progn + (re-search-forward (concat "end[ \t\n\r]+" name "[ \t\n\r]*;")) + (or (mdc-within-comment) + (mdc-within-string)))) + (error (error (concat "Unended class \"" name "\""))))) + (point))) + +(defun mdc-narrow-name (full-name) + "Strip package names from FULL-NAME in dot notation" + (let (index (len (length full-name))) + (cond + ((and (>= len 1) + (equal (substring full-name 0 1) "\[")) + (substring full-name 1 (- len 1))) + (t + (setq index (string-match "[^.]+$" full-name)) + (substring full-name index))))) + +(defun mdc-outer-names (full-name) + "Strip innermost class name from FULL-NAME in dot notation" + (let (index (len (length full-name))) + (cond + ((and (>= len 1) + (equal (substring full-name 0 1) "\[")) + (substring full-name 1 (- len 1))) + (t + (setq index (string-match "\\.[^.]+$" full-name)) + (if index + (substring full-name 0 index) + ()))))) + +(defun mdc-inner-names (full-name) + "Strip outermost class name from FULL-NAME in dot notation" + (let (index (len (length full-name))) + (cond + ((and (>= len 1) + (equal (substring full-name 0 1) "\[")) + (substring full-name 1 (- len 1))) + (t + (string-match "^[^.]+\\." full-name) + (setq index (match-end 0)) + (if index + (substring full-name index) + ()))))) + +(defun mdc-narrow-outer-name (full-name) + "Strip sub-package names and class name from FULL-NAME in dot notation" + (let (index (len (length full-name))) + (cond + ((and (>= len 1) + (equal (substring full-name 0 1) "\[")) + (substring full-name 1 (- len 1))) + (t + (string-match "^[^.]+" full-name) + (setq index (match-end 0)) + (substring full-name 0 index))))) + +(defun mdc-store-class-name (class) + "Store class in class names htable." + (mdc-store-class-name-as class (mdc-narrow-name class))) + +(defun mdc-store-class-name-as (class name) + "Store class under name in class names htable." + (hash-add class name mdc-class-names-htable)) + +(defun mdc-normalize-class-name (name) + "Normalize class name by prepending package names that define it." + (let ((narrow-name (mdc-narrow-name name)) (full-name nil)) + (if (equal name narrow-name) + ;; a narrow class name may find directly in htable + (setq full-name + (hash-lookup name mdc-class-names-htable)) + ;; otherwise htable may contain outer names to prepend + (let ((narrow-outer-name (mdc-narrow-outer-name name)) + outer-names) + (setq outer-names + (hash-lookup narrow-outer-name mdc-class-names-htable)) + (if outer-names + (setq full-name + (concat outer-names "." (mdc-inner-names name)))) + ;; additionally check narrow-outer-name with appended "." + ;; as stored for short class definitions (see mdc-obtain-class-info) + (setq outer-names (hash-lookup (concat narrow-outer-name ".") + mdc-class-names-htable)) + (if outer-names + (setq full-name + (concat outer-names "." (mdc-inner-names name)))))) + ;; return either found full name or call argument + (or full-name name))) + +(defun mdc-import-classes-from-source (file) + "Scan FILE and store defined class names in mdc-class-names-htable. + FILE is searched for in br-sys-search-dirs and br-lib-search-dirs." + ;; load file into a buffer and call mdc-obtain-class-info + (let (filename (buffer-bak (current-buffer))) + (setq filename + (if (file-name-absolute-p file) + file + (mdc-complete-filename + file + (append br-sys-search-dirs br-lib-search-dirs)))) + (if (not filename) + (error (concat "(mdc-import): file not found, " file))) + (let ((package (mdc-obtain-package-info filename))) + (mdc-get-file-buffer filename) + (save-excursion + (save-restriction + (widen) + (goto-char (point-min)) + (mdc-obtain-class-info (point-max) nil package nil))) + (mdc-release-file-buffer)) + (set-buffer buffer-bak))) + +(defun mdc-complete-filename (file search-dirs) + "Search FILE in SEARCH-DIRS and return first occurence with full pathname; + return nil if file not found." + (let ((filename nil) hits) + (setq hits + (mapcar + (function + (lambda (dir) + (if (or (null dir) (equal dir "") + (progn (setq dir (file-name-as-directory dir)) + (br-skip-dir-p dir))) + nil + (if (and (file-directory-p dir) + (file-readable-p dir)) + (directory-files dir t (concat "^" file "$")))))) + search-dirs)) + (while (and hits (not filename)) + (setq + filename (caar hits) + hits (cdr hits))) + filename)) + +;;; ************************************************************************ +;;; Private section for getting files into buffers +;;; ************************************************************************ + +(defconst mdc-tmp-buffer-name "*mdc-tmp<%d>*" + "Name of temporary buffer used for parsing source files.") + +(defun mdc-get-file-buffer (filename) + "Return FILENAMEs buffer, create a new temporary buffer if needed. + Make returned buffer current." + (let (buffer + (mdc-view-file-function 'mdc-insert-file-contents)) + (setq buffer (get-file-buffer filename)) + (if buffer + ;; make existing buffer current + (set-buffer buffer) + ;; create temporary buffer + (setq buffer (funcall mdc-view-file-function filename))) + buffer)) + +(defun mdc-release-file-buffer (&optional buffer) + "Kill BUFFER (default: current buffer) if its name is a + mdc-tmp-buffer-name." + (let ((buffer-name (buffer-name buffer)) + (end-pos (string-match "<" mdc-tmp-buffer-name))) + (if (and + (> (length buffer-name) end-pos) + (equal (substring buffer-name 0 end-pos) + (substring mdc-tmp-buffer-name 0 end-pos))) + (progn (set-buffer-modified-p nil) + (kill-buffer buffer))))) + +(defun mdc-insert-file-contents (filename) + "Insert FILENAME contents into a temporary buffer and select buffer. +Does not run any find-file or mode specific hooks. Marks buffer read-only to +prevent any accidental editing. + +Set `mdc-view-file-function' to this function when parsing OO-Browser source +files for fast loading of many files." + (let ((number 1) buf) + ;; find an unused temporary buffer + (while (get-buffer (format mdc-tmp-buffer-name number)) + (setq number (1+ number))) + ;; create a new temporary buffer + (setq buf (get-buffer-create (format mdc-tmp-buffer-name number))) + (switch-to-buffer buf) + ;; Don't bother saving anything for this temporary buffer + (buffer-disable-undo buf) + (setq buffer-auto-save-file-name nil + buffer-read-only nil) + (erase-buffer) + (insert-file-contents filename t) + (br-scan-mode) + (setq buffer-read-only t) + buf)) + +;;; ************************************************************************ +;;; Internal variables +;;; ************************************************************************ + +(defvar mdc-docstring "" + "Documentation string for Modelica.") + +(defconst mdc-identifier-chars "_.a-zA-Z0-9" + "String of chars and char ranges that may be used within a Modelica identifier, including '.' for concatenation of names.") + +(defconst mdc-identifier-before + "\\(\\<\\)" + "Chars before a Modelica identifier.") + +(defconst mdc-identifier-after + "\\(\\>\\)" + "Chars after a Modelica identifier.") + +(defconst mdc-identifier (concat "\\([_a-zA-Z][" mdc-identifier-chars "]*\\)") + "Regular expression matching a Modelica identifier.") + +(defconst mdc-class-name-before + (concat mdc-identifier-before + "\\(" mdc-class-modifier-keyword "\\)*" mdc-class-keyword) + "Regexp preceding the class name in a class definition.") + +(defconst mdc-class-def-regexp + (concat mdc-class-name-before mdc-identifier) + "Regular expression used to match to class definitions in source text. +Class name identifier is grouping 'mdc-class-def-name-grpn'.") + +(defconst mdc-class-def-name-grpn 7) + +(defconst mdc-lang-prefix "mdc-" + "Prefix string that starts \"br-mdc.el\" symbol names.") + +(defconst mdc-parent-regexp + (concat mdc-identifier-before "extends[ \t\n\r]+" + mdc-identifier) + "Parent identifier is group 'mdc-parent-name-grpn'.") + +(defconst mdc-parent-name-grpn 2) + +(defconst mdc-short-definition-regexp + (concat "\\([ \t\n\r]*=[ \t\n\r]*\\)" + mdc-identifier) + "Short definition identifier is group 'mdc-short-definition-grpn'.") + +(defconst mdc-short-definition-grpn 2) + +(defconst mdc-import-regexp + (concat mdc-identifier-before "import[ \t\n\r]+" + mdc-identifier) + "Imported class identifier is 'mdc-import-name-grpn'.") + +(defconst mdc-import-name-grpn 2) + +(defconst mdc-import-file-regexp + (concat mdc-identifier-before "import[ \t\n\r]+\"\\([^\"]+\\)\"") + "Import file name is group 'mdc-import-file-grpn'.") + +(defconst mdc-import-file-grpn 2) + +(defvar mdc-class-names-htable nil + "Hash table of full class names using narrow class names as key.") + +(defvar mdc-class-names-htable-backup nil + "Backup for hash table of full class names using narrow class names as key.") + +(defvar mdc-class-names-files nil + "Set of file names whose contents is known in mdc-class-names-htable") + +(defconst mdc-src-file-regexp "[^.]\\.\\(mo\\)$" + "Regular expression matching a unique part of Modelica source file name and no others.") + +(defvar mdc-children-htable nil + "Htable whose elements are of the form: (LIST-OF-CHILD-CLASSES . CLASS-NAME). +Used to traverse Modelica inheritance graph. 'br-build-children-htable' builds +this list.") +(defvar mdc-parents-htable nil + "Htable whose elements are of the form: (LIST-OF-PARENT-CLASSES . CLASS-NAME). +Used to traverse Modelica inheritance graph. 'br-build-parents-htable' builds +this list.") +(defvar mdc-paths-htable nil + "Htable whose elements are of the form: (LIST-OF-CLASS-NAMES . FILE-PATH). +FILE-PATH gives the location of classes found in LIST-OF-CLASS-NAMES. +'br-build-paths-htable' builds this list.") + + +(defvar mdc-lib-parents-htable nil + "Htable whose elements are of the form: (LIST-OF-PARENT-CLASSES . CLASS-NAME). +Only classes from stable software libraries are used to build the list.") +(defvar mdc-lib-paths-htable nil + "Htable whose elements are of the form: (LIST-OF-CLASS-NAMES . FILE-PATH). +FILE-PATH gives the location of classes found in LIST-OF-CLASS-NAMES. +Only classes from stable software libraries are used to build the list.") + +(defvar mdc-sys-parents-htable nil + "Htable whose elements are of the form: (LIST-OF-PARENT-CLASSES . CLASS-NAME). +Only classes from systems that are likely to change are used to build the +list.") +(defvar mdc-sys-paths-htable nil + "Alist whose elements are of the form: (LIST-OF-CLASS-NAMES . FILE-PATH). +FILE-PATH gives the location of classes found in LIST-OF-CLASS-NAMES. +Only classes from systems that are likely to change are used to build the +list.") + +(defvar mdc-lib-prev-search-dirs nil + "Used to check if 'mdc-lib-classes-htable' must be regenerated.") +(defvar mdc-sys-prev-search-dirs nil + "Used to check if 'mdc-sys-classes-htable' must be regenerated.") + +(defvar mdc-env-spec nil + "Non-nil value means Environment specification has been given but not yet built. +Nil means current Environment has been built, though it may still require +updating.") + +(provide 'br-mdc) diff --git a/mdc-browse.el b/mdc-browse.el new file mode 100755 index 0000000..2327fcb --- /dev/null +++ b/mdc-browse.el @@ -0,0 +1,165 @@ +;;!emacs +;; +;; FILE: mdc-browse.el +;; (derived from Bob Weiner's java-brows.el) +;; SUMMARY: Modelica source code browser. +;; USAGE: GNU Emacs Lisp Library +;; KEYWORDS: continuous systems modeling, oop, tools +;; +;; AUTHOR: Ruediger Franke +;; ORG: Modelica Design Group +;; +;; ORIG-DATE: 20-Nov-97 +;; LAST-MOD: 03-Apr-01 +;; +;; Copyright (C) 1997--2001 Free Software Foundation, Inc. +;; See the file BR-COPY for license information. +;; +;; This file is intended for use with OO-Browser. + +(defconst mdc-browse-version "1.2.0") + +;; Synched up with OO-Browser 4.07 + +;; +;; DESCRIPTION: +;; +;; Support for inheritance browsing for Modelica. +;; +;; DESCRIP-END. + +;; Installation: +;; (1) Put the files +;; mdc-browse.el +;; br-mdc.el +;; to an Emacs lisp directory, e.g. ~/elisp +;; +;; (2) Add the following lines to your ~/.emacs file +;; in order to make the package known to Emacs and to OO-Browser. +;; +;; ;; Modelica browsing +;; (autoload 'mdc-browse "mdc-browse" "Modelica Class Browsing" t) +;; (autoload 'br-mdc "br-mdc" "Modelica Class Browsing" t) +;; +;; (defvar br-env-lang-avector +;; '[ +;; ("C++/C" . "c++-") +;; ("Eiffel" . "eif-") +;; ("Info" . "info-") +;; ("Java" . "java-") +;; ("Lisp" . "clos-") +;; ("Modelica" . "mdc-") +;; ("Obj-C" . "objc-") +;; ("Python" . "python-") +;; ] +;; "Association vector of elements of OO-Browser languages.") +;; +;; (3) Start OO-Browser as described in its documentation +;; +;; (4) Optionally byte-compile the lisp code +;; +;; (5) Please send comments and suggestions to +;; Ruediger Franke + +;;; HISTORY: see ChangeLog + +;; ************************************************************************ +;; Other required Elisp libraries +;; ************************************************************************ + +(mapcar 'require '(br-mdc br-start br)) + +;;; ************************************************************************ +;;; Public functions +;;; ************************************************************************ + +;;;###autoload +(defun modelica-browse (&optional env-file no-ui) + "call mdc-browse" + (interactive "P") + (mdc-browse env-file no-ui)) + +;;;###autoload +(defun mdc-browse (&optional env-file no-ui) + "Invoke the Modelica OO-Browser. +This allows browsing through Modelica library and system class hierarchies. +With an optional non-nil prefix argument ENV-FILE, prompt for Environment +file to use. Alternatively, a string value of ENV-FILE is used as the +Environment file name. See also the file \"br-help\"." + (interactive "P") + + ;; widen class list windows to allow for long class names + ;; (do this here as it is defined using defconst in br.el) + (setq br-min-width-window 35) + + (let ((same-lang (equal br-lang-prefix mdc-lang-prefix)) + (load-succeeded t) + same-env) + (if same-lang + nil + ;; Save other language Environment in memory + (if br-lang-prefix (br-env-copy nil)) + (setq br-lang-prefix mdc-lang-prefix + *br-save-wconfig* nil)) + (setq same-env (or (equal mdc-env-file env-file) + (and (null env-file) + (or mdc-lib-search-dirs mdc-sys-search-dirs)))) + (cond + ;; Continue browsing an Environment + ((and same-env same-lang)) + ((and same-env (not same-lang)) + (mdc-browse-setup env-file) (br-env-copy t)) + ;; + ;; Create default Environment file specification if needed and none + ;; exists. + ;; + (t (or env-file (file-exists-p mdc-env-file) + (br-env-create mdc-env-file mdc-lang-prefix)) + (or env-file (setq env-file mdc-env-file)) + ;; + ;; Start browsing a new Environment. + ;; + (mdc-browse-setup env-file) + (setq load-succeeded (br-env-init env-file same-lang nil)) + (if load-succeeded + (setq *br-save-wconfig* nil + mdc-env-file load-succeeded + mdc-sys-search-dirs br-sys-search-dirs + mdc-lib-search-dirs br-lib-search-dirs)))) + (cond (load-succeeded + (if no-ui + nil + (br-browse) + (or (and same-lang same-env) (br-refresh)))) + (no-ui nil) + (t (message "(mdc-browse): You must build the Environment to browse it."))))) + +;; Don't filter Environment classes when listed. +(defun mdc-class-list-filter (class-list top-only-flag) + "Return class-list." + class-list) + +(defun mdc-mode-setup () + "Load best available Modelica major mode and set 'br-lang-mode' to the function that invokes it." + (fset 'br-lang-mode + (cond ((fboundp 'modelica-mode) + 'modelica-mode) + ((load "modelica-mode" 'missing-ok 'nomessage) + 'modelica-mode) + (t (error + "(mdc-mode-setup): Can't load major mode for Modelica code."))))) + +;;; ************************************************************************ +;;; Internal functions +;;; ************************************************************************ + +(defun mdc-browse-setup (env-file) + "Setup language dependend functions and constants for OO-Browser." + (br-setup-functions) + ;; info facility + (fset 'br-store-class-info 'mdc-store-class-info) + (fset 'br-class-info 'br-entry-info) + (mdc-mode-setup) + (br-setup-constants env-file)) + +(provide 'mdc-browse) diff --git a/modelica-mode.el b/modelica-mode.el new file mode 100755 index 0000000..9a87d32 --- /dev/null +++ b/modelica-mode.el @@ -0,0 +1,1154 @@ +;;; modelica-mode.el --- major mode for editing Modelica files + +;; Copyright (C) 1997--2001 Ruediger Franke +;; Copyright (C) 1997--2001 Free Software Foundation, Inc. + +;; Keywords: languages, continuous system modeling +;; Author: Ruediger Franke + +;; This code has been written for use with Emacs and shares its licensing. + +;; GNU Emacs is free software; you can redistribute it and/or modify it +;; under the terms of the GNU General Public License as published by +;; the Free Software Foundation; either version 2, or (at your option) +;; any later version. + +;; GNU Emacs is distributed in the hope that it will be useful, but +;; WITHOUT ANY WARRANTY; without even the implied warranty of +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +;; General Public License for more details. + +;; You should have received a copy of the GNU General Public License +;; along with GNU Emacs; see the file COPYING. If not, write to the Free +;; Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA +;; 02111-1307, USA. + +(defconst modelica-mode-version "1.4.1") + +;;; Synched up with: GNU Emacs 20.7, XEmacs 21.1. + +;;; Commentary: + +;; This package provides a fundamental Modelica mode. +;; It covers: +;; - show / hide of annotations +;; C-c C-s show annotation of current statement +;; C-c C-h hide annotation of current statement +;; M-s show all annotations +;; M-h hide all annotations +;; +;; - indentation of lines, e.g. +;; TAB indent current line +;; M-x C-\ indent current region +;; C-j indent current line, create a new line, indent it +;; (like TAB ENTER TAB) +;; +;; - hide/show of annotations +;; C-h hide annotations +;; C-s show annotations +;; +;; - automatic insertion of end statements +;; C-c C-e search backwards for the last unended begin of a code block, +;; insert the according end-statement +;; +;; - move commands which know about statements and statement blocks +;; M-f move to next beginning of a statement +;; M-b move to previous beginning of a statement +;; M-n move to next beginning of a statement block +;; M-p move to previous beginning of a statement block +;; M-a move to beginning of current statement block +;; M-e move to end of current statement block +;; +;; - commands for writing comments treat documentation strings as well +;; M-; insert a comment for current statement (standard Emacs) +;; M-" insert a documentation string for current statement +;; M-j continue comment or documentation string on next line +;; +;; - syntax highlighting using font-lock-mode +;; +;; Current limitations: +;; - conditional expessions are only supported on right hand sides of +;; equations; otherwise simple expressions are assumed +;; +;; Installation: +;; (1) Put the file +;; modelica-mode.el +;; to an Emacs lisp directory, e.g. ~/elisp +;; +;; (2) Add the following lines to your ~/.emacs file +;; +;; (setq load-path (cons "~/elisp" load-path)) +;; (autoload 'modelica-mode "modelica-mode" "Modelica Editing Mode" t) +;; (setq auto-mode-alist (cons '("\.mo$" . modelica-mode) auto-mode-alist)) +;; +;; (3) Activate the mode by loading a file with the extension ".mo" +;; or by invoking +;; M-x modelica-mode +;; +;; (4) Optionally byte-compile the lisp code +;; +;; (5) Please send comments and suggestions to +;; Ruediger Franke + +;;; History +;; see ChangeLog + +;;; constants + +(defconst mdc-class-modifier-keyword + "\\(encapsulated\\|final\\|inner\\|outer\\|partial\\|re\\(declare\\|placeable\\)\\)[ \t\n\r]+" + "*Keyword regexp optionally found before a class keyword.") + +(defconst mdc-class-keyword + "\\(block\\|c\\(lass\\|onnector\\)\\|function\\|model\\|package\\|record\\|type\\)[ \t\n\r]+" + "*Keyword regexp preceding a Modelica class declaration or definition.") + +;;; Interface to font-lock + +(defvar mdc-font-lock-keywords nil + "Keywords to highlight for Modelica. See variable `font-lock-keywords'.") + +(if mdc-font-lock-keywords + () + (setq mdc-font-lock-keywords + (list + (list (concat "\\<" + "\\(do\\|" + "\\(end[ \t\n]+\\(if\\|for\\|wh\\(en\\|ile\\)\\)\\)\\|" + ; (regexp-opt + ; '("import" "within" "extends" + ; "for" "while" "in" "loop" "when" + ; "if" "then" "else" "elseif" "elsewhen" + ; "and" "not" "or")) + "and\\|e\\(lse\\(if\\|when\\)?\\|xtends\\)\\|for\\|" + "i\\(mport\\|[fn]\\)\\|loop\\|not\\|or\\|then\\|" + "w\\(h\\(en\\|ile\\)\\|ithin\\)" + "\\)\\>") + 0 'font-lock-keyword-face) + (list (concat "\\<" + ;(regexp-opt + ; '("algorithm" "equation" "public" "protected") t) + "\\(algorithm\\|equation\\|p\\(rotected\\|ublic\\)\\)" + "\\>") + 0 'font-lock-keyword-face) + (list (concat "\\<" + ;(regexp-opt + ; '("redeclare" "final" "partial" "replaceable" + ; "inner" "outer" "encapsulated" + ; "discrete" "parameter" "constant" + ; "flow" "input" "output" "external" + ; "block" "class" "connector" "function" "model" + ; "package" "record" "type" + ; "end") t) + "\\(block\\|c\\(lass\\|on\\(nector\\|stant\\)\\)\\|" + "discrete\\|e\\(n\\(capsulated\\|d\\)\\|xternal\\)\\|" + "f\\(inal\\|low\\|unction\\)\\|in\\(ner\\|put\\)\\|" + "model\\|out\\(er\\|put\\)\\|pa\\(ckage\\|r\\(ameter\\|" + "tial\\(\\)?\\)\\)\\|re\\(cord\\|declare\\|" + "placeable\\)\\|type\\)" + "\\>") + 0 'font-lock-type-face) + (list (concat "\\<" + ;(regexp-opt + ; '("der" "analysisType" "initial" "terminal" + ; "noEvent" "samle" "pre" "edge" "change" + ; "reinit" "abs" "sign" "sqrt" "div" "mod" + ; "rem" "ceil" "floor" "integer" "delay" + ; "cardinality" + ; "promote" "ndims" "size" "scalar" "vector" "matrix" + ; "transpose" "outerProduct" "identity" "diagonal" + ; "zeros" "ones" "fill" "linspace" "min" "max" "sum" + ; "product" "symmetric" "cross" "skew" + ;) t) + "\\(a\\(bs\\|nalysisType\\)\\|c\\(ardinality\\|eil\\|" + "hange\\|ross\\)\\|d\\(e\\(lay\\|r\\)\\|i\\(agonal\\|" + "v\\)\\)\\|edge\\|f\\(ill\\|loor\\)\\|i\\(dentity\\|" + "n\\(itial\\|teger\\)\\)\\|linspace\\|m\\(a\\(trix\\|" + "x\\)\\|in\\|od\\)\\|n\\(dims\\|oEvent\\)\\|o\\(nes\\|" + "uterProduct\\)\\|pr\\(e\\|o\\(duct\\|mote\\)\\)\\|" + "re\\(init\\|m\\)\\|s\\(amle\\|calar\\|i\\(gn\\|" + "ze\\)\\|kew\\|qrt\\|um\\|ymmetric\\)\\|t\\(erminal\\|" + "ranspose\\)\\|vector\\|zeros\\)" + "\\>") + 0 'font-lock-function-name-face) + (list (concat "\\<" + ;(regexp-opt + ; '("assert" "terminate") t) + "\\(assert\\|terminate\\)" + "\\>") + 0 'font-lock-warning-face) + (list (concat "\\<" + ;(regexp-opt + ; '("annotation" "connect") t) + "\\(annotation\\|connect\\)" + "\\>") + 0 (if (string-match "XEmacs" (emacs-version)) + ;; XEmacs 21.1 still uses old font-lock version + (identity 'font-lock-preprocessor-face) + (identity 'font-lock-builtin-face))) + (list (concat "\\<" + ;(regexp-opt + ; '("false" "true") t) + "\\(false\\|true\\)" + "\\>") + 0 (if (string-match "XEmacs" (emacs-version)) + ;; XEmacs 21.1 still uses old font-lock version + (identity 'font-lock-reference-face) + (identity 'font-lock-constant-face))) + (list (concat "\\<" + ;(regexp-opt + ; '("time") t) + "\\(time\\)" + "\\>") + 0 'font-lock-variable-name-face)))) + +;;; The mode + +(defvar mdc-basic-offset 2 + "*basic offset for indentation in Modelica Mode") + +(defvar mdc-comment-offset 3 + "*offset for indentation in comments in Modelica Mode") + +(defvar mdc-statement-offset 2 + "*offset for indentation in statements in Modelica Mode") + +(defvar mdc-mode-syntax-table nil + "Syntax table used while in Modelica mode.") + +(defvar mdc-mode-abbrev-table nil + "Abbrev table used while in Modelica mode.") +(define-abbrev-table 'mdc-mode-abbrev-table ()) + +(if mdc-mode-syntax-table + () ; Do not change the table if it is already set up. + (setq mdc-mode-syntax-table (make-syntax-table)) + + (modify-syntax-entry ?_ "w" mdc-mode-syntax-table) + (modify-syntax-entry ?. "w" mdc-mode-syntax-table) + (if (string-match "XEmacs" (emacs-version)) + (modify-syntax-entry ?/ ". 1456" mdc-mode-syntax-table) + (modify-syntax-entry ?/ ". 124b" mdc-mode-syntax-table)) + + (modify-syntax-entry ?* ". 23" mdc-mode-syntax-table) + (modify-syntax-entry ?\n "> b" mdc-mode-syntax-table)) + +(defvar mdc-mode-map nil + "Keymap for Modelica mode.") + +(if mdc-mode-map + () + (setq mdc-mode-map (make-sparse-keymap)) + (define-key mdc-mode-map "\C-j" 'mdc-newline-and-indent) + (define-key mdc-mode-map "\C-c\C-e" 'mdc-insert-end) + (define-key mdc-mode-map "\C-c\C-s" 'mdc-show-annotation) + (define-key mdc-mode-map "\C-c\C-h" 'mdc-hide-annotation) + (define-key mdc-mode-map "\es" 'mdc-show-all-annotations) + (define-key mdc-mode-map "\eh" 'mdc-hide-all-annotations) + (define-key mdc-mode-map "\C-c\C-c" 'comment-region) + (define-key mdc-mode-map "\e\"" 'mdc-indent-for-docstring) + (define-key mdc-mode-map "\e;" 'mdc-indent-for-comment) + (define-key mdc-mode-map "\ej" 'mdc-indent-new-comment-line) + (define-key mdc-mode-map "\ef" 'mdc-forward-statement) + (define-key mdc-mode-map "\eb" 'mdc-backward-statement) + (define-key mdc-mode-map "\en" 'mdc-forward-block) + (define-key mdc-mode-map "\ep" 'mdc-backward-block) + (define-key mdc-mode-map "\ea" 'mdc-to-block-begin) + (define-key mdc-mode-map "\ee" 'mdc-to-block-end)) + +(defvar mdc-mode-menu + '("Modelica" + ("Move to" + [" - next statement" mdc-forward-statement t] + [" - previous statement" mdc-backward-statement t] + [" - start of code block" mdc-to-block-begin t] + [" - end of code block" mdc-to-block-end t] + ) + [" - next code block" mdc-forward-block t] + [" - previous code block" mdc-backward-block t] + "-" + ("Annotation" + [" - show all" mdc-show-all-annotations t] + [" - hide all" mdc-hide-all-annotations t] + ) + [" - show current" mdc-show-annotation t] + [" - hide current" mdc-hide-annotation + :keys "C-c C-h" :active t] + "-" + ("Indent" + [" - for comment" mdc-indent-for-comment t] + [" - for docstring" mdc-indent-for-docstring t] + ["Newline and indent" mdc-newline-and-indent + :keys "C-j" :active t] + ["New comment line" mdc-indent-new-comment-line t] + ) + [" - line" indent-for-tab-command t] + [" - region" indent-region (mark)] + "-" + ["Comment out region" comment-region (mark)] + ["Uncomment region" (comment-region (point) (mark) '(4)) + :keys "C-u C-c C-c" :active (mark)] + "-" + ["End code block" mdc-insert-end t] + ) + "Menu for Modelica mode.") + +;; define Modelica menu if easymenu is available +(if (condition-case nil + (require 'easymenu) + (error nil)) + (easy-menu-define mdc-mode-menu-symbol + mdc-mode-map + "Menu for Modelica mode" + mdc-mode-menu)) + +;;;###autoload +(defun modelica-mode () + "Major mode for editing Modelica files." + (interactive) + (kill-all-local-variables) + + (setq major-mode 'modelica-mode) + (setq mode-name "Modelica") + + (use-local-map mdc-mode-map) + (set-syntax-table mdc-mode-syntax-table) + (setq local-abbrev-table mdc-mode-abbrev-table) + + (make-local-variable 'indent-line-function) + (setq indent-line-function 'mdc-indent-line) + + ;; comment syntax + (make-local-variable 'comment-column) + (make-local-variable 'comment-start) + (make-local-variable 'comment-start-skip) + (make-local-variable 'comment-end) + (make-local-variable 'comment-multi-line) + (setq comment-column 32 + comment-start "// " + comment-start-skip "/\\*+ *\\|// *" + comment-end "" + comment-multi-line nil) + + ;; settings for font-lock-mode + (make-local-variable 'font-lock-keywords) + (setq font-lock-keywords mdc-font-lock-keywords) + ;; font-lock-mode for newer GNU Emacs versions + (make-local-variable 'font-lock-defaults) + (setq font-lock-defaults '(mdc-font-lock-keywords nil nil)) + + ;; hide/show annotations + (make-local-variable 'line-move-ignore-invisible) + (setq line-move-ignore-invisible t) + (if (functionp 'add-to-invisibility-spec) + (add-to-invisibility-spec '(mdc-annotation . t)) + ;; XEmacs 21.1 does not know function add-to-invisibility-spec + (make-local-variable 'buffer-invisibility-spec) + (setq buffer-invisibility-spec '((mdc-annotation . t)))) + (mdc-hide-all-annotations) + + ;; add menu + (if mdc-mode-menu-symbol + (easy-menu-add mdc-mode-menu-symbol)) + + (run-hooks 'modelica-mode-hook)) + +(defun mdc-indent-for-comment () + "Indent this line's comment to comment-column, + or insert an empty comment." + (interactive) + (indent-for-comment) + (mdc-indent-line)) + +(defun mdc-indent-for-docstring () + "Indent this statement's documentation string to comment-column, + or insert an empty documentation string." + (interactive) + (let ((deleted "") save-point) + ;; move behind current statement + (skip-chars-forward " \t") + (condition-case nil + (progn + (mdc-forward-statement) + (forward-comment (- (point-max)))) + (error + (progn + (end-of-line) + (skip-chars-backward " \t")))) + ;; remove ending ";", if any, and store it in "deleted" + (if (or (looking-at "[ \t\n]") + (eobp)) + (forward-char -1)) + (if (looking-at ";") + (progn + (delete-char 1) + (setq deleted ";")) + (forward-char 1)) + ;; move backwards to last non-blank + (skip-chars-backward " \t") + (if (or (looking-at "[ \t\n]") + (eobp)) + (forward-char -1)) + (if (looking-at "\"") + ;; indent docstring + (progn + (forward-char 1) + (insert-string deleted) + (forward-char (- (1+ (length deleted)))) + (mdc-within-string t) + (while (mdc-behind-string t)) + (setq save-point (point)) + (skip-chars-backward " \t") + (delete-region (point) save-point) + (indent-to (max comment-column (1+ (current-column)))) + (forward-char 1)) + ;; insert new docstring + (forward-char 1) + (indent-to (max comment-column (1+ (current-column)))) + (insert-string (concat "\"\"" deleted)) + (forward-char (- (1+ (length deleted)))))) + (mdc-indent-line)) + +(defun mdc-indent-new-comment-line () + "indent-new-comment-line for Modelica mode. The function additionally + considers documentation strings" + (interactive) + (mdc-indent-line) + (let (starter) + (cond + ;; treat documentation string + ((mdc-within-string) + (insert "\"\n\"")) + ;; adapt comment-multi-line and + ;; call default indent-new-comment-line + (t + (setq starter (mdc-within-comment)) + (if (equal starter "/*") + (setq comment-multi-line t) + (setq comment-multi-line nil)) + (indent-new-comment-line)))) + (mdc-indent-line)) + +(defun mdc-indent-line () + "Indentation for Modelica." + (let ((pos (- (point-max) (point))) beg beg-anno end-anno) + (beginning-of-line) + (setq beg (point)) + ;; no indentation of invisible text (hidden annotations) + (if (mdc-within-overlay 'invisible) + () + ;; no indentation if preceeding newline is quoted + (if (and (> (point) 2) + (progn + (forward-char -2) + (looking-at "[\\]\n"))) + (forward-char 2) + ;; else indent line + (goto-char beg) + (skip-chars-forward " \t") + (let ((indent (mdc-calculate-indent))) + (if (= indent (current-column)) + ;; nothing to be done + () + (delete-region beg (point)) + (indent-to indent))))) + ;; return to the old position inside the line + (if (> (- (point-max) pos) (point)) + (goto-char (- (point-max) pos))))) + +(defun mdc-calculate-indent () + "Calculate indentation for current line; + assumes point to be over the first non-blank of the line" + (save-excursion + (let ((case-fold-search nil) + offset (last-open nil) (save-point (point)) + ref-point ref-column) + (cond + ;; multi-line comment has fixed indentation, relative to its start + ((mdc-within-comment t) + (setq ref-column (current-column)) + (goto-char save-point) + (if (looking-at "\\*/") + ref-column + (+ ref-column mdc-comment-offset))) + ;; concatenation of strings + ((and (looking-at "\"") + (mdc-behind-string t)) + ;; move point to the very first string constant + ;; in order to consider concatenation on the same line + (while (mdc-behind-string t)) + (current-column)) + ;; continued single-line comment + ((and (looking-at "//") + (forward-comment -1) + (looking-at "//")) + (current-column)) + ;; default looks for last unended begin-like statement + (t + (goto-char save-point) ; needed after check for singele-line comments + (setq offset mdc-basic-offset) + ;; goto left for labels, end's etc. + (if (looking-at + (concat + ; ("algorithm" "elseif" "elsewhen" "end" "equation" "external" + ; "in" "loop" "protected" "public") + "\\(algorithm\\|e\\(lse\\(if\\|when\\)\\|nd\\|quation\\|" + "xternal\\)\\|in\\|loop\\|p\\(rotected\\|ublic\\)\\)" + "\\>")) + (setq offset (- offset mdc-basic-offset))) + (if (and + (looking-at + (concat + ; ("else" "then") + "\\(else\\|then\\)" + "\\>")) + (not (mdc-within-equation))) + (setq offset (- offset mdc-basic-offset))) + (condition-case nil + (let () + (mdc-last-unended-begin t) + ;; correct offset + (if (looking-at "end\\>") + ;; found an 'end', means no basic offset + (setq offset (- offset mdc-basic-offset))) + ;; check indentation in statements + (setq ref-column (current-column)) + (setq ref-point (point)) + (goto-char save-point) + (mdc-statement-start ref-point) + (if (>= (point) save-point) + ;; indent relative to ref-point as new statement starts + (max 0 (+ ref-column offset)) + ;; else add mdc-statement-offset + ;; provided that point is behind ref-point + ;; and point is not within a begin-like statement + (if (and (> (point) ref-point) + (not (and (mdc-forward-begin) + (> (point) save-point)))) + (setq offset (+ offset mdc-statement-offset))) + (setq last-open + (car (cdr (parse-partial-sexp (point) save-point)))) + (if (not last-open) + (max 0 (+ ref-column offset)) + (goto-char last-open) + (+ 1 (current-column))))) + (error 0))))))) + +(defun mdc-empty-line () + "Return t if current line is empty, else return nil" + (save-excursion + (beginning-of-line) + (skip-chars-forward " \t") + (eolp))) + +(defun mdc-within-comment (&optional move-point) + "Return comment starter if point is within a comment, nil otherwise; + optionally move point to the beginning of the comment" + (let ((starter nil) (save-point (point))) + ;; check single-line comment + (setq starter (mdc-within-single-line-comment move-point)) + (if (not starter) + ;; check multi-line comment + (condition-case nil + (if (and (re-search-backward "/\\*\\|\\*/") + (looking-at "/\\*")) + ;; check if we arrived in a single-line comment + (if (progn (forward-char) + (mdc-within-single-line-comment move-point)) + ;; then the original starting point is not a comment + () + ;; else accept multi-line comment + (backward-char) + (setq starter "/*"))) + (error nil))) + (if (and starter move-point) + (setq save-point (point))) + (goto-char save-point) + starter)) + +(defun mdc-within-single-line-comment (&optional move-point) + "Return comment starter if point is within a single-line comment, + nil otherwise; optionally move point to the beginning of the comment" + (let ((starter nil) (save-point (point))) + ;; check single-line comment + (condition-case nil + (if (and (re-search-backward "//\\|\n") + (looking-at "//")) + (setq starter "//")) + (error nil)) + (if (and starter move-point) + (setq save-point (point))) + (goto-char save-point) + starter)) + +(defun mdc-within-string (&optional move-point) + "Return t if point is within a string constant, nil otherwise; + optionally move point to the starting double quote of the string" + (if (and (boundp 'font-lock-mode) font-lock-mode) + ;; use result of font-lock-mode to also cover multi-line strings + (let (within-string) + (setq within-string + (text-property-any (point) (min (+ (point) 1) (point-max)) + 'face 'font-lock-string-face)) + (if (and within-string move-point) + (let ((point-next + (text-property-not-all (point) (point-max) + 'face 'font-lock-string-face))) + (goto-char point-next) + (backward-sexp))) + within-string) + (let ((within-string nil) (save-point (point)) (start-point nil)) + (condition-case nil + (while + (progn + (re-search-backward "\\(^\\|[^\\]\\)[\"\n]") + (looking-at "\\(^\\|.\\)\"")) + (if (not (looking-at "^")) + (forward-char 1)) + (if within-string + (setq within-string nil) + (setq within-string t) + (or start-point (setq start-point (point))))) + (error nil)) + (if (and within-string move-point start-point) + (goto-char start-point) + (goto-char save-point)) + within-string))) + +(defun mdc-behind-string (&optional move-point) + "Check for string concatenation. Return t if only blanks are between point + and the preceeding string constant, nil otherwise. Optionally move point + to the starting double quote of the preceeding string." + (let ((behind-string nil) + (save-point (point))) + (if (and (> (point) 1) + (progn + (skip-chars-backward " \t\n") + (if (> (point) 1) + (forward-char -1)) + (looking-at "\"")) + (mdc-within-string move-point)) + (setq behind-string t)) + (if (or (not move-point) + (not behind-string)) + (goto-char save-point)) + behind-string)) + +(defun mdc-within-matrix-expression (&optional move-point) + "Return t if an opening bracket is found backwards from point, + nil otherwise; optionally move point to the bracket" + (let ((save-point (point)) (matrix-expression nil)) + (condition-case nil + (let () + (while (progn + (re-search-backward "[\]\[]") + (mdc-within-comment t))) + (if (looking-at "[\[]") + (progn + (setq matrix-expression t) + (if move-point + (setq save-point (point)))))) + (error nil)) + (goto-char save-point) + matrix-expression)) + +(defun mdc-within-equation (&optional move-point) + "return t if point is within right hand side of an equation, nil otherwise; + optionally move point to the identifying '=' or ':='" + (let ((equation nil) (save-point (point))) + (condition-case nil + (let () + (while (progn + (re-search-backward + (concat "\\([^=]:?=[^=]\\)\\|;")) + (mdc-within-comment))) + (if (looking-at ";") + (setq equation nil) + (setq equation t) + (if (looking-at "[^:]=") + (forward-char 1)) + (if move-point + (setq save-point (point))))) + (error nil)) + (goto-char save-point) + equation)) + +(defun mdc-statement-start (&optional ref-point) + "Move point to the first character of the current statement; + optional argument points to the last end or unended begin" + (let ((save-point (point))) + (if ref-point + () + (condition-case nil + (mdc-last-unended-begin t) + (error (goto-char (point-min)))) + (setq ref-point (point)) + (goto-char save-point)) + (while (progn + (re-search-backward + ;; ("]" ")" ";" + ;; "algorithm" "equation" "external" + ;; "else" "elseif" "elsewhen" + ;; "loop" "protected" "public" "then") + (concat + "[\]\);]\\|" + "\\<" + "\\(algorithm\\|e\\(lse\\(if\\|when\\)?\\|quation\\|" + "xternal\\)\\|loop\\|p\\(rotected\\|ublic\\)\\|then\\)" + "\\>") + ref-point 'no-error) + (and + (> (point) ref-point) + (or (mdc-within-comment t) + (mdc-within-string) + (if (looking-at "[\]\)]") + (progn + (forward-char 1) + (forward-sexp -1) + t)) + (if (looking-at ";") + (mdc-within-matrix-expression t) + (mdc-within-equation t)))))) + (cond + ((= (point) ref-point) + ;; we arrived at last unended begin, + ;; but might be looking for first statement of block + (mdc-forward-begin) + (forward-comment (- (buffer-size))) + (if (> (point) save-point) + (goto-char ref-point))) + ((looking-at ";") + (forward-char 1)) + (t + (forward-word 1))) + (forward-comment (buffer-size)))) + +(defun mdc-short-class-definition () + "return t if point is over a short class definition" + (looking-at (concat + "\\(" mdc-class-modifier-keyword "\\)*" + mdc-class-keyword + "[A-Za-z_][0-9A-Za-z_]*[ \t\n\r]+="))) + +(defun mdc-end-ident () + "return t if last word is an 'end'" + (save-excursion + (forward-word -1) + (looking-at "end\\>"))) + +(defun mdc-last-unended-begin (&optional indentation-only) + "Position point at last unended begin; + raise an error if nothing found. + If indentation-only is true, then position point at last begin or end." + ;; find last unended begin-like keyword + (let ((depth 1)) + (while (>= depth 1) + (while (progn + (re-search-backward + (concat + "\\<" + ; ("block" "class" "connector" "end" + ; "for" "function" "if" "model" "package" + ; "record" "type" "when" "while") + "\\(block\\|c\\(lass\\|onnector\\)\\|end\\|" + "f\\(or\\|unction\\)\\|if\\|model\\|package\\|" + "record\\|type\\|wh\\(en\\|ile\\)\\)" + "\\>")) + (or (mdc-within-comment t) + (mdc-short-class-definition) + (mdc-within-string) + (mdc-end-ident) + (and (looking-at "if") (mdc-within-equation t))))) + (if (looking-at "end\\>") + (if indentation-only + (setq depth -1) + (setq depth (+ depth 1))) + (setq depth (- depth 1)))) + ;; step backwards over class prefixes + (if (>= depth 0) + (let ((save-point (point))) + (while (progn + (forward-word -1) + (and + (looking-at mdc-class-modifier-keyword) + (not (mdc-within-comment)))) + (setq save-point (point))) + (goto-char save-point))))) + +(defun mdc-forward-begin () + "Move point forward over a begin-like statement. + Return block ident (string) or nil if not found. + Point is assumed over the start of the begin-like statement upon call." + (let ((ident nil) (save-point (point)) start-point) + (cond + ((looking-at "\\(for\\|while\\)\\>") + (setq ident (buffer-substring (match-beginning 0) (match-end 0))) + (while (progn + (re-search-forward "\\") + (mdc-within-comment)))) + ;;(regexp-opt '("if" "elseif" "when" "elsewhen")) + ((looking-at "\\(else\\(if\\|when\\)\\|if\\|when\\)\\>") + (setq ident (buffer-substring (match-beginning 0) (match-end 0))) + (while (progn + (re-search-forward "\\") + (mdc-within-comment)))) + ((looking-at + (concat "\\(" mdc-class-modifier-keyword "\\)\\|" + "\\(" mdc-class-keyword "\\)")) + ;; move over class modifiers + (while (looking-at mdc-class-modifier-keyword) + (forward-word 1) + (forward-comment (buffer-size))) + ;; check and move over class specifier + (if (not (looking-at mdc-class-keyword)) + (goto-char save-point) + (forward-word 1) + (forward-comment (buffer-size)) + ;; move over class name and look it up + (setq start-point (point)) + (re-search-forward "\\>") + (setq ident (buffer-substring start-point (point))) + (forward-comment (buffer-size)) + ;; check short class definition + (if (looking-at "=") + (progn + (setq ident nil) + (goto-char save-point)) + ;; else move over documentation strings + (while (looking-at "\"") + (forward-char 1) + (if (looking-at "\"") + (forward-char 1) + (re-search-forward "[^\\]\"")) + (forward-comment (buffer-size))))))) + (forward-comment (buffer-size)) + ident)) + +(defun mdc-newline-and-indent () + "Indent current line before calling 'newline-and-indent'" + (interactive) + (mdc-indent-line) + (newline-and-indent)) + +(defun mdc-insert-end () + "Insert end statement for current block." + (interactive) + (let ((case-fold-search nil) + indentation (save-point (point)) (block-start nil) (end-ident "")) + (save-excursion + (condition-case nil + (mdc-last-unended-begin) + (error (error "Couldn't find unended begin."))) + (setq indentation (current-column)) + (setq end-ident (mdc-forward-begin)) + (if (<= save-point (point)) + (setq block-start t))) + ;; insert newline or clear up an empty line + (if (not (mdc-empty-line)) + (insert "\n") + (setq save-point (point)) + (beginning-of-line) + (delete-region (point) save-point)) + ;; insert proper end + (indent-to indentation) + (insert (concat "end " end-ident ";")) + ;; step back if block just starts + (if (not block-start) + () + (forward-line -1) + (end-of-line)) + ;; insert newline + (insert "\n") + (mdc-indent-line))) + +;; active regions, and auto-newline/hungry delete key +;; (copied from cc-mode.el) +(defun mdc-keep-region-active () + ;; Do whatever is necessary to keep the region active in + ;; XEmacs 19. ignore byte-compiler warnings you might see + (and (boundp 'zmacs-region-stays) + (setq zmacs-region-stays t))) + +(defun mdc-forward-statement () + "Move point to next beginning of a statement" + (interactive) + (mdc-keep-region-active) + (let ((case-fold-search nil) + (save-point (point)) + pos) + (mdc-statement-start) + (if (> (point) save-point) + ;; ready after skipping leading comment or blanks + () + ;; else move forward + (setq save-point (point)) + (if (mdc-forward-begin) + ;; ready after moving over begin-like statement + () + ;; move forward behind next ";" ending a statement + (while + (progn + (re-search-forward ";" (point-max) t) + (or (mdc-within-comment) + (mdc-within-string) + (mdc-within-matrix-expression)))) + (forward-comment (point-max)) + (if (= (point) (point-max)) + (progn + (goto-char save-point) + (error "No next statement")) + ;; move backwards over possibly skipped statements + ;; without ending ";" + (setq pos (point)) + (while (> (point) save-point) + (setq pos (point)) + (forward-comment (- (point-max))) + (if (> (point) (point-min)) + (forward-char -1)) + (mdc-statement-start)) + (goto-char pos)))))) + +(defun mdc-backward-statement () + "Move point to previous beginning of a statement" + (interactive) + (mdc-keep-region-active) + (let ((case-fold-search nil) (save-point (point))) + (mdc-statement-start) + (if (< (point) save-point) + ;; ready after having moved to start of current statement + () + ;; else move backward + (setq save-point (point)) + (forward-comment (- (point-max))) + (if (> (point) (point-min)) + (forward-char -1)) + (mdc-statement-start) + (if (= (point) save-point) + (error "No previous statement"))))) + +(defun mdc-forward-block () + "Move point to next beginning of a block at the same nesting level + or a level higher if no next block found on the same level." + (interactive) + (let ((save-point (point))) + (condition-case nil + (progn + (mdc-to-block-begin) + (if (> (point) save-point) + ;; we moved already forward to a block begin + () + (mdc-to-block-end) + (mdc-forward-statement) + (mdc-to-block-begin) + (if (< (point) save-point) + (mdc-forward-block)))) + ;; in case of error and if we did move yet, + ;; move forward one statement + (error (if (= (point) save-point) + (mdc-forward-statement)))))) + +(defun mdc-backward-block () + "Move point to previous beginning of a block at the same nesting level + or a level higher if no previous block found on the same level." + (interactive) + (let ((save-point (point))) + (condition-case nil + (progn + (mdc-to-block-begin) + (if (< (point) save-point) + ;; we moved already backward to the beginning of a block + () + (mdc-backward-statement) + (mdc-to-block-begin))) + ;; in case of error and if we did not move yet, + ;; move backward one statement + ;; and move to beginning of that block if one ends there + (error (progn + (if (= (point) save-point) + (progn + (mdc-backward-statement) + (if (looking-at "\\") + (mdc-to-block-begin))))))))) + +(defun mdc-to-block-begin () + "Move point to beginning of current statement block" + (interactive) + (mdc-keep-region-active) + (let ((case-fold-search nil) + (save-point (point))) + (condition-case nil + (progn + (mdc-statement-start) + (mdc-forward-begin) + (mdc-last-unended-begin)) + (error (progn + (goto-char save-point) + (error "No statement block")))))) + +(defun mdc-to-block-end () + "Move point to end of current statement block" + (interactive) + (mdc-keep-region-active) + (let ((case-fold-search nil) + ident (save-point (point))) + (condition-case nil + (progn + (mdc-statement-start) + (mdc-forward-begin) + (mdc-last-unended-begin) + (setq ident (mdc-forward-begin)) + (while (progn + (re-search-forward + (concat "\\")) + (or + (mdc-within-comment) + (mdc-within-string))))) + (error (progn + (goto-char save-point) + (error (if ident + (format "Missing \"end %s\"" ident) + "No statement block to end"))))))) + +;; snarfed from outline.el (outline-flag-region) +;; Comments about GNU Emacs 20.7 and XEmacs 21.1: +;; GNU Emacs: +;; - ellipse ... is not displayed as we stop hiding before end of line +;; (advantages of only hiding embraced annotation text are +;; correct visible syntax and visible end of hidden text) +;; XEmacs: +;; - isearch-open-invisible property does not work, i.e. +;; hidden text is not shown if isearch finds it +;; General: +;; - intangible property, to skip hidden text when moving by chars, +;; does not work with XEmacs and does not fully work with GNU Emacs +;; (e.g. C-a/C-e stop in hidden annotation, search/replace finds +;; at most one occurence, reopen of externally modified files is strange) +;; - read-only property, to avoid modifications of hidden text, +;; does not work with GNU Emacs +;; --> we don't set intangible or read-only property +(defun mdc-flag-region (from to flag) + "Hides or shows lines from FROM to TO, according to FLAG. +If FLAG is nil then text is shown, while if FLAG is t the text is hidden." + (save-excursion + (goto-char from) + (mdc-discard-overlays from to 'mdc-annotation) + (if flag + (let ((o (make-overlay from to))) + (overlay-put o 'invisible 'mdc-annotation) + (overlay-put o 'isearch-open-invisible + 'mdc-isearch-open-invisible))))) + +;; snarfed from outline.el (outline-isearch-open-invisible); +;; Function to be set as an outline-isearch-open-invisible' property +;; to the overlay that makes the outline invisible +;; (see `mdc-flag-region'). +(defun mdc-isearch-open-invisible (overlay) ()) + +;; snarfed from outline.el (outline-discard-overlays) +(defun mdc-discard-overlays (beg end value) + (if (< end beg) + (setq beg (prog1 end (setq end beg)))) + (save-excursion + (let ((overlays (overlays-in beg end)) + o) + (while overlays + (setq o (car overlays)) + (if (eq (overlay-get o 'invisible) value) + (delete-overlay o)) + (setq overlays (cdr overlays)))))) + +;; define overlay functions for XEmacs 21.1 +(if (not (functionp 'overlays-in)) + (defun overlays-in (beg end) + (extent-list (current-buffer) beg end))) + +(if (not (functionp 'make-overlay)) + (defalias 'make-overlay 'make-extent)) + +(if (not (functionp 'delete-overlay)) + (defalias 'delete-overlay 'delete-extent)) + +(if (not (functionp 'overlay-put)) + (defalias 'overlay-put 'set-extent-property)) + +(if (not (functionp 'overlay-get)) + (defalias 'overlay-get 'extent-property)) + +;; test for overlay +(if (not (functionp 'overlays-at)) + ;; XEmacs 21.1 + (defun mdc-within-overlay (prop) + "Return overlay value if point is contained in an overlay + with property prop, nil otherwise." + (extent-at (point) (current-buffer) prop)) + (defun mdc-within-overlay (prop) + "Return overlay value if point is contained in an overlay + with property prop, nil otherwise." + (let ((overlays (overlays-at (point))) + (value nil) + o) + (while (and overlays (not value)) + (setq o (car overlays)) + (setq value (overlay-get o prop)) + (setq overlays (cdr overlays))) + value))) + +(defun mdc-hide-annotations (beg end) + "Hide all annotations." + (save-excursion + (let (beg-hide end-hide) + (goto-char beg) + (while + (and (< (point) end) + (search-forward-regexp "\\