-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathjscs.el
186 lines (160 loc) · 5.85 KB
/
jscs.el
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
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
;;; jscs.el --- Consistent JavaScript editing using JSCS -*- lexical-binding: t; -*-
;; Copyright (C) 2015 papaeye
;; Author: papaeye <[email protected]>
;; Keywords: languages, convenience
;; Version: 0.1.0
;; Homepage: https://github.com/papaeye/emacs-jscs
;; Package-Requires: ((emacs "24.1") (langfmt "0.1.0"))
;; 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/>.
;;; Commentary:
;; jscs.el provides consistent JavaScript editing using JSCS.
;;
;; Installation:
;;
;; 1. Install JSCS <http://jscs.info/>.
;;
;; 2. Put jscs.el and langfmt.el <https://github.com/papaeye/go-mode.el>
;; somewhere in your `load-path'.
;;
;; 3. Add the following code into your .emacs:
;;
;; (autoload 'jscs-indent-apply "jscs" nil t)
;; (autoload 'jscs-fix "jscs" nil t)
;; (autoload 'jscs-fix-before-save "jscs" nil t)
;;
;; Usage:
;;
;; To apply JSCS indentation rules to JavaScript modes,
;; add the following code into your .emacs:
;;
;; (with-eval-after-load 'js
;; (add-hook 'js-mode-hook #'jscs-indent-apply))
;;
;; (with-eval-after-load 'js2-mode
;; (add-hook 'js2-mode-hook #'jscs-indent-apply))
;;
;; To run "jscs --fix" interactively, run \\[jscs-fix].
;;
;; To run "jscs --fix" on the current buffer when saving,
;; add the following code into your .emacs:
;;
;; (add-hook 'before-save-hook #'jscs-fix-before-save)
;;; Code:
(require 'json)
(require 'langfmt)
(defvar js-indent-level)
(defvar js2-basic-offset)
(defgroup jscs nil
"Consistent JavaScript editing using JSCS"
:group 'tools)
(defcustom jscs-command "jscs"
"The 'jscs' command."
:type 'string
:group 'jscs)
(defvar jscs--presets-path
(expand-file-name "../lib/node_modules/jscs/presets"
(file-name-directory (executable-find jscs-command))))
(defun jscs--read-jscsrc ()
(let ((dir (locate-dominating-file default-directory ".jscsrc")))
(when dir
(json-read-file (expand-file-name ".jscsrc" dir)))))
(defun jscs--read-preset (name)
(let ((preset (expand-file-name (concat name ".json") jscs--presets-path)))
(if (file-readable-p preset)
(json-read-file preset)
(error "Preset %s is not found" name))))
(defun jscs--config-list (config)
(let ((preset (cdr (assq 'preset config))))
(if (stringp preset)
(cons config (jscs--config-list (jscs--read-preset preset)))
(list config))))
(defun jscs-indent--rule-validate-indentation (config)
(let ((indent (cdr (assq 'validateIndentation config))))
(prog1 indent
(when (listp indent)
(setq indent (cdr (assq 'value indent))))
(cond
((integerp indent)
(cond
((memq major-mode '(js-mode json-mode))
(setq-local js-indent-level indent))
((eq major-mode 'js2-mode)
(setq js2-basic-offset indent)))
(setq indent-tabs-mode nil))
((string= indent "\t")
(setq indent-tabs-mode t))))))
(defun jscs-indent--rule-maximum-line-length (config)
(let ((rule (cdr (assq 'maximumLineLength config)))
tab-size)
(prog1 rule
(when (listp rule)
(setq tab-size (cdr (assq 'tabSize rule)))
(when (integerp tab-size)
(setq tab-width tab-size))))))
(defvar jscs-indent--rule-functions
(list #'jscs-indent--rule-validate-indentation
#'jscs-indent--rule-maximum-line-length))
(defun jscs-indent--apply (config)
(let ((config-list (jscs--config-list config)))
(dolist (func jscs-indent--rule-functions)
(let ((tail config-list)
done)
(while (and (not done) tail)
(setq done (funcall func (car tail)))
(setq tail (cdr tail)))))))
;;;###autoload
(defun jscs-indent-apply ()
(interactive)
(let ((jscsrc (jscs--read-jscsrc)))
(when jscsrc
(jscs-indent--apply jscsrc))))
;;;###autoload (autoload 'jscs-fix "jscs" nil t)
;;;###autoload (autoload 'jscs-fix-before-save "jscs" nil t)
(define-langfmt jscs-fix
"Format the current buffer according to the JSCS tool."
:group 'jscs
:modes '(js-mode js2-mode js3-mode)
:runner #'jscs-fix--runner
:error-filter #'jscs-fix--error-filter)
(defun jscs-fix--runner (tmpfile patchbuf errbuf)
(let ((exit (call-process jscs-command nil errbuf nil
"--fix" "--reporter" "inline" tmpfile)))
(if (= exit 1)
(progn
(message "No configuration found")
(when errbuf
(langfmt-kill-error-buffer errbuf)))
(if (zerop (call-process-region (point-min) (point-max) "diff"
nil patchbuf nil "-n" "-" tmpfile))
(message (if (zerop exit)
"Buffer is already jscs-fixed"
"Could not apply jscs-fix"))
(langfmt-apply-rcs-patch patchbuf)
(message (if (zerop exit)
"Applied jscs-fix"
"Applied jscs-fix partially")))
(when errbuf
(if (zerop exit)
(langfmt-kill-error-buffer errbuf)
(jscs-fix--process-errors (buffer-file-name) tmpfile errbuf))))))
(defun jscs-fix--error-filter (filename tmpfile)
(while (search-forward-regexp
(concat "^\\(?:"
(regexp-quote tmpfile)
"\\): line \\([0-9]+\\), col \\([0-9]+\\), \\(.+\\)")
nil t)
(replace-match (concat (file-name-nondirectory filename)
":" (match-string 1) ":" (match-string 2)
": " (match-string 3))
t t)))
(provide 'jscs)
;;; jscs.el ends here