tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

project-text-search.js (3913B)


      1 /* This Source Code Form is subject to the terms of the Mozilla Public
      2 * License, v. 2.0. If a copy of the MPL was not distributed with this
      3 * file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */
      4 
      5 /**
      6 * Redux actions for the search state
      7 *
      8 * @module actions/search
      9 */
     10 
     11 import { isFulfilled } from "../utils/async-value";
     12 import {
     13  getFirstSourceActorForGeneratedSource,
     14  getSourceList,
     15  getSettledSourceTextContent,
     16  isSourceBlackBoxed,
     17  getSearchOptions,
     18 } from "../selectors/index";
     19 import { createLocation } from "../utils/location";
     20 import { matchesGlobPatterns } from "../utils/source";
     21 import { loadSourceText } from "./sources/loadSourceText";
     22 import { searchKeys } from "../constants";
     23 
     24 export function searchSources(query, onUpdatedResults, signal) {
     25  return async ({ dispatch, getState, searchWorker }) => {
     26    dispatch({
     27      type: "SET_PROJECT_SEARCH_QUERY",
     28      query,
     29    });
     30 
     31    const searchOptions = getSearchOptions(
     32      getState(),
     33      searchKeys.PROJECT_SEARCH
     34    );
     35    const validSources = getSourceList(getState()).filter(
     36      source =>
     37        !isSourceBlackBoxed(getState(), source) &&
     38        !matchesGlobPatterns(source, searchOptions.excludePatterns)
     39    );
     40    // Sort original entries first so that search results are more useful.
     41    // Deprioritize third-party scripts, so their results show last.
     42    validSources.sort((a, b) => {
     43      function isThirdParty(source) {
     44        return (
     45          source?.url &&
     46          (source.url.includes("node_modules") ||
     47            source.url.includes("bower_components"))
     48        );
     49      }
     50 
     51      if (a.isOriginal && !isThirdParty(a)) {
     52        return -1;
     53      }
     54 
     55      if (b.isOriginal && !isThirdParty(b)) {
     56        return 1;
     57      }
     58 
     59      if (!isThirdParty(a) && isThirdParty(b)) {
     60        return -1;
     61      }
     62      if (isThirdParty(a) && !isThirdParty(b)) {
     63        return 1;
     64      }
     65      return 0;
     66    });
     67    const results = [];
     68    for (const source of validSources) {
     69      const sourceActor = getFirstSourceActorForGeneratedSource(
     70        getState(),
     71        source.id
     72      );
     73      await dispatch(loadSourceText(source, sourceActor));
     74 
     75      // This is the only asynchronous call in this method.
     76      // We may have stopped the search by closing the search panel or changing the query.
     77      // Avoid any further unecessary computation when the React Component tells us the query was cancelled.
     78      if (signal.aborted) {
     79        return;
     80      }
     81 
     82      const result = await searchSource(source, sourceActor, query, {
     83        getState,
     84        searchWorker,
     85      });
     86      if (signal.aborted) {
     87        return;
     88      }
     89 
     90      if (result) {
     91        results.push(result);
     92        onUpdatedResults(results, false, signal);
     93      }
     94    }
     95    onUpdatedResults(results, true, signal);
     96  };
     97 }
     98 
     99 export async function searchSource(
    100  source,
    101  sourceActor,
    102  query,
    103  { getState, searchWorker }
    104 ) {
    105  const state = getState();
    106  const location = createLocation({
    107    source,
    108    sourceActor,
    109  });
    110 
    111  const content = getSettledSourceTextContent(state, location);
    112  let matches = [];
    113 
    114  if (content && isFulfilled(content) && content.value.type === "text") {
    115    const options = getSearchOptions(state, searchKeys.PROJECT_SEARCH);
    116    matches = await searchWorker.findSourceMatches(
    117      content.value,
    118      query,
    119      options
    120    );
    121  }
    122  if (!matches.length) {
    123    return null;
    124  }
    125  return {
    126    type: "RESULT",
    127    location,
    128    // `matches` are generated by project-search worker's `findSourceMatches` method
    129    matches: matches.map(m => ({
    130      type: "MATCH",
    131      location: createLocation({
    132        ...location,
    133        // `matches` only contain line and column
    134        // `location` will already refer to the right source/sourceActor
    135        line: m.line,
    136        column: m.column,
    137      }),
    138      matchIndex: m.matchIndex,
    139      match: m.match,
    140      value: m.value,
    141    })),
    142  };
    143 }