diff --git a/rubocopfmt.el b/rubocopfmt.el index 33a4d9d..ba4c200 100644 --- a/rubocopfmt.el +++ b/rubocopfmt.el @@ -81,113 +81,6 @@ inside a `before-save-hook'." (const :tag "None" nil)) :group 'rubocopfmt) -;;;###autoload -(defun rubocopfmt () - "Format the current buffer with rubocop." - (interactive) - (let* ((coding-system-for-read 'utf-8) - (coding-system-for-write 'utf-8) - (tmpfile (make-temp-file "rubocopfmt" nil ".rb")) - (resultbuf (get-buffer-create "*Rubocopfmt result*")) - (patchbuf (get-buffer-create "*Rubocopfmt patch*")) - (buffer-file (file-truename buffer-file-name)) - (src-dir (file-name-directory buffer-file)) - (src-file (file-name-nondirectory buffer-file)) - (fmt-command rubocopfmt-rubocop-command) - (fmt-args (list "--stdin" src-file - "--auto-correct" - "--format" "emacs"))) - - (if (rubocopfmt--bundled-path-p src-dir) - (setq fmt-command "bundle" - fmt-args (append (list "exec" rubocopfmt-rubocop-command) - fmt-args))) - - (if rubocopfmt-disabled-cops - (setq fmt-args (append fmt-args (list "--except" - (combine-and-quote-strings - rubocopfmt-disabled-cops ","))))) - - (unwind-protect - (save-restriction - (widen) - (write-region nil nil tmpfile) - (with-current-buffer resultbuf (erase-buffer)) - (with-current-buffer patchbuf (erase-buffer)) - - (let ((current-directory src-dir)) - (message "Calling rubocop from directory \"%s\": %s %s" - src-dir fmt-command (mapconcat 'identity fmt-args " ")) - (apply #'call-process-region (point-min) (point-max) - fmt-command nil resultbuf nil fmt-args) - (if (rubocopfmt--parse-result resultbuf tmpfile) - (call-process-region (point-min) (point-max) "diff" - nil patchbuf nil "-n" "-" tmpfile))) - - (if (= (buffer-size patchbuf) 0) - (message "Buffer is already rubocopfmted") - (rubocopfmt--apply-rcs-patch patchbuf) - (message "Applied rubocopfmt"))) - - (delete-file tmpfile) - (kill-buffer resultbuf) - (kill-buffer patchbuf)))) - -(defun rubocopfmt--parse-result (resultbuf tmpfile) - "Parse Rubocop result in RESULTBUF and write corrections to TMPFILE." - (let ((split 0)) - (with-current-buffer resultbuf - (goto-char (point-min)) - ;; Only find the separator when RuboCop has printed complaints. - (setq split (search-forward "\n====================\n" nil t)) - - ;; If no RuboCop complaints were printed, we need to find the separator at - ;; the beginning of the buffer. This separation helps prevent false - ;; positive separator matches. - (unless split - (setq split (search-forward "====================\n" nil t))) - - (if split - (when (> split 22) - (goto-char (point-min)) - (when (search-forward "[Corrected]" split t) - (write-region split (point-max) tmpfile) - t)) - (rubocopfmt--process-errors resultbuf) - nil)))) - -(defun rubocopfmt--process-errors (resultbuf) - "Display contents of RESULTBUF as errors." - (if (eq rubocopfmt-show-errors 'echo) - (with-current-buffer resultbuf - (message (buffer-string)))) - - (if (eq rubocopfmt-show-errors 'buffer) - (let ((errbuf (get-buffer-create "*Rubocopfmt errors*"))) - (with-current-buffer errbuf - (erase-buffer) - (goto-char (point-min)) - (insert-buffer-substring resultbuf)) - (display-buffer errbuf)))) - -(defun rubocopfmt--bundled-path-p (directory) - "Check if there is a Gemfile in DIRECTORY, or any parent directory." - (rubocopfmt--file-search-upward directory "Gemfile")) - -(defun rubocopfmt--file-search-upward (directory file) - "Search DIRECTORY for FILE and return its full path if found, or NIL if not. - -If FILE is not found in DIRECTORY, the parent of DIRECTORY will be searched." - (let ((parent-dir (file-truename (concat (file-name-directory directory) "../"))) - (current-path (if (not (string= (substring directory (- (length directory) 1)) "/")) - (concat directory "/" file) - (concat directory file)))) - - (if (file-exists-p current-path) - current-path - (when (and (not (string= (file-truename directory) parent-dir)) - (< (length parent-dir) (length (file-truename directory)))) - (rubocopfmt--file-search-upward parent-dir file))))) (defun rubocopfmt--apply-rcs-patch (patch-buffer) "Apply an RCS-formatted diff from PATCH-BUFFER to the current buffer." @@ -262,6 +155,115 @@ function." (goto-char (point-min)) (forward-line (1- line))) +(defun rubocopfmt--bundled-path-p (directory) + "Check if there is a Gemfile in DIRECTORY, or any parent of DIRECTORY." + (rubocopfmt--file-search-upward directory "Gemfile")) + +(defun rubocopfmt--file-search-upward (directory file) + "Search DIRECTORY for FILE and return its full path if found, or NIL if not. + +If FILE is not found in DIRECTORY, the parent of DIRECTORY will be searched." + (let ((parent-dir (file-truename (concat (file-name-directory directory) "../"))) + (current-path (if (not (string= (substring directory (- (length directory) 1)) "/")) + (concat directory "/" file) + (concat directory file)))) + + (if (file-exists-p current-path) + current-path + (when (and (not (string= (file-truename directory) parent-dir)) + (< (length parent-dir) (length (file-truename directory)))) + (rubocopfmt--file-search-upward parent-dir file))))) + +(defun rubocopfmt--parse-result (resultbuf tmpfile) + "Parse Rubocop result in RESULTBUF and write corrections to TMPFILE." + (let ((split 0)) + (with-current-buffer resultbuf + (goto-char (point-min)) + ;; Only find the separator when RuboCop has printed complaints. + (setq split (search-forward "\n====================\n" nil t)) + + ;; If no RuboCop complaints were printed, we need to find the separator at + ;; the beginning of the buffer. This separation helps prevent false + ;; positive separator matches. + (unless split + (setq split (search-forward "====================\n" nil t))) + + (if split + (when (> split 22) + (goto-char (point-min)) + (when (search-forward "[Corrected]" split t) + (write-region split (point-max) tmpfile) + t)) + (rubocopfmt--process-errors resultbuf) + nil)))) + +(defun rubocopfmt--process-errors (resultbuf) + "Display contents of RESULTBUF as errors." + (if (eq rubocopfmt-show-errors 'echo) + (with-current-buffer resultbuf + (message (buffer-string)))) + + (if (eq rubocopfmt-show-errors 'buffer) + (let ((errbuf (get-buffer-create "*Rubocopfmt Errors*"))) + (with-current-buffer errbuf + (erase-buffer) + (goto-char (point-min)) + (insert-buffer-substring resultbuf)) + (display-buffer errbuf)))) + + +;;;###autoload +(defun rubocopfmt () + "Format the current buffer with rubocop." + (interactive) + (let* ((coding-system-for-read 'utf-8) + (coding-system-for-write 'utf-8) + (tmpfile (make-temp-file "rubocopfmt" nil ".rb")) + (resultbuf (get-buffer-create "*Rubocopfmt Result*")) + (patchbuf (get-buffer-create "*Rubocopfmt Patch*")) + (buffer-file (file-truename buffer-file-name)) + (src-dir (file-name-directory buffer-file)) + (src-file (file-name-nondirectory buffer-file)) + (fmt-command rubocopfmt-rubocop-command) + (fmt-args (list "--stdin" src-file + "--auto-correct" + "--format" "emacs"))) + + (if (rubocopfmt--bundled-path-p src-dir) + (setq fmt-command "bundle" + fmt-args (append (list "exec" rubocopfmt-rubocop-command) + fmt-args))) + + (if rubocopfmt-disabled-cops + (setq fmt-args (append fmt-args (list "--except" + (combine-and-quote-strings + rubocopfmt-disabled-cops ","))))) + + (unwind-protect + (save-restriction + (widen) + (write-region nil nil tmpfile) + (with-current-buffer resultbuf (erase-buffer)) + (with-current-buffer patchbuf (erase-buffer)) + + (let ((current-directory src-dir)) + (message "Calling rubocop from directory \"%s\": %s %s" + src-dir fmt-command (mapconcat 'identity fmt-args " ")) + (apply #'call-process-region (point-min) (point-max) + fmt-command nil resultbuf nil fmt-args) + (if (rubocopfmt--parse-result resultbuf tmpfile) + (call-process-region (point-min) (point-max) "diff" + nil patchbuf nil "-n" "-" tmpfile))) + + (if (= (buffer-size patchbuf) 0) + (message "Buffer is already rubocopfmted") + (rubocopfmt--apply-rcs-patch patchbuf) + (message "Applied rubocopfmt"))) + + (delete-file tmpfile) + (kill-buffer resultbuf) + (kill-buffer patchbuf)))) + ;;;###autoload (define-minor-mode rubocopfmt-mode "Enable format-on-save for `ruby-mode' buffers via rubocopfmt."