lectures.alex.balgavy.eu

Lecture notes from university.
git clone git://git.alex.balgavy.eu/lectures.alex.balgavy.eu.git
Log | Files | Refs | Submodules

css_completions.py (32774B)


      1 import re
      2 import sublime
      3 import sublime_plugin
      4 import timeit
      5 
      6 from functools import cached_property, wraps
      7 
      8 __all__ = ['CSSCompletions']
      9 
     10 KIND_CSS_PROPERTY = (sublime.KIND_ID_KEYWORD, "p", "property")
     11 KIND_CSS_FUNCTION = (sublime.KIND_ID_FUNCTION, "f", "function")
     12 KIND_CSS_CONSTANT = (sublime.KIND_ID_VARIABLE, "c", "constant")
     13 
     14 ENABLE_TIMING = False
     15 
     16 
     17 def timing(func):
     18     @wraps(func)
     19     def wrap(*args, **kw):
     20         if ENABLE_TIMING:
     21             ts = timeit.default_timer()
     22         result = func(*args, **kw)
     23         if ENABLE_TIMING:
     24             te = timeit.default_timer()
     25             print(f"{func.__name__}({args}, {kw}) took: {1000.0 * (te - ts):2.3f} ms")
     26         return result
     27     return wrap
     28 
     29 
     30 def get_properties():
     31     """
     32     Gets the properties.
     33 
     34     Prepare some common property values for when there is more than one way to
     35     specify a certain value type. The color value for example can be specified
     36     by `rgb()` or `hsl()` and so on. Example where `|` denotes the caret:
     37 
     38         color: rg|   -->   color: rgb(|);
     39 
     40     This is also helpful when multiple properties share the same value types.
     41     """
     42     common_values = {
     43         'animation_direction': [
     44             'alternate', 'alternate-reverse', 'normal', 'reverse'
     45         ],
     46         'absolute_size': [
     47             'xx-small', 'x-small', 'small', 'medium', 'large', 'x-large', 'xx-large'
     48         ],
     49         'absolute_weight': [
     50             '100', '200', '300', '400', '500', '600', '700', '800', '900',
     51             'normal', 'bold'
     52         ],
     53         'basic_shape': [
     54             ['circle()', 'circle($1)'],
     55             ['ellipse()', 'ellipse($1)'],
     56             ['inset()', 'inset($1)'],
     57             ['polygon()', 'polygon($1)']
     58         ],
     59         'blend_mode': [
     60             'normal', 'multiply', 'screen', 'overlay', 'darken', 'lighten',
     61             'color-dodge', 'color-burn', 'hard-light', 'soft-light', 'difference',
     62             'exclusion', 'hue', 'saturation', 'color', 'luminosity'
     63         ],
     64         'border_style': [
     65             'none', 'hidden', 'dotted', 'dashed', 'solid', 'double',
     66             'groove', 'ridge', 'inset', 'outset'
     67         ],
     68         'border_width': ['thin', 'medium', 'thick'],
     69         'break_before_after': [
     70             'always', 'left', 'right', 'recto', 'verso', 'page', 'column', 'region'
     71         ],
     72         'break_inside': [
     73             'auto', 'avoid', 'avoid-page', 'avoid-column', 'avoid-region'
     74         ],
     75         'color': [
     76             'currentColor',
     77             'transparent',
     78             ['rgb()', 'rgb(${1:0}, ${2:0}, ${3:0})'],
     79             ['rgba()', 'rgba(${1:0}, ${2:0}, ${3:0}, ${4:1.0})'],
     80             ['hsl()', 'hsl(${1:0}, ${2:100%}, ${3:50%})'],
     81             ['hsla()', 'hsla(${1:0}, ${2:100%}, ${3:50%}, ${4:1.0})'],
     82             # Named colors
     83             'aliceblue', 'antiquewhite', 'aqua', 'aquamarine', 'azure', 'beige',
     84             'bisque', 'black', 'blanchedalmond', 'blue', 'blueviolet', 'brown',
     85             'burlywood', 'cadetblue', 'chartreuse', 'chocolate', 'coral',
     86             'cornflowerblue', 'cornsilk', 'crimson', 'cyan', 'darkblue',
     87             'darkcyan', 'darkgoldenrod', 'darkgray', 'darkgrey', 'darkgreen',
     88             'darkkhaki', 'darkmagenta', 'darkolivegreen', 'darkorange',
     89             'darkorchid', 'darkred', 'darksalmon', 'darkseagreen',
     90             'darkslateblue', 'darkslategray', 'darkslategrey', 'darkturquoise',
     91             'darkviolet', 'deeppink', 'deepskyblue', 'dimgray', 'dimgrey',
     92             'dodgerblue', 'firebrick', 'floralwhite', 'forestgreen', 'fuchsia',
     93             'gainsboro', 'ghostwhite', 'gold', 'goldenrod', 'gray', 'grey',
     94             'green', 'greenyellow', 'honeydew', 'hotpink', 'indianred', 'indigo',
     95             'ivory', 'khaki', 'lavender', 'lavenderblush', 'lawngreen',
     96             'lemonchiffon', 'lightblue', 'lightcoral', 'lightcyan',
     97             'lightgoldenrodyellow', 'lightgray', 'lightgrey', 'lightgreen',
     98             'lightpink', 'lightsalmon', 'lightseagreen', 'lightskyblue',
     99             'lightslategray', 'lightslategrey', 'lightsteelblue', 'lightyellow',
    100             'lime', 'limegreen', 'linen', 'magenta', 'maroon', 'mediumaquamarine',
    101             'mediumblue', 'mediumorchid', 'mediumpurple', 'mediumseagreen',
    102             'mediumslateblue', 'mediumspringgreen', 'mediumturquoise',
    103             'mediumvioletred', 'midnightblue', 'mintcream', 'mistyrose',
    104             'moccasin', 'navajowhite', 'navy', 'oldlace', 'olive', 'olivedrab',
    105             'orange', 'orangered', 'orchid', 'palegoldenrod', 'palegreen',
    106             'paleturquoise', 'palevioletred', 'papayawhip', 'peachpuff', 'peru',
    107             'pink', 'plum', 'powderblue', 'purple', 'rebeccapurple', 'red',
    108             'rosybrown', 'royalblue', 'saddlebrown', 'salmon', 'sandybrown',
    109             'seagreen', 'seashell', 'sienna', 'silver', 'skyblue', 'slateblue',
    110             'slategray', 'slategrey', 'snow', 'springgreen', 'steelblue', 'tan',
    111             'teal', 'thistle', 'tomato', 'turquoise', 'violet', 'wheat', 'white',
    112             'whitesmoke', 'yellow', 'yellowgreen'
    113         ],
    114         'counter_symbols': [
    115             'cyclic', 'numeric', 'alphabetic', 'symbolic', 'additive', 'fixed'
    116         ],
    117         'font_variant_alternates': [
    118             'normal', 'historical-forms',
    119             ['stylistic()', 'stylistic($1)'],
    120             ['styleset()', 'styleset($1)'],
    121             ['character-variant()', 'character-variant($1)'],
    122             ['swash()', 'swash($1)'],
    123             ['ornaments()', 'ornaments($1)'],
    124             ['annotation()', 'annotation($1)']
    125         ],
    126         'generic_name': [
    127             'serif', 'sans-serif', 'cursive', 'fantasy', 'monospace'
    128         ],
    129         'gradient': [
    130             ['conic-gradient()', 'conic-gradient($1)'],
    131             ['linear-gradient()', 'linear-gradient($1)'],
    132             ['radial-gradient()', 'radial-gradient($1)'],
    133             ['repeating-conic-gradient()', 'repeating-conic-gradient($1)'],
    134             ['repeating-linear-gradient()', 'repeating-linear-gradient($1)'],
    135             ['repeating-radial-gradient()', 'repeating-radial-gradient($1)']
    136         ],
    137         'grid': [
    138             ['repeat()', 'repeat(${1:2}, ${2:1fr})'],
    139             ['minmax()', 'minmax(${1:100px}, ${2:1fr})'],
    140         ],
    141         'list_style_type': [
    142             'none', 'inline', 'disc', 'circle', 'square', 'decimal',
    143             'decimal-leading-zero', 'arabic-indic', 'binary', 'bengali',
    144             'cambodian', 'khmer', 'devanagari', 'gujarati', 'gurmukhi',
    145             'kannada', 'lower-hexadecimal', 'lao', 'malayalam', 'mongolian',
    146             'myanmar', 'octal', 'oriya', 'persian', 'urdu', 'telugu',
    147             'tibetan', 'thai', 'upper-hexadecimal', 'lower-roman',
    148             'upper-roman', 'lower-greek', 'lower-alpha', 'lower-latin',
    149             'upper-alpha', 'upper-latin', 'afar', 'ethiopic-halehame-aa-et',
    150             'ethiopic-halehame-aa-er', 'amharic', 'ethiopic-halehame-am-et',
    151             'amharic-abegede', 'ethiopic-abegede-am-et', 'cjk-earthly-branch',
    152             'cjk-heavenly-stem', 'ethiopic', 'ethiopic-halehame-gez',
    153             'ethiopic-abegede', 'ethiopic-abegede-gez', 'hangul-consonant',
    154             'hangul', 'lower-norwegian', 'oromo', 'ethiopic-halehame-om-et',
    155             'sidama', 'ethiopic-halehame-sid-et', 'somali',
    156             'ethiopic-halehame-so-et', 'tigre', 'ethiopic-halehame-tig',
    157             'tigrinya-er', 'ethiopic-halehame-ti-er', 'tigrinya-er-abegede',
    158             'ethiopic-abegede-ti-er', 'tigrinya-et', 'ethiopic-halehame-ti-et',
    159             'tigrinya-et-abegede', 'ethiopic-abegede-ti-et', 'upper-greek',
    160             'upper-norwegian', 'asterisks', 'footnotes', 'hebrew', 'armenian',
    161             'lower-armenian', 'upper-armenian', 'georgian', 'cjk-ideographic',
    162             'hiragana', 'katakana', 'hiragana-iroha', 'katakana-iroha'
    163         ],
    164         'position': ['top', 'right', 'bottom', 'left', 'center'],
    165         'relative_size': ['larger', 'smaller'],
    166         'relative_weight': ['bolder', 'lighter'],
    167         'repeat_style': [
    168             'repeat', 'repeat-x', 'repeat-y', 'space', 'round', 'no-repeat'
    169         ],
    170         'string': ['\"$1\"'],
    171         'timing_function': [
    172             'linear',
    173             'ease', 'ease-in', 'ease-out', 'ease-in-out', 'step-start', 'step-end',
    174             ['cubic-bezier()', 'cubic-bezier(${1:0.0}, ${2:0.0}, ${3:1.0}, ${4:1.0})'],
    175             ['steps()', 'steps(${1:2}, ${2:start})'],
    176         ],
    177         'uri': [['url()', 'url($1)']],
    178     }
    179 
    180     properties_dict = {
    181         'align-content': [
    182             'center', 'flex-end', 'flex-start', 'space-around', 'space-between',
    183             'stretch'
    184         ],
    185         'align-items': [
    186             'baseline', 'center', 'flex-end', 'flex-start', 'stretch'
    187         ],
    188         'align-self': [
    189             'auto', 'baseline', 'center', 'flex-end', 'flex-start', 'stretch'
    190         ],
    191         'alignment-baseline': [
    192             'baseline', 'middle', 'auto', 'before-edge', 'after-edge', 'central',
    193             'text-before-edge', 'text-after-edge', 'ideographic', 'alphabetic',
    194             'hanging', 'mathematical'
    195         ],
    196         'animation': [
    197             'none', '<timing_function>', 'infinite', '<animation_direction>',
    198             'forwards', 'backwards', 'both', 'running', 'paused'
    199         ],
    200         'animation-name': ['none', '<custom-ident>'],
    201         'animation-duration': ['<time>'],
    202         'animation-timing-function': ['<timing_function>'],
    203         'animation-delay': ['<time>'],
    204         'animation-iteration-count': ['infinite', '<number>'],
    205         'animation-direction': ['<animation_direction>'],
    206         'animation-fill-mode': ['none', 'forwards', 'backwards', 'both'],
    207         'animation-play-state': ['running', 'paused'],
    208         'backface-visibility': ['visible', 'hidden'],
    209         'background': [
    210             '<color>', '<gradient>', '<position>', '<uri>',
    211             'repeat', 'repeat-x', 'repeat-y', 'no-repeat', 'scroll', 'fixed'
    212         ],
    213         'background-attachment': ['fixed', 'local', 'scroll'],
    214         'background-blend-mode': ['<blend_mode>'],
    215         'background-clip': ['border-box', 'padding-box', 'content-box'],
    216         'background-color': ['<color>'],
    217         'background-image': ['<uri>', 'none'],
    218         'background-origin': ['border-box', 'padding-box', 'content-box'],
    219         'background-position': ['<position>'],
    220         'background-repeat': ['<repeat_style>'],
    221         'background-size': [
    222             'auto', 'cover', 'contain', '<length>', '<percentage>'
    223         ],
    224         'baseline-shift': ['baseline', 'sub', 'super'],
    225         'border': ['<border_width>', '<border_style>', '<color>'],
    226         'border-width': ['<border_width>'],
    227         'border-style': ['<border_style>'],
    228         'border-color': ['<color>'],
    229         'border-collapse': ['collapse', 'separate'],
    230         'border-radius': ['<length>', '<percentage>'],
    231         'border-spacing': ['<length>'],
    232         'border-image': [
    233             '<border-image-source>', '<border-image-slice>',
    234             '<border-image-width>', '<border-image-outset>',
    235             '<border-image-repeat>'
    236         ],
    237         'border-image-outset': ['<length>', '<number>'],
    238         'border-image-repeat': ['stretch', 'repeat', 'round', 'space'],
    239         'border-image-slice': ['fill', '<number>', '<percentage>'],
    240         'border-image-source': ['none', '<image>'],
    241         'border-image-width': ['<length>', '<percentage>', '<number>', 'auto'],
    242         'border-top | border-right | border-bottom | border-left': [
    243             '<border_width>', '<border_style>', '<color>'
    244         ],
    245         'border-top-color | border-right-color | border-bottom-color | border-left-color': ['<color>'],
    246         'border-top-left-radius | border-top-right-radius | border-bottom-right-radius | border-bottom-left-radius': [
    247             '<length>', '<percentage>'
    248         ],
    249         'border-top-style | border-right-style | border-bottom-style | border-left-style': ['<border_style>'],
    250         'border-top-width | border-right-width | border-bottom-width | border-left-width': ['<border_width>'],
    251         'bottom': ['<length>', '<percentage>', 'auto'],
    252         'box-decoration-break': ['slice', 'clone'],
    253         'box-shadow': ['none', 'inset', '<color>'],
    254         'box-sizing': ['content-box', 'border-box'],
    255         'break-after': ['<break_before_after>', '<break_inside>'],
    256         'break-before': ['<break_before_after>', '<break_inside>'],
    257         'break-inside': ['<break_inside>'],
    258         'caption-side': ['top', 'bottom'],
    259         'clear': ['none', 'left', 'right', 'both'],
    260         'clip': [
    261             ['rect()', 'rect(${1:0}, ${2:0}, ${3:0}, ${4:0})'],
    262             'auto'
    263         ],
    264         'clip-path': ['none', '<uri>', '<basic_shape>'],
    265         'clip-rule': ['nonzero', 'evenodd'],
    266         'color': ['<color>'],
    267         'color-interpolation': ['auto', 'sRGB', 'linearRGB'],
    268         'color-interpolation-filters': ['auto', 'sRGB', 'linearRGB'],
    269         'color-profile': ['auto', 'sRGB', '<uri>'],
    270         'color-rendering': ['auto', 'optimizeSpeed', 'optimizeQuality'],
    271         'columns': ['auto'],
    272         'column-count': ['auto', '<number>'],
    273         'column-fill': ['auto', 'balance'],
    274         'column-gap': ['normal', '<length>'],
    275         'column-rule': ['<border_width>', '<border_style>', '<color>'],
    276         'column-rule-color': ['<color>'],
    277         'column-rule-style': ['<border_style>'],
    278         'column-rule-width': ['<border_width>'],
    279         'column-span': ['none'],
    280         'column-width': ['auto', '<length>'],
    281         'content': [
    282             'none', 'normal', '<string>', '<uri>',
    283             'open-quote', 'close-quote', 'no-open-quote', 'no-close-quote',
    284             ['attr()', 'attr($1)'],
    285             ['counter()', 'counter($1)']
    286         ],
    287         'counter-increment': ['none', '<custom_ident>', '<integer>'],
    288         'counter-reset': ['none', '<custom_ident>', '<integer>'],
    289         'cursor': [
    290             '<uri>', 'auto', 'default', 'none', 'context-menu', 'help',
    291             'pointer', 'progress', 'wait', 'cell', 'crosshair', 'text',
    292             'vertical-text', 'alias', 'copy', 'move', 'no-drop', 'not-allowed',
    293             'e-resize', 'n-resize', 'ne-resize', 'nw-resize', 's-resize',
    294             'se-resize', 'sw-resize', 'w-resize', 'ew-resize', 'ns-resize',
    295             'nesw-resize', 'nwse-resize', 'col-resize', 'row-resize',
    296             'all-scroll', 'zoom-in', 'zoom-out'
    297         ],
    298         'direction': ['ltr', 'rtl'],
    299         'display': [
    300             'none', 'inline', 'block', 'contents', 'list-item', 'inline-block',
    301             'inline-table', 'table', 'table-cell', 'table-column',
    302             'table-column-group', 'table-footer-group', 'table-header-group',
    303             'table-row', 'table-row-group', 'table-caption', 'flex',
    304             'inline-flex', 'grid', 'inline-grid', 'ruby', 'ruby-base',
    305             'ruby-text', 'ruby-base-container', 'ruby-text-container', 'run-in'
    306         ],
    307         'dominant-baseline': [
    308             'auto', 'middle', 'central', 'text-before-edge',
    309             'text-after-edge', 'ideographic', 'alphabetic', 'hanging',
    310             'mathematical', 'use-script', 'no-change', 'reset-size'
    311         ],
    312         'empty-cells': ['show', 'hide'],
    313         'fill': ['<color>'],
    314         'fill-rule': ['nonzero', 'evenodd'],
    315         'filter': [
    316             '<uri>',
    317             ['blur()', 'blur(${1:5px})'],
    318             ['brightness()', 'brightness(${1:1.0})'],
    319             ['contrast()', 'contrast(${1:100%})'],
    320             ['drop-shadow()', 'drop-shadow(${1:1px} ${2:1px})'],
    321             ['grayscale()', 'grayscale(${1:50%})'],
    322             ['hue-rotate()', 'hue-rotate(${1:90deg})'],
    323             ['invert()', 'invert(${1:50%})'],
    324             ['opacity()', 'opacity(${1:100%})'],
    325             ['saturate()', 'saturate(${1:50%})'],
    326             ['sepia()', 'sepia(${1:50%})']
    327         ],
    328         'flex': ['none'],
    329         'flex-grow': ['<number>'],
    330         'flex-shrink': ['<number>'],
    331         'flex-basis': ['auto', '<width>'],
    332         'flex-flow': [
    333             'row', 'row-reverse', 'column', 'column-reverse', 'nowrap', 'wrap',
    334             'wrap-reverse'
    335         ],
    336         'flex-direction': ['row', 'row-reverse', 'column', 'column-reverse'],
    337         'flex-wrap': ['nowrap', 'wrap', 'wrap-reverse'],
    338         'float': ['left', 'right', 'none'],
    339         'flood-color': ['<color>'],
    340         'font': [
    341             '<absolute_weight>', '<generic_name>', '<relative_weight>',
    342             'caption', 'icon', 'italic', 'menu', 'message-box', 'oblique',
    343             'small-caps', 'small-caption', 'status-bar'
    344         ],
    345         'font-display': ['auto', 'block', 'swap', 'fallback', 'optional'],
    346         'font-family': ['<generic_name>'],
    347         'font-feature-settings': ['normal', '<string>'],
    348         'font-kerning': ['auto', 'normal', 'none'],
    349         'font-language-override': ['normal', '<string>'],
    350         'font-size': [
    351             '<absolute_size>', '<relative_size>', '<length>', '<percentage>'
    352         ],
    353         'font-size-adjust': ['none', '<number>'],
    354         'font-style': ['normal', 'italic', 'oblique'],
    355         'font-stretch': [
    356             'normal', 'semi-condensed', 'condensed', 'extra-condensed',
    357             'ultra-condensed', 'semi-expanded', 'expanded', 'extra-expanded',
    358             'ultra-expanded'
    359         ],
    360         'font-synthesis': ['none', 'weight', 'style'],
    361         'font-variant': ['normal', 'small-caps'],
    362         'font-variant-alternates': ['<font_variant_alternates>'],
    363         'font-variant-caps': [
    364             'normal', 'small-caps', 'all-small-caps', 'petite-caps',
    365             'all-petite-caps', 'unicase', 'titling-case'
    366         ],
    367         'font-variant-east-asian': [
    368             'normal', 'ruby', 'jis78', 'jis83', 'jis90', 'jis04', 'simplified',
    369             'traditional'
    370         ],
    371         'font-variant-ligatures': [
    372             'normal', 'none', 'common-ligatures', 'no-common-ligatures',
    373             'discretionary-ligatures', 'no-discretionary-ligatures',
    374             'historical-ligatures', 'no-historical-ligatures', 'contextual',
    375             'no-contextual'
    376         ],
    377         'font-variant-numeric': [
    378             'normal', 'ordinal', 'slashed-zero', 'lining-nums', 'oldstyle-nums',
    379             'proportional-nums', 'tabular-nums', 'diagonal-fractions',
    380             'stacked-fractions'
    381         ],
    382         'font-variant-position': ['normal', 'sub', 'super'],
    383         'font-weight': ['<absolute_weight>', '<relative_weight>'],
    384         'grid': [],
    385         'grid-area': [],
    386         'grid-auto-columns': ['auto', '<percentage>', '<length>'],
    387         'grid-auto-flow': ['row', 'column', 'dense'],
    388         'grid-auto-rows': ['auto', '<percentage>', '<length>'],
    389         'grid-column-gap': ['<length>', '<percentage>'],
    390         'grid-gap': ['<length>', '<percentage>'],
    391         'grid-row-gap': ['<length>', '<percentage>'],
    392         'grid-template-areas': [],
    393         'grid-template-columns': ['auto', '<grid>', '<percentage>', '<length>'],
    394         'grid-template-rows': ['auto', '<grid>', '<percentage>', '<length>'],
    395         'grid-column': ['<number>'],
    396         'grid-column-end': ['<number>'],
    397         'grid-column-start': ['<number>'],
    398         'grid-row': ['<number>'],
    399         'grid-row-end': ['<number>'],
    400         'grid-row-start': ['<number>'],
    401         'height': ['<length>', '<percentage>', 'auto', 'fit-content'],
    402         'hyphens': ['none', 'manual', 'auto'],
    403         'image-rendering': [
    404             'auto', 'optimizeSpeed', 'optimizeQuality', 'pixelated'
    405         ],
    406         'ime-mode': ['auto', 'normal', 'active', 'inactive', 'disabled'],
    407         'isolation': ['auto', 'isolation'],
    408         'justify-content | justify-items | justify-self': [
    409             'start', 'end', 'flex-start', 'flex-end', 'center', 'left', 'right',
    410             'safe start', 'safe end', 'safe flex-start', 'safe flex-end',
    411             'safe center', 'safe left', 'safe right', 'unsafe start',
    412             'unsafe end', 'unsafe flex-start', 'unsafe flex-end', 'unsafe center',
    413             'unsafe left', 'unsafe right', 'normal', 'baseline', 'first baseline',
    414             'last baseline', 'space-between', 'space-around', 'space-evenly',
    415             'stretch', 'legacy', 'lecacy center', 'legacy left', 'legacy right'
    416         ],
    417         'kerning': ['auto'],
    418         'left': ['<length>', '<percentage>', 'auto'],
    419         'letter-spacing': ['normal', '<length>'],
    420         'lighting-color': ['<color>'],
    421         'line-height': ['normal', '<number>', '<length>', '<percentage>'],
    422         'list-style': ['<list_style_type>', 'inside', 'outside', '<uri>'],
    423         'list-style-image': ['<uri>', 'none'],
    424         'list-style-position': ['inside', 'outside'],
    425         'list-style-type': ['<list_style_type>'],
    426         'margin': ['auto', '<margin-width>'],
    427         'margin-top | margin-right | margin-bottom | margin-left': [
    428             'auto', '<margin-width>'
    429         ],
    430         'marker-end | marker-start | marker-mid': ['<uri>', 'none'],
    431         'marks': ['crop', 'cross', 'none'],
    432         'mask': ['<uri>', 'none'],
    433         'mask-type': ['luminance', 'alpha'],
    434         'max-height': ['<length>', '<percentage>', 'fit-content', 'none'],
    435         'max-width': ['<length>', '<percentage>', 'fit-content', 'none'],
    436         'min-height': ['<length>', '<percentage>', 'fit-content'],
    437         'min-width': ['<length>', '<percentage>', 'fit-content'],
    438         'mix-blend-mode': ['<blend_mode>'],
    439         'object-fit': ['fill', 'contain', 'cover', 'none', 'scale-down'],
    440         'object-position': ['<position>'],
    441         'opacity': ['<number>'],
    442         'order': ['<integer>'],
    443         'orphans': ['<integer>'],
    444         'outline': [
    445             '<color>', '<border_style>', '<border_width>', '<length>'
    446         ],
    447         'outline-color': ['<color>', 'invert'],
    448         'outline-offset': ['<length>'],
    449         'outline-style': ['<border_style>'],
    450         'outline-width': ['<border_width>', '<length>'],
    451         'overflow | overflow-x | overflow-y': [
    452             'visible', 'hidden', 'scroll', 'auto'
    453         ],
    454         'overflow-wrap': ['normal', 'break-word'],
    455         'padding': ['auto', '<padding-width>'],
    456         'padding-top | padding-right | padding-bottom | padding-left': [
    457             'auto', '<padding-width>'
    458         ],
    459         'page-break-after': ['auto', 'always', 'avoid', 'left', 'right'],
    460         'page-break-before': ['auto', 'always', 'avoid', 'left', 'right'],
    461         'page-break-inside': ['avoid', 'auto'],
    462         'paint-order': ['normal', 'fill', 'stroke', 'markers'],
    463         'perspective': ['none'],
    464         'perspective-origin': ['<position>'],
    465         'pointer-events': [
    466             'auto', 'none', 'all', 'visiblePainted', 'visibleFill',
    467             'visibleStroke', 'visible', 'painted', 'fill', 'stroke'
    468         ],
    469         'position': ['static', 'relative', 'absolute', 'fixed', 'sticky'],
    470         'quotes': ['none', '<string>'],
    471         'resize': ['none', 'both', 'horizontal', 'vertical'],
    472         'right': ['<length>', '<percentage>', 'auto'],
    473         'scroll-behavior': ['auto', 'smooth'],
    474         'shape-image-threshold': ['<number>'],
    475         'shape-margin': ['<length>', '<percentage>'],
    476         'shape-outside': [
    477             'none', 'margin-box', 'content-box', 'border-box', 'padding-box',
    478             '<basic_shape>', '<uri>'
    479         ],
    480         'shape-rendering': [
    481             'auto', 'optimizeSpeed', 'crispEdges', 'geometricPrecision'
    482         ],
    483         'size': [
    484             'a3', 'a4', 'a5', 'b4', 'b5', 'jis-b4', 'jis-b5', 'landscape',
    485             'ledger', 'legal', 'letter', 'portrait'
    486         ],
    487         'stop-color': ['<color>'],
    488         'stroke': ['<color>'],
    489         'stroke-dasharray': ['none'],
    490         'stroke-linecap': ['butt', 'round', 'square'],
    491         'stroke-linejoin': ['round', 'miter', 'bevel'],
    492         'system': ['<counter_symbols>'],
    493         'table-layout': ['auto', 'fixed'],
    494         'text-align': ['left', 'right', 'center', 'justify', 'justify-all'],
    495         'text-align-last': ['start', 'end', 'left', 'right', 'center', 'justify'],
    496         'text-anchor': ['start', 'middle', 'end'],
    497         'text-decoration': [
    498             'none', 'underline', 'overline', 'line-through', 'blink'
    499         ],
    500         'text-decoration-color': ['<color>'],
    501         'text-decoration-line': ['none', 'underline', 'overline', 'line-through'],
    502         'text-decoration-style': ['solid', 'double', 'dotted', 'dashed', 'wavy'],
    503         'text-indent': ['<length>', '<percentage>'],
    504         'text-orientation': ['mixed', 'upright', 'sideways', 'use-glyph-orientation'],
    505         'text-overflow': ['clip', 'ellipsis'],
    506         'text-rendering': [
    507             'auto', 'optimizeSpeed', 'optimizeLegibility', 'geometricPrecision'
    508         ],
    509         'text-shadow': ['none', '<color>'],
    510         'text-transform': ['capitalize', 'uppercase', 'lowercase', 'none'],
    511         'text-underline-position': ['auto', 'under', 'left', 'right'],
    512         'top': ['<length>', '<percentage>', 'auto'],
    513         'transform': [
    514             'none',
    515             ['matrix()', 'matrix(${1:1}, ${2:1}, ${3:1}, ${4:1}, ${5:2}, ${6:2})'],
    516             [
    517                 'matrix3d()',
    518                 'matrix3d('
    519                 '${1:1}, ${2:1}, ${3:0}, ${4:0}, '
    520                 '${5:1}, ${6:1}, ${7:0}, ${8:0}, '
    521                 '${9:0}, ${10:0}, ${11:1}, ${12:0}, '
    522                 '${13:2}, ${14:2}, ${15:0}, ${16:1}'
    523                 ')'
    524             ],
    525             ['perspective()', 'perspective(${1:0})'],
    526             ['rotate()', 'rotate(${1:45deg})'],
    527             ['rotate3d()', 'rotate3d(${1:0}, ${2:0}, ${3:1}, ${4:45deg})'],
    528             ['rotateX()', 'rotateX(${1:45deg})'],
    529             ['rotateY()', 'rotateY(${1:45deg})'],
    530             ['rotateZ()', 'rotateZ(${1:45deg})'],
    531             ['scale()', 'scale(${1:1.0})'],
    532             ['scale3d()', 'scale3d(${1:1.0}, ${2:1.0}, ${3:1.0})'],
    533             ['scaleX()', 'scaleX(${1:1.0})'],
    534             ['scaleY()', 'scaleY(${1:1.0})'],
    535             ['scaleZ()', 'scaleZ(${1:1.0})'],
    536             ['skew()', 'skew(${1:10deg})'],
    537             ['skewX()', 'skewX(${1:10deg})'],
    538             ['skewY()', 'skewY(${1:10deg})'],
    539             ['translate()', 'translate(${1:10px})'],
    540             ['translate3d()', 'translate3d(${1:10px}, ${2:0px}, ${3:0px})'],
    541             ['translateX()', 'translateX(${1:10px})'],
    542             ['translateY()', 'translateY(${1:10px})'],
    543             ['translateZ()', 'translateZ(${1:10px})']
    544         ],
    545         'transform-origin': ['<position>'],
    546         'transform-style': ['preserve-3d', 'flat'],
    547         'transition': [],
    548         'transition-delay': ['<time>'],
    549         'transition-duration': ['<time>'],
    550         'transition-property': ['none', '<custom-ident>'],
    551         'transition-timing-function': ['<timing_function>'],
    552         'unicode-bidi': ['normal', 'embed', 'bidi-override'],
    553         'unicode-range': [],
    554         'user-select': ['auto', 'text', 'none', 'contain'],
    555         'vertical-align': [
    556             'baseline', 'sub', 'super', 'text-top', 'text-bottom', 'middle',
    557             'top', 'bottom', '<percentage>', '<length>'
    558         ],
    559         'visibility': ['visible', 'hidden', 'collapse'],
    560         'white-space': ['normal', 'pre', 'nowrap', 'pre-wrap', 'pre-line'],
    561         'widows': ['<integer>'],
    562         'width': ['<length>', '<percentage>', 'auto', 'fit-content'],
    563         'will-change': ['auto', 'contents', 'scroll-position', '<custom-ident>'],
    564         'word-break': ['normal', 'break-all', 'keep-all'],
    565         'word-spacing': ['normal', '<length>'],
    566         'word-wrap': ['normal', 'break-word'],
    567         'writing-mode': [
    568             'horizontal-tb', 'vertical-rl', 'vertical-lr', 'sideways-rl',
    569             'sideways-lr'
    570         ],
    571         'z-index': ['auto', '<integer>'],
    572     }
    573 
    574     props = {}
    575 
    576     for names, values in properties_dict.items():
    577         # Values that are allowed for all properties
    578         allowed_values = ['all', 'inherit', 'initial', 'unset', ['var()', 'var($1)']]
    579 
    580         # Determine which values are available for the current property name
    581         for value in values:
    582             if value[0] == '<' and value[-1] == '>':
    583                 key = value[1:-1]
    584                 if key in common_values:
    585                     allowed_values += common_values[key]
    586             else:
    587                 allowed_values.append(value)
    588 
    589         for name in names.split(' | '):
    590             props[name] = allowed_values
    591 
    592     return props
    593 
    594 
    595 def match_selector(view, pt, scope):
    596     # This will catch scenarios like:
    597     # - .foo {font-style: |}
    598     # - <style type="text/css">.foo { font-weight: b|</style>
    599     return any(view.match_selector(p, scope) for p in (pt, pt - 1))
    600 
    601 
    602 def next_none_whitespace(view, pt):
    603     for pt in range(pt, view.size()):
    604         ch = view.substr(pt)
    605         if ch not in ' \t':
    606             return ch
    607 
    608 
    609 class CSSCompletions(sublime_plugin.EventListener):
    610     @cached_property
    611     def props(self):
    612         return get_properties()
    613 
    614     @cached_property
    615     def re_name(self):
    616         return re.compile(r"([a-zA-Z-]+)\s*:[^:;{}]*$")
    617 
    618     @cached_property
    619     def re_value(self):
    620         return re.compile(r"^(?:\s*(:)|([ \t]*))([^:]*)([;}])")
    621 
    622     @timing
    623     def on_query_completions(self, view, prefix, locations):
    624 
    625         settings = sublime.load_settings('CSS.sublime-settings')
    626         if settings.get('disable_default_completions'):
    627             return None
    628 
    629         selector = settings.get('default_completions_selector', '')
    630         if isinstance(selector, list):
    631             selector = ''.join(selector)
    632 
    633         pt = locations[0]
    634         if not match_selector(view, pt, selector):
    635             return None
    636 
    637         if match_selector(view, pt, "meta.property-value.css meta.function-call"):
    638             items = self.complete_function_argument(view, prefix, pt)
    639         elif match_selector(view, pt, "meta.property-value.css"):
    640             items = self.complete_property_value(view, prefix, pt)
    641         elif match_selector(view, pt, "meta.property-list.css, meta.property-name.css"):
    642             items = self.complete_property_name(view, prefix, pt)
    643         else:
    644             # TODO: provide selectors, at-rules
    645             items = None
    646 
    647         if items:
    648             return sublime.CompletionList(items, sublime.INHIBIT_WORD_COMPLETIONS)
    649         return None
    650 
    651     def complete_property_name(self, view, prefix, pt):
    652         if match_selector(view, pt, "meta.group"):
    653             # don't append semicolon in groups e.g.: `@media screen (prop: |)`
    654             suffix = ": $0"
    655         else:
    656             suffix = ": $0;"
    657         text = view.substr(sublime.Region(pt, view.line(pt).end()))
    658         matches = self.re_value.search(text)
    659         if matches:
    660             colon, space, value, term = matches.groups()
    661             if colon:
    662                 # don't append anything if the next character is a colon
    663                 suffix = ""
    664             elif value:
    665                 # only append colon if value already exists
    666                 suffix = ":" if space else ": "
    667             elif term == ";":
    668                 # ommit semicolon if rule is already terminated
    669                 suffix = ": $0"
    670 
    671         return (
    672             sublime.CompletionItem(
    673                 trigger=prop,
    674                 completion=prop + suffix,
    675                 completion_format=sublime.COMPLETION_FORMAT_SNIPPET,
    676                 kind=KIND_CSS_PROPERTY
    677             ) for prop in self.props
    678         )
    679 
    680     def complete_property_value(self, view, prefix, pt):
    681         completions = []
    682         text = view.substr(sublime.Region(view.line(pt).begin(), pt - len(prefix)))
    683         matches = self.re_name.search(text)
    684         if matches:
    685             prop = matches.group(1)
    686             values = self.props.get(prop)
    687             if values:
    688                 details = f"<code>{prop}</code> property-value"
    689 
    690                 if match_selector(view, pt, "meta.group"):
    691                     # don't append semicolon in groups e.g.: `@media screen (prop: val)`
    692                     suffix = ""
    693                 elif next_none_whitespace(view, pt) == ";":
    694                     suffix = ""
    695                 else:
    696                     suffix = "$0;"
    697 
    698                 for value in values:
    699                     if isinstance(value, list):
    700                         desc, snippet = value
    701                         kind = KIND_CSS_FUNCTION
    702                     else:
    703                         desc = value
    704                         snippet = value
    705                         kind = KIND_CSS_CONSTANT
    706 
    707                     completions.append(sublime.CompletionItem(
    708                         trigger=desc,
    709                         completion=snippet + suffix,
    710                         completion_format=sublime.COMPLETION_FORMAT_SNIPPET,
    711                         kind=kind,
    712                         details=details
    713                     ))
    714 
    715         return completions
    716 
    717     def complete_function_argument(self, view, prefix, pt):
    718         return None