css-abbrev.el 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278
  1. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  2. ;;
  3. ;;; CSS abbrev:
  4. (emmet-defparameter
  5. emmet-css-unit-aliases
  6. (gethash "unitAliases" (gethash "css" emmet-preferences)))
  7. (defun emmet-css-arg-number (input)
  8. (emmet-parse
  9. " *\\(\\(?:-\\|\\)[0-9.]+\\)\\(-\\|[A-Za-z]*\\)" 3 "css number arguments"
  10. (cons (list (elt it 1)
  11. (let ((unit (elt it 2)))
  12. (if (= (length unit) 0)
  13. (if (find ?. (elt it 1)) "em" "px")
  14. (gethash unit emmet-css-unit-aliases unit))))
  15. input)))
  16. (emmet-defparameter
  17. emmet-css-color-shorten-if-possible
  18. (gethash "shortenIfPossible" (gethash "color" (gethash "css" emmet-preferences))))
  19. (emmet-defparameter
  20. emmet-css-color-case
  21. (gethash "case" (gethash "color" (gethash "css" emmet-preferences))))
  22. (emmet-defparameter
  23. emmet-css-color-trailing-aliases
  24. (gethash "trailingAliases" (gethash "color" (gethash "css" emmet-preferences))))
  25. (defun emmet-css-arg-color (input)
  26. (emmet-parse
  27. (concat " *#\\([0-9a-fA-F]\\{1,6\\}\\)\\(rgb\\|\\)\\(["
  28. (emmet-join-string
  29. (emmet-get-keys-of-hash emmet-css-color-trailing-aliases) "")
  30. "]\\|\\)")
  31. 4 "css color argument"
  32. (let ((color
  33. (let* ((n (elt it 1))
  34. (l (length n)))
  35. (substring
  36. (cond ((= l 1) (concat (make-list 6 (string-to-char n))))
  37. ((= l 2) (concat n n n))
  38. ((= l 3) (concat
  39. (loop for c in (string-to-list n)
  40. append (list c c))))
  41. (t (concat n n)))
  42. 0 6))))
  43. (cons
  44. (let ((rgb-mode (string= (elt it 2) "rgb")))
  45. (if rgb-mode
  46. (format "rgb(%d,%d,%d)"
  47. (string-to-number (substring color 0 2) 16)
  48. (string-to-number (substring color 2 4) 16)
  49. (string-to-number (substring color 4 6) 16))
  50. (concat
  51. "#"
  52. (let ((filter (cond ((string= emmet-css-color-case "auto") #'identity)
  53. ((string= emmet-css-color-case "up") #'upcase)
  54. (t #'downcase))))
  55. (funcall
  56. filter
  57. (if (and emmet-css-color-shorten-if-possible
  58. (eql (aref color 0) (aref color 1))
  59. (eql (aref color 2) (aref color 3))
  60. (eql (aref color 4) (aref color 5)))
  61. (concat (mapcar #'(lambda (i) (aref color i)) '(0 2 4)))
  62. color))))))
  63. (if (< 0 (length (elt it 3)))
  64. (cons (gethash (elt it 3) emmet-css-color-trailing-aliases) input)
  65. input)))))
  66. (defun emmet-css-arg-something (input)
  67. (emmet-parse
  68. " *\\([^ ]+\\)" 2 "css argument"
  69. (cons (elt it 1) input)))
  70. (defun emmet-css-parse-arg (input)
  71. (emmet-run emmet-css-arg-number it
  72. (emmet-run emmet-css-arg-color it
  73. (emmet-run emmet-css-arg-something it
  74. (if (equal input "")
  75. it
  76. (cons input ""))))))
  77. (defun emmet-css-important-p (input)
  78. (let ((len (length input)))
  79. (and (< 0 len)
  80. (char-equal (aref input (1- len)) ?!))))
  81. (defun emmet-css-parse-args (args)
  82. (when args
  83. (let ((rt nil))
  84. (loop
  85. (emmet-pif
  86. (emmet-css-parse-arg args)
  87. (loop for i on it do (push (car i) rt)
  88. while (consp (cdr i))
  89. finally (setq args (cdr i)))
  90. (return (nreverse rt)))))))
  91. (defun emmet-css-split-args (exp)
  92. (emmet-aif
  93. (string-match "\\(?:[ #0-9$]\\|-[0-9]\\)" exp)
  94. (list (substring exp 0 it) (substring exp it))
  95. (list exp nil)))
  96. (defun emmet-css-split-vendor-prefixes (input)
  97. (emmet-parse
  98. "\\(-[wmso]+-\\|-\\|\\)\\(.*\\)" 3 "css vendor prefixes"
  99. (list (elt it 2)
  100. (let ((vp (elt it 1)))
  101. (if (not (string= vp ""))
  102. (if (string= vp "-") 'auto
  103. (string-to-list (subseq vp 1 -1))))))))
  104. (defun emmet-css-subexpr (exp)
  105. (let* ((importantp (emmet-css-important-p exp)))
  106. (destructuring-bind (exp vp)
  107. (emmet-css-split-vendor-prefixes exp)
  108. (destructuring-bind (key args)
  109. (emmet-css-split-args (if importantp (subseq exp 0 -1) exp))
  110. `(,key ,vp
  111. ,importantp
  112. ,@(emmet-css-parse-args args))))))
  113. (defun emmet-css-toknize (str)
  114. (let* ((i (split-string str "+"))
  115. (rt nil))
  116. (loop
  117. (let ((f (first i))
  118. (s (second i)))
  119. (if f
  120. (if (and s (or (string= s "")
  121. (string-match "^\\(?:[ #0-9$]\\|-[0-9]\\)" s)))
  122. (progn
  123. (setf rt (cons (concat f "+" s) rt))
  124. (setf i (cddr i)))
  125. (progn
  126. (setf rt (cons f rt))
  127. (setf i (cdr i))))
  128. (return (nreverse rt)))))))
  129. (defun emmet-css-expr (input)
  130. (mapcar #'emmet-css-subexpr
  131. (emmet-css-toknize input)))
  132. (emmet-defparameter
  133. emmet-css-snippets
  134. (gethash "snippets" (gethash "css" emmet-snippets)))
  135. (emmet-defparameter
  136. emmet-sass-snippets
  137. (gethash "snippets" (gethash "sass" emmet-snippets)))
  138. (emmet-defparameter
  139. emmet-css-unitless-properties
  140. (gethash "unitlessProperties" (gethash "css" emmet-preferences)))
  141. (emmet-defparameter
  142. emmet-css-unitless-properties-regex
  143. (concat "^\\(:?" (emmet-join-string
  144. emmet-css-unitless-properties "\\|")
  145. "\\):.*$"))
  146. (defun emmet-css-instantiate-lambda (str)
  147. (cl-flet ((insert-space-between-name-and-body
  148. (str)
  149. (if (string-match "^\\([a-z-]+:\\)\\(.+\\)$" str)
  150. (emmet-join-string
  151. (mapcar (lambda (ref) (match-string ref str)) '(1 2)) " ")
  152. str))
  153. (split-string-to-body
  154. (str args-sym)
  155. (let ((rt '(concat)) (idx-max 0))
  156. (loop for i from 0 to 255 do
  157. (emmet-aif
  158. (string-match "\\(?:|\\|${\\(?:\\([0-9]\\)\\|\\)\\(?::\\(.+?\\)\\|\\)}\\)" str)
  159. (destructuring-bind (mat idx def)
  160. (mapcar (lambda (ref) (match-string ref str)) '(0 1 2))
  161. (setf rt
  162. `((or
  163. (nth ,(let ((cur-idx (if idx (1- (string-to-number idx)) i)))
  164. (setf idx-max (max cur-idx idx-max)))
  165. ,args-sym)
  166. ,(or def ""))
  167. ,(substring str 0 it) ;; ordered to reverse
  168. ,@rt))
  169. (setf str (substring str (+ it (length mat)))))
  170. ;; don't use nreverse. cause bug in emacs-lisp.
  171. (return (cons idx-max (reverse (cons str rt)))))))))
  172. (let ((args (gensym))
  173. (str (insert-space-between-name-and-body str)))
  174. (destructuring-bind (idx-max . body) (split-string-to-body str args)
  175. (eval
  176. `(lambda (&rest ,args)
  177. (progn
  178. (when (nthcdr ,idx-max ,args)
  179. (setf (nthcdr ,idx-max ,args)
  180. (list (emmet-join-string
  181. (nthcdr ,idx-max ,args) " "))))
  182. ,body)))))))
  183. (emmet-defparameter
  184. emmet-vendor-prefixes-properties
  185. (gethash "vendorPrefixesProperties" (gethash "css" emmet-preferences)))
  186. (emmet-defparameter
  187. emmet-vendor-prefixes-default
  188. (list "webkit" "moz" "ms" "o"))
  189. (defun emmet-css-transform-vendor-prefixes (line vp)
  190. (let ((key (subseq line 0 (or (position ?: line) (length line)))))
  191. (let ((vps (if (eql vp 'auto)
  192. (gethash key
  193. emmet-vendor-prefixes-properties
  194. emmet-vendor-prefixes-default)
  195. (mapcar (lambda (v)
  196. (cond ((= v ?w) "webkit")
  197. ((= v ?m) "moz")
  198. ((= v ?s) "ms")
  199. ((= v ?o) "o")))
  200. vp))))
  201. (emmet-join-string
  202. (append (mapcar (lambda (v) (concat "-" v "-" line)) vps)
  203. (list line))
  204. "\n"))))
  205. (defun emmet-css-transform-exprs (exprs)
  206. (emmet-join-string
  207. (mapcar
  208. #'(lambda (expr)
  209. (let*
  210. ((hash-map (if emmet-use-sass-syntax emmet-sass-snippets emmet-css-snippets))
  211. (basement
  212. (emmet-aif
  213. (or (gethash (car expr) hash-map) (gethash (car expr) emmet-css-snippets))
  214. (let ((set it) (fn nil) (unitlessp nil))
  215. (if (stringp set)
  216. (progn
  217. ;; new pattern
  218. ;; creating print function
  219. (setf fn (emmet-css-instantiate-lambda set))
  220. ;; get unitless or no
  221. (setf unitlessp
  222. (not (null (string-match
  223. emmet-css-unitless-properties-regex set))))
  224. ;; caching
  225. (puthash (car expr) (cons fn unitlessp) hash-map))
  226. (progn
  227. ;; cache hit.
  228. (setf fn (car set))
  229. (setf unitlessp (cdr set))))
  230. (apply fn
  231. (mapcar
  232. #'(lambda (arg)
  233. (if (listp arg)
  234. (if unitlessp (car arg)
  235. (apply #'concat arg))
  236. arg))
  237. (cdddr expr))))
  238. (concat (car expr) ": "
  239. (emmet-join-string
  240. (mapcar #'(lambda (arg)
  241. (if (listp arg) (apply #'concat arg) arg))
  242. (cdddr expr)) " ")
  243. ";"))))
  244. (let ((line
  245. (if (caddr expr)
  246. (concat (subseq basement 0 -1) " !important;")
  247. basement)))
  248. ;; remove trailing semicolon while editing Sass files
  249. (if (and emmet-use-sass-syntax (equal ";" (subseq line -1)))
  250. (setq line (subseq line 0 -1)))
  251. (emmet-aif
  252. (cadr expr)
  253. (emmet-css-transform-vendor-prefixes line it)
  254. line))))
  255. exprs)
  256. "\n"))
  257. (defun emmet-css-transform (input)
  258. (emmet-css-transform-exprs (emmet-css-expr input)))