(in-package :buffer-util) (defparameter newline-string (string #\Newline)) (defun concat (&rest rest) (apply #'concatenate 'string rest)) (defmethod fresh-line ((s string)) (if (eql (char s (1- (length s))) #\Newline) s (concat s newline-string))) (defmethod fresh-line (s) (fresh-line (princ-to-string s))) (defun maybe-append (prefix string) (if (string= string "") "" (concat prefix (fresh-line string)))) (defun string-to-lines (text) (let (lines) (vim:multi-line-map (lambda (start end line) (push line lines)) text) (nreverse lines))) (defgeneric append-lines (text buffer &optional newline) (:documentation "Given some text (single line string, multi-line string, or list of single-line strings), and a buffer (number, name, buffer class), append the text to the buffer.")) (defmethod append-lines ((text list) (buf-name string) &optional newline) (let* ((buffer (vim:find-buffer buf-name)) (line-num (vim:buffer-line-count buffer))) (if newline (vim:replace-lines text :start line-num :end line-num :buffer buffer) (progn (vim:append-to-buffer (car text) :buffer buffer) (vim:replace-lines (cdr text) :start line-num :end line-num :buffer buffer))))) (defmethod append-lines ((text string) (buffer string) &optional newline) (if (position #\Newline text) (append-lines (string-to-lines text) buffer newline) (append-lines (list text) buffer newline))) (defmethod append-lines (text (buffer number) &optional newline) (append-lines text (vim:bufname buffer) newline)) (defclass vim-buffer-output-stream (si::fundamental-character-output-stream) ((buffer-of :initarg :buffer :initform nil :accessor buffer-of) (internal-buffer-of :initform nil :accessor internal-buffer-of))) (defmethod initialize-instance :after ((stream vim-buffer-output-stream) &key) (setf (internal-buffer-of stream) (vim:find-buffer (buffer-of stream)))) #| (defmethod si::stream-write-char ((stream vim-buffer-output-stream) character) (vim::append-char-int (internal-buffer-of stream) character)) ; This is a bit broken - it appears that vim:append-lines-int interferes with vim:append-char-int (defmethod si::stream-write-string ((stream vim-buffer-output-stream) string &optional start end) (vim::append-lines-int (internal-buffer-of stream) (string-to-lines (subseq string start end)))) |# (defmethod si::stream-write-char ((stream vim-buffer-output-stream) character) (append-lines (princ-to-string character) (buffer-of stream))) (defmethod si::stream-write-string ((stream vim-buffer-output-stream) string &optional (start 0) end) (append-lines (subseq string start end) (buffer-of stream))) #| |# (defun scroll-window (winnr &optional upwards) (vim:with-window winnr (vim:normal (if upwards "gg" "G")))) (defgeneric scroll-window-of (buffer &optional upwards)) (defmethod scroll-window-of ((buffer string) &optional upwards) (scroll-window (vim:bufwinnr buffer) upwards)) (defmethod scroll-window-of ((buffer vim-buffer-output-stream) &optional upwards) #+nil (scroll-window (buffer-of buffer) upwards)) (defun find-paren (&optional repeat backwards) (vim:funcall "searchpair" "(" "" ")" (concat "W" (if backwards "b" "") (if repeat "r" "")) "synIDattr ( synID (line (\".\"), col(\".\"), 0), \"name\") == \"lispString\" || synIDattr ( synID (line (\".\"), col(\".\"), 0), \"name\") == \"lispComment\"")) (defun ltrim (s) (subseq s (position-if (lambda (x) (not (member x '(#\Space #\Tab)))) s))) (defun delete-leading-whitespace (lines) (with-output-to-string (s) (vim:multi-line-map (lambda (start end line) (if end (write-line (ltrim line) s) (write-string (ltrim line) s))) lines))) (defun buffer-subseq-lines (p1 p2) "Returns a subsequence of the buffer (as a list of lines), from p1 to p2." (assert (eql (vim:bufnr-of p1) (vim:bufnr-of p2)) (p1 p2) "Must use buffer-subseq-lines on positions in the same buffer: ~S ~S" p1 p2) (vim:with-buffer (vim:bufnr-of p1) (let* ((start (vim:line-of p1)) (end (vim:line-of p2)) (start-col (vim:col-of p1)) (end-col (vim:col-of p2))) ;(format t "start: ~D end: ~D start-col: ~D end-col: ~D~%" ; start end start-col end-col) (if (= start end) (list (subseq (vim:get-line start) start-col (1+ end-col))) (let ((start-line (subseq (vim:get-line start) start-col)) (middle-lines (vim:buffer-lines :buffer (vim:find-buffer (vim:bufname "%")) :start (1+ start) :end end)) (end-line (subseq (vim:get-line end) 0 (1+ end-col)))) (nconc (list start-line) middle-lines (list end-line))))))) (defun join-lines (lines) (with-output-to-string (s) (loop for (line . next) on lines if next do (write-line line s) else do (write-string line s)))) (defun buffer-subseq (p1 p2) "Returns a subsequence of the buffer (as a single string), from p1 to p2. p1 & 2 are instances of vim:point." (assert (eql (vim:bufnr-of p1) (vim:bufnr-of p2)) (p1 p2) "Must use buffer-subseq on positions in the same buffer: ~S ~S" p1 p2) (join-lines (buffer-subseq-lines p1 p2))) (defun get-cur-expr (&optional repeat (pos (vim:get-pos) pos-p)) (when pos-p (vim:goto-pos pos)) (let* ((lparen-pos (vim:get-pos-after (find-paren repeat t))) (rparen-pos (vim:get-pos-after (find-paren repeat))) (expr (buffer-subseq lparen-pos rparen-pos))) (when (string/= expr "") (setf expr (delete-leading-whitespace expr))) (vim:goto-pos pos) expr)) (defun get-word-under-cursor () (let ((line (vim:get-line))) (when line (let* ((cursor (cdr (vim:window-cursor))) (left (or (position #\Space line :end cursor :from-end t) 0)) (right (or (position #\Space line :start cursor) (length line))) (word (string-trim " ()" (subseq line left right))) ) word)))) (defun buffer-is-showing (buffer-name) (let ((buf-number (vim:bufnr buffer-name)) (open-buffers (vim:tabpagebuflist))) (member buf-number open-buffers))) (defun move-cursor-to-window (buffer-name) (when (buffer-is-showing buffer-name) (vim:wincmd "w" (vim:bufwinnr buffer-name)))) (defun split-open-buffer (buffer-name) (if (buffer-is-showing buffer-name) (move-cursor-to-window buffer-name) (vim:cmd "split " buffer-name))) (defun hide-buffer (buffer-name) (when (buffer-is-showing buffer-name) (move-cursor-to-window buffer-name) (vim:close))) (defun full-pathname (buffer-name) (vim:expr (format nil "fnamemodify(\"~A\", \":p\")" buffer-name)))