code-gauge 1.14.0 → 3.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (54) hide show
  1. package/README.md +97 -108
  2. package/dist/cli.cjs +3 -3
  3. package/dist/cli.cjs.map +1 -1
  4. package/dist/cli.js +3 -3
  5. package/dist/cli.js.map +1 -1
  6. package/dist/cliConfig.cjs +1 -1
  7. package/dist/cliConfig.cjs.map +1 -1
  8. package/dist/cliConfig.d.ts +16 -84
  9. package/dist/cliConfig.js +1 -1
  10. package/dist/cliConfig.js.map +1 -1
  11. package/dist/crossFileDuplication.cjs +1 -1
  12. package/dist/crossFileDuplication.cjs.map +1 -1
  13. package/dist/crossFileDuplication.d.ts +24 -10
  14. package/dist/crossFileDuplication.js +1 -1
  15. package/dist/crossFileDuplication.js.map +1 -1
  16. package/dist/duplication.cjs +1 -1
  17. package/dist/duplication.cjs.map +1 -1
  18. package/dist/duplication.d.ts +153 -4
  19. package/dist/duplication.js +1 -1
  20. package/dist/duplication.js.map +1 -1
  21. package/dist/index.cjs +1 -1
  22. package/dist/index.d.ts +3 -3
  23. package/dist/index.js +1 -1
  24. package/dist/languages.cjs +1 -1
  25. package/dist/languages.cjs.map +1 -1
  26. package/dist/languages.js +1 -1
  27. package/dist/languages.js.map +1 -1
  28. package/dist/metrics.cjs +1 -2
  29. package/dist/metrics.cjs.map +1 -1
  30. package/dist/metrics.d.ts +9 -4
  31. package/dist/metrics.js +1 -2
  32. package/dist/metrics.js.map +1 -1
  33. package/dist/nativeMetrics.cjs +1 -1
  34. package/dist/nativeMetrics.cjs.map +1 -1
  35. package/dist/nativeMetrics.d.ts +2 -2
  36. package/dist/nativeMetrics.js +1 -1
  37. package/dist/nativeMetrics.js.map +1 -1
  38. package/dist/ncss.cjs +1 -1
  39. package/dist/ncss.cjs.map +1 -1
  40. package/dist/ncss.d.ts +11 -5
  41. package/dist/ncss.js +1 -1
  42. package/dist/ncss.js.map +1 -1
  43. package/dist/types.d.ts +14 -79
  44. package/package.json +7 -5
  45. package/dist/architectureMetrics.cjs +0 -2
  46. package/dist/architectureMetrics.cjs.map +0 -1
  47. package/dist/architectureMetrics.d.ts +0 -41
  48. package/dist/architectureMetrics.js +0 -2
  49. package/dist/architectureMetrics.js.map +0 -1
  50. package/dist/typescriptProject.cjs +0 -3
  51. package/dist/typescriptProject.cjs.map +0 -1
  52. package/dist/typescriptProject.d.ts +0 -24
  53. package/dist/typescriptProject.js +0 -3
  54. package/dist/typescriptProject.js.map +0 -1
@@ -1,16 +1,101 @@
1
1
  import type Parser from 'tree-sitter';
2
2
  import type { DuplicationMetrics, DuplicationOptions } from './types.js';
3
3
  export declare const defaultDuplicationOptions: Required<DuplicationOptions>;
4
+ export interface Token {
5
+ /** Normalization target: identifiers to anonymize, literal kind tags, or the raw token text. */
6
+ kind: 'id' | 'text';
7
+ text: string;
8
+ /**
9
+ * Two INDEPENDENT hashes of `text` (djb2 and FNV-1a), precomputed so fingerprinting nested
10
+ * regions never re-hashes a token. Feeding the same per-token hash to both fingerprint
11
+ * accumulators would collapse the key to 32 effective bits: one djb2 collision between two
12
+ * token texts would then equate whole regions.
13
+ */
14
+ textHash: number;
15
+ textHash2: number;
16
+ /** Hash pair of a value-carrying literal's value, folded into data-like region fingerprints. */
17
+ literalHash?: number;
18
+ literalHash2?: number;
19
+ /**
20
+ * True for verbatim-kept NAMES (member/callee/type names, named grammar leaves): together with
21
+ * value-carrying literals these are the content-bearing tokens the near-miss content gate
22
+ * counts. Keywords, operators, and punctuation come from unnamed nodes and stay false.
23
+ */
24
+ isName?: boolean;
25
+ /** 0-based source rows the token occupies, so line coverage counts only matched-token lines. */
26
+ startRow: number;
27
+ endRow: number;
28
+ }
29
+ /** A token span with its source position; plain data so cross-file matching can retain it. */
30
+ export interface TokenRange {
31
+ startTokenIndex: number;
32
+ endTokenIndex: number;
33
+ startIndex: number;
34
+ endIndex: number;
35
+ startLine: number;
36
+ endLine: number;
37
+ }
38
+ /** A contiguous run of matched tokens; gapped (merged) duplicates carry several per occurrence. */
39
+ interface TokenSegment {
40
+ startTokenIndex: number;
41
+ endTokenIndex: number;
42
+ }
43
+ interface DuplicateCandidate {
44
+ fingerprint: string;
45
+ tokenCount: number;
46
+ startTokenIndex: number;
47
+ endTokenIndex: number;
48
+ startIndex: number;
49
+ endIndex: number;
50
+ startLine: number;
51
+ endLine: number;
52
+ }
53
+ export interface CountedOccurrence {
54
+ /** Matched token runs; more than one once gapped groups are merged. */
55
+ segments: TokenSegment[];
56
+ /**
57
+ * Set on a retained group's occurrences that a partial gapped merge also paired into a merged
58
+ * group: their spans are counted there, so block counting must not count them again.
59
+ */
60
+ sharedWithMergedGroup?: boolean;
61
+ /** Sum of segment token counts (the gap tokens are not matched content). */
62
+ tokenCount: number;
63
+ startTokenIndex: number;
64
+ endTokenIndex: number;
65
+ startIndex: number;
66
+ endIndex: number;
67
+ startLine: number;
68
+ endLine: number;
69
+ }
4
70
  /** A duplicate region found in one file, exported for cross-file matching by fingerprint. */
5
71
  export interface CrossFileDuplicateCandidate {
6
72
  /** Content key: equal fingerprints mean equal normalized token sequences (up to hash collision). */
7
73
  fingerprint: string;
8
74
  tokenCount: number;
75
+ /** Token positions within the owning file, for cross-file gapped (Type-3) merging. */
76
+ startTokenIndex: number;
77
+ endTokenIndex: number;
9
78
  startIndex: number;
10
79
  endIndex: number;
11
80
  startLine: number;
12
81
  endLine: number;
13
82
  }
83
+ /**
84
+ * One file's contribution to cross-file clone detection: catalogued candidates plus the normalized
85
+ * token stream and statement structure, so the project-level pass can match partial statement runs
86
+ * (windows that a single file cannot know repeat elsewhere) and merge gap-adjacent groups.
87
+ */
88
+ export interface CrossFileDuplicationFileData {
89
+ candidates: CrossFileDuplicateCandidate[];
90
+ tokens: Token[];
91
+ containerStatements: TokenRange[][];
92
+ /**
93
+ * 1-based lines that are neither blank nor comment-only, so cross-file line coverage counts only
94
+ * code lines (blank rows inside multi-row tokens such as template literals carry no content).
95
+ * Optional for backward compatibility; without it, every matched-token row counts.
96
+ */
97
+ codeLineNumbers?: Set<number>;
98
+ }
14
99
  /**
15
100
  * Detects copy-pasted regions within a file. Regions are compared by their normalized token
16
101
  * sequence: identifiers are anonymized consistently by first-occurrence order (`a.f(a, b)` matches
@@ -25,9 +110,73 @@ export interface CrossFileDuplicateCandidate {
25
110
  */
