[mcclim-cvs] CVS mcclim/Drei

thenriksen thenriksen at common-lisp.net
Thu Jan 3 12:32:08 UTC 2008


Update of /project/mcclim/cvsroot/mcclim/Drei
In directory clnet:/tmp/cvs-serv3433/Drei

Modified Files:
	fundamental-syntax.lisp lisp-syntax.lisp lr-syntax.lisp 
	packages.lisp 
Log Message:
Added syntax highlighting of Lisp syntax. Yay!

Doesn't highlight fully as much as it used to, as it's slightly more
complicated to get fast enough.

Also, not terribly heavily optimized.


--- /project/mcclim/cvsroot/mcclim/Drei/fundamental-syntax.lisp	2008/01/02 14:43:40	1.8
+++ /project/mcclim/cvsroot/mcclim/Drei/fundamental-syntax.lisp	2008/01/03 12:32:08	1.9
@@ -54,11 +54,17 @@
 (defclass line-object ()
   ((%start-mark :reader start-mark
                 :initarg :start-mark)
+   (%line-length :reader line-length
+                 :initarg :line-length)
    (%chunks :accessor chunks
             :initform (make-array 5
                        :adjustable t
                        :fill-pointer 0))))
 
+(defun line-end-offset (line)
+  "Return the end buffer offset of `line'."
+  (+ (offset (start-mark line)) (line-length line)))
+
 (defun get-chunk (buffer chunk-start-offset line-end-offset)
   (let* ((chunk-end-offset (buffer-find-nonchar
                             buffer chunk-start-offset
@@ -116,14 +122,16 @@
           (setf (offset scan) (offset low-mark))
           (loop while (mark<= scan high-mark)
              for i from low-index
-             do (progn (insert* lines i (make-instance
-                                         'line-object
-                                         :start-mark (clone-mark scan)))
-                       (end-of-line scan)
-                       (if (end-of-buffer-p scan)
-                           (loop-finish)
-                           ;; skip newline
-                           (forward-object scan)))))))))
+             do (progn (let ((line-start-mark (clone-mark scan)))
+                         (insert* lines i (make-instance
+                                           'line-object
+                                           :start-mark line-start-mark
+                                           :line-length (- (offset (end-of-line scan))
+                                                           (offset line-start-mark))))
+                         (if (end-of-buffer-p scan)
+                             (loop-finish)
+                             ;; skip newline
+                             (forward-object scan))))))))))
 		
 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 ;;;
@@ -195,7 +203,32 @@
 
 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 ;;;
