summaryrefslogtreecommitdiff
path: root/contrib/uncrustify-mode.el
blob: 83868c6a146cee89f5b4062d5e8c9f9d06acd526 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
;;; uncrustify-mode.el --- Minor mode to automatically uncrustify.

;; Copyright (C) 2012  tabi
;; Author: Tabito Ohtani <koko1000ban@gmail.com>
;; Version: 0.01
;; Keywords: uncrustify

;; 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 <http://www.gnu.org/licenses/>.

;;; Installation:

;; drop requirements and this file into a directory in your `load-path',
;; and put these lines into your .emacs file.

;; (require 'uncrustify-mode)
;; (add-hook 'c-mode-common-hook
;;    '(lambda ()
;;        (uncrustify-mode 1)))

;;; ChangeLog:
;; * 0.0.1:
;;   Initial version.


;; case
(eval-when-compile
  (require 'cl))

;;; Variables:

(defcustom uncrustify-config-path
  "~/.uncrustify.cfg"
  "uncrustify config file path"
  :group 'uncrustify
  :type 'file)
(make-variable-buffer-local 'uncrustify-config-path)

(defcustom uncrustify-bin
  "uncrustify -q"
  "The command to run uncrustify."
  :group 'uncrustify)

;;; Functions:

(defun uncrustify-get-lang-from-mode (&optional mode)
  "uncrustify lang option"
  (let ((m (or mode major-mode)))
    (case m
      ('c-mode "C")
      ('c++-mode "CPP")
      ('d-mode "D")
      ('java-mode "JAVA")
      ('objc-mode "OC")
      (t
       nil))))

(defun uncrustify-point->line (point)
  "Get the line number that POINT is on."
  ;; I'm not bothering to use save-excursion because I think I'm
  ;; calling this function from inside other things that are likely to
  ;; use that and all I really need to do is restore my current
  ;; point. So that's what I'm doing manually.
  (let ((line 1)
        (original-point (point)))
    (goto-char (point-min))
    (while (< (point) point)
      (incf line)
      (forward-line))
    (goto-char original-point)
    line))

(defun uncrustify-invoke-command (lang start-in end-in)
  "Run uncrustify on the current region or buffer."
  (if lang
      (let ((start (or start-in (point-min)))
            (end   (or end-in   (point-max)))
            (original-line (uncrustify-point->line (point)))
            (cmd (concat uncrustify-bin " -c " uncrustify-config-path " -l " lang))
            (out-buf (get-buffer-create "*uncrustify-out*"))
            (error-buf (get-buffer-create "*uncrustify-errors*")))

        (with-current-buffer error-buf (erase-buffer))
        (with-current-buffer out-buf (erase-buffer))

        ;; Inexplicably, save-excursion doesn't work to restore the
        ;; point. I'm using it to restore the mark and point and manually
        ;; navigating to the proper new-line.
        (let ((result
               (save-excursion
                 (let ((ret (shell-command-on-region start end cmd t t error-buf nil)))
                   (if (and
                        (numberp ret)
                        (zerop ret))
                       ;; Success! Clean up.
                       (progn
                         (message "Success! uncrustify modify buffer.")
                         (kill-buffer error-buf)
                         t)
                     ;; Oops! Show our error and give back the text that
                     ;; shell-command-on-region stole.
                     (progn (undo)
                            (with-current-buffer error-buf
                              (message "uncrustify error: <%s> <%s>" ret (buffer-string)))
                            nil))))))

          ;; This goto-line is outside the save-excursion becuase it'd get
          ;; removed otherwise.  I hate this bug. It makes things so ugly.
          (goto-line original-line)
          (not result)))
    (message "uncrustify not support this mode : %s" major-mode)))

(defun uncrustify ()
  (interactive)
  (save-restriction
    (widen)
    (uncrustify-invoke-command (uncrustify-get-lang-from-mode) (region-beginning) (region-end))))

(defun uncrustify-buffer ()
  (interactive)
  (save-restriction
    (widen)
    (uncrustify-invoke-command (uncrustify-get-lang-from-mode) (point-min) (point-max))))

;;; mode

(defun uncrustify-write-hook ()
  "Uncrustifys a buffer during `write-file-hooks' for `uncrustify-mode'.
   if uncrustify returns not nil then the buffer isn't saved."
  (if uncrustify-mode
      (save-restriction
        (widen)
        (uncrustify-invoke-command (uncrustify-get-lang-from-mode) (point-min) (point-max)))))

;;;###autoload
(define-minor-mode uncrustify-mode
  "Automatically `uncrustify' when saving."
  :lighter " Uncrustify"
  (if (not (uncrustify-get-lang-from-mode))
      (message "uncrustify not support this mode : %s" major-mode)
  (if (version<= "24" emacs-version)
    (if uncrustify-mode
        (add-hook 'write-file-hooks 'uncrustify-write-hook nil t)
      (remove-hook 'uncrustify-write-hook t))
    (make-local-hook 'write-file-hooks)
    (funcall (if uncrustify-mode #'add-hook #'remove-hook)
             'write-file-hooks 'uncrustify-write-hook))))

(provide 'uncrustify-mode)

;;; uncrustify-mode.el ends here