Update of /project/mcclim/cvsroot/mcclim/Drei
In directory clnet:/tmp/cvs-serv31402/Drei
Modified Files:
search-commands.lisp packages.lisp drei.lisp
Added Files:
targets.lisp
Log Message:
Added Drei "target" concept, facilitating search/replace-commands that
act over multiple buffers (or "targets").
--- /project/mcclim/cvsroot/mcclim/Drei/search-commands.lisp 2006/11/08 01:15:33 1.1
+++ /project/mcclim/cvsroot/mcclim/Drei/search-commands.lisp 2007/11/16 09:28:44 1.2
@@ -8,6 +8,8 @@
;;; Matthieu Villeneuve (matthieu.villeneuve(a)free.fr)
;;; (c) copyright 2005 by
;;; Aleksandar Bakic (a_bakic(a)yahoo.com)
+;;; (c) copyright 2007 by
+;;; Troels Henriksen (athas(a)sigkill.dk)
;;; This library is free software; you can redistribute it and/or
;;; modify it under the terms of the GNU Library General Public
@@ -28,6 +30,29 @@
(in-package :drei-commands)
+(defun simple-search (drei-instance search-function
+ targets more-targets-predicate more-targets-fn)
+ (let ((old-buffer (buffer drei-instance))
+ (old-offset (offset (point drei-instance))))
+ (activate-target-specification targets)
+ (or (loop until (funcall search-function (point drei-instance))
+ if (funcall more-targets-predicate targets)
+ do (funcall more-targets-fn targets)
+ else return nil
+ finally (return t))
+ (setf (buffer drei-instance) old-buffer
+ (offset (point drei-instance)) old-offset))))
+
+(defun simple-search-forward (drei-instance search-function &optional
+ (targets (funcall *default-target-creator* drei-instance)))
+ (simple-search drei-instance search-function targets
+ #'subsequent-targets-p #'next-target))
+
+(defun simple-search-backward (drei-instance search-function &optional
+ (targets (funcall *default-target-creator* drei-instance)))
+ (simple-search drei-instance search-function targets
+ #'preceding-targets-p #'previous-target))
+
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;
;;; String search
@@ -36,13 +61,19 @@
((string 'string :prompt "String Search"))
"Prompt for a string and search forward for it.
If found, leaves point after string. If not, leaves point where it is."
- (search-forward *current-point* string :test (case-relevant-test string)))
+ (simple-search-forward *current-window*
+ #'(lambda (mark)
+ (search-forward mark string
+ :test (case-relevant-test string)))))
(define-command (com-reverse-string-search :name t :command-table search-table)
((string 'string :prompt "Reverse String Search"))
"Prompt for a string and search backward for it.
If found, leaves point before string. If not, leaves point where it is."
- (search-backward *current-point* string :test (case-relevant-test string)))
+ (simple-search-backward *current-window*
+ #'(lambda (mark)
+ (search-backward mark string
+ :test (case-relevant-test string)))))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;
@@ -52,13 +83,17 @@
((word 'string :prompt "Search word"))
"Prompt for a whitespace delimited word and search forward for it.
If found, leaves point after the word. If not, leaves point where it is."
- (search-word-forward *current-point* word))
+ (simple-search-forward *current-window*
+ #'(lambda (mark)
+ (search-word-forward mark word))))
(define-command (com-reverse-word-search :name t :command-table search-table)
((word 'string :prompt "Search word"))
"Prompt for a whitespace delimited word and search backward for it.
If found, leaves point before the word. If not, leaves point where it is."
- (search-word-backward *current-point* word))
+ (simple-search-backward *current-window*
+ #'(lambda (mark)
+ (search-backward mark word))))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;
@@ -66,51 +101,75 @@
(make-command-table 'isearch-drei-table :errorp nil)
-(defun isearch-command-loop (pane forwardp)
- (let* ((point (point pane))
- (orig-offset (offset point)))
- (unless (endp (isearch-states pane))
- (setf (isearch-previous-string pane)
- (search-string (first (isearch-states pane)))))
- (setf (isearch-mode pane) t)
- (setf (isearch-states pane)
+(defun isearch-command-loop (drei-instance forwardp)
+ (let* ((point (point drei-instance))
+ (orig-offset (offset point))
+ (orig-buffer (buffer drei-instance)))
+ (unless (endp (isearch-states drei-instance))
+ (setf (isearch-previous-string drei-instance)
+ (search-string (first (isearch-states drei-instance)))))
+ (setf (isearch-mode drei-instance) t)
+ (setf (isearch-states drei-instance)
(list (make-instance 'isearch-state
:search-string ""
:search-mark (clone-mark point)
+ :search-buffer orig-buffer
:search-forward-p forwardp
- :search-success-p t)))
+ :search-success-p t
+ :targets (funcall *default-target-creator* drei-instance))))
+ (activate-target-specification (targets (first (isearch-states drei-instance))))
(simple-command-loop 'isearch-drei-table
- (isearch-mode pane)
+ (isearch-mode drei-instance)
((display-message "Mark saved where search started")
- (setf (offset (mark pane)) orig-offset)
- (setf (isearch-mode pane) nil))
+ (setf (offset (mark drei-instance)) orig-offset)
+ (setf (isearch-mode drei-instance) nil))
((display-message "Returned point to original location")
- (setf (offset (point pane)) orig-offset)
- (setf (isearch-mode pane) nil)
+ (setf (buffer drei-instance) orig-buffer)
+ (setf (offset (point drei-instance)) orig-offset)
+ (setf (isearch-mode drei-instance) nil)
(signal 'abort-gesture :event *current-gesture*)))))
-(defun isearch-from-mark (pane mark string forwardp)
- (let* ((point (point pane))
+(defun isearch-from-mark (drei-instance mark string forwardp)
+ (let* ((point (point drei-instance))
(mark2 (clone-mark mark))
(success (funcall (if forwardp #'search-forward #'search-backward)
mark2
string
- :test (case-relevant-test string))))
- (when success
- (setf (offset point) (offset mark2)
- (offset mark) (if forwardp
- (- (offset mark2) (length string))
- (+ (offset mark2) (length string)))))
+ :test (case-relevant-test string)))
+ (state (first (isearch-states drei-instance))))
+ (if success
+ (setf (offset point) (offset mark2)
+ (offset mark) (if forwardp
+ (- (offset mark2) (length string))
+ (+ (offset mark2) (length string))))
+ (when (funcall (if forwardp
+ #'subsequent-targets-p
+ #'preceding-targets-p)
+ (targets state))
+ (funcall (if forwardp #'next-target #'previous-target)
+ (targets state))
+ (if (isearch-from-mark drei-instance (clone-mark (point drei-instance))
+ string forwardp)
+ (return-from isearch-from-mark t)
+ (progn (pop (isearch-states drei-instance))
+ (funcall (if forwardp #'previous-target #'next-target)
+ (targets state))
+ (setf (offset (point drei-instance))
+ (offset (search-mark state)))
+ nil))))
(display-message "~:[Failing ~;~]Isearch~:[ backward~;~]: ~A"
success forwardp (display-string string))
(push (make-instance 'isearch-state
- :search-string string
- :search-mark mark
- :search-forward-p forwardp
- :search-success-p success)
- (isearch-states pane))
+ :search-string string
+ :search-mark mark
+ :search-buffer (buffer drei-instance)
+ :search-forward-p forwardp
+ :search-success-p success
+ :targets (targets state))
+ (isearch-states drei-instance))
(unless success
- (beep))))
+ (beep))
+ success))
(define-command (com-isearch-forward :name t :command-table search-table) ()
(display-message "Isearch: ")
@@ -280,15 +339,20 @@
(defun query-replace-find-next-match (state)
(with-accessors ((string string1)
- (buffers buffers)
- (mark mark)) state
- (let ((offset-before (offset mark)))
+ (targets targets)) state
+ (let* ((mark (point (drei-instance (targets state))))
+ (offset-before (offset mark)))
(search-forward mark string :test (case-relevant-test string))
- (/= (offset mark) offset-before))))
+ (if (= (offset mark) offset-before)
+ (when (subsequent-targets-p targets)
+ (next-target targets)
+ (beginning-of-buffer (point (buffer (drei-instance targets))))
+ (query-replace-find-next-match state))
+ t))))
(define-command (com-query-replace :name t :command-table search-table) ()
- (let* ((pane *current-window*)
- (old-state (query-replace-state pane))
+ (let* ((drei *current-window*)
+ (old-state (query-replace-state drei))
(old-string1 (when old-state (string1 old-state)))
(old-string2 (when old-state (string2 old-state)))
(string1 (handler-case
@@ -313,21 +377,25 @@
(error () (progn (beep)
(display-message "Empty string")
(return-from com-query-replace nil))))))
- (setf (query-replace-state pane) (make-instance 'query-replace-state
+ (setf (query-replace-state drei) (make-instance 'query-replace-state
:string1 string1
:string2 string2
- :mark *current-point*))
- (when (query-replace-find-next-match (query-replace-state pane))
- (setf (query-replace-mode pane) t)
- (display-message "Replace ~A with ~A:"
- string1 string2)
- (simple-command-loop 'query-replace-drei-table
- (query-replace-mode pane)
- ((setf (query-replace-mode pane) nil)
- (display-message "Replaced ~A occurence~:P"
- (occurrences (query-replace-state pane))))
- ((setf (query-replace-mode pane) nil)
- (signal 'abort-gesture :event *current-gesture*))))))
+ :targets (funcall *default-target-creator* drei)))
+ (activate-target-specification (targets (query-replace-state drei)))
+ (if (query-replace-find-next-match (query-replace-state drei))
+ (progn
+ (setf (query-replace-mode drei) t)
+ (display-message "Replace ~A with ~A:"
+ string1 string2)
+ (simple-command-loop 'query-replace-drei-table
+ (query-replace-mode drei)
+ ((setf (query-replace-mode drei) nil)
+ (deactivate-target-specification (targets (query-replace-state drei)))
+ (display-message "Replaced ~A occurence~:P"
+ (occurrences (query-replace-state drei))))
+ ((setf (query-replace-mode drei) nil)
+ (signal 'abort-gesture :event *current-gesture*))))
+ (display-message "Replaced 0 occurences"))))
(set-key 'com-query-replace
'search-table
@@ -338,15 +406,17 @@
(state (query-replace-state pane)))
(with-accessors ((string1 string1)
(string2 string2)
- (occurrences occurrences)) state
- (let ((string1-length (length string1)))
- (backward-object (mark state) string1-length)
- (replace-one-string (mark state)
+ (occurrences occurrences)
+ (targets targets)) state
+ (let ((string1-length (length string1))
+ (mark (point (drei-instance targets))))
+ (backward-object mark string1-length)
+ (replace-one-string mark
string1-length
string2
(no-upper-p string1))
(incf occurrences)
- (if (query-replace-find-next-match (query-replace-state pane))
+ (if (query-replace-find-next-match state)
(display-message "Replace ~A with ~A:"
string1 string2)
(setf (query-replace-mode pane) nil))))))
@@ -359,10 +429,12 @@
(state (query-replace-state pane)))
(with-accessors ((string1 string1)
(string2 string2)
- (occurrences occurrences)) state
- (let ((string1-length (length string1)))
- (backward-object (mark state) string1-length)
- (replace-one-string (mark state)
+ (occurrences occurrences)
+ (targets targets)) state
+ (let ((string1-length (length string1))
+ (mark (point (drei-instance targets))))
+ (backward-object mark string1-length)
+ (replace-one-string mark
string1-length
string2
(no-upper-p string1))
@@ -377,15 +449,17 @@
(state (query-replace-state pane)))
(with-accessors ((string1 string1)
(string2 string2)
- (occurrences occurrences)) state
- (let ((string1-length (length string1)))
- (loop do (backward-object (mark state) string1-length)
- (replace-one-string (mark state)
+ (occurrences occurrences)
+ (targets targets)) state
+ (let ((string1-length (length string1))
+ (mark (point (drei-instance targets))))
+ (loop do (backward-object mark string1-length)
+ (replace-one-string mark
string1-length
string2
(no-upper-p string1))
(incf occurrences)
- while (query-replace-find-next-match (query-replace-state pane))
+ while (query-replace-find-next-match state)
finally (setf (query-replace-mode pane) nil))))))
(define-command (com-query-replace-skip :name t :command-table query-replace-drei-table) ()
@@ -435,14 +509,18 @@
:delimiter-gestures nil
:activation-gestures
'(:newline :return))))
- (re-search-forward *current-point* (normalise-minibuffer-regex string))))
+ (simple-search-forward *current-window*
+ #'(lambda (mark)
+ (re-search-forward mark (normalise-minibuffer-regex string))))))
(define-command (com-regex-search-backward :name t :command-table search-table) ()
(let ((string (accept 'string :prompt "RE search backward"
:delimiter-gestures nil
:activation-gestures
'(:newline :return))))
- (re-search-backward *current-point* (normalise-minibuffer-regex string))))
+ (simple-search-backward *current-window*
+ #'(lambda (mark)
+ (re-search-backward mark (normalise-minibuffer-regex string))))))
(define-command (com-how-many :name t :command-table search-table)
((regex 'string :prompt "How many matches for"))
--- /project/mcclim/cvsroot/mcclim/Drei/packages.lisp 2007/08/13 21:58:44 1.15
+++ /project/mcclim/cvsroot/mcclim/Drei/packages.lisp 2007/11/16 09:28:44 1.16
@@ -186,9 +186,9 @@
#:offset-to-screen-position
#:page-down #:page-up
#:indent-tabs-mode
- #:isearch-state #:search-string #:search-mark
+ #:isearch-state #:search-string #:search-mark #:search-buffer
#:search-forward-p #:search-success-p
- #:query-replace-state #:string1 #:string2 #:buffers #:mark #:occurrences
+ #:query-replace-state #:string1 #:string2 #:targets #:occurrences
;; Undo.
#:undo-mixin #:undo-tree #:undo-accumulate #:performing-undo
@@ -433,7 +433,17 @@
#:start-mark
#:end-mark
- #:make-buffer-stream)
+ #:make-buffer-stream
+
+ #:target-specification
+ #:activate-target-specification
+ #:deactivate-target-specification
+ #:subsequent-targets-p #:preceding-targets-p
+ #:next-target #:previous-target
+ #:previous-target
+ #:no-more-targets
+ #:*default-target-creator*
+ #:buffer-list-target-specification)
(:documentation "Implementation of much syntax-aware, yet no
syntax-specific, core functionality of Drei."))
--- /project/mcclim/cvsroot/mcclim/Drei/drei.lisp 2007/01/14 20:03:00 1.15
+++ /project/mcclim/cvsroot/mcclim/Drei/drei.lisp 2007/11/16 09:28:44 1.16
@@ -325,8 +325,10 @@
(defclass isearch-state ()
((search-string :initarg :search-string :accessor search-string)
(search-mark :initarg :search-mark :accessor search-mark)
+ (search-buffer :initarg :search-buffer :accessor search-buffer)
(search-forward-p :initarg :search-forward-p :accessor search-forward-p)
- (search-success-p :initarg :search-success-p :accessor search-success-p)))
+ (search-success-p :initarg :search-success-p :accessor search-success-p)
+ (targets :initarg :targets :accessor targets )))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;
@@ -335,7 +337,7 @@
(defclass query-replace-state ()
((string1 :initarg :string1 :accessor string1)
(string2 :initarg :string2 :accessor string2)
- (mark :initarg :mark :accessor mark)
+ (targets :initarg :targets :accessor targets)
(occurences :initform 0 :accessor occurrences)))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
@@ -675,7 +677,11 @@
:active active)
cursors)))
-(defmethod (setf buffer) :after (buffer (object drei))
+(defmethod (setf buffer) :before ((buffer drei-buffer) (object drei))
+ (with-slots (buffer point) object
+ (setf (point buffer) point)))
+
+(defmethod (setf buffer) :after ((buffer drei-buffer) (object drei))
(with-slots (point mark top bot) object
(setf point (clone-mark (point buffer))
mark (clone-mark (low-mark buffer) :right)
--- /project/mcclim/cvsroot/mcclim/Drei/targets.lisp 2007/11/16 09:28:46 NONE
+++ /project/mcclim/cvsroot/mcclim/Drei/targets.lisp 2007/11/16 09:28:46 1.1
;;; -*- Mode: Lisp; Package: DREI-CORE -*-
;;; (c) copyright 2007 by
;;; Troels Henriksen (athas(a)sigkill.dk)
;;; This library is free software; you can redistribute it and/or
;;; modify it under the terms of the GNU Library General Public
;;; License as published by the Free Software Foundation; either
;;; version 2 of the License, or (at your option) any later version.
;;;
;;; This library is distributed in the hope that it will be useful,
;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
;;; Library General Public License for more details.
;;;
;;; You should have received a copy of the GNU Library General Public
;;; License along with this library; if not, write to the
;;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
;;; Boston, MA 02111-1307 USA.
;;; Facilities and protocols for iterating through buffer objects, the
;;; point being that the buffer may be magically exchanged for some
;;; other buffer, permitting easy iteration through multiple buffers
;;; as a single sequence. This is meant to support Climacs'
;;; Group-facility, I'm not sure what else it could be used for.
(in-package :drei-core)
(defclass target-specification ()
((%drei :reader drei-instance
:initarg :drei-instance
:initform (error "A Drei instance must be provided for a target specification")))
(:documentation "The base class for target specifications,
objects that permit browsing through targets for various
operations. `Target-specification' instances start off
deactivated."))
(defgeneric activate-target-specification (target-specification)
(:documentation "Cause the Drei instance associated with
`target-specification' to switch to the \"current\" target of
`target-specification', whatever that is. It is illegal to call
any other target function on a `target-specification' object
until it has been activated by this function, and it is illegal
to call this function on an already activated
`target-specification' instance."))
(defgeneric deactivate-target-specification (target-specification)
(:documentation "Deactivate the `target-specification'
instance, restoring whatever state the call to
`activate-target-specification' modified. It is illegal to call
`deactivate-target-specification' on a deactivated
`target-specification' instance."))
(defgeneric subsequent-targets-p (target-specification)
(:documentation "Return true if there are more targets to act
on, that is, if the `next-target' function would not signal an
error."))
(defgeneric preceding-targets-p (target-specification)
(:documentation "Return true if there are targets to act on in
sequence before the current target, that is, if the
`previous-target' function would not signal an error."))
(defgeneric next-target (target-specification)
(:documentation "Change to the next target specified by the
target specification. Signals an error of type `no-more-targets'
if `subsequent-targets-p' is false."))
(defgeneric previous-target (target-specification)
(:documentation "Change to the previous target specified by the
target specification. Signals an error of type `no-more-targets'
if `preceding-targets-p' is false."))
(define-condition no-more-targets (simple-error)
()
(:report (lambda (condition stream)
(declare (ignore condition))
(format stream "No more targets available for iteration")))
(:documentation "Signal that there are no more targets
available for iteration, either forward or backwards in the
sequence of targets."))
(defclass current-buffer-target (target-specification)
((%buffer :accessor buffer))
(:documentation "A target specification class specifying just
one buffer, the current buffer of the Drei instance at the time
of object creation. This is mostly used as a dummy target
specification to make target-aware commands behave \"normally\"
when no particular targets are specified."))
(defmethod initialize-instance :after ((obj current-buffer-target) &rest initargs)
(declare (ignore initargs))
(setf (buffer obj) (buffer (drei-instance obj))))
(defmethod activate-target-specification ((spec current-buffer-target))
;; Noop.
)
(defmethod deactivate-target-specification ((spec current-buffer-target))
;; Noop.
)
(defmethod subsequent-targets-p ((spec current-buffer-target))
nil)
(defmethod preceding-targets-p ((spec current-buffer-target))
nil)
(defmethod next-target ((spec current-buffer-target))
(error 'no-more-targets))
(defmethod previous-target ((spec current-buffer-target))
(error 'no-more-targets))
(defvar *default-target-creator* #'(lambda (drei)
(make-instance 'current-buffer-target :drei-instance drei))
"A function of a single argument, the Drei instance, that
creates a target specification object (or subtype thereof) that
should be used for aquiring targets.")
(defclass buffer-list-target-specification (target-specification)
((%buffers :initarg :buffers
:initform '()
:accessor buffers)
(%buffer-count :accessor buffer-count)
(%current-buffer-index :initform 0
:accessor current-buffer-index))
(:documentation "A target specification that has a provided
list of existing buffers as its target."))
(defmethod initialize-instance :after ((obj buffer-list-target-specification)
&rest initargs)
(declare (ignore initargs))
(setf (buffer-count obj) (length (buffers obj)))
;; If the current buffer is in the list of buffers, we move it to
;; the head of the list, since it makes sense to make it the
;; starting point.
(when (/= (length (setf (buffers obj)
(remove (buffer (drei-instance obj)) (buffers obj))))
(buffer-count obj))
(push (buffer (drei-instance obj)) (buffers obj))))
(defmethod activate-target-specification ((spec buffer-list-target-specification))
(unless (or (null (buffers spec))
(eq (buffer (drei-instance spec)) (first (buffers spec))))
(setf (buffer (drei-instance spec)) (first (buffers spec)))
(beginning-of-buffer (point (drei-instance spec)))))
(defmethod deactivate-target-specification ((spec buffer-list-target-specification)))
(defmethod subsequent-targets-p ((spec buffer-list-target-specification))
(/= (1+ (current-buffer-index spec)) (buffer-count spec)))
(defmethod preceding-targets-p ((spec buffer-list-target-specification))
(plusp (current-buffer-index spec)))
(defmethod next-target ((spec buffer-list-target-specification))
(if (subsequent-targets-p spec)
(progn
(setf (buffer (drei-instance spec))
(elt (buffers spec) (incf (current-buffer-index spec))))
(beginning-of-buffer (point (drei-instance spec))))
(error 'no-more-targets)))
(defmethod previous-target ((spec buffer-list-target-specification))
(if (preceding-targets-p spec)
(progn
(setf (buffer (drei-instance spec))
(elt (buffers spec) (decf (current-buffer-index spec))))
(end-of-buffer (point (drei-instance spec))))
(error 'no-more-targets)))