forked from psachin/insert-shebang
-
Notifications
You must be signed in to change notification settings - Fork 0
/
insert-shebang.el
282 lines (248 loc) · 10.3 KB
/
insert-shebang.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
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
;;; insert-shebang.el --- Insert shebang line automatically.
;; Copyright (C) 2013-2017 Sachin Patil
;; Author: Sachin Patil <iclcoolster@gmail.com>
;; URL: http://github.com/psachin/insert-shebang
;; Keywords: shebang, tool, convenience
;; Version: 0.9.6
;; This file is NOT a part of GNU Emacs.
;; `insert-shebang' is free software distributed under the terms of
;; the GNU General Public License, version 3. For details, see the
;; file COPYING.
;;; Commentary:
;; Inserts shebang line automatically
;; URL: http://github.com/psachin/insert-shebang
;; Install
;; Using `package'
;; M-x package-install insert-shebang
;; Unless installed from a `package', add the directory containing
;; this file to `load-path', and then:
;; (require 'insert-shebang)
;;
;; Customize
;; M-x customize-group RET insert-shebang RET
;;
;; See ReadMe.org for more info.
;;; Code:
(defgroup insert-shebang nil
"Inserts shebang line automatically."
:group 'extensions
:link '(url-link :tag "Github" "https://github.com/psachin/insert-shebang"))
(defcustom insert-shebang-env-path "/usr/bin/env"
"Full path to `env' binary.
You can find the path to `env' by typing `which env' in the
terminal."
:type '(string)
:group 'insert-shebang)
(defcustom insert-shebang-file-types
'(("py" . "python")
("groovy" . "groovy")
("fish" . "fish")
("robot" . "robot")
("rb" . "ruby")
("lua" . "lua")
("php" . "php")
("sh" . "bash")
("pl" . "perl"))
"*If nil, add all your file extensions and file types here."
:type '(alist :key-type (string :tag "Extension")
:value-type (string :tag "Interpreter"))
:group 'insert-shebang)
(defcustom insert-shebang-ignore-extensions
'("txt" "org")
"*Add extensions you want to ignore.
List of file extensions to be ignored by default."
:type '(repeat (string :tag "extn"))
:group 'insert-shebang)
(defcustom insert-shebang-custom-headers nil
"Put your custom headers for other file types here.
For example '#include <stdio.h>' for c file etc.
Example:
File type: c
Header: #include <stdio.h>
File type: f90
Header: program
File type: f95
Header: program"
:type '(alist :key-type (string :tag "Extension")
:value-type (string :tag "Header"))
:group 'insert-shebang)
(defcustom insert-shebang-header-scan-limit 6
"Define how much initial characters to scan from starting for custom headers.
This is to avoid differentiating header `#include <stdio.h>` with
`#include <linux/modules.h>` or `#include <strings.h>`."
:type '(integer :tag "Limit")
:group 'insert-shebang)
(defcustom insert-shebang-track-ignored-filename "~/.insert-shebang.log"
"Filepath where list of ignored files are stored.
Set to nil if you do not want to keep log of ignored files."
:type '(string)
:group 'insert-shebang)
(defun insert-shebang-get-extension-and-insert (filename)
"Get extension from FILENAME and insert shebang.
FILENAME is a buffer name from which the extension is to be
extracted."
(if (file-name-extension filename)
(let ((file-extn (replace-regexp-in-string "[\<0-9\>]" ""
(file-name-extension filename))))
;; check if this extension is ignored
(if (car (member file-extn insert-shebang-ignore-extensions))
(progn (message "Extension ignored"))
;; if not, check in extension list
(progn
(if (car (assoc file-extn insert-shebang-custom-headers))
(progn ;; insert custom header
(let ((val (cdr (assoc file-extn insert-shebang-custom-headers))))
(if (= (point-min) (point-max))
;; insert custom-header at (point-min)
(insert-shebang-custom-header val)
(progn
(insert-shebang-scan-first-line-custom-header val)))))
(progn
;; get value against the key
(if (car (assoc file-extn insert-shebang-file-types))
;; if key exists in list 'insert-shebang-file-types'
(progn
;; set variable val to value of key
(let ((val (cdr (assoc file-extn insert-shebang-file-types))))
;; if buffer is new
(if (= (point-min) (point-max))
(insert-shebang-eval val)
;; if buffer has something, then
(progn
(insert-shebang-scan-first-line-eval val)))))
;; if key don't exists
(progn
(message "Can't guess file type. Type: 'M-x customize-group RET \
insert-shebang' to add/customize"))))))))))
(defun insert-shebang-eval (val)
"Insert shebang with prefix 'eval' string in current buffer.
With VAL as an argument."
(with-current-buffer (buffer-name)
(goto-char (point-min))
(insert (format "#!%s %s" insert-shebang-env-path val))
(newline)
(goto-char (point-min))
(end-of-line)))
(defun insert-shebang-custom-header (val)
"Insert custom header.
With VAL as an argument."
(with-current-buffer (buffer-name)
(goto-char (point-min))
(insert val)
(newline)
(goto-char (point-min))
(end-of-line)))
(defun insert-shebang-scan-first-line-eval (val)
"Scan very first line of the file.
With VAL as an argument and look if it has matching shebang-line."
(save-excursion
(goto-char (point-min))
;; search for shebang pattern
(if (ignore-errors (re-search-forward "^#![ ]?\\([a-zA-Z_./]+\\)"))
(message "insert-shebang: File has shebang line")
;; prompt user
(if (y-or-n-p "File do not have shebang line, \
do you want to insert it now? ")
(progn
(insert-shebang-eval val))
(progn
(insert-shebang-log-ignored-files
(replace-regexp-in-string "[\<0-9\>]" "" (original-buffer-name))))))))
(defun insert-shebang-scan-first-line-custom-header (val)
"Scan very first line of the file and look if it has matching header.
With VAL as an argument."
(save-excursion
(goto-char (point-min))
;; search for shebang pattern
(if (ignore-errors
(re-search-forward
(format "^%s"
(substring val 0 insert-shebang-header-scan-limit))))
(message "insert-shebang: File has header")
;; prompt user
(if (y-or-n-p "File do not have header, do you want to insert it now? ")
(progn
(goto-char (point-min))
(insert-shebang-custom-header val))
(progn
(insert-shebang-log-ignored-files
(replace-regexp-in-string "[\<0-9\>]" "" (original-buffer-name))))))))
(defun insert-shebang-read-log-file (log-file-path)
"Return a list of ignored files.
LOG-FILE-PATH is set in `insert-shebang-track-ignored-filename'"
(with-temp-buffer
(insert-file-contents log-file-path)
;; every new line is treated as an element.
(split-string (buffer-string) "\n" t)))
(defun insert-shebang-write-log-file (log-file-path log-file-list)
"Write list of files to be ignored to log file.
LOG-FILE-PATH is set in `insert-shebang-track-ignored-filename'
and LOG-FILE-LIST is a list of ignored files with fullpath."
(with-temp-buffer
;; every element on a new line.
(insert (mapconcat 'identity log-file-list "\n"))
(when (file-writable-p log-file-path)
(write-region (point-min)
(point-max)
log-file-path))))
(defun insert-shebang-create-log-file (logfile)
"Function to create log file if does not exist.
LOGFILE name is defined in `insert-shebang-track-ignored-filename'."
(if (not (file-exists-p (expand-file-name logfile)))
(write-region 1 1 (expand-file-name logfile) t)))
(defun insert-shebang-log-ignored-files (filename)
"Keep log of ignored files.
Ignore them on next visit.
FILENAME is `buffer-name'."
;; if `insert-shebang-track-ignored-filename' is `nil', don't track
;; ignored files.
(if (not (equal insert-shebang-track-ignored-filename nil))
(progn
;; if file not exist, create it
(insert-shebang-create-log-file insert-shebang-track-ignored-filename)
;; set variables
(let* ((log-file-path (expand-file-name
insert-shebang-track-ignored-filename))
(log-file-list (insert-shebang-read-log-file log-file-path)))
;; add new 'ignored' file to the list
(add-to-list 'log-file-list (expand-file-name filename))
;; Updated list in the log-file
;; (message "%s" log-file-list)
(insert-shebang-write-log-file log-file-path log-file-list)))))
(defun insert-shebang-open-log-buffer ()
"Open log of ignored file(s) in a separate buffer for editing."
(interactive "*")
(when (file-readable-p insert-shebang-track-ignored-filename)
(find-file-other-window insert-shebang-track-ignored-filename)))
(defun original-buffer-name ()
"Get un-uniquified buffer name."
(file-name-nondirectory (buffer-file-name)))
;;;###autoload
(defun insert-shebang ()
"Insert shebang line automatically.
Calls function `insert-shebang-get-extension-and-insert'. With argument as
`buffer-name'."
(interactive "*")
;; if `insert-shebang-track-ignored-filename' is `nil', don't track
;; ignored files.
(if (not (equal insert-shebang-track-ignored-filename nil))
(progn
;; if file not exist, create it
(insert-shebang-create-log-file insert-shebang-track-ignored-filename)
;; ignore current-buffer, if it's path exist in ignored file list.
(let* ((log-file-path (expand-file-name
insert-shebang-track-ignored-filename))
(log-file-list (insert-shebang-read-log-file log-file-path))
(filename (replace-regexp-in-string "[\<0-9\>]" ""
(expand-file-name
(original-buffer-name)))))
(if (member filename log-file-list)
;; do nothing.
(progn)
;; call `insert-shebang-get-extension-and-insert'.
(progn
(insert-shebang-get-extension-and-insert (original-buffer-name))))))
(insert-shebang-get-extension-and-insert (original-buffer-name))))
;;;###autoload(add-hook 'find-file-hook 'insert-shebang)
(provide 'insert-shebang)
;;; insert-shebang.el ends here