summaryrefslogtreecommitdiffstats
path: root/devtools/client/debugger/src/workers/search
diff options
context:
space:
mode:
Diffstat (limited to 'devtools/client/debugger/src/workers/search')
-rw-r--r--devtools/client/debugger/src/workers/search/get-matches.js45
-rw-r--r--devtools/client/debugger/src/workers/search/index.js17
-rw-r--r--devtools/client/debugger/src/workers/search/moz.build10
-rw-r--r--devtools/client/debugger/src/workers/search/project-search.js70
-rw-r--r--devtools/client/debugger/src/workers/search/tests/get-matches.spec.js99
-rw-r--r--devtools/client/debugger/src/workers/search/worker.js9
6 files changed, 250 insertions, 0 deletions
diff --git a/devtools/client/debugger/src/workers/search/get-matches.js b/devtools/client/debugger/src/workers/search/get-matches.js
new file mode 100644
index 0000000000..972dea6818
--- /dev/null
+++ b/devtools/client/debugger/src/workers/search/get-matches.js
@@ -0,0 +1,45 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */
+
+import assert from "../../utils/assert";
+import buildQuery from "../../utils/build-query";
+
+export default function getMatches(query, text, options) {
+ if (!query || !text || !options) {
+ return [];
+ }
+ const regexQuery = buildQuery(query, options, {
+ isGlobal: true,
+ });
+ const matchedLocations = [];
+ const lines = text.split("\n");
+ for (let i = 0; i < lines.length; i++) {
+ let singleMatch;
+ const line = lines[i];
+ while ((singleMatch = regexQuery.exec(line)) !== null) {
+ // Flow doesn't understand the test above.
+ if (!singleMatch) {
+ throw new Error("no singleMatch");
+ }
+
+ matchedLocations.push({
+ line: i,
+ ch: singleMatch.index,
+ match: singleMatch[0],
+ });
+
+ // When the match is an empty string the regexQuery.lastIndex will not
+ // change resulting in an infinite loop so we need to check for this and
+ // increment it manually in that case. See issue #7023
+ if (singleMatch[0] === "") {
+ assert(
+ !regexQuery.unicode,
+ "lastIndex++ can cause issues in unicode mode"
+ );
+ regexQuery.lastIndex++;
+ }
+ }
+ }
+ return matchedLocations;
+}
diff --git a/devtools/client/debugger/src/workers/search/index.js b/devtools/client/debugger/src/workers/search/index.js
new file mode 100644
index 0000000000..928bbf50e6
--- /dev/null
+++ b/devtools/client/debugger/src/workers/search/index.js
@@ -0,0 +1,17 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */
+
+import { WorkerDispatcher } from "devtools/client/shared/worker-utils";
+
+const WORKER_URL = "resource://devtools/client/debugger/dist/search-worker.js";
+
+export class SearchDispatcher extends WorkerDispatcher {
+ constructor(jestUrl) {
+ super(jestUrl || WORKER_URL);
+ }
+
+ getMatches = this.task("getMatches");
+
+ findSourceMatches = this.task("findSourceMatches");
+}
diff --git a/devtools/client/debugger/src/workers/search/moz.build b/devtools/client/debugger/src/workers/search/moz.build
new file mode 100644
index 0000000000..b7223ac81a
--- /dev/null
+++ b/devtools/client/debugger/src/workers/search/moz.build
@@ -0,0 +1,10 @@
+# vim: set filetype=python:
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+DIRS += []
+
+CompiledModules(
+ "index.js",
+)
diff --git a/devtools/client/debugger/src/workers/search/project-search.js b/devtools/client/debugger/src/workers/search/project-search.js
new file mode 100644
index 0000000000..f3751b57c4
--- /dev/null
+++ b/devtools/client/debugger/src/workers/search/project-search.js
@@ -0,0 +1,70 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */
+
+// Maybe reuse file search's functions?
+
+import getMatches from "./get-matches";
+
+export function findSourceMatches(content, queryText, options) {
+ if (queryText == "") {
+ return [];
+ }
+
+ const text = content.value;
+ const lines = text.split("\n");
+
+ return getMatches(queryText, text, options).map(({ line, ch, match }) => {
+ const { value, matchIndex } = truncateLine(lines[line], ch);
+ return {
+ line: line + 1,
+ column: ch,
+
+ matchIndex,
+ match,
+ value,
+ };
+ });
+}
+
+// This is used to find start of a word, so that cropped string look nice
+const startRegex = /([ !@#$%^&*()_+\-=\[\]{};':"\\|,.<>\/?])/g;
+// Similarly, find
+const endRegex = new RegExp(
+ [
+ "([ !@#$%^&*()_+-=[]{};':\"\\|,.<>/?])",
+ '[^ !@#$%^&*()_+-=[]{};\':"\\|,.<>/?]*$"/',
+ ].join("")
+);
+// For texts over 100 characters this truncates the text (for display)
+// around the context of the matched text.
+function truncateLine(text, column) {
+ if (text.length < 100) {
+ return {
+ matchIndex: column,
+ value: text,
+ };
+ }
+
+ // Initially take 40 chars left to the match
+ const offset = Math.max(column - 40, 0);
+ // 400 characters should be enough to figure out the context of the match
+ const truncStr = text.slice(offset, column + 400);
+ let start = truncStr.search(startRegex);
+ let end = truncStr.search(endRegex);
+
+ if (start > column) {
+ // No word separator found before the match, so we take all characters
+ // before the match
+ start = -1;
+ }
+ if (end < column) {
+ end = truncStr.length;
+ }
+ const value = truncStr.slice(start + 1, end);
+
+ return {
+ matchIndex: column - start - offset - 1,
+ value,
+ };
+}
diff --git a/devtools/client/debugger/src/workers/search/tests/get-matches.spec.js b/devtools/client/debugger/src/workers/search/tests/get-matches.spec.js
new file mode 100644
index 0000000000..30b0ce316d
--- /dev/null
+++ b/devtools/client/debugger/src/workers/search/tests/get-matches.spec.js
@@ -0,0 +1,99 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */
+
+import getMatches from "../get-matches";
+
+describe("search", () => {
+ describe("getMatches", () => {
+ it("gets basic string match", () => {
+ const text = "the test string with test in it multiple times test.";
+ const query = "test";
+ const matchLocations = getMatches(query, text, {
+ caseSensitive: true,
+ wholeWord: false,
+ regexMatch: false,
+ });
+ expect(matchLocations).toHaveLength(3);
+ });
+
+ it("gets basic string match case-sensitive", () => {
+ const text = "the Test string with test in it multiple times test.";
+ const query = "Test";
+ const matchLocations = getMatches(query, text, {
+ caseSensitive: true,
+ wholeWord: false,
+ regexMatch: false,
+ });
+ expect(matchLocations).toHaveLength(1);
+ });
+
+ it("gets whole word string match", () => {
+ const text = "the test string test in it multiple times whoatestthe.";
+ const query = "test";
+ const matchLocations = getMatches(query, text, {
+ caseSensitive: true,
+ wholeWord: true,
+ regexMatch: false,
+ });
+ expect(matchLocations).toHaveLength(2);
+ });
+
+ it("gets regex match", () => {
+ const text = "the test string test in it multiple times whoatestthe.";
+ const query = "(\\w+)\\s+(\\w+)";
+ const matchLocations = getMatches(query, text, {
+ caseSensitive: true,
+ wholeWord: false,
+ regexMatch: true,
+ });
+ expect(matchLocations).toHaveLength(4);
+ });
+
+ it("doesnt fail on empty data", () => {
+ const text = "";
+ const query = "";
+ const matchLocations = getMatches(query, text, {
+ caseSensitive: true,
+ wholeWord: false,
+ regexMatch: true,
+ });
+ expect(matchLocations).toHaveLength(0);
+ });
+
+ it("fails gracefully when the line is too long", () => {
+ const text = Array(100002).join("x");
+ const query = "query";
+ const matchLocations = getMatches(query, text, {
+ caseSensitive: true,
+ wholeWord: false,
+ regexMatch: true,
+ });
+ expect(matchLocations).toHaveLength(0);
+ });
+
+ // regression test for #6896
+ it("doesn't crash on the regex 'a*'", () => {
+ const text = "abc";
+ const query = "a*";
+ const matchLocations = getMatches(query, text, {
+ caseSensitive: true,
+ wholeWord: false,
+ regexMatch: true,
+ });
+ expect(matchLocations).toHaveLength(4);
+ });
+
+ // regression test for #6896
+ it("doesn't crash on the regex '^'", () => {
+ const text = "012";
+ const query = "^";
+ const matchLocations = getMatches(query, text, {
+ caseSensitive: true,
+ wholeWord: false,
+ regexMatch: true,
+ });
+ expect(matchLocations).toHaveLength(1);
+ });
+ });
+});
diff --git a/devtools/client/debugger/src/workers/search/worker.js b/devtools/client/debugger/src/workers/search/worker.js
new file mode 100644
index 0000000000..b452697516
--- /dev/null
+++ b/devtools/client/debugger/src/workers/search/worker.js
@@ -0,0 +1,9 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */
+
+import getMatches from "./get-matches";
+import { findSourceMatches } from "./project-search";
+import { workerHandler } from "../../../../shared/worker-utils";
+
+self.onmessage = workerHandler({ getMatches, findSourceMatches });