creeper-adventure/.venv/share/emacs/site-lisp/pyflyby.el
2022-03-31 20:20:07 -05:00

108 lines
4 KiB
EmacsLisp

(require 'find-func) ;; for find-library-name
(defvar pyflyby-bin-path
;; Try to find the bin path based on where pyflyby.el lives.
(let* ((pyflyby-el-name
(or load-file-name
(ignore-errors (find-library-name "pyflyby"))
"pyflyby.el"))
(module-path (file-truename pyflyby-el-name))
(binpath (file-truename
(concat (file-name-directory module-path) "../../bin"))))
(and (file-directory-p binpath) binpath))
"Path containing pyflyby executables.")
(defun pyflyby--pipe-to-command (start end program &rest args)
"Send text from START to END to process running PROGRAM ARGS.
Returns (exit-value stdout stderr)."
;; Yuck: call-process-region can only redirect stderr to a file, not to a
;; buffer/string.
(let* ((stdout-buffer (generate-new-buffer " *pyflyby stdout*"))
(stderr-buffer (generate-new-buffer " *pyflyby stderr*"))
(stderr-file (make-temp-file
(expand-file-name
"pyflyby-log."
(or small-temporary-file-directory
temporary-file-directory))))
(exec-path (if pyflyby-bin-path
(cons pyflyby-bin-path exec-path) exec-path))
(exit-value (apply 'call-process-region
start end program nil
(list stdout-buffer stderr-file)
nil args))
(stdout-text (with-current-buffer stdout-buffer (buffer-string)))
(stderr-text (with-current-buffer stderr-buffer
(format-insert-file stderr-file nil)
(buffer-string))))
(delete-file stderr-file)
(kill-buffer stdout-buffer)
(kill-buffer stderr-buffer)
(list exit-value stdout-text stderr-text)))
(defun pyflyby--replace-text (text)
"Replace current buffer with TEXT.
Tries to keep point in the same place."
(let* ((p (point))
(ptext (buffer-substring-no-properties
p (min (point-max) (+ p 1000))))
(size (buffer-size)))
;; Replace the buffer.
(erase-buffer)
(insert text)
;; Go to previously saved point. We don't use save-excursion
;; since that doesn't work when the whole buffer is replaced,
;; destroying markers. First see if the position matches when
;; counting from end of the buffer.
(setq p (max 0 (+ p (buffer-size) (- size))))
(goto-char p)
(unless (search-forward ptext (+ p 1) t)
;; Next, try searching for the location based on text near old
;; point.
(goto-char (- p 500))
(if (search-forward ptext (+ p 1000) t)
(goto-char (match-beginning 0))
(goto-char p)))
(recenter)))
(defun pyflyby-transform-region-with-command (command &rest args)
(unless (eq major-mode 'python-mode)
(error "Pyflyby should only be used on python buffers"))
(let* ((result
(apply 'pyflyby--pipe-to-command (point-min) (point-max) command args))
(exit-value (nth 0 result))
(newtext (nth 1 result))
(logtext (nth 2 result))
(logtext (replace-regexp-in-string "/dev/stdin: ?" "" logtext nil t))
(nlogtext (if (= 0 (length logtext)) "" (concat "\n" logtext))))
(if (= exit-value 0)
;; Process exited successfully.
(if (string= newtext
(buffer-substring-no-properties (point-min) (point-max)))
;; No change.
(message "No changes by `%s'.%s" command nlogtext)
;; There were changes.
(pyflyby--replace-text newtext)
(message logtext))
;; Process failed.
(error "%s failed with exit code %d.%s" command exit-value logtext))))
(defun pyflyby-tidy-imports ()
(interactive "*")
(pyflyby-transform-region-with-command "tidy-imports"))
(defun pyflyby-reformat-imports ()
(interactive "*")
(pyflyby-transform-region-with-command "reformat-imports"))
(defalias 'tidy-imports 'pyflyby-tidy-imports)
(defalias 'reformat-imports 'pyflyby-reformat-imports)
(provide 'pyflyby)