[lice @ add backtrace to debugger]
[lice.git] / simple.lisp
blob9e3f4671fd6ca2ddd921e667880580a02ae50dd0
1 (in-package :lice)
3 (defvar *kill-ring* nil
4 "The kill ring.")
6 (defvar *kill-ring-max* 60
7 "Maximum length of kill ring before oldest elements are thrown away.")
9 (defvar *kill-ring-yank-pointer* nil
10 "The tail of the kill ring whose car is the last thing yanked.")
12 (define-condition kill-ring-empty (lice-condition)
13 () (:documentation "Raised when a yank is attempted but the kill ring is empty"))
15 ;; (when (or (and (< n 0)
16 ;; (< (point (current-buffer)) 0))
17 ;; (> (point (current-buffer))
18 ;; (point-max)))
19 ;; (decf (marker-position (buffer-point (current-buffer))) n))
21 (defcommand forward-char ((&optional (n 1))
22 :prefix)
23 "Move the point forward N characters in the current buffer."
24 (incf (marker-position (buffer-point (current-buffer))) n)
25 (cond ((< (point) (begv))
26 (goto-char (begv))
27 (signal 'beginning-of-buffer))
28 ((> (point) (zv))
29 (goto-char (zv))
30 (signal 'end-of-buffer))))
32 (defcommand backward-char ((&optional (n 1))
33 :prefix)
34 (forward-char (- n)))
36 (defun buffer-beginning-of-line ()
37 "Return the point in the buffer that is the beginning of the line that P is on."
38 (if (or (not (char-before))
39 (char= (char-before) #\Newline))
40 (point)
41 (let ((bol (buffer-scan-newline (current-buffer) (point) 0 0)))
42 (if (and (char= (char-after bol) #\Newline)
43 (< bol (1- (buffer-size (current-buffer)))))
44 (1+ bol)
45 bol))))
47 (defun buffer-end-of-line ()
48 "Return the point in the buffer that is the end of the line that P is on."
49 (if (or (not (char-after))
50 (char= (char-after) #\Newline))
51 (point)
52 (let ((eol (buffer-scan-newline (current-buffer) (point) (1- (buffer-size (current-buffer))) 1)))
53 ;; XXX: a bit of a kludge. if the eol char isn't a newline then it
54 ;; has to be the end of the buffer, so advance the point by one,
55 ;; which is the actual end of the line.
56 (if (char= (char-after eol) #\Newline)
57 eol
58 (1+ eol)))))
60 (defun forward-line (n)
61 "Move n lines forward (backward if n is negative).
62 Precisely, if point is on line I, move to the start of line I + n.
63 If there isn't room, go as far as possible (no error).
64 Returns the count of lines left to move. If moving forward,
65 that is n - number of lines moved; if backward, n + number moved.
66 With positive n, a non-empty line at the end counts as one line
67 successfully moved (for the return value)."
68 (cond ((and (> n 0)
69 (= (point) (zv)))
70 (signal 'end-of-buffer))
71 ((and (< n 0)
72 (= (point) (begv)))
73 (signal 'beginning-of-buffer)))
74 (if (> n 0)
75 (multiple-value-bind (p lines) (buffer-scan-newline (current-buffer)
76 (point (current-buffer))
77 (1- (buffer-size (current-buffer)))
79 ;; Increment p by one so the point is at the beginning of the
80 ;; line.
81 (when (or (char= (char-after p) #\Newline)
82 (= p (1- (buffer-size (current-buffer)))))
83 (incf p))
84 (goto-char p)
85 (when (zerop lines)
86 (signal 'end-of-buffer))
87 (- n lines))
88 (if (and (= n 0)
89 (not (char-before)))
91 (multiple-value-bind (p lines)
92 (buffer-scan-newline (current-buffer)
93 (point) 0
94 ;; A little mess to figure out how
95 ;; many newlines to search for to
96 ;; give the proper output.
97 (if (zerop n)
99 (if (and (char-after (point))
100 (char= (char-after (point)) #\Newline))
101 (- n 2)
102 (1- n))))
103 (when (char= (char-after p) #\Newline)
104 (incf p))
105 (goto-char p)
106 (when (and (< n 0)
107 (zerop lines))
108 (signal 'beginning-of-buffer))
109 (+ n lines)))))
111 (defun current-column ()
112 "Return the current column that the current buffer's point is on."
113 (let ((bol (buffer-beginning-of-line)))
114 (- (point) bol)))
116 (defcommand self-insert-command ((arg)
117 :prefix)
118 "Insert the character you type.
119 Whichever character you type to run this command is inserted."
120 (dformat +debug-v+ "currentb: ~a ~a~%" (current-buffer) *current-buffer*)
121 (if (>= arg 2)
122 (insert-move-point (current-buffer) (make-string arg :initial-element (key-char *current-event*)))
123 (when (> arg 0)
124 (insert-move-point (current-buffer) (key-char *current-event*)))))
126 (defcommand newline ((&optional n)
127 :prefix)
128 "Insert N new lines."
129 (insert-move-point (current-buffer) (make-string (or n 1) :initial-element #\Newline)))
131 (defcommand open-line ((n) :prefix)
132 "Insert a newline and leave point before it.
133 **If there is a fill prefix and/or a left-margin, insert them on the new line
134 **if the line would have been blank.
135 With arg N, insert N newlines."
136 (let ((loc (point)))
137 (dotimes (i n) (newline 1))
138 (goto-char loc)))
140 (defcommand next-line ((&optional (arg 1))
141 :prefix)
142 "Move cursor vertically down N lines."
143 (let ((col (current-column)))
144 (forward-line arg)
145 (if (<= col (- (buffer-end-of-line) (point)))
146 (goto-char (+ (point) col))
147 (goto-char (buffer-end-of-line)))))
149 (defcommand previous-line ((&optional (arg 1))
150 :prefix)
151 "Move cursor vertically up N lines."
152 (let ((col (current-column)))
153 ;; FIXME: this is all fucked
154 (forward-line (- arg))
155 ;;(forward-line 0)
156 ;;(backward-char 1)
157 ;;(forward-line 0)
158 (if (<= col (- (buffer-end-of-line) (point)))
159 (goto-char (+ (point) col))
160 (goto-char (buffer-end-of-line)))))
162 (defcommand delete-backward-char ()
163 "Delete the previous N characters."
164 (buffer-delete (current-buffer) (point (current-buffer)) -1))
166 (defcommand delete-char ()
167 "Delete the following N characters."
168 (buffer-delete (current-buffer) (point (current-buffer)) 1))
170 (defcommand beginning-of-line ()
171 "Move the point to the beginning of the line in the current buffer."
172 (setf (marker-position (buffer-point (current-buffer))) (buffer-beginning-of-line)))
174 (defcommand end-of-line ()
175 "Move the point to the end of the line in the current buffer."
176 (setf (marker-position (buffer-point (current-buffer))) (buffer-end-of-line)))
178 (defcommand erase-buffer ((&optional buffer))
179 "Erase the contents of the current buffer."
180 (buffer-erase (or buffer (current-buffer))))
182 (defcommand execute-extended-command ((prefix)
183 :prefix)
184 "Read a user command from the minibuffer."
185 (let ((cmd (read-command (case prefix
186 (1 "M-x ")
187 (4 "C-u M-x ")
188 (t (format nil "~a M-x " prefix))))))
189 (if (lookup-command cmd)
190 (progn
191 (setf *prefix-arg* prefix)
192 (dispatch-command cmd))
193 (message "No Match"))))
195 (defcommand switch-to-buffer ((buffer &optional norecord)
196 (:buffer "Switch To Buffer: " (buffer-name (other-buffer (current-buffer)))))
197 "Select buffer buffer in the current window.
198 If buffer does not identify an existing buffer,
199 then this function creates a buffer with that name.
201 When called from Lisp, buffer may be a buffer, a string (a buffer name),
202 or nil. If buffer is nil, then this function chooses a buffer
203 using `other-buffer'.
204 Optional second arg norecord non-nil means
205 do not put this buffer at the front of the list of recently selected ones.
206 This function returns the buffer it switched to.
208 WARNING: This is NOT the way to work on another buffer temporarily
209 within a Lisp program! Use `set-buffer' instead. That avoids messing with
210 the window-buffer correspondences."
211 (unless buffer
212 (setf buffer (other-buffer (current-buffer))))
213 (let ((w (frame-current-window (selected-frame))))
214 (when (typep w 'minibuffer-window)
215 (error "its a minibuffer"))
216 (setf buffer (get-buffer-create buffer))
217 (set-buffer buffer)
218 (unless norecord
219 (record-buffer buffer))
220 (set-window-buffer w buffer)))
222 (defcommand save-buffers-kill-emacs ()
223 ;; TODO: save-some-buffers
224 (throw 'lice-quit t))
226 (defcommand kill-buffer ((buffer)
227 (:buffer "Kill buffer: " (buffer-name (current-buffer)) t))
228 "Kill the buffer BUFFER.
229 The argument may be a buffer or may be the name of a buffer.
230 defaults to the current buffer.
232 Value is t if the buffer is actually killed, nil if user says no.
234 The value of `kill-buffer-hook' (which may be local to that buffer),
235 if not void, is a list of functions to be called, with no arguments,
236 before the buffer is actually killed. The buffer to be killed is current
237 when the hook functions are called.
239 Any processes that have this buffer as the `process-buffer' are killed
240 with SIGHUP."
241 (let* ((target (get-buffer buffer))
242 (other (other-buffer target)))
243 (if target
244 (progn
245 ;; all windows carrying the buffer need a new buffer
246 (loop for w in (frame-window-list (selected-frame))
247 do (when (eq (window-buffer w) target)
248 (set-window-buffer w other)))
249 (setf *buffer-list* (delete target *buffer-list*)))
250 (error "No such buffer ~a" buffer))))
252 (defun eval-echo (string)
253 (multiple-value-bind (sexpr pos) (read-from-string string)
254 (if (= pos (length string))
255 (message "~s" (eval sexpr))
256 (error "Trailing garbage is ~a" string))))
258 (defun eval-print (string)
259 (multiple-value-bind (sexpr pos) (read-from-string string)
260 (if (= pos (length string))
261 (insert (format nil "~%~s~%" (eval sexpr)))
262 (error "Trailing garbage is ~a" string))))
264 (defcommand eval-expression ((s)
265 (:string "Eval: "))
266 ;;(handler-case
267 (eval-echo s))
268 ;;(error (c) (message "Eval error: ~s" c))))
270 (defcommand exchange-point-and-mark ()
271 (let ((p (point)))
272 (goto-char (marker-position (mark-marker)))
273 (set-marker (mark-marker) p)))
275 (defcommand set-mark-command ()
276 (set-marker (mark-marker) (point))
277 (message "Mark set"))
279 ;; (defun kill-ring-save (beg end)
280 ;; "Save the region to the kill ring."
282 (defcommand scroll-up ()
283 (let ((win (get-current-window)))
284 (window-scroll-up win (max 1 (- (window-height win)
285 *next-screen-context-lines*)))))
287 (defcommand scroll-down ()
288 (let ((win (get-current-window)))
289 (window-scroll-down win (max 1 (- (window-height win)
290 *next-screen-context-lines*)))))
292 (defcommand end-of-buffer ()
293 "Move point to the end of the buffer; leave mark at previous position.
294 With arg N, put point N/10 of the way from the end.
296 If the buffer is narrowed, this command uses the beginning and size
297 of the accessible part of the buffer."
298 (set-mark-command)
299 (goto-char (point-max)))
301 (defcommand beginning-of-buffer ()
302 "Move point to the beginning of the buffer; leave mark at previous position.
303 With arg N, put point N/10 of the way from the beginning.
305 If the buffer is narrowed, this command uses the beginning and size
306 of the accessible part of the buffer."
307 (set-mark-command)
308 (goto-char (point-min)))
310 (defcommand split-window-vertically ()
311 (split-window (get-current-window)))
313 (defcommand split-window-horizontally ()
314 (split-window (get-current-window) nil t))
316 (defcommand other-window ()
317 (let ((w (next-window (get-current-window) t)))
318 (if w
319 (select-window w)
320 (message "No other window."))))
322 (defcommand switch-to-buffer-other-window ((buffer)
323 (:buffer "Switch to buffer in other window: " (buffer-name (other-buffer (current-buffer)))))
324 (let* ((cw (get-current-window))
325 (w (or (next-window cw)
326 (split-window cw))))
327 (select-window w)
328 (switch-to-buffer buffer)))
330 (defcommand keyboard-quit ()
331 (signal 'quit))
333 ;;; kill ring
335 (defun kill-new (string &optional replace)
336 "Make STRING the latest kill in the kill ring.
337 Set the kill-ring-yank pointer to point to it.
338 Optional second argument REPLACE non-nil means that STRING will replace
339 the front of the kill ring, rather than being added to the list."
340 (if (and replace
341 *kill-ring*)
342 (setf (car *kill-ring*) string)
343 (push string *kill-ring*))
344 (when (> (length *kill-ring*) *kill-ring-max*)
345 (setf (cdr (nthcdr (1- *kill-ring-max*) *kill-ring*)) nil))
346 (setf *kill-ring-yank-pointer* *kill-ring*))
348 (defun copy-region-as-kill (start end &optional (buffer (current-buffer)))
349 (multiple-value-setq (start end) (validate-region start end buffer))
350 (kill-new (buffer-substring start end buffer)))
352 (defcommand kill-ring-save ()
353 (copy-region-as-kill (mark) (point)))
355 (defcommand kill-region ((beg end)
356 :region-beginning
357 :region-end)
358 "Kill between point and mark.
359 The text is deleted but saved in the kill ring.
360 The command C-y can retrieve it from there.
361 (If you want to kill and then yank immediately, use M-w.)"
362 (copy-region-as-kill beg end)
363 (delete-region beg end))
366 (defcommand kill-line ()
367 (kill-region (point)
368 (progn
369 (when (eobp)
370 (signal 'end-of-buffer))
371 (if (char= (buffer-char-after (current-buffer) (point)) #\Newline)
372 (forward-line 1)
373 (goto-char (buffer-end-of-line)))
374 (point))))
376 (defun current-kill (n &optional do-not-move)
377 "Rotate the yanking point by N places, and then return that kill.
378 If optional arg DO-NOT-MOVE is non-nil, then don't actually move the
379 yanking point; just return the Nth kill forward."
380 (unless *kill-ring*
381 (signal 'kill-ring-empty))
382 (let ((argth-kill-element
383 (nthcdr (mod (- n (length *kill-ring-yank-pointer*))
384 (length *kill-ring*))
385 *kill-ring*)))
386 (unless do-not-move
387 (setf *kill-ring-yank-pointer* argth-kill-element))
388 (car argth-kill-element)))
390 (defcommand yank ()
391 (set-mark-command)
392 (insert (current-kill 0)))
394 (defcommand yank-pop ()
395 (unless (eq *last-command* 'yank)
396 (error "Previous command was not a yank: ~a" *last-command*))
397 (setf *this-command* 'yank)
398 (delete-region (mark) (point))
399 (insert (current-kill 1)))
401 ;;; universal argument
403 (defun prefix-numeric-value (prefix)
404 "Return numeric meaning of raw prefix argument RAW.
405 A raw prefix argument is what you get from :raw-prefix.
406 Its numeric meaning is what you would get from :prefix."
407 ;; TODO
408 (cond ((null prefix)
410 ((eq prefix '-)
412 ((and (consp prefix)
413 (integerp (car prefix)))
414 (car prefix))
415 ((integerp prefix)
416 prefix)
417 (t 1)))
419 (defun prefix-arg ()
420 "Return numeric meaning of *prefix-arg*"
421 (prefix-numeric-value *prefix-arg*))
423 (defun raw-prefix-arg ()
424 "Return the current prefix arg in raw form."
425 *prefix-arg*)
427 (defvar *overriding-map-is-bound* nil)
428 (defvar *saved-overriding-map* nil)
429 (defvar *universal-argument-num-events* nil)
431 (defvar *universal-argument-map*
432 (let ((map (make-sparse-keymap)))
433 ;;(define-key map (kbd "t") 'universal-argument-other-key)
434 (define-key map t 'universal-argument-other-key)
435 ;;(define-key map [switch-frame] nil)
436 (define-key map (kbd "C-u") 'universal-argument-more)
437 (define-key map (kbd "-") 'universal-argument-minus)
438 (define-key map (kbd "0") 'digit-argument)
439 (define-key map (kbd "1") 'digit-argument)
440 (define-key map (kbd "2") 'digit-argument)
441 (define-key map (kbd "3") 'digit-argument)
442 (define-key map (kbd "4") 'digit-argument)
443 (define-key map (kbd "5") 'digit-argument)
444 (define-key map (kbd "6") 'digit-argument)
445 (define-key map (kbd "7") 'digit-argument)
446 (define-key map (kbd "8") 'digit-argument)
447 (define-key map (kbd "9") 'digit-argument)
448 ;; (define-key map [kp-0] 'digit-argument)
449 ;; (define-key map [kp-1] 'digit-argument)
450 ;; (define-key map [kp-2] 'digit-argument)
451 ;; (define-key map [kp-3] 'digit-argument)
452 ;; (define-key map [kp-4] 'digit-argument)
453 ;; (define-key map [kp-5] 'digit-argument)
454 ;; (define-key map [kp-6] 'digit-argument)
455 ;; (define-key map [kp-7] 'digit-argument)
456 ;; (define-key map [kp-8] 'digit-argument)
457 ;; (define-key map [kp-9] 'digit-argument)
458 ;; (define-key map [kp-subtract] 'universal-argument-minus)
459 map)
460 "Keymap used while processing \\[universal-argument].")
462 (defun ensure-overriding-map-is-bound ()
463 "Check `*overriding-terminal-local-map*' is `*universal-argument-map*'."
464 (unless *overriding-map-is-bound*
465 (setf *saved-overriding-map* *overriding-terminal-local-map*
466 *overriding-terminal-local-map* *universal-argument-map*
467 *overriding-map-is-bound* t)))
469 (defun restore-overriding-map ()
470 "Restore `*overriding-terminal-local-map*' to its saved value."
471 (setf *overriding-terminal-local-map* *saved-overriding-map*
472 *overriding-map-is-bound* nil))
474 (defcommand universal-argument ()
475 (setf *prefix-arg* (list 4)
476 *universal-argument-num-events* (length (this-command-keys)))
477 (ensure-overriding-map-is-bound))
479 (defcommand universal-argument-more ((arg)
480 :raw-prefix)
481 (if (consp arg)
482 (setf *prefix-arg* (list (* 4 (car arg))))
483 (if (eq arg '-)
484 (setf *prefix-arg* (list -4))
485 (progn
486 (setf *prefix-arg* arg)
487 (restore-overriding-map))))
488 (setf *universal-argument-num-events* (length (this-command-keys))))
490 (defcommand negative-argument ((arg)
491 :raw-prefix)
492 "Begin a negative numeric argument for the next command.
493 \\[universal-argument] following digits or minus sign ends the argument."
494 (cond ((integerp arg)
495 (setf *prefix-arg* (- arg)))
496 ((eq arg '-)
497 (setf *prefix-arg* nil))
499 (setf *prefix-arg* '-)))
500 (setf *universal-argument-num-events* (length (this-command-keys)))
501 (ensure-overriding-map-is-bound))
503 (defcommand digit-argument ((arg)
504 :raw-prefix)
505 "Part of the numeric argument for the next command.
506 \\[universal-argument] following digits or minus sign ends the argument."
507 (let* ((char (last-command-char))
508 (digit (- (logand (char-code char) #o177) (char-code #\0))))
509 (cond ((integerp arg)
510 (setf *prefix-arg* (+ (* arg 10)
511 (if (< arg 0) (- digit) digit))))
512 ((eq arg '-)
513 ;; Treat -0 as just -, so that -01 will work.
514 (setf *prefix-arg* (if (zerop digit) '- (- digit))))
516 (setf *prefix-arg* digit))))
517 (setf *universal-argument-num-events* (length (this-command-keys)))
518 (ensure-overriding-map-is-bound))
520 ;; For backward compatibility, minus with no modifiers is an ordinary
521 ;; command if digits have already been entered.
522 (defcommand universal-argument-minus ((arg)
523 :raw-prefix)
524 (if (integerp arg)
525 (universal-argument-other-key arg)
526 (negative-argument arg)))
528 ;; Anything else terminates the argument and is left in the queue to be
529 ;; executed as a command.
530 (defcommand universal-argument-other-key ((arg)
531 :raw-prefix)
532 (setf *prefix-arg* arg)
533 (let* ((keylist (this-command-keys)))
534 (setf *unread-command-events* keylist))
535 ;; (append (nthcdr *universal-argument-num-events* keylist)
536 ;; *unread-command-events*)))
537 ;;FIXME: (reset-this-command-lengths)
538 (restore-overriding-map))
541 ;; (defcommand append-to-buffer ((buffer :buffer "Append to buffer: " (buffer-name (other-buffer (current-buffer))))
542 ;; (start :region-beginning)
543 ;; (end :region-end))
544 ;; "Append to specified buffer the text of the region.
545 ;; It is inserted into that buffer before its point.
547 ;; When calling from a program, give three arguments:
548 ;; buffer (or buffer name), start and end.
549 ;; start and end specify the portion of the current buffer to be copied."
550 ;; (let ((oldbuf (current-buffer)))
551 ;; (save-excursion
552 ;; (let* ((append-to (get-buffer-create buffer))
553 ;; (windows (get-buffer-window-list append-to t t))
554 ;; point)
555 ;; (set-buffer append-to)
556 ;; (setf point (point))
557 ;; (barf-if-buffer-read-only)
558 ;; (insert-buffer-substring oldbuf start end)
559 ;; (dolist (window windows)
560 ;; (when (= (window-point window) point)
561 ;; (set-window-point window (point))))))))
563 (defcommand transpose-chars ((arg)
564 :prefix)
565 "Interchange characters around point, moving forward one character.
566 With prefix arg ARG, effect is to take character before point
567 and drag it forward past ARG other characters (backward if ARG negative).
568 If no argument and at end of line, the previous two chars are exchanged."
569 (and (null arg) (eolp) (forward-char -1))
570 (transpose-subr 'forward-char (prefix-numeric-value arg)))
572 (defcommand transpose-words ((arg)
573 :prefix)
574 "Interchange words around point, leaving point at end of them.
575 With prefix arg ARG, effect is to take word before or around point
576 and drag it forward past ARG other words (backward if ARG negative).
577 If ARG is zero, the words around or after point and around or after mark
578 are interchanged."
579 ;; FIXME: `foo a!nd bar' should transpose into `bar and foo'.
580 (transpose-subr 'forward-word arg))
582 ;; (defun transpose-sexps ((arg)
583 ;; :prefix)
584 ;; "Like \\[transpose-words] but applies to sexps.
585 ;; Does not work on a sexp that point is in the middle of
586 ;; if it is a list or string."
587 ;; (transpose-subr
588 ;; (lambda (arg)
589 ;; ;; Here we should try to simulate the behavior of
590 ;; ;; (cons (progn (forward-sexp x) (point))
591 ;; ;; (progn (forward-sexp (- x)) (point)))
592 ;; ;; Except that we don't want to rely on the second forward-sexp
593 ;; ;; putting us back to where we want to be, since forward-sexp-function
594 ;; ;; might do funny things like infix-precedence.
595 ;; (if (if (> arg 0)
596 ;; ;;(looking-at "\\sw\\|\\s_")
597 ;; ;; FIXME: we don't have looking-at or syntax classes. Fake it for now
598 ;; (or (alpha-char-p (char-after (point)))
599 ;; (find (char-after (point)) "*/+-%$!@&"))
600 ;; (and (not (bobp))
601 ;; (save-excursion (forward-char -1)
602 ;; ;; (looking-at "\\sw\\|\\s_")
603 ;; ;; FIXME: we don't have looking-at or syntax classes. Fake it for now
604 ;; (or (alpha-char-p (char-after (point)))
605 ;; (find (char-after (point)) "*/+-%$!@&"))
606 ;; )))
607 ;; ;; Jumping over a symbol. We might be inside it, mind you.
608 ;; (progn (funcall (if (> arg 0)
609 ;; 'skip-syntax-backward 'skip-syntax-forward)
610 ;; "w_")
611 ;; (cons (save-excursion (forward-sexp arg) (point)) (point)))
612 ;; ;; Otherwise, we're between sexps. Take a step back before jumping
613 ;; ;; to make sure we'll obey the same precedence no matter which direction
614 ;; ;; we're going.
615 ;; (funcall (if (> arg 0) 'skip-syntax-backward 'skip-syntax-forward) " .")
616 ;; (cons (save-excursion (forward-sexp arg) (point))
617 ;; (progn (while (or (forward-comment (if (> arg 0) 1 -1))
618 ;; (not (zerop (funcall (if (> arg 0)
619 ;; 'skip-syntax-forward
620 ;; 'skip-syntax-backward)
621 ;; ".")))))
622 ;; (point)))))
623 ;; arg 'special))
625 (defcommand transpose-lines ((arg)
626 :prefix)
627 "Exchange current line and previous line, leaving point after both.
628 With argument ARG, takes previous line and moves it past ARG lines.
629 With argument 0, interchanges line point is in with line mark is in."
630 (transpose-subr (function
631 (lambda (arg)
632 (if (> arg 0)
633 (progn
634 ;; Move forward over ARG lines,
635 ;; but create newlines if necessary.
636 (setq arg (forward-line arg))
637 (if (char/= (preceding-char) #\Newline)
638 (setq arg (1+ arg)))
639 (if (> arg 0)
640 (newline arg)))
641 (forward-line arg))))
642 arg))
644 (defun transpose-subr (mover arg &optional special)
645 (let ((aux (if special mover
646 (lambda (x)
647 (cons (progn (funcall mover x) (point))
648 (progn (funcall mover (- x)) (point))))))
649 pos1 pos2)
650 (cond
651 ((= arg 0)
652 (save-excursion
653 (setq pos1 (funcall aux 1))
654 (goto-char (mark))
655 (setq pos2 (funcall aux 1))
656 (transpose-subr-1 pos1 pos2))
657 (exchange-point-and-mark))
658 ((> arg 0)
659 (setq pos1 (funcall aux -1))
660 (setq pos2 (funcall aux arg))
661 (transpose-subr-1 pos1 pos2)
662 (goto-char (car pos2)))
664 (setq pos1 (funcall aux -1))
665 (goto-char (car pos1))
666 (setq pos2 (funcall aux arg))
667 (transpose-subr-1 pos1 pos2)))))
669 (defun transpose-subr-1 (pos1 pos2)
670 (when (> (car pos1) (cdr pos1)) (setq pos1 (cons (cdr pos1) (car pos1))))
671 (when (> (car pos2) (cdr pos2)) (setq pos2 (cons (cdr pos2) (car pos2))))
672 (when (> (car pos1) (car pos2))
673 (let ((swap pos1))
674 (setq pos1 pos2 pos2 swap)))
675 (if (> (cdr pos1) (car pos2)) (error "Don't have two things to transpose"))
676 ;; (atomic-change-group
677 (let (word2)
678 ;; FIXME: We first delete the two pieces of text, so markers that
679 ;; used to point to after the text end up pointing to before it :-(
680 (setq word2 (delete-and-extract-region (car pos2) (cdr pos2)))
681 (goto-char (car pos2))
682 (insert (delete-and-extract-region (car pos1) (cdr pos1)))
683 (goto-char (car pos1))
684 (insert word2)))
686 (provide :lice-0.1/simple)