]> code.delx.au - gnu-emacs-elpa/blob - company-css.el
company-css: add a bunch of CSS3 properties
[gnu-emacs-elpa] / company-css.el
1 ;;; company-css.el --- company-mode completion back-end for css-mode -*- lexical-binding: t -*-
2
3 ;; Copyright (C) 2009, 2011, 2014 Free Software Foundation, Inc.
4
5 ;; Author: Nikolaj Schumacher
6
7 ;; This file is part of GNU Emacs.
8
9 ;; GNU Emacs is free software: you can redistribute it and/or modify
10 ;; it under the terms of the GNU General Public License as published by
11 ;; the Free Software Foundation, either version 3 of the License, or
12 ;; (at your option) any later version.
13
14 ;; GNU Emacs is distributed in the hope that it will be useful,
15 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
16 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 ;; GNU General Public License for more details.
18
19 ;; You should have received a copy of the GNU General Public License
20 ;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
21
22 ;;; Commentary:
23
24 ;;; Code:
25
26 (require 'company)
27 (require 'cl-lib)
28
29 (defconst company-css-property-alist
30 ;; see http://www.w3.org/TR/CSS21/propidx.html
31 '(("azimuth" angle "left-side" "far-left" "left" "center-left" "center"
32 "center-right" "right" "far-right" "right-side" "behind" "leftwards"
33 "rightwards")
34 ("background" background-color background-image background-repeat
35 background-attachment background-position
36 background-clip background-origin background-size)
37 ("background-attachment" "scroll" "fixed")
38 ("background-color" color "transparent")
39 ("background-image" uri "none")
40 ("background-position" percentage length "left" "center" "right" percentage
41 length "top" "center" "bottom" "left" "center" "right" "top" "center"
42 "bottom")
43 ("background-repeat" "repeat" "repeat-x" "repeat-y" "no-repeat")
44 ("border" border-width border-style border-color)
45 ("border-bottom" border)
46 ("border-bottom-color" border-color)
47 ("border-bottom-style" border-style)
48 ("border-bottom-width" border-width)
49 ("border-collapse" "collapse" "separate")
50 ("border-color" color "transparent")
51 ("border-left" border)
52 ("border-left-color" border-color)
53 ("border-left-style" border-style)
54 ("border-left-width" border-width)
55 ("border-right" border)
56 ("border-right-color" border-color)
57 ("border-right-style" border-style)
58 ("border-right-width" border-width)
59 ("border-spacing" length length)
60 ("border-style" border-style)
61 ("border-top" border)
62 ("border-top-color" border-color)
63 ("border-top-style" border-style)
64 ("border-top-width" border-width)
65 ("border-width" border-width)
66 ("bottom" length percentage "auto")
67 ("caption-side" "top" "bottom")
68 ("clear" "none" "left" "right" "both")
69 ("clip" shape "auto")
70 ("color" color)
71 ("content" "normal" "none" string uri counter "attr()" "open-quote"
72 "close-quote" "no-open-quote" "no-close-quote")
73 ("counter-increment" identifier integer "none")
74 ("counter-reset" identifier integer "none")
75 ("cue" cue-before cue-after)
76 ("cue-after" uri "none")
77 ("cue-before" uri "none")
78 ("cursor" uri "*" "auto" "crosshair" "default" "pointer" "move" "e-resize"
79 "ne-resize" "nw-resize" "n-resize" "se-resize" "sw-resize" "s-resize"
80 "w-resize" "text" "wait" "help" "progress")
81 ("direction" "ltr" "rtl")
82 ("display" "inline" "block" "list-item" "run-in" "inline-block" "table"
83 "inline-table" "table-row-group" "table-header-group" "table-footer-group"
84 "table-row" "table-column-group" "table-column" "table-cell"
85 "table-caption" "none")
86 ("elevation" angle "below" "level" "above" "higher" "lower")
87 ("empty-cells" "show" "hide")
88 ("float" "left" "right" "none")
89 ("font" font-style font-variant font-weight font-size "/" line-height
90 font-family "caption" "icon" "menu" "message-box" "small-caption"
91 "status-bar")
92 ("font-family" family-name generic-family)
93 ("font-size" absolute-size relative-size length percentage)
94 ("font-style" "normal" "italic" "oblique")
95 ("font-variant" "normal" "small-caps")
96 ("font-weight" "normal" "bold" "bolder" "lighter" "100" "200" "300" "400"
97 "500" "600" "700" "800" "900")
98 ("height" length percentage "auto")
99 ("left" length percentage "auto")
100 ("letter-spacing" "normal" length)
101 ("line-height" "normal" number length percentage)
102 ("list-style" list-style-type list-style-position list-style-image)
103 ("list-style-image" uri "none")
104 ("list-style-position" "inside" "outside")
105 ("list-style-type" "disc" "circle" "square" "decimal" "decimal-leading-zero"
106 "lower-roman" "upper-roman" "lower-greek" "lower-latin" "upper-latin"
107 "armenian" "georgian" "lower-alpha" "upper-alpha" "none")
108 ("margin" margin-width)
109 ("margin-bottom" margin-width)
110 ("margin-left" margin-width)
111 ("margin-right" margin-width)
112 ("margin-top" margin-width)
113 ("max-height" length percentage "none")
114 ("max-width" length percentage "none")
115 ("min-height" length percentage)
116 ("min-width" length percentage)
117 ("orphans" integer)
118 ("outline" outline-color outline-style outline-width)
119 ("outline-color" color "invert")
120 ("outline-style" border-style)
121 ("outline-width" border-width)
122 ("overflow" "visible" "hidden" "scroll" "auto"
123 ;; CSS3:
124 "no-display" "no-content")
125 ("padding" padding-width)
126 ("padding-bottom" padding-width)
127 ("padding-left" padding-width)
128 ("padding-right" padding-width)
129 ("padding-top" padding-width)
130 ("page-break-after" "auto" "always" "avoid" "left" "right")
131 ("page-break-before" "auto" "always" "avoid" "left" "right")
132 ("page-break-inside" "avoid" "auto")
133 ("pause" time percentage)
134 ("pause-after" time percentage)
135 ("pause-before" time percentage)
136 ("pitch" frequency "x-low" "low" "medium" "high" "x-high")
137 ("pitch-range" number)
138 ("play-during" uri "mix" "repeat" "auto" "none")
139 ("position" "static" "relative" "absolute" "fixed")
140 ("quotes" string string "none")
141 ("richness" number)
142 ("right" length percentage "auto")
143 ("speak" "normal" "none" "spell-out")
144 ("speak-header" "once" "always")
145 ("speak-numeral" "digits" "continuous")
146 ("speak-punctuation" "code" "none")
147 ("speech-rate" number "x-slow" "slow" "medium" "fast" "x-fast" "faster"
148 "slower")
149 ("stress" number)
150 ("table-layout" "auto" "fixed")
151 ("text-align" "left" "right" "center" "justify")
152 ("text-decoration" "none" "underline" "overline" "line-through" "blink")
153 ("text-indent" length percentage)
154 ("text-transform" "capitalize" "uppercase" "lowercase" "none")
155 ("top" length percentage "auto")
156 ("unicode-bidi" "normal" "embed" "bidi-override")
157 ("vertical-align" "baseline" "sub" "super" "top" "text-top" "middle"
158 "bottom" "text-bottom" percentage length)
159 ("visibility" "visible" "hidden" "collapse")
160 ("voice-family" specific-voice generic-voice "*" specific-voice
161 generic-voice)
162 ("volume" number percentage "silent" "x-soft" "soft" "medium" "loud"
163 "x-loud")
164 ("white-space" "normal" "pre" "nowrap" "pre-wrap" "pre-line")
165 ("widows" integer)
166 ("width" length percentage "auto")
167 ("word-spacing" "normal" length)
168 ("z-index" "auto" integer)
169 ;; CSS3
170 ("animation" animation-name animation-duration animation-timing-function
171 animation-delay animation-iteration-count animation-direction
172 animation-fill-mode)
173 ("animation-delay" time)
174 ("animation-direction" "normal" "reverse" "alternate" "alternate-reverse")
175 ("animation-duration" time)
176 ("animation-fill-mode" "none" "forwards" "backwards" "both")
177 ("animation-iteration-count" integer "infinite")
178 ("animation-name" "none")
179 ("animation-play-state" "paused" "running")
180 ("animation-timing-function" transition-timing-function
181 "step-start" "step-end" "steps(,)")
182 ("backface-visibility" "visible" "hidden")
183 ("background-clip" background-origin)
184 ("background-origin" "border-box" "padding-box" "content-box")
185 ("background-size" length percentage "auto" "cover" "contain")
186 ("border-image" border-image-outset border-image-repeat border-image-source
187 border-image-slice border-image-width)
188 ("border-image-outset" length)
189 ("border-image-repeat" "stretch" "repeat" "round" "space")
190 ("border-image-source" uri "none")
191 ("border-image-slice" length)
192 ("border-image-width" length percentage)
193 ("border-radius" length)
194 ("border-top-left-radius" length)
195 ("border-top-right-radius" length)
196 ("border-bottom-left-radius" length)
197 ("border-bottom-right-radius" length)
198 ("box-decoration-break" "slice" "clone")
199 ("box-shadow" length color)
200 ("box-sizing" "content-box" "border-box")
201 ("break-after" "auto" "always" "avoid" "left" "right" "page" "column"
202 "avoid-page" "avoid-column")
203 ("break-before" break-after)
204 ("break-inside" "avoid" "auto")
205 ("columns" column-width column-count)
206 ("column-count" integer)
207 ("column-fill" "auto" "balance")
208 ("column-gap" length "normal")
209 ("column-rule" column-rule-width column-rule-style column-rule-color)
210 ("column-rule-color" color)
211 ("column-rule-style" border-style)
212 ("column-rule-width" border-width)
213 ("column-span" "all" "none")
214 ("column-width" length "auto")
215 ("marquee-direction" "forward" "reverse")
216 ("marquee-play-count" integer "infinite")
217 ("marquee-speed" "slow" "normal" "fast")
218 ("marquee-style" "scroll" "slide" "alternate")
219 ("opacity" number)
220 ("overflow-x" overflow)
221 ("overflow-y" overflow)
222 ("overflow-style" "auto" "marquee-line" "marquee-block")
223 ("perspective" "none" length)
224 ("perspective-origin" percentage length "left" "center" "right" "top" "bottom")
225 ("text-overflow" "clip" "ellipsis" "'.'" "','")
226 ("transform" "matrix(,,,,,)" "translate(,)" "translateX()" "translateY()"
227 "scale()" "scaleX()" "scaleY()" "rotate()" "skewX()" "skewY()" "none")
228 ("transform-origin" perspective-origin)
229 ("transform-style" "flat" "preserve-3d")
230 ("transition" transition-property transition-duration
231 transition-timing-function transition-delay)
232 ("transition-delay" time)
233 ("transition-duration" time)
234 ("transition-timing-function"
235 "ease" "linear" "ease-in" "ease-out" "ease-in-out" "cubic-bezier(,,,)")
236 ("transition-property" "none" "all" identifier))
237 "A list of CSS properties and their possible values.")
238
239 (defconst company-css-value-classes
240 '((absolute-size "xx-small" "x-small" "small" "medium" "large" "x-large"
241 "xx-large")
242 (border-style "none" "hidden" "dotted" "dashed" "solid" "double" "groove"
243 "ridge" "inset" "outset")
244 (border-width "thick" "medium" "thin")
245 (color "aqua" "black" "blue" "fuchsia" "gray" "green" "lime" "maroon" "navy"
246 "olive" "orange" "purple" "red" "silver" "teal" "white" "yellow")
247 (counter "counter(,)")
248 (family-name "Courier" "Helvetica" "Times")
249 (generic-family "serif" "sans-serif" "cursive" "fantasy" "monospace")
250 (generic-voice "male" "female" "child")
251 (margin-width "auto") ;; length percentage
252 (relative-size "larger" "smaller")
253 (shape "rect(,,,)")
254 (uri "url()"))
255 "A list of CSS property value classes and their contents.")
256 ;; missing, because not completable
257 ;; <angle><frequency><identifier><integer><length><number><padding-width>
258 ;; <percentage><specific-voice><string><time><uri>
259
260 (defconst company-css-html-tags
261 '("a" "abbr" "acronym" "address" "applet" "area" "b" "base" "basefont" "bdo"
262 "big" "blockquote" "body" "br" "button" "caption" "center" "cite" "code"
263 "col" "colgroup" "dd" "del" "dfn" "dir" "div" "dl" "dt" "em" "fieldset"
264 "font" "form" "frame" "frameset" "h1" "h2" "h3" "h4" "h5" "h6" "head" "hr"
265 "html" "i" "iframe" "img" "input" "ins" "isindex" "kbd" "label" "legend"
266 "li" "link" "map" "menu" "meta" "noframes" "noscript" "object" "ol"
267 "optgroup" "option" "p" "param" "pre" "q" "s" "samp" "script" "select"
268 "small" "span" "strike" "strong" "style" "sub" "sup" "table" "tbody" "td"
269 "textarea" "tfoot" "th" "thead" "title" "tr" "tt" "u" "ul" "var")
270 "A list of HTML tags for use in CSS completion.")
271
272 (defconst company-css-pseudo-classes
273 '("active" "after" "before" "first" "first-child" "first-letter" "first-line"
274 "focus" "hover" "lang" "left" "link" "right" "visited")
275 "Identifiers for CSS pseudo-elements and pseudo-classes.")
276
277 (defconst company-css-property-cache (make-hash-table :size 115 :test 'equal))
278
279 (defun company-css-property-values (attribute)
280 "Access the `company-css-property-alist' cached and flattened."
281 (or (gethash attribute company-css-property-cache)
282 (let (results)
283 (dolist (value (cdr (assoc attribute company-css-property-alist)))
284 (if (symbolp value)
285 (dolist (child (or (cdr (assoc value company-css-value-classes))
286 (company-css-property-values
287 (symbol-name value))))
288 (push child results))
289 (push value results)))
290 (setq results (sort results 'string<))
291 (puthash attribute results company-css-property-cache)
292 results)))
293
294 ;;; bracket detection
295
296 (defconst company-css-braces-syntax-table
297 (let ((table (make-syntax-table)))
298 (setf (aref table ?{) '(4 . 125))
299 (setf (aref table ?}) '(5 . 123))
300 table)
301 "A syntax table giving { and } paren syntax.")
302
303 (defun company-css-inside-braces-p ()
304 "Return non-nil, if point is within matched { and }."
305 (ignore-errors
306 (with-syntax-table company-css-braces-syntax-table
307 (let ((parse-sexp-ignore-comments t))
308 (scan-lists (point) -1 1)))))
309
310 ;;; tags
311 (defconst company-css-tag-regexp
312 (concat "\\(?:\\`\\|}\\)[[:space:]]*"
313 ;; multiple
314 "\\(?:"
315 ;; previous tags:
316 "\\(?:#\\|\\_<[[:alpha:]]\\)[[:alnum:]-#]*\\(?:\\[[^]]*\\]\\)?"
317 ;; space or selectors
318 "\\(?:[[:space:]]+\\|[[:space:]]*[+,>][[:space:]]*\\)"
319 "\\)*"
320 "\\(\\(?:#\\|\\_<[[:alpha:]]\\)\\(?:[[:alnum:]-#]*\\_>\\)?\\_>\\|\\)"
321 "\\=")
322 "A regular expression matching CSS tags.")
323
324 ;;; pseudo id
325 (defconst company-css-pseudo-regexp
326 (concat "\\(?:\\`\\|}\\)[[:space:]]*"
327 ;; multiple
328 "\\(?:"
329 ;; previous tags:
330 "\\(?:#\\|\\_<[[:alpha:]]\\)[[:alnum:]-#]*\\(?:\\[[^]]*\\]\\)?"
331 ;; space or delimiters
332 "\\(?:[[:space:]]+\\|[[:space:]]*[+,>][[:space:]]*\\)"
333 "\\)*"
334 "\\(?:\\(?:\\#\\|\\_<[[:alpha:]]\\)[[:alnum:]-#]*\\):"
335 "\\([[:alpha:]-]+\\_>\\|\\)\\_>\\=")
336 "A regular expression matching CSS pseudo classes.")
337
338 ;;; properties
339
340 (defun company-css-grab-property ()
341 "Return the CSS property before point, if any.
342 Returns \"\" if no property found, but feasible at this position."
343 (when (company-css-inside-braces-p)
344 (company-grab-symbol)))
345
346 ;;; values
347 (defconst company-css-property-value-regexp
348 "\\_<\\([[:alpha:]-]+\\):\\(?:[^};]*[[:space:]]+\\)?\\([^};]*\\_>\\|\\)\\="
349 "A regular expression matching CSS tags.")
350
351 ;;;###autoload
352 (defun company-css (command &optional arg &rest ignored)
353 "`company-mode' completion back-end for `css-mode'."
354 (interactive (list 'interactive))
355 (cl-case command
356 (interactive (company-begin-backend 'company-css))
357 (prefix (and (derived-mode-p 'css-mode)
358 (or (company-grab company-css-tag-regexp 1)
359 (company-grab company-css-pseudo-regexp 1)
360 (company-grab company-css-property-value-regexp 2)
361 (company-css-grab-property))))
362 (candidates
363 (cond
364 ((company-grab company-css-tag-regexp 1)
365 (all-completions arg company-css-html-tags))
366 ((company-grab company-css-pseudo-regexp 1)
367 (all-completions arg company-css-pseudo-classes))
368 ((company-grab company-css-property-value-regexp 2)
369 (all-completions arg
370 (company-css-property-values
371 (company-grab company-css-property-value-regexp 1))))
372 ((company-css-grab-property)
373 (all-completions arg company-css-property-alist))))
374 (sorted t)))
375
376 (provide 'company-css)
377 ;;; company-css.el ends here