]> code.delx.au - gnu-emacs-elpa/blob - company-css.el
New transformer: company-sort-prefer-same-case-prefix
[gnu-emacs-elpa] / company-css.el
1 ;;; company-css.el --- company-mode completion backend 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 (declare-function web-mode-language-at-pos "web-mode" (&optional pos))
30
31 (defconst company-css-property-alist
32 ;; see http://www.w3.org/TR/CSS21/propidx.html
33 '(("azimuth" angle "left-side" "far-left" "left" "center-left" "center"
34 "center-right" "right" "far-right" "right-side" "behind" "leftwards"
35 "rightwards")
36 ("background" background-color background-image background-repeat
37 background-attachment background-position
38 background-clip background-origin background-size)
39 ("background-attachment" "scroll" "fixed")
40 ("background-color" color "transparent")
41 ("background-image" uri "none")
42 ("background-position" percentage length "left" "center" "right" percentage
43 length "top" "center" "bottom" "left" "center" "right" "top" "center"
44 "bottom")
45 ("background-repeat" "repeat" "repeat-x" "repeat-y" "no-repeat")
46 ("border" border-width border-style border-color)
47 ("border-bottom" border)
48 ("border-bottom-color" border-color)
49 ("border-bottom-style" border-style)
50 ("border-bottom-width" border-width)
51 ("border-collapse" "collapse" "separate")
52 ("border-color" color "transparent")
53 ("border-left" border)
54 ("border-left-color" border-color)
55 ("border-left-style" border-style)
56 ("border-left-width" border-width)
57 ("border-right" border)
58 ("border-right-color" border-color)
59 ("border-right-style" border-style)
60 ("border-right-width" border-width)
61 ("border-spacing" length length)
62 ("border-style" border-style)
63 ("border-top" border)
64 ("border-top-color" border-color)
65 ("border-top-style" border-style)
66 ("border-top-width" border-width)
67 ("border-width" border-width)
68 ("bottom" length percentage "auto")
69 ("caption-side" "top" "bottom")
70 ("clear" "none" "left" "right" "both")
71 ("clip" shape "auto")
72 ("color" color)
73 ("content" "normal" "none" string uri counter "attr()" "open-quote"
74 "close-quote" "no-open-quote" "no-close-quote")
75 ("counter-increment" identifier integer "none")
76 ("counter-reset" identifier integer "none")
77 ("cue" cue-before cue-after)
78 ("cue-after" uri "none")
79 ("cue-before" uri "none")
80 ("cursor" uri "*" "auto" "crosshair" "default" "pointer" "move" "e-resize"
81 "ne-resize" "nw-resize" "n-resize" "se-resize" "sw-resize" "s-resize"
82 "w-resize" "text" "wait" "help" "progress")
83 ("direction" "ltr" "rtl")
84 ("display" "inline" "block" "list-item" "run-in" "inline-block" "table"
85 "inline-table" "table-row-group" "table-header-group" "table-footer-group"
86 "table-row" "table-column-group" "table-column" "table-cell"
87 "table-caption" "none")
88 ("elevation" angle "below" "level" "above" "higher" "lower")
89 ("empty-cells" "show" "hide")
90 ("float" "left" "right" "none")
91 ("font" font-style font-weight font-size "/" line-height
92 font-family "caption" "icon" "menu" "message-box" "small-caption"
93 "status-bar" "normal" "small-caps"
94 ;; CSS3
95 font-stretch)
96 ("font-family" family-name generic-family)
97 ("font-size" absolute-size relative-size length percentage)
98 ("font-style" "normal" "italic" "oblique")
99 ("font-weight" "normal" "bold" "bolder" "lighter" "100" "200" "300" "400"
100 "500" "600" "700" "800" "900")
101 ("height" length percentage "auto")
102 ("left" length percentage "auto")
103 ("letter-spacing" "normal" length)
104 ("line-height" "normal" number length percentage)
105 ("list-style" list-style-type list-style-position list-style-image)
106 ("list-style-image" uri "none")
107 ("list-style-position" "inside" "outside")
108 ("list-style-type" "disc" "circle" "square" "decimal" "decimal-leading-zero"
109 "lower-roman" "upper-roman" "lower-greek" "lower-latin" "upper-latin"
110 "armenian" "georgian" "lower-alpha" "upper-alpha" "none")
111 ("margin" margin-width)
112 ("margin-bottom" margin-width)
113 ("margin-left" margin-width)
114 ("margin-right" margin-width)
115 ("margin-top" margin-width)
116 ("max-height" length percentage "none")
117 ("max-width" length percentage "none")
118 ("min-height" length percentage)
119 ("min-width" length percentage)
120 ("orphans" integer)
121 ("outline" outline-color outline-style outline-width)
122 ("outline-color" color "invert")
123 ("outline-style" border-style)
124 ("outline-width" border-width)
125 ("overflow" "visible" "hidden" "scroll" "auto"
126 ;; CSS3:
127 "no-display" "no-content")
128 ("padding" padding-width)
129 ("padding-bottom" padding-width)
130 ("padding-left" padding-width)
131 ("padding-right" padding-width)
132 ("padding-top" padding-width)
133 ("page-break-after" "auto" "always" "avoid" "left" "right")
134 ("page-break-before" "auto" "always" "avoid" "left" "right")
135 ("page-break-inside" "avoid" "auto")
136 ("pause" time percentage)
137 ("pause-after" time percentage)
138 ("pause-before" time percentage)
139 ("pitch" frequency "x-low" "low" "medium" "high" "x-high")
140 ("pitch-range" number)
141 ("play-during" uri "mix" "repeat" "auto" "none")
142 ("position" "static" "relative" "absolute" "fixed")
143 ("quotes" string string "none")
144 ("richness" number)
145 ("right" length percentage "auto")
146 ("speak" "normal" "none" "spell-out")
147 ("speak-header" "once" "always")
148 ("speak-numeral" "digits" "continuous")
149 ("speak-punctuation" "code" "none")
150 ("speech-rate" number "x-slow" "slow" "medium" "fast" "x-fast" "faster"
151 "slower")
152 ("stress" number)
153 ("table-layout" "auto" "fixed")
154 ("text-align" "left" "right" "center" "justify")
155 ("text-indent" length percentage)
156 ("text-transform" "capitalize" "uppercase" "lowercase" "none")
157 ("top" length percentage "auto")
158 ("unicode-bidi" "normal" "embed" "bidi-override")
159 ("vertical-align" "baseline" "sub" "super" "top" "text-top" "middle"
160 "bottom" "text-bottom" percentage length)
161 ("visibility" "visible" "hidden" "collapse")
162 ("voice-family" specific-voice generic-voice "*" specific-voice
163 generic-voice)
164 ("volume" number percentage "silent" "x-soft" "soft" "medium" "loud"
165 "x-loud")
166 ("white-space" "normal" "pre" "nowrap" "pre-wrap" "pre-line")
167 ("widows" integer)
168 ("width" length percentage "auto")
169 ("word-spacing" "normal" length)
170 ("z-index" "auto" integer)
171 ;; CSS3
172 ("align-content" align-stretch "space-between" "space-around")
173 ("align-items" align-stretch "baseline")
174 ("align-self" align-items "auto")
175 ("animation" animation-name animation-duration animation-timing-function
176 animation-delay animation-iteration-count animation-direction
177 animation-fill-mode)
178 ("animation-delay" time)
179 ("animation-direction" "normal" "reverse" "alternate" "alternate-reverse")
180 ("animation-duration" time)
181 ("animation-fill-mode" "none" "forwards" "backwards" "both")
182 ("animation-iteration-count" integer "infinite")
183 ("animation-name" "none")
184 ("animation-play-state" "paused" "running")
185 ("animation-timing-function" transition-timing-function
186 "step-start" "step-end" "steps(,)")
187 ("backface-visibility" "visible" "hidden")
188 ("background-clip" background-origin)
189 ("background-origin" "border-box" "padding-box" "content-box")
190 ("background-size" length percentage "auto" "cover" "contain")
191 ("border-image" border-image-outset border-image-repeat border-image-source
192 border-image-slice border-image-width)
193 ("border-image-outset" length)
194 ("border-image-repeat" "stretch" "repeat" "round" "space")
195 ("border-image-source" uri "none")
196 ("border-image-slice" length)
197 ("border-image-width" length percentage)
198 ("border-radius" length)
199 ("border-top-left-radius" length)
200 ("border-top-right-radius" length)
201 ("border-bottom-left-radius" length)
202 ("border-bottom-right-radius" length)
203 ("box-decoration-break" "slice" "clone")
204 ("box-shadow" length color)
205 ("box-sizing" "content-box" "border-box")
206 ("break-after" "auto" "always" "avoid" "left" "right" "page" "column"
207 "avoid-page" "avoid-column")
208 ("break-before" break-after)
209 ("break-inside" "avoid" "auto")
210 ("columns" column-width column-count)
211 ("column-count" integer)
212 ("column-fill" "auto" "balance")
213 ("column-gap" length "normal")
214 ("column-rule" column-rule-width column-rule-style column-rule-color)
215 ("column-rule-color" color)
216 ("column-rule-style" border-style)
217 ("column-rule-width" border-width)
218 ("column-span" "all" "none")
219 ("column-width" length "auto")
220 ("filter" url "blur()" "brightness()" "contrast()" "drop-shadow()"
221 "grayscale()" "hue-rotate()" "invert()" "opacity()" "saturate()" "sepia()")
222 ("flex" flex-grow flex-shrink flex-basis)
223 ("flex-basis" percentage length "auto")
224 ("flex-direction" "row" "row-reverse" "column" "column-reverse")
225 ("flex-flow" flex-direction flex-wrap)
226 ("flex-grow" number)
227 ("flex-shrink" number)
228 ("flex-wrap" "nowrap" "wrap" "wrap-reverse")
229 ("font-feature-setting" normal string number)
230 ("font-kerning" "auto" "normal" "none")
231 ("font-language-override" "normal" string)
232 ("font-size-adjust" "none" number)
233 ("font-stretch" "normal" "ultra-condensed" "extra-condensed" "condensed"
234 "semi-condensed" "semi-expanded" "expanded" "extra-expanded" "ultra-expanded")
235 ("font-synthesis" "none" "weight" "style")
236 ("font-variant" font-variant-alternates font-variant-caps
237 font-variant-east-asian font-variant-ligatures font-variant-numeric
238 font-variant-position)
239 ("font-variant-alternates" "normal" "historical-forms" "stylistic()"
240 "styleset()" "character-variant()" "swash()" "ornaments()" "annotation()")
241 ("font-variant-caps" "normal" "small-caps" "all-small-caps" "petite-caps"
242 "all-petite-caps" "unicase" "titling-caps")
243 ("font-variant-east-asian" "jis78" "jis83" "jis90" "jis04" "simplified"
244 "traditional" "full-width" "proportional-width" "ruby")
245 ("font-variant-ligatures" "normal" "none" "common-ligatures"
246 "no-common-ligatures" "discretionary-ligatures" "no-discretionary-ligatures"
247 "historical-ligatures" "no-historical-ligatures" "contextual" "no-contextual")
248 ("font-variant-numeric" "normal" "ordinal" "slashed-zero"
249 "lining-nums" "oldstyle-nums" "proportional-nums" "tabular-nums"
250 "diagonal-fractions" "stacked-fractions")
251 ("font-variant-position" "normal" "sub" "super")
252 ("hyphens" "none" "manual" "auto")
253 ("justify-content" align-common "space-between" "space-around")
254 ("line-break" "auto" "loose" "normal" "strict")
255 ("marquee-direction" "forward" "reverse")
256 ("marquee-play-count" integer "infinite")
257 ("marquee-speed" "slow" "normal" "fast")
258 ("marquee-style" "scroll" "slide" "alternate")
259 ("opacity" number)
260 ("order" number)
261 ("outline-offset" length)
262 ("overflow-x" overflow)
263 ("overflow-y" overflow)
264 ("overflow-style" "auto" "marquee-line" "marquee-block")
265 ("overflow-wrap" "normal" "break-word")
266 ("perspective" "none" length)
267 ("perspective-origin" percentage length "left" "center" "right" "top" "bottom")
268 ("resize" "none" "both" "horizontal" "vertical")
269 ("tab-size" integer length)
270 ("text-align-last" "auto" "start" "end" "left" "right" "center" "justify")
271 ("text-decoration" text-decoration-color text-decoration-line text-decoration-style)
272 ("text-decoration-color" color)
273 ("text-decoration-line" "none" "underline" "overline" "line-through" "blink")
274 ("text-decoration-style" "solid" "double" "dotted" "dashed" "wavy")
275 ("text-overflow" "clip" "ellipsis")
276 ("text-shadow" color length)
277 ("text-underline-position" "auto" "under" "left" "right")
278 ("transform" "matrix(,,,,,)" "translate(,)" "translateX()" "translateY()"
279 "scale()" "scaleX()" "scaleY()" "rotate()" "skewX()" "skewY()" "none")
280 ("transform-origin" perspective-origin)
281 ("transform-style" "flat" "preserve-3d")
282 ("transition" transition-property transition-duration
283 transition-timing-function transition-delay)
284 ("transition-delay" time)
285 ("transition-duration" time)
286 ("transition-timing-function"
287 "ease" "linear" "ease-in" "ease-out" "ease-in-out" "cubic-bezier(,,,)")
288 ("transition-property" "none" "all" identifier)
289 ("word-wrap" overflow-wrap)
290 ("word-break" "normal" "break-all" "keep-all"))
291 "A list of CSS properties and their possible values.")
292
293 (defconst company-css-value-classes
294 '((absolute-size "xx-small" "x-small" "small" "medium" "large" "x-large"
295 "xx-large")
296 (align-common "flex-start" "flex-end" "center")
297 (align-stretch align-common "stretch")
298 (border-style "none" "hidden" "dotted" "dashed" "solid" "double" "groove"
299 "ridge" "inset" "outset")
300 (border-width "thick" "medium" "thin")
301 (color "aqua" "black" "blue" "fuchsia" "gray" "green" "lime" "maroon" "navy"
302 "olive" "orange" "purple" "red" "silver" "teal" "white" "yellow")
303 (counter "counter(,)")
304 (family-name "Courier" "Helvetica" "Times")
305 (generic-family "serif" "sans-serif" "cursive" "fantasy" "monospace")
306 (generic-voice "male" "female" "child")
307 (margin-width "auto") ;; length percentage
308 (relative-size "larger" "smaller")
309 (shape "rect(,,,)")
310 (uri "url()"))
311 "A list of CSS property value classes and their contents.")
312 ;; missing, because not completable
313 ;; <angle><frequency><identifier><integer><length><number><padding-width>
314 ;; <percentage><specific-voice><string><time><uri>
315
316 (defconst company-css-html-tags
317 '("a" "abbr" "acronym" "address" "applet" "area" "b" "base" "basefont" "bdo"
318 "big" "blockquote" "body" "br" "button" "caption" "center" "cite" "code"
319 "col" "colgroup" "dd" "del" "dfn" "dir" "div" "dl" "dt" "em" "fieldset"
320 "font" "form" "frame" "frameset" "h1" "h2" "h3" "h4" "h5" "h6" "head" "hr"
321 "html" "i" "iframe" "img" "input" "ins" "isindex" "kbd" "label" "legend"
322 "li" "link" "map" "menu" "meta" "noframes" "noscript" "object" "ol"
323 "optgroup" "option" "p" "param" "pre" "q" "s" "samp" "script" "select"
324 "small" "span" "strike" "strong" "style" "sub" "sup" "table" "tbody" "td"
325 "textarea" "tfoot" "th" "thead" "title" "tr" "tt" "u" "ul" "var"
326 ;; HTML5
327 "section" "article" "aside" "header" "footer" "nav" "figure" "figcaption"
328 "time" "mark" "main")
329 "A list of HTML tags for use in CSS completion.")
330
331 (defconst company-css-pseudo-classes
332 '("active" "after" "before" "first" "first-child" "first-letter" "first-line"
333 "focus" "hover" "lang" "left" "link" "right" "visited")
334 "Identifiers for CSS pseudo-elements and pseudo-classes.")
335
336 (defconst company-css-property-cache (make-hash-table :size 115 :test 'equal))
337
338 (defun company-css-property-values (attribute)
339 "Access the `company-css-property-alist' cached and flattened."
340 (or (gethash attribute company-css-property-cache)
341 (let (results)
342 (dolist (value (cdr (assoc attribute company-css-property-alist)))
343 (if (symbolp value)
344 (dolist (child (or (cdr (assoc value company-css-value-classes))
345 (company-css-property-values
346 (symbol-name value))))
347 (push child results))
348 (push value results)))
349 (setq results (sort results 'string<))
350 (puthash attribute
351 (if (fboundp 'delete-consecutive-dups)
352 (delete-consecutive-dups results)
353 (delete-dups results))
354 company-css-property-cache)
355 results)))
356
357 ;;; bracket detection
358
359 (defconst company-css-braces-syntax-table
360 (let ((table (make-syntax-table)))
361 (setf (aref table ?{) '(4 . 125))
362 (setf (aref table ?}) '(5 . 123))
363 table)
364 "A syntax table giving { and } paren syntax.")
365
366 (defun company-css-inside-braces-p ()
367 "Return non-nil, if point is within matched { and }."
368 (ignore-errors
369 (with-syntax-table company-css-braces-syntax-table
370 (let ((parse-sexp-ignore-comments t))
371 (scan-lists (point) -1 1)))))
372
373 ;;; tags
374 (defconst company-css-tag-regexp
375 (concat "\\(?:\\`\\|}\\)[[:space:]]*"
376 ;; multiple
377 "\\(?:"
378 ;; previous tags:
379 "\\(?:#\\|\\_<[[:alpha:]]\\)[[:alnum:]-#]*\\(?:\\[[^]]*\\]\\)?"
380 ;; space or selectors
381 "\\(?:[[:space:]]+\\|[[:space:]]*[+,>][[:space:]]*\\)"
382 "\\)*"
383 "\\(\\(?:#\\|\\_<[[:alpha:]]\\)\\(?:[[:alnum:]-#]*\\_>\\)?\\_>\\|\\)"
384 "\\=")
385 "A regular expression matching CSS tags.")
386
387 ;;; pseudo id
388 (defconst company-css-pseudo-regexp
389 (concat "\\(?:\\`\\|}\\)[[:space:]]*"
390 ;; multiple
391 "\\(?:"
392 ;; previous tags:
393 "\\(?:#\\|\\_<[[:alpha:]]\\)[[:alnum:]-#]*\\(?:\\[[^]]*\\]\\)?"
394 ;; space or delimiters
395 "\\(?:[[:space:]]+\\|[[:space:]]*[+,>][[:space:]]*\\)"
396 "\\)*"
397 "\\(?:\\(?:\\#\\|\\_<[[:alpha:]]\\)[[:alnum:]-#]*\\):"
398 "\\([[:alpha:]-]+\\_>\\|\\)\\_>\\=")
399 "A regular expression matching CSS pseudo classes.")
400
401 ;;; properties
402
403 (defun company-css-grab-property ()
404 "Return the CSS property before point, if any.
405 Returns \"\" if no property found, but feasible at this position."
406 (when (company-css-inside-braces-p)
407 (company-grab-symbol)))
408
409 ;;; values
410 (defconst company-css-property-value-regexp
411 "\\_<\\([[:alpha:]-]+\\):\\(?:[^{};]*[[:space:]]+\\)?\\([^{};]*\\_>\\|\\)\\="
412 "A regular expression matching CSS tags.")
413
414 ;;;###autoload
415 (defun company-css (command &optional arg &rest ignored)
416 "`company-mode' completion backend for `css-mode'."
417 (interactive (list 'interactive))
418 (cl-case command
419 (interactive (company-begin-backend 'company-css))
420 (prefix (and (or (derived-mode-p 'css-mode)
421 (and (derived-mode-p 'web-mode)
422 (string= (web-mode-language-at-pos) "css")))
423 (or (company-grab company-css-tag-regexp 1)
424 (company-grab company-css-pseudo-regexp 1)
425 (company-grab company-css-property-value-regexp 2)
426 (company-css-grab-property))))
427 (candidates
428 (cond
429 ((company-grab company-css-tag-regexp 1)
430 (all-completions arg company-css-html-tags))
431 ((company-grab company-css-pseudo-regexp 1)
432 (all-completions arg company-css-pseudo-classes))
433 ((company-grab company-css-property-value-regexp 2)
434 (all-completions arg
435 (company-css-property-values
436 (company-grab company-css-property-value-regexp 1))))
437 ((company-css-grab-property)
438 (all-completions arg company-css-property-alist))))
439 (sorted t)))
440
441 (provide 'company-css)
442 ;;; company-css.el ends here