summaryrefslogtreecommitdiff
path: root/elpa/skewer-mode-20200304.1142/skewer-css.el
blob: 457c74259f0330994dfb2323625e86a493d41d7b (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
;;; skewer-css.el --- skewer support for live-interaction CSS -*- lexical-binding: t; -*-

;; This is free and unencumbered software released into the public domain.

;;; Commentary:

;; This minor mode provides functionality for CSS like plain Skewer
;; does for JavaScript.

;; * C-x C-e -- `skewer-css-eval-current-declaration'
;; * C-M-x   -- `skewer-css-eval-current-rule'
;; * C-c C-k -- `skewer-css-eval-buffer'

;; These functions assume there are no comments within a CSS rule,
;; *especially* not within a declaration. In the former case, if you
;; keep the comment free of CSS syntax it should be able to manage
;; reasonably well. This may be fixed someday.

;;; Code:

(require 'css-mode)
(require 'skewer-mode)

(defun skewer-css-trim (string)
  "Trim and compress whitespace in the string."
  (let ((cleaned (replace-regexp-in-string "[\t\n ]+" " " string)))
    (replace-regexp-in-string "^[\t\n ]+\\|[\t\n ]+$" "" cleaned)))

;; Parsing

(defun skewer-css-beginning-of-rule ()
  "Move to the beginning of the current rule and return point."
  (skewer-css-end-of-rule)
  (re-search-backward "{")
  (when (re-search-backward "[}/]" nil 'start)
    (forward-char))
  (re-search-forward "[^ \t\n]")
  (backward-char)
  (point))

(defun skewer-css-end-of-rule ()
  "Move to the end of the current rule and return point."
  (if (eql (char-before) ?})
      (point)
    (re-search-forward "}")))

(defun skewer-css-end-of-declaration ()
  "Move to the end of the current declaration and return point."
  (if (eql (char-before) ?\;)
      (point)
    (re-search-forward ";")))

(defun skewer-css-beginning-of-declaration ()
  "Move to the end of the current declaration and return point."
  (skewer-css-end-of-declaration)
  (re-search-backward ":")
  (backward-sexp 1)
  (point))

(defun skewer-css-selectors ()
  "Return the selectors for the current rule."
  (save-excursion
    (let ((start (skewer-css-beginning-of-rule))
          (end (1- (re-search-forward "{"))))
      (skewer-css-trim
       (buffer-substring-no-properties start end)))))

(defun skewer-css-declaration ()
  "Return the current declaration as a pair of strings."
  (save-excursion
    (let ((start (skewer-css-beginning-of-declaration))
          (end (skewer-css-end-of-declaration)))
      (let* ((clip (buffer-substring-no-properties start end))
             (pair (split-string clip ":")))
        (mapcar #'skewer-css-trim pair)))))

;; Evaluation

(defun skewer-css (rule)
  "Add RULE as a new stylesheet."
  (skewer-eval rule nil :type "css"))

(defun skewer-css-eval-current-declaration ()
  "Evaluate the declaration at the point."
  (interactive)
  (save-excursion
    (let ((selectors (skewer-css-selectors))
          (rule (skewer-css-declaration))
          (start (skewer-css-beginning-of-declaration))
          (end (skewer-css-end-of-declaration)))
      (skewer-flash-region start end)
      (skewer-css (apply #'format "%s { %s: %s }" selectors rule)))))

(defun skewer-css-eval-current-rule ()
  "Evaluate the rule at the point."
  (interactive)
  (save-excursion
    (let* ((start (skewer-css-beginning-of-rule))
           (end (skewer-css-end-of-rule))
           (rule (buffer-substring-no-properties start end)))
      (skewer-flash-region start end)
      (skewer-css (skewer-css-trim rule)))))

(defun skewer-css-eval-buffer ()
  "Send the entire current buffer as a new stylesheet."
  (interactive)
  (skewer-css (buffer-substring-no-properties (point-min) (point-max))))

(defun skewer-css-clear-all ()
  "Remove *all* Skewer-added styles from the document."
  (interactive)
  (skewer-eval nil nil :type "cssClearAll"))

;; Minor mode definition

(defvar skewer-css-mode-map
  (let ((map (make-sparse-keymap)))
    (prog1 map
      (define-key map (kbd "C-x C-e") 'skewer-css-eval-current-declaration)
      (define-key map (kbd "C-M-x") 'skewer-css-eval-current-rule)
      (define-key map (kbd "C-c C-k") 'skewer-css-eval-buffer)
      (define-key map (kbd "C-c C-c") 'skewer-css-clear-all)))
  "Keymap for skewer-css-mode.")

;;;###autoload
(define-minor-mode skewer-css-mode
  "Minor mode for interactively loading new CSS rules."
  :lighter " skewer-css"
  :keymap skewer-css-mode-map
  :group 'skewer)

(provide 'skewer-css)

;;; skewer-css.el ends here