dotemacs

My Emacs configuration
git clone git://git.entf.net/dotemacs
Log | Files | Refs | LICENSE

company-css.el (19808B)


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