blob: 5000d8a613df8f7ff605960eeba7fdc66c7b7f37 [file] [log] [blame]
// 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.
/**
* @implements {SDK.SourceMap}
* @unrestricted
*/
Sass.ASTSourceMap = class {
/**
* @param {string} compiledURL
* @param {string} sourceMapURL
* @param {!Map<string, !Sass.SASSSupport.AST>} models
* @param {?function(!Sass.ASTSourceMap, !Array<!TextUtils.TextRange>, !Array<string>):!Promise<?SDK.SourceMap.EditResult>} editCallback
*/
constructor(compiledURL, sourceMapURL, models, editCallback) {
this._editCallback = editCallback;
this._compiledURL = compiledURL;
this._sourceMapURL = sourceMapURL;
/** @type {!Map<string, !Sass.SASSSupport.AST>} */
this._models = models;
/** @type {!Map<!Sass.SASSSupport.TextNode, !Sass.SASSSupport.TextNode>} */
this._compiledToSource = new Map();
/** @type {!Multimap<!Sass.SASSSupport.TextNode, !Sass.SASSSupport.TextNode>} */
this._sourceToCompiled = new Multimap();
}
/**
* @override
* @return {string}
*/
compiledURL() {
return this._compiledURL;
}
/**
* @override
* @return {string}
*/
url() {
return this._sourceMapURL;
}
/**
* @override
* @return {!Array<string>}
*/
sourceURLs() {
return this._models.keysArray().filter(url => url !== this._compiledURL);
}
/**
* @override
* @param {string} sourceURL
* @param {!Common.ResourceType} contentType
* @return {!Common.ContentProvider}
*/
sourceContentProvider(sourceURL, contentType) {
var model = this.modelForURL(sourceURL);
var sourceContent = model ? model.document.text.value() : '';
return Common.StaticContentProvider.fromString(sourceURL, contentType, sourceContent);
}
/**
* @override
* @param {string} sourceURL
* @return {?string}
*/
embeddedContentByURL(sourceURL) {
var model = this.modelForURL(sourceURL);
return model ? model.document.text.value() : '';
}
/**
* @override
* @param {number} lineNumber
* @param {number=} columnNumber
* @return {?SDK.SourceMapEntry}
*/
findEntry(lineNumber, columnNumber) {
columnNumber = columnNumber || 0;
var compiledNode = this.compiledModel().findNodeForPosition(lineNumber, columnNumber);
if (!compiledNode)
return null;
var sourceNode = this.toSourceNode(compiledNode);
if (!sourceNode)
return null;
return new SDK.SourceMapEntry(
lineNumber, columnNumber, sourceNode.document.url, sourceNode.range.startLine, sourceNode.range.startColumn);
}
/**
* @override
* @return {boolean}
*/
editable() {
return !!this._editCallback;
}
/**
* @override
* @param {!Array<!TextUtils.TextRange>} ranges
* @param {!Array<string>} texts
* @return {!Promise<?SDK.SourceMap.EditResult>}
*/
editCompiled(ranges, texts) {
return this._editCallback.call(null, this, ranges, texts);
}
/**
* @return {!Sass.SASSSupport.AST}
*/
compiledModel() {
return /** @type {!Sass.SASSSupport.AST} */ (this._models.get(this._compiledURL));
}
/**
* @return {!Map<string, !Sass.SASSSupport.AST>}
*/
sourceModels() {
var sourceModels = /** @type {!Map<string, !Sass.SASSSupport.AST>} */ (new Map(this._models));
sourceModels.delete(this._compiledURL);
return sourceModels;
}
/**
* @return {!Map<string, !Sass.SASSSupport.AST>}
*/
models() {
return /** @type {!Map<string, !Sass.SASSSupport.AST>} */ (new Map(this._models));
}
/**
* @param {string} url
* @return {?Sass.SASSSupport.AST}
*/
modelForURL(url) {
return this._models.get(url) || null;
}
/**
* @param {!Sass.SASSSupport.TextNode} compiled
* @param {!Sass.SASSSupport.TextNode} source
*/
addMapping(compiled, source) {
this._compiledToSource.set(compiled, source);
this._sourceToCompiled.set(source, compiled);
}
/**
* @param {!Sass.SASSSupport.TextNode} compiled
* @param {!Sass.SASSSupport.TextNode} source
*/
removeMapping(compiled, source) {
this._compiledToSource.delete(compiled);
this._sourceToCompiled.delete(source, compiled);
}
/**
* @param {!Sass.SASSSupport.TextNode} compiled
* @return {?Sass.SASSSupport.TextNode}
*/
toSourceNode(compiled) {
return this._compiledToSource.get(compiled) || null;
}
/**
* @param {!Sass.SASSSupport.TextNode} source
* @return {!Array<!Sass.SASSSupport.TextNode>}
*/
toCompiledNodes(source) {
var compiledNodes = this._sourceToCompiled.get(source);
return compiledNodes ? compiledNodes.valuesArray() : [];
}
/**
* @param {!Array<!Sass.SASSSupport.AST>} updated
* @param {!Map<!Sass.SASSSupport.Node, !Sass.SASSSupport.Node>=} outNodeMapping
* @return {?Sass.ASTSourceMap}
*/
rebase(updated, outNodeMapping) {
outNodeMapping = outNodeMapping || new Map();
outNodeMapping.clear();
var models = /** @type {!Map<string, !Sass.SASSSupport.AST>} */ (new Map(this._models));
for (var newAST of updated) {
var oldAST = models.get(newAST.document.url);
if (!oldAST.match(newAST, outNodeMapping))
return null;
models.set(newAST.document.url, newAST);
}
var newMap = new Sass.ASTSourceMap(this._compiledURL, this._sourceMapURL, models, this._editCallback);
var compiledNodes = this._compiledToSource.keysArray();
for (var i = 0; i < compiledNodes.length; ++i) {
var compiledNode = compiledNodes[i];
var sourceNode = /** @type {!Sass.SASSSupport.TextNode} */ (this._compiledToSource.get(compiledNode));
var mappedCompiledNode =
/** @type {!Sass.SASSSupport.TextNode} */ (outNodeMapping.get(compiledNode) || compiledNode);
var mappedSourceNode =
/** @type {!Sass.SASSSupport.TextNode} */ (outNodeMapping.get(sourceNode) || sourceNode);
newMap.addMapping(mappedCompiledNode, mappedSourceNode);
}
return newMap;
}
};