Diffdown is a real-time collaborative Markdown editor/previewer built on the AT Protocol
diffdown.com
1import { parser } from '@lezer/css';
2import { syntaxTree, LRLanguage, indentNodeProp, continuedIndent, foldNodeProp, foldInside, LanguageSupport } from '@codemirror/language';
3import { NodeWeakMap, IterMode } from '@lezer/common';
4
5let _properties = null;
6function properties() {
7 if (!_properties && typeof document == "object" && document.body) {
8 let { style } = document.body, names = [], seen = new Set;
9 for (let prop in style)
10 if (prop != "cssText" && prop != "cssFloat") {
11 if (typeof style[prop] == "string") {
12 if (/[A-Z]/.test(prop))
13 prop = prop.replace(/[A-Z]/g, ch => "-" + ch.toLowerCase());
14 if (!seen.has(prop)) {
15 names.push(prop);
16 seen.add(prop);
17 }
18 }
19 }
20 _properties = names.sort().map(name => ({ type: "property", label: name, apply: name + ": " }));
21 }
22 return _properties || [];
23}
24const pseudoClasses = /*@__PURE__*/[
25 "active", "after", "any-link", "autofill", "backdrop", "before",
26 "checked", "cue", "default", "defined", "disabled", "empty",
27 "enabled", "file-selector-button", "first", "first-child",
28 "first-letter", "first-line", "first-of-type", "focus",
29 "focus-visible", "focus-within", "fullscreen", "has", "host",
30 "host-context", "hover", "in-range", "indeterminate", "invalid",
31 "is", "lang", "last-child", "last-of-type", "left", "link", "marker",
32 "modal", "not", "nth-child", "nth-last-child", "nth-last-of-type",
33 "nth-of-type", "only-child", "only-of-type", "optional", "out-of-range",
34 "part", "placeholder", "placeholder-shown", "read-only", "read-write",
35 "required", "right", "root", "scope", "selection", "slotted", "target",
36 "target-text", "valid", "visited", "where"
37].map(name => ({ type: "class", label: name }));
38const values = /*@__PURE__*/[
39 "above", "absolute", "activeborder", "additive", "activecaption", "after-white-space",
40 "ahead", "alias", "all", "all-scroll", "alphabetic", "alternate", "always",
41 "antialiased", "appworkspace", "asterisks", "attr", "auto", "auto-flow", "avoid", "avoid-column",
42 "avoid-page", "avoid-region", "axis-pan", "background", "backwards", "baseline", "below",
43 "bidi-override", "blink", "block", "block-axis", "bold", "bolder", "border", "border-box",
44 "both", "bottom", "break", "break-all", "break-word", "bullets", "button", "button-bevel",
45 "buttonface", "buttonhighlight", "buttonshadow", "buttontext", "calc", "capitalize",
46 "caps-lock-indicator", "caption", "captiontext", "caret", "cell", "center", "checkbox", "circle",
47 "cjk-decimal", "clear", "clip", "close-quote", "col-resize", "collapse", "color", "color-burn",
48 "color-dodge", "column", "column-reverse", "compact", "condensed", "contain", "content",
49 "contents", "content-box", "context-menu", "continuous", "copy", "counter", "counters", "cover",
50 "crop", "cross", "crosshair", "currentcolor", "cursive", "cyclic", "darken", "dashed", "decimal",
51 "decimal-leading-zero", "default", "default-button", "dense", "destination-atop", "destination-in",
52 "destination-out", "destination-over", "difference", "disc", "discard", "disclosure-closed",
53 "disclosure-open", "document", "dot-dash", "dot-dot-dash", "dotted", "double", "down", "e-resize",
54 "ease", "ease-in", "ease-in-out", "ease-out", "element", "ellipse", "ellipsis", "embed", "end",
55 "ethiopic-abegede-gez", "ethiopic-halehame-aa-er", "ethiopic-halehame-gez", "ew-resize", "exclusion",
56 "expanded", "extends", "extra-condensed", "extra-expanded", "fantasy", "fast", "fill", "fill-box",
57 "fixed", "flat", "flex", "flex-end", "flex-start", "footnotes", "forwards", "from",
58 "geometricPrecision", "graytext", "grid", "groove", "hand", "hard-light", "help", "hidden", "hide",
59 "higher", "highlight", "highlighttext", "horizontal", "hsl", "hsla", "hue", "icon", "ignore",
60 "inactiveborder", "inactivecaption", "inactivecaptiontext", "infinite", "infobackground", "infotext",
61 "inherit", "initial", "inline", "inline-axis", "inline-block", "inline-flex", "inline-grid",
62 "inline-table", "inset", "inside", "intrinsic", "invert", "italic", "justify", "keep-all",
63 "landscape", "large", "larger", "left", "level", "lighter", "lighten", "line-through", "linear",
64 "linear-gradient", "lines", "list-item", "listbox", "listitem", "local", "logical", "loud", "lower",
65 "lower-hexadecimal", "lower-latin", "lower-norwegian", "lowercase", "ltr", "luminosity", "manipulation",
66 "match", "matrix", "matrix3d", "medium", "menu", "menutext", "message-box", "middle", "min-intrinsic",
67 "mix", "monospace", "move", "multiple", "multiple_mask_images", "multiply", "n-resize", "narrower",
68 "ne-resize", "nesw-resize", "no-close-quote", "no-drop", "no-open-quote", "no-repeat", "none",
69 "normal", "not-allowed", "nowrap", "ns-resize", "numbers", "numeric", "nw-resize", "nwse-resize",
70 "oblique", "opacity", "open-quote", "optimizeLegibility", "optimizeSpeed", "outset", "outside",
71 "outside-shape", "overlay", "overline", "padding", "padding-box", "painted", "page", "paused",
72 "perspective", "pinch-zoom", "plus-darker", "plus-lighter", "pointer", "polygon", "portrait",
73 "pre", "pre-line", "pre-wrap", "preserve-3d", "progress", "push-button", "radial-gradient", "radio",
74 "read-only", "read-write", "read-write-plaintext-only", "rectangle", "region", "relative", "repeat",
75 "repeating-linear-gradient", "repeating-radial-gradient", "repeat-x", "repeat-y", "reset", "reverse",
76 "rgb", "rgba", "ridge", "right", "rotate", "rotate3d", "rotateX", "rotateY", "rotateZ", "round",
77 "row", "row-resize", "row-reverse", "rtl", "run-in", "running", "s-resize", "sans-serif", "saturation",
78 "scale", "scale3d", "scaleX", "scaleY", "scaleZ", "screen", "scroll", "scrollbar", "scroll-position",
79 "se-resize", "self-start", "self-end", "semi-condensed", "semi-expanded", "separate", "serif", "show",
80 "single", "skew", "skewX", "skewY", "skip-white-space", "slide", "slider-horizontal",
81 "slider-vertical", "sliderthumb-horizontal", "sliderthumb-vertical", "slow", "small", "small-caps",
82 "small-caption", "smaller", "soft-light", "solid", "source-atop", "source-in", "source-out",
83 "source-over", "space", "space-around", "space-between", "space-evenly", "spell-out", "square", "start",
84 "static", "status-bar", "stretch", "stroke", "stroke-box", "sub", "subpixel-antialiased", "svg_masks",
85 "super", "sw-resize", "symbolic", "symbols", "system-ui", "table", "table-caption", "table-cell",
86 "table-column", "table-column-group", "table-footer-group", "table-header-group", "table-row",
87 "table-row-group", "text", "text-bottom", "text-top", "textarea", "textfield", "thick", "thin",
88 "threeddarkshadow", "threedface", "threedhighlight", "threedlightshadow", "threedshadow", "to", "top",
89 "transform", "translate", "translate3d", "translateX", "translateY", "translateZ", "transparent",
90 "ultra-condensed", "ultra-expanded", "underline", "unidirectional-pan", "unset", "up", "upper-latin",
91 "uppercase", "url", "var", "vertical", "vertical-text", "view-box", "visible", "visibleFill",
92 "visiblePainted", "visibleStroke", "visual", "w-resize", "wait", "wave", "wider", "window", "windowframe",
93 "windowtext", "words", "wrap", "wrap-reverse", "x-large", "x-small", "xor", "xx-large", "xx-small"
94].map(name => ({ type: "keyword", label: name })).concat(/*@__PURE__*/[
95 "aliceblue", "antiquewhite", "aqua", "aquamarine", "azure", "beige",
96 "bisque", "black", "blanchedalmond", "blue", "blueviolet", "brown",
97 "burlywood", "cadetblue", "chartreuse", "chocolate", "coral", "cornflowerblue",
98 "cornsilk", "crimson", "cyan", "darkblue", "darkcyan", "darkgoldenrod",
99 "darkgray", "darkgreen", "darkkhaki", "darkmagenta", "darkolivegreen",
100 "darkorange", "darkorchid", "darkred", "darksalmon", "darkseagreen",
101 "darkslateblue", "darkslategray", "darkturquoise", "darkviolet",
102 "deeppink", "deepskyblue", "dimgray", "dodgerblue", "firebrick",
103 "floralwhite", "forestgreen", "fuchsia", "gainsboro", "ghostwhite",
104 "gold", "goldenrod", "gray", "grey", "green", "greenyellow", "honeydew",
105 "hotpink", "indianred", "indigo", "ivory", "khaki", "lavender",
106 "lavenderblush", "lawngreen", "lemonchiffon", "lightblue", "lightcoral",
107 "lightcyan", "lightgoldenrodyellow", "lightgray", "lightgreen", "lightpink",
108 "lightsalmon", "lightseagreen", "lightskyblue", "lightslategray",
109 "lightsteelblue", "lightyellow", "lime", "limegreen", "linen", "magenta",
110 "maroon", "mediumaquamarine", "mediumblue", "mediumorchid", "mediumpurple",
111 "mediumseagreen", "mediumslateblue", "mediumspringgreen", "mediumturquoise",
112 "mediumvioletred", "midnightblue", "mintcream", "mistyrose", "moccasin",
113 "navajowhite", "navy", "oldlace", "olive", "olivedrab", "orange", "orangered",
114 "orchid", "palegoldenrod", "palegreen", "paleturquoise", "palevioletred",
115 "papayawhip", "peachpuff", "peru", "pink", "plum", "powderblue",
116 "purple", "rebeccapurple", "red", "rosybrown", "royalblue", "saddlebrown",
117 "salmon", "sandybrown", "seagreen", "seashell", "sienna", "silver", "skyblue",
118 "slateblue", "slategray", "snow", "springgreen", "steelblue", "tan",
119 "teal", "thistle", "tomato", "turquoise", "violet", "wheat", "white",
120 "whitesmoke", "yellow", "yellowgreen"
121].map(name => ({ type: "constant", label: name })));
122const tags = /*@__PURE__*/[
123 "a", "abbr", "address", "article", "aside", "b", "bdi", "bdo", "blockquote", "body",
124 "br", "button", "canvas", "caption", "cite", "code", "col", "colgroup", "dd", "del",
125 "details", "dfn", "dialog", "div", "dl", "dt", "em", "figcaption", "figure", "footer",
126 "form", "header", "hgroup", "h1", "h2", "h3", "h4", "h5", "h6", "hr", "html", "i", "iframe",
127 "img", "input", "ins", "kbd", "label", "legend", "li", "main", "meter", "nav", "ol", "output",
128 "p", "pre", "ruby", "section", "select", "small", "source", "span", "strong", "sub", "summary",
129 "sup", "table", "tbody", "td", "template", "textarea", "tfoot", "th", "thead", "tr", "u", "ul"
130].map(name => ({ type: "type", label: name }));
131const atRules = /*@__PURE__*/[
132 "@charset", "@color-profile", "@container", "@counter-style", "@font-face", "@font-feature-values",
133 "@font-palette-values", "@import", "@keyframes", "@layer", "@media", "@namespace", "@page",
134 "@position-try", "@property", "@scope", "@starting-style", "@supports", "@view-transition"
135].map(label => ({ type: "keyword", label }));
136const identifier = /^(\w[\w-]*|-\w[\w-]*|)$/, variable = /^-(-[\w-]*)?$/;
137function isVarArg(node, doc) {
138 var _a;
139 if (node.name == "(" || node.type.isError)
140 node = node.parent || node;
141 if (node.name != "ArgList")
142 return false;
143 let callee = (_a = node.parent) === null || _a === void 0 ? void 0 : _a.firstChild;
144 if ((callee === null || callee === void 0 ? void 0 : callee.name) != "Callee")
145 return false;
146 return doc.sliceString(callee.from, callee.to) == "var";
147}
148const VariablesByNode = /*@__PURE__*/new NodeWeakMap();
149const declSelector = ["Declaration"];
150function astTop(node) {
151 for (let cur = node;;) {
152 if (cur.type.isTop)
153 return cur;
154 if (!(cur = cur.parent))
155 return node;
156 }
157}
158function variableNames(doc, node, isVariable) {
159 if (node.to - node.from > 4096) {
160 let known = VariablesByNode.get(node);
161 if (known)
162 return known;
163 let result = [], seen = new Set, cursor = node.cursor(IterMode.IncludeAnonymous);
164 if (cursor.firstChild())
165 do {
166 for (let option of variableNames(doc, cursor.node, isVariable))
167 if (!seen.has(option.label)) {
168 seen.add(option.label);
169 result.push(option);
170 }
171 } while (cursor.nextSibling());
172 VariablesByNode.set(node, result);
173 return result;
174 }
175 else {
176 let result = [], seen = new Set;
177 node.cursor().iterate(node => {
178 var _a;
179 if (isVariable(node) && node.matchContext(declSelector) && ((_a = node.node.nextSibling) === null || _a === void 0 ? void 0 : _a.name) == ":") {
180 let name = doc.sliceString(node.from, node.to);
181 if (!seen.has(name)) {
182 seen.add(name);
183 result.push({ label: name, type: "variable" });
184 }
185 }
186 });
187 return result;
188 }
189}
190/**
191Create a completion source for a CSS dialect, providing a
192predicate for determining what kind of syntax node can act as a
193completable variable. This is used by language modes like Sass and
194Less to reuse this package's completion logic.
195*/
196const defineCSSCompletionSource = (isVariable) => context => {
197 let { state, pos } = context, node = syntaxTree(state).resolveInner(pos, -1);
198 let isDash = node.type.isError && node.from == node.to - 1 && state.doc.sliceString(node.from, node.to) == "-";
199 if (node.name == "PropertyName" ||
200 (isDash || node.name == "TagName") && /^(Block|Styles)$/.test(node.resolve(node.to).name))
201 return { from: node.from, options: properties(), validFor: identifier };
202 if (node.name == "ValueName")
203 return { from: node.from, options: values, validFor: identifier };
204 if (node.name == "PseudoClassName")
205 return { from: node.from, options: pseudoClasses, validFor: identifier };
206 if (isVariable(node) || (context.explicit || isDash) && isVarArg(node, state.doc))
207 return { from: isVariable(node) || isDash ? node.from : pos,
208 options: variableNames(state.doc, astTop(node), isVariable),
209 validFor: variable };
210 if (node.name == "TagName") {
211 for (let { parent } = node; parent; parent = parent.parent)
212 if (parent.name == "Block")
213 return { from: node.from, options: properties(), validFor: identifier };
214 return { from: node.from, options: tags, validFor: identifier };
215 }
216 if (node.name == "AtKeyword")
217 return { from: node.from, options: atRules, validFor: identifier };
218 if (!context.explicit)
219 return null;
220 let above = node.resolve(pos), before = above.childBefore(pos);
221 if (before && before.name == ":" && above.name == "PseudoClassSelector")
222 return { from: pos, options: pseudoClasses, validFor: identifier };
223 if (before && before.name == ":" && above.name == "Declaration" || above.name == "ArgList")
224 return { from: pos, options: values, validFor: identifier };
225 if (above.name == "Block" || above.name == "Styles")
226 return { from: pos, options: properties(), validFor: identifier };
227 return null;
228};
229/**
230CSS property, variable, and value keyword completion source.
231*/
232const cssCompletionSource = /*@__PURE__*/defineCSSCompletionSource(n => n.name == "VariableName");
233
234/**
235A language provider based on the [Lezer CSS
236parser](https://github.com/lezer-parser/css), extended with
237highlighting and indentation information.
238*/
239const cssLanguage = /*@__PURE__*/LRLanguage.define({
240 name: "css",
241 parser: /*@__PURE__*/parser.configure({
242 props: [
243 /*@__PURE__*/indentNodeProp.add({
244 Declaration: /*@__PURE__*/continuedIndent()
245 }),
246 /*@__PURE__*/foldNodeProp.add({
247 "Block KeyframeList": foldInside
248 })
249 ]
250 }),
251 languageData: {
252 commentTokens: { block: { open: "/*", close: "*/" } },
253 indentOnInput: /^\s*\}$/,
254 wordChars: "-"
255 }
256});
257/**
258Language support for CSS.
259*/
260function css() {
261 return new LanguageSupport(cssLanguage, cssLanguage.data.of({ autocomplete: cssCompletionSource }));
262}
263
264export { css, cssCompletionSource, cssLanguage, defineCSSCompletionSource };