178 lines
6.5 KiB
EmacsLisp
178 lines
6.5 KiB
EmacsLisp
;;; prettysharp.el --- Support for formatting C# with prettysharp
|
|
|
|
;;; Commentary:
|
|
|
|
;; Copyright 2019 John Doty. All rights reserved.
|
|
|
|
;; This program is free software: you can redistribute it and/or modify it
|
|
;; under the terms of the GNU General Public License as published by the Free
|
|
;; Software Foundation, either version 3 of the License, or (at your option)
|
|
;; any later version.
|
|
|
|
;; This program is distributed in the hope that it will be useful, but
|
|
;; WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
|
;; or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
|
;; for more details.
|
|
|
|
;; You should have received a copy of the GNU General Public License along
|
|
;; with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
|
|
;; Author: John Doty
|
|
;; Version: 1.0.0
|
|
;; Keywords: convenience wp edit csharp
|
|
;; URL: https://github.com/decarabas/prettysharp
|
|
;;
|
|
;; This file is not part of GNU Emacs.
|
|
|
|
;;; Code:
|
|
(defgroup prettysharp nil
|
|
"Minor mode to format C# code on save."
|
|
:group 'languages
|
|
:prefix "prettysharp"
|
|
:link '(url-link "https://github.com/DeCarabas/PrettySharp"))
|
|
|
|
(defcustom prettysharp-command "prettysharp"
|
|
"The 'prettysharp' command."
|
|
:type 'string
|
|
:group 'prettysharp)
|
|
|
|
(defcustom prettysharp-show-errors 'echo
|
|
"Where to display prettysharp error output.
|
|
|
|
It can either be displayed in its own buffer, in the echo area,
|
|
or not at all."
|
|
:type '(choice
|
|
(const :tag "Own buffer" buffer)
|
|
(const :tag "Echo area" echo)
|
|
(const :tag "None" nil))
|
|
:group 'prettysharp)
|
|
|
|
(defconst prettysharp/error-buffer-name " *PrettySharp Errors*"
|
|
"The title of the buffer we show errors in.")
|
|
|
|
(defconst prettysharp/patch-buffer-name " *PrettySharp Patch*"
|
|
"The title of the buffer we format the patch in.")
|
|
|
|
(defun prettysharp/show-errors (outfile)
|
|
"Display the errors in OUTFILE.
|
|
|
|
Errors are displayed according to the value of prettysharp-show-errors."
|
|
(when prettysharp-show-errors
|
|
(let ((error-buffer (get-buffer-create prettysharp/error-buffer-name)))
|
|
(with-current-buffer error-buffer
|
|
(setq buffer-read-only nil)
|
|
(insert-file-contents outfile nil nil nil 'replace)
|
|
(cond
|
|
((eq prettysharp-show-errors 'buffer)
|
|
(goto-char (point-min))
|
|
(insert "prettysharp errors:\n")
|
|
(compilation-mode)
|
|
(display-buffer error-buffer))
|
|
|
|
((eq prettysharp-show-errors 'echo)
|
|
(message "%s" (buffer-string)))
|
|
)))))
|
|
|
|
(defun prettysharp/clear-errors ()
|
|
"Clear any errors from PrettySharp."
|
|
(let ((error-buffer (get-buffer prettysharp/error-buffer-name)))
|
|
(when error-buffer
|
|
(message "Clear errors?")
|
|
(with-current-buffer error-buffer
|
|
(setq buffer-read-only nil)
|
|
(erase-buffer))
|
|
(kill-buffer error-buffer))))
|
|
|
|
(defun prettysharp/make-patch (outfile patch-buffer)
|
|
"Diff the contents of the current buffer with OUTFILE, generate an RCS-style patch, and put the results into PATCH-BUFFER."
|
|
(call-process-region (point-min) (point-max) "diff" nil
|
|
patch-buffer nil "-n" "--strip-trailing-cr"
|
|
"-" outfile))
|
|
|
|
(defun prettysharp/apply-diff (patch-buffer)
|
|
"Apply the contents of PATCH-BUFFER as an RCS diff against the current buffer."
|
|
(let ((target-buffer (current-buffer))
|
|
(line-offset 0))
|
|
(save-excursion
|
|
(goto-char (point-min))
|
|
(with-current-buffer patch-buffer
|
|
(goto-char (point-min))
|
|
(while (not (eobp))
|
|
(cond
|
|
|
|
((looking-at "^$")
|
|
;; blank line is a valid command, meaning do nothing.
|
|
(forward-line))
|
|
|
|
((looking-at "^a\\([0-9]+\\) \\([0-9]+\\)$")
|
|
;; add lines.
|
|
(forward-line)
|
|
(let ((text-start (point))
|
|
(insert-at (string-to-number (match-string 1)))
|
|
(line-count (string-to-number (match-string 2))))
|
|
(forward-line line-count)
|
|
(let ((to-insert (buffer-substring text-start (point))))
|
|
(with-current-buffer target-buffer
|
|
(save-excursion
|
|
(goto-char (point-min))
|
|
(forward-line (+ insert-at line-offset))
|
|
;; It can happen that forward-line moves us to the end of
|
|
;; the buffer but not a blank line; in this case we need
|
|
;; to insert a newline.
|
|
(if (and (eobp) (looking-at "$") (not (looking-at "^")))
|
|
(insert "\n"))
|
|
(insert to-insert))
|
|
(setq line-offset (+ line-offset line-count))))))
|
|
|
|
((looking-at "^d\\([0-9]+\\) \\([0-9]+\\)$")
|
|
;; delete lines.
|
|
(forward-line)
|
|
(let ((delete-at (string-to-number (match-string 1)))
|
|
(line-count (string-to-number (match-string 2))))
|
|
(with-current-buffer target-buffer
|
|
(save-excursion
|
|
(goto-char (point-min))
|
|
(forward-line (1- (+ delete-at line-offset)))
|
|
(let ((delete-start (point)))
|
|
(forward-line line-count)
|
|
(delete-region delete-start (point))))
|
|
(setq line-offset (- line-offset line-count)))))
|
|
|
|
(t
|
|
(error "Unrecognized RCS command in prettysharp/apply-diff"))
|
|
))))))
|
|
|
|
(defun prettysharp ()
|
|
"Format the current buffer according to prettysharp."
|
|
(interactive)
|
|
(let ((outfile (make-temp-file "prettysharp" nil "cs"))
|
|
(patch-buffer (get-buffer-create prettysharp/patch-buffer-name))
|
|
(coding-system-for-read 'utf-8)
|
|
(coding-system-for-write 'utf-8))
|
|
|
|
(unwind-protect
|
|
(save-restriction
|
|
(widen)
|
|
(with-current-buffer patch-buffer
|
|
(erase-buffer))
|
|
|
|
(if (zerop (call-process-region (point-min) (point-max) prettysharp-command
|
|
nil (list :file outfile) nil))
|
|
(progn
|
|
(prettysharp/make-patch outfile patch-buffer)
|
|
(prettysharp/apply-diff patch-buffer)
|
|
(prettysharp/clear-errors))
|
|
(prettysharp/show-errors outfile)))
|
|
|
|
(delete-file outfile))))
|
|
|
|
;;;###autoload
|
|
(define-minor-mode prettysharp-mode
|
|
"Minor mode to run prettysharp on file save."
|
|
:lighter " pretty#"
|
|
(if prettysharp-mode
|
|
(add-hook 'before-save-hook 'prettysharp nil 'local)
|
|
(remove-hook 'before-save-hook 'prettysharp 'local)))
|
|
|
|
(provide 'prettysharp)
|
|
;;; prettysharp.el ends here
|