source: tags/pre_1_0_pre_hash_modifications/ccl/hemlock/src/main.lisp @ 2475

Last change on this file since 2475 was 2475, checked in by anonymous, 14 years ago

This commit was manufactured by cvs2svn to create tag

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 13.8 KB
1;;; -*- Log: hemlock.log; Package: Hemlock-Internals -*-
3;;; **********************************************************************
4;;; This code was written as part of the CMU Common Lisp project at
5;;; Carnegie Mellon University, and has been placed in the public domain.
7#+CMU (ext:file-comment
8  "$Header$")
10;;; **********************************************************************
12;;; Hemlock initialization code and random debugging stuff.
14;;; Written by Bill Chiles and Rob MacLachlan
17(in-package :hemlock-internals)
21(in-package :extensions)
22(export '(save-all-buffers *hemlock-version*))
23(in-package :hemlock-internals)
28;;;; Definition of *hemlock-version*.
30(defvar *hemlock-version* "3.5")
31(pushnew :hemlock *features*)
32#+(or CMU scl)
33(setf (getf ext:*herald-items* :hemlock) 
34      `("    Hemlock " ,*hemlock-version*))
39(defvar *hemlock-initialized* nil)
41(defun %init-hemlock ()
42  "Initialize hemlock's internal data structures."
43  ;;
44  ;; This function is defined in Buffer.Lisp.  It creates fundamental mode
45  ;; and the buffer main.  Until this is done it is not possible to define
46  ;; or use Hemlock variables.
47  (setup-initial-buffer)
48  ;;
49  ;; Define some of the system variables.
50  (define-some-variables)
51  ;;
52  ;; Site initializations such as window system variables.
53  (site-init)
54  ;;
55  ;; Set up syntax table data structures.
56  (%init-syntax-table)
57  ;;
58  ;; Define print representations for funny characters.
59  (%init-line-image)
60  (setq *hemlock-initialized* t))
63;;;; Define some globals.
65;;; These globals cannot be defined in the appropriate file due to compilation
66;;; or load time constraints.
69;;; The following belong in other files, but those files are loaded before
70;;; table.lisp which defines MAKE-STRING-TABLE.
72;;; vars.lisp
73(defvar *global-variable-names* (make-string-table)
74  "A String Table of global variable names, the values are the symbol names.") 
76;;; buffer.lisp
77(defvar *mode-names* (make-string-table) "A String Table of Mode names.")
78(defvar *buffer-names* (make-string-table)
79  "A String Table of Buffer names and their corresponding objects.")
81;;; interp.lisp
82(defvar *command-names* (make-string-table) "String table of command names.")
84;;; syntax.lisp
85(defvar *character-attribute-names* (make-string-table)
86 "String Table of character attribute names and their corresponding keywords.")
92;;; This is necessary to define "Default Status Line Fields" which belongs
93;;; beside the other modeline variables.  This DEFVAR would live in
94;;; Morecoms.Lisp, but it is compiled and loaded after this file.
96(declaim (special hemlock::*recursive-edit-count*))
99 :name :edit-level :width 15
100 :function #'(lambda (buffer window)
101               (declare (ignore buffer window))
102               (if (zerop hemlock::*recursive-edit-count*)
103                   ""
104                   (format nil "Edit Level: ~2,'0D "
105                           hemlock::*recursive-edit-count*))))
107;;; This is necessary to define "Default Status Line Fields" which belongs
108;;; beside the other modeline variables.  This DEFVAR would live in
109;;; Completion.Lisp, but it is compiled and loaded after this file.
111(declaim (special hemlock::*completion-mode-possibility*))
112;;; Hack for now until completion mode is added.
113(defvar hemlock::*completion-mode-possibility* "")
116 :name :completion :width 40
117 :function #'(lambda (buffer window)
118               (declare (ignore buffer window))
119               hemlock::*completion-mode-possibility*))
122(defun define-some-variables ()
123  (defhvar "Default Modes"
124    "This variable contains the default list of modes for new buffers."
125    :value '("Fundamental" "Save"))
126  (defhvar "Echo Area Height"
127    "Number of lines in the echo area window."
128    :value 3)
129  (defhvar "Make Buffer Hook"
130    "This hook is called with the new buffer whenever a buffer is created.")
131  (defhvar "Delete Buffer Hook"
132    "This hook is called with the buffer whenever a buffer is deleted.")
133  (defhvar "Enter Recursive Edit Hook"
134    "This hook is called with the new buffer when a recursive edit is
135     entered.")
136  (defhvar "Exit Recursive Edit Hook"
137    "This hook is called with the value returned when a recursive edit
138     is exited.")
139  (defhvar "Abort Recursive Edit Hook"
140    "This hook is called with the editor-error args when a recursive
141     edit is aborted.")
142  (defhvar "Buffer Major Mode Hook"
143    "This hook is called with the buffer and the new mode when a buffer's
144     major mode is changed.")
145  (defhvar "Buffer Minor Mode Hook"
146    "This hook is called a minor mode is changed.  The arguments are
147     the buffer, the mode affected and T or NIL depending on when the
148     mode is being turned on or off.")
149  (defhvar "Buffer Writable Hook"
150    "This hook is called whenever someone sets whether the buffer is
151     writable.")
152  (defhvar "Buffer Name Hook"
153    "This hook is called with the buffer and the new name when the name of a
154     buffer is changed.")
155  (defhvar "Buffer Pathname Hook"
156    "This hook is called with the buffer and the new Pathname when the Pathname
157     associated with the buffer is changed.")
158  (defhvar "Buffer Modified Hook"
159    "This hook is called whenever a buffer changes from unmodified to modified
160     and vice versa.  It takes the buffer and the new value for modification
161     flag.")
162  (defhvar "Buffer Package Hook"
163      "This hook is called with the new package name whenever a (Lisp) buffer's package changes")
164  (defhvar "Set Buffer Hook"
165    "This hook is called with the new buffer when the current buffer is set.")
166  (defhvar "After Set Buffer Hook"
167    "This hook is invoked with the old buffer after the current buffer has
168     been changed.")
169  (defhvar "Set Window Hook"
170    "This hook is called with the new window when the current window
171     is set.")
172  (defhvar "Make Window Hook"
173    "This hook is called with a new window when one is created.")
174  (defhvar "Delete Window Hook"
175    "This hook is called with a window before it is deleted.")
176  (defhvar "Window Buffer Hook"
177    "This hook is invoked with the window and new buffer when a window's
178     buffer is changed.")
179  (defhvar "Delete Variable Hook"
180    "This hook is called when a variable is deleted with the args to
181     delete-variable.")
182  (defhvar "Entry Hook"
183    "this hook is called when the editor is entered.")
184  (defhvar "Exit Hook"
185    "This hook is called when the editor is exited.")
186  (defhvar "Redisplay Hook"
187    "This is called on the current window from REDISPLAY after checking the
188     window display start, window image, and recentering.  After calling the
189     functions in this hook, we do the above stuff and call the smart
190     redisplay method for the device."
191    :value nil)
192  (defhvar "Key Echo Delay"
193    "Wait this many seconds before echoing keys in the command loop.  This
194     feature is inhibited when nil."
195    :value 1.0)
196  (defhvar "Input Hook"
197    "The functions in this variable are invoked each time a character enters
198     Hemlock."
199    :value nil)
200  (defhvar "Abort Hook"
201    "These functions are invoked when ^G is typed.  No arguments are passed."
202    :value nil)
203  (defhvar "Command Abort Hook"
204    "These functions get called when commands are aborted, such as with
205     EDITOR-ERROR."
206    :value nil)
207  (defhvar "Character Attribute Hook"
208    "This hook is called with the attribute, character and new value
209     when the value of a character attribute is changed.")
210  (defhvar "Shadow Attribute Hook"
211    "This hook is called when a mode character attribute is made.")
212  (defhvar "Unshadow Attribute Hook"
213    "This hook is called when a mode character attribute is deleted.")
214  (defhvar "Default Modeline Fields"
215    "The default list of modeline-fields for MAKE-WINDOW."
216    :value *default-modeline-fields*)
217  (defhvar "Default Status Line Fields"
218    "This is the default list of modeline-fields for the echo area window's
219     modeline which is used for general information."
220    :value (list (make-modeline-field
221                  :name :hemlock-banner :width 27
222                  :function #'(lambda (buffer window)
223                                (declare (ignore buffer window))
224                                (format nil "Hemlock ~A  "
225                                        *hemlock-version*)))
226                 (modeline-field :edit-level)
227                 (modeline-field :completion)))
228  (defhvar "Maximum Modeline Pathname Length"
229    "When set, this variable is the maximum length of the display of a pathname
230     in a modeline.  When the pathname is too long, the :buffer-pathname
231     modeline-field function chops off leading directory specifications until
232     the pathname fits.  \"...\" indicates a truncated pathname."
233    :value nil
234    :hooks (list 'maximum-modeline-pathname-length-hook)))
238;;;; ED.
240(defvar *editor-has-been-entered* ()
241  "True if and only if the editor has been entered.")
242(defvar *in-the-editor* ()
243  "True if we are inside the editor.  This is used to prevent ill-advised
244   \"recursive\" edits.")
246(defvar *after-editor-initializations-funs* nil
247  "A list of functions to be called after the editor has been initialized upon
248   entering the first time.")
250(defmacro after-editor-initializations (&rest forms)
251  "Causes forms to be executed after the editor has been initialized.
252   Forms supplied with successive uses of this macro will be executed after
253   forms supplied with previous uses."
254  `(push #'(lambda () ,@forms)
255         *after-editor-initializations-funs*))
258(defun cl-user::hemlock (&optional x
259                         &key (init t)
260                              (display (hemlock-ext:getenv "DISPLAY")))
261  "Invokes the editor, Hemlock.  If X is supplied and is a symbol, the
262   definition of X is put into a buffer, and that buffer is selected.  If X is
263   a pathname, the file specified by X is visited in a new buffer.  If X is not
264   supplied or Nil, the editor is entered in the same state as when last
265   exited.  When :init is supplied as t (the default), the file
266   \"hemlock-init.lisp\", or \".hemlock-init.lisp\" is loaded from the home
267   directory, but the Lisp command line switch -hinit can be used to specify a
268   different name.  Any compiled version of the source is preferred when
269   choosing the file to load.  If the argument is non-nil and not t, then it
270   should be a pathname that will be merged with the home directory."
271  (when *in-the-editor* (error "You are already in the editor, you bogon!"))
272  (let ((*in-the-editor* t)
273        (display (unless *editor-has-been-entered*
274                   (maybe-load-hemlock-init init)
275                   ;; Device dependent initializaiton.
276                   (init-raw-io display))))
277    (catch 'editor-top-level-catcher
278      (site-wrapper-macro
279       (unless *editor-has-been-entered*
280         ;; Make an initial window, and set up redisplay's internal
281         ;; data structures.
282         (%init-redisplay display)
283         (setq *editor-has-been-entered* t)
284         ;; Pick up user initializations to be done after initialization.
285         (invoke-hook (reverse *after-editor-initializations-funs*)))
286       (catch 'hemlock-exit
287         (catch 'editor-top-level-catcher
288           (cond ((and x (symbolp x))
289                  (let* ((name (nstring-capitalize
290                                (concatenate 'simple-string "Edit " (string x))))
291                         (buffer (or (getstring name *buffer-names*)
292                                     (make-buffer name)))
293                         (*print-case* :downcase))
294                    (delete-region (buffer-region buffer))
295                    (with-output-to-mark
296                        (*standard-output* (buffer-point buffer))
297                      (eval `(grindef ,x))      ; hackish, I know...
298                      (terpri)
299                      (hemlock::change-to-buffer buffer)
300                      (buffer-start (buffer-point buffer)))))
301                 ((or (stringp x) (pathnamep x))
302                  (hemlock::find-file-command () x))
303                 (x
304                  (error
305                   "~S is not a symbol or pathname.  I can't edit it!" x))))
307         (invoke-hook hemlock::entry-hook)
308         (unwind-protect
309           (loop
310            (catch 'editor-top-level-catcher
311              (handler-bind ((error #'(lambda (condition)
312                                        (lisp-error-error-handler condition
313                                                                  :internal))))
314                (invoke-hook hemlock::abort-hook)
315                (%command-loop))))
316           (invoke-hook hemlock::exit-hook)))))))
318(defun maybe-load-hemlock-init (init)
319  (when init
320    (let* ((switch #+NILGB (find "hinit" *command-line-switches*
321                         :test #'string-equal
322                         :key #'cmd-switch-name))
323           (spec-name
324            (if (not (eq init t))
325                init
326                (and switch
327                     (or (cmd-switch-value switch)
328                         (car (cmd-switch-words switch))))))
329           (home (user-homedir-pathname)))
330      (when home
331        (if spec-name
332            (load (merge-pathnames spec-name home) :if-does-not-exist nil)
333            (or (load (merge-pathnames (make-pathname :name "hemlock-init") home)
334                      :if-does-not-exist nil)
335                (load (merge-pathnames (make-pathname :name ".hemlock-init") home)
336                      :if-does-not-exist nil)))))))
341;;; SAVE-ALL-BUFFERS -- Public.
343(defun save-all-buffers (&optional (list-unmodified-buffers nil))
344  "This prompts users with each modified buffer as to whether they want to
345   write it out.  If the buffer has no associated file, this will also prompt
346   for a file name.  Supplying the optional argument non-nil causes this
347   to prompt for every buffer."
348  (dolist (buffer *buffer-list*)
349    (when (or list-unmodified-buffers (buffer-modified buffer))
350      (maybe-save-buffer buffer))))
352(defun maybe-save-buffer (buffer)
353  (let* ((modified (buffer-modified buffer))
354         (pathname (buffer-pathname buffer))
355         (name (buffer-name buffer))
356         (string (if pathname (namestring pathname))))
357    (format t "Buffer ~S is ~:[UNmodified~;modified~], Save it? "
358            name modified)
359    (force-output)
360    (when (y-or-n-p)
361      (let ((name (read-line-default "File to write" string)))
362        (format t "Writing file ~A..." name)
363        (force-output)
364        (write-file (buffer-region buffer) name)
365        (write-line "write WON")))))
367(defun read-line-default (prompt default)
368  (format t "~A:~@[ [~A]~] " prompt default)
369  (force-output)
370  (do ((result (read-line) (read-line)))
371      (())
372    (declare (simple-string result))
373    (when (plusp (length result)) (return result))
374    (when default (return default))
375    (format t "~A:~@[ [~A]~] " prompt default)
376    (force-output)))
378(unless *hemlock-initialized*
379  (%init-hemlock))
Note: See TracBrowser for help on using the repository browser.