OLD | NEW |
1 // Copyright 2014 The Chromium Authors. All rights reserved. | 1 // Copyright 2014 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | |
5 /** | 4 /** |
6 * @constructor | |
7 * @implements {WebInspector.ProjectSearchConfig} | 5 * @implements {WebInspector.ProjectSearchConfig} |
8 * @param {string} query | 6 * @unrestricted |
9 * @param {boolean} ignoreCase | |
10 * @param {boolean} isRegex | |
11 */ | 7 */ |
12 WebInspector.SearchConfig = function(query, ignoreCase, isRegex) | 8 WebInspector.SearchConfig = class { |
13 { | 9 /** |
| 10 * @param {string} query |
| 11 * @param {boolean} ignoreCase |
| 12 * @param {boolean} isRegex |
| 13 */ |
| 14 constructor(query, ignoreCase, isRegex) { |
14 this._query = query; | 15 this._query = query; |
15 this._ignoreCase = ignoreCase; | 16 this._ignoreCase = ignoreCase; |
16 this._isRegex = isRegex; | 17 this._isRegex = isRegex; |
17 this._parse(); | 18 this._parse(); |
| 19 } |
| 20 |
| 21 /** |
| 22 * @param {{query: string, ignoreCase: boolean, isRegex: boolean}} object |
| 23 * @return {!WebInspector.SearchConfig} |
| 24 */ |
| 25 static fromPlainObject(object) { |
| 26 return new WebInspector.SearchConfig(object.query, object.ignoreCase, object
.isRegex); |
| 27 } |
| 28 |
| 29 /** |
| 30 * @override |
| 31 * @return {string} |
| 32 */ |
| 33 query() { |
| 34 return this._query; |
| 35 } |
| 36 |
| 37 /** |
| 38 * @override |
| 39 * @return {boolean} |
| 40 */ |
| 41 ignoreCase() { |
| 42 return this._ignoreCase; |
| 43 } |
| 44 |
| 45 /** |
| 46 * @override |
| 47 * @return {boolean} |
| 48 */ |
| 49 isRegex() { |
| 50 return this._isRegex; |
| 51 } |
| 52 |
| 53 /** |
| 54 * @return {{query: string, ignoreCase: boolean, isRegex: boolean}} |
| 55 */ |
| 56 toPlainObject() { |
| 57 return {query: this.query(), ignoreCase: this.ignoreCase(), isRegex: this.is
Regex()}; |
| 58 } |
| 59 |
| 60 _parse() { |
| 61 var filePattern = |
| 62 '-?f(ile)?:(([^\\\\ ]|\\\\.)+)'; // After file: prefix: any symbol exce
pt space and backslash or any symbol escaped with a backslash. |
| 63 var quotedPattern = |
| 64 '"(([^\\\\"]|\\\\.)+)"'; // Inside double quotes: any symbol except dou
ble quote and backslash or any symbol escaped with a backslash. |
| 65 |
| 66 // A word is a sequence of any symbols except space and backslash or any sym
bols escaped with a backslash, that does not start with file:. |
| 67 var unquotedWordPattern = '(\\s*(?!-?f(ile)?:)[^\\\\ ]|\\\\.)+'; |
| 68 var unquotedPattern = |
| 69 unquotedWordPattern + '( +' + unquotedWordPattern + ')*'; // A word or
several words separated by space(s). |
| 70 |
| 71 var pattern = '(' + filePattern + ')|(' + quotedPattern + ')|(' + unquotedPa
ttern + ')'; |
| 72 var regexp = new RegExp(pattern, 'g'); |
| 73 var queryParts = this._query.match(regexp) || []; |
| 74 |
| 75 /** |
| 76 * @type {!Array.<!WebInspector.SearchConfig.QueryTerm>} |
| 77 */ |
| 78 this._fileQueries = []; |
| 79 |
| 80 /** |
| 81 * @type {!Array.<string>} |
| 82 */ |
| 83 this._queries = []; |
| 84 |
| 85 for (var i = 0; i < queryParts.length; ++i) { |
| 86 var queryPart = queryParts[i]; |
| 87 if (!queryPart) |
| 88 continue; |
| 89 var fileQuery = this._parseFileQuery(queryPart); |
| 90 if (fileQuery) { |
| 91 this._fileQueries.push(fileQuery); |
| 92 /** @type {!Array.<!WebInspector.SearchConfig.RegexQuery>} */ |
| 93 this._fileRegexQueries = this._fileRegexQueries || []; |
| 94 this._fileRegexQueries.push( |
| 95 {regex: new RegExp(fileQuery.text, this.ignoreCase ? 'i' : ''), isNe
gative: fileQuery.isNegative}); |
| 96 continue; |
| 97 } |
| 98 if (this._isRegex) { |
| 99 this._queries.push(queryPart); |
| 100 continue; |
| 101 } |
| 102 if (queryPart.startsWith('"')) { |
| 103 if (!queryPart.endsWith('"')) |
| 104 continue; |
| 105 this._queries.push(this._parseQuotedQuery(queryPart)); |
| 106 continue; |
| 107 } |
| 108 this._queries.push(this._parseUnquotedQuery(queryPart)); |
| 109 } |
| 110 } |
| 111 |
| 112 /** |
| 113 * @override |
| 114 * @param {string} filePath |
| 115 * @return {boolean} |
| 116 */ |
| 117 filePathMatchesFileQuery(filePath) { |
| 118 if (!this._fileRegexQueries) |
| 119 return true; |
| 120 for (var i = 0; i < this._fileRegexQueries.length; ++i) { |
| 121 if (!!filePath.match(this._fileRegexQueries[i].regex) === this._fileRegexQ
ueries[i].isNegative) |
| 122 return false; |
| 123 } |
| 124 return true; |
| 125 } |
| 126 |
| 127 /** |
| 128 * @override |
| 129 * @return {!Array.<string>} |
| 130 */ |
| 131 queries() { |
| 132 return this._queries; |
| 133 } |
| 134 |
| 135 _parseUnquotedQuery(query) { |
| 136 return query.replace(/\\(.)/g, '$1'); |
| 137 } |
| 138 |
| 139 _parseQuotedQuery(query) { |
| 140 return query.substring(1, query.length - 1).replace(/\\(.)/g, '$1'); |
| 141 } |
| 142 |
| 143 /** |
| 144 * @param {string} query |
| 145 * @return {?WebInspector.SearchConfig.QueryTerm} |
| 146 */ |
| 147 _parseFileQuery(query) { |
| 148 var match = query.match(/^(-)?f(ile)?:/); |
| 149 if (!match) |
| 150 return null; |
| 151 var isNegative = !!match[1]; |
| 152 query = query.substr(match[0].length); |
| 153 var result = ''; |
| 154 for (var i = 0; i < query.length; ++i) { |
| 155 var char = query[i]; |
| 156 if (char === '*') { |
| 157 result += '.*'; |
| 158 } else if (char === '\\') { |
| 159 ++i; |
| 160 var nextChar = query[i]; |
| 161 if (nextChar === ' ') |
| 162 result += ' '; |
| 163 } else { |
| 164 if (String.regexSpecialCharacters().indexOf(query.charAt(i)) !== -1) |
| 165 result += '\\'; |
| 166 result += query.charAt(i); |
| 167 } |
| 168 } |
| 169 return new WebInspector.SearchConfig.QueryTerm(result, isNegative); |
| 170 } |
18 }; | 171 }; |
19 | 172 |
20 /** @typedef {!{regex: !RegExp, isNegative: boolean}} */ | 173 /** @typedef {!{regex: !RegExp, isNegative: boolean}} */ |
21 WebInspector.SearchConfig.RegexQuery; | 174 WebInspector.SearchConfig.RegexQuery; |
22 | 175 |
23 /** | |
24 * @param {{query: string, ignoreCase: boolean, isRegex: boolean}} object | |
25 * @return {!WebInspector.SearchConfig} | |
26 */ | |
27 WebInspector.SearchConfig.fromPlainObject = function(object) | |
28 { | |
29 return new WebInspector.SearchConfig(object.query, object.ignoreCase, object
.isRegex); | |
30 }; | |
31 | |
32 WebInspector.SearchConfig.prototype = { | |
33 /** | |
34 * @override | |
35 * @return {string} | |
36 */ | |
37 query: function() | |
38 { | |
39 return this._query; | |
40 }, | |
41 | |
42 /** | |
43 * @override | |
44 * @return {boolean} | |
45 */ | |
46 ignoreCase: function() | |
47 { | |
48 return this._ignoreCase; | |
49 }, | |
50 | |
51 /** | |
52 * @override | |
53 * @return {boolean} | |
54 */ | |
55 isRegex: function() | |
56 { | |
57 return this._isRegex; | |
58 }, | |
59 | |
60 /** | |
61 * @return {{query: string, ignoreCase: boolean, isRegex: boolean}} | |
62 */ | |
63 toPlainObject: function() | |
64 { | |
65 return { query: this.query(), ignoreCase: this.ignoreCase(), isRegex: th
is.isRegex() }; | |
66 }, | |
67 | |
68 _parse: function() | |
69 { | |
70 var filePattern = "-?f(ile)?:(([^\\\\ ]|\\\\.)+)"; // After file: prefix
: any symbol except space and backslash or any symbol escaped with a backslash. | |
71 var quotedPattern = "\"(([^\\\\\"]|\\\\.)+)\""; // Inside double quotes:
any symbol except double quote and backslash or any symbol escaped with a backs
lash. | |
72 | |
73 // A word is a sequence of any symbols except space and backslash or any
symbols escaped with a backslash, that does not start with file:. | |
74 var unquotedWordPattern = "(\\s*(?!-?f(ile)?:)[^\\\\ ]|\\\\.)+"; | |
75 var unquotedPattern = unquotedWordPattern + "( +" + unquotedWordPattern
+ ")*"; // A word or several words separated by space(s). | |
76 | |
77 var pattern = "(" + filePattern + ")|(" + quotedPattern + ")|(" + unquot
edPattern + ")"; | |
78 var regexp = new RegExp(pattern, "g"); | |
79 var queryParts = this._query.match(regexp) || []; | |
80 | |
81 /** | |
82 * @type {!Array.<!WebInspector.SearchConfig.QueryTerm>} | |
83 */ | |
84 this._fileQueries = []; | |
85 | |
86 /** | |
87 * @type {!Array.<string>} | |
88 */ | |
89 this._queries = []; | |
90 | |
91 for (var i = 0; i < queryParts.length; ++i) { | |
92 var queryPart = queryParts[i]; | |
93 if (!queryPart) | |
94 continue; | |
95 var fileQuery = this._parseFileQuery(queryPart); | |
96 if (fileQuery) { | |
97 this._fileQueries.push(fileQuery); | |
98 /** @type {!Array.<!WebInspector.SearchConfig.RegexQuery>} */ | |
99 this._fileRegexQueries = this._fileRegexQueries || []; | |
100 this._fileRegexQueries.push({ regex: new RegExp(fileQuery.text,
this.ignoreCase ? "i" : ""), isNegative: fileQuery.isNegative }); | |
101 continue; | |
102 } | |
103 if (this._isRegex) { | |
104 this._queries.push(queryPart); | |
105 continue; | |
106 } | |
107 if (queryPart.startsWith("\"")) { | |
108 if (!queryPart.endsWith("\"")) | |
109 continue; | |
110 this._queries.push(this._parseQuotedQuery(queryPart)); | |
111 continue; | |
112 } | |
113 this._queries.push(this._parseUnquotedQuery(queryPart)); | |
114 } | |
115 }, | |
116 | |
117 /** | |
118 * @override | |
119 * @param {string} filePath | |
120 * @return {boolean} | |
121 */ | |
122 filePathMatchesFileQuery: function(filePath) | |
123 { | |
124 if (!this._fileRegexQueries) | |
125 return true; | |
126 for (var i = 0; i < this._fileRegexQueries.length; ++i) { | |
127 if (!!filePath.match(this._fileRegexQueries[i].regex) === this._file
RegexQueries[i].isNegative) | |
128 return false; | |
129 } | |
130 return true; | |
131 }, | |
132 | |
133 /** | |
134 * @override | |
135 * @return {!Array.<string>} | |
136 */ | |
137 queries: function() | |
138 { | |
139 return this._queries; | |
140 }, | |
141 | |
142 _parseUnquotedQuery: function(query) | |
143 { | |
144 return query.replace(/\\(.)/g, "$1"); | |
145 }, | |
146 | |
147 _parseQuotedQuery: function(query) | |
148 { | |
149 return query.substring(1, query.length - 1).replace(/\\(.)/g, "$1"); | |
150 }, | |
151 | |
152 /** | |
153 * @param {string} query | |
154 * @return {?WebInspector.SearchConfig.QueryTerm} | |
155 */ | |
156 _parseFileQuery: function(query) | |
157 { | |
158 var match = query.match(/^(-)?f(ile)?:/); | |
159 if (!match) | |
160 return null; | |
161 var isNegative = !!match[1]; | |
162 query = query.substr(match[0].length); | |
163 var result = ""; | |
164 for (var i = 0; i < query.length; ++i) { | |
165 var char = query[i]; | |
166 if (char === "*") { | |
167 result += ".*"; | |
168 } else if (char === "\\") { | |
169 ++i; | |
170 var nextChar = query[i]; | |
171 if (nextChar === " ") | |
172 result += " "; | |
173 } else { | |
174 if (String.regexSpecialCharacters().indexOf(query.charAt(i)) !==
-1) | |
175 result += "\\"; | |
176 result += query.charAt(i); | |
177 } | |
178 } | |
179 return new WebInspector.SearchConfig.QueryTerm(result, isNegative); | |
180 } | |
181 }; | |
182 | 176 |
183 /** | 177 /** |
184 * @constructor | 178 * @unrestricted |
185 * @param {string} text | |
186 * @param {boolean} isNegative | |
187 */ | 179 */ |
188 WebInspector.SearchConfig.QueryTerm = function(text, isNegative) | 180 WebInspector.SearchConfig.QueryTerm = class { |
189 { | 181 /** |
| 182 * @param {string} text |
| 183 * @param {boolean} isNegative |
| 184 */ |
| 185 constructor(text, isNegative) { |
190 this.text = text; | 186 this.text = text; |
191 this.isNegative = isNegative; | 187 this.isNegative = isNegative; |
| 188 } |
192 }; | 189 }; |
OLD | NEW |