; -*- Mode:LISP; Package:STEVE; Readtable:CL; Base:10 -*-

;Copyright (c) May 1983 by Christopher Eliot
; and Massachusetts Institute of Technology.
;Permission to copy all or part of this material is granted, provided
; that the copies are not made or distributed for resale, the MIT
; copyright notice and reference to the source file and the software
; distribution version appear, and that notice is given that copying
; is by permission of Massachusetts Institute of Technology.
;
;Major and minor mode functions for steve.
;

;;; A major mode must have a variable *MODE-bindings* containing the
;;; *editor-bindings* for that mode. Also *MODE-syntax* is local-bound
;;; to the variable syntax-table.

(defstruct (mode (:conc-name mode-))
  name
  superior
  ;:inferiors ;Would this be useful?
  keys
  syntax
  variables)

(defun (:mode fs:file-attribute-bindings) (pathname kwd mode)
  ;; Note: mode can be a symbol or string.
  ;; (setq mode (lookup-mode mode))
  (loop for (var value . crap) on (major-mode-variables mode) by #'cddr
	collect var into vars
	collect value into values
	finally (return (values vars values () ()))))

(defun mode-lookup-symbol (name)
  (intern (string-append (string-upcase name) "-MODE") 'steve))

(defun lookup-mode (name)
  (let ((sym (mode-lookup-symbol name)))
    (and (boundp sym) (symeval sym))))

;Notice that this does not allow for upward propegation of changes.
;Actually that may be reasonable, But the issue should be kept alive 
;until decided.
(defsubst derive-syntax (table)
  (copy-syntax-table table))

(defmacro define-mode (name &optional superior keys syntax &restl variables)
  (when (null superior)
    (when (null keys) (setq keys '(make-empty-key-binding-table)))
    (when (null syntax) (setq syntax '(make-empty-syntax-table))))
  `(progn (defunmetax ,(intern (string-append name "-MODE") 'steve) () 
	   (major-mode ',name))
	  (defparameter
	   ,(mode-lookup-symbol name)
	   (make-mode
	    :name ',name
	    :superior ',superior
	    ;; :inferiors nil
	    :keys ,(if (null keys)
		       `(derive-bindings (mode-keys (lookup-mode ',superior)))
		       `,keys)
	    :syntax ,(if (null syntax) 
			 `(derive-syntax (mode-syntax
					  (lookup-mode ',superior)))
			 `,syntax)
	    :variables ',(copy-list variables)))))

(defun init-buffer-locals (&optional (buffer *editor-buffer*))
  ;;This creates an environment without a major mode.
  (let ((filename (probe-file (buffer-file-name buffer)))
	(plist))
    (cond ((not (null filename))
	   (setq plist (send filename :file-plist))
	   (remprop plist :mode)		;This differs from below.
	   (setf (buffer-environment buffer) 
		 (calculate-buffer-environment (cdr plist) filename)))
	  (t (setf (buffer-environment buffer) (null-load-environment))))
    (setq *context-change-flag* t)))

;;This is really about the same as init-buffer-locals.
;;The only difference is that this sets the major mode too.
(defun initialize-buffer-environment (buffer)
  (let ((filename (probe-file (buffer-file-name buffer)))
	(plist))
    (cond ((not (null filename))
	   (setq plist (send filename :file-plist))
	   (when (null (get plist :mode))
	     (putprop plist (default-major-mode (send filename :type)) :mode))
	   (setf (buffer-environment buffer)
		 (calculate-buffer-environment (cdr plist) filename)))
	  (t (setf (buffer-environment buffer) (null-load-environment))
	     (major-mode (default-major-mode (send (buffer-file-name buffer)
						   :type)))))
    (setq *context-change-flag* t)))

(defvar default-major-mode-alist nil)

(defun default-major-mode (file-type)
  (loop for (type mode) in default-major-mode-alist
	if (string-equal type file-type) return mode
	finally (return "Fundamental")))

(defun major-mode-variables (mode-name &aux (mode (lookup-mode mode-name)))
  (cond ((null mode) (ed-warning "Invalid Mode ~a" mode-name))
	(t (nconc (major-mode-superior-variables mode)
		  (list 'syntax-table (or (mode-syntax mode) syntax-table)
			'*editor-bindings* (mode-keys mode)
			'*major-mode* mode-name)))))

(defun major-mode-superior-variables (mode)
  (cond ((null mode) nil)
	(t (let ((vars (cons '*alist* (mode-variables mode)))
		 (shadowable-vars
		  (major-mode-superior-variables
		   (lookup-mode (mode-superior mode)))))
	     (nconc (loop for (var value . shit) on shadowable-vars by #'cddr
			  if (not (get vars var))
			  nconc (list var value))
		    (copy-list (cdr vars)))))))

(defun major-mode (&optional (mode-name (mx-prompter #'read-symbol "Mode: "))
		    (buffer *editor-buffer*))
  (init-buffer-locals buffer)
  (loop for (var value . creap) on (major-mode-variables mode-name) by #'cddr
	do (bind-in-buffer buffer var value)))

(defun set-mode-variable (mode variable value)
  (setf (mode-variables mode)
	(list* variable value
	       (loop for (var value . shit) on (mode-variables mode) by #'cddr
		     unless (eq var variable)
		     nconc (list var value))))
  (setq *context-change-flag* t)
  variable)
