Changeset 707


Ignore:
Timestamp:
Mar 22, 2004, 9:41:04 AM (21 years ago)
Author:
Gary Byers
Message:

Process Hemlock commands in a per-frame thread.

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/ccl/examples/cocoa-editor.lisp

    r678 r707  
    177177;;; Ask Hemlock to count the characters in the buffer.
    178178(defun hemlock-buffer-length (buffer)
    179   (hemlock::count-characters (hemlock::buffer-region buffer)))
     179  (hi::with-buffer-gap-info (buffer)
     180    (hemlock::count-characters (hemlock::buffer-region buffer))))
    180181
    181182;;; Find the line containing (or immediately preceding) index, which is
     
    183184;;; in that line or the trailing #\newline, as appropriate.
    184185(defun hemlock-char-at-index (cache index)
    185   (multiple-value-bind (line idx) (update-line-cache-for-index cache index)
    186     (let* ((len (hemlock::line-length line)))
    187       (if (< idx len)
    188         (hemlock::line-character line idx)
    189         #\newline))))
     186  (hi::with-buffer-gap-info ((buffer-cache-buffer cache))
     187    (multiple-value-bind (line idx) (update-line-cache-for-index cache index)
     188      (let* ((len (hemlock::line-length line)))
     189        (if (< idx len)
     190          (hemlock::line-character line idx)
     191          #\newline)))))
    190192
    191193;;; Given an absolute position, move the specified mark to the appropriate
    192194;;; offset on the appropriate line.
    193195(defun move-hemlock-mark-to-absolute-position (mark cache abspos)
    194   (multiple-value-bind (line idx) (update-line-cache-for-index cache abspos)
    195     (hemlock::move-to-position mark idx line)))
     196  (hi::with-buffer-gap-info ((buffer-cache-buffer cache))
     197    (multiple-value-bind (line idx) (update-line-cache-for-index cache abspos)
     198      (hemlock::move-to-position mark idx line))))
    196199
    197200;;; Return the absolute position of the mark in the containing buffer.
     
    199202;;; number of preceding lines.
    200203(defun mark-absolute-position (mark)
    201   (let* ((pos (hemlock::mark-charpos mark)))
    202     (do* ((line (hemlock::line-previous (hemlock::mark-line mark))
    203                 (hemlock::line-previous line)))
    204          ((null line) pos)
    205       (incf pos (1+ (hemlock::line-length line))))))
     204  (hi::with-buffer-gap-info ((hi::line-%buffer (hi::mark-line mark)))
     205    (let* ((pos (hi::mark-charpos mark)))
     206      (do* ((line (hi::line-previous (hi::mark-line mark))
     207                  (hi::line-previous line)))
     208           ((null line) pos)
     209        (incf pos (1+ (hi::line-length line)))))))
    206210
    207211;;; Return the length of the abstract string, i.e., the number of
     
    212216    (or (buffer-cache-buflen cache)
    213217        (setf (buffer-cache-buflen cache)
    214               (hemlock-buffer-length (buffer-cache-buffer cache))))))
     218              (let* ((buffer (buffer-cache-buffer cache)))
     219                (hi::with-buffer-gap-info (buffer)
     220                  (hemlock-buffer-length buffer)))))))
    215221
    216222
     
    232238         (external-format (if buffer (hi::buffer-external-format buffer )))
    233239         (raw-length (if buffer (hemlock-buffer-length buffer) 0)))
     240   
    234241    (if (eql 0 raw-length)
    235242      (make-objc-instance 'ns:ns-mutable-data :with-length 0)
     
    282289
    283290
    284 ;;; Lisp-text-storage objects
    285 (defclass lisp-text-storage (ns:ns-text-storage)
     291;;; hemlock-text-storage objects
     292(defclass hemlock-text-storage (ns:ns-text-storage)
    286293    ((string :foreign-type :id))
    287294  (:metaclass ns:+ns-object))
     
    289296;;; Access the string.  It'd be nice if this was a generic function;
    290297;;; we could have just made a reader method in the class definition.
    291 (define-objc-method ((:id string) lisp-text-storage)
     298(define-objc-method ((:id string) hemlock-text-storage)
    292299  (slot-value self 'string))
    293300
    294 (define-objc-method ((:id :init-with-string s) lisp-text-storage)
     301(define-objc-method ((:id :init-with-string s) hemlock-text-storage)
    295302  (let* ((newself (send-super 'init)))
    296303    (setf (slot-value newself 'string) s)
     
    298305
    299306;;; This is the only thing that's actually called to create a
    300 ;;; lisp-text-storage object.  (It also creates the underlying
     307;;; hemlock-text-storage object.  (It also creates the underlying
    301308;;; hemlock-buffer-string.)
    302309(defun make-textstorage-for-hemlock-buffer (buffer)
    303   (make-objc-instance 'lisp-text-storage
     310  (make-objc-instance 'hemlock-text-storage
    304311                      :with-string
    305312                      (make-instance
     
    314321(define-objc-method ((:id :attributes-at-index (:unsigned index)
    315322                          :effective-range ((* :<NSR>ange) rangeptr))
    316                      lisp-text-storage)
     323                     hemlock-text-storage)
    317324  (declare (ignorable index))
    318325  (let* ((buffer-cache (hemlock-buffer-string-cache (slot-value self 'string)))
     
    328335(define-objc-method ((:void :replace-characters-in-range (:<NSR>ange r)
    329336                            :with-string string)
    330                      lisp-text-storage)
     337                     hemlock-text-storage)
    331338  (#_NSLog #@"replace-characters-in-range (%d %d) with-string %@"
    332339           :unsigned (pref r :<NSR>ange.location)
     
    338345(define-objc-method ((:void :set-attributes attributes
    339346                            :range (:<NSR>ange r))
    340                      lisp-text-storage)
     347                     hemlock-text-storage)
    341348  (#_NSLog #@"set-attributes %@ range (%d %d)"
    342349           :id attributes
     
    344351           :unsigned (pref r :<NSR>ange.length)))
    345352
     353(defun for-each-textview-using-storage (textstorage f)
     354  (let* ((layouts (send textstorage 'layout-managers)))
     355    (unless (%null-ptr-p layouts)
     356      (dotimes (i (send layouts 'count))
     357        (let* ((layout (send layouts :object-at-index i))
     358               (containers (send layout 'text-containers)))
     359          (unless (%null-ptr-p containers)
     360            (dotimes (j (send containers 'count))
     361              (let* ((container (send containers :object-at-index j))
     362                     (tv (send container 'text-view)))
     363                (funcall f tv)))))))))
    346364
    347365;;; Again, it's helpful to see the buffer name when debugging.
    348366(define-objc-method ((:id description)
    349                      lisp-text-storage)
     367                     hemlock-text-storage)
    350368  (send (@class ns-string) :string-with-format #@"%s : string %@"
    351369        (:address (#_object_getClassName self) :id (slot-value self 'string))))
     370
     371;;; This needs to happen on the main thread.
     372(define-objc-method ((:void ensure-selection-visible)
     373                     hemlock-text-storage)
     374  (for-each-textview-using-storage
     375   self
     376   #'(lambda (tv)
     377       (send tv :scroll-range-to-visible (send tv 'selected-range)))))
     378
     379;;; This needs to run on the main thread.
     380(define-objc-method ((void update-hemlock-selection)
     381                     hemlock-text-storage)
     382    (let* ((string (send self 'string))
     383           (buffer (buffer-cache-buffer (hemlock-buffer-string-cache string)))
     384           (point (hi::buffer-point buffer))
     385           (pos (mark-absolute-position point)))
     386      (for-each-textview-using-storage
     387       self
     388       #'(lambda (tv)
     389           (slet ((selection (ns-make-range pos 0)))
     390                 (send tv :set-selected-range selection))))))
     391
     392
    352393
    353394(defun close-hemlock-textstorage (ts)
     
    418459  (let* ((buffer (text-view-buffer self)))
    419460    (when buffer
    420       (let* ((info (hemlock-frame-command-info (send self 'window))))
    421         (when info
    422           (let* ((key-event (nsevent-to-key-event event)))
    423             (when event
    424               (unless (eq buffer hi::*current-buffer*)
    425                 (setf (hi::current-buffer) buffer))
    426               (let* ((pane (text-view-pane self)))
    427                 (unless (eql pane (hi::current-window))
    428                   (setf (hi::current-window) pane)))
    429               #+debug
    430               (format t "~& key-event = ~s" key-event)
    431               (let* ((w (send self 'window))
    432                      (hi::*echo-area-buffer* (hemlock-frame-echo-area-buffer w))
    433                      (hi::*echo-area-stream*
    434                       (hemlock-frame-echo-area-stream w))
    435                      (hi::*echo-area-window* (slot-value w 'echo-area-view ))
    436                      (hi::*echo-area-region*
    437                       (hi::buffer-region hi::*echo-area-buffer*)))
    438                 (hi::interpret-key-event key-event info)))))))))
     461      (let* ((q (hemlock-frame-event-queue (send self 'window))))
     462        (hi::enqueue-key-event q (nsevent-to-key-event event)))))
     463  ;; Probably not the right place for this, but needs to happen
     464  ;; -somewhere-, and needs to happen in the event thread.
     465  (send self :scroll-range-to-visible (send self 'selected-range))
     466  )
     467
     468(defun enqueue-buffer-operation (buffer thunk)
     469  (dolist (w (hi::buffer-windows buffer))
     470    (let* ((q (hemlock-frame-event-queue (send w 'window)))
     471           (op (hi::make-buffer-operation :thunk thunk)))
     472      (hi::enqueue-key-event q op))))
     473
    439474 
    440 ;;; Process a key-down NSEvent in a lisp text view by translating it
     475;;; Process a key-down NSEvent in a Hemlock text view by translating it
    441476;;; into a Hemlock key event and passing it into the Hemlock command
    442477;;; interpreter.  The underlying buffer becomes Hemlock's current buffer
     
    709744    (send hemlock-frame :make-first-responder view)))
    710745
     746(defmethod text-view-buffer ((self echo-area-view))
     747  (buffer-cache-buffer (hemlock-buffer-string-cache (send (send self 'text-storage) 'string))))
     748
    711749;;; The "document" for an echo-area isn't a real NSDocument.
    712750(defclass echo-area-document (ns:ns-object)
     
    732770                                             (incf *hemlock-frame-count*)))
    733771                                   :modes '("Echo Area")))
    734            (stream (hi::make-hemlock-output-stream
    735                     (hi::region-end (hi::buffer-region buffer)) :full))
    736772           (textstorage (make-textstorage-for-hemlock-buffer buffer))
    737773           (doc (make-objc-instance 'echo-area-document))
     
    756792        (send container :set-height-tracks-text-view nil)
    757793        (setf (hemlock-frame-echo-area-buffer hemlock-frame) buffer
    758               (hemlock-frame-echo-area-stream hemlock-frame) stream
    759794              (slot-value doc 'textstorage) textstorage
    760795              (hi::buffer-document buffer) doc)
     
    769804        echo-area))))
    770805               
    771        
    772 (defmethod hemlock-frame-command-info ((w ns:ns-window))
    773   nil)
    774 
    775 
    776806(defclass hemlock-frame (ns:ns-window)
    777807    ((echo-area-view :foreign-type :id)
    778      (command-info :initform (hi::make-command-interpreter-info)
    779                    :accessor hemlock-frame-command-info)
     808     (event-queue :initform (ccl::init-dll-header (hi::make-frame-event-queue))
     809                  :reader hemlock-frame-event-queue)
     810     (command-thread :initform nil)
    780811     (echo-area-buffer :initform nil :accessor hemlock-frame-echo-area-buffer)
    781812     (echo-area-stream :initform nil :accessor hemlock-frame-echo-area-stream))
    782813  (:metaclass ns:+ns-object))
    783814
    784 
    785 (defmethod shared-initialize :after ((w hemlock-frame)
    786                                      slot-names
    787                                      &key &allow-other-keys)
    788   (declare (ignore slot-names))
    789   (let ((info (hemlock-frame-command-info w)))
    790     (when info
    791       (setf (hi::command-interpreter-info-frame info) w))))
    792 
    793 
    794 
    795 
    796 
    797 
     815(defun hemlock-thread-function (q buffer pane echo-buffer echo-window)
     816  (let* ((hi::*real-editor-input* q)
     817         (hi::*editor-input* q)
     818         (hi::*current-buffer* hi::*current-buffer*)
     819         (hi::*current-window* pane)
     820         (hi::*echo-area-window* echo-window)
     821         (hi::*echo-area-buffer* echo-buffer)
     822         (region (hi::buffer-region echo-buffer))
     823         (hi::*echo-area-region* region)
     824         (hi::*echo-area-stream* (hi::make-hemlock-output-stream
     825                              (hi::region-end region) :full))
     826         (hi::*cache-modification-tick* -1)
     827         (hi::now-tick 0)
     828         (hi::*disembodied-buffer-counter* 0)
     829         (hi::*in-a-recursive-edit* nil)
     830         (hi::*last-key-event-typed* nil)
     831         (hi::*input-transcript* nil)
     832         (hi::*line-cache-length* 200)
     833         (hi::*open-line* nil)
     834         (hi::*open-chars* (make-string hi::*line-cache-length* ))
     835         (hi::*left-open-pos* 0)
     836         (hi::*right-open-pos* 0)
     837         (hemlock::*target-column* 0)
     838         (hemlock::*last-comment-start* 0)
     839         (hemlock::*last-search-string* ())
     840         (hemlock::*last-search-pattern*
     841            (hemlock::new-search-pattern :string-insensitive :forward "Foo"))
     842         )
     843    (setf (hi::current-buffer) buffer)
     844    (hi::%command-loop)))
     845
     846
     847(define-objc-method ((:void close) hemlock-frame)
     848  (let* ((proc (slot-value self 'command-thread)))
     849    (when proc
     850      (setf (slot-value self 'command-thread) nil)
     851      (process-kill proc)))
     852  (send-super 'close))
     853 
    798854(defun new-hemlock-document-window ()
    799855  (let* ((w (new-cocoa-window :class (find-class 'hemlock-frame)
     
    915971;;; This function must run in the main event thread.
    916972(defun %hemlock-frame-for-textstorage (ts ncols nrows container-tracks-text-view-width)
    917   (let* ((pane (textpane-for-textstorage ts ncols nrows container-tracks-text-view-width)))
    918     (send pane 'window)))
     973  (let* ((pane (textpane-for-textstorage ts ncols nrows container-tracks-text-view-width))
     974         (frame (send pane 'window))
     975         (buffer (text-view-buffer (text-pane-text-view pane))))
     976    (setf (slot-value frame 'command-thread)
     977          (process-run-function (format nil "Hemlock window thread")
     978                                #'(lambda ()
     979                                    (hemlock-thread-function
     980                                     (hemlock-frame-event-queue frame)
     981                                     buffer
     982                                     pane
     983                                     (hemlock-frame-echo-area-buffer frame)
     984                                     (slot-value frame 'echo-area-view)))))
     985    frame))
     986         
     987   
    919988
    920989
     
    925994
    926995
    927 (defun for-each-textview-using-storage (textstorage f)
    928   (let* ((layouts (send textstorage 'layout-managers)))
    929     (unless (%null-ptr-p layouts)
    930       (dotimes (i (send layouts 'count))
    931         (let* ((layout (send layouts :object-at-index i))
    932                (containers (send layout 'text-containers)))
    933           (unless (%null-ptr-p containers)
    934             (dotimes (j (send containers 'count))
    935               (let* ((container (send containers :object-at-index j))
    936                      (tv (send container 'text-view)))
    937                 (funcall f tv)))))))))
    938996
    939997
    940998 
    941999(defun hi::document-begin-editing (document)
    942   (send (slot-value document 'textstorage) 'begin-editing))
     1000  (send (slot-value document 'textstorage)
     1001        :perform-selector-on-main-thread
     1002        (@selector "beginEditing")
     1003        :with-object (%null-ptr)
     1004        :wait-until-done t))
     1005
     1006
    9431007
    9441008(defun hi::document-end-editing (document)
     1009  (send (slot-value document 'textstorage)
     1010        :perform-selector-on-main-thread
     1011        (@selector "endEditing")
     1012        :with-object (%null-ptr)
     1013        :wait-until-done t))
     1014
     1015(defun hi::document-set-point-position (document)
    9451016  (let* ((textstorage (slot-value document 'textstorage)))
    946     (send textstorage 'end-editing)
    947     (for-each-textview-using-storage
    948      textstorage
    949      #'(lambda (tv)
    950          (send tv :scroll-range-to-visible (send tv 'selected-range))))))
    951 
    952 (defun hi::document-set-point-position (document)
    953   (let* ((textstorage (slot-value document 'textstorage))
    954          (string (send textstorage 'string))
    955          (buffer (buffer-cache-buffer (hemlock-buffer-string-cache string)))
    956          (point (hi::buffer-point buffer))
    957          (pos (mark-absolute-position point)))
    958     (for-each-textview-using-storage
    959      textstorage
    960      #'(lambda (tv)
    961          (slet ((selection (ns-make-range pos 0)))
    962           (send tv :set-selected-range selection))))))
     1017    (send textstorage
     1018          :perform-selector-on-main-thread
     1019          (@selector "updateHemlockSelection")
     1020          :with-object (%null-ptr)
     1021          :wait-until-done t)))
    9631022
    9641023
     
    10601119                                   
    10611120 
    1062 (defclass lisp-editor-window-controller (ns:ns-window-controller)
     1121(defclass hemlock-editor-window-controller (ns:ns-window-controller)
    10631122    ()
    10641123  (:metaclass ns:+ns-object))
    10651124
    10661125   
    1067 ;;; The LispEditorWindowController is the textview's "delegate": it
    1068 ;;; gets consulted before certain actions are performed, and can
    1069 ;;; perform actions on behalf of the textview.
    1070 
    1071 
    1072 
    1073 ;;; The LispEditorDocument class.
    1074 
    1075 
    1076 (defclass lisp-editor-document (ns:ns-document)
     1126
     1127
     1128;;; The HemlockEditorDocument class.
     1129
     1130
     1131(defclass hemlock-editor-document (ns:ns-document)
    10771132    ((textstorage :foreign-type :id))
    10781133  (:metaclass ns:+ns-object))
    10791134
    1080 (define-objc-method ((:id init) lisp-editor-document)
     1135(define-objc-method ((:id init) hemlock-editor-document)
    10811136  (let* ((doc (send-super 'init)))
    10821137    (unless (%null-ptr-p doc)
    10831138      (let* ((buffer (make-hemlock-buffer
    10841139                      (lisp-string-from-nsstring (send doc 'display-name))
    1085                       :modes '("Lisp"))))
     1140                      :modes '("Lisp" "Editor"))))
    10861141        (setf (slot-value doc 'textstorage)
    10871142              (make-textstorage-for-hemlock-buffer buffer)
     
    10921147(define-objc-method ((:id :read-from-file filename
    10931148                          :of-type type)
    1094                      lisp-editor-document)
     1149                     hemlock-editor-document)
    10951150  (declare (ignorable type))
    10961151  (let* ((pathname (lisp-string-from-nsstring filename))
     
    11301185        (when cache (buffer-cache-buffer cache))))))
    11311186
    1132 (defmethod hi::document-panes ((document lisp-editor-document))
     1187(defmethod hi::document-panes ((document hemlock-editor-document))
    11331188  (let* ((ts (slot-value document 'textstorage))
    11341189         (panes ()))
     
    11421197
    11431198(define-objc-method ((:id :data-representation-of-type type)
    1144                       lisp-editor-document)
     1199                      hemlock-editor-document)
    11451200  (declare (ignorable type))
    11461201  (let* ((buffer (hemlock-document-buffer self)))
     
    11551210;;; name and pathname in synch with the document.
    11561211(define-objc-method ((:void :set-file-name full-path)
    1157                      lisp-editor-document)
     1212                     hemlock-editor-document)
    11581213  (send-super :set-file-name full-path)
    11591214  (let* ((buffer (hemlock-document-buffer self)))
     
    11631218        (setf (hi::buffer-pathname buffer) new-pathname)))))
    11641219 
    1165 (define-objc-method ((:void make-window-controllers) lisp-editor-document)
     1220(define-objc-method ((:void make-window-controllers) hemlock-editor-document)
    11661221  (let* ((controller (make-objc-instance
    1167                       'lisp-editor-window-controller
     1222                      'hemlock-editor-window-controller
    11681223                      :with-window (%hemlock-frame-for-textstorage
    11691224                                    (slot-value self 'textstorage)
     
    11741229    (send controller 'release)))         
    11751230
    1176 #|
    1177 (define-objc-method ((:void :window-controller-did-load-nib acontroller)
    1178                      lisp-editor-document)
    1179   (send-super :window-controller-did-load-nib  acontroller)
    1180   ;; Apple/NeXT thinks that adding extra whitespace around cut & pasted
    1181   ;; text is "smart".  Really, really smart insertion and deletion
    1182   ;; would alphabetize the selection for you (byChars: or byWords:);
    1183   ;; sadly, if you want that behavior you'll have to do it yourself.
    1184   ;; Likewise with the extra spaces.
    1185   (with-slots (text-view echoarea packagename filedata) self
    1186     (send text-view :set-alignment  #$NSNaturalTextAlignment)
    1187     (send text-view :set-smart-insert-delete-enabled nil)
    1188     (send text-view :set-rich-text nil)
    1189     (send text-view :set-uses-font-panel t)
    1190     (send text-view :set-uses-ruler nil)
    1191     (with-lock-grabbed (*open-editor-documents-lock*)
    1192       (push (make-cocoa-editor-info
    1193              :document (%setf-macptr (%null-ptr) self)
    1194              :controller (%setf-macptr (%null-ptr) acontroller)
    1195              :listener nil)
    1196             *open-editor-documents*))
    1197     (setf (slot-value acontroller 'textview) text-view
    1198           (slot-value acontroller 'echoarea) echoarea
    1199           (slot-value acontroller 'packagename) packagename)
    1200     (send text-view :set-delegate acontroller)
    1201     (let* ((font (default-font)))
    1202       (multiple-value-bind (height width)
    1203           (size-of-char-in-font font)
    1204         (size-textview-containers text-view height width 24 80))
    1205       (send text-view
    1206             :set-typing-attributes
    1207             (create-text-attributes
    1208              :font font
    1209              :color (send (@class ns-color) 'black-color)))
    1210       (unless (%null-ptr-p filedata)
    1211         (send text-view
    1212               :replace-characters-in-range (ns-make-range 0 0)
    1213               :with-string (make-objc-instance
    1214                             'ns-string
    1215                             :with-data filedata
    1216                             :encoding #$NSASCIIStringEncoding))
    1217 ))))
    1218 |#
    1219 
    1220 (define-objc-method ((:void close) lisp-editor-document)
     1231
     1232(define-objc-method ((:void close) hemlock-editor-document)
    12211233  (let* ((textstorage (slot-value self 'textstorage)))
    12221234    (setf (slot-value self 'textstorage) (%null-ptr))
     
    12371249        (send textview :page-up nil)))))
    12381250
    1239 (defun hi::get-key-event (text-view ignore)
    1240   (declare (ignore ignore))
    1241   (let* ((event (send (send text-view 'window)
    1242                       :next-event-matching-mask #$NSKeyDownMask)))
    1243     (nsevent-to-key-event event)))
     1251
     1252(defun hi::allocate-temporary-object-pool ()
     1253  (create-autorelease-pool))
     1254
     1255(defun hi::free-temporary-objects (pool)
     1256  (release-autorelease-pool pool))
    12441257
    12451258(provide "COCOA-EDITOR")
Note: See TracChangeset for help on using the changeset viewer.