;-*- Mode:LISP; Package:STEVE; Readtable:CL; Base:10 -*- ;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. ; ; ;Killing commands. ; ;The kill ring is a vector of vectors. ;Each entry is a vector of strings. ; (defparameter kill-ring-size 8) (defparameter kill-ring (make-vector kill-ring-size :initial-element (make-vector 1 :initial-element (make-string 0)))) (defvar kill-ring-index 0) (defun push-kill (carcass) (setq kill-ring-index (1+& kill-ring-index)) (if (>=& kill-ring-index kill-ring-size) (setq kill-ring-index 0)) (setf (aref kill-ring kill-ring-index) carcass)) (defun pop-kill () (prog1 (or (svref kill-ring kill-ring-index) (ed-lose "No kill to pop")) (loop do (setq kill-ring-index (1-& kill-ring-index)) do (when (-p kill-ring-index) (setq kill-ring-index (1-& kill-ring-size))) until (svref kill-ring kill-ring-index)))) (defun get-kill (&optional (offset 0)) (svref kill-ring (mod& (-& kill-ring-index offset) kill-ring-size))) (defsubst string-copy (string length) (%string-replace (make-string length) string 0 0 length)) (defun count-lines-between (bp1 bp2) (count-lines (bp-line bp1) (bp-line bp2))) (defun count-lines (line target) (do ((line line (line-next line)) (count 1 (1+& count))) ((eq line target) count))) (defvar *last-kill-command-count* -1000) (defvar *last-un-kill-command-count* -1000) (defun push-or-append-kill-after (carcass) (if (=& (1+& *last-kill-command-count*) *editor-command-count*) (push-kill (append-carcasses (pop-kill) carcass)) (push-kill carcass)) (setq *last-kill-command-count* *editor-command-count*)) (defun push-or-append-kill-before (carcass) (if (=& (1+& *last-kill-command-count*) *editor-command-count*) (push-kill (append-carcasses carcass (pop-kill))) (push-kill carcass)) (setq *last-kill-command-count* *editor-command-count*)) (defun kill-between-marks (bp1 bp2 &aux lines) (multiple-value (bp1 bp2) (order-bps bp1 bp2)) (setq lines (count-lines-between bp1 bp2)) (let ((carcass (make-vector lines :initial-element nil))) (if (=& lines 1) (setf (aref carcass 0) (string-subseq (line-chars (bp-line bp1)) (bp-position bp1) (-& (bp-position bp2) (bp-position bp1)))) (setf (aref carcass 0) ;First line. (string-subseq (line-chars (bp-line bp1)) (bp-position bp1) (-& (line-char-count (bp-line bp1)) (bp-position bp1))) (aref carcass (1-& lines)) ;last line. (string-subseq (line-chars (bp-line bp2)) 0 (bp-position bp2)))) ;Now get complete lines between. (loop for line first (line-next (bp-line bp1)) then (line-next line) for i from 1 below (1-& lines) until (null line) do (setf (aref carcass i) (string-subseq (line-chars line) 0 (line-char-count line)))) (if (eq *editor-cursor* bp1) (push-or-append-kill-after carcass) (push-or-append-kill-before carcass)))) (defun delete-between-marks (bp1 bp2) (multiple-value (bp1 bp2) (order-bps bp1 bp2)) (if (eq (bp-line bp1) (bp-line bp2)) (send bp1 :delete-characters (-& (bp-position bp2) (bp-position bp1))) (progn (send bp1 :delete-chars-from-position) (send bp2 :delete-characters (-& (bp-position bp2))) ;;Clobber link from deleted lines so it can't get us. (setf (line-next (line-previous (bp-line bp2))) nil) (send (bp-line bp1) :set-next (bp-line bp2)) (send (bp-line bp2) :delete-line-separator)))) (defun kill-lines-from-bp (bp1 lines) ;Forward only. (let ((carcass (make-vector (1+& lines) :initial-element nil))) (setf (aref carcass 0) (string-subseq (line-chars (bp-line bp1)) (bp-position bp1) (line-char-count (bp-line bp1))) (aref carcass lines) "") ;Now get complete lines. (loop for i from 1 below lines for line first (line-next (bp-line bp1)) then (line-next line) until (null line) do (setf (aref carcass i) (string-copy (line-chars line) (line-char-count line)))) (push-or-append-kill-after carcass))) (defun backward-kill-lines-from-bp (bp1 lines) ;Backward only. (setq lines (abs& lines)) (let ((carcass (make-vector (1+& lines) :initial-element nil))) (setf (aref carcass lines) (string-subseq (line-chars (bp-line bp1)) 0 (bp-position bp1)) (aref carcass 0) "") ;Now get complete lines. (loop for i from (1-& lines) above 0 for line first (line-previous (bp-line bp1)) then (line-previous line) until (null line) do (setf (aref carcass i) (string-copy (line-chars line) (line-char-count line)))) (push-or-append-kill-before carcass))) (defun kill-one-line-from-bp (bp1) ;Forward only. (let ((carcass (make-vector 1))) (setf (aref carcass 0) (string-subseq (line-chars (bp-line bp1)) (bp-position bp1) (-& (line-char-count (bp-line bp1)) (bp-position bp1)))) (push-or-append-kill-after carcass))) (defun backward-kill-one-line-from-bp (bp1) ;backward only. (let ((carcass (make-vector 1))) (setf (aref carcass 0) (string-subseq (line-chars (bp-line bp1)) 0 (bp-position bp1))) (push-or-append-kill-before carcass))) (defun kill-characters-from-bp (bp chars) (let ((bp2 (copy-bp bp))) (send bp2 :advance-pos chars) ;works for negative chars. (kill-between-marks bp bp2))) (defun kill-and-delete-from-bp (bp chars) (let ((bp2 (copy-bp bp))) (send bp2 :advance-pos chars) (kill-between-marks bp bp2) (delete-between-marks bp bp2) (send (bp-line bp2) :remove-bp bp2))) (defun append-carcasses (c1 c2) (let* ((len1 (vector-length c1)) (len2 (vector-length c2)) (result (make-vector (+& len1 len2 -1)))) (loop for i from 0 below (1-& len1) do (setf (aref result i) (aref c1 i))) (setf (aref result (1-& len1)) (string-append (aref c1 (1-& len1)) (aref c2 0))) (loop for i from 1 below len2 for j upfrom len1 do (setf (aref result j) (aref c2 i))) result)) ; ;Editor Kill commands. ; (editor-bind-key #\meta-w copy-region) (defun copy-region () (kill-between-marks (get-mark) *editor-cursor*) nil) (editor-bind-key #\control-w kill-region) (defun kill-region () (kill-between-marks (get-mark) *editor-cursor*) (delete-between-marks (pop-mark) *editor-cursor*) nil) (editor-bind-key '(#\control-x #\X) put-q-reg) (defun put-q-reg (&optional (var nil)) (setq var (if (null var) (mx-prompter #'read "Put to Variable (Q reg): ") (intern (string-upcase var)))) (kill-between-marks (pop-mark) *editor-cursor*) (set var (pop-kill)) nil) (editor-bind-key '(#\control-x #\G) get-q-reg) (defun get-q-reg (&optional (var nil)) (setq var (if (null var) (mx-prompter #'read "Get from Variable (Q reg): ") (intern (string-upcase var)))) (cond ((stringp (symeval var)) (send *editor-cursor* :insert-string (symeval var))) ((vectorp (symeval var)) (un-kill (symeval var))) (t (ed-lose "Variable contents illegal as Q register"))) nil) (editor-bind-key #\control-k kill-lines) ;;; This should go in EM, but... (defun rest-of-line-empty? (point) (with-bp (bp point) (send bp :forward-over-horizontal-white-space) (send bp :end-of-line?))) (defun kill-lines () (cond ((null (argument?)) (not-buffer-end) (cond ((send *editor-cursor* :end-of-line?) (let ((carcass (make-vector 2))) (setf (aref carcass 0) "") (setf (aref carcass 1) "") (push-or-append-kill-after carcass) (send *editor-cursor* :delete-characters 1))) ((rest-of-line-empty? *editor-cursor*) (let ((argument-supplied? t) (*argument* 1)) (kill-lines))) (:else (kill-one-line-from-bp *editor-cursor*) (send *editor-cursor* :delete-chars-from-position)))) ((+p *argument*) (not-buffer-end) (kill-lines-from-bp *editor-cursor* *argument*) (loop for i from 1 to *argument* ;Now delete the lines. do (send *editor-cursor* :delete-chars-from-position) until (buffer-end?) do (send *editor-cursor* :delete-char) until (buffer-end?))) ((0p *argument*) (not-buffer-begin) (backward-kill-one-line-from-bp *editor-cursor*) (send *editor-cursor* :delete-characters (-& (bp-position *editor-cursor*)))) (t (not-buffer-begin) (backward-kill-lines-from-bp *editor-cursor* *argument*) (loop for i from 1 to (-& *argument*) do (send *editor-cursor* :delete-characters (-& (bp-position *editor-cursor*))) until (buffer-begin?) do (send (bp-line *editor-cursor*) :delete-line-separator) until (buffer-begin?)))) nil) (editor-bind-key #\control-y un-kill) (defun un-kill (&optional (carcass nil arg?)) (when (null arg?) (setq carcass (if (real-arg-sup?) (get-kill (1-& *argument*)) ;1-origined (get-kill)))) (unless (null carcass) (let ((mark (send *editor-cursor* :get-mark))) (setq *last-un-kill-command-count* *editor-command-count*) (loop with max-index = (1-& (vector-length carcass)) for i from 0 to max-index do (unless (null (aref carcass i)) (send (edit-cursor-line *editor-cursor*) :insert-string (edit-cursor-position *editor-cursor*) (aref carcass i)) (when (<& i max-index) (send (edit-cursor-line *editor-cursor*) :break (edit-cursor-position *editor-cursor*))))) (push-mark *editor-cursor*) (send *editor-cursor* :goto-mark mark) (unless (and (null arg?) (C-U-only?)) (exchange-point-and-mark)))) nil) (editor-bind-key #\meta-y un-kill-pop) (defun un-kill-pop () (when (=& (1+& *last-un-kill-command-count*) *editor-command-count*) (delete-between-marks (pop-mark) *editor-cursor*)) (pop-kill) (un-kill)) (editor-bind-key #\control-meta-w append-next-kill) (defun append-next-kill () (setq *last-kill-command-count* *editor-command-count*) nil) (defun do-not-append-next-kill () (setq *last-kill-command-count* -100) nil) (editor-bind-key #\meta-d kill-word) (defun kill-word (&optional (how-many *argument*)) (if (-p how-many) (backward-kill-word (-& how-many)) (let ((bp2 (copy-bp *editor-cursor*))) (do ((i 1 (1+& i))) ((>& i how-many)) (send bp2 :forward-over-not-word) (send bp2 :forward-over-word)) (kill-between-marks *editor-cursor* bp2) (delete-between-marks *editor-cursor* bp2) nil))) (editor-bind-key #\meta-rubout backward-kill-word) (defun backward-kill-word (&optional (how-many *argument*)) (if (-p how-many) (kill-word (-& how-many)) (let ((bp2 (copy-bp *editor-cursor*))) (do ((i 1 (1+& i))) ((>& i how-many)) (send bp2 :backward-over-not-word) (send bp2 :backward-over-word)) (kill-between-marks *editor-cursor* bp2) (delete-between-marks *editor-cursor* bp2) nil))) (editor-bind-key #\control-meta-k kill-sexp) (defun kill-sexp (&optional (how-many *argument*)) (cond ((-p how-many) (backward-kill-sexp (-& how-many))) (t (let ((bp2 (copy-bp *editor-cursor*))) (loop for i from 1 to how-many do (bp-forward-over-sexp bp2)) (kill-between-marks *editor-cursor* bp2) (delete-between-marks *editor-cursor* bp2)))) nil) (editor-bind-key #\control-meta-rubout backward-kill-sexp) (defun backward-kill-sexp (&optional (how-many *argument*)) (cond ((-p how-many) (kill-sexp (-& how-many))) (t (let ((bp2 (copy-bp *editor-cursor*))) (loop for i from 1 to how-many do (bp-backward-over-sexp bp2)) (kill-between-marks *editor-cursor* bp2) (Delete-between-marks *editor-cursor* bp2)))) nil) ;;; The following functions shouldn't be in this file but will ;;; ramain here until a better home can be found (defun process-words-as-strings (function) (setq *last-kill-command-count* -1000) ;ickypoo hack. (cond ((-p *argument*) (setq *argument* (-& *argument*)) (preserving-point (loop repeat *argument* do (send *editor-cursor* :backward-over-not-word) (send *editor-cursor* :backward-over-word)) (process-words-as-strings-1 function))) (t (process-words-as-strings-1 function)))) (defun process-words-as-strings-1 (function) (kill-word *argument*) (loop with carcass = (get-kill) for i from 0 below (vector-length carcass) do (setf (aref carcass i) (funcall function (aref carcass i))) finally (un-kill carcass) finally (pop-kill)) (pop-mark) nil) (defun process-region-as-chars (function) (multiple-value-bind (bp1 bp2) (order-bps *editor-cursor* (get-mark)) (cond ((eq (bp-line bp1) (bp-line bp2)) (send (bp-line bp1) :translate function (bp-position bp1) (bp-position bp2))) (t (let ((line)) (setq line (bp-line bp1)) (send line :translate function (bp-position bp1) (line-char-count line)) (setq line (bp-line bp2)) (send line :translate function 0 (bp-position bp2)) (loop for nline first (line-next (bp-line bp1)) then (line-next nline) until (eq nline line) do (send nline :translate function 0 (line-char-count nline))))))) nil) ;;;This is not quite the same as the (unimplemented) CommonLisp ;;;function STRING-CAPITALIZE. (defun string-capcase (string &optional (skip 0)) (loop for i from skip below (string-length string) for char = (char string i) if (word-char? char) return (progn (setf (char string i) (char-upcase char)) (loop for j from (1+& i) below (string-length string) if (not (word-char? (char string j))) return (string-capcase string j) do (setf (char string j) (char-downcase (Char String j)))))) string) (editor-bind-key #\meta-c uppercase-initial) (defun uppercase-initial () (process-words-as-strings #'string-capcase) nil) (editor-bind-key #\meta-u uppercase-word) (defun uppercase-word () (process-words-as-strings #'string-upcase) nil) (editor-bind-key #\meta-l lowercase-word) (defun lowercase-word () (process-words-as-strings #'string-downcase) nil) (editor-bind-key '(#\control-x #\control-u) uppercase-region) (defun uppercase-region () (process-region-as-chars #'char-upcase) nil) (editor-bind-key '(#\control-x #\page) lowercase-region) (defun lowercase-region () (process-region-as-chars #'char-downcase) nil)