diff --git a/edit/codemirror-default.css b/edit/codemirror-default.css index 1720aa72..7eacbf8b 100644 --- a/edit/codemirror-default.css +++ b/edit/codemirror-default.css @@ -31,3 +31,6 @@ .CodeMirror-search-hint { color: #888; } +.cm-uso-variable { + font-weight: bold; +} diff --git a/edit/codemirror-default.js b/edit/codemirror-default.js index 9a6142f6..9a687770 100644 --- a/edit/codemirror-default.js +++ b/edit/codemirror-default.js @@ -1,4 +1,4 @@ -/* global CodeMirror prefs loadScript */ +/* global CodeMirror prefs loadScript editor */ 'use strict'; @@ -132,3 +132,65 @@ return isBlank; }); })(); + +(() => { + const USO_VAR = 'uso-variable'; + const USO_VALID_VAR = 'variable-3 ' + USO_VAR; + const USO_INVALID_VAR = 'error ' + USO_VAR; + + CodeMirror.registerHelper('hint', 'css', function (cm) { + const {line, ch} = cm.getCursor(); + const {styles, text} = cm.getLineHandle(line); + if (!styles || !editor) { + return; + } + let prev = 0; + for (let i = 1; i < styles.length; i += 2) { + let end = styles[i]; + if (prev <= ch && ch <= end && + (styles[i + 1] || '').includes(USO_VAR)) { + const adjust = text[prev] === '/' ? 4 : 0; + prev += adjust; + end -= adjust; + const leftPart = text.slice(prev, ch); + const list = Object.keys(editor.getStyle().usercssData.vars) + .filter(name => name.startsWith(leftPart)); + console.log(leftPart, ...list); + return { + list, + from: {line, ch: prev}, + to: {line, ch: end}, + }; + } + prev = end; + } + }); + + const hooks = CodeMirror.mimeModes['text/css'].tokenHooks; + const originalCommentHook = hooks['/']; + hooks['/'] = tokenizeUsoVariables; + + function tokenizeUsoVariables(stream) { + const token = originalCommentHook.apply(this, arguments); + if (token[1] !== 'comment') { + return token; + } + const {string, start, pos} = stream; + // /*[[install-key]]*/ + // 01234 43210 + if (string[start + 2] === '[' && + string[start + 3] === '[' && + string[pos - 3] === ']' && + string[pos - 4] === ']') { + if (editor && + Object.hasOwnProperty.call( + editor.getStyle().usercssData.vars, + string.slice(start + 4, pos - 4))) { + token[0] = USO_VALID_VAR; + } else { + token[0] = USO_INVALID_VAR; + } + } + return token; + } +})(); diff --git a/edit/lint-codemirror-helper.js b/edit/lint-codemirror-helper.js index 6106ca1c..7dd12503 100644 --- a/edit/lint-codemirror-helper.js +++ b/edit/lint-codemirror-helper.js @@ -1,16 +1,38 @@ -/* global CodeMirror CSSLint stylelint linterConfig */ +/* global CodeMirror CSSLint parserlib stylelint linterConfig */ 'use strict'; -CodeMirror.registerHelper('lint', 'csslint', code => - CSSLint.verify(code, deepCopy(linterConfig.getCurrent('csslint'))) - .messages.map(message => ({ - from: CodeMirror.Pos(message.line - 1, message.col - 1), - to: CodeMirror.Pos(message.line - 1, message.col), - message: message.message, - rule: message.rule.id, - severity : message.type - })) -); +CodeMirror.registerHelper('lint', 'csslint', code => { + if (!CSSLint.suppressUsoVarError) { + CSSLint.suppressUsoVarError = true; + parserlib.css.Tokens[parserlib.css.Tokens.COMMENT].hide = false; + const isUsoVar = ({value}) => value.startsWith('/*[[') && value.endsWith(']]*/'); + CSSLint.addRule({ + id: 'uso-vars', + init(parser, reporter) { + parser.addListener('error', function ({message, line, col}) { + if (!isUsoVar(this._tokenStream._token)) { + const {_lt, _ltIndex: i} = this._tokenStream; + if (i < 2 || !_lt.slice(0, i - 1).reverse().some(isUsoVar)) { + reporter.error(message, line, col); + } + } + }); + }, + }); + } + const rules = deepCopy(linterConfig.getCurrent('csslint')); + Object.defineProperty(rules, 'errors', {get: () => 0, set: () => 0}); + rules['uso-vars'] = 1; + return CSSLint.verify(code, rules).messages + .map(({line, col, message, rule, type}) => line && { + message, + from: {line: line - 1, ch: col - 1}, + to: {line: line - 1, ch: col}, + rule: rule.id, + severity: type + }) + .filter(Boolean); +}); CodeMirror.registerHelper('lint', 'stylelint', code => stylelint.lint({