;;; latte.el --- Major mode for editing Latte source.
;; Copyright 1998,199 Zanshin Inc.
;; The contents of this file are subject to the Zanshin Public License Version
;; 1.0 (the "License"); you may not use this file except in compliance with the
;; License. You should have received a copy of the License with Latte; see
;; the file COPYING. You may also obtain a copy of the License at
;; .
;;
;; Software distributed under the License is distributed on an "AS IS" basis,
;; WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
;; for the specific language governing rights and limitations under the
;; License.
;;
;; The Original Code is Latte.
;;
;; The Initial Developer of the Original Code is Zanshin, Inc.
;;; Author:
;; Bob Glickstein
;;; Plug:
;; Check out my book, "Writing GNU Emacs Extensions," from O'Reilly
;; and Associates.
;;; Code:
(require 'derived)
(defvar latte-mode-map nil
"*Keymap for Latte mode.")
(if latte-mode-map
nil
(setq latte-mode-map (copy-keymap text-mode-map))
(substitute-key-definition 'backward-delete-char
'backward-delete-char-untabify
latte-mode-map)
(substitute-key-definition 'delete-backward-char
'backward-delete-char-untabify
latte-mode-map)
(define-key latte-mode-map "\t" 'indent-for-tab-command)
)
(defvar latte-mode-syntax-table nil
"*Syntax table for Latte mode.")
(if latte-mode-syntax-table
nil
(setq latte-mode-syntax-table (copy-syntax-table text-mode-syntax-table))
(modify-syntax-entry ?\\ "\\" latte-mode-syntax-table))
(defvar latte-font-lock-keywords
'(("\\\\;.*" . font-lock-comment-face)
("\\\\\\\\" . font-lock-keyword-face)
("\\\\/" . font-lock-keyword-face)
("\\\\['`]" . font-lock-keyword-face)
("\\\\,@?" . font-lock-keyword-face)
;; The following regex was created with
;; (sregexq "\\\""
;; (0+ (or (not-char "\\")
;; (sequence "\\" (not-char "\""))))
;; "\\\"")
;; See
("\\\\\"\\([^\\\\]\\|\\\\[^\"]\\)*\\\\\"" 0 font-lock-string-face t)
("{\\(\\\\\\(def\\|if\\|lambda\\)\\)\\>" 1 font-lock-keyword-face)
("{\\(\\\\[A-Za-z_][A-Za-z_0-9?!+-]*\\)" 1 font-lock-function-name-face)
("\\\\[&=]?[A-Za-z_][A-Za-z_0-9?!+-]*" . font-lock-variable-name-face))
"*Value of font-lock-keywords for Latte mode.")
(define-derived-mode latte-mode text-mode "Latte"
"Major mode for editing Latte source."
(make-local-variable 'comment-column)
(make-local-variable 'comment-start-skip)
(make-local-variable 'comment-start)
(make-local-variable 'comment-end)
(make-local-variable 'font-lock-defaults)
(make-local-variable 'indent-line-function)
(make-local-variable 'imenu-generic-expression)
(setq comment-column 40
comment-start-skip "\\\\;\\s-*"
comment-start "\\; "
comment-end "" ;xxx also set comment-indent-function?
font-lock-defaults '(latte-font-lock-keywords t)
indent-line-function 'latte-indent-line
)
(setq imenu-generic-expression
'((nil "^{\\\\def\\s-*\\\\\\([A-Za-z_][A-Za-z_0-9?!+-]*\\)" 1))))
(defconst latte-keyword-regex
(concat "\\\\\\("
(mapconcat 'symbol-name
'(def if let while)
"\\|")
"\\)\\>"))
(defun latte-indent-line ()
(save-match-data
(let* ((start (point))
;; the position of the first non-whitespace on the line
(bol (progn (back-to-indentation)
(point)))
;; the position of the innermost open-brace enclosing bol
(open (condition-case nil
(let ((looping t))
(while looping
(backward-up-list 1)
(setq looping
(not (= (following-char) ?{))))
(point))
(error nil)))
;; the column of that open-brace
(open-column (and open (current-column)))
;; whether that open-brace is followed by one of a few
;; keywords with special indentation rules
(keyword (and open
(progn
(forward-char 1)
(and (looking-at latte-keyword-regex)
(match-string 1)))))
;; the position of the first non-whitespace on the previous
;; nonblank line
(prev-bol (progn
(goto-char bol)
(and (zerop (forward-line -1))
(progn
(end-of-line)
(skip-chars-backward " \t\n\r\f")
(and (not (memq (preceding-char)
'(0 ?\ ?\t ?\n ?\r ?\f)))
(progn
(back-to-indentation)
(point)))))))
;; the indentation of the previous nonblank line relative
;; to start
(prev-indent (and prev-bol
(current-indentation)))
;; the position of the open-brace enclosing the beginning
;; of the previous line
(prev-open (and prev-bol
(progn
(condition-case nil
(let ((looping t))
(while looping
(backward-up-list 1)
(setq looping
(not (= (following-char) ?{))))
(point))
(error nil)))))
;; the result
(column (cond (keyword (+ open-column 2))
;; if current line and previous line are
;; contained in the same level of braces, use
;; previous line's indentation
((and prev-open
(equal open prev-open))
prev-indent)
;; if current line is enclosed in braces, use
;; the brace's column + 1
(open (1+ open-column))
;; Use previous line's indentation
(prev-indent prev-indent)
;; No indentation
(t 0))))
(goto-char bol)
(if (and (= start bol)
(>= (current-indentation) column))
;; repeated presses of TAB should indent the line further
(indent-relative)
(indent-line-to column)))))
;;; latte.el ends here