-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathskewer-reload-stylesheets.el
196 lines (140 loc) · 7.08 KB
/
skewer-reload-stylesheets.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
;;; skewer-reload-stylesheets.el --- live-edit CSS, SCSS, Less, and friends.
;; This is free and unencumbered software released into the public domain.
;; Author: Nate Eagleson <[email protected]>
;; Created: November 23, 2013
;; Package-Requires: ((skewer-mode "1.5.3"))
;; Version: 0.3.0
;;; Commentary:
;; This minor mode provides live-editing of CSS and transpile-to-CSS languages
;; via skewer.
;; skewer-css works for many cases, but if you're dealing with multiple
;; stylesheets and involved cascading (a.k.a. "legacy code"), it isn't so
;; useful. What you see while live-editing is not what you see when you
;; refresh, since skewer-css puts the updated CSS in new style tags, thus
;; changing their specificity.
;; skewer-css also doesn't work with Less, SCSS, or any of the lesser-known
;; compile-to-CSS languages - just vanilla CSS (though there is the skewer-less
;; package, if you run Less's in-browser JS version for development).
;; Enter this minor mode.
;; It refreshes stylesheets after saves by adding or updating a query string to
;; the relevant link tags in the browser.
;; Thus, what you see on a fresh pageload is always exactly what you see while
;; live-editing.
;;; Setup
;; Install from MELPA, then put the following somewhere in your init file:
;; (add-hook 'css-mode-hook 'skewer-reload-stylesheets-start-editing)
;; If you're live-editing Less, SCSS, or similar, add
;; `skewer-reload-stylesheets-start-editing' to the appropriate hook variable,
;; then set `skewer-reload-css-compile-command' to your transpile command:
;;
;; (setq skewer-reload-stylesheets-compile-command "gulp css")
;;
;; This variable is best set in .dir-locals.el, so it can be set correctly
;; per-project.
;;; Usage
;; Open a browser window for the URL whose stylesheets you want to live-edit.
;; Skewer that window.
;; In emacs, open the stylesheet(s) you want to live-edit.
;; Make some changes in the stylesheet and save it. The updates will immediately
;; be reflected in the skewered windows.
;; and there you are - cross-browser live-editing for arbitrarily complex
;; stylesheets.
;; Note that browser plugins like
;; [Custom Javascript for Websites](https://chrome.google.com/webstore/detail/custom-javascript-for-web/poakhlngfciodnhlhhgnaaelnpjljija?hl=en)
;; make it easy to auto-skewer URLs on pageload, so you don't have to manually
;; re-skewer after every refresh.
;; Key bindings:
;; * C-x C-r -- `skewer-reload-stylesheets-reload-buffer`
;; Note that this keybinding is deprecated, as current usage reloads
;; stylesheets with an after-save-hook, so there is no need for a custom
;; keybinding.
;;; Code:
(require 'skewer-mode)
(defvar skewer-reload-stylesheets-data-root (file-name-directory load-file-name)
"Location of data files needed by skewer-reload-stylesheets-mode.")
(defvar skewer-reload-stylesheets-compile-command nil
"Command to compile current stylesheet into a CSS file.
If using SCSS, Less, or similar, set this to your compile command.
The command will be run asynchronously after a save, and all stylesheets in the
document will be reloaded once the build is complete.
By reloading all stylesheets, we work around the need to figure out which
stylesheets actually need to be reloaded, which could be involved for a complex
set of stylesheets.
The best place to define this is .dir-locals.el for a given project.
TODO Automatically pick up compile command from scss-mode config?
There may be a lot of smart defaulting we could do for the various CSS
variant modes.
TODO See if there's a need to reload only changed stylesheets. If a project had
a lot of stylesheets, reloading them all could be pretty slow, but the
compile-to-CSS projects I've seen tend to have just one main one along with a
few vendor files that will usually be 304s.")
(defun skewer-reload-stylesheets-reload-buffer ()
"Save current buffer and ask skewer to reload it."
(declare (obsolete skewer-reload-stylesheets-reload-on-save "0.1.0"))
(interactive)
(save-buffer)
(skewer-reload-stylesheets-reload))
(defun skewer-reload-stylesheets-compile-sentinel (process event)
"Sentinel to handle status changes in CSS compilation jobs.
TODO It would be nice to handle failed compiles more cleanly.
Right now it just throws an error, but things like syntax errors are
expected outcomes while programming."
(cond ((equal event "finished\n")
(skewer-eval "skewer.reloadStylesheets.reloadAll();"))
;; TODO Just use 't' as my second cond?
;; Is any event other than successful exit a failure?
((string-prefix-p "exited abnormally" event)
(error "CSS compilation failure: %s" event))))
(defun skewer-reload-stylesheets-reload ()
"Ask browser to reload the stylesheet for the current buffer."
(if skewer-reload-stylesheets-compile-command
;; Compile CSS, and reload all stylesheets when finished.
(let ((compile-css (start-process-shell-command
"skewer-make-css"
"*skewer-reload-stylesheets-compile-log*"
skewer-reload-stylesheets-compile-command)))
(set-process-sentinel compile-css
'skewer-reload-stylesheets-compile-sentinel))
;; No compile command, so we must be in a CSS file. Just reload the
;; corresponding stylesheets.
;; TODO I tried to use skewer-apply, but it said skewer.reloadStylesheet was
;; not a valid function.
(skewer-eval (concat "skewer.reloadStylesheets.reload(\"" (buffer-file-name) "\");"))))
(defun skewer-reload-stylesheets-reload-on-save ()
"Ask skewer to reload stylesheets immediately after save.
Call this in your css-mode-hook to automatically reload stylesheets on save."
(add-hook 'after-save-hook
'skewer-reload-stylesheets-reload
nil
t))
(defun skewer-reload-stylesheets-skewer-js-hook ()
"Skewer hook function to insert JS for reloading CSS files."
(insert-file-contents
(expand-file-name "skewer-reload-stylesheets.js" skewer-reload-stylesheets-data-root)))
(add-hook 'skewer-js-hook 'skewer-reload-stylesheets-skewer-js-hook)
;;;###autoload
(defun skewer-reload-stylesheets-start-editing ()
"Configure current buffer for live-editing.
Add this to your stylesheet editing mode hook to make every
stylesheet live-editable by default."
;; Check for "is simple-httpd running?" snagged from simple-httpd.el's
;; stop-httpd function.
;; TODO Submit a PR to simple-httpd for checking if the server is running.
(unless (process-status "httpd")
(run-skewer))
(skewer-reload-stylesheets-mode)
(skewer-reload-stylesheets-reload-on-save))
;; Minor mode definition
(defvar skewer-reload-stylesheets-mode-map
(let ((map (make-sparse-keymap)))
(prog1 map
(define-key map (kbd "C-x C-r") 'skewer-reload-stylesheets-reload-buffer)))
"Keymap for skewer-reload-stylesheets-mode.")
;;;###autoload
(define-minor-mode skewer-reload-stylesheets-mode
"Minor mode for interactively reloading CSS stylesheets."
:lighter " reload-ss"
:keymap skewer-reload-stylesheets-mode-map
:group 'skewer)
(provide 'skewer-reload-stylesheets)
;;; skewer-reload-stylesheets.el ends here