source: branches/acode-rewrite/source/cocoa-ide/hemlock/src/views.lisp

Last change on this file was 16082, checked in by Gary Byers, 11 years ago

Merge trunk changes into this branch. Expect some things to explode.

File size: 12.9 KB
RevLine 
[7833]1;;; -*- Mode: Lisp; Package: hemlock-internals -*-
2
3(in-package :hemlock-internals)
4;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
5;;
6;; A HEMLOCK-VIEW represents text displayed in a (pane inside a) window.
7;; Conceptually it consists of a text buffer, a modeline for semi-permanent status
8;; info, an echo area for transient status info, and a text input area for reading
9;; prompted input. Currently the last two are conflated.
10;;
11;; A HEMLOCK-VIEW never changes which text buffer it displays (unlike in emacs). A
12;; text buffer can be displayed in multiple HEMLOCK-VIEW's, although currently there
13;; is no UI to make that happen. But we try take care to distinguish per-buffer info
14;; from per-view info. The former is stored in the buffer struct and in buffer
15;; variables. The latter is currently stored in HEMLOCK-VIEW slots, although I'd
16;; like to introduce user-definable "view variables" and get rid of some of these
17;; user-level slots. [Note: currently, multiple views on a buffer are but a remote
18;; dream. Basic things like the insertion point are still per buffer when they
19;; should be per view]
[16082]20
21;; NOTE: In CCL, it's no longer a dream. See definition
22;; (objc:defmethod (#/duplicate: :void) ((self hemlock-frame) sender)
[7833]23;;
24;; The user interacts with a HEMLOCK-VIEW using events. Each time the user presses a
25;; key, the OS arranges to invoke our event handler. The event handler computes and
26;; invokes a hemlock command bound to the key. The command is invoked in a
27;; dynamic context most suitable for modifying the text buffer associated with the
28;; HEMLOCK-VIEW, but by jumping through a few hoops, it can modify other buffers.
29
30(defvar *current-view* nil)
31
[7933]32(defun current-view (&optional (must-exist t))
33 (or *current-view*
34 (and must-exist (error "Hemlock view context not established"))))
[7844]35
[7833]36(defclass hemlock-view ()
[7862]37 ((pane :initarg :pane :reader hemlock-view-pane)
38 (buffer :initarg :buffer :reader hemlock-view-buffer)
[7833]39 (echo-area-buffer :initarg :echo-area-buffer :reader hemlock-echo-area-buffer)
40 (echo-area-stream :reader hemlock-echo-area-stream)
41
42 ;; Input state
43 (quote-next-p :initform nil :accessor hemlock-view-quote-next-p)
44 (current-command :initform (make-array 10 :fill-pointer 0 :adjustable t)
[7993]45 :accessor hemlock-current-command)
46 (last-command :initform (make-array 10 :fill-pointer 0 :adjustable t)
47 :accessor hemlock-last-command)
[7833]48 (prefix-argument-state :initform (make-prefix-argument-state)
49 :accessor hemlock-prefix-argument-state)
50 ;; If set, events are diverted to the echo area for reading prompt-for-xxx input.
51 (prompted-input-state :initform nil :accessor hemlock-prompted-input-state)
52
53 (cancel-message :initform nil :accessor hemlock-cancel-message)
54
55 ;; User level "view variables", for now give each its own slot.
56 (last-command-type :initform nil :accessor hemlock-last-command-type)
57 (target-column :initform 0 :accessor hemlock-target-column)
58 ))
59
[15019]60(defmethod print-object ((view hemlock-view) stream)
61 (print-unreadable-object (view stream :type t :identity t)
62 (if (slot-boundp view 'buffer)
63 (prin1 (buffer-name (hemlock-view-buffer view)) stream)
64 (princ "???" stream))))
65
[8207]66(defun hemlock-view-p (object)
67 (typep object 'hemlock-view))
68
[7833]69(defmethod initialize-instance ((view hemlock-view) &key)
70 (call-next-method)
71 (with-slots (echo-area-buffer echo-area-stream) view
72 (setf echo-area-stream
73 (make-hemlock-output-stream (buffer-end-mark echo-area-buffer) :full))))
74
75(defun current-prefix-argument-state ()
[7933]76 (hemlock-prefix-argument-state (current-view)))
[7833]77
[7844]78(defun last-key-event-typed ()
79 "This function returns the last key-event typed by the user and read as input."
[7993]80 (let* ((view (current-view))
81 (keys (hemlock-current-command view)))
82 (when (= (length keys) 0) ;; the normal case, when executing a command.
83 (setq keys (hemlock-last-command view)))
84 (when (> (length keys) 0)
85 (aref keys (1- (length keys))))))
[7844]86
87(defun last-char-typed ()
[7993]88 (let ((key (last-key-event-typed)))
89 (and key (key-event-char key))))
[7844]90
[7833]91;; This handles errors in event handling. It assumes it's called in a normal
92;; event handling context for some view.
[8207]93(defun lisp-error-error-handler (condition &key debug-p)
[7844]94 (with-standard-standard-output
95 (handler-case
[7911]96 (progn
[8207]97 (hemlock-ext:report-hemlock-error (current-view) condition debug-p)
[7911]98 (let ((emsg (ignore-errors (princ-to-string condition))))
99 (abort-to-toplevel (or emsg "Error"))))
[7844]100 (error (cc)
101 (ignore-errors (format t "~&Event error handling failed"))
102 (ignore-errors (format t ": ~a" cc))
103 (abort)))))
[7833]104
105
106;; This resets the command accumulation state in the current view.
107(defmethod reset-command-state ()
[7933]108 (let ((view (current-view)))
[7833]109 ;; This resets c-q
110 (setf (hemlock-view-quote-next-p view) nil)
111 ;; This resets c-x (multi-key command) and c-c (modifier prefix command)
112 (setf (fill-pointer (hemlock-current-command view)) 0)
113 ;; This resets the numarg state.
114 (prefix-argument-resetting-state (hemlock-prefix-argument-state view))))
115
116;; This is called for ^G and for lisp errors. It aborts all editor state,
117;; including recursive reading input and incremental search.
118(defun abort-to-toplevel (&optional (message "Cancelled"))
119 ;; This assumes it's called in normal event state.
120 (assert (and *current-view* (find-restart 'exit-event-handler)))
121 (reset-command-state)
122 (invoke-hook hemlock::abort-hook) ;; reset ephemeral modes such as i-search.
[7933]123 (setf (hemlock-cancel-message (current-view)) message)
[7833]124 (let ((eps (current-echo-parse-state :must-exist nil)))
125 (when eps
126 (exit-echo-parse eps :aborted)))
127 (exit-event-handler))
128
129;; Called for editor errors. This aborts command accumulation and i-search,
130;; but not recursive reading of input.
131(defun abort-current-command (&optional (message "Cancelled"))
132 ;; This assumes it's called in normal event state.
133 (assert (and *current-view* (find-restart 'exit-event-handler)))
134 (reset-command-state)
135 (invoke-hook hemlock::abort-hook)
[7933]136 (setf (hemlock-cancel-message (current-view)) message)
[7833]137 (exit-event-handler))
138
139(defun exit-event-handler (&optional message)
140 (when (and *current-view* message)
141 (setf (hemlock-cancel-message *current-view*) message))
142 (let ((restart (find-restart 'exit-event-handler)))
143 (if restart
144 (ccl::invoke-restart-no-return restart)
145 (abort))))
146
[7993]147;; These are only used in event handling, and as such are serialized
148(defparameter *translation-temp-1* (make-array 10 :fill-pointer 0 :adjustable t))
149(defparameter *translation-temp-2* (make-array 10 :fill-pointer 0 :adjustable t))
150
151(defmethod translate-and-lookup-command (keys)
[7833]152 ;; Returns NIL if we're in the middle of a command (either multi-key, as in c-x,
153 ;; or translation prefix, as in ESC for Meta-), else a command.
154 (multiple-value-bind (translated-key prefix-p)
[7993]155 (translate-key keys *translation-temp-1* *translation-temp-2*)
[7833]156 (multiple-value-bind (res t-bindings)
157 (get-current-binding translated-key)
158 (etypecase res
159 (command
160 (values res t-bindings))
161 (hash-table ;; we're part-way through a multi-key command
162 nil)
163 (null
164 (if prefix-p ;; we're part-way through a translation prefix
165 nil
166 (values (get-default-command) nil)))))))
167
[7993]168
[7833]169;; This has a side effect of resetting the quoting state and current command.
170(defmethod get-command-binding-for-key ((view hemlock-view) key)
[7993]171 (let ((current-keys (hemlock-current-command view)))
172 (vector-push-extend key current-keys)
173 (multiple-value-bind (main-binding t-bindings)
174 (if (shiftf (hemlock-view-quote-next-p view) nil)
175 (values (get-self-insert-command) nil)
176 (let ((eps (hemlock-prompted-input-state view)))
177 (or (and eps (eps-parse-key-handler eps))
178 (translate-and-lookup-command current-keys))))
179 (when main-binding
180 (let ((vec (hemlock-last-command view))) ;; reuse vector
181 (setf (hemlock-last-command view) current-keys)
182 (setf (fill-pointer vec) 0)
183 (setf (hemlock-current-command view) vec))
184 (values main-binding t-bindings)))))
[7833]185
186(defvar *last-last-command-type*)
187(defvar *last-prefix-argument*)
188
[7993]189(defun invoke-command (command p)
190 (funcall (command-function command) p))
[7844]191
[7833]192(defmethod execute-hemlock-key ((view hemlock-view) key)
[10614]193 #+debug (log-debug "~&execute-hemlock-key ~s" key)
[7993]194 (with-output-to-listener
195 (if (or (symbolp key) (functionp key))
196 (funcall key)
197 (multiple-value-bind (main-binding transparent-bindings)
198 (get-command-binding-for-key view key)
[10614]199 #+debug (log-debug "~& binding ~s ~s" main-binding transparent-bindings)
[8207]200 (ring-push key *key-event-history*)
[11914]201 ;; If the key represents an "alphabetic" character (of which there
202 ;; are about 94000), and the event has no modifiers or only a shift
203 ;; modifier, treat it if it were bound to "Self Insert".
[12171]204
[11914]205 (when (eq main-binding (get-default-command))
[12315]206 (let* ((modifiers (key-event-bits-modifiers (key-event-bits key)))
207 (char (key-event-char key)))
208 (when (and char
209 (graphic-char-p char)
[11914]210 (or (null modifiers)
211 (equal '("Shift") modifiers)))
212 (setq main-binding (get-self-insert-command)))))
[7993]213 (when main-binding
214 (let* ((*last-last-command-type* (shiftf (hemlock-last-command-type view) nil))
215 (*last-prefix-argument* (hemlock::prefix-argument-resetting-state)))
216 (dolist (binding transparent-bindings)
217 (invoke-command binding *last-prefix-argument*))
218 (invoke-command main-binding *last-prefix-argument*)))))))
[7833]219
[12422]220
221
[7833]222(defmethod update-echo-area-after-command ((view hemlock-view))
223 (let* ((eps (hemlock-prompted-input-state view)))
224 ;;if we're in the process of returning from a recursive parse,
225 ;; don't do anything, let the outer event handle it.
226 (unless (and eps (eps-parse-results eps))
227 (let ((msg (shiftf (hemlock-cancel-message view) nil)))
228 (if msg
[14253]229 (loud-message "~a" msg)
[7833]230 ;; Echo command in progress if there is one, unless in a recursive parse
231 (unless eps
232 (let ((cmd (hemlock-current-command view)))
233 (unless (eql 0 (length cmd))
[7993]234 (let ((cstr (concatenate 'string (pretty-key-string cmd) " ")))
[7833]235 (message cstr))))))))))
236
[7862]237(defmethod hemlock-view-current-buffer ((view hemlock-view))
238 (if (hemlock-prompted-input-state view)
239 (hemlock-echo-area-buffer view)
240 (hemlock-view-buffer view)))
241
[7919]242(defun buffer-modification-state (buffer)
243 (multiple-value-bind (start end) (buffer-selection-range buffer)
244 (list* (buffer-signature buffer) start end)))
245
[7933]246(defvar *next-view-start* nil)
247
248(defun set-scroll-position (how &optional where)
249 "Set the desired scroll position of the current view"
250 (when (markp where)
[8207]251 (unless (eq (mark-buffer where)
252 (hemlock-view-buffer (current-view)))
253 (error "~s is not a mark in the current view." where))
[7933]254 (setq where (mark-absolute-position where)))
255 (setf *next-view-start* (cons how where)))
256
[7833]257(defmethod handle-hemlock-event ((view hemlock-view) key)
258 ;; Key can also be a function, in which case it will get executed in the view event context
[10614]259 #+debug (log-debug "handle-hemlock-event ~s~:[~; (recursive)~]"
[7911]260 key
261 (and (eq view *current-view*)
262 (eq (hemlock-view-current-buffer view) *current-buffer*)))
263 (if (and (eq view *current-view*)
264 (eq (hemlock-view-current-buffer view) *current-buffer*))
265 ;; KLUDGE: This might happen with stuff that normally switches buffers (e.g. meta-.)
266 ;; but happens not to. Because of the stupid buffer binding/unbinding, it's currently
267 ;; problematic to just recurse here, so don't.
268 (progn
269 ;; TODO: should this catch exit-event or let outer one do it? Check callers.
270 (execute-hemlock-key view key)
271 )
272 (ccl::with-standard-abort-handling "Abort editor event handling"
273 (let* ((*current-view* view)
274 (*current-buffer* (hemlock-view-current-buffer view))
[7933]275 (*next-view-start* nil) ;; gets set by scrolling commands
[7919]276 (text-buffer (hemlock-view-buffer view))
277 (mod (buffer-modification-state text-buffer)))
[8062]278 (modifying-buffer-storage (*current-buffer*)
279 (restart-case
[8207]280 (handler-bind ((error #'(lambda (c)
281 (lisp-error-error-handler c :debug-p t))))
[8062]282 (execute-hemlock-key view key))
283 (exit-event-handler () :report "Exit from hemlock event handler")))
284 ;; Update display
285 (if *next-view-start*
286 (destructuring-bind (how . where) *next-view-start*
287 (hemlock-ext:scroll-view view how where))
288 (unless (equal mod (buffer-modification-state text-buffer))
289 ;; Modified buffer, make sure user sees what happened
290 (hemlock-ext:ensure-selection-visible view)))
291 (update-echo-area-after-command view)))))
Note: See TracBrowser for help on using the repository browser.