code-gauge 1.14.0 → 2.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.
@@ -1,16 +1,95 @@
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
+ }
14
93
  /**
15
94
  * Detects copy-pasted regions within a file. Regions are compared by their normalized token
16
95
  * sequence: identifiers are anonymized consistently by first-occurrence order (`a.f(a, b)` matches
@@ -25,9 +104,68 @@ export interface CrossFileDuplicateCandidate {
25
104
  */
26
105
  export declare function measureDuplication(root: Parser.SyntaxNode, codeLineNumbers: Set<number>, options?: DuplicationOptions): DuplicationMetrics;
27
106
  /**
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
107
+ * Collects this file's contribution to cross-file clone detection: fingerprinted candidates for
108
+ * whole block-like subtrees plus each statement container's full run (so wholly copied files and
109
+ * class bodies match even when no inner block clears the threshold on its own), together with the
110
+ * normalized token stream and statement structure that let measureCrossFileDuplication match
111
+ * partial statement runs and merge gap-adjacent groups project-wide. Nested and overlapping
31
112
  * candidates are all returned; the project-level selection keeps only maximal ones.
32
113
  */
33
- export declare function collectCrossFileDuplicateCandidates(root: Parser.SyntaxNode, options?: DuplicationOptions): CrossFileDuplicateCandidate[];
114
+ export declare function collectCrossFileDuplicateCandidates(root: Parser.SyntaxNode, options?: DuplicationOptions): CrossFileDuplicationFileData;
115
+ /** literalCountPrefix[i] = value-carrying literal tokens in tokens[0..i), for O(1) density checks. */
116
+ export declare function buildLiteralCountPrefix(tokens: Token[]): Int32Array;
117
+ /** One file's token stream and statement containers, as a window-matching context. */
118
+ export interface SequenceWindowContext {
119
+ tokens: Token[];
120
+ literalCountPrefix: Int32Array;
121
+ containers: TokenRange[][];
122
+ }
123
+ export interface ContextualSequenceCandidate {
124
+ candidate: DuplicateCandidate;
125
+ contextIndex: number;
126
+ }
127
+ /**
128
+ * Enumerates runs of consecutive sibling statements over one or more contexts (files). Every
129
+ * container statement participates; only the window length is capped, so enumeration stays linear
130
+ * in the statement count. Windows are grouped by a cheap rolling hash of per-statement
131
+ * fingerprints, and only locally maximal repeated windows — those whose one-statement extensions
132
+ * stop repeating — become candidates with an exact (window-consistent) fingerprint. Without the
133
+ * maximality filter a degenerate file of near-identical statements would fingerprint every
134
+ * sub-window of every repeated region. With `requireMultipleContexts` a window only counts as
135
+ * repeated when its occurrences span at least two contexts (CPD-style cross-file matching): a
136
+ * repeat confined to one file is that file's own concern, and emitting it here would flood the
137
+ * project-level selection with unusable single-file groups.
138
+ */
139
+ export declare function collectSequenceWindowCandidates(contexts: SequenceWindowContext[], minTokens: number, requireMultipleContexts: boolean): ContextualSequenceCandidate[];
140
+ /**
141
+ * Merges duplicate groups separated by a small token gap into one gapped (Type-3) clone group: a
142
+ * copy edited in one spot splits into two exact groups whose occurrences sit side by side in the
143
+ * same order. Occurrences are paired greedily in source order; a merge happens when the pairing
144
+ * fully consumes at least one group with at least two pairs. Equal-cardinality groups whose
145
+ * occurrences all pair merge into one group as before. When cardinalities differ (a fragment also
146
+ * occurs standalone: prefix ×3, suffix ×2), the fully-paired group is subsumed into the merged
147
+ * gapped group while the other group is RETAINED with ALL its occurrences: dropping the leftover
148
+ * would lose duplicated-line coverage, and reporting it alone would make a single-occurrence group
149
+ * (contradicting duplicateBlockGroupCount's "appears more than once" meaning). Line coverage
150
+ * unions ranges, so the overlap between the retained exact group and the merged group is harmless.
151
+ * Merging repeats to a fixpoint so a clone edited in several spots still reassembles; it
152
+ * terminates because a full merge shrinks the group count and a partial merge keeps it while
153
+ * strictly growing the bounded total span of merged occurrences. Gap tokens are not matched
154
+ * content: line coverage and sizes count only the matched segments. Generic so cross-file merging
155
+ * can thread file identity through occurrences.
156
+ */
157
+ export declare function mergeAdjacentGroups<T extends CountedOccurrence>(groups: T[][], maxGapTokens: number): T[][];
158
+ /**
159
+ * Redundant copies one group adds to duplicateBlockCount. Each redundant occurrence contributes
160
+ * one count per matched fragment, so merging a gapped clone's fragments into one group does not
161
+ * halve the count a `duplicateBlock` threshold sees: an edited two-fragment pair still counts 2,
162
+ * exactly as its unmerged fragments did. Occurrence shapes can differ within one group (a
163
+ * gap-merged exact pair plus an appended whole-block near-miss copy), so every occurrence's
164
+ * fragments are summed and one representative — the largest — is deducted, keeping the count
165
+ * independent of source order. Occurrences a partial gapped merge also paired into a merged group
166
+ * are skipped: their spans are already counted there, and such a retained group deducts no
167
+ * representative of its own — the merged group's representative already stands for the shared
168
+ * content — so no token span contributes to the count twice.
169
+ */
170
+ export declare function countRedundantFragments(group: CountedOccurrence[]): number;
171
+ 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=C(s),d=[...T(s,u,c,i),...ee(s,u,l,i)],p=X(Y(t(d,e=>e.length>=2)),a),m=k(s,u,c,i,o,p);return ie([...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=C(i),c=T(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(R(`s:${U(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:_(e);if(e.childCount===0)v(e,t);else if(l!==void 0)t.push(y(l,b(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 _(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 v(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(y(e.text,void 0,n,r,!0),y(`:`,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)&&!w(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(y(e.text,void 0,n,r,e.isNamed)):t.push(y(s,b(e,s),n,r))}function y(e,t,n,r,i=!1){let a={kind:`text`,text:e,textHash:K(e),textHash2:q(e),startRow:n,endRow:r};return t!==void 0&&s.has(e)&&(a.literalHash=K(t),a.literalHash2=q(t)),i&&(a.isName=!0),a}function b(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(``):S(e.text)}const x=new Set([`"`,`'`,"`"]);function S(e){let t=e[0];return e.length>=2&&t!==void 0&&x.has(t)&&e.endsWith(t)?e.slice(1,-1):e}function C(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 w(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 T(e,t,n,r){let i=[];for(let a of n)a.endTokenIndex-a.startTokenIndex<r||i.push(R(`b:${U(e,t,a.startTokenIndex,a.endTokenIndex)}`,a.startTokenIndex,a.endTokenIndex,a,a));return i}function ee(e,t,n,r){return E([{tokens:e,literalCountPrefix:t,containers:n}],r,!1).map(({candidate:e})=>e)}function E(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)=>O(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(D)),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:${U(l.tokens,l.literalCountPrefix,s.startTokenIndex,c.endTokenIndex)}`;r.push({candidate:R(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(D(n))||!l(r,n.length)||!u(n)||(f.add(D(n)),p.push(n))}}return r}function D(e){return`${e.containerIndex}:${e.start}:${e.length}`}function O(e,t,n){let r=t.map(t=>W(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=J(o,l),s+=c.endTokenIndex-c.startTokenIndex;let u=i-e+1;a[u]=u>=2&&s>=n?J(o,u):void 0}i.push(a)}return{windowKeysByStart:i,statementHashes:r}}function k(e,t,n,r,i,a){if(i>=100)return[];let o=A(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=>te(e,t,c)),u=l.map(({sequence:e})=>re(e)),d=F(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))&&!(ne(a,o)*100<=50*Math.max(a.contentTotal,o.contentTotal))&&I(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]?[P(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(M(l)),l=[],u.clear()),l.push(e),u.add(t);l.length>0&&n.push(M(l)),c.length===0&&(s[r]?.length??0)===0&&n.push(P(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]?[P(o[e])]:[]))}return g.sort(Z),g}function A(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(j(e))for(let t of e.children)i(t);else r.push(e.range)};for(let e of t)i(e);return r}function j(e){let[t]=e.children;return e.children.length>=2||t!==void 0&&j(t)}function M(e){let t=e[0];if(!t||e.length===1)return t??N();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 N(){return{segments:[],tokenCount:0,startTokenIndex:0,endTokenIndex:0,startIndex:0,endIndex:0,startLine:0,endLine:0}}function P(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 te(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 ne(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 re(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 F(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 I(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+=L(e);return a}function L(e){let t=e-(e>>>1&1431655765);return t=(t&858993459)+(t>>>2&858993459),Math.imul(t+(t>>>4)&252645135,16843009)>>>24&255}function R(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 z=[],B=[];function V(e){let t=z[e];return t===void 0&&(t=K(`$${e}`),z[e]=t),t}function H(e){let t=B[e];return t===void 0&&(t=q(`$${e}`),B[e]=t),t}function U(e,t,n,r){let[i,a]=G(e,n,r,p((t[r]??0)-(t[n]??0),r-n));return`${i}:${a}:${r-n}`}function W(e,t,n){let[r,i]=G(e,t,n,!1);return r^Math.imul(i,31)}function G(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=V(e),c=H(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 K(e){let t=5381;for(let n=0;n<e.length;n+=1)t=Math.imul(t,33)^e.charCodeAt(n);return t}function q(e){let t=-2128831035;for(let n=0;n<e.length;n+=1)t=Math.imul(t^e.charCodeAt(n),16777619);return t}function J(e,t){return Math.imul(e,31)+t}function Y(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 X(e,t){if(t<=0||e.length<2)return e;e.sort(Z);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=Q(a,o,t),c=s??Q(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(Z),n=!0;break}}return e}function Z(e,t){let n=e[0],r=t[0];return(n?.startTokenIndex??0)-(r?.startTokenIndex??0)||(n?.endTokenIndex??0)-(r?.endTokenIndex??0)}function Q(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 $(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 ie(e,t,n){let r=0,i=0,a=[],o=new Set;for(let s of e){r+=$(s);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{C as buildLiteralCountPrefix,h as collectCrossFileDuplicateCandidates,E as collectSequenceWindowCandidates,$ as countRedundantFragments,f as defaultDuplicationOptions,m as measureDuplication,X as mergeAdjacentGroups};
2
2
  //# sourceMappingURL=duplication.js.map