26
111
  export declare function measureDuplication(root: Parser.SyntaxNode, codeLineNumbers: Set<number>, options?: DuplicationOptions): DuplicationMetrics;
27
112
  /**
28
- * Collects this file's duplicate-candidate fingerprints for cross-file clone detection: whole
29
- * block-like subtrees plus each statement container's full run (so wholly copied files and class
30
- * bodies match even when no inner block clears the threshold on its own). Nested and overlapping
113
+ * Collects this file's contribution to cross-file clone detection: fingerprinted candidates for
114
+ * whole block-like subtrees plus each statement container's full run (so wholly copied files and
115
+ * class bodies match even when no inner block clears the threshold on its own), together with the
116
+ * normalized token stream and statement structure that let measureCrossFileDuplication match
117
+ * partial statement runs and merge gap-adjacent groups project-wide. Nested and overlapping
31
118
  * candidates are all returned; the project-level selection keeps only maximal ones.
32
119
  */
33
- export declare function collectCrossFileDuplicateCandidates(root: Parser.SyntaxNode, options?: DuplicationOptions): CrossFileDuplicateCandidate[];
120
+ export declare function collectCrossFileDuplicateCandidates(root: Parser.SyntaxNode, options?: DuplicationOptions): CrossFileDuplicationFileData;
121
+ /** literalCountPrefix[i] = value-carrying literal tokens in tokens[0..i), for O(1) density checks. */
122
+ export declare function buildLiteralCountPrefix(tokens: Token[]): Int32Array;
123
+ /** One file's token stream and statement containers, as a window-matching context. */
124
+ export interface SequenceWindowContext {
125
+ tokens: Token[];
126
+ literalCountPrefix: Int32Array;
127
+ containers: TokenRange[][];
128
+ }
129
+ export interface ContextualSequenceCandidate {
130
+ candidate: DuplicateCandidate;
131
+ contextIndex: number;
132
+ }
133
+ /**
134
+ * Enumerates runs of consecutive sibling statements over one or more contexts (files). Every
135
+ * container statement participates; only the window length is capped, so enumeration stays linear
136
+ * in the statement count. Windows are grouped by a cheap rolling hash of per-statement
137
+ * fingerprints, and only locally maximal repeated windows — those whose one-statement extensions
138
+ * stop repeating — become candidates with an exact (window-consistent) fingerprint. Without the
139
+ * maximality filter a degenerate file of near-identical statements would fingerprint every
140
+ * sub-window of every repeated region. With `requireMultipleContexts` a window only counts as
141
+ * repeated when its occurrences span at least two contexts (CPD-style cross-file matching): a
142
+ * repeat confined to one file is that file's own concern, and emitting it here would flood the
143
+ * project-level selection with unusable single-file groups.
144
+ */
145
+ export declare function collectSequenceWindowCandidates(contexts: SequenceWindowContext[], minTokens: number, requireMultipleContexts: boolean): ContextualSequenceCandidate[];
146
+ /**
147
+ * Merges duplicate groups separated by a small token gap into one gapped (Type-3) clone group: a
148
+ * copy edited in one spot splits into two exact groups whose occurrences sit side by side in the
149
+ * same order. Occurrences are paired greedily in source order; a merge happens when the pairing
150
+ * fully consumes at least one group with at least two pairs. Equal-cardinality groups whose
151
+ * occurrences all pair merge into one group as before. When cardinalities differ (a fragment also
152
+ * occurs standalone: prefix ×3, suffix ×2), the fully-paired group is subsumed into the merged
153
+ * gapped group while the other group is RETAINED with ALL its occurrences: dropping the leftover
154
+ * would lose duplicated-line coverage, and reporting it alone would make a single-occurrence group
155
+ * (contradicting duplicateBlockGroupCount's "appears more than once" meaning). Line coverage
156
+ * unions ranges, so the overlap between the retained exact group and the merged group is harmless.
157
+ * Merging repeats to a fixpoint so a clone edited in several spots still reassembles; it
158
+ * terminates because a full merge shrinks the group count and a partial merge keeps it while
159
+ * strictly growing the bounded total span of merged occurrences. Gap tokens are not matched
160
+ * content: line coverage and sizes count only the matched segments. Generic so cross-file merging
161
+ * can thread file identity through occurrences.
162
+ */
163
+ export declare function mergeAdjacentGroups<T extends CountedOccurrence>(groups: T[][], maxGapTokens: number): T[][];
164
+ /**
165
+ * Redundant copies one group adds to duplicateBlockCount. Each redundant occurrence contributes
166
+ * one count per matched fragment, so merging a gapped clone's fragments into one group does not
167
+ * halve duplicateBlockCount: an edited two-fragment pair still counts 2, exactly as its unmerged
168
+ * fragments did. Occurrence shapes can differ within one group (a
169
+ * gap-merged exact pair plus an appended whole-block near-miss copy), so every occurrence's
170
+ * fragments are summed and one representative — the largest — is deducted, keeping the count
171
+ * independent of source order. Occurrences a partial gapped merge also paired into a merged group
172
+ * are skipped: their spans are already counted there, and such a retained group deducts no
173
+ * representative of its own — the merged group's representative already stands for the shared
174
+ * content — so no token span contributes to the count twice.
175
+ */
176
+ export declare function countRedundantFragments(group: CountedOccurrence[]): number;
177
+ /** Adds the 1-based code lines the segment's matched tokens cover; shared with cross-file coverage. */
178
+ export declare function collectSegmentLines(segment: {
179
+ startTokenIndex: number;
180
+ endTokenIndex: number;
181
+ }, tokens: Token[], codeLineNumbers: Set<number> | undefined, duplicatedLines: Set<number>): void;
182
+ export {};
@@ -1,2 +1,2 @@
1
- import{dedupeByRegion as e,selectMaximalGroups as t}from"./duplicateSelection.js";const n=new Set(`statement_block.block.compound_statement.body_statement.constructor_body.do_block.if_statement.for_statement.for_in_statement.enhanced_for_statement.for_range_loop.while_statement.do_statement.try_statement.try_with_resources_statement.with_statement.switch_statement.switch_expression.switch_case.switch_block_statement_group.switch_rule.case_clause.case_statement.match_statement.match_arm.except_clause.catch_clause.finally_clause.elif_clause.ensure.expression_statement.return_statement.return_expression.if_expression.for_expression.while_expression.loop_expression.match_expression.jsx_element.jsx_self_closing_element.if.unless.case.case_match.while.until.for.begin.when`.split(`.`)),r=new Set([`program`,`source_file`,`translation_unit`,`module`,`statement_block`,`block`,`compound_statement`,`body_statement`,`constructor_body`,`class_body`,`block_body`,`do_block`,`do`,`ensure`,`then`,`else`,`case_statement`,`switch_block_statement_group`,`switch_rule`,`expression_case`,`type_case`,`communication_case`,`default_case`]),i=new Set([`identifier`,`constant`,`instance_variable`,`class_variable`,`global_variable`]),a=new Set([`shorthand_property_identifier`,`shorthand_property_identifier_pattern`]),o=new Map([[`number`,`#num`],[`number_literal`,`#num`],[`integer`,`#num`],[`float`,`#num`],[`integer_literal`,`#num`],[`float_literal`,`#num`],[`int_literal`,`#num`],[`rune_literal`,`#char`],[`imaginary_literal`,`#num`],[`decimal_integer_literal`,`#num`],[`hex_integer_literal`,`#num`],[`octal_integer_literal`,`#num`],[`binary_integer_literal`,`#num`],[`decimal_floating_point_literal`,`#num`],[`hex_floating_point_literal`,`#num`],[`string_fragment`,`#str`],[`multiline_string_fragment`,`#str`],[`string_content`,`#str`],[`raw_string_content`,`#str`],[`heredoc_content`,`#str`],[`heredoc_beginning`,`#heredoc`],[`heredoc_end`,`#heredoc`],[`string`,`#str`],[`template_string`,`#str`],[`string_literal`,`#str`],[`interpreted_string_literal`,`#str`],[`raw_string_literal`,`#str`],[`raw_string`,`#str`],[`escape_sequence`,`#str`],[`char_literal`,`#char`],[`character_literal`,`#char`],[`character`,`#char`],[`regex_pattern`,`#regex`]]),s=new Set([`#num`,`#str`,`#char`,`#regex`]),c=new Set([`comment`,`line_comment`,`block_comment`]),l=new Set([`string_fragment`,`multiline_string_fragment`,`string_content`,`raw_string_content`,`escape_sequence`,`heredoc_content`]),u=new Set([`string_fragment`,`multiline_string_fragment`,`string_content`,`raw_string_content`,`escape_sequence`,`heredoc_content`,`string_start`,`string_end`]),d=new Map([[`call_expression`,`function`],[`method_invocation`,`name`],[`call`,`method`],[`attribute`,`attribute`],[`macro_invocation`,`macro`],[`field_access`,`field`],[`new_expression`,`constructor`],[`keyword_argument`,`name`],[`element_value_pair`,`key`],[`generic_function`,`function`],[`template_function`,`name`]]),f={minTokens:40,maxGapTokens:30,minSimilarityPercent:70};function p(e,t){return e*5>=t}function m(e,n,r){let i=r?.minTokens??f.minTokens,a=r?.maxGapTokens??f.maxGapTokens,o=r?.minSimilarityPercent??f.minSimilarityPercent,s=[],c=[],l=[];g(e,s,c,l);let u=S(s),d=[...w(s,u,c,i),...te(s,u,l,i)],p=Z(X(t(d,e=>e.length>=2)),a),m=D(s,u,c,i,o,p);return ne([...p.filter(e=>e.length>0),...m],n,s)}function h(t,n){let r=n?.minTokens??f.minTokens,i=[],a=[],o=[];g(t,i,a,o);let s=S(i),c=w(i,s,a,r);for(let e of o){let t=e[0],n=e.at(-1);!t||!n||n.endTokenIndex-t.startTokenIndex<r||c.push(z(`s:${W(i,s,t.startTokenIndex,n.endTokenIndex)}`,t.startTokenIndex,n.endTokenIndex,t.node,n.node))}return e(c).map(({fingerprint:e,tokenCount:t,startIndex:n,endIndex:r,startLine:i,endLine:a})=>({fingerprint:e,tokenCount:t,startIndex:n,endIndex:r,startLine:i,endLine:a}))}function g(e,t,i,a){function o(e){let s=t.length,l=e.childCount===0?void 0:ee(e);if(e.childCount===0)_(e,t);else if(l!==void 0)t.push(v(l,y(e,l),e.startPosition.row,e.endPosition.row));else if(!c.has(e.type)){let t=[],n=e.isNamed&&r.has(e.type);for(let r of e.children){let e=o(r);n&&r.isNamed&&!c.has(r.type)&&t.push(e)}n&&t.length>0&&a.push(t)}let u={startTokenIndex:s,endTokenIndex:t.length,node:e};return e.isNamed&&n.has(e.type)&&i.push(u),u}o(e)}function ee(e){let t=e.isNamed?o.get(e.type):void 0;if(t!==void 0)return e.namedChildren.every(e=>u.has(e.type))?t:void 0}function _(e,t){if(c.has(e.type))return;let n=e.startPosition.row,r=e.endPosition.row;if(e.isNamed&&a.has(e.type)){t.push(v(e.text,void 0,n,r,!0),v(`:`,void 0,n,r),{kind:`id`,text:e.text,textHash:0,textHash2:0,startRow:n,endRow:r});return}if(e.isNamed&&i.has(e.type)&&!C(e)){t.push({kind:`id`,text:e.text,textHash:0,textHash2:0,startRow:n,endRow:r});return}let s=e.isNamed?o.get(e.type):void 0;s===void 0?t.push(v(e.text,void 0,n,r,e.isNamed)):t.push(v(s,y(e,s),n,r))}function v(e,t,n,r,i=!1){let a={kind:`text`,text:e,textHash:q(e),textHash2:J(e),startRow:n,endRow:r};return t!==void 0&&s.has(e)&&(a.literalHash=q(t),a.literalHash2=J(t)),i&&(a.isName=!0),a}function y(e,t){if(t!==`#str`&&t!==`#char`||l.has(e.type))return e.text;let n=e.namedChildren.filter(e=>l.has(e.type));return n.length>0?n.map(e=>e.text).join(``):x(e.text)}const b=new Set([`"`,`'`,"`"]);function x(e){let t=e[0];return e.length>=2&&t!==void 0&&b.has(t)&&e.endsWith(t)?e.slice(1,-1):e}function S(e){let t=new Int32Array(e.length+1);for(let[n,r]of e.entries())t[n+1]=(t[n]??0)+(r.literalHash===void 0?0:1);return t}function C(e){let t=e.parent;if(!t)return!1;if(t.type===`method_reference`||t.type===`call`&&t.childForFieldName(`function`)?.id===e.id||e.type===`constant`&&t.type===`call`&&t.childForFieldName(`receiver`)?.id===e.id||t.type===`method_invocation`&&t.childForFieldName(`object`)?.id===e.id&&/^\p{Lu}/u.test(e.text))return!0;if((t.type===`scoped_identifier`||t.type===`qualified_identifier`)&&(t.childForFieldName(`name`)?.id===e.id||t.childForFieldName(`path`)?.id===e.id)){let e=t;for(;e.parent&&(e.parent.type===`scoped_identifier`||e.parent.type===`qualified_identifier`||e.parent.type===`generic_function`||e.parent.type===`template_function`);)e=e.parent;if(e.parent?.type===`call_expression`&&e.parent.childForFieldName(`function`)?.id===e.id)return!0}if(t.type===`literal_element`&&t.parent?.type===`keyed_element`&&t.parent.namedChild(0)?.id===t.id)return!0;let n=d.get(t.type);return n!==void 0&&t.childForFieldName(n)?.id===e.id}function w(e,t,n,r){let i=[];for(let a of n)a.endTokenIndex-a.startTokenIndex<r||i.push(z(`b:${W(e,t,a.startTokenIndex,a.endTokenIndex)}`,a.startTokenIndex,a.endTokenIndex,a.node,a.node));return i}function te(e,t,n,r){let i=[],a=new Map,o=n.map(t=>E(e,t,r));for(let[e,t]of o.entries())for(let[n,r]of t.windowKeysByStart.entries())for(let t of r){if(t===void 0)continue;let r=a.get(t);r?(r.count+=1,r.containerIndex!==e&&(r.containerIndex=-1),r.minStart=Math.min(r.minStart,n),r.maxStart=Math.max(r.maxStart,n)):a.set(t,{count:1,containerIndex:e,minStart:n,maxStart:n})}let s=(e,t)=>{if(e===void 0)return!1;let n=a.get(e);return n!==void 0&&n.count>=2&&(n.containerIndex===-1||n.maxStart-n.minStart>=t)},c=e=>{let t=o[e.containerIndex]?.statementHashes??[],n=t[e.start];for(let r=e.start+1;r<e.start+e.length;r+=1)if(t[r]!==n)return!0;return!1},l=[];for(let[e,t]of o.entries())for(let[n,r]of t.windowKeysByStart.entries())for(let[i,a]of r.entries()){if(!s(a,i)||!c({containerIndex:e,start:n,length:i}))continue;let r=t.windowKeysByStart[n]?.[i+1],o=t.windowKeysByStart[n-1]?.[i+1];s(r,i+1)||s(o,i+1)||l.push({containerIndex:e,start:n,length:i})}let u=new Set(l.map(T)),d=l;for(;d.length>0;){let r=[];for(let a of d){let o=n[a.containerIndex],s=o?.[a.start],c=o?.[a.start+a.length-1];if(!s||!c)continue;let l=`s:${W(e,t,s.startTokenIndex,c.endTokenIndex)}`;i.push(z(l,s.startTokenIndex,c.endTokenIndex,s.node,c.node)),r.push(a)}d=[];for(let e of r)for(let t of[e.start,e.start+1]){let n={containerIndex:e.containerIndex,start:t,length:e.length-1},r=o[e.containerIndex]?.windowKeysByStart[t]?.[n.length];u.has(T(n))||!s(r,n.length)||!c(n)||(u.add(T(n)),d.push(n))}}return i}function T(e){return`${e.containerIndex}:${e.start}:${e.length}`}function E(e,t,n){let r=t.map(t=>G(e,t.startTokenIndex,t.endTokenIndex)),i=[];for(let e=0;e<t.length;e+=1){let a=[],o=5381,s=0,c=Math.min(t.length,e+100);for(let i=e;i<c;i+=1){let c=t[i],l=r[i];if(!c||l===void 0)break;o=Y(o,l),s+=c.endTokenIndex-c.startTokenIndex;let u=i-e+1;a[u]=u>=2&&s>=n?Y(o,u):void 0}i.push(a)}return{windowKeysByStart:i,statementHashes:r}}function D(e,t,n,r,i,a){if(i>=100)return[];let o=O(n.filter(e=>{let n=e.endTokenIndex-e.startTokenIndex,i=(t[e.endTokenIndex]??0)-(t[e.startTokenIndex]??0);return n>=r&&!p(i,n)}).toSorted((e,t)=>e.startTokenIndex-t.startTokenIndex||t.endTokenIndex-e.endTokenIndex));if(o.length<2)return[];let s=o.map(e=>{let t=[];for(let[n,r]of a.entries())r.some(t=>t.startTokenIndex<e.endTokenIndex&&e.startTokenIndex<t.endTokenIndex)&&t.push(n);return t}),c=new Map,l=o.map(t=>N(e,t,c)),u=l.map(({sequence:e})=>F(e)),d=I(u),f=o.map((e,t)=>t),m=e=>{let t=e;for(;f[t]!==t;)t=f[t]??t;for(;f[e]!==t;){let n=f[e]??t;f[e]=t,e=n}return t};for(let[e,t]of d){let n=Math.floor(e/u.length),r=e%u.length,a=l[n],o=l[r],c=u[n],d=u[r];if(!(!a||!o||!c||!d)&&!((s[n]?.length??0)>0&&(s[r]?.length??0)>0)&&!(t*100<10*Math.min(c.size,d.size))&&!(P(a,o)*100<=50*Math.max(a.contentTotal,o.contentTotal))&&L(a.sequence,o.sequence)*100>=i*Math.max(a.sequence.length,o.sequence.length)){let e=m(n),t=m(r);f[Math.max(e,t)]=Math.min(e,t)}}let h=new Map;for(let e of o.keys()){let t=m(e),n=h.get(t)??[];n.push(e),h.set(t,n)}let g=[];for(let e of h.values()){if(e.length<2)continue;let t=e.filter(e=>(s[e]?.length??0)===0),n=e.filter(e=>(s[e]?.length??0)>0);if(n.length===0){g.push(e.flatMap(e=>o[e]?[M(o[e])]:[]));continue}if(t.length===0)continue;let r=t=>e.some(e=>{let n=o[e];return n!==void 0&&t.startTokenIndex<n.endTokenIndex&&n.startTokenIndex<t.endTokenIndex}),i=[...new Set(n.flatMap(e=>s[e]??[]))].filter(e=>{let t=a[e];return t!==void 0&&t.length>0&&t.every(r)}).toSorted((e,t)=>e-t),[c,...l]=i;if(c!==void 0){let t=new Set,n=[];for(let r of e){let e=o[r];if(!e)continue;let c=[];for(let n of i)for(let r of a[n]??[])!t.has(r)&&r.startTokenIndex<e.endTokenIndex&&e.startTokenIndex<r.endTokenIndex&&(t.add(r),c.push({occurrence:r,groupIndex:n}));c.sort((e,t)=>e.occurrence.startTokenIndex-t.occurrence.startTokenIndex||e.occurrence.endTokenIndex-t.occurrence.endTokenIndex);let l=[],u=new Set;for(let{occurrence:e,groupIndex:t}of c)u.has(t)&&(n.push(A(l)),l=[],u.clear()),l.push(e),u.add(t);l.length>0&&n.push(A(l)),c.length===0&&(s[r]?.length??0)===0&&n.push(M(e))}n.sort((e,t)=>e.startTokenIndex-t.startTokenIndex||e.endTokenIndex-t.endTokenIndex),a[c]=n;for(let e of l)a[e]=[]}else t.length>=2&&g.push(t.flatMap(e=>o[e]?[M(o[e])]:[]))}return g.sort(Q),g}function O(e){let t=[],n=[];for(let r of e){for(;n.length>0&&n.at(-1).range.endTokenIndex<=r.startTokenIndex;)n.pop();let e=n.at(-1);if(e&&e.range.startTokenIndex===r.startTokenIndex&&e.range.endTokenIndex===r.endTokenIndex)continue;let i={range:r,children:[]};e?e.children.push(i):t.push(i),n.push(i)}let r=[],i=e=>{if(k(e))for(let t of e.children)i(t);else r.push(e.range)};for(let e of t)i(e);return r}function k(e){let[t]=e.children;return e.children.length>=2||t!==void 0&&k(t)}function A(e){let t=e[0];return!t||e.length===1?t??j():{segments:e.flatMap(e=>e.segments).toSorted((e,t)=>e.startTokenIndex-t.startTokenIndex),tokenCount:e.reduce((e,t)=>e+t.tokenCount,0),startTokenIndex:Math.min(...e.map(e=>e.startTokenIndex)),endTokenIndex:Math.max(...e.map(e=>e.endTokenIndex)),startIndex:Math.min(...e.map(e=>e.startIndex)),endIndex:Math.max(...e.map(e=>e.endIndex)),startLine:Math.min(...e.map(e=>e.startLine)),endLine:Math.max(...e.map(e=>e.endLine))}}function j(){return{segments:[],tokenCount:0,startTokenIndex:0,endTokenIndex:0,startIndex:0,endIndex:0,startLine:0,endLine:0}}function M(e){return{segments:[{startTokenIndex:e.startTokenIndex,endTokenIndex:e.endTokenIndex}],tokenCount:e.endTokenIndex-e.startTokenIndex,startTokenIndex:e.startTokenIndex,endTokenIndex:e.endTokenIndex,startIndex:e.node.startIndex,endIndex:e.node.endIndex,startLine:e.node.startPosition.row+1,endLine:e.node.endPosition.row+1}}function N(e,t,n){let r=new Int32Array(t.endTokenIndex-t.startTokenIndex),i=new Map,a=new Map,o=0;for(let s=t.startTokenIndex;s<t.endTokenIndex;s+=1){let c=e[s];if(!c)continue;let l;if(c.kind===`id`){let e=i.get(c.text);e===void 0&&(e=i.size,i.set(c.text,e)),l=-(e+1)}else{let e=`${c.textHash}:${c.textHash2}:${c.literalHash??0}:${c.literalHash2??0}`,t=n.get(e);t===void 0&&(t=n.size,n.set(e,t)),l=t,(c.isName===!0||c.literalHash!==void 0)&&(a.set(l,(a.get(l)??0)+1),o+=1)}r[s-t.startTokenIndex]=l}return{sequence:r,contentCountBySymbol:a,contentTotal:o}}function P(e,t){let[n,r]=e.contentCountBySymbol.size<=t.contentCountBySymbol.size?[e,t]:[t,e],i=0;for(let[e,t]of n.contentCountBySymbol)i+=Math.min(t,r.contentCountBySymbol.get(e)??0);return i}function F(e){let t=new Set;for(let n=0;n+5<=e.length;n+=1){let r=5381;for(let t=0;t<5;t+=1)r=Math.imul(r,31)+(e[n+t]??0)|0;t.add(r)}return t}function I(e){let t=new Map;for(let[n,r]of e.entries())for(let e of r){let r=t.get(e);r||(r=[],t.set(e,r)),r.push(n)}let n=new Map;for(let r of t.values())for(let t=0;t<r.length;t+=1){let i=r[t]??0;for(let a=t+1;a<r.length;a+=1){let t=i*e.length+(r[a]??0);n.set(t,(n.get(t)??0)+1)}}return n}function L(e,t){let n=e.length+31>>>5,r=new Map;for(let[t,i]of e.entries()){let e=r.get(i);e||(e=new Uint32Array(n),r.set(i,e));let a=t>>>5;e[a]=(e[a]??0)|1<<(t&31)}let i=new Uint32Array(n);for(let e of t){let t=r.get(e),a=1,o=0;for(let e=0;e<n;e+=1){let n=i[e]??0,r=((t?.[e]??0)|n)>>>0,s=(n<<1|a)>>>0;a=n>>>31;let c=r-s-o;o=+(c<0),i[e]=r&~c}}let a=0;for(let e of i)a+=R(e);return a}function R(e){let t=e-(e>>>1&1431655765);return t=(t&858993459)+(t>>>2&858993459),Math.imul(t+(t>>>4)&252645135,16843009)>>>24&255}function z(e,t,n,r,i){return{fingerprint:e,tokenCount:n-t,startTokenIndex:t,endTokenIndex:n,startIndex:r.startIndex,endIndex:i.endIndex,startLine:r.startPosition.row+1,endLine:i.endPosition.row+1}}const B=[],V=[];function H(e){let t=B[e];return t===void 0&&(t=q(`$${e}`),B[e]=t),t}function U(e){let t=V[e];return t===void 0&&(t=J(`$${e}`),V[e]=t),t}function W(e,t,n,r){let[i,a]=K(e,n,r,p((t[r]??0)-(t[n]??0),r-n));return`${i}:${a}:${r-n}`}function G(e,t,n){let[r,i]=K(e,t,n,!1);return r^Math.imul(i,31)}function K(e,t,n,r){let i=new Map,a=5381,o=52711;for(let s=t;s<n;s+=1){let t=e[s];if(!t)continue;let n,c;if(t.kind===`id`){let e=i.get(t.text);e===void 0&&(e=i.size,i.set(t.text,e)),n=H(e),c=U(e)}else n=t.textHash,c=t.textHash2;a=Math.imul(a,31)+n|0,o=Math.imul(o,37)^c,r&&t.literalHash!==void 0&&t.literalHash2!==void 0&&(a=Math.imul(a,31)+t.literalHash|0,o=Math.imul(o,37)^t.literalHash2)}return[a,o]}function q(e){let t=5381;for(let n=0;n<e.length;n+=1)t=Math.imul(t,33)^e.charCodeAt(n);return t}function J(e){let t=-2128831035;for(let n=0;n<e.length;n+=1)t=Math.imul(t^e.charCodeAt(n),16777619);return t}function Y(e,t){return Math.imul(e,31)+t}function X(e){let t=[];for(let n of e.values()){let e=n.map(e=>({segments:[{startTokenIndex:e.startTokenIndex,endTokenIndex:e.endTokenIndex}],tokenCount:e.tokenCount,startTokenIndex:e.startTokenIndex,endTokenIndex:e.endTokenIndex,startIndex:e.startIndex,endIndex:e.endIndex,startLine:e.startLine,endLine:e.endLine}));e.sort((e,t)=>e.startTokenIndex-t.startTokenIndex||e.endTokenIndex-t.endTokenIndex),t.push(e)}return t}function Z(e,t){if(t<=0||e.length<2)return e;e.sort(Q);for(let n=!0;n;){n=!1;for(let r=0;r<e.length&&!n;r+=1)for(let i=r+1;i<e.length;i+=1){let a=e[r],o=e[i];if(!a||!o)continue;let s=$(a,o,t)??$(o,a,t);if(s){e[r]=s,e.splice(i,1),e.sort(Q),n=!0;break}}}return e}function Q(e,t){let n=e[0],r=t[0];return(n?.startTokenIndex??0)-(r?.startTokenIndex??0)||(n?.endTokenIndex??0)-(r?.endTokenIndex??0)}function $(e,t,n){if(e.length===t.length){for(let[r,i]of e.entries()){let a=t[r];if(!a)return;let o=a.startTokenIndex-i.endTokenIndex;if(o<0||o>n)return;let s=e[r+1];if(s&&a.endTokenIndex>s.startTokenIndex)return}return e.map((e,n)=>{let r=t[n];return r?{segments:[...e.segments,...r.segments],tokenCount:e.tokenCount+r.tokenCount,startTokenIndex:e.startTokenIndex,endTokenIndex:r.endTokenIndex,startIndex:e.startIndex,endIndex:r.endIndex,startLine:e.startLine,endLine:r.endLine}:e})}}function ne(e,t,n){let r=0,i=0,a=[],o=new Set;for(let s of e){let e=s.map(e=>e.segments.length);r+=e.reduce((e,t)=>e+t,0)-Math.max(...e,0);for(let e of s){i=Math.max(i,e.tokenCount);for(let r of e.segments)for(let e=r.startTokenIndex;e<r.endTokenIndex;e+=1){let r=n[e];for(let e=r?.startRow??0;e<=(r?.endRow??-1);e+=1)t.has(e+1)&&o.add(e+1)}}a.push(s.map(({startLine:e,endLine:t})=>({startLine:e,endLine:t})).toSorted((e,t)=>e.startLine-t.startLine))}return a.sort((e,t)=>(e[0]?.startLine??0)-(t[0]?.startLine??0)),{duplicateBlockCount:r,duplicateBlockGroupCount:e.length,duplicateBlockGroups:a,duplicateLineCount:o.size,duplicationRatio:t.size===0?0:o.size/t.size,maxDuplicateBlockSize:i}}export{h as collectCrossFileDuplicateCandidates,f as defaultDuplicationOptions,m as measureDuplication};
1
+ import{dedupeByRegion as e,selectMaximalGroups as t}from"./duplicateSelection.js";const n=new Set(`statement_block.block.compound_statement.body_statement.constructor_body.do_block.if_statement.for_statement.for_in_statement.enhanced_for_statement.for_range_loop.while_statement.do_statement.try_statement.try_with_resources_statement.with_statement.switch_statement.switch_expression.switch_case.switch_block_statement_group.switch_rule.case_clause.case_statement.match_statement.match_arm.except_clause.catch_clause.finally_clause.elif_clause.ensure.expression_statement.return_statement.return_expression.if_expression.for_expression.while_expression.loop_expression.match_expression.jsx_element.jsx_self_closing_element.if.unless.case.case_match.while.until.for.begin.when`.split(`.`)),r=new Set([`program`,`source_file`,`translation_unit`,`module`,`statement_block`,`block`,`compound_statement`,`body_statement`,`constructor_body`,`class_body`,`block_body`,`do_block`,`do`,`ensure`,`then`,`else`,`case_statement`,`switch_block_statement_group`,`switch_rule`,`expression_case`,`type_case`,`communication_case`,`default_case`]),i=new Set([`identifier`,`constant`,`instance_variable`,`class_variable`,`global_variable`]),a=new Set([`shorthand_property_identifier`,`shorthand_property_identifier_pattern`]),o=new Map([[`number`,`#num`],[`number_literal`,`#num`],[`integer`,`#num`],[`float`,`#num`],[`integer_literal`,`#num`],[`float_literal`,`#num`],[`int_literal`,`#num`],[`rune_literal`,`#char`],[`imaginary_literal`,`#num`],[`decimal_integer_literal`,`#num`],[`hex_integer_literal`,`#num`],[`octal_integer_literal`,`#num`],[`binary_integer_literal`,`#num`],[`decimal_floating_point_literal`,`#num`],[`hex_floating_point_literal`,`#num`],[`string_fragment`,`#str`],[`multiline_string_fragment`,`#str`],[`string_content`,`#str`],[`raw_string_content`,`#str`],[`heredoc_content`,`#str`],[`heredoc_beginning`,`#heredoc`],[`heredoc_end`,`#heredoc`],[`string`,`#str`],[`template_string`,`#str`],[`string_literal`,`#str`],[`interpreted_string_literal`,`#str`],[`raw_string_literal`,`#str`],[`raw_string`,`#str`],[`escape_sequence`,`#str`],[`char_literal`,`#char`],[`character_literal`,`#char`],[`character`,`#char`],[`regex_pattern`,`#regex`]]),s=new Set([`#num`,`#str`,`#char`,`#regex`]),c=new Set([`comment`,`line_comment`,`block_comment`]),l=new Set([`string_fragment`,`multiline_string_fragment`,`string_content`,`raw_string_content`,`escape_sequence`,`heredoc_content`]),u=new Set([`string_fragment`,`multiline_string_fragment`,`string_content`,`raw_string_content`,`escape_sequence`,`heredoc_content`,`string_start`,`string_end`]),d=new Map([[`call_expression`,`function`],[`method_invocation`,`name`],[`call`,`method`],[`attribute`,`attribute`],[`macro_invocation`,`macro`],[`field_access`,`field`],[`new_expression`,`constructor`],[`keyword_argument`,`name`],[`element_value_pair`,`key`],[`generic_function`,`function`],[`template_function`,`name`]]),f={minTokens:40,maxGapTokens:30,minSimilarityPercent:70};function p(e,t){return e*5>=t}function m(e,n,r){let i=r?.minTokens??f.minTokens,a=r?.maxGapTokens??f.maxGapTokens,o=r?.minSimilarityPercent??f.minSimilarityPercent,s=[],c=[],l=[];g(e,s,c,l);let u=S(s),d=[...w(s,u,c,i),...te(s,u,l,i)],p=Y(J(t(d,e=>e.length>=2)),a),m=O(s,u,c,i,o,p);return ae([...p.filter(e=>e.length>0),...m],n,s)}function h(t,n){let r=n?.minTokens??f.minTokens,i=[],a=[],o=[];g(t,i,a,o);let s=S(i),c=w(i,s,a,r);for(let e of o){let t=e[0],n=e.at(-1);!t||!n||n.endTokenIndex-t.startTokenIndex<r||c.push(L(`s:${H(i,s,t.startTokenIndex,n.endTokenIndex)}`,t.startTokenIndex,n.endTokenIndex,t,n))}return{candidates:e(c),tokens:i,containerStatements:o}}function g(e,t,i,a){function o(e){let s=t.length,l=e.childCount===0?void 0:ee(e);if(e.childCount===0)_(e,t);else if(l!==void 0)t.push(v(l,y(e,l),e.startPosition.row,e.endPosition.row));else if(!c.has(e.type)){let t=[],n=e.isNamed&&r.has(e.type);for(let r of e.children){let e=o(r);n&&r.isNamed&&!c.has(r.type)&&t.push(e)}n&&t.length>0&&a.push(t)}let u={startTokenIndex:s,endTokenIndex:t.length,startIndex:e.startIndex,endIndex:e.endIndex,startLine:e.startPosition.row+1,endLine:e.endPosition.row+1};return e.isNamed&&n.has(e.type)&&i.push(u),u}o(e)}function ee(e){let t=e.isNamed?o.get(e.type):void 0;if(t!==void 0)return e.namedChildren.every(e=>u.has(e.type))?t:void 0}function _(e,t){if(c.has(e.type))return;let n=e.startPosition.row,r=e.endPosition.row;if(e.isNamed&&a.has(e.type)){t.push(v(e.text,void 0,n,r,!0),v(`:`,void 0,n,r),{kind:`id`,text:e.text,textHash:0,textHash2:0,startRow:n,endRow:r});return}if(e.isNamed&&i.has(e.type)&&!C(e)){t.push({kind:`id`,text:e.text,textHash:0,textHash2:0,startRow:n,endRow:r});return}let s=e.isNamed?o.get(e.type):void 0;s===void 0?t.push(v(e.text,void 0,n,r,e.isNamed)):t.push(v(s,y(e,s),n,r))}function v(e,t,n,r,i=!1){let a={kind:`text`,text:e,textHash:G(e),textHash2:K(e),startRow:n,endRow:r};return t!==void 0&&s.has(e)&&(a.literalHash=G(t),a.literalHash2=K(t)),i&&(a.isName=!0),a}function y(e,t){if(t!==`#str`&&t!==`#char`||l.has(e.type))return e.text;let n=e.namedChildren.filter(e=>l.has(e.type));return n.length>0?n.map(e=>e.text).join(``):x(e.text)}const b=new Set([`"`,`'`,"`"]);function x(e){let t=e[0];return e.length>=2&&t!==void 0&&b.has(t)&&e.endsWith(t)?e.slice(1,-1):e}function S(e){let t=new Int32Array(e.length+1);for(let[n,r]of e.entries())t[n+1]=(t[n]??0)+(r.literalHash===void 0?0:1);return t}function C(e){let t=e.parent;if(!t)return!1;if(t.type===`method_reference`||t.type===`call`&&t.childForFieldName(`function`)?.id===e.id||e.type===`constant`&&t.type===`call`&&t.childForFieldName(`receiver`)?.id===e.id||t.type===`method_invocation`&&t.childForFieldName(`object`)?.id===e.id&&/^\p{Lu}/u.test(e.text))return!0;if((t.type===`scoped_identifier`||t.type===`qualified_identifier`)&&(t.childForFieldName(`name`)?.id===e.id||t.childForFieldName(`path`)?.id===e.id)){let e=t;for(;e.parent&&(e.parent.type===`scoped_identifier`||e.parent.type===`qualified_identifier`||e.parent.type===`generic_function`||e.parent.type===`template_function`);)e=e.parent;if(e.parent?.type===`call_expression`&&e.parent.childForFieldName(`function`)?.id===e.id)return!0}if(t.type===`literal_element`&&t.parent?.type===`keyed_element`&&t.parent.namedChild(0)?.id===t.id)return!0;let n=d.get(t.type);return n!==void 0&&t.childForFieldName(n)?.id===e.id}function w(e,t,n,r){let i=[];for(let a of n)a.endTokenIndex-a.startTokenIndex<r||i.push(L(`b:${H(e,t,a.startTokenIndex,a.endTokenIndex)}`,a.startTokenIndex,a.endTokenIndex,a,a));return i}function te(e,t,n,r){return T([{tokens:e,literalCountPrefix:t,containers:n}],r,!1).map(({candidate:e})=>e)}function T(e,t,n){let r=[],i=[],a=[];for(let[t,n]of e.entries())for(let e of n.containers)i.push(t),a.push(e);let o=t=>e[i[t]??0],s=new Map,c=a.map((e,n)=>D(o(n)?.tokens??[],e,t));for(let[e,t]of c.entries()){let n=i[e]??0;for(let[r,i]of t.windowKeysByStart.entries())for(let t of i){if(t===void 0)continue;let i=s.get(t);i?(i.count+=1,i.containerIndex!==e&&(i.containerIndex=-1),i.contextIndex!==n&&(i.contextIndex=-1),i.minStart=Math.min(i.minStart,r),i.maxStart=Math.max(i.maxStart,r)):s.set(t,{count:1,containerIndex:e,contextIndex:n,minStart:r,maxStart:r})}}let l=(e,t)=>{if(e===void 0)return!1;let r=s.get(e);return r===void 0||r.count<2?!1:n?r.contextIndex===-1:r.containerIndex===-1||r.maxStart-r.minStart>=t},u=e=>{let t=c[e.containerIndex]?.statementHashes??[],n=t[e.start];for(let r=e.start+1;r<e.start+e.length;r+=1)if(t[r]!==n)return!0;return!1},d=[];for(let[e,t]of c.entries())for(let[n,r]of t.windowKeysByStart.entries())for(let[i,a]of r.entries()){if(!l(a,i)||!u({containerIndex:e,start:n,length:i}))continue;let r=t.windowKeysByStart[n]?.[i+1],o=t.windowKeysByStart[n-1]?.[i+1];l(r,i+1)||l(o,i+1)||d.push({containerIndex:e,start:n,length:i})}let f=new Set(d.map(E)),p=d;for(;p.length>0;){let e=[];for(let t of p){let n=a[t.containerIndex],s=n?.[t.start],c=n?.[t.start+t.length-1],l=o(t.containerIndex);if(!s||!c||!l)continue;let u=`s:${H(l.tokens,l.literalCountPrefix,s.startTokenIndex,c.endTokenIndex)}`;r.push({candidate:L(u,s.startTokenIndex,c.endTokenIndex,s,c),contextIndex:i[t.containerIndex]??0}),e.push(t)}p=[];for(let t of e)for(let e of[t.start,t.start+1]){let n={containerIndex:t.containerIndex,start:e,length:t.length-1},r=c[t.containerIndex]?.windowKeysByStart[e]?.[n.length];f.has(E(n))||!l(r,n.length)||!u(n)||(f.add(E(n)),p.push(n))}}return r}function E(e){return`${e.containerIndex}:${e.start}:${e.length}`}function D(e,t,n){let r=t.map(t=>U(e,t.startTokenIndex,t.endTokenIndex)),i=[];for(let e=0;e<t.length;e+=1){let a=[],o=5381,s=0,c=Math.min(t.length,e+100);for(let i=e;i<c;i+=1){let c=t[i],l=r[i];if(!c||l===void 0)break;o=q(o,l),s+=c.endTokenIndex-c.startTokenIndex;let u=i-e+1;a[u]=u>=2&&s>=n?q(o,u):void 0}i.push(a)}return{windowKeysByStart:i,statementHashes:r}}function O(e,t,n,r,i,a){if(i>=100)return[];let o=k(n.filter(e=>{let n=e.endTokenIndex-e.startTokenIndex,i=(t[e.endTokenIndex]??0)-(t[e.startTokenIndex]??0);return n>=r&&!p(i,n)}).toSorted((e,t)=>e.startTokenIndex-t.startTokenIndex||t.endTokenIndex-e.endTokenIndex));if(o.length<2)return[];let s=o.map(e=>{let t=[];for(let[n,r]of a.entries())r.some(t=>t.startTokenIndex<e.endTokenIndex&&e.startTokenIndex<t.endTokenIndex)&&t.push(n);return t}),c=new Map,l=o.map(t=>ne(e,t,c)),u=l.map(({sequence:e})=>ie(e)),d=P(u),f=o.map((e,t)=>t),m=e=>{let t=e;for(;f[t]!==t;)t=f[t]??t;for(;f[e]!==t;){let n=f[e]??t;f[e]=t,e=n}return t};for(let[e,t]of d){let n=Math.floor(e/u.length),r=e%u.length,a=l[n],o=l[r],c=u[n],d=u[r];if(!(!a||!o||!c||!d)&&!((s[n]?.length??0)>0&&(s[r]?.length??0)>0)&&!(t*100<10*Math.min(c.size,d.size))&&!(re(a,o)*100<=50*Math.max(a.contentTotal,o.contentTotal))&&F(a.sequence,o.sequence)*100>=i*Math.max(a.sequence.length,o.sequence.length)){let e=m(n),t=m(r);f[Math.max(e,t)]=Math.min(e,t)}}let h=new Map;for(let e of o.keys()){let t=m(e),n=h.get(t)??[];n.push(e),h.set(t,n)}let g=[];for(let e of h.values()){if(e.length<2)continue;let t=e.filter(e=>(s[e]?.length??0)===0),n=e.filter(e=>(s[e]?.length??0)>0);if(n.length===0){g.push(e.flatMap(e=>o[e]?[N(o[e])]:[]));continue}if(t.length===0)continue;let r=t=>e.some(e=>{let n=o[e];return n!==void 0&&t.startTokenIndex<n.endTokenIndex&&n.startTokenIndex<t.endTokenIndex}),i=[...new Set(n.flatMap(e=>s[e]??[]))].filter(e=>{let t=a[e];return t!==void 0&&t.length>0&&t.every(r)}).toSorted((e,t)=>e-t),[c,...l]=i;if(c!==void 0){let t=new Set,n=[];for(let r of e){let e=o[r];if(!e)continue;let c=[];for(let n of i)for(let r of a[n]??[])!t.has(r)&&r.startTokenIndex<e.endTokenIndex&&e.startTokenIndex<r.endTokenIndex&&(t.add(r),c.push({occurrence:r,groupIndex:n}));c.sort((e,t)=>e.occurrence.startTokenIndex-t.occurrence.startTokenIndex||e.occurrence.endTokenIndex-t.occurrence.endTokenIndex);let l=[],u=new Set;for(let{occurrence:e,groupIndex:t}of c)u.has(t)&&(n.push(j(l)),l=[],u.clear()),l.push(e),u.add(t);l.length>0&&n.push(j(l)),c.length===0&&(s[r]?.length??0)===0&&n.push(N(e))}n.sort((e,t)=>e.startTokenIndex-t.startTokenIndex||e.endTokenIndex-t.endTokenIndex);for(let e of n)e.sharedWithMergedGroup=void 0;a[c]=n;for(let e of l)a[e]=[]}else t.length>=2&&g.push(t.flatMap(e=>o[e]?[N(o[e])]:[]))}return g.sort(X),g}function k(e){let t=[],n=[];for(let r of e){for(;n.length>0&&n.at(-1).range.endTokenIndex<=r.startTokenIndex;)n.pop();let e=n.at(-1);if(e&&e.range.startTokenIndex===r.startTokenIndex&&e.range.endTokenIndex===r.endTokenIndex)continue;let i={range:r,children:[]};e?e.children.push(i):t.push(i),n.push(i)}let r=[],i=e=>{if(A(e))for(let t of e.children)i(t);else r.push(e.range)};for(let e of t)i(e);return r}function A(e){let[t]=e.children;return e.children.length>=2||t!==void 0&&A(t)}function j(e){let t=e[0];if(!t||e.length===1)return t??M();let n=[];for(let t of e.flatMap(e=>e.segments).toSorted((e,t)=>e.startTokenIndex-t.startTokenIndex||e.endTokenIndex-t.endTokenIndex)){let e=n.at(-1);e&&t.startTokenIndex<e.endTokenIndex?e.endTokenIndex=Math.max(e.endTokenIndex,t.endTokenIndex):n.push({...t})}return{segments:n,tokenCount:n.reduce((e,t)=>e+t.endTokenIndex-t.startTokenIndex,0),startTokenIndex:Math.min(...e.map(e=>e.startTokenIndex)),endTokenIndex:Math.max(...e.map(e=>e.endTokenIndex)),startIndex:Math.min(...e.map(e=>e.startIndex)),endIndex:Math.max(...e.map(e=>e.endIndex)),startLine:Math.min(...e.map(e=>e.startLine)),endLine:Math.max(...e.map(e=>e.endLine))}}function M(){return{segments:[],tokenCount:0,startTokenIndex:0,endTokenIndex:0,startIndex:0,endIndex:0,startLine:0,endLine:0}}function N(e){return{segments:[{startTokenIndex:e.startTokenIndex,endTokenIndex:e.endTokenIndex}],tokenCount:e.endTokenIndex-e.startTokenIndex,startTokenIndex:e.startTokenIndex,endTokenIndex:e.endTokenIndex,startIndex:e.startIndex,endIndex:e.endIndex,startLine:e.startLine,endLine:e.endLine}}function ne(e,t,n){let r=new Int32Array(t.endTokenIndex-t.startTokenIndex),i=new Map,a=new Map,o=0;for(let s=t.startTokenIndex;s<t.endTokenIndex;s+=1){let c=e[s];if(!c)continue;let l;if(c.kind===`id`){let e=i.get(c.text);e===void 0&&(e=i.size,i.set(c.text,e)),l=-(e+1)}else{let e=`${c.textHash}:${c.textHash2}:${c.literalHash??0}:${c.literalHash2??0}`,t=n.get(e);t===void 0&&(t=n.size,n.set(e,t)),l=t,(c.isName===!0||c.literalHash!==void 0)&&(a.set(l,(a.get(l)??0)+1),o+=1)}r[s-t.startTokenIndex]=l}return{sequence:r,contentCountBySymbol:a,contentTotal:o}}function re(e,t){let[n,r]=e.contentCountBySymbol.size<=t.contentCountBySymbol.size?[e,t]:[t,e],i=0;for(let[e,t]of n.contentCountBySymbol)i+=Math.min(t,r.contentCountBySymbol.get(e)??0);return i}function ie(e){let t=new Set;for(let n=0;n+5<=e.length;n+=1){let r=5381;for(let t=0;t<5;t+=1)r=Math.imul(r,31)+(e[n+t]??0)|0;t.add(r)}return t}function P(e){let t=new Map;for(let[n,r]of e.entries())for(let e of r){let r=t.get(e);r||(r=[],t.set(e,r)),r.push(n)}let n=new Map;for(let r of t.values())for(let t=0;t<r.length;t+=1){let i=r[t]??0;for(let a=t+1;a<r.length;a+=1){let t=i*e.length+(r[a]??0);n.set(t,(n.get(t)??0)+1)}}return n}function F(e,t){let n=e.length+31>>>5,r=new Map;for(let[t,i]of e.entries()){let e=r.get(i);e||(e=new Uint32Array(n),r.set(i,e));let a=t>>>5;e[a]=(e[a]??0)|1<<(t&31)}let i=new Uint32Array(n);for(let e of t){let t=r.get(e),a=1,o=0;for(let e=0;e<n;e+=1){let n=i[e]??0,r=((t?.[e]??0)|n)>>>0,s=(n<<1|a)>>>0;a=n>>>31;let c=r-s-o;o=+(c<0),i[e]=r&~c}}let a=0;for(let e of i)a+=I(e);return a}function I(e){let t=e-(e>>>1&1431655765);return t=(t&858993459)+(t>>>2&858993459),Math.imul(t+(t>>>4)&252645135,16843009)>>>24&255}function L(e,t,n,r,i){return{fingerprint:e,tokenCount:n-t,startTokenIndex:t,endTokenIndex:n,startIndex:r.startIndex,endIndex:i.endIndex,startLine:r.startLine,endLine:i.endLine}}const R=[],z=[];function B(e){let t=R[e];return t===void 0&&(t=G(`$${e}`),R[e]=t),t}function V(e){let t=z[e];return t===void 0&&(t=K(`$${e}`),z[e]=t),t}function H(e,t,n,r){let[i,a]=W(e,n,r,p((t[r]??0)-(t[n]??0),r-n));return`${i}:${a}:${r-n}`}function U(e,t,n){let[r,i]=W(e,t,n,!1);return r^Math.imul(i,31)}function W(e,t,n,r){let i=new Map,a=5381,o=52711;for(let s=t;s<n;s+=1){let t=e[s];if(!t)continue;let n,c;if(t.kind===`id`){let e=i.get(t.text);e===void 0&&(e=i.size,i.set(t.text,e)),n=B(e),c=V(e)}else n=t.textHash,c=t.textHash2;a=Math.imul(a,31)+n|0,o=Math.imul(o,37)^c,r&&t.literalHash!==void 0&&t.literalHash2!==void 0&&(a=Math.imul(a,31)+t.literalHash|0,o=Math.imul(o,37)^t.literalHash2)}return[a,o]}function G(e){let t=5381;for(let n=0;n<e.length;n+=1)t=Math.imul(t,33)^e.charCodeAt(n);return t}function K(e){let t=-2128831035;for(let n=0;n<e.length;n+=1)t=Math.imul(t^e.charCodeAt(n),16777619);return t}function q(e,t){return Math.imul(e,31)+t}function J(e){let t=[];for(let n of e.values()){let e=n.map(e=>({segments:[{startTokenIndex:e.startTokenIndex,endTokenIndex:e.endTokenIndex}],tokenCount:e.tokenCount,startTokenIndex:e.startTokenIndex,endTokenIndex:e.endTokenIndex,startIndex:e.startIndex,endIndex:e.endIndex,startLine:e.startLine,endLine:e.endLine}));e.sort((e,t)=>e.startTokenIndex-t.startTokenIndex||e.endTokenIndex-t.endTokenIndex),t.push(e)}return t}function Y(e,t){if(t<=0||e.length<2)return e;e.sort(X);for(let n=!0;n;){n=!1;for(let r=0;r<e.length&&!n;r+=1)for(let i=r+1;i<e.length;i+=1){let a=e[r],o=e[i];if(!a||!o)continue;let s=Z(a,o,t),c=s??Z(o,a,t);if(!c)continue;let l=s?c.firstConsumed:c.secondConsumed,u=s?c.secondConsumed:c.firstConsumed;l&&u?(e[r]=c.merged,e.splice(i,1)):u?e[i]=c.merged:e[r]=c.merged;for(let e of c.pairedRetained)e.sharedWithMergedGroup=!0;e.sort(X),n=!0;break}}return e}function X(e,t){let n=e[0],r=t[0];return(n?.startTokenIndex??0)-(r?.startTokenIndex??0)||(n?.endTokenIndex??0)-(r?.endTokenIndex??0)}function Z(e,t,n){let r=e.filter(e=>!e.sharedWithMergedGroup),i=t.filter(e=>!e.sharedWithMergedGroup),a=[],o=0,s=-1;for(let e of i){for(;o<r.length;){let t=r[o];if(t&&t.endTokenIndex+n<e.startTokenIndex)o+=1;else break}let t=r[o];t&&t.endTokenIndex<=e.startTokenIndex&&t.startTokenIndex>=s&&(a.push([t,e]),s=e.endTokenIndex,o+=1)}let c=a.length===e.length,l=a.length===t.length;if(!(a.length<2||!c&&!l))return{merged:a.map(([e,t])=>({...e,sharedWithMergedGroup:void 0,segments:[...e.segments,...t.segments],tokenCount:e.tokenCount+t.tokenCount,endTokenIndex:t.endTokenIndex,endIndex:t.endIndex,endLine:t.endLine})),firstConsumed:c,secondConsumed:l,pairedRetained:c===l?[]:a.map(([e,t])=>c?t:e)}}function Q(e){let t=0,n=0,r=!1;for(let i of e){if(i.sharedWithMergedGroup){r=!0;continue}t+=i.segments.length,n=Math.max(n,i.segments.length)}return r?t:t-n}function ae(e,t,n){let r=0,i=0,a=[],o=new Set;for(let s of e){r+=Q(s);for(let e of s){i=Math.max(i,e.tokenCount);for(let r of e.segments)$(r,n,t,o)}a.push(s.map(({startLine:e,endLine:t})=>({startLine:e,endLine:t})).toSorted((e,t)=>e.startLine-t.startLine))}return a.sort((e,t)=>(e[0]?.startLine??0)-(t[0]?.startLine??0)),{duplicateBlockCount:r,duplicateBlockGroupCount:e.length,duplicateBlockGroups:a,duplicateLineCount:o.size,duplicateLineNumbers:[...o].toSorted((e,t)=>e-t),duplicationRatio:t.size===0?0:o.size/t.size,maxDuplicateBlockSize:i}}function $(e,t,n,r){for(let i=e.startTokenIndex;i<e.endTokenIndex;i+=1){let e=t[i];for(let t=e?.startRow??0;t<=(e?.endRow??-1);t+=1)(!n||n.has(t+1))&&r.add(t+1)}}export{S as buildLiteralCountPrefix,h as collectCrossFileDuplicateCandidates,$ as collectSegmentLines,T as collectSequenceWindowCandidates,Q as countRedundantFragments,f as defaultDuplicationOptions,m as measureDuplication,Y as mergeAdjacentGroups};
2
2
  //# sourceMappingURL=duplication.js.map