-;;; exploit the parse 
+;;; exploit the parse
+
+(defun offset-in-line-p (line offset)
+  "Return true if `offset' is in the buffer region delimited by
+`line'."
+  (<= (offset (start-mark line)) offset
+      (line-end-offset line)))
+
+(defun line-containing-offset (syntax mark-or-offset)
+  "Return the line `mark-or-offset' is in for `syntax'. `Syntax'
+must be a `fundamental-syntax' object."
+  ;; Perform binary search looking for line containing `offset1'.
+  (as-offsets ((offset mark-or-offset))
+    (with-accessors ((lines lines)) syntax
+      (loop with low-index = 0
+         with high-index = (nb-elements lines)
+         for middle = (floor (+ low-index high-index) 2)
+         for this-line = (element* lines middle)
+         for line-start = (start-mark this-line)
+         do (cond ((offset-in-line-p this-line offset)
+                   (loop-finish))
+                  ((mark> offset line-start)
+                   (setf low-index (1+ middle)))
+                  ((mark< offset line-start)
+                   (setf high-index middle)))
+         finally (return this-line)))))
 
 ;; do this better
 (defmethod syntax-line-indentation ((syntax fundamental-syntax) mark tab-width)
--- /project/mcclim/cvsroot/mcclim/Drei/lisp-syntax.lisp	2008/01/02 14:21:06	1.44
+++ /project/mcclim/cvsroot/mcclim/Drei/lisp-syntax.lisp	2008/01/03 12:32:08	1.45
@@ -147,6 +147,9 @@
     (or (image syntax)
         (default-image))))
 
+(defconstant +keyword-package+ (find-package :keyword)
+  "The KEYWORD package.")
+
 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 ;;;
 ;;; Swank interface functions.
@@ -1479,6 +1482,39 @@
     (or (typep (parent form) 'form*)
         (null (parent form)))))
 
+(defgeneric eval-feature-conditional (conditional-form syntax))
+
+(defmethod eval-feature-conditional (conditional-form (syntax lisp-syntax))
+  nil)
+
+;; Adapted from slime.el
+
+(defmethod eval-feature-conditional ((conditional token-mixin) (syntax lisp-syntax))
+  (let* ((string (form-string syntax conditional))
+	 (symbol (parse-symbol string :package +keyword-package+)))
+    (member symbol *features*)))
+
+(defmethod eval-feature-conditional ((conditional list-form) (syntax lisp-syntax))
+  (let ((children (children conditional)))
+    (when (third-noncomment children)
+      (flet ((eval-fc (conditional)
+	       (funcall #'eval-feature-conditional conditional syntax)))
+	(let* ((type (second-noncomment children))
+	       (conditionals  (butlast
+			       (nthcdr
+				2
+				(remove-if
+				 #'comment-p
+				 children))))
+	       (type-string (form-string syntax type))
+	       (type-symbol (parse-symbol type-string :package +keyword-package+)))
+	  (case type-symbol
+	    (:and (funcall #'every #'eval-fc conditionals))
+	    (:or (funcall #'some #'eval-fc conditionals))
+	    (:not (when conditionals
+		    (funcall #'(lambda (f l) (not (apply f l)))
+			     #'eval-fc conditionals)))))))))
+
 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 ;;;
 ;;; Asking about parse state at some point
@@ -1731,242 +1767,22 @@
 ;;;
 ;;; display
 
-(defparameter *reader-conditional-faces*
-  (list (make-face :error +red+)
-        (make-face :string +gray50+ (make-text-style nil :italic nil))
-        (make-face :keyword +gray50+)
-        (make-face :macro +gray50+)
-        (make-face :special-form +gray50+)
-        (make-face :lambda-list-keyword +gray50+)
-        (make-face :comment +gray50+)
-        (make-face :reader-conditional +gray50+)))
-
-(define-standard-faces lisp-syntax
-  (make-face :error +red+)
-  (make-face :string +rosy-brown+ (make-text-style nil :italic nil))
-  (make-face :keyword +orchid+)
-  (make-face :macro +purple+)
-  (make-face :special-form +purple+)
-  (make-face :lambda-list-keyword +dark-green+)
-  (make-face :comment +maroon+)
-  (make-face :reader-conditional +gray50+))
-
-(defmethod display-parse-tree ((parse-symbol (eql nil)) stream (view textual-drei-syntax-view)
-                               (syntax lisp-syntax))
-  nil)
-
-(defmethod display-parse-tree ((parse-symbol error-symbol) stream
-                               (view textual-drei-syntax-view) (syntax lisp-syntax))
-  (let ((children (children parse-symbol)))
-    (loop until (or (null (cdr children))
-		    (typep (parser-state (cadr children)) 'error-state))
-	  do (display-parse-tree (pop children) stream view syntax))
-    (if (and (null (cdr children))
-	     (not (typep (parser-state parse-symbol) 'error-state)))
-	(display-parse-tree (car children) stream view syntax)
-	(with-face (:error)
-	  (loop for child in children
-		do (display-parse-tree child stream view syntax))))))
-
-(defmethod display-parse-tree ((parse-symbol error-lexeme) stream
-                               (view textual-drei-syntax-view) (syntax lisp-syntax))
-  (with-face (:error)
-    (call-next-method)))
-
-(defmethod display-parse-tree ((parse-symbol unmatched-right-parenthesis-lexeme)
-			       stream (view textual-drei-syntax-view) (syntax lisp-syntax))
-  (with-face (:error)
-    (call-next-method)))
-
-(defmethod display-parse-tree ((parse-symbol token-mixin) stream
-                               (view textual-drei-syntax-view) (syntax lisp-syntax))
-  (if (> (the fixnum (end-offset parse-symbol)) (the fixnum (start-offset parse-symbol)))
-      (let ((symbol (form-to-object syntax parse-symbol :no-error t)))
-        (with-output-as-presentation (stream symbol 'symbol :single-box :highlighting)
-          (cond ((eql (buffer-object (buffer syntax) (start-offset parse-symbol)) #\:)
-                 (with-face (:keyword)
-                   (call-next-method)))
-                ((eql (buffer-object (buffer syntax) (start-offset parse-symbol)) #\&)
-                 (with-face (:lambda-list-keyword)
-                   (call-next-method)))
-                ((and (symbolp symbol)
-                      (macro-function symbol)
-                      (form-operator-p syntax parse-symbol))
-                 (with-face (:macro)
-                   (call-next-method)))
-                ((and (symbolp symbol)
-                      (special-operator-p symbol)
-                      (form-operator-p syntax parse-symbol))
-                 (with-face (:special-form)
-                   (call-next-method)))
-                (t (call-next-method)))))
-      (call-next-method)))
-
-(defmethod display-parse-tree ((parser-symbol literal-object-form) stream (view textual-drei-syntax-view)
-                               (syntax lisp-syntax))
-  (updating-output
-      (stream :unique-id (list view parser-symbol)
-              :id-test #'equal
-              :cache-value parser-symbol
-              :cache-test #'eql)
-    (let ((object (form-to-object syntax parser-symbol)))
-      (present object (presentation-type-of object) :stream stream))))
-
-(defmethod display-parse-tree ((parser-symbol lisp-lexeme) stream (view textual-drei-syntax-view)
-                               (syntax lisp-syntax))
-  (flet ((cache-test (t1 t2)
-           (and (eq t1 t2)
-                (eq (slot-value t1 'ink)
-                    (medium-ink (sheet-medium stream)))
-                (eq (slot-value t1 'face)
-                    (text-style-face (medium-text-style (sheet-medium stream)))))))
-    (updating-output
-        (stream :unique-id (list view parser-symbol)
-                :id-test #'equal
-                :cache-value parser-symbol
-                :cache-test #'cache-test)
-      (with-slots (ink face) parser-symbol
-        (setf ink (medium-ink (sheet-medium stream))
-              face (text-style-face (medium-text-style (sheet-medium stream))))
-        (write-string (form-string syntax parser-symbol) stream)))))
-
-(define-presentation-type lisp-string ()
-                          :description "lisp string")
-
-(defmethod display-parse-tree ((parse-symbol complete-string-form) stream
-                               (view textual-drei-syntax-view) (syntax lisp-syntax))
-  (let ((children (children parse-symbol)))
-    (if (third children)
-        (let ((string (buffer-substring (buffer syntax)
-                                        (start-offset (second children))
-                                        (end-offset (car (last children 2))))))
-          (with-output-as-presentation (stream string 'lisp-string
-                                               :single-box :highlighting)
-            (with-face (:string)
-              (display-parse-tree (pop children) stream view syntax)
-	      (loop until (null (cdr children))
-                 do (display-parse-tree (pop children) stream view syntax))
-              (display-parse-tree (pop children) stream view syntax))))
-        (with-face (:string)
-         (progn (display-parse-tree (pop children) stream view syntax)
-                (display-parse-tree (pop children) stream view syntax))))))
-
-(defmethod display-parse-tree ((parse-symbol incomplete-string-form) stream
-                               (view textual-drei-syntax-view) (syntax lisp-syntax))
-  (let ((children (children parse-symbol)))
-    (if (second children)
-        (let ((string (buffer-substring (buffer syntax)
-                                        (start-offset (second children))
-                                        (end-offset (car (last children))))))
-          (with-output-as-presentation (stream string 'lisp-string
-                                               :single-box :highlighting)
-            (with-face (:string)
-              (display-parse-tree (pop children) stream view syntax)
-              (loop until (null children)
-                 do (display-parse-tree (pop children) stream view syntax)))))
-        (with-face (:string)
-         (display-parse-tree (pop children) stream view syntax)))))
-
-(defmethod display-parse-tree ((parse-symbol line-comment-form) stream
-                               (view textual-drei-syntax-view) (syntax lisp-syntax))
-  (with-face (:comment)
-    (call-next-method)))
-
-(defmethod display-parse-tree ((parse-symbol long-comment-form) stream
-                               (view textual-drei-syntax-view) (syntax lisp-syntax))
-  (with-face (:comment)
-    (call-next-method)))
-
-(defmethod display-parse-tree ((parse-symbol reader-conditional-positive-form)
-			       stream (view textual-drei-syntax-view) (syntax lisp-syntax))
-  (let ((conditional (second-noncomment (children parse-symbol))))
-    (if (eval-feature-conditional conditional syntax)
-	(call-next-method)
-	(let ((*current-faces* *reader-conditional-faces*))
-	  (with-face (:reader-conditional)
-	    (call-next-method))))))
-
-(defmethod display-parse-tree ((parse-symbol reader-conditional-negative-form)
-				stream (view textual-drei-syntax-view) (syntax lisp-syntax))
-  (let ((conditional (second-noncomment (children parse-symbol))))
-    (if (eval-feature-conditional conditional syntax)
-	(let ((*current-faces* *reader-conditional-faces*))
-	  (with-face (:reader-conditional)
-	    (call-next-method)))
-	(call-next-method))))
-
-(defgeneric eval-feature-conditional (conditional-form syntax))
-
-(defmethod eval-feature-conditional (conditional-form (syntax lisp-syntax))
-  nil)
-
-;; Adapted from slime.el
-
-(defconstant +keyword-package+ (find-package :keyword)
-  "The KEYWORD package.")
-
-(defmethod eval-feature-conditional ((conditional token-mixin) (syntax lisp-syntax))
-  (let* ((string (form-string syntax conditional))
-	 (symbol (parse-symbol string :package +keyword-package+)))
-    (member symbol *features*)))
-
-(defmethod eval-feature-conditional ((conditional list-form) (syntax lisp-syntax))
-  (let ((children (children conditional)))
-    (when (third-noncomment children)
-      (flet ((eval-fc (conditional)
-	       (funcall #'eval-feature-conditional conditional syntax)))
-	(let* ((type (second-noncomment children))
-	       (conditionals  (butlast
-			       (nthcdr
-				2
-				(remove-if
-				 #'comment-p
-				 children))))
-	       (type-string (form-string syntax type))
-	       (type-symbol (parse-symbol type-string :package +keyword-package+)))
-	  (case type-symbol
-	    (:and (funcall #'every #'eval-fc conditionals))
-	    (:or (funcall #'some #'eval-fc conditionals))
-	    (:not (when conditionals
-		    (funcall #'(lambda (f l) (not (apply f l)))
-			     #'eval-fc conditionals)))))))))
+;; Note that we do not colour keyword symbols or special forms yet,
+;; that is because the only efficient way to do so is to mark them as
+;; interesting in the parser itself, it is too slow to check for it in
+;; highlighting rules.
+(make-syntax-highlighting-rules emacs-style-highlighting
+  (error-symbol (:face :ink +red+))
+  (string-form (:face :ink +rosy-brown+
+                      :style (make-text-style nil :italic nil)))
+  (comment (:face :ink +maroon+ :style (make-text-style :serif :bold :large))))
+
+(defparameter *syntax-highlighting-rules* 'emacs-style-highlighting
+  "The syntax highlighting rules used for highlighting Lisp
+syntax.")
 
-(defmethod display-parse-tree ((parse-symbol complete-list-form) stream
-                               (view textual-drei-syntax-view) (syntax lisp-syntax))
-  (let* ((children (children parse-symbol))
-         (point-offset (the fixnum (offset (point view))))
-         ;; The following is true if the location if the point
-         ;; warrants highlighting of a set of matching parentheses.
-         (should-highlight (and (active view)
-                                (or (= (the fixnum (end-offset parse-symbol)) point-offset)
-                                    (= (the fixnum (start-offset parse-symbol)) point-offset)))))
-    (if should-highlight
-        (with-text-face (stream :bold)
-          (display-parse-tree (car children) stream view syntax))
-        (display-parse-tree (car children) stream view syntax))
-    (loop for child-list on (cdr children)
-       if (and should-highlight (null (cdr child-list))) do
-       (with-text-face (stream :bold)
-         (display-parse-tree (car child-list) stream view syntax))
-       else do
-       (display-parse-tree (car child-list) stream view syntax))))
-
-(defmethod display-parse-tree ((parse-symbol incomplete-list-form) stream
-                               (view textual-drei-syntax-view) (syntax lisp-syntax))
-  (update-parse syntax)
-  (let* ((children (children parse-symbol))
-         (point-offset (the fixnum (offset (point view))))
-         ;; The following is set to true if the location if the point
-         ;; warrants highlighting of the beginning parenthesis
-         (should-highlight (and (active view)
-                                (= (the fixnum (start-offset parse-symbol)) point-offset))))
-    (with-face (:error)
-      (if should-highlight
-          (with-text-face (stream :bold)
-            (display-parse-tree (car children) stream view syntax))
-          (display-parse-tree (car children) stream view syntax)))
-    (loop for child in (cdr children) do
-      (display-parse-tree child stream view syntax))))
+(defmethod syntax-highlighting-rules ((syntax lisp-syntax))
+  *syntax-highlighting-rules*)
 
 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 ;;;
--- /project/mcclim/cvsroot/mcclim/Drei/lr-syntax.lisp	2008/01/02 14:43:40	1.6
+++ /project/mcclim/cvsroot/mcclim/Drei/lr-syntax.lisp	2008/01/03 12:32:08	1.7
@@ -28,7 +28,8 @@
 (in-package :drei-lr-syntax)
 
 (defclass lr-syntax-mixin () 
-     ((stack-top :initform nil)
+     ((stack-top :initform nil
+                 :accessor stack-top)
       (potentially-valid-trees)
       (lookahead-lexeme :initform nil :accessor lookahead-lexeme)
       (current-state)
@@ -289,6 +290,66 @@
 
 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 ;;;
+;;; Utility functions
+
+(defun invoke-do-parse-symbols-forward (start-offset nearby-symbol fn)
+  "Loop across the parse symbols of the syntax, calling `fn' on
+any parse symbol that starts at or after
+`start-offset'. `Nearby-symbol' is the symbol at which the
+iteration will start. First, if `nearby-symbol' is at or after
+`start-offset', `fn' will be called on
+`nearby-symbol'. Afterwards, the children of `nearby-symbol' will
+be looped over. Finally, the process will be repeated for each
+sibling of `nearby-symbol'. It is guaranteed that `fn' will not
+be called twice for the same parser symbol."
+  (labels ((act (parse-symbol previous)
+             (when (>= (end-offset parse-symbol) start-offset)
+               (when (>= (start-offset parse-symbol) start-offset)
+                 (funcall fn parse-symbol))
+               (loop for child in (children parse-symbol)
+                  unless (eq child previous)
+                  do (act child parse-symbol)))
+             (unless (or (null (parent parse-symbol))
+                         (eq (parent parse-symbol) previous))
+               (act (parent parse-symbol) parse-symbol))))
+    (act nearby-symbol nearby-symbol)))
+
+(defmacro do-parse-symbols-forward ((symbol start-offset enclosing-symbol)
+                                    &body body)
+  "Loop across the parse symbols of the syntax, evaluating `body'
+with `symbol' bound for each parse symbol that starts at or after
+`start-offset'. `enclosing-symbol' is the symbol at which the
+iteration will start. First, if `enclosing-symbol' is at or after
+`start-offset', `symbol' will be bound to
+`enclosing-symbol'. Afterwards, the children of
+`enclosing-symbol' will be looped over. Finally, the process will
+be repeated for each sibling of `nearby-symbol'. It is guaranteed
+that `symbol' will not bound to the same parser symbol twice."
+  `(invoke-do-parse-symbols-forward ,start-offset ,enclosing-symbol
+                                    #'(lambda (,symbol)
+                                        , at body)))
+
+(defun parser-symbol-containing-offset (syntax offset)
+  "Find the most specific (leaf) parser symbol in `syntax' that
+contains `offset'. If there is no such parser symbol, return the
+stack-top of `syntax'."
+  (labels ((check (parser-symbol)
+             (cond ((or (and (<= (start-offset parser-symbol) offset)
+                             (< offset (end-offset parser-symbol)))
+                        (= offset (start-offset parser-symbol)))
+                    (return-from parser-symbol-containing-offset
+                      (if (null (children parser-symbol))
+                          parser-symbol
+                          (or (check-children (children parser-symbol))
+                              parser-symbol))))
+                   (t nil)))
+           (check-children (children)
+             (find-if #'check children)))
+    (or (check-children (children (stack-top syntax)))
+        (stack-top syntax))))
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;;;
 ;;; update syntax
 
 (defmethod update-syntax ((syntax lr-syntax-mixin) prefix-size suffix-size
@@ -317,85 +378,182 @@
 
 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 ;;;
-;;; Redisplay. This is just some minor conveniences, not an actual
-;;; generic redisplay implementation for LR syntaxes.
-
-(defvar *current-faces* nil
-  "The current faces used by the syntax for redisplay. Will be
-bound during redisplay.")
-
-(defstruct (face (:type list)
-                 (:constructor make-face (name colour &optional style)))
-  name colour (style nil))
-
-(defgeneric get-faces (syntax)
-  (:documentation "Return a list of all the defined standard
-faces of `syntax'.")
+;;; General redisplay for LR syntaxes, subclasses of `lr-syntax-mixin'
+;;; should be able to easily define some syntax rules, and need not
+;;; bother with all this complexity.
+;;;
+;;;          _______________
+;;;         /               \
+;;;        /                 \
+;;;       /                   \
+;;;       |   XXXX     XXXX   |
+;;;       |   XXXX     XXXX   |
+;;;       |   XXX       XXX   |
+;;;       |         X         |
+;;;       \__      XXX      __/
+;;;         |\     XXX     /|
+;;;         | |           | |
+;;;         | I I I I I I I |
+;;;         |  I I I I I I  |
+;;;         \_             _/
+;;;           \_         _/
+;;;             \_______/
+;;;     XXX                    XXX
+;;;    XXXXX                  XXXXX
+;;;    XXXXXXXXX         XXXXXXXXXX
+;;;           XXXXX   XXXXX
+;;;              XXXXXXX
+;;;           XXXXX   XXXXX
+;;;    XXXXXXXXX         XXXXXXXXXX
+;;;    XXXXX                  XXXXX
+;;;     XXX                    XXX
+
+(defmacro make-syntax-highlighting-rules (name &body rules)
+  "Define a set of rules for highlighting a syntax. `Name', which
+must be a symbol, is the name of this set of rules, and will be
+bound to a function implementing the rules. `Rules' is a list of
+rules of the form `(parser-symbol (type args...))', where
+`parser-symbol' is a type that might be encountered in a parse
+tree for the syntax. The rule specifies how to highlight that
+kind of object (and all its children). `Type' can be one of three
+symbols.
+
+  `:face', in which case `args' will be used as arguments to a
+  call to `make-face'. The resulting face will be used to draw
+  the parsersymbol.
+
+  `:options', in which case `args' will be used as arguments to
+  `make-drawing-options'. The resulting options will be used to
+  draw the parser symbol.
+
+  `:function', in which case `args' must be a single element, a
+  function that takes two arguments. These arguments are the
+  syntax and the parser symbol, and the return value of this
+  function is the `drawing-options' object that will be used to
+  draw the parser-symbol."
+  (check-type name symbol)
+  `(progn
+     (fmakunbound ',name)
+     (defgeneric ,name (syntax parser-symbol)
+       (:method (syntax (parser-symbol parser-symbol))
+         nil))
+     ,@(flet ((make-rule-exp (type args)
+                             (ecase type
+                               (:face `#'(lambda (syntax parser-symbol)
+                                           (declare (ignore syntax parser-symbol))
+                                           (make-drawing-options :face (make-face , at args))))
+                               (:options `#'(lambda (syntax parser-symbol)
+                                              (declare (ignore syntax parser-symbol))
+                                              (make-drawing-options , at args)))
+                               (:function (first args)))))
+             (loop for (parser-symbol (type . args)) in rules
+                collect `(let ((rule ,(make-rule-exp type args)))
+                           (defmethod ,name (syntax (parser-symbol ,parser-symbol))
+                             (funcall rule syntax parser-symbol)))))))
+
+(make-syntax-highlighting-rules default-syntax-highlighting)
+
+(defgeneric syntax-highlighting-rules (syntax)
+  (:documentation "Return the drawing options that should be used
+for displaying `parser-symbol's for `syntax'. A method should be
+defined on this function for any syntax that wants syntax
+highlighting.")
   (:method ((syntax lr-syntax-mixin))
-    '()))
+    'default-syntax-highlighting))
 
-(defun get-face (name)
-  "Retrieve face named `name' from `*current-faces*'."
-  (find name *current-faces* :key #'face-name))
-
-(defmacro define-standard-faces (syntax &body faces)
-  "Define the list of standard faces used by `syntax' to be
-`faces', which must be a sequence of forms evaluating to
-face-values ((name, colour, style)-triples)."
-  `(let ((faces-list (list , at faces)))
-     (defmethod get-faces ((syntax ,syntax))
-       faces-list)))
-
-(defmacro with-face ((face &optional (stream-symbol 'stream)) &body body)
-  `(with-drawing-options (,stream-symbol :ink (face-colour (get-face ,face))
-                                         :text-style (face-style (get-face ,face)))
-     , at body))
-
-(defgeneric display-parse-tree (parse-symbol stream view syntax)
-  (:documentation "Display the given parse-symbol on `stream',
-assuming `view' to be the relevant Drei vire and `syntax' being
-the syntax object responsible for the parse symbol."))
-
-(defmethod display-parse-tree :before ((parse-symbol lexeme)
-                                       stream (view textual-drei-syntax-view)
-                                       (syntax lr-syntax-mixin))
-  (handle-whitespace stream view (buffer view)
-                     *white-space-start* (start-offset parse-symbol))
-  (setf *white-space-start* (end-offset parse-symbol)))
-
-(defmethod display-parse-tree :around ((parse-symbol parser-symbol)
-                                       stream (view textual-drei-syntax-view)
-                                       (syntax lr-syntax-mixin))
-  (with-accessors ((top top) (bot bot)) view
-    (when (and (start-offset parse-symbol)
-               (mark< (start-offset parse-symbol) bot)
-               (mark> (end-offset parse-symbol) top))
-      (call-next-method))))
-
-(defmethod display-parse-tree ((parse-symbol parser-symbol)
-                               stream (view textual-drei-syntax-view)
-                               (syntax lr-syntax-mixin))
-  (with-accessors ((top top) (bot bot)) view
-    (loop for child in (children parse-symbol)
-       when (and (start-offset child)
-                 (mark> (end-offset child) top))
-       do (if (mark< (start-offset child) bot)
-              (display-parse-tree child stream view syntax)
-              (return)))))
-
-(defmethod display-syntax-view ((stream clim-stream-pane) (view textual-drei-syntax-view)
-                                (syntax lr-syntax-mixin))
-  (update-parse syntax)
-  (with-accessors ((top top) (bot bot)) view
-    (with-accessors ((cursor-positions cursor-positions)) view
-      ;; There must always be room for at least one element of line
-      ;; information.
-      (setf cursor-positions (make-array (1+ (number-of-lines-in-region top bot))
-                              :initial-element nil)
-            *current-line* 0
-            (aref cursor-positions 0) (multiple-value-list
-                                       (stream-cursor-position stream))))
-    (setf *white-space-start* (offset top)))
-  (let ((*current-faces* (get-faces syntax)))
-    (with-slots (stack-top) syntax
-      (display-parse-tree stack-top stream view syntax))))
+(defun get-drawing-options (highlighting-rules syntax parse-symbol)
+  "Get the drawing options with which `parse-symbol' should be
+drawn. If `parse-symbol' is NIL, return NIL."
+  (when parse-symbol
+    (funcall highlighting-rules syntax parse-symbol)))
+
+(defstruct (pump-state
+             (:constructor make-pump-state
+                           (parser-symbol offset drawing-options
+                                          highlighting-rules)))
+  "A pump state object used in the LR syntax
+module. `parser-symbol' is the a parse symbol object `offset' is
+in. `Drawing-options' is a stack with elements `(end-offset
+drawing-options)', where `end-offset' specifies there the drawing
+options specified by `drawing-options' stop. `Highlighting-rules'
+is the rules that are used for syntax highlighting."
+  parser-symbol offset
+  drawing-options highlighting-rules)
+
+(defmethod pump-state-for-offset-with-syntax ((view textual-drei-syntax-view)
+                                              (syntax lr-syntax-mixin) (offset integer))
+  (update-parse syntax 0 offset)
+  (let ((parser-symbol (parser-symbol-containing-offset syntax offset))
+        (highlighting-rules (syntax-highlighting-rules syntax)))
+    (labels ((initial-drawing-options (parser-symbol)
+               (if (null parser-symbol)
+                   (cons (size (buffer view)) +default-drawing-options+)
+                   (let ((drawing-options
+                          (get-drawing-options highlighting-rules syntax parser-symbol)))
+                     (if (null drawing-options)
+                         (initial-drawing-options (parent parser-symbol))
+                         (cons (end-offset parser-symbol) drawing-options))))))
+      (make-pump-state parser-symbol offset
+                       (list (initial-drawing-options parser-symbol)
+                             (cons (1+ (size (buffer view))) +default-drawing-options+))
+                       highlighting-rules))))
+
+(defun find-next-stroke-end (syntax pump-state)
+  "Assuming that `pump-state' contains the previous pump state,
+find out where the next stroke should end, and possibly push some
+drawing options onto `pump-state'."
+  (with-accessors ((start-symbol pump-state-parser-symbol)
+                   (offset pump-state-offset)
+                   (drawing-options pump-state-drawing-options)
+                   (highlighting-rules pump-state-highlighting-rules))
+      pump-state
+    (let ((line (line-containing-offset syntax offset)))
+      (flet ((finish (offset symbol &optional stroke-drawing-options)
+               (setf start-symbol symbol)
+               (loop until (> (car (first drawing-options)) offset)
+                  do (pop drawing-options))
+               (unless (null stroke-drawing-options)
+                 (push (cons (end-offset symbol) stroke-drawing-options)
+                       drawing-options))
+               (return-from find-next-stroke-end
+                 offset)))
+        (if (null start-symbol)
+            ;; This means that all remaining lines are blank.
+            (finish (line-end-offset line) nil)
+            (or (do-parse-symbols-forward (symbol offset start-symbol)
+                  (let ((symbol-drawing-options
+                         (get-drawing-options highlighting-rules syntax symbol)))
+                    (cond ((> (start-offset symbol) (line-end-offset line))
+                           (finish (line-end-offset line) start-symbol))
+                          ((and (> (start-offset symbol) offset)
+                                (not (drawing-options-equal (or symbol-drawing-options
+                                                                +default-drawing-options+)
+                                                            (cdr (first drawing-options)))))
+                           (finish (start-offset symbol) symbol symbol-drawing-options))
+                          ((and (= (start-offset symbol) offset)
+                                (offset-beginning-of-line-p (buffer syntax) offset)
+                                (and symbol-drawing-options
+                                     (not (drawing-options-equal symbol-drawing-options
+                                                                 (cdr (first drawing-options))))))
+                           (finish (start-offset symbol) symbol symbol-drawing-options)))))
+                ;; If there are no more parse symbols, we just go
+                ;; line-by-line from here. This should mean that all
+                ;; remaining lines are blank.
+                (finish (line-end-offset line) nil)))))))
+
+(defmethod stroke-pump-with-syntax ((view textual-drei-syntax-view)
+                                    (syntax lr-syntax-mixin) stroke
+                                    (pump-state pump-state))
+  ;; `Pump-state' will be destructively modified.
+  (prog1 pump-state
+    (with-accessors ((offset pump-state-offset)
+                     (current-drawing-options pump-state-drawing-options))
+        pump-state
+      (let ((old-drawing-options (cdr (first current-drawing-options)))
+            (end-offset (find-next-stroke-end syntax pump-state)))
+        (setf (stroke-start-offset stroke) offset
+              (stroke-end-offset stroke) end-offset
+              (stroke-drawing-options stroke) old-drawing-options
+              offset (if (offset-end-of-line-p (buffer view) end-offset)
+                         (1+ end-offset)
+                         end-offset))))))
--- /project/mcclim/cvsroot/mcclim/Drei/packages.lisp	2008/01/02 14:43:40	1.29
+++ /project/mcclim/cvsroot/mcclim/Drei/packages.lisp	2008/01/03 12:32:08	1.30
@@ -169,8 +169,6 @@
            #:display-syntax-name
            #:syntax-line-indentation
            #:eval-defun
-           #:record-line-vertical-offset
-           #:line-vertical-offset
            #:syntax-line-comment-string
            #:line-comment-region #:comment-region
            #:line-uncomment-region #:uncomment-region
@@ -487,13 +485,15 @@
   (:use :clim-lisp :clim :drei-buffer :drei-base 
         :drei-syntax :flexichain :drei :drei-core)
   (:export #:fundamental-syntax #:scan
-           #:*current-line* #:*white-space-start* #:handle-whitespace)
+           #:start-mark #:line-length #:line-end-offset
+           #:line-containing-offset #:offset-in-line-p)
   (:documentation "Implementation of the basic syntax module for
 editing plain text."))
 
 (defpackage :drei-lr-syntax
   (:use :clim-lisp :clim :clim-extensions :drei-buffer :drei-base
-	:drei-syntax :drei :drei-core :drei-fundamental-syntax)
+        :drei-syntax :drei :drei-core :drei-fundamental-syntax
+        :esa-utils)
   (:export #:lr-syntax-mixin #:stack-top #:initial-state
 	   #:skip-inter #:lex #:define-lexer-state
 	   #:lexer-toplevel-state #:lexer-error-state
@@ -505,10 +505,8 @@
 	   #:action #:new-state #:done
 	   #:reduce-fixed-number #:reduce-until-type #:reduce-all 
 	   #:error-state #:error-reduce-state
-           #:*current-faces*
-           #:make-face #:face-name #:face-colour #:face-style
-           #:get-faces #:define-standard-faces #:with-face
-           #:display-parse-tree)
+           #:make-syntax-highlighting-rules
+           #:syntax-highlighting-rules)
   (:documentation "Underlying LR parsing functionality."))
 
 (defpackage :drei-lisp-syntax
@@ -564,8 +562,6 @@
            #:at-end-of-string-p
            #:at-beginning-of-children-p
            #:at-end-of-children-p
-           #:structurally-at-beginning-of-children-p
-           #:structurally-at-end-of-children-p
            #:comment-at-mark
 
            ;; Lambda list classes.




More information about the Mcclim-cvs mailing list