;;;; oml-mode.el --- o:XML Integrated Development Environment ;; oml-mode is a copy of xslide modified by Martin Klang to do o:XML ;; Copyright (C) 1998, 1999, 2000 Tony Graham ;; Author: Tony Graham ;; Contributors: Simon Brooke, Girard Milmeister, Norman Walsh, ;; Moritz Maass, Lassi Tuura ;; Created: 21 August 1998 ;; Version: $Revision: 1.15 $ ;; Keywords: languages, oml, xml ;;; This file is not part of GNU Emacs. ;; This program 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 ;; of the License, or (at your option) any later version. ;; ;; This program 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 this program; if not, write to the Free Software ;; Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ;;;; Commentary: ;; Functions for editing o:XML programs ;; Requires oml-mode-font.el ;; Requires 'etags for `find-tag-default' ;; Requires 'imenu for "Goto" menu ;; ;;;; Code: (provide 'oml-mode) (require 'cl) (require 'compile) (require 'font-lock) ;; XEmacs users don't always have imenu.el installed, so use ;; condition-case to cope if oml-mode causes an error by requiring imenu. (eval-and-compile (condition-case nil (require 'imenu) (error nil))) ;; Need etags for `find-tag-default' (require 'etags) (require 'oml-mode-data "oml-mode-data") (require 'oml-mode-abbrev "oml-mode-abbrev") (require 'oml-mode-font "oml-mode-font") (require 'oml-mode-process "oml-mode-process") ;; Define core `oml' group. (defgroup oml nil "Major mode for editing o:XML." :prefix "oml-" :group 'languages) (defgroup oml-faces nil "Font faces used in o:XML mode." :group 'oml :group 'faces) (defgroup oml-process nil "Running o:XML processors from o:XML mode." :group 'oml) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Version information (defconst oml-mode-version "0.2b2" "Version number of oml-mode o:XML mode.") (defun oml-mode-version () "Returns the value of the variable oml-mode-version." oml-mode-version) (defconst oml-mode-maintainer-address "bugs@o-xml.org") ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Variables (defvar oml-indent-tabs-mode nil "*Initial value of indent-tabs-mode on entering oml-mode") (defvar oml-default-filespec "*.oml" "*Inital prompt value for `oml-etags''s FILESPEC argument.") (defvar oml-filespec-history (list oml-default-filespec) "Minibuffer history list for `oml-etags' and `oml-grep''s FILESPEC argument.") (defvar oml-grep-pattern-history nil "Minibuffer history list for `oml-grep''s PATTERN argument.") (defvar oml-grep-case-sensitive-flag nil "*Non-nil disables case insensitive searches by `oml-grep'.") (defvar oml-comment-start "" "*Comment end character sequence") (defvar oml-comment-max-column 70 "*Maximum column number for text in a comment") (defcustom oml-initial-program-file (locate-library "oml-mode-initial.oml" t) "*File containing initial program inserted into empty o:XML buffers" :type '(choice (file :must-match t) (const :tag "No initial program" nil)) :group 'oml) (defcustom oml-initial-program-initial-point 0 "*Initial position of point in initial program" :type '(integer) :group 'oml) (defvar oml-indent-attributes nil "*Whether to indent attributes on lines following an open tag. If non-nil, attributes will be aligned with the space after the element name, otherwise by two spaces.") ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; functions (defun oml-read-from-minibuffer (prompt default history) "Read from minibuffer with default and command history." (let ((value nil)) (if (string-equal "" (setq value (read-from-minibuffer (if default (format "%s(default `%s') " prompt default) (format "%s" prompt)) nil nil nil history))) default value))) ;; o:XML IDE house style puts all comments starting on a favourite column (defun oml-comment (comment) "Insert COMMENT starting at the usual column. With a prefix argument, e.g. \\[universal-argument] \\[oml-comment], insert separator comment lines above and below COMMENT in the manner of `oml-big-comment'." (interactive "sComment: ") (insert "\n") (backward-char) (oml-electric-tab) (let ((fill-column (1- oml-comment-max-column)) (fill-prefix (make-string (1+ (length oml-comment-start)) ?\ )) ;; (comment-start oml-init-comment-fill-prefix) (saved-auto-fill-function auto-fill-function)) (auto-fill-mode 1) (insert oml-comment-start) (insert " ") (indent-to (length fill-prefix)) (fill-region (point) (save-excursion (insert comment) (point)) nil 1 1) ;; The fill does the right thing, but it always ends with ;; an extra newline, so delete the newline. (delete-backward-char 1) (if (not saved-auto-fill-function) (auto-fill-mode 0)) (insert " ") (insert oml-comment-end) (insert "\n") (if font-lock-mode (save-excursion (font-lock-fontify-keywords-region (oml-font-lock-region-point-min) (oml-font-lock-region-point-max)))))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Mode map stuff (defvar oml-mode-map nil "Keymap for o:XML mode.") (if oml-mode-map () (setq oml-mode-map (make-sparse-keymap)) (define-key oml-mode-map [tab] 'oml-electric-tab) ;; (define-key oml-mode-map "\M-\t" 'oml-complete) (define-key oml-mode-map [(meta tab)] 'oml-complete) ;; (define-key oml-mode-map "\"" 'oml-electric-quote) ;; (define-key oml-mode-map "'" 'oml-electric-apos) (define-key oml-mode-map "/" 'oml-electric-slash) (define-key oml-mode-map "<" 'oml-electric-less-than) (define-key oml-mode-map ">" 'oml-electric-greater-than) ;; (define-key oml-mode-map "[" 'oml-electric-lsqb) ;; (define-key oml-mode-map "(" 'oml-electric-lpar) ;; (define-key oml-mode-map "{" 'oml-electric-lcub) (define-key oml-mode-map [(control c) (control c)] 'oml-comment) (define-key oml-mode-map [(control c) (control p)] 'oml-process) (define-key oml-mode-map "\C-c<" 'oml-insert-tag) ;; (define-key oml-mode-map [(control m)] 'oml-electric-return) ;; (define-key oml-mode-map \10 'oml-electric-return) (define-key oml-mode-map "\177" 'backward-delete-char-untabify) ;; (define-key oml-mode-map "\M-\C-e" 'oml-next-rule) ;; (define-key oml-mode-map "\M-\C-a" 'oml-previous-rule) ;; (define-key oml-mode-map "\M-\C-h" 'mark-oml-rule) ) (defun oml-electric-greater-than () (interactive) (insert ">") (if font-lock-mode (save-excursion (font-lock-fontify-region (oml-font-lock-region-point-min) (oml-font-lock-region-point-max))))) (defun oml-electric-apos () "Function called when \"'\" is pressed in o:XML mode" (interactive) (insert "'") (if (looking-at "\\([\"/})]\\|$\\)") (save-excursion (insert "'")))) (defun oml-electric-quote () "Function called when '\"' is pressed in o:XML mode" (interactive) (insert "\"") (if (looking-at "\\(['/})]\\|$\\)") (save-excursion (insert "\"")))) (defun oml-electric-lsqb () "Function called when \"[\" is pressed in o:XML mode" (interactive) (insert "[") (if (looking-at "\\([\"'/})]\\|$\\)") (save-excursion (insert "]")))) (defun oml-electric-lpar () "Function called when \"(\" is pressed in o:XML mode" (interactive) (insert "(") (if (looking-at "\\([\]\"'/}]\\|$\\)") (save-excursion (insert ")")))) (defun oml-electric-lcub () "Function called when \"{\" is pressed in o:XML mode" (interactive) (insert "{") (if (looking-at "\\([\])\"'/}]\\|$\\)") (save-excursion (insert "}")))) (defun oml-electric-less-than () "Function called when \"<\" is pressed in o:XML mode" (interactive) (insert "<") (oml-electric-tab)) (defun oml-match-opening-tag (a) "Function called to match the next opening tag to a closing tag" (if (looking-at "]") nil t) (cond ((looking-at (concat " \t]+\\)>") ;; (message "End tag: %s" (match-string 1)) ; find matching tag: (oml-match-opening-tag (match-string 1))) ;;original ;; (re-search-backward ;; (concat "<" (match-string 1) "[ \t\n\r>]") nil t)) ((looking-at "<\\(\\([^/>]\\|/[^>]\\)+\\)/>")) ;; (message "Empty tag: %s" (match-string 1))) ((looking-at "")) ((looking-at "<\\([^/> \n\t]+\\)") ;; (message "Start tag: %s" (match-string 1)) (throw 'start-tag (match-string 1))) ((bobp) (throw 'start-tag nil))))) nil)))) (if element-name (progn (insert element-name) (oml-electric-greater-than)))))) (defun oml-electric-return () (interactive) (insert "\n") (oml-electric-tab)) (defun oml-electric-tab () "Function called when TAB is pressed in o:XML mode." (interactive) (save-excursion (beginning-of-line) (delete-horizontal-space) (if (or (looking-at ") (setq open (1- open))) ((eq here '?\<) (setq open (1+ open))) ) ) (forward-char) ) (< open 0) ; true if we've counted more ; closes than opens ) ) ) (defun oml-calculate-indent () "Calculate what the indent should be for the current line" (interactive) (let* ((limit (point)) (name "[^<>=\"' \t\n]+") (string "\\(\"[^<>\"]*\"\\|'[^<>']*'\\)") (ostring "\\(\"[^<>\"]*\\|'[^<>']*\\)") (attval (concat name "=" string)) (oattval (concat name "=" ostring)) (element (concat "<\\(" name "\\)" "\\([ \t\n]+" attval "\\)*")) (meta (concat " stay put ((save-excursion (re-search-forward "" limit t)) (current-column)) ;; open comment => indent by five ((looking-at "