| Index: Source/devtools/front_end/bindings/SASSStructureMapping.js
|
| diff --git a/Source/devtools/front_end/bindings/SASSStructureMapping.js b/Source/devtools/front_end/bindings/SASSStructureMapping.js
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..593a9667492e0ad39147dd5cd4b9d7ab60b8fa60
|
| --- /dev/null
|
| +++ b/Source/devtools/front_end/bindings/SASSStructureMapping.js
|
| @@ -0,0 +1,301 @@
|
| +// Copyright 2015 The Chromium Authors. All rights reserved.
|
| +// Use of this source code is governed by a BSD-style license that can be
|
| +// found in the LICENSE file.
|
| +
|
| +/**
|
| + * @constructor
|
| + * @param {!Map<string, string>} sassSources
|
| + */
|
| +WebInspector.SASSStructureMapping = function(cssStruct, sassSources)
|
| +{
|
| + this._sources = sassSources;
|
| + this._cssStruct = cssStruct;
|
| + this._sassStructs = new Map();
|
| +}
|
| +
|
| +WebInspector.SASSStructureMapping.prototype = {
|
| + cssPropertyValueToSASS: function(cssValueRange, sourceMap)
|
| + {
|
| + var entry = sourceMap.findEntry(cssValueRange.endLine, cssValueRange.endColumn);
|
| + var url = entry.sourceURL;
|
| + var struct = this._structForURL(url);
|
| + if (!struct) {
|
| + console.error("Failed to get structure of " + url);
|
| + return null;
|
| + }
|
| + var sassValue = this._sassValue(struct, entry);
|
| + if (!sassValue)
|
| + return null;
|
| + return {
|
| + url: url,
|
| + range: sassValue.range
|
| + };
|
| + },
|
| +
|
| + cssPropertyToSASS: function(cssPropertyRange, sourceMap)
|
| + {
|
| + var entry = sourceMap.findEntry(cssPropertyRange.startLine, cssPropertyRange.startColumn);
|
| + var struct = this._structForURL(entry.sourceURL);
|
| + if (!struct) {
|
| + console.error("Failed to get structure of " + entry.sourceURL);
|
| + return null;
|
| + }
|
| + var sassProperty = this._sassProperty(struct, entry);
|
| + if (!sassProperty)
|
| + return null;
|
| + return {
|
| + url: entry.sourceURL,
|
| + range: sassProperty.range
|
| + };
|
| + },
|
| +
|
| + sassPropertyValueToCSS: function(sassURL, sassValueRange, sourceMap)
|
| + {
|
| + var reversedEntries = sourceMap.findEntriesReversed(sassURL, sassValueRange.endLine, sassValueRange.endColumn - 1);
|
| + var result = [];
|
| + for (var i = 0; i < reversedEntries.length; ++i) {
|
| + var value = this._cssValue(reversedEntries[i]);
|
| + console.assert(value, "Failed to find reversed mapped property value.");
|
| + result.push(value);
|
| + }
|
| + return result;
|
| + },
|
| +
|
| + _sassProperty: function(struct, entry)
|
| + {
|
| + for (var i = 0; i < struct.properties.length; ++i) {
|
| + var property = struct.properties[i];
|
| + if (this._rangeContainsSourceMapping(property.range, entry))
|
| + return property;
|
| + }
|
| + return null;
|
| + },
|
| +
|
| + _sassVariable: function(struct, entry)
|
| + {
|
| + for (var i = 0; i < struct.variables.length; ++i) {
|
| + var variable = struct.variables[i];
|
| + if (this._rangeContainsSourceMapping(variable.range, entry))
|
| + return variable;
|
| + }
|
| + return null;
|
| + },
|
| +
|
| + _sassValue: function(struct, entry)
|
| + {
|
| + var sassProperty = this._sassProperty(struct, entry);
|
| + if (sassProperty && this._rangeContainsSourceMapping(sassProperty.value.range, entry))
|
| + return sassProperty.value;
|
| +
|
| + var sassVariable = this._sassVariable(struct, entry);
|
| + if (sassVariable && this._rangeContainsSourceMapping(sassVariable.value.range, entry))
|
| + return sassVariable.value;
|
| + for (var i = 0; i < struct.mixinValues.length; ++i) {
|
| + var mixinValue = struct.mixinValues[i];
|
| + if (this._rangeContainsSourceMapping(mixinValue.range, entry))
|
| + return mixinValue;
|
| + }
|
| + return null;
|
| + },
|
| +
|
| + _cssProperty: function(entry)
|
| + {
|
| + for (var i = 0; i < this._cssStruct.length; ++i) {
|
| + var rule = this._cssStruct[i];
|
| + for (var j = 0; j < rule.properties.length; ++j) {
|
| + var property = rule.properties[j];
|
| + if (this._rangeContainsCompiledMapping(property.range, entry))
|
| + return property;
|
| + }
|
| + }
|
| + return null;
|
| + },
|
| +
|
| + _cssValue: function(entry)
|
| + {
|
| + var cssProperty = this._cssProperty(entry);
|
| + if (cssProperty && this._rangeContainsCompiledMapping(cssProperty.valueRange, entry))
|
| + return cssProperty;
|
| + return null;
|
| + },
|
| +
|
| + _rangeContainsSourceMapping: function(range, entry)
|
| + {
|
| + if (range.startLine === range.endLine)
|
| + return range.startLine === entry.sourceLineNumber && range.startColumn <= entry.sourceColumnNumber && entry.sourceColumnNumber <= range.endColumn;
|
| + if (range.startLine === entry.sourceLineNumber)
|
| + return range.startColumn <= entry.sourceColumnNumber;
|
| + if (range.endLine === entry.sourceLineNumber)
|
| + return entry.sourceColumnNumber <= range.endColumn;
|
| + return range.startLine < entry.sourceLineNumber && entry.sourceLineNumber < range.endLine;
|
| + },
|
| +
|
| + _rangeContainsCompiledMapping: function(range, entry)
|
| + {
|
| + if (range.startLine === range.endLine)
|
| + return range.startLine === entry.lineNumber && range.startColumn <= entry.columnNumber && entry.columnNumber <= range.endColumn;
|
| + if (range.startLine === entry.lineNumber)
|
| + return range.startColumn <= entry.columnNumber;
|
| + if (range.endLine === entry.lineNumber)
|
| + return entry.columnNumber <= range.endColumn;
|
| + return range.startLine < entry.lineNumber && entry.lineNumber < range.endLine;
|
| + },
|
| +
|
| + _structForURL: function(sassURL)
|
| + {
|
| + if (this._sassStructs.has(sassURL))
|
| + return this._sassStructs.get(sassURL);
|
| + var source = this._sources.get(sassURL);
|
| + if (!source)
|
| + return null;
|
| + var result = WebInspector.SASSStructureMapping.parseSASS(source);
|
| + this._sassStructs.set(sassURL, result);
|
| + return result;
|
| + }
|
| +}
|
| +
|
| +
|
| +WebInspector.SASSStructureMapping.SCSSParserStates = {
|
| + Initial: "Initial",
|
| + PropertyName: "PropertyName",
|
| + PropertyValue: "PropertyValue",
|
| + VariableName: "VariableName",
|
| + VariableValue: "VariableValue",
|
| + MixinInclude: "MixinInclude",
|
| + MixinValue: "MixinValue"
|
| +}
|
| +
|
| +WebInspector.SASSStructureMapping.parseSASS = function(text)
|
| +{
|
| + var lines = text.split("\n");
|
| + var properties = [];
|
| + var variables = [];
|
| + var mixinValues = [];
|
| +
|
| + var States = WebInspector.SASSStructureMapping.SCSSParserStates;
|
| + var state = States.Initial;
|
| + var property;
|
| + var variable;
|
| + var mixin;
|
| + var UndefTokenType = {};
|
| +
|
| + /**
|
| + * @param {string} tokenValue
|
| + * @param {?string} tokenTypes
|
| + * @param {number} column
|
| + * @param {number} newColumn
|
| + */
|
| + function processToken(tokenValue, tokenTypes, column, newColumn)
|
| + {
|
| + var tokenType = tokenTypes ? tokenTypes.split(" ").keySet() : UndefTokenType;
|
| + switch (state) {
|
| + case States.Initial:
|
| + if (tokenType["css-variable-2"]) {
|
| + variable = {
|
| + name: {
|
| + content: tokenValue,
|
| + range: new WebInspector.TextRange(lineNumber, column, lineNumber, newColumn)
|
| + },
|
| + value: {
|
| + content: "",
|
| + },
|
| + range: WebInspector.TextRange.createFromLocation(lineNumber, column)
|
| + }
|
| + state = States.VariableName;
|
| + } else if (tokenType["css-property"] || tokenType["css-meta"]) {
|
| + property = {
|
| + name: {
|
| + content: tokenValue,
|
| + range: new WebInspector.TextRange(lineNumber, column, lineNumber, newColumn)
|
| + },
|
| + value: {
|
| + content: "",
|
| + },
|
| + range: WebInspector.TextRange.createFromLocation(lineNumber, column)
|
| + }
|
| + state = States.PropertyName;
|
| + } else if (tokenType["css-def"] && tokenValue === "@include") {
|
| + state = States.MixinInclude;
|
| + }
|
| + break;
|
| + case States.VariableName:
|
| + if (tokenValue === ")" && tokenType === UndefTokenType) {
|
| + state = States.Initial;
|
| + } else if (tokenValue === ":" && tokenType === UndefTokenType) {
|
| + state = States.VariableValue;
|
| + variable.value.range = WebInspector.TextRange.createFromLocation(lineNumber, newColumn);
|
| + } else if (tokenType !== UndefTokenType) {
|
| + state = States.Initial;
|
| + }
|
| + break;
|
| + case States.VariableValue:
|
| + if (tokenValue === ";" && tokenType === UndefTokenType) {
|
| + variable.value.range.endLine = lineNumber;
|
| + variable.value.range.endColumn = column;
|
| + variable.range.endLine = lineNumber;
|
| + variable.range.endColumn = newColumn;
|
| + variables.push(variable);
|
| + state = States.Initial;
|
| + } else {
|
| + variable.value.content += tokenValue;
|
| + }
|
| + break;
|
| + case States.PropertyName:
|
| + if (tokenValue === ":" && tokenType === UndefTokenType) {
|
| + state = States.PropertyValue;
|
| + property.value.range = WebInspector.TextRange.createFromLocation(lineNumber, newColumn);
|
| + } else if (tokenType["property"]) {
|
| + property.name.contet += tokenValue;
|
| + }
|
| + break;
|
| + case States.PropertyValue:
|
| + if (tokenValue === ";" && tokenType === UndefTokenType) {
|
| + property.value.range.endLine = lineNumber;
|
| + property.value.range.endColumn = column;
|
| + property.range.endLine = lineNumber;
|
| + property.range.endColumn = newColumn;
|
| + properties.push(property);
|
| + state = States.Initial;
|
| + } else {
|
| + property.value.content += tokenValue;
|
| + }
|
| + break;
|
| + case States.MixinInclude:
|
| + if (tokenValue === "(" && tokenType === UndefTokenType) {
|
| + state = States.MixinValue;
|
| + mixin = {
|
| + range: WebInspector.TextRange.createFromLocation(lineNumber, newColumn),
|
| + value: ""
|
| + };
|
| + } else if (tokenValue === ";" && tokenType === UndefTokenType) {
|
| + state = States.Initial;
|
| + if (mixin)
|
| + mixinValues.push(mixin);
|
| + mixin = null;
|
| + }
|
| + break;
|
| + case States.MixinValue:
|
| + if (tokenValue === ")" && tokenType === UndefTokenType) {
|
| + state = States.MixinInclude;
|
| + mixin.range.endLine = lineNumber;
|
| + mixin.range.endColumn = column;
|
| + } else {
|
| + mixin.value += tokenValue;
|
| + }
|
| + break;
|
| + default:
|
| + console.assert(false, "Unknown CSS parser state.");
|
| + }
|
| + }
|
| + var tokenizer = new WebInspector.CodeMirrorUtils.TokenizerFactory().createTokenizer("text/x-scss");
|
| + var lineNumber;
|
| + for (lineNumber = 0; lineNumber < lines.length; ++lineNumber) {
|
| + var line = lines[lineNumber];
|
| + tokenizer(line, processToken);
|
| + }
|
| + return {
|
| + variables: variables,
|
| + properties: properties,
|
| + mixinValues: mixinValues
|
| + };
|
| +}
|
|
|