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) + ,@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 ,@args)))) + (:options `#'(lambda (syntax parser-symbol) + (declare (ignore syntax parser-symbol)) + (make-drawing-options ,@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 ,@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))) - ,@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.