summaryrefslogtreecommitdiffstats
path: root/devtools/client/debugger/src/utils/pause/mapScopes/rangeMetadata.js
blob: 7e04c34e3575b6c47efb183560d7ef31844d6637 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
/* 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 { locColumn } from "./locColumn";
import { positionCmp } from "./positionCmp";
import { filterSortedArray } from "./filtering";

// * match - Range contains a single identifier with matching start location
// * contains - Range contains a single identifier with non-matching start
// * multiple - Range contains multiple identifiers
// * empty - Range contains no identifiers

export async function loadRangeMetadata(
  location,
  originalAstScopes,
  { sourceMapLoader }
) {
  const originalRanges = await sourceMapLoader.getOriginalRanges(
    location.source.id
  );

  const sortedOriginalAstBindings = [];
  for (const item of originalAstScopes) {
    for (const name of Object.keys(item.bindings)) {
      for (const ref of item.bindings[name].refs) {
        sortedOriginalAstBindings.push(ref);
      }
    }
  }
  sortedOriginalAstBindings.sort((a, b) => positionCmp(a.start, b.start));

  let i = 0;

  return originalRanges.map(range => {
    const bindings = [];

    while (
      i < sortedOriginalAstBindings.length &&
      (sortedOriginalAstBindings[i].start.line < range.line ||
        (sortedOriginalAstBindings[i].start.line === range.line &&
          locColumn(sortedOriginalAstBindings[i].start) < range.columnStart))
    ) {
      i++;
    }

    while (
      i < sortedOriginalAstBindings.length &&
      sortedOriginalAstBindings[i].start.line === range.line &&
      locColumn(sortedOriginalAstBindings[i].start) >= range.columnStart &&
      locColumn(sortedOriginalAstBindings[i].start) < range.columnEnd
    ) {
      const lastBinding = bindings[bindings.length - 1];
      // Only add bindings when they're in new positions
      if (
        !lastBinding ||
        positionCmp(lastBinding.start, sortedOriginalAstBindings[i].start) !== 0
      ) {
        bindings.push(sortedOriginalAstBindings[i]);
      }
      i++;
    }

    let type = "empty";
    let singleDeclaration = true;
    if (bindings.length === 1) {
      const binding = bindings[0];
      if (
        binding.start.line === range.line &&
        binding.start.column === range.columnStart
      ) {
        type = "match";
      } else {
        type = "contains";
      }
    } else if (bindings.length > 1) {
      type = "multiple";
      const binding = bindings[0];
      const declStart =
        binding.type !== "ref" ? binding.declaration.start : null;

      singleDeclaration = bindings.every(b => {
        return (
          declStart &&
          b.type !== "ref" &&
          positionCmp(declStart, b.declaration.start) === 0
        );
      });
    }

    return {
      type,
      singleDeclaration,
      ...range,
    };
  });
}

export function findMatchingRange(sortedOriginalRanges, bindingRange) {
  return filterSortedArray(sortedOriginalRanges, range => {
    if (range.line < bindingRange.start.line) {
      return -1;
    }
    if (range.line > bindingRange.start.line) {
      return 1;
    }

    if (range.columnEnd <= locColumn(bindingRange.start)) {
      return -1;
    }
    if (range.columnStart > locColumn(bindingRange.start)) {
      return 1;
    }

    return 0;
  }).pop();
}