; -*- Mode:LISP; Package:STEVE; Readtable:CL; Base:10 -*- 1983 ;Copyright (c) May 1983 by Christopher Eliot ; and Massachusetts Institute of Technology. ;Permission to copy all or part of this material is granted, provided ; that the copies are not made or distributed for resale, the MIT ; copyright notice and reference to the source file and the software ; distribution version appear, and that notice is given that copying ; is by permission of Massachusetts Institute of Technology. ; History: ; removed references to editor-defun-key 5/8/84 (defvar re-generate-screen-really) (defvar *editor-cursor*) (defvar *editor-buffer*) (defvar *editor-current-key*) (defvar creamed-tty-lines-to 0) (defconstant left-paren #\() (defconstant right-paren #\)) (defvar *argument* 1) (defvar argument-supplied? ()) ;These are used by #\control-0 etc. ;AUTO-DIGIT-ARG-SAVE stores the old *ARGUMENT* so the new value can ;be computed when more digits are added. Auto digit save is similar. (defvar auto-digit-arg-save nil) (define-bit-prefix #\control-^ char-control-bit "C-") (define-bit-prefix #\altmode char-meta-bit "M-") (define-bit-prefix #\control-\\ char-meta-bit "M-") (define-bit-prefix #\control-z (logior& char-control-bit char-meta-bit) "C-M-") (define-prefix-char #\control-x "C-X ") (editor-bind-key #\control-q (quoted-insert)) (editor-bind-key #\control-b backward-character) (defun backward-character () (send *editor-cursor* :advance-pos (minus *argument*)) nil) (editor-bind-key #\control-h backward-character) (editor-bind-key #\backspace backward-character) (editor-bind-key #\control-f forward-character) (defun forward-character () (send *editor-cursor* :advance-pos *argument*) nil) (defvar real-line-target-column nil) (defvar real-line-goal-column nil) (editor-bind-key #\control-n down-real-line) (defun down-real-line () (cond ((-p *argument*) (setq *argument* (-& *argument*)) ;Cannot handle *:min-fixnum. BIG deal (up-real-line)) (t (cond ((argument?) (not-last-line)) ((null (line-next (bp-line *editor-cursor*))) (send (bp-line *editor-cursor*) :insert-line ""))) (or (memq *editor-last-command* '(down-real-line up-real-line)) real-line-goal-column (setq real-line-target-column (edit-cursor-position-really *editor-cursor*))) (let ((line (nth-next-line (bp-line *editor-cursor*) *argument*))) (send *editor-cursor* :move line (position-from-graphic-position line (or real-line-goal-column real-line-target-column)))) nil))) (editor-bind-key #\control-p up-real-line) (defun up-real-line () (cond ((-p *argument*) (setq *argument* (-& *argument*)) (down-real-line)) (t (not-first-line) ;Error out if at beginning of buffer. (or (memq *editor-last-command* '(up-real-line down-real-line)) real-line-goal-column (setq real-line-target-column (edit-cursor-position-really *editor-cursor*))) (let ((line (nth-previous-line (bp-line *editor-cursor*) *argument*))) (send *editor-cursor* :move line (position-from-graphic-position line (or real-line-goal-column real-line-target-column)))) nil))) (editor-bind-key '(#\control-x #\control-n) set-goal-column) (defun set-goal-column () (cond ((and (null argument-supplied?) (=& 1 *argument*)) (setq real-line-goal-column (edit-cursor-position-really *editor-cursor*)) (with-notify-line-remaining (format terminal-io "Goal column is ~a" real-line-goal-column))) (t (setq real-line-goal-column nil) (with-notify-line-remaining (oustr "No goal column" terminal-io))))) (editor-bind-key #\control-d delete-character) (defun delete-character () (not-buffer-end) (cond ((argument?) (kill-and-delete-from-bp *editor-cursor* *argument*)) (t (send *editor-cursor* :delete-characters *argument*))) nil) ;;; Note that the key assignments of Rubout and c-Rubout have been swapped ;;; This makes Lisp Mode's bindings the default (editor-bind-key #\control-rubout backward-delete-character) (defun backward-delete-character () (not-buffer-begin) (cond ((not (null *overwrite-mode*)) (send *editor-cursor* :advance-pos (-& *argument*)) (self-insert #\space *argument*) (send *editor-cursor* :advance-pos (-& *argument*))) ((=& *argument* 1) (send *editor-cursor* :delete-characters -1)) (t (kill-and-delete-from-bp *editor-cursor* (-& *argument*)))) nil) ;The complicated computation to figure out how big the tab was can be ;eliminated by using the stored location of the cursor as the ;current "position-really". However, this is not valid except right after a ;screen re-fresh. This code is slower, but only for one char out of eight, ;and it makes fewer assumptions which might prove dangerous. (editor-bind-key #\rubout backward-delete-hacking-tabs) (defun backward-delete-hacking-tabs () (not-buffer-begin) (cond ((null *overwrite-mode*) ;;If there is a tab which gets expanded then this kill ;;will not really be right, but it can't be so there. (unless (null (argument?)) (kill-characters-from-bp *editor-cursor* (-& *argument*))) (loop for i from 1 to *argument* for char-was = (send *editor-cursor* :get-char-backward) do (send *editor-cursor* :delete-char) if (char= char-was #\tab) do (self-insert #\space (1-& (graphic-char-length #\tab (position-really (bp-line *editor-cursor*) (bp-position *editor-cursor*) (window-stream-x-size (edit-cursor-window *editor-cursor*)))))))) (t (backward-delete-character))) nil) (editor-bind-key #\return crlf) (defun crlf () (let ((pos (bp-position *editor-cursor*)) (line (bp-line *editor-cursor*))) (when (and (not (string-equal comment-end "")) (=& (line-char-count line) (+& pos (string-length comment-end))) (send line :match-string comment-end pos)) (send *editor-cursor* :set-pos (line-char-count line)))) (loop for i from 1 to *argument* for line = (bp-line *editor-cursor*) if (and (=& (bp-position *editor-cursor*) (line-char-count line)) (line-next line) (empty-line? (line-next line)) (line-next (line-next line)) (empty-line? (line-next (line-next line)))) do (send *editor-cursor* :to-beginning-of-next-line) else do (send (bp-line *editor-cursor*) :break (bp-position *editor-cursor*))) nil) (editor-bind-key #\line linefeed) (defun linefeed () (delete-horizontal-space) (loop for i from 1 to *argument* do (crlf)) (funcall (internal-editor-lookup-key #\tab)) nil) (editor-bind-key #\page new-window) (defun new-window () (cond ((and (=& *argument* 4) (null argument-supplied?)) ;just control-U (let ((y (window-stream-y-now (edit-cursor-window *editor-cursor*)))) (cursorpos y 0 terminal-io) (send terminal-io :raw-oustr (svref old-screen-image y) 0 *tty-width*) (send terminal-io :set-cursor-fried-flag t))) (t (let ((y-size (window-stream-y-size (edit-cursor-window *editor-cursor*)))) (cond ((and (null argument-supplied?) (=& *argument* 1)) (setq *argument* (ceiling (* y-size *recentering-fraction*))) (clear-one-screen-image old-screen-image) (send terminal-io :clear-screen) (setq re-generate-screen-really t) (setq *last-major-mode* "No Mode")) ((-p *argument*) (setq *argument* (+& y-size *argument*)))) (setq *argument* (max& 0 (min& *argument* (1-& y-size))))) (loop for i from 0 to *argument* for line first (bp-line *editor-cursor*) then (line-previous line) while (not (null (line-previous line))) finally (unless (null line) (setf (edit-cursor-home-line *editor-cursor*) line))))) nil) (editor-bind-key #\control-meta-r reposition-window) (defun reposition-window () (do ((line (bp-line *editor-cursor*) (line-previous line)) (chars)) ((null line) (ed-warn :unbalanced-parens)) (setq chars (line-chars line)) (when (and (+p (line-char-count line)) (char= (schar chars 0) left-paren)) (setf (edit-cursor-home-line *editor-cursor*) line) (return t))) nil) (editor-bind-key #\control-a beginning-of-line) (defun beginning-of-line () (send *editor-cursor* :set-pos 0) nil) (editor-bind-key #\control-e end-of-line) (defun end-of-line () (send *editor-cursor* :set-pos (line-char-count (bp-line *editor-cursor*))) nil) (editor-bind-key #\control-g editor-abort) (defun editor-abort () (steve-beep) nil) (editor-bind-key #\bell editor-abort) (editor-bind-key '(#\control-x #\bell) editor-abort) (editor-bind-key '(#\control-x #\control-g) editor-abort) (editor-bind-key #\control-o open-line) (defun open-line () (loop for i from 1 to *argument* do (send (bp-line *editor-cursor*) :break (bp-position *editor-cursor*)) do (send *editor-cursor* :to-end-of-previous-line)) nil) ; ;File i/o commands and related functions. ; (editor-bind-key #\meta-~ buffer-not-modified) (defun buffer-not-modified () (setf (buffer-modified? *editor-buffer*) nil) nil) (editor-bind-key '(#\control-x #\control-q) set-file-read-only) (defun set-file-read-only (&optional (buffer *editor-buffer*)) (cond ((=& *argument* 0) (setf (buffer-access buffer) buffer-access-any) (with-error-line-remaining (oustr "Any Access Allowed" terminal-io))) ((+p *argument*) (setf (buffer-access buffer) (logior& (buffer-access buffer) buffer-access-file-read-only)) (with-error-line-remaining (oustr "File Read Only" terminal-io))) (t (setf (buffer-access buffer) (logior& (buffer-access buffer) buffer-access-buffer-read-only)) (with-error-line-remaining (oustr "Buffer Read Only" terminal-io)))) nil) (editor-bind-key '(#\control-x #\control-w) write-file) (defun write-file () (with-query-line (princ "Write file: " terminal-io) (let ((filename (editor-default (read-file-name)))) (output-buffer-to-file filename *editor-buffer* T))) nil) (editor-bind-key '(#\control-x #\control-s) save-file) (defun save-file (&optional (buffer *editor-buffer*) &aux ok-to-write?) (cond ((null (buffer-modified? buffer)) (with-notify-line-remaining (oustr "(No changes need to be written)" terminal-io))) ((send buffer :send-if-handles :not-last-version?) (with-double-second-line (with-double-line (format terminal-io "The file now on disk is not what you last visited or saved!~ ~%Should I write the file anyway (Y or N)? ") (setq ok-to-write? (ed-y-or-n-p))))) (:else (setq ok-to-write? t))) (when ok-to-write? (send buffer :set-file-name (buffer-file-name buffer)) (output-buffer-to-file (buffer-file-name buffer) buffer)) nil) (editor-bind-key '(#\control-x #\control-v) visit-file) (defun visit-file () (with-query-line (princ "Visit File: " terminal-io) (let ((file-name (read-file-name))) (when (buffer-modified? *editor-buffer*) (with-error-line (format terminal-io "Write out changes to ~a (Y or N)? " (send (buffer-file-name *editor-buffer*) :string-for-editor)) (when (ed-y-or-n-p) (save-file)))) (read-file-into-buffer (editor-default file-name)))) nil) (editor-bind-key '(#\control-x #\control-r) visit-file) (bindmetax revert-file revert-file) (defun revert-file () (with-query-line (oustr "Restore file from disk? " terminal-io) (when (ed-y-or-n-p) (mark-beginning) (let ((line (count-between (pop-mark) *editor-cursor*)) (index (bp-position *editor-cursor*))) (read-file-into-buffer (buffer-file-name *editor-buffer*)) (let (we-won) (unwind-protect (progn (let ((*argument* (1-& line))) (forward-line)) (send *editor-cursor* :advance-pos index) (setq we-won t)) (unless we-won (send *editor-cursor* :move (last-line) 0)))))))) (editor-bind-key '(#\control-x #\control-f) find-file) (defun find-file () (with-query-line (princ "Find File: " terminal-io) (point-selected (editor-default (read-file-name)))) nil) (editor-bind-key '(#\control-x #\B) select-buffer) (defun select-buffer () (with-query-line (format terminal-io "Select buffer (~a): " (buffer-name *last-buffer-selected*)) (let ((name (prescan #'read-buffer-name 0))) (if (not (stringp name)) (steve-beep) (select-buffer-named name *last-buffer-selected*)))) nil) (editor-bind-key '#\control-meta-l select-last-buffer) (defun select-last-buffer () (if *last-buffer-selected* (point-selected *last-buffer-selected*) (ed-warn "No buffer to select")) nil) (editor-bind-key '#\meta-page select-last-buffer) ; ;A bunch of cursor movement and window movement functions. ; (defun adjust-window-by-lines (lines &optional (point *editor-cursor*)) (let ((home (nth-next-line (edit-cursor-home-line point) lines))) (setf (edit-cursor-home-line point) home (edit-cursor-home-pos point) 0) (when (null (point-on-screen? point)) (send point :move (edit-cursor-home-line point) 0)))) (editor-bind-key #\control-v next-screen) (defun next-screen (&optional (point *editor-cursor*)) (cond ((argument?) (adjust-window-by-lines *argument* point)) ((buffer-end-on-screen? point) (ed-warn "Last Screen")) (t (loop with win = (edit-cursor-window point) with x-size = (window-stream-x-size win) with line = (edit-cursor-home-line point) with max-i = (max& (-& (window-stream-y-size win) 2) 1) for i first 0 then (+& i (aprox-line-height line x-size)) for next = (line-next line) while (<& i max-i) until (null next) do (setq line next) finally (setf (edit-cursor-home-line point) line) finally (setf (edit-cursor-home-pos point) 0) finally (send point :move line 0)))) nil) (defun previous-screen (&optional (point *editor-cursor*)) (cond ((argument?) (adjust-window-by-lines (-& *argument*) point)) (t (not-buffer-begin point) (loop with win = (edit-cursor-window point) with x-size = (window-stream-x-size win) with line = (edit-cursor-home-line point) with max-i = (max& (-& (window-stream-y-size win) 2) 1) for i first 0 then (+& i (aprox-line-height line x-size)) for previous = (line-previous line) while (<& i max-i) until (null previous) do (setq line previous) finally (setf (edit-cursor-home-line point) line) finally (setf (edit-cursor-home-pos point) 0) finally (send point :move line 0)))) nil) (editor-bind-key #\meta-v previous-screen) (editor-bind-key #\meta-r move-to-screen-edge) (defun move-to-screen-edge () (let* ((x-size (window-stream-x-size (edit-cursor-window *editor-cursor*))) (y-size (window-stream-y-size (edit-cursor-window *editor-cursor*))) (target (cond ((and (=& *argument* 1) (null argument-supplied?)) (/& (1+& y-size) 3)) ((-p *argument*) (+& y-size *argument*)) (t *argument*)))) (setq target (max& 0 (min& target y-size))) (loop for i from 0 to target for line first (edit-cursor-home-line *editor-cursor*) then (line-next line) for j first (aprox-line-height line x-size) then (+& j (aprox-line-height line x-size)) while (and (<& j target) (line-next line)) ; Finally (send *editor-cursor* :move line ; (position-from-graphic-position ; line ; (edit-cursor-position-really *editor-cursor*))) finally (send *editor-cursor* :move line 0))) nil) (editor-bind-key #\meta-< goto-beginning) ;See documentation for hair. (defun goto-beginning () (push-mark *editor-cursor*) (send *editor-cursor* :move (buffer-content *editor-buffer*) 0) (setf (edit-cursor-home-line *editor-cursor*) (buffer-content *editor-buffer*)) nil) (editor-bind-key #\meta-> goto-end) (defun goto-end () (let ((last (last-line))) (push-mark *editor-cursor*) (send *editor-cursor* :move last (line-char-count last))) nil) (editor-bind-key #\meta-i insert-tab) (defun insert-tab () (self-insert #\tab) nil) (editor-bind-key #\meta-tab insert-tab) (editor-bind-key #\control-i #\tab) ;Multiple windows. (defvar dash-buffer) (defun make-dash-buffer (&aux (width (1-& (window-stream-x-size (edit-cursor-window *editor-cursor*))))) (setq dash-buffer (make-instance 'buffer :name "Dash Buffer" :content (make-instance 'line :chars (make-string width :initial-element #\-) :char-count width :bashed? 0 :graphic-length width :buffer-pointers nil :previous nil :next nil) :file-name nil :environment nil :mark-ring (make-vector mark-ring-size :initial-element nil) :mark-ring-index 0 :access 0 :modified? nil)) (setf (line-buffer (buffer-content dash-buffer)) dash-buffer)) (defun put-dashes (y) (or (boundp 'dash-buffer) (make-dash-buffer)) (let ((point (make-instance 'edit-cursor :buffer dash-buffer :line (buffer-content dash-buffer) :position 0 :home-line (buffer-content dash-buffer) :window (create-window 0 y *tty-width* 1)))) (push point *all-edit-cursors*))) (defun remove-dashes () (loop for point in *all-edit-cursors* if (eq (bp-buffer point) dash-buffer) do (setq *all-edit-cursors* (delq point *all-edit-cursors*)))) (editor-bind-key '(#\control-x #\1) one-window) (defun one-window () (remove-dashes) (loop for point in *all-edit-cursors* do (setf (edit-cursor-window point) nil)) (setf (edit-cursor-window *editor-cursor*) (create-window 0 0 *tty-width* *window-height*)) nil) (editor-bind-key '(#\control-x #\2) two-windows) (defun two-windows () (let* ((window (edit-cursor-window *editor-cursor*)) (new-window (send window :split-window))) (put-dashes (1-& (window-stream-y-pos new-window))) (select-point (create-edit-cursor (bp-buffer *editor-cursor*) (bp-line *editor-cursor*) (bp-position *editor-cursor*) new-window))) (setq re-generate-screen-really t) nil) (editor-bind-key '(#\control-x #\3) view-two-windows) (defun view-two-windows () (let* ((window (edit-cursor-window *editor-cursor*)) (new-window (send window :split-window))) (put-dashes (1-& (window-stream-y-pos new-window))) (create-edit-cursor (bp-buffer *editor-cursor*) (bp-line *editor-cursor*) (bp-position *editor-cursor*) new-window)) (setq re-generate-screen-really t) nil) (editor-bind-key '(#\control-x #\4) visit-in-other-window) (defun visit-in-other-window () (let* ((window (edit-cursor-window *editor-cursor*)) (new-window (send window :split-window))) (put-dashes (1-& (window-stream-y-pos new-window))) (select-point (create-edit-cursor (bp-buffer *editor-cursor*) (bp-line *editor-cursor*) (bp-position *editor-cursor*) new-window)) (find-file)) (setq re-generate-screen-really t) nil) (defun find-other-window () (or (loop for point in (memq *editor-cursor* *all-edit-cursors*) if (and (not (eq *editor-cursor* point)) (not (null (edit-cursor-window point))) (not (eq dash-buffer (bp-buffer point)))) return point) (loop for point in *all-edit-cursors* if (and (not (eq *editor-cursor* point)) (not (null (edit-cursor-window point))) (not (eq dash-buffer (bp-buffer point)))) return point))) (editor-bind-key '(#\control-x #\O) other-window) (defun other-window () (let ((point (find-other-window))) (cond ((null point) (select-last-buffer)) (t (select-point point)))) nil) (editor-bind-key #\control-meta-v scroll-other-window) (defun scroll-other-window () (let ((point (find-other-window))) (when (null point) (ed-warn "There is only one window")) (next-screen point)) nil) ; ;Now some of the "syntactic" commands. ; (editor-bind-key #\meta-f forward-word) (defun forward-word () (cond ((plusp& *argument*) (not-buffer-end) (preserving-point-on-error (loop for i from 1 to *argument* do (send *editor-cursor* :forward-over-not-word) (not-buffer-end) (send *editor-cursor* :forward-over-word)))) ((zerop& *argument*) nil) (t (setq *argument* (-& *argument*)) (backward-word))) nil) (editor-bind-key #\meta-b backward-word) (defun backward-word () (cond ((plusp& *argument*) (not-buffer-begin) (preserving-point-on-error (loop for i from 1 to *argument* do (send *editor-cursor* :backward-over-not-word) (not-buffer-begin) (send *editor-cursor* :backward-over-word)))) ((zerop& *argument*) nil) (t (setq *argument* (-& *argument*)) (forward-word))) nil) (editor-bind-key #\control-meta-f forward-sexp) (defun forward-sexp () (cond ((plusp& *argument*) (not-buffer-end) (preserving-point-on-error (loop for i from 1 to *argument* do (bp-forward-over-sexp *editor-cursor*)))) ((zerop& *argument*) nil) (t (setq *argument* (-& *argument*)) (backward-sexp))) nil) (editor-bind-key #\control-meta-b backward-sexp) (defun backward-sexp () (cond ((plusp& *argument*) (not-buffer-begin) (preserving-point-on-error (loop for i from 1 to *argument* do (bp-backward-over-sexp *editor-cursor*)))) ((zerop& *argument*) nil) (t (setq *argument* (-& *argument*)) (forward-sexp))) nil) (editor-bind-key #\control-meta-\) forward-up-list) (defun forward-up-list () (not-buffer-end) (let ((bp (copy-bp *editor-cursor*))) (loop for line = (bp-line bp) for pos = (bp-position bp) do (bp-forward-over-sexp bp) until (char= (send bp :get-char) right-paren) if (and (eq (bp-line bp) line) (=& (bp-position bp) pos)) do (send bp :advance-pos 1)) (send bp :advance-pos 1) (send *editor-cursor* :move (bp-line bp) (bp-position bp)) (send bp :send-if-handles :expire)) nil) (editor-bind-key #\control-meta-\( backward-up-list) (defun backward-up-list () (not-buffer-begin) (let ((bp (copy-bp *editor-cursor*))) (loop for line = (bp-line bp) for pos = (bp-position bp) do (bp-backward-over-sexp bp) until (char= (send bp :peek-char-backward) left-paren) if (and (eq (bp-line bp) line) (=& (bp-position bp) pos)) do (send bp :advance-pos -1)) (send bp :advance-pos -1) (send *editor-cursor* :move (bp-line bp) (bp-position bp)) (send bp :send-if-handles :expire)) nil) (editor-bind-key #\control-meta-u backward-up-list) (editor-bind-key #\control-meta-n forward-list) (defun forward-list () (cond ((zerop *argument*) nil) ((minusp *argument*) (setq *argument* (- *argument*)) (backward-list)) (:normally (not-buffer-end) (loop repeat *argument* do (loop for line = (bp-line *editor-cursor*) for pos = (bp-position *editor-cursor*) do (send *editor-cursor* :forward-over-syntax (logior& prefix-mask white-space-mask)) until (or (send *editor-cursor* :end-of-buffer?) (char= (send *editor-cursor* :get-char) left-paren)) do (send *editor-cursor* :forward-over-sexp) if (and (eq (bp-line *editor-cursor*) line) (=& (bp-position *editor-cursor*) pos)) do (send *editor-cursor* :advance-pos 1)) (or (send *editor-cursor* :end-of-buffer?) (send *editor-cursor* :forward-over-sexp))) nil))) (editor-bind-key #\control-meta-p backward-list) (defun backward-list () (cond ((zerop *argument*) nil) ((minusp *argument*) (setq *argument* (- *argument*)) (forward-list)) (:normally (not-buffer-begin) (loop repeat *argument* do (loop for line = (bp-line *editor-cursor*) for pos = (bp-position *editor-cursor*) do (send *editor-cursor* :backward-over-syntax (logior& prefix-mask white-space-mask)) until (or (send *editor-cursor* :beginning-of-buffer?) (char= (send *editor-cursor* :peek-char-backward) right-paren)) do (send *editor-cursor* :backward-over-sexp) if (and (eq (bp-line *editor-cursor*) line) (=& (bp-position *editor-cursor*) pos)) do (send *editor-cursor* :advance-pos 1)) (or (send *editor-cursor* :beginning-of-buffer?) (send *editor-cursor* :backward-over-sexp))) nil))) (editor-bind-key #\control-meta-d down-list) (defun down-list () (not-buffer-end) (loop for line = (bp-line *editor-cursor*) for pos = (bp-position *editor-cursor*) do (send *editor-cursor* :forward-over-syntax (logior& prefix-mask white-space-mask)) until (or (send *editor-cursor* :end-of-buffer?) (char= (send *editor-cursor* :get-char) left-paren)) do (send *editor-cursor* :forward-over-sexp) if (and (eq (bp-line *editor-cursor*) line) (=& (bp-position *editor-cursor*) pos)) do (send *editor-cursor* :advance-pos 1)) (or (send *editor-cursor* :end-of-buffer?) (send *editor-cursor* :advance-pos 1)) nil) ; ;sentence, paragraph and comment functions. ; (defvar *single-space-sentence-delimiter*) ;(defun sentence-end (bp) ;;Waste a full CONS per character! YOW! ; (let ((mark (send bp :get-mark))) ; (prog1 (and (memq (send bp :get-char-forward) '(#\. #\? #\!)) ; (loop while (memq (send bp :get-char-forward) '(#\) #\] #\' #\")) ; finally ; (return ; (or (memq (send bp :peek-char-backward) ; '(#\return #\page #\tab)) ; (and (char= (send bp :peek-char-backward) #\space) ; (or *single-space-sentence-delimiter* ; (char= (send bp :get-char) #\space))))))) ; (send bp :goto-mark mark)))) (defun sentence-end (bp) (let* ((line (bp-line bp)) (chars (line-chars line)) (max (line-char-count line)) (pos (bp-position bp))) (and (<& pos max) (memq (schar chars pos) '(#\. #\? #\!)) (loop do (setq pos (1+& pos)) until (>=& pos max) while (memq (schar chars pos) '(#\) #\] #\' #\")) finally (return t)) (or (>=& pos max) (char= (schar chars pos) #\space)) (or *single-space-sentence-delimiter* (>= (setq pos (1+& pos)) max) (char= (schar chars pos) #\space))))) (defun forward-sentence (&optional (bp *editor-cursor*)) (cond ((-p *argument*) (let ((*argument* (-& *argument*))) (backward-sentence bp))) (t (not-buffer-end bp) (loop repeat *argument* do (loop do (send bp :advance-pos 1) until (and (=& (bp-position bp) (1-& (line-char-count (bp-line bp)))) (paragraph-last-line (bp-line bp))) until (sentence-end bp)) do (send bp :advance-pos 1)))) nil) ;;; Fixed to leave point in the right place (defun backward-sentence (&optional (bp *editor-cursor*)) (cond ((-p *argument*) (let ((*argument* (-& *argument*))) (forward-sentence bp))) (t (not-buffer-begin bp) (send bp :backward-over-not-word) (loop repeat *argument* do (loop do (send bp :advance-pos -1) until (and (0p (bp-position bp)) (paragraph-first-line (bp-line bp))) until (sentence-end bp)) ) (send bp :forward-over-not-word))) nil) (editor-bind-key #\meta-a backward-sentence) (editor-bind-key #\meta-e forward-sentence) (editor-bind-key #\meta-k kill-sentence) (defun kill-sentence () (let ((bp (copy-bp *editor-cursor*))) (forward-sentence bp) (push-mark-no-copy bp) (kill-region) (send bp :send-if-handles :expire) nil)) (editor-bind-key '(#\control-x #\rubout) backward-kill-sentence) (defun backward-kill-sentence () (let ((bp (copy-bp *editor-cursor*))) (backward-sentence bp) (push-mark-no-copy bp) (kill-region) (send bp :send-if-handles :expire) nil)) ; ;Paragraphs. ;We do not have search patterns, so we have to make do with something ;weaker. ; ;We will allow paragraphs to be delimited by either a line with leading ;white-space (for text) or a blank line, for program modes. ; (defvar *paragraph-text-style*) ;(defun paragraph-line (line) ; (or (empty-line? line) ; (null (line-previous line)) ; (and *paragraph-text-style* ; (or (null (line-next line)) ; (0p (line-char-count (line-next line))) ; (white-space? (char (line-chars (line-next line)) 0)))))) (defun paragraph-first-line (line) (and (not (empty-line? line)) (or (null (line-previous line)) (empty-line? (line-previous line)) (and *paragraph-text-style* (white-space? (schar (line-chars line) 0))) (char= (schar (line-chars line) 0) #\page) (and (char= (schar (line-chars (line-previous line)) 0) #\page) (loop for pos from 1 below (line-char-count (line-previous line)) for char = (schar (line-chars (line-previous line)) pos) always (white-space? char)))))) (defun paragraph-last-line (line) (and (not (empty-line? line)) (or (null (line-next line)) (empty-line? (line-next line)) (and *paragraph-text-style* (white-space? (schar (line-chars (line-next line)) 0)))))) (editor-bind-key #\meta-h mark-paragraph) (defun mark-paragraph () (let ((mark (copy-bp *editor-cursor*))) (push-mark *editor-cursor*) (let ((*argument* (if (-p *argument*) 1 *argument*))) (forward-paragraph mark)) (let ((*argument* (if (-p *argument*) *argument* 1))) (backward-paragraph *editor-cursor*)) (push-mark-no-copy mark) nil)) (defun forward-paragraph (&optional (bp *editor-cursor*)) (cond ((-p *argument*) (let ((*argument* (-& *argument*))) (backward-paragraph bp))) (t (not-buffer-end) (loop repeat *argument* ;;Move over blank lines. do (loop for line first (bp-line bp) then next for next = (line-next line) while (and (not (null next)) (empty-line? line)) finally (send bp :move line 0)) ;;Search for end of paragraph. do (loop for line first (bp-line bp) then next for next = (line-next line) until (paragraph-last-line line) finally (if (null next) (send bp :move line (line-char-count line)) (send bp :move next 0))))))) (defun backward-paragraph (&optional (bp *editor-cursor*)) (cond ((-p *argument*) (let ((*argument* (-& *argument*))) (forward-paragraph bp))) (t (not-buffer-begin) (loop repeat *argument* ;;Skip blank lines. if (and (0p (bp-position bp)) (line-previous (bp-line bp))) do (send bp :to-end-of-previous-line) do (loop for line first (bp-line bp) then prev for prev = (line-previous line) while (and (not (null prev)) (empty-line? line)) finally (send bp :move line 0)) ;;Now search for paragraph beginning. do (loop for line first (bp-line bp) then prev for prev = (line-previous line) until (paragraph-first-line line) finally (send bp :move line 0)))))) (editor-bind-key #\meta-] forward-paragraph) (editor-bind-key #\meta-[ backward-paragraph) ;The mark ring. ;Now the emacs mark ring commands. (editor-bind-key #\control-@ set-pop-mark) (defun set-pop-mark () (cond ((and (null argument-supplied?) (=& *argument* 1)) (push-mark *editor-cursor*)) ((=& *argument* 4) (goto-mark (pop-mark))) ((>=& *argument* 16) (pop-mark))) nil) (editor-bind-key #\control-space set-pop-mark) (editor-bind-key '(#\control-x #\control-x) exchange-point-and-mark) (defun exchange-point-and-mark () (goto-mark (prog1 (pop-mark) (push-mark *editor-cursor*))) nil) (editor-bind-key #\control-< mark-beginning) (defun mark-beginning () (push-mark-no-copy (make-bp *editor-buffer* (buffer-content *editor-buffer*) 0)) nil) (editor-bind-key #\control-> mark-end) (defun mark-end () (let ((line (last-line))) (push-mark-no-copy (make-bp *editor-buffer* line (line-char-count line)))) nil) (editor-bind-key #\meta-@ mark-word) (defun mark-word () (let ((mark (copy-bp *editor-cursor*))) (loop for i from 0 below *argument* do (send mark :forward-over-not-word) do (send mark :forward-over-word)) (push-mark-no-copy mark) nil)) (editor-bind-key #\control-meta-h mark-defun) (defun mark-defun () (find-beginning-of-defun *editor-cursor*) (bp-forward-over-sexp *editor-cursor*) (push-mark-no-copy (make-bp *editor-buffer* (bp-line *editor-cursor*) (bp-position *editor-cursor*))) (bp-backward-over-sexp *editor-cursor*) nil) (editor-bind-key #\control-meta-backspace mark-defun) (editor-bind-key #\meta-backspace mark-defun) (editor-bind-key '(#\control-x #\h) mark-whole-buffer) (defun mark-whole-buffer () (mark-end) (send *editor-cursor* :move (buffer-content *editor-buffer*) 0) nil) (editor-bind-key '(#\control-x #\control-p) mark-page) (defun mark-page () (let ((bp (copy-bp *editor-cursor*))) (send *editor-cursor* :set-pos 0) (send *editor-cursor* :backward-to-char #\page) (send bp :set-pos (line-char-count (bp-line bp))) (send bp :forward-to-char #\page) (unless (buffer-end? bp) (send bp :advance-pos 1)) (push-mark-no-copy bp)) nil) ;;; This is broken (still?) (editor-bind-key '(#\control-x #\[) previous-page) (defun previous-page () (cond ((plusp& *argument*) (not-buffer-begin) (loop repeat *argument* do (send *editor-cursor* :advance-pos -1) (send *editor-cursor* :backward-to-char #\page))) ((zerop& *argument*) nil) (t (setq *argument* (-& *argument*)) (next-page))) nil) (editor-bind-key '(#\control-x #\]) next-page) (defun next-page () (cond ((plusp& *argument*) (not-buffer-end) (loop repeat *argument* do (send *editor-cursor* :forward-to-char #\page) (send *editor-cursor* :advance-pos 1))) ((zerop& *argument*) nil) (t (setq *argument* (-& *argument*)) (previous-page))) nil) ;More syntactic commands. (editor-bind-key #\control-meta-a beginning-of-defun) (defun beginning-of-defun (&optional (bp *editor-cursor*)) (cond ((-p *argument*) (setq *argument* (-& *argument*)) (end-of-defun bp)) (t (not-buffer-begin bp) (loop for i from 1 to *argument* do (send bp :advance-pos -1) ;always at least one char. do (find-beginning-of-defun bp)))) nil) (editor-bind-key #\control-meta-[ beginning-of-defun) (defun find-beginning-of-defun (point) (do ((line (bp-line point) (line-previous line)) (chars)) ((null line)) (send point :move line 0) (setq chars (line-chars line)) (when (and (+p (line-char-count line)) (char= (schar chars 0) left-paren)) (return t)))) (editor-bind-key #\control-meta-e end-of-defun) ;;; This is wrong: seems to only search for an empty line (defun end-of-defun (&optional (bp *editor-cursor*)) (cond ((-p *argument*) (let ((*argument* (-& *argument*))) (beginning-of-defun bp))) (t (not-buffer-end bp) (loop repeat *argument* do (loop for line first (bp-line bp) then next for next = (line-next line) while (and next (empty-line? line)) finally (send bp :move line (line-char-count line))) do (loop for line first (bp-line bp) then next for next = (line-next line) while (and next (null (empty-line? line))) finally (send bp :move line (line-char-count line))))))) (editor-bind-key #\control-meta-] end-of-defun) (editor-bind-key #\meta-\( make-parens) (defun make-parens () (self-insert left-paren 1) (preserving-point (when argument-supplied? (forward-sexp)) (let ((*display-matching-paren* 0)) (self-insert right-paren 1))) nil) (editor-bind-key #\control-t transpose-characters) (defun transpose-characters () (cond ((and (not (argument?)) (send *editor-cursor* :end-of-line?)) ;Twiddle the two characters before the cursor. (send *editor-cursor* :advance-pos -2) (let ((char (send *editor-cursor* :get-char))) (send *editor-cursor* :delete-char) (send *editor-cursor* :advance-pos 1) (send *editor-cursor* :insert-char char))) (t (send *editor-cursor* :advance-pos -1) (let ((char (send *editor-cursor* :get-char))) (send *editor-cursor* :delete-char) (send *editor-cursor* :advance-pos *argument*) (send *editor-cursor* :insert-char char) ))) nil) (editor-bind-key '(#\control-x #\t) transpose-regions) (defun transpose-regions () (setq *last-kill-command-count* -1000) (let ((mark (get-mark)) (carcass)) (kill-region) (setq carcass (pop-kill)) (goto-mark (pop-mark)) (kill-region) (un-kill carcass) (goto-mark mark) (un-kill) (pop-kill) nil)) ;;; Finally, a generic twiddler 2/83 -- Tim (defun exchange-subr (motion-fn n) (cond ((plusp& n) (exchange-subr-+n motion-fn n)) ((minusp& n) (exchange-subr--n motion-fn n)) (:zerop& (exchange-subr-0 motion-fn n))) nil) ;;; For 0 argument: exchange at point & mark (defun exchange-subr-0 (motion-fn n) (region (bp1 bp2) (with-bp* ((bp1l (funcall-for-bp motion-fn -1 (funcall-for-bp motion-fn 1 bp1))) (bp2r (funcall-for-bp motion-fn 1 bp2)) (bp2l (funcall-for-bp motion-fn -1 bp2r)) (bp1r (funcall-for-bp motion-fn 1 bp1l))) (let ((buf2 (copy-interval bp2l bp2r t))) (cond ((equal bp1r bp2l) (delete-interval bp2l bp2r t) (move-bp *editor-cursor* (insert-interval bp1l buf2)) (move-bp (mark) bp2r)) (:normally (let ((buf1 (copy-interval bp1l bp1r t))) (delete-interval bp2l bp2r t) (delete-interval bp1l bp1r t) (move-bp *editor-cursor* (insert-interval bp2l buf1)) (move-bp (mark) (insert-interval bp1r buf2))))))))) ;;; For positive args: twiddle and move past (defun exchange-subr-+n (motion-fn n) (move-bp *editor-cursor* (funcall-for-bp motion-fn 1 (funcall-for-bp motion-fn -2 (funcall-for-bp motion-fn 1 *editor-cursor*)))) (dotimes (i n) (with-bp* ((bp1r (make-temp-bp *editor-cursor*)) (bp2r (funcall-for-bp motion-fn 1 bp1r)) (bp2l (funcall-for-bp motion-fn -1 bp2r)) (bp1l (funcall-for-bp motion-fn -1 bp1r))) (let ((buf2 (copy-interval bp2l bp2r t))) (cond ((equal bp1r bp2l) (delete-interval bp2l bp2r t) (insert-interval bp1l buf2) (move-bp *editor-cursor* bp2r)) (:normally (let ((buf1 (copy-interval bp1l bp1r t))) (delete-interval bp2l bp2r t) (delete-interval bp1l bp1r t) (insert-interval bp1r buf2) (move-bp *editor-cursor* (insert-interval bp2l buf1))))))))) ;;; For negative args: move back in between then twiddle (defun exchange-subr--n (motion-fn n) (move-bp *editor-cursor* (funcall-for-bp motion-fn 1 (funcall-for-bp motion-fn -1 *editor-cursor*))) (do ((i 0 (1- i))) ((<= i n)) (with-bp* ((bp2r (make-temp-bp *editor-cursor*)) (bp1l (funcall-for-bp motion-fn -2 bp2r)) (bp1r (funcall-for-bp motion-fn 1 bp1l)) (bp2l (funcall-for-bp motion-fn -1 bp2r))) (let ((buf2 (copy-interval bp2l bp2r t))) (cond ((equal bp1r bp2l) (delete-interval bp2l bp2r t) (move-bp *editor-cursor* (insert-interval bp1l buf2))) (:normally (let ((buf1 (copy-interval bp1l bp1r t))) (delete-interval bp2l bp2r t) (delete-interval bp1l bp1r t) (insert-interval bp2l buf1) (move-bp *editor-cursor* (insert-interval bp1r buf2))))))))) ;;; Now we can describe these in terms of the above functions (defun transpose-words () (exchange-subr #'forward-word *argument*)) (defun transpose-sexps () (exchange-subr #'forward-sexp *argument*)) (defun transpose-lines () (exchange-subr #'forward-line *argument*)) (editor-bind-key '(#\control-x #\control-t) transpose-lines) (editor-bind-key '#\meta-t transpose-words) (editor-bind-key '#\control-meta-t transpose-sexps) ;More random stuff. (editor-bind-key #\meta-m back-to-indentation) (defun back-to-indentation () (send *editor-cursor* :set-pos 0) (loop while (not (send *editor-cursor* :end-of-line?)) for chr = (send *editor-cursor* :get-char-forward) do (when (not (horizontal-white-space? chr)) (send *editor-cursor* :advance-pos -1) (return))) nil) (editor-bind-key #\meta-return back-to-indentation) (editor-bind-key #\control-meta-m back-to-indentation) (editor-bind-key #\meta-^ delete-indentation) (defun delete-indentation () (send *editor-cursor* :set-pos 0) (send (bp-line *editor-cursor*) :delete-line-separator) (delete-horizontal-space) (unless (or (char= right-paren (send *editor-cursor* :get-char)) (char= left-paren (send *editor-cursor* :peek-char-backward))) (self-insert #\space 1)) nil) (editor-bind-key #\control-meta-^ delete-indentation) (editor-bind-key #\control-meta-o split-line) (defun split-line () (send *editor-cursor* :forward-over-horizontal-white-space) (multiple-value-bind (xpos ypos) (edit-cursor-position-really *editor-cursor*) (send (bp-line *editor-cursor*) :break (bp-position *editor-cursor*)) (send *editor-cursor* :to-end-of-previous-line) ;Fix for new :break. (indent-line-to-column (line-next (bp-line *editor-cursor*)) xpos) (loop with line = (line-next (bp-line *editor-cursor*)) for i from 1 below *argument* ;Skip first iteration. do (send line :break 0)) nil)) (editor-bind-key #\meta-s center-line) (defun center-line () (let ((bp (copy-bp *editor-cursor*)) (x-size (window-stream-x-size (edit-cursor-window *editor-cursor*)))) (loop for i from 0 below (abs& *argument*) do (send bp :set-pos 0) do (delete-horizontal-space bp) do (send bp :set-pos (line-char-count (bp-line bp))) do (send bp :backward-over-horizontal-white-space) do (indent-line-to-column (bp-line bp) (max& 0 (/& (-& x-size (bp-position bp)) 2))) do (and (line-next (bp-line bp)) (send bp :to-beginning-of-next-line))) (when (or argument-supplied? (not (=& 1 *argument*))) (send *editor-cursor* :move (bp-line bp) (bp-position bp))) (send bp :send-if-handles :expire) nil)) (editor-bind-key #\control-meta-@ mark-sexp) (defun mark-sexp () (let ((mark (make-bp *editor-buffer* (bp-line *editor-cursor*) (bp-position *editor-cursor*)))) (cond ((=& *argument* 0)) ((>& *argument* 0) (not-buffer-end) (loop for i from 1 to *argument* do (bp-forward-over-sexp mark))) (t (not-buffer-begin) (loop for i from 1 to (- *argument*) do (bp-backward-over-sexp mark)))) (push-mark-no-copy mark) nil)) (defun lines-in-sexp (point &aux (bp (copy-bp point))) (bp-forward-over-sexp bp) (count-lines-between point bp)) (editor-bind-key #\control-meta-q indent-sexp) (defun indent-sexp () (not-buffer-end) (loop with bp = (copy-bp *editor-cursor*) for line first (line-next (bp-line *editor-cursor*)) then (line-next line) for i from 1 below (lines-in-sexp *editor-cursor*) do (send bp :move line 0) do (lisp-indentor (copy-bp bp)))) (editor-bind-key #\control-meta-g indent-sexp) (editor-bind-key #\control-meta-z exit-editing-level) (defun exit-editing-level () (throw 'exit-editor t)) (editor-bind-key '(#\control-x #\control-z) return-to-superior) (defun return-to-superior () (with-no-passall (send terminal-io :set-device-mode :ttysync *terminal-ttsync-on-exit*) (setq steve:creamed-tty-lines-to steve:*tty-height*) (editor-destroy-mode-line-for-exiting) (valret) (send terminal-io :set-device-mode :ttysync nil) (cursorpos 'c terminal-io)) nil) (editor-bind-key #\control-altmode exit-editing-level) (editor-bind-key #\control-C not-in-this-editor-you-dont) (defun not-in-this-editor-you-dont () (ed-warn "Control-C is reserved for future use..."))