Changeset 285711 in webkit
- Timestamp:
- Nov 12, 2021, 2:54:05 AM (5 years ago)
- Location:
- trunk/Source/WebInspectorUI
- Files:
-
- 1 added
- 5 edited
- 1 copied
- 1 moved
-
ChangeLog (modified) (1 diff)
-
UserInterface/Controllers/QueryController.js (added)
-
UserInterface/Controllers/ResourceQueryController.js (modified) (3 diffs)
-
UserInterface/Main.html (modified) (3 diffs)
-
UserInterface/Models/QueryMatch.js (moved) (moved from trunk/Source/WebInspectorUI/UserInterface/Models/ResourceQueryMatch.js ) (3 diffs)
-
UserInterface/Models/QueryResult.js (copied) (copied from trunk/Source/WebInspectorUI/UserInterface/Models/ResourceQueryResult.js ) (5 diffs)
-
UserInterface/Models/ResourceQueryResult.js (modified) (3 diffs)
-
UserInterface/Test.html (modified) (3 diffs)
Legend:
- Unmodified
- Added
- Removed
-
trunk/Source/WebInspectorUI/ChangeLog
r285615 r285711 1 2021-11-12 Razvan Caliman <rcaliman@apple.com> 2 3 Web Inspector: Extract reusable logic from ResourceQueryController, ResourceQueryResult and ResourceQueryMatch 4 https://bugs.webkit.org/show_bug.cgi?id=231604 5 <rdar://problem/84160281> 6 7 Reviewed by Devin Rousso. 8 9 Extract reusable logic from `ResourceQueryController` into a generic `QueryController` 10 to enable subclassing for other specialized use cases. 11 12 * UserInterface/Controllers/QueryController.js: Added. 13 (WI.QueryController.prototype.executeQuery): 14 (WI.QueryController.prototype.findQueryMatches.pushMatch): 15 (WI.QueryController.prototype.findQueryMatches.matchNextSpecialCharacter): 16 (WI.QueryController.prototype.findQueryMatches.backtrack): 17 (WI.QueryController.prototype.findQueryMatches): 18 (WI.QueryController): 19 20 Keep only the reusable matching logic in `QueryController`. 21 Subclasses like `ResourceQueryController` are responsible for agregating 22 the data to be queried, customization for special characters and sorting results. 23 24 * UserInterface/Controllers/ResourceQueryController.js: 25 (WI.ResourceQueryController.prototype.executeQuery): 26 (WI.ResourceQueryController.prototype._findQueryMatches.pushMatch): Deleted. 27 (WI.ResourceQueryController.prototype._findQueryMatches.matchNextSpecialCharacter): Deleted. 28 (WI.ResourceQueryController.prototype._findQueryMatches.backtrack): Deleted. 29 (WI.ResourceQueryController.prototype._findQueryMatches): Deleted. 30 31 * UserInterface/Main.html: 32 33 * UserInterface/Models/QueryMatch.js: Renamed from Source/WebInspectorUI/UserInterface/Models/ResourceQueryMatch.js. 34 35 `ResourceQueryMatch` doesn't contain any resource-specific logic. It can be generalized to `QueryMatch`. 36 37 * UserInterface/Models/QueryResult.js: Copied from Source/WebInspectorUI/UserInterface/Models/ResourceQueryResult.js. 38 (WI.QueryResult): 39 (WI.QueryResult.prototype.get value): 40 (WI.QueryResult.prototype.get rank): 41 (WI.QueryResult.prototype.get matchingTextRanges): 42 (WI.QueryResult.prototype._calculateRank.getMultiplier): 43 (WI.QueryResult.prototype._calculateRank): 44 (WI.QueryResult.prototype._createMatchingTextRanges): 45 46 A generic `QueryResult` can be extracted from `ResourceQueryResult` containing 47 the reusable logic for ranking results and identifing matching text ranges. 48 49 * UserInterface/Models/ResourceQueryResult.js: 50 (WI.ResourceQueryResult): 51 (WI.ResourceQueryResult.prototype.get resource): 52 (WI.ResourceQueryResult.prototype.__test_createMatchesMask): 53 (WI.ResourceQueryResult.prototype.get rank): Deleted. 54 (WI.ResourceQueryResult.prototype.get matchingTextRanges): Deleted. 55 (WI.ResourceQueryResult.prototype._calculateRank.getMultiplier): Deleted. 56 (WI.ResourceQueryResult.prototype._calculateRank): Deleted. 57 (WI.ResourceQueryResult.prototype._createMatchingTextRanges): Deleted. 58 59 `ResourceQueryResult` extends `QueryResult` with resource-specifc members: 60 - the `cookie` property which holds the optional line and column info used when jumping to matched files 61 - the `resource` property which maps to the generic `QueryResult.value`; this is used in tests and when sorting in `ResourceQueryController` 62 63 * UserInterface/Test.html: 64 1 65 2021-11-10 Tim Nguyen <ntim@apple.com> 2 66 -
trunk/Source/WebInspectorUI/UserInterface/Controllers/ResourceQueryController.js
r238023 r285711 24 24 */ 25 25 26 WI.ResourceQueryController = class ResourceQueryController extends WI. Object26 WI.ResourceQueryController = class ResourceQueryController extends WI.QueryController 27 27 { 28 28 constructor() … … 74 74 } 75 75 76 let matches = this. _findQueryMatches(query, cachedData.searchString, cachedData.specialCharacterIndices);76 let matches = this.findQueryMatches(query, cachedData.searchString, cachedData.specialCharacterIndices); 77 77 if (matches.length) 78 78 results.push(new WI.ResourceQueryResult(resource, matches, cookie)); … … 89 89 90 90 // Private 91 92 _findQueryMatches(query, searchString, specialCharacterIndices)93 {94 if (query.length > searchString.length)95 return [];96 97 let matches = [];98 let queryIndex = 0;99 let searchIndex = 0;100 let specialIndex = 0;101 let deadBranches = new Array(query.length).fill(Infinity);102 let type = WI.ResourceQueryMatch.Type.Special;103 104 function pushMatch(index)105 {106 matches.push(new WI.ResourceQueryMatch(type, index, queryIndex));107 searchIndex = index + 1;108 queryIndex++;109 }110 111 function matchNextSpecialCharacter()112 {113 if (specialIndex >= specialCharacterIndices.length)114 return false;115 116 let originalSpecialIndex = specialIndex;117 while (specialIndex < specialCharacterIndices.length) {118 // Normal character matching can move past special characters,119 // so advance the special character index if it's before the120 // current search string position.121 let index = specialCharacterIndices[specialIndex++];122 if (index < searchIndex)123 continue;124 125 if (query[queryIndex] === searchString[index]) {126 pushMatch(index);127 return true;128 }129 }130 131 specialIndex = originalSpecialIndex;132 return false;133 }134 135 function backtrack()136 {137 while (matches.length) {138 queryIndex--;139 140 let lastMatch = matches.pop();141 if (lastMatch.type !== WI.ResourceQueryMatch.Type.Special)142 continue;143 144 deadBranches[lastMatch.queryIndex] = lastMatch.index;145 searchIndex = matches.lastValue ? matches.lastValue.index + 1 : 0;146 return true;147 }148 149 return false;150 }151 152 while (queryIndex < query.length && searchIndex <= searchString.length) {153 if (type === WI.ResourceQueryMatch.Type.Special && !matchNextSpecialCharacter())154 type = WI.ResourceQueryMatch.Type.Normal;155 156 if (type === WI.ResourceQueryMatch.Type.Normal) {157 let index = searchString.indexOf(query[queryIndex], searchIndex);158 if (index >= 0 && index < deadBranches[queryIndex]) {159 pushMatch(index);160 type = WI.ResourceQueryMatch.Type.Special;161 } else if (!backtrack())162 return [];163 }164 }165 166 if (queryIndex < query.length)167 return [];168 169 return matches;170 }171 91 172 92 _findSpecialCharacterIndices(string) -
trunk/Source/WebInspectorUI/UserInterface/Main.html
r283276 r285711 469 469 <script src="Models/PropertyPath.js"></script> 470 470 <script src="Models/PropertyPreview.js"></script> 471 <script src="Models/QueryMatch.js"></script> 472 <script src="Models/QueryResult.js"></script> 471 473 <script src="Models/Recording.js"></script> 472 474 <script src="Models/RecordingAction.js"></script> … … 477 479 <script src="Models/RenderingFrameTimelineRecord.js"></script> 478 480 <script src="Models/ResourceCollection.js"></script> 479 <script src="Models/ResourceQueryMatch.js"></script>480 481 <script src="Models/ResourceQueryResult.js"></script> 481 482 <script src="Models/ResourceTimelineRecord.js"></script> … … 888 889 <script src="Controllers/Annotator.js"></script> 889 890 <script src="Controllers/CodeMirrorEditingController.js"></script> 891 <script src="Controllers/QueryController.js"></script> 890 892 891 893 <script src="Controllers/AnimationManager.js"></script> -
trunk/Source/WebInspectorUI/UserInterface/Models/QueryMatch.js
r285710 r285711 1 1 /* 2 * Copyright (C) 20 16Apple Inc. All rights reserved.2 * Copyright (C) 2021 Apple Inc. All rights reserved. 3 3 * 4 4 * Redistribution and use in source and binary forms, with or without … … 24 24 */ 25 25 26 WI. ResourceQueryMatch = class ResourceQueryMatch26 WI.QueryMatch = class QueryMatch 27 27 { 28 28 constructor(type, index, queryIndex) 29 29 { 30 console.assert(Object.values(WI.QueryMatch.Type).includes(type), type); 31 console.assert(index >= 0, index); 32 console.assert(queryIndex >= 0, queryIndex); 30 33 this._type = type; 31 34 this._index = index; … … 40 43 }; 41 44 42 WI. ResourceQueryMatch.Type = {45 WI.QueryMatch.Type = { 43 46 Normal: Symbol("normal"), 44 Special: Symbol("special") 47 Special: Symbol("special"), 45 48 }; -
trunk/Source/WebInspectorUI/UserInterface/Models/QueryResult.js
r285710 r285711 1 1 /* 2 * Copyright (C) 20 16Apple Inc. All rights reserved.2 * Copyright (C) 2021 Apple Inc. All rights reserved. 3 3 * 4 4 * Redistribution and use in source and binary forms, with or without … … 24 24 */ 25 25 26 WI. ResourceQueryResult = class ResourceQueryResult26 WI.QueryResult = class QueryResult 27 27 { 28 constructor( resource, matches, cookie)28 constructor(value, matches) 29 29 { 30 30 console.assert(matches.length, "Query matches list can't be empty."); 31 31 32 this._ resource = resource;32 this._value = value; 33 33 this._matches = matches; 34 this._cookie = cookie || null;35 34 } 36 35 37 36 // Public 38 37 39 get resource() { return this._resource; } 40 get cookie() { return this._cookie; } 38 get value() { return this._value; } 41 39 42 40 get rank() … … 50 48 get matchingTextRanges() 51 49 { 52 if (!this._matchingTextRanges) 53 this._matchingTextRanges = this._createMatchingTextRanges(); 50 this._matchingTextRanges ??= this._createMatchingTextRanges(); 54 51 55 52 return this._matchingTextRanges; … … 65 62 66 63 function getMultiplier(match) { 67 if (match.type === WI. ResourceQueryMatch.Type.Special)64 if (match.type === WI.QueryMatch.Type.Special) 68 65 return specialMultiplier; 69 66 … … 127 124 return ranges; 128 125 } 129 130 // Testing131 132 __test_createMatchesMask()133 {134 let filename = this._resource.displayName;135 let lastIndex = -1;136 let result = "";137 138 for (let match of this._matches) {139 let gap = " ".repeat(match.index - lastIndex - 1);140 result += gap;141 result += filename[match.index];142 lastIndex = match.index;143 }144 145 return result;146 }147 126 }; -
trunk/Source/WebInspectorUI/UserInterface/Models/ResourceQueryResult.js
r220119 r285711 24 24 */ 25 25 26 WI.ResourceQueryResult = class ResourceQueryResult 26 WI.ResourceQueryResult = class ResourceQueryResult extends WI.QueryResult 27 27 { 28 28 constructor(resource, matches, cookie) 29 29 { 30 console.assert(matches.length, "Query matches list can't be empty."); 30 console.assert(resource instanceof WI.Resource, resource); 31 super(resource, matches); 31 32 32 this._resource = resource;33 this._matches = matches;34 33 this._cookie = cookie || null; 35 34 } … … 37 36 // Public 38 37 39 get resource() { return this. _resource; }38 get resource() { return this.value; } 40 39 get cookie() { return this._cookie; } 41 42 get rank()43 {44 if (this._rank === undefined)45 this._calculateRank();46 47 return this._rank;48 }49 50 get matchingTextRanges()51 {52 if (!this._matchingTextRanges)53 this._matchingTextRanges = this._createMatchingTextRanges();54 55 return this._matchingTextRanges;56 }57 58 // Private59 60 _calculateRank()61 {62 const normalWeight = 10;63 const consecutiveWeight = 5;64 const specialMultiplier = 5;65 66 function getMultiplier(match) {67 if (match.type === WI.ResourceQueryMatch.Type.Special)68 return specialMultiplier;69 70 return 1;71 }72 73 this._rank = 0;74 75 let previousMatch = null;76 let consecutiveMatchStart = null;77 for (let match of this._matches) {78 this._rank += normalWeight * getMultiplier(match);79 80 let consecutive = previousMatch && previousMatch.index === match.index - 1;81 if (consecutive) {82 if (!consecutiveMatchStart)83 consecutiveMatchStart = previousMatch;84 85 // If the first match in this consecutive series was a special character, give a86 // bonus (more likely to match a specific word in the text). Otherwise, multiply87 // by the current length of the consecutive sequence (gives priority to fewer88 // longer sequences instead of more short sequences).89 this._rank += consecutiveWeight * getMultiplier(consecutiveMatchStart) * (match.index - consecutiveMatchStart.index);90 } else if (consecutiveMatchStart)91 consecutiveMatchStart = null;92 93 previousMatch = match;94 95 // The match index is deducted from the total rank, so matches that occur closer to96 // the beginning of the string are ranked higher. Increase the amount subtracted if97 // the match is special, so as to favor matches towards the beginning of the string.98 if (!consecutive)99 this._rank -= match.index * getMultiplier(match);100 }101 }102 103 _createMatchingTextRanges()104 {105 if (!this._matches.length)106 return [];107 108 let ranges = [];109 let startIndex = this._matches[0].index;110 let endIndex = startIndex;111 for (let i = 1; i < this._matches.length; ++i) {112 let match = this._matches[i];113 114 // Increment endIndex for consecutive match.115 if (match.index === endIndex + 1) {116 endIndex++;117 continue;118 }119 120 // Begin a new range when a gap between this match and the previous match is found.121 ranges.push(new WI.TextRange(0, startIndex, 0, endIndex + 1));122 startIndex = match.index;123 endIndex = startIndex;124 }125 126 ranges.push(new WI.TextRange(0, startIndex, 0, endIndex + 1));127 return ranges;128 }129 40 130 41 // Testing … … 132 43 __test_createMatchesMask() 133 44 { 134 let filename = this. _resource.displayName;45 let filename = this.resource.displayName; 135 46 let lastIndex = -1; 136 47 let result = ""; -
trunk/Source/WebInspectorUI/UserInterface/Test.html
r276680 r285711 197 197 <script src="Models/PropertyDescriptor.js"></script> 198 198 <script src="Models/PropertyPreview.js"></script> 199 <script src="Models/QueryMatch.js"></script> 200 <script src="Models/QueryResult.js"></script> 199 201 <script src="Models/Recording.js"></script> 200 202 <script src="Models/RecordingAction.js"></script> … … 205 207 <script src="Models/RenderingFrameTimelineRecord.js"></script> 206 208 <script src="Models/ResourceCollection.js"></script> 207 <script src="Models/ResourceQueryMatch.js"></script>208 209 <script src="Models/ResourceQueryResult.js"></script> 209 210 <script src="Models/ResourceTimelineRecord.js"></script> … … 244 245 <script src="Proxies/HeapSnapshotProxy.js"></script> 245 246 <script src="Proxies/HeapSnapshotWorkerProxy.js"></script> 247 248 <script src="Controllers/QueryController.js"></script> 246 249 247 250 <script src="Controllers/AnimationManager.js"></script>
Note:
See TracChangeset
for help on using the changeset viewer.