OLD | NEW |
1 /* | 1 /* |
2 * Copyright (C) 2012 Google Inc. All rights reserved. | 2 * Copyright (C) 2012 Google Inc. All rights reserved. |
3 * | 3 * |
4 * Redistribution and use in source and binary forms, with or without | 4 * Redistribution and use in source and binary forms, with or without |
5 * modification, are permitted provided that the following conditions are | 5 * modification, are permitted provided that the following conditions are |
6 * met: | 6 * met: |
7 * | 7 * |
8 * * Redistributions of source code must retain the above copyright | 8 * * Redistributions of source code must retain the above copyright |
9 * notice, this list of conditions and the following disclaimer. | 9 * notice, this list of conditions and the following disclaimer. |
10 * * Redistributions in binary form must reproduce the above | 10 * * Redistributions in binary form must reproduce the above |
(...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
45 this._networkProject = networkProject; | 45 this._networkProject = networkProject; |
46 this._addingRevisionCounter = 0; | 46 this._addingRevisionCounter = 0; |
47 this._reset(); | 47 this._reset(); |
48 WebInspector.fileManager.addEventListener(WebInspector.FileManager.EventType
s.SavedURL, this._fileSaveFinished, this); | 48 WebInspector.fileManager.addEventListener(WebInspector.FileManager.EventType
s.SavedURL, this._fileSaveFinished, this); |
49 WebInspector.moduleSetting("cssSourceMapsEnabled").addChangeListener(this._t
oggleSourceMapSupport, this); | 49 WebInspector.moduleSetting("cssSourceMapsEnabled").addChangeListener(this._t
oggleSourceMapSupport, this); |
50 this._cssModel.addEventListener(WebInspector.CSSStyleModel.Events.StyleSheet
Changed, this._styleSheetChanged, this); | 50 this._cssModel.addEventListener(WebInspector.CSSStyleModel.Events.StyleSheet
Changed, this._styleSheetChanged, this); |
51 this._workspace.addEventListener(WebInspector.Workspace.Events.UISourceCodeA
dded, this._uiSourceCodeAdded, this); | 51 this._workspace.addEventListener(WebInspector.Workspace.Events.UISourceCodeA
dded, this._uiSourceCodeAdded, this); |
52 this._workspace.addEventListener(WebInspector.Workspace.Events.UISourceCodeC
ontentCommitted, this._uiSourceCodeContentCommitted, this); | 52 this._workspace.addEventListener(WebInspector.Workspace.Events.UISourceCodeC
ontentCommitted, this._uiSourceCodeContentCommitted, this); |
53 this._workspace.addEventListener(WebInspector.Workspace.Events.ProjectRemove
d, this._reset, this); | 53 this._workspace.addEventListener(WebInspector.Workspace.Events.ProjectRemove
d, this._reset, this); |
54 this._networkMapping = networkMapping; | 54 this._networkMapping = networkMapping; |
| 55 |
| 56 this._editor = new WebInspector.SASSSourceMapping.Editor(workspace, networkM
apping, cssModel, this); |
55 } | 57 } |
56 | 58 |
57 WebInspector.SASSSourceMapping.prototype = { | 59 WebInspector.SASSSourceMapping.prototype = { |
58 /** | 60 /** |
59 * @param {!WebInspector.Event} event | 61 * @param {!WebInspector.Event} event |
60 */ | 62 */ |
61 _styleSheetChanged: function(event) | 63 _styleSheetChanged: function(event) |
62 { | 64 { |
63 var id = /** @type {!CSSAgent.StyleSheetId} */ (event.data.styleSheetId)
; | 65 var id = /** @type {!CSSAgent.StyleSheetId} */ (event.data.styleSheetId)
; |
64 if (this._addingRevisionCounter) { | 66 if (this._addingRevisionCounter) { |
65 --this._addingRevisionCounter; | 67 --this._addingRevisionCounter; |
66 return; | 68 return; |
67 } | 69 } |
| 70 return; |
68 var header = this._cssModel.styleSheetHeaderForId(id); | 71 var header = this._cssModel.styleSheetHeaderForId(id); |
69 if (!header) | 72 if (!header) |
70 return; | 73 return; |
71 | 74 |
72 this.removeHeader(header); | 75 this.removeHeader(header); |
73 }, | 76 }, |
74 | 77 |
75 /** | 78 /** |
76 * @param {!WebInspector.Event} event | 79 * @param {!WebInspector.Event} event |
77 */ | 80 */ |
(...skipping 566 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
644 this._addingRevisionCounter = 0; | 647 this._addingRevisionCounter = 0; |
645 this._completeSourceMapURLForCSSURL = {}; | 648 this._completeSourceMapURLForCSSURL = {}; |
646 this._cssURLsForSASSURL = {}; | 649 this._cssURLsForSASSURL = {}; |
647 /** @type {!Object.<string, !Array.<function(?WebInspector.SourceMap)>>}
*/ | 650 /** @type {!Object.<string, !Array.<function(?WebInspector.SourceMap)>>}
*/ |
648 this._pendingSourceMapLoadingCallbacks = {}; | 651 this._pendingSourceMapLoadingCallbacks = {}; |
649 /** @type {!Object.<string, !{deadlineMs: number, dataByURL: !Object.<st
ring, !{timer: number, previousPoll: number}>}>} */ | 652 /** @type {!Object.<string, !{deadlineMs: number, dataByURL: !Object.<st
ring, !{timer: number, previousPoll: number}>}>} */ |
650 this._pollDataForSASSURL = {}; | 653 this._pollDataForSASSURL = {}; |
651 /** @type {!Object.<string, !WebInspector.SourceMap>} */ | 654 /** @type {!Object.<string, !WebInspector.SourceMap>} */ |
652 this._sourceMapByURL = {}; | 655 this._sourceMapByURL = {}; |
653 this._sourceMapByStyleSheetURL = {}; | 656 this._sourceMapByStyleSheetURL = {}; |
| 657 }, |
| 658 |
| 659 _sourceMapForCSSURL: function(cssURL) |
| 660 { |
| 661 var sassURL = this._completeSourceMapURLForCSSURL[cssURL]; |
| 662 if (!sassURL) |
| 663 return null; |
| 664 return this._sourceMapByURL[sassURL] || null; |
654 } | 665 } |
655 } | 666 } |
| 667 |
| 668 WebInspector.SASSSourceMapping.Editor = function(workspace, networkMapping, cssM
odel, sassSourceMapping) |
| 669 { |
| 670 this._workspace = workspace; |
| 671 this._networkMapping = networkMapping; |
| 672 this._cssModel = cssModel; |
| 673 this._sassSourceMapping = sassSourceMapping; |
| 674 |
| 675 this._workspace.addEventListener(WebInspector.Workspace.Events.UISourceCodeA
dded, this._uiSourceCodeAddedToWorkspace, this); |
| 676 this._workspace.addEventListener(WebInspector.Workspace.Events.UISourceCodeR
emoved, this._uiSourceCodeRemoved, this); |
| 677 |
| 678 this._contents = new Map(); |
| 679 this._changedURLs = new Set(); |
| 680 this._throttler = new WebInspector.Throttler(0); |
| 681 } |
| 682 |
| 683 WebInspector.SASSSourceMapping.Editor.prototype = { |
| 684 _uiSourceCodeRemoved: function(event) |
| 685 { |
| 686 event.data.removeEventListener(WebInspector.UISourceCode.Events.WorkingC
opyCommitted, this._sourceCodeCommitted, this); |
| 687 }, |
| 688 |
| 689 _uiSourceCodeAddedToWorkspace: function(event) |
| 690 { |
| 691 var uiSourceCode = event.data; |
| 692 var url = uiSourceCode.originURL(); |
| 693 if (!this._cssModel.styleSheetIdsForURL(url).length) |
| 694 return; |
| 695 uiSourceCode.addEventListener(WebInspector.UISourceCode.Events.WorkingCo
pyCommitted, this._sourceCodeCommitted, this); |
| 696 uiSourceCode.requestContentPromise().then(setContent.bind(this)); |
| 697 |
| 698 function setContent(text) |
| 699 { |
| 700 this._contents.set(url, text); |
| 701 } |
| 702 }, |
| 703 |
| 704 _killMappingForURL: function(cssURL) |
| 705 { |
| 706 var ids = this._cssModel.styleSheetIdsForURL(cssURL); |
| 707 for (var id of ids) { |
| 708 var header = this._cssModel.styleSheetHeaderForId(id); |
| 709 this._sassSourceMapping.removeHeader(header); |
| 710 } |
| 711 }, |
| 712 |
| 713 _sourceCodeCommitted: function(event) |
| 714 { |
| 715 if (this._muteSourceCodeCommitted) |
| 716 return; |
| 717 |
| 718 var sourceURL = this._networkMapping.networkURL(event.target); |
| 719 this._changedURLs.add(sourceURL); |
| 720 this._throttler.schedule(this._processChangedSourceCodes.bind(this)); |
| 721 }, |
| 722 |
| 723 _processChangedSourceCodes: function() |
| 724 { |
| 725 console.log("React!"); |
| 726 //FIXME: handle all changes. |
| 727 var cssSourceURL = this._changedURLs.valuesArray()[0]; |
| 728 this._changedURLs.clear(); |
| 729 |
| 730 var uiSourceCode = this._networkMapping.uiSourceCodeForURL(cssSourceURL,
this._cssModel.target()); |
| 731 var historyLength = uiSourceCode.history.length; |
| 732 var oldCSSContent = this._contents.get(cssSourceURL); |
| 733 var newCSSContent = uiSourceCode.history[historyLength - 1].content; |
| 734 |
| 735 var sourceMap = this._sassSourceMapping._sourceMapForCSSURL(cssSourceURL
); |
| 736 if (!sourceMap) |
| 737 return Promise.resolve(); |
| 738 var edits = WebInspector.SourceMapEditor.computeEdits(cssSourceURL, oldC
SSContent, newCSSContent); |
| 739 edits.sort(sequentialOrder); |
| 740 // New sourcemap which maps to content. |
| 741 var newSourceMap = sourceMap.clone(); |
| 742 for (var i = edits.length - 1; i >= 0; --i) |
| 743 newSourceMap.compiledRangeEdited(edits[i].oldRange, edits[i].newRang
e()); |
| 744 |
| 745 var parser = new WebInspector.CSSParser(); |
| 746 |
| 747 var oldCSSStruct, newCSSStruct; |
| 748 |
| 749 var parsePromise = parser.parsePromise(oldCSSContent) |
| 750 .then(function(struct) { oldCSSStruct = struct; return parser.parseP
romise(newCSSContent);}) |
| 751 .then(function(struct) { newCSSStruct = struct; parser.dispose(); }) |
| 752 .then(onCSSStructs); |
| 753 |
| 754 var urlsToRequest = new Set(); |
| 755 for (var i = 0; i < edits.length; ++i) { |
| 756 var edit = edits[i]; |
| 757 urlsToRequest.add(edit.sourceURL); |
| 758 var mapping = sourceMap.findEntry(edit.oldRange.startLine, edit.oldR
ange.startColumn); |
| 759 urlsToRequest.add(mapping.sourceURL); |
| 760 } |
| 761 |
| 762 var sources = new Map(); |
| 763 var sourcePromises = []; |
| 764 for (var url of urlsToRequest) { |
| 765 var uiSourceCode = this._networkMapping.uiSourceCodeForURL(url, this
._cssModel.target()); |
| 766 var promise = uiSourceCode.requestContentPromise().then(setSource.bi
nd(null, sources, url)); |
| 767 sourcePromises.push(promise); |
| 768 } |
| 769 |
| 770 return Promise.all([parsePromise, Promise.all(sourcePromises)]).spread(d
oEdits.bind(this)) |
| 771 .catch(killMapping.bind(this, cssSourceURL)); |
| 772 |
| 773 function setSource(sources, sourceURL, content) |
| 774 { |
| 775 if (typeof content !== "string") |
| 776 throw new Error("Failed to fetch content of " + sourceURL); |
| 777 sources.set(sourceURL, content); |
| 778 } |
| 779 |
| 780 function onCSSStructs() |
| 781 { |
| 782 return WebInspector.SASSSourceMapping.diffCSSStructs(oldCSSStruct, n
ewCSSStruct); |
| 783 } |
| 784 |
| 785 function doEdits(structuralDiff) |
| 786 { |
| 787 function mapCSStoSASS(cssLine, cssColumn, sassEdit, useEnd) |
| 788 { |
| 789 var sassLineNumber = useEnd ? sassEdit.newRange().endLine : sass
Edit.newRange().startLine; |
| 790 var sassColumnNumber = useEnd ? sassEdit.newRange().endColumn :
sassEdit.newRange().startColumn; |
| 791 var mapping = new WebInspector.SourceMap.Entry(cssLine, cssColum
n, sassEdit.sourceURL, sassLineNumber, sassColumnNumber); |
| 792 ensureMappings.set(sassEdit, mapping); |
| 793 } |
| 794 |
| 795 if (this._changedURLs.size > 0) |
| 796 return; |
| 797 var structureMapping = new WebInspector.SASSStructureMapping(newCSSS
truct, sources); |
| 798 |
| 799 var edits = []; |
| 800 var ensureMappings = new Map(); |
| 801 for (var i = 0; i < structuralDiff.length; ++i) { |
| 802 //FIXME: handle other types of edits. |
| 803 var diff = structuralDiff[i]; |
| 804 if (diff.type === "ValueChanged") { |
| 805 var cssProperty = diff.property; |
| 806 var sassValue = structureMapping.cssPropertyValueToSASS(WebI
nspector.TextRange.fromObject(cssProperty.valueRange), newSourceMap); |
| 807 var sassContent = sources.get(sassValue.url); |
| 808 var sassEdit = new WebInspector.SourceMapEdit(sassValue.url,
sassValue.range, sassValue.range.extract(sassContent), " " + cssProperty.value.
trim()); |
| 809 edits.push(sassEdit); |
| 810 mapCSStoSASS(cssProperty.valueRange.startLine, cssProperty.v
alueRange.startColumn, sassEdit, false); |
| 811 |
| 812 var backEdits = structureMapping.sassPropertyValueToCSS(sass
Value.url, sassValue.range, newSourceMap); |
| 813 if (backEdits.length === 1) |
| 814 continue; |
| 815 for (var j = 0; j < backEdits.length; ++j) { |
| 816 var backEditProperty = backEdits[j]; |
| 817 if (backEditProperty === cssProperty) |
| 818 continue; |
| 819 var oldRange = WebInspector.TextRange.fromObject(backEdi
tProperty.valueRange); |
| 820 var oldText = oldRange.extract(newCSSContent); |
| 821 var cssEdit = new WebInspector.SourceMapEdit(cssSourceUR
L, oldRange, oldText, sassEdit.newText); |
| 822 edits.push(cssEdit); |
| 823 var mapping = new WebInspector.SourceMap.Entry(oldRange.
startLine, oldRange.startColumn, sassValue.url, sassValue.range.startLine, sassV
alue.range.startColumn); |
| 824 ensureMappings.set(cssEdit, mapping); |
| 825 } |
| 826 } else if (diff.type === "PropertyAdded") { |
| 827 var cssProperty = diff.property; |
| 828 if (diff.after) { |
| 829 var afterCSSProperty = diff.after; |
| 830 var afterSASSProperty = structureMapping.cssPropertyToSA
SS(afterCSSProperty.range, newSourceMap); |
| 831 var url = afterSASSProperty.url; |
| 832 var sassContent = sources.get(url); |
| 833 var oldRange = WebInspector.TextRange.createFromLocation
(afterSASSProperty.range.endLine, afterSASSProperty.range.endColumn); |
| 834 var indent = (new WebInspector.TextRange(afterSASSProper
ty.range.startLine, 0, afterSASSProperty.range.startLine, afterSASSProperty.rang
e.startColumn)).extract(sassContent); |
| 835 if (!/^\s+$/.test(indent)) indent = ""; |
| 836 |
| 837 // Split property addition into chunks to preserve mappi
ngs. |
| 838 var newText = String.sprintf("\n%s", indent); |
| 839 var edit1 = new WebInspector.SourceMapEdit(url, oldRange
, "", newText); |
| 840 mapCSStoSASS(cssProperty.range.startLine, cssProperty.ra
nge.startColumn, edit1, true); |
| 841 |
| 842 var newText = String.sprintf("%s:", cssProperty.name.tri
m()); |
| 843 var edit2 = new WebInspector.SourceMapEdit(url, oldRange
, "", newText); |
| 844 mapCSStoSASS(cssProperty.valueRange.startLine, cssProper
ty.valueRange.startColumn, edit2, true); |
| 845 |
| 846 var newText = String.sprintf(" %s;", cssProperty.value.t
rim()); |
| 847 var edit3 = new WebInspector.SourceMapEdit(url, oldRange
, "", newText); |
| 848 mapCSStoSASS(cssProperty.range.endLine, cssProperty.rang
e.endColumn, edit3, true); |
| 849 |
| 850 edits.push(edit3, edit2, edit1); |
| 851 } else if (diff.before) { |
| 852 var beforeCSSProperty = diff.before; |
| 853 var beforeSASSProperty = structureMapping.cssPropertyToS
ASS(beforeCSSProperty.range, newSourceMap); |
| 854 var url = beforeSASSProperty.url; |
| 855 var sassContent = sources.get(url); |
| 856 var oldRange = WebInspector.TextRange.createFromLocation
(beforeSASSProperty.range.startLine, beforeSASSProperty.range.startColumn); |
| 857 var indent = (new WebInspector.TextRange(beforeSASSPrope
rty.range.startLine, 0, beforeSASSProperty.range.startLine, beforeSASSProperty.r
ange.startColumn)).extract(sassContent); |
| 858 if (!/^\s+$/.test(indent)) indent = ""; |
| 859 |
| 860 // Split property addition into chunks to preserve mappi
ngs. |
| 861 var newText = String.sprintf("%s:", cssProperty.name.tri
m()); |
| 862 var edit1 = new WebInspector.SourceMapEdit(url, oldRange
, "", newText); |
| 863 mapCSStoSASS(cssProperty.range.startLine, cssProperty.ra
nge.startColumn, edit1); |
| 864 |
| 865 var newText = String.sprintf(" %s;", cssProperty.value.t
rim()); |
| 866 var edit2 = new WebInspector.SourceMapEdit(url, oldRange
, "", newText); |
| 867 mapCSStoSASS(cssProperty.valueRange.startLine, cssProper
ty.valueRange.startColumn, edit2); |
| 868 |
| 869 var newText = String.sprintf("\n%s", indent); |
| 870 var edit3 = new WebInspector.SourceMapEdit(url, oldRange
, "", newText); |
| 871 mapCSStoSASS(cssProperty.range.endLine, cssProperty.rang
e.endColumn, edit3); |
| 872 |
| 873 edits.push(edit3, edit2, edit1); |
| 874 } |
| 875 //FIXME: do back edits. |
| 876 } else if (diff.type === "PropertyRemoved") { |
| 877 var cssProperty = diff.property; |
| 878 var sassProperty = structureMapping.cssPropertyToSASS(cssPro
perty.range, sourceMap); |
| 879 var sassContent = sources.get(sassProperty.url); |
| 880 var lineRemoveRange = new WebInspector.TextRange(sassPropert
y.range.startLine, 0, sassProperty.range.endLine + 1, 0); |
| 881 var oldRange = (lineRemoveRange.extract(sassContent).trim()
=== sassProperty.range.extract(sassContent).trim()) ? lineRemoveRange : sassProp
erty.range; |
| 882 var sassEdit = new WebInspector.SourceMapEdit(sassProperty.u
rl, oldRange, oldRange.extract(sassContent), ""); |
| 883 edits.push(sassEdit); |
| 884 //FIXME: do back edits. |
| 885 } |
| 886 } |
| 887 |
| 888 // Categorize edits per url. |
| 889 var editsPerURL = new Map(); |
| 890 for (var edit of edits) { |
| 891 if (!editsPerURL.has(edit.sourceURL)) |
| 892 editsPerURL.set(edit.sourceURL, []); |
| 893 var list = editsPerURL.get(edit.sourceURL); |
| 894 list.push(edit); |
| 895 } |
| 896 |
| 897 // Apply to CSS. |
| 898 var cssEdits = editsPerURL.get(cssSourceURL); |
| 899 if (cssEdits) { |
| 900 cssEdits.stableSort(sequentialOrder); |
| 901 // Apply edits in a reversed order so that they do not conflict
with each other. |
| 902 for (var i = cssEdits.length - 1; i >= 0; --i) { |
| 903 var cssEdit = cssEdits[i]; |
| 904 newCSSContent = cssEdit.applyToText(newCSSContent); |
| 905 newSourceMap.compiledRangeEdited(cssEdit.oldRange, cssEdit.n
ewRange()); |
| 906 var ensureMapping = ensureMappings.get(cssEdit); |
| 907 // Add missing mappings to source map. |
| 908 if (ensureMapping) |
| 909 newSourceMap.ensureHasMapping(ensureMapping); |
| 910 } |
| 911 var uiSourceCode = this._networkMapping.uiSourceCodeForURL(cssSo
urceURL, this._cssModel.target()); |
| 912 this._muteSourceCodeCommitted = true; |
| 913 uiSourceCode.addRevision(newCSSContent); |
| 914 this._muteSourceCodeCommitted = false; |
| 915 } |
| 916 this._contents.set(cssSourceURL, newCSSContent); |
| 917 |
| 918 // Apply to SASS. |
| 919 for (var sassURL of editsPerURL.keys()) { |
| 920 if (sassURL === cssSourceURL) |
| 921 continue; |
| 922 var sassContent = sources.get(sassURL); |
| 923 var sassEdits = editsPerURL.get(sassURL); |
| 924 sassEdits.stableSort(sequentialOrder); |
| 925 // Apply edits in a reversed order so that they do not conflict
with each other. |
| 926 for (var i = sassEdits.length - 1; i >= 0; --i) { |
| 927 var sassEdit = sassEdits[i]; |
| 928 sassContent = sassEdit.applyToText(sassContent); |
| 929 newSourceMap.sourceRangeEdited(sassEdit.sourceURL, sassEdit.
oldRange, sassEdit.newRange()); |
| 930 var ensureMapping = ensureMappings.get(sassEdit); |
| 931 // Add missing mappings to source map. |
| 932 if (ensureMapping) |
| 933 newSourceMap.ensureHasMapping(ensureMapping); |
| 934 } |
| 935 var uiSourceCode = this._networkMapping.uiSourceCodeForURL(sassU
RL, this._cssModel.target()); |
| 936 uiSourceCode.addRevision(sassContent); |
| 937 } |
| 938 |
| 939 // Hot-Swap source maps. |
| 940 sourceMap._mappings = newSourceMap._mappings; |
| 941 sourceMap._reverseMappingsBySourceURL = newSourceMap._reverseMapping
sBySourceURL; |
| 942 } |
| 943 |
| 944 function sequentialOrder(range1, range2) |
| 945 { |
| 946 return range1.oldRange.follows(range2.oldRange) ? 1 : -1; |
| 947 } |
| 948 |
| 949 function killMapping(sourceURL, e) |
| 950 { |
| 951 console.error(e); |
| 952 this._killMappingForURL(sourceURL); |
| 953 } |
| 954 } |
| 955 } |
| 956 |
| 957 WebInspector.SASSSourceMapping.diffCSSStructs = function(oldCSSStruct, newCSSStr
uct) |
| 958 { |
| 959 oldCSSStruct = oldCSSStruct.filter(styleRulesFilter); |
| 960 newCSSStruct = newCSSStruct.filter(styleRulesFilter); |
| 961 if (oldCSSStruct.length !== newCSSStruct.length) |
| 962 throw new Error("not implemented for rule diff."); |
| 963 var structuralDiff = []; |
| 964 for (var i = 0; i < oldCSSStruct.length; ++i) { |
| 965 var oldRule = oldCSSStruct[i]; |
| 966 var newRule = newCSSStruct[i]; |
| 967 var removedSet = new Set(); |
| 968 var addedSet = new Set(); |
| 969 if (oldRule.properties.length !== newRule.properties.length) |
| 970 WebInspector.SASSSourceMapping.cssPropertiesDiff(oldRule.properties,
newRule.properties, removedSet, addedSet); |
| 971 for (var property of removedSet.values()) { |
| 972 structuralDiff.push({ |
| 973 type: "PropertyRemoved", |
| 974 property: property |
| 975 }); |
| 976 } |
| 977 var firstValidProperty = null; |
| 978 for (var j = 0; j < newRule.properties.length; ++j) { |
| 979 var property = newRule.properties[j]; |
| 980 if (!addedSet.has(property)) { |
| 981 firstValidProperty = property; |
| 982 break; |
| 983 } |
| 984 } |
| 985 var lastValidProperty = null; |
| 986 for (var j = 0; j < newRule.properties.length; ++j) { |
| 987 var property = newRule.properties[j]; |
| 988 if (!addedSet.has(property)) { |
| 989 lastValidProperty = property; |
| 990 continue; |
| 991 } |
| 992 var diff = { |
| 993 type: "PropertyAdded", |
| 994 property: property, |
| 995 }; |
| 996 if (lastValidProperty) |
| 997 diff.after = lastValidProperty; |
| 998 else if (firstValidProperty) |
| 999 diff.before = firstValidProperty; |
| 1000 structuralDiff.push(diff); |
| 1001 } |
| 1002 |
| 1003 var p1 = 0; |
| 1004 var p2 = 0; |
| 1005 while (p1 < oldRule.properties.length && p2 < newRule.properties.length)
{ |
| 1006 if (removedSet.has(oldRule.properties[p1])) { |
| 1007 ++p1; |
| 1008 continue; |
| 1009 } |
| 1010 if (addedSet.has(newRule.properties[p2])) { |
| 1011 ++p2; |
| 1012 continue; |
| 1013 } |
| 1014 var oldProperty = oldRule.properties[p1++]; |
| 1015 var newProperty = newRule.properties[p2++]; |
| 1016 if (oldProperty.name !== newProperty.name) { |
| 1017 structuralDiff.push({ |
| 1018 type: "NameChanged", |
| 1019 property: newProperty |
| 1020 }); |
| 1021 } else if (oldProperty.value !== newProperty.value) { |
| 1022 structuralDiff.push({ |
| 1023 type: "ValueChanged", |
| 1024 property: newProperty |
| 1025 }); |
| 1026 } |
| 1027 } |
| 1028 } |
| 1029 return structuralDiff; |
| 1030 |
| 1031 function styleRulesFilter(rule) |
| 1032 { |
| 1033 return !!rule.properties; |
| 1034 } |
| 1035 } |
| 1036 |
| 1037 WebInspector.SASSSourceMapping.cssPropertiesDiff = function(properties1, propert
ies2, removedSet, addedSet) |
| 1038 { |
| 1039 var charCode = 33; |
| 1040 var encodedProperties = new Map(); |
| 1041 var text1 = doEncode(properties1); |
| 1042 var text2 = doEncode(properties2); |
| 1043 var differ = new diff_match_patch(); |
| 1044 var diff = differ.diff_main(text1, text2); |
| 1045 |
| 1046 var p1 = 0, p2 = 0; |
| 1047 for (var i = 0; i < diff.length; ++i) { |
| 1048 var token = diff[i]; |
| 1049 if (token[0] === 0) { |
| 1050 p1 += token[1].length; |
| 1051 p2 += token[1].length; |
| 1052 } else if (token[0] === -1) { |
| 1053 for (var j = 0; j < token[1].length; ++j) { |
| 1054 var property = properties1[p1++]; |
| 1055 removedSet.add(property); |
| 1056 } |
| 1057 } else { |
| 1058 for (var j = 0; j < token[1].length; ++j) { |
| 1059 var property = properties2[p2++]; |
| 1060 addedSet.add(property); |
| 1061 } |
| 1062 } |
| 1063 } |
| 1064 |
| 1065 function doEncode(properties) |
| 1066 { |
| 1067 var text = ""; |
| 1068 for (var i = 0; i < properties.length; ++i) { |
| 1069 var encoded = encodedProperties.get(properties[i].name); |
| 1070 if (!encoded) { |
| 1071 encoded = String.fromCharCode(charCode++); |
| 1072 encodedProperties.set(properties[i].name, encoded); |
| 1073 } |
| 1074 text += encoded; |
| 1075 } |
| 1076 return text; |
| 1077 } |
| 1078 } |
OLD | NEW |