[{"data":1,"prerenderedAt":1599},["ShallowReactive",2],{"navigation_docs":3,"-guides-similarity":244,"-guides-similarity-surround":1594},[4,29,99,119,189],{"title":5,"icon":6,"path":7,"stem":8,"children":9},"Start",false,"\u002Fstart","1.start",[10,14,19,24],{"title":11,"path":7,"stem":12,"icon":13},"What jscpd finds","1.start\u002Findex","i-lucide-house",{"title":15,"path":16,"stem":17,"icon":18},"Installation","\u002Fstart\u002Finstallation","1.start\u002F2.installation","i-lucide-download",{"title":20,"path":21,"stem":22,"icon":23},"Quickstart","\u002Fstart\u002Fquickstart","1.start\u002F3.quickstart","i-lucide-rocket",{"title":25,"path":26,"stem":27,"icon":28},"Configuration basics","\u002Fstart\u002Fconfiguration","1.start\u002F4.configuration","i-lucide-settings-2",{"title":30,"icon":6,"path":31,"stem":32,"children":33,"page":6},"Guides","\u002Fguides","2.guides",[34,39,44,49,54,59,64,69,74,79,84,89,94],{"title":35,"path":36,"stem":37,"icon":38},"Use jscpd in CI","\u002Fguides\u002Fci","2.guides\u002F01.ci","i-lucide-github",{"title":40,"path":41,"stem":42,"icon":43},"Pre-commit hooks","\u002Fguides\u002Fpre-commit","2.guides\u002F02.pre-commit","i-lucide-git-commit",{"title":45,"path":46,"stem":47,"icon":48},"Fail only on new duplication","\u002Fguides\u002Fbaseline","2.guides\u002F03.baseline","i-lucide-git-compare",{"title":50,"path":51,"stem":52,"icon":53},"Monorepos","\u002Fguides\u002Fmonorepos","2.guides\u002F04.monorepos","i-lucide-folder-tree",{"title":55,"path":56,"stem":57,"icon":58},"Cross-format detection","\u002Fguides\u002Fcross-format","2.guides\u002F05.cross-format","i-lucide-layers",{"title":60,"path":61,"stem":62,"icon":63},"Agents","\u002Fguides\u002Fagents","2.guides\u002F06.agents","i-lucide-cpu",{"title":65,"path":66,"stem":67,"icon":68},"Editors","\u002Fguides\u002Feditors","2.guides\u002F07.editors","i-lucide-code",{"title":70,"path":71,"stem":72,"icon":73},"Duplication over git history","\u002Fguides\u002Fhistory","2.guides\u002F08.history","i-lucide-chart-no-axes-combined",{"title":75,"path":76,"stem":77,"icon":78},"Dashboard and health score","\u002Fguides\u002Fdashboard","2.guides\u002F09.dashboard","i-lucide-layout-dashboard",{"title":80,"path":81,"stem":82,"icon":83},"Dead code","\u002Fguides\u002Fdead-code","2.guides\u002F10.dead-code","i-lucide-ghost",{"title":85,"path":86,"stem":87,"icon":88},"Comparing two codebases","\u002Fguides\u002Fcompare","2.guides\u002F11.compare","i-lucide-git-compare-arrows",{"title":90,"path":91,"stem":92,"icon":93},"Semantic clones","\u002Fguides\u002Fsemantic-clones","2.guides\u002F12.semantic-clones","i-lucide-brain-circuit",{"title":95,"path":96,"stem":97,"icon":98},"Similar functions","\u002Fguides\u002Fsimilarity","2.guides\u002F13.similarity","i-lucide-list-tree",{"title":100,"icon":6,"path":101,"stem":102,"children":103,"page":6},"Concepts","\u002Fconcepts","3.concepts",[104,109,114],{"title":105,"path":106,"stem":107,"icon":108},"Types of code clones","\u002Fconcepts\u002Fclone-types","3.concepts\u002F1.clone-types","i-lucide-copy",{"title":110,"path":111,"stem":112,"icon":113},"How detection works","\u002Fconcepts\u002Fhow-detection-works","3.concepts\u002F2.how-detection-works","i-lucide-scan-search",{"title":115,"path":116,"stem":117,"icon":118},"The health score","\u002Fconcepts\u002Fhealth-score","3.concepts\u002F3.health-score","i-lucide-heart-pulse",{"title":120,"icon":6,"path":121,"stem":122,"children":123,"page":6},"Reference","\u002Freference","4.reference",[124,129,134,139,176,181,185],{"title":125,"path":126,"stem":127,"icon":128},"CLI options","\u002Freference\u002Fcli","4.reference\u002F1.cli","i-lucide-terminal",{"title":130,"path":131,"stem":132,"icon":133},"The config file","\u002Freference\u002Fconfig-file","4.reference\u002F2.config-file","i-lucide-settings",{"title":135,"path":136,"stem":137,"icon":138},"Exit codes","\u002Freference\u002Fexit-codes","4.reference\u002F3.exit-codes","i-lucide-log-out",{"title":140,"icon":6,"path":141,"stem":142,"children":143},"Reporters","\u002Freference\u002Freporters","4.reference\u002F4.reporters\u002Findex",[144,146,151,156,161,166,171],{"title":140,"path":141,"stem":142,"icon":145},"i-lucide-file-chart-column",{"title":147,"path":148,"stem":149,"icon":150},"HTML reporter","\u002Freference\u002Freporters\u002Fhtml","4.reference\u002F4.reporters\u002F2.html","i-lucide-file-code",{"title":152,"path":153,"stem":154,"icon":155},"JSON reporter","\u002Freference\u002Freporters\u002Fjson","4.reference\u002F4.reporters\u002F3.json","i-lucide-braces",{"title":157,"path":158,"stem":159,"icon":160},"Badge reporter","\u002Freference\u002Freporters\u002Fbadge","4.reference\u002F4.reporters\u002F4.badge","i-lucide-award",{"title":162,"path":163,"stem":164,"icon":165},"SARIF reporter","\u002Freference\u002Freporters\u002Fsarif","4.reference\u002F4.reporters\u002F5.sarif","i-lucide-shield-check",{"title":167,"path":168,"stem":169,"icon":170},"CodeClimate reporter","\u002Freference\u002Freporters\u002Fcodeclimate","4.reference\u002F4.reporters\u002F6.codeclimate","i-lucide-flag-triangle-right",{"title":172,"path":173,"stem":174,"icon":175},"OpenMetrics reporter","\u002Freference\u002Freporters\u002Fopenmetrics","4.reference\u002F4.reporters\u002F7.openmetrics","i-lucide-activity",{"title":177,"path":178,"stem":179,"icon":180},"Supported formats","\u002Freference\u002Fsupported-formats","4.reference\u002F5.supported-formats","i-lucide-languages",{"title":182,"path":183,"stem":184,"icon":38},"GitHub Action","\u002Freference\u002Fgithub-action","4.reference\u002F6.github-action",{"title":186,"path":187,"stem":188,"icon":68},"Rust crates","\u002Freference\u002Frust-crates","4.reference\u002F7.rust-crates",{"title":190,"icon":6,"path":191,"stem":192,"children":193,"page":6},"Project","\u002Fproject","5.project",[194,199,204,209,234,239],{"title":195,"path":196,"stem":197,"icon":198},"Changelog","\u002Fproject\u002Fchangelog","5.project\u002F1.changelog","i-lucide-scroll-text",{"title":200,"path":201,"stem":202,"icon":203},"Migrating from v4 to v5","\u002Fproject\u002Fmigration","5.project\u002F2.migration","i-lucide-arrow-right-left",{"title":205,"path":206,"stem":207,"icon":208},"jscpd v4 (TypeScript)","\u002Fproject\u002Fv4","5.project\u002F3.v4","i-simple-icons-typescript",{"title":210,"icon":6,"path":211,"stem":212,"children":213},"Benchmarks","\u002Fproject\u002Fbenchmarks","5.project\u002F4.benchmarks\u002Findex",[214,216,221,225,230],{"title":210,"path":211,"stem":212,"icon":215},"i-lucide-gauge",{"title":217,"path":218,"stem":219,"icon":220},"Detection Speed","\u002Fproject\u002Fbenchmarks\u002Fdetection-speed","5.project\u002F4.benchmarks\u002F2.detection-speed","i-lucide-zap",{"title":222,"path":223,"stem":224,"icon":58},"Multi-Format Detection","\u002Fproject\u002Fbenchmarks\u002Fcross-format","5.project\u002F4.benchmarks\u002F3.cross-format",{"title":226,"path":227,"stem":228,"icon":229},"AI Token Efficiency","\u002Fproject\u002Fbenchmarks\u002Fai-token-efficiency","5.project\u002F4.benchmarks\u002F4.ai-token-efficiency","i-lucide-sparkles",{"title":231,"path":232,"stem":233,"icon":93},"Embedding Models","\u002Fproject\u002Fbenchmarks\u002Fembedding-models","5.project\u002F4.benchmarks\u002F5.embedding-models",{"title":235,"path":236,"stem":237,"icon":238},"Research","\u002Fproject\u002Fresearch","5.project\u002F5.research","i-lucide-graduation-cap",{"title":240,"path":241,"stem":242,"icon":243},"Articles","\u002Fproject\u002Farticles","5.project\u002F6.articles","i-lucide-newspaper",{"id":245,"title":95,"body":246,"description":1585,"extension":1586,"links":1587,"meta":1588,"navigation":1589,"path":96,"seo":1590,"stem":97,"__hash__":1593},"docs\u002F2.guides\u002F13.similarity.md",{"type":247,"value":248,"toc":1567},"minimark",[249,272,287,292,356,359,367,386,390,395,400,403,510,517,551,555,566,588,591,649,691,707,711,720,727,810,813,817,835,845,849,875,879,892,935,959,965,969,1098,1116,1222,1225,1232,1300,1304,1325,1411,1440,1444,1499,1503,1531,1535,1563],[250,251,252,253,257,258,261,262,265,266,271],"p",{},"A copied function rarely stays an exact copy. Its variables get new names, a constant changes, a log line goes in, and the token passes of jscpd lose it, because they need a long run of tokens that repeats. ",[254,255,256],"code",{},"--similarity"," compares whole functions by the structure of their syntax trees instead. It finds a copy with other names and other literals, and a copy with a statement or two added or changed, and reports the pair as a clone of kind ",[254,259,260],{},"similar"," with the method ",[254,263,264],{},"ast",". These are ",[267,268,270],"a",{"href":269},"\u002Fconcepts\u002Fclone-types#type-3-near-miss-clones","Type-3 near-miss clones",".",[273,274,275,276,278,279,282,283,286],"note",{},"This page describes ",[254,277,256],{}," from the release after 5.4.0. Up to 5.4.0 it compared JavaScript and TypeScript functions only, by the sequence of their node types, and scored more loosely, so a pair that scored ",[254,280,281],{},"0.85"," there can score lower now. Refresh a baseline or a ",[254,284,285],{},"--threshold"," that was set on the old results.",[288,289,291],"h2",{"id":290},"quick-start","Quick start",[293,294,300],"pre",{"className":295,"code":296,"filename":297,"language":298,"meta":299,"style":299},"language-bash shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","jscpd --similarity src\u002F            # pairs that score 0.82 or more\njscpd --similarity 0.7 src\u002F        # looser: a couple of added or removed statements\njscpd --similarity -r edn src\u002F     # every pair with its score, in report\u002Fjscpd-report.edn\n","Terminal","bash","",[254,301,302,322,338],{"__ignoreMap":299},[303,304,307,311,315,318],"span",{"class":305,"line":306},"line",1,[303,308,310],{"class":309},"sBMFI","jscpd",[303,312,314],{"class":313},"sfazB"," --similarity",[303,316,317],{"class":313}," src\u002F",[303,319,321],{"class":320},"sHwdD","            # pairs that score 0.82 or more\n",[303,323,325,327,329,333,335],{"class":305,"line":324},2,[303,326,310],{"class":309},[303,328,314],{"class":313},[303,330,332],{"class":331},"sbssI"," 0.7",[303,334,317],{"class":313},[303,336,337],{"class":320},"        # looser: a couple of added or removed statements\n",[303,339,341,343,345,348,351,353],{"class":305,"line":340},3,[303,342,310],{"class":309},[303,344,314],{"class":313},[303,346,347],{"class":313}," -r",[303,349,350],{"class":313}," edn",[303,352,317],{"class":313},[303,354,355],{"class":320},"     # every pair with its score, in report\u002Fjscpd-report.edn\n",[250,357,358],{},"A pair shows up in the same report as the token clones:",[293,360,365],{"className":361,"code":363,"language":364,"meta":299},[362],"language-text","Clone found (python, similar (ast) ~1.00)\n - insurance.py [1:1 - 8:26] (8 lines, 62 tokens)\n   shipping.py [1:1 - 8:29]\n","text",[254,366,363],{"__ignoreMap":299},[250,368,369,372,373,376,377,385],{},[254,370,371],{},"shipping.py"," prices a parcel and ",[254,374,375],{},"insurance.py"," prices a policy. Every name and every number differs, so no run of tokens repeats, but the two functions call the same functions in the same order. To try it, run it on ",[267,378,382],{"href":379,"rel":380},"https:\u002F\u002Fgithub.com\u002Fkucherenko\u002Fjscpd\u002Ftree\u002Fmaster\u002Ffixtures\u002Fsimilarity-demo",[381],"nofollow",[254,383,384],{},"fixtures\u002Fsimilarity-demo",": a renamed copy, a copy that calls other methods, an edited copy, copies in Markdown code blocks and in test files, and one pair for each language.",[288,387,389],{"id":388},"how-it-works","How it works",[250,391,392,394],{},[254,393,256],{}," takes four steps. It picks the functions, normalizes the syntax tree of each one, turns the tree into a set of fingerprints, and searches for the pairs whose sets overlap enough.",[396,397,399],"h3",{"id":398},"which-code-is-compared","Which code is compared",[250,401,402],{},"jscpd parses each file with its tree-sitter grammar, and Clojure with a reader of its own. A unit is a function or a method, and what counts as one depends on the language:",[404,405,406,419],"table",{},[407,408,409],"thead",{},[410,411,412,416],"tr",{},[413,414,415],"th",{},"Language",[413,417,418],{},"Units",[420,421,422,431,439,447,455,463,475,483,494],"tbody",{},[410,423,424,428],{},[425,426,427],"td",{},"Python",[425,429,430],{},"Functions and methods. A function in a function is part of it; a method of a class declared in a function is a unit of its own.",[410,432,433,436],{},[425,434,435],{},"JavaScript, TypeScript",[425,437,438],{},"Functions and methods with a body outside every function, and the variable or field a function is assigned to there. Callbacks are part of the function that passes them.",[410,440,441,444],{},[425,442,443],{},"Java",[425,445,446],{},"Methods with a body, not constructors and not the methods of classes declared in a method.",[410,448,449,452],{},[425,450,451],{},"Kotlin, Scala, C#, PHP, Swift",[425,453,454],{},"Functions and methods with a body outside every function. Constructors are left out.",[410,456,457,460],{},[425,458,459],{},"C, C++",[425,461,462],{},"Function definitions outside every function. Lambdas are part of the function around them.",[410,464,465,468],{},[425,466,467],{},"Ruby",[425,469,470,471,474],{},"Methods, ",[254,472,473],{},"def self."," ones too, outside every method.",[410,476,477,480],{},[425,478,479],{},"Go",[425,481,482],{},"Functions and methods with a body.",[410,484,485,488],{},[425,486,487],{},"Rust",[425,489,490,491,271],{},"Functions with a body, not those in a function or in a ",[254,492,493],{},"mod tests",[410,495,496,499],{},[425,497,498],{},"Clojure",[425,500,501,502,505,506,509],{},"Every top-level form except ",[254,503,504],{},"ns",", read with the ",[254,507,508],{},":clj"," branch of reader conditionals. A form keeps its own lines inside a reader conditional and ends on the line where its last part starts.",[250,511,512,513,516],{},"The code blocks of Markdown files and the scripts of Vue, Svelte and Astro files count too. Each block is parsed in its own language, and a pair there is reported at the lines of the host file, as in ",[254,514,515],{},"guide.md:python [7:1 - 14:28]",". Units pair only within one language, except that JavaScript pairs with TypeScript and C with C++.",[250,518,519,520,523,524,523,527,530,531,534,535,538,539,542,543,546,547,550],{},"Test files are left out, by the conventions of each language: ",[254,521,522],{},"test_*.py",", ",[254,525,526],{},"*_test.go",[254,528,529],{},"*.test.ts",", a ",[254,532,533],{},"tests\u002F"," or ",[254,536,537],{},"__tests__\u002F"," folder and the like. jscpd reads the path below the scanned folder, so a project that lives in a folder named ",[254,540,541],{},"tests"," is still compared. Python's ",[254,544,545],{},".pyi"," stubs and Clojure's ",[254,548,549],{},".edn"," data are not read.",[396,552,554],{"id":553},"normalizing-the-tree","Normalizing the tree",[250,556,557,558,561,562,565],{},"The syntax tree of a unit becomes a nested list, and every node is a list headed by its type. The names of the functions and methods the unit calls keep their spelling, and so do its operators. Every other name, a local, a parameter or a field, becomes ",[254,559,560],{},":symbol",", and every literal becomes ",[254,563,564],{},":literal",". Comments and punctuation drop out, and so do parentheses around a single expression. This function:",[293,567,571],{"className":568,"code":569,"language":570,"meta":299,"style":299},"language-python shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","def price(parcel, rates):\n    weight = parcel.weight * 1.2\n    return max(lookup(rates, weight), 5)\n","python",[254,572,573,578,583],{"__ignoreMap":299},[303,574,575],{"class":305,"line":306},[303,576,577],{},"def price(parcel, rates):\n",[303,579,580],{"class":305,"line":324},[303,581,582],{},"    weight = parcel.weight * 1.2\n",[303,584,585],{"class":305,"line":340},[303,586,587],{},"    return max(lookup(rates, weight), 5)\n",[250,589,590],{},"becomes:",[293,592,596],{"className":593,"code":594,"language":595,"meta":299,"style":299},"language-clojure shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","[:function_definition :symbol [:parameters :symbol :symbol]\n [:block\n  [:assignment :symbol [:symbol \"=\"]\n   [:binary_operator [:attribute :symbol :symbol] [:symbol \"*\"] :literal]]\n  [:return_statement\n   [:call [:symbol \"max\"]\n    [:argument_list\n     [:call [:symbol \"lookup\"] [:argument_list :symbol :symbol]]\n     :literal]]]]]\n","clojure",[254,597,598,603,608,613,619,625,631,637,643],{"__ignoreMap":299},[303,599,600],{"class":305,"line":306},[303,601,602],{},"[:function_definition :symbol [:parameters :symbol :symbol]\n",[303,604,605],{"class":305,"line":324},[303,606,607],{}," [:block\n",[303,609,610],{"class":305,"line":340},[303,611,612],{},"  [:assignment :symbol [:symbol \"=\"]\n",[303,614,616],{"class":305,"line":615},4,[303,617,618],{},"   [:binary_operator [:attribute :symbol :symbol] [:symbol \"*\"] :literal]]\n",[303,620,622],{"class":305,"line":621},5,[303,623,624],{},"  [:return_statement\n",[303,626,628],{"class":305,"line":627},6,[303,629,630],{},"   [:call [:symbol \"max\"]\n",[303,632,634],{"class":305,"line":633},7,[303,635,636],{},"    [:argument_list\n",[303,638,640],{"class":305,"line":639},8,[303,641,642],{},"     [:call [:symbol \"lookup\"] [:argument_list :symbol :symbol]]\n",[303,644,646],{"class":305,"line":645},9,[303,647,648],{},"     :literal]]]]]\n",[250,650,651,523,654,523,657,660,661,664,665,667,668,660,671,674,675,677,678,523,681,523,684,660,687,690],{},[254,652,653],{},"price",[254,655,656],{},"parcel",[254,658,659],{},"rates"," and ",[254,662,663],{},"weight"," all read as ",[254,666,560],{},", and ",[254,669,670],{},"1.2",[254,672,673],{},"5"," as ",[254,676,564],{},", while ",[254,679,680],{},"max",[254,682,683],{},"lookup",[254,685,686],{},"*",[254,688,689],{},"="," keep their spelling. A function that prices a policy with other names and other numbers turns into the same list.",[250,692,693,694,660,697,700,701,660,704,271],{},"Called names and operators count on purpose. ",[254,695,696],{},"filter(is_paid, orders)",[254,698,699],{},"remove(is_paid, orders)"," have one shape and do opposite things, and so do ",[254,702,703],{},"total + fee",[254,705,706],{},"total - fee",[396,708,710],{"id":709},"fingerprints-and-the-score","Fingerprints and the score",[250,712,713,714,534,716,719],{},"Every list in the normalized tree, written out as text, is a fingerprint, and so is every atom such as ",[254,715,564],{},[254,717,718],{},"[:symbol \"max\"]",". The function above has 44 nodes and 30 distinct fingerprints: the whole function, its block, each statement, each call, and so on down to the atoms.",[250,721,722,723,726],{},"Two functions score the Jaccard index of their fingerprint sets: the number of fingerprints they share divided by the number that either of them has. The same structure scores ",[254,724,725],{},"1.0",". An edit changes the list that holds it and every list around it up to the function itself, so it costs a few fingerprints at once. This is what one edit does to a function of ten lines and 115 nodes:",[404,728,729,739],{},[407,730,731],{},[410,732,733,736],{},[413,734,735],{},"Edit",[413,737,738],{},"Score",[420,740,741,749,756,764,772,786,797],{},[410,742,743,746],{},[425,744,745],{},"Other names and other literals",[425,747,748],{},"1.00",[410,750,751,754],{},[425,752,753],{},"Parentheses around an expression, comments",[425,755,748],{},[410,757,758,761],{},[425,759,760],{},"Two statements swapped",[425,762,763],{},"0.93",[410,765,766,769],{},[425,767,768],{},"One call statement added",[425,770,771],{},"0.89",[410,773,774,783],{},[425,775,776,777,779,780],{},"One operator changed, ",[254,778,686],{}," to ",[254,781,782],{},"+",[425,784,785],{},"0.78",[410,787,788,795],{},[425,789,790,791,794],{},"One ",[254,792,793],{},"if"," block removed",[425,796,785],{},[410,798,799,807],{},[425,800,801,802,779,804],{},"One called function changed, ",[254,803,683],{},[254,805,806],{},"fetch_rate",[425,808,809],{},"0.75",[250,811,812],{},"A bigger function loses less from one edit, because the edit is a smaller share of its subtrees.",[396,814,816],{"id":815},"the-search","The search",[250,818,819,820,823,824,827,828,831,832,834],{},"The search is exact: it finds every pair whose score reaches the threshold, with no sampling. jscpd ranks the fingerprints of all units from the rarest to the most common and indexes each unit by its rarest few. Two sets whose Jaccard index reaches ",[254,821,822],{},"t"," must share one of the first ",[254,825,826],{},"n - ceil(t × n) + 1"," fingerprints of a set of size ",[254,829,830],{},"n",", so jscpd compares only the pairs that do, and it skips the pairs whose sizes are too far apart to reach ",[254,833,822],{},". Most pairs are never compared, and every candidate gets its exact score.",[250,836,837,838,840,841,844],{},"A pair that a token clone already covers, an exact, renamed or merged clone of the same lines, is not reported as ",[254,839,260],{}," again, though the ",[254,842,843],{},"edn"," report still lists it. When a fragment has several copies, detection pairs every copy with the first one, and the pairs among the other copies are implied.",[396,846,848],{"id":847},"size-limits","Size limits",[250,850,851,852,855,856,859,860,863,864,660,867,870,871,874],{},"A unit needs at least ",[254,853,854],{},"--min-nodes"," nodes in its normalized tree, 20 by default, and it must span at least ",[254,857,858],{},"--min-lines"," lines. Small functions match too easily, since any two getters have one shape. ",[254,861,862],{},"--min-tokens"," does not apply to units. Code that the token passes skip does not count either: what lies between ",[254,865,866],{},"jscpd:ignore-start",[254,868,869],{},"jscpd:ignore-end"," or matches ",[254,872,873],{},"--ignore-pattern"," adds nothing to a unit's tree, and a unit inside such code is not compared.",[288,876,878],{"id":877},"thresholds","Thresholds",[250,880,881,882,884,885,667,888,891],{},"The ratio after ",[254,883,256],{}," is the lowest score that gets reported, a number in ",[254,886,887],{},"(0, 1]",[254,889,890],{},"0.82"," when the flag has no value:",[293,893,895],{"className":295,"code":894,"filename":297,"language":298,"meta":299,"style":299},"jscpd --similarity src\u002F          # 0.82: renames, other literals, a one-line edit\njscpd --similarity 0.7 src\u002F      # looser: a couple of added or removed statements\njscpd --similarity 1 src\u002F        # the same structure only\n",[254,896,897,908,921],{"__ignoreMap":299},[303,898,899,901,903,905],{"class":305,"line":306},[303,900,310],{"class":309},[303,902,314],{"class":313},[303,904,317],{"class":313},[303,906,907],{"class":320},"          # 0.82: renames, other literals, a one-line edit\n",[303,909,910,912,914,916,918],{"class":305,"line":324},[303,911,310],{"class":309},[303,913,314],{"class":313},[303,915,332],{"class":331},[303,917,317],{"class":313},[303,919,920],{"class":320},"      # looser: a couple of added or removed statements\n",[303,922,923,925,927,930,932],{"class":305,"line":340},[303,924,310],{"class":309},[303,926,314],{"class":313},[303,928,929],{"class":331}," 1",[303,931,317],{"class":313},[303,933,934],{"class":320},"        # the same structure only\n",[250,936,937,939,940,947,948,951,952,955,956,958],{},[254,938,890],{}," keeps a renamed copy and a copy with one statement added or swapped. In a short function it drops a copy that calls another function or uses another operator. In ",[267,941,944],{"href":942,"rel":943},"https:\u002F\u002Fgithub.com\u002Fkucherenko\u002Fjscpd\u002Ftree\u002Fmaster\u002Ffixtures\u002Ftype3-demo\u002Fsimilar-functions",[381],[254,945,946],{},"fixtures\u002Ftype3-demo\u002Fsimilar-functions",", a copy with every name changed, a guard and a logging call added, and one other function called scores ",[254,949,950],{},"0.66",", so it takes ",[254,953,954],{},"--similarity 0.6"," to see it. A ratio outside ",[254,957,887],{}," prints a warning and turns the search off.",[250,960,961,962,964],{},"To see what a lower ratio would add before you change it, run the ",[254,963,843],{}," reporter at the lower ratio and read the scores between the two.",[288,966,968],{"id":967},"options","Options",[404,970,971,987],{},[407,972,973],{},[410,974,975,978,981,984],{},[413,976,977],{},"Flag",[413,979,980],{},"Config key",[413,982,983],{},"What it does",[413,985,986],{},"Default",[420,988,989,1012,1032,1051,1078],{},[410,990,991,996,1001,1006],{},[425,992,993],{},[254,994,995],{},"--similarity [RATIO]",[425,997,998],{},[254,999,1000],{},"similarity",[425,1002,1003,1004],{},"Turns the search on. RATIO is the lowest score reported, a number in ",[254,1005,887],{},[425,1007,1008,1009,1011],{},"off, ",[254,1010,890],{}," when the flag has no value",[410,1013,1014,1019,1024,1027],{},[425,1015,1016],{},[254,1017,1018],{},"--min-nodes \u003CN>",[425,1020,1021],{},[254,1022,1023],{},"minNodes",[425,1025,1026],{},"The fewest nodes a unit's normalized tree needs to be compared",[425,1028,1029],{},[254,1030,1031],{},"20",[410,1033,1034,1039,1044,1047],{},[425,1035,1036],{},[254,1037,1038],{},"--min-lines \u003CN>",[425,1040,1041],{},[254,1042,1043],{},"minLines",[425,1045,1046],{},"The fewest lines a unit spans, as for token clones",[425,1048,1049],{},[254,1050,673],{},[410,1052,1053,1058,1063,1075],{},[425,1054,1055],{},[254,1056,1057],{},"--kind \u003CLIST>",[425,1059,1060],{},[254,1061,1062],{},"kind",[425,1064,1065,1066,1068,1069,1071,1072],{},"Keeps only some kinds of clones: ",[254,1067,264],{}," for these pairs, ",[254,1070,260],{}," for them and the merges of ",[254,1073,1074],{},"--max-gap-lines",[425,1076,1077],{},"every kind",[410,1079,1080,1085,1090,1096],{},[425,1081,1082],{},[254,1083,1084],{},"--reporters edn",[425,1086,1087],{},[254,1088,1089],{},"reporters",[425,1091,1092,1093],{},"Writes every pair with its score to ",[254,1094,1095],{},"jscpd-report.edn",[425,1097],{},[250,1099,1100,523,1103,660,1105,1108,1109,660,1112,1115],{},[254,1101,1102],{},"--ignore",[254,1104,873],{},[254,1106,1107],{},"--format"," choose the code as they do for token clones, and ",[254,1110,1111],{},"--skip-local",[254,1113,1114],{},"--skip-isolated"," drop function pairs as they drop token clones. In a config file:",[293,1117,1122],{"className":1118,"code":1119,"filename":1120,"language":1121,"meta":299,"style":299},"language-json shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","{\n  \"similarity\": 0.82,\n  \"minNodes\": 20,\n  \"reporters\": [\"console\", \"edn\"],\n  \"output\": \".metrics\"\n}\n",".jscpd.json","json",[254,1123,1124,1130,1150,1165,1198,1217],{"__ignoreMap":299},[303,1125,1126],{"class":305,"line":306},[303,1127,1129],{"class":1128},"sMK4o","{\n",[303,1131,1132,1135,1138,1141,1144,1147],{"class":305,"line":324},[303,1133,1134],{"class":1128},"  \"",[303,1136,1000],{"class":1137},"spNyl",[303,1139,1140],{"class":1128},"\"",[303,1142,1143],{"class":1128},":",[303,1145,1146],{"class":331}," 0.82",[303,1148,1149],{"class":1128},",\n",[303,1151,1152,1154,1156,1158,1160,1163],{"class":305,"line":340},[303,1153,1134],{"class":1128},[303,1155,1023],{"class":1137},[303,1157,1140],{"class":1128},[303,1159,1143],{"class":1128},[303,1161,1162],{"class":331}," 20",[303,1164,1149],{"class":1128},[303,1166,1167,1169,1171,1173,1175,1178,1180,1183,1185,1188,1191,1193,1195],{"class":305,"line":615},[303,1168,1134],{"class":1128},[303,1170,1089],{"class":1137},[303,1172,1140],{"class":1128},[303,1174,1143],{"class":1128},[303,1176,1177],{"class":1128}," [",[303,1179,1140],{"class":1128},[303,1181,1182],{"class":313},"console",[303,1184,1140],{"class":1128},[303,1186,1187],{"class":1128},",",[303,1189,1190],{"class":1128}," \"",[303,1192,843],{"class":313},[303,1194,1140],{"class":1128},[303,1196,1197],{"class":1128},"],\n",[303,1199,1200,1202,1205,1207,1209,1211,1214],{"class":305,"line":621},[303,1201,1134],{"class":1128},[303,1203,1204],{"class":1137},"output",[303,1206,1140],{"class":1128},[303,1208,1143],{"class":1128},[303,1210,1190],{"class":1128},[303,1212,1213],{"class":313},".metrics",[303,1215,1216],{"class":1128},"\"\n",[303,1218,1219],{"class":305,"line":627},[303,1220,1221],{"class":1128},"}\n",[288,1223,1224],{"id":1204},"Output",[250,1226,1227,1228,261,1230,1143],{},"Every reporter shows these pairs next to the token clones, as clones of kind ",[254,1229,260],{},[254,1231,264],{},[1233,1234,1235,1250,1274,1294],"ul",{},[1236,1237,1238,1239,1242,1243,1246,1247,271],"li",{},"The console prints ",[254,1240,1241],{},"Clone found (python, similar (ast) ~0.85)",", and the ",[254,1244,1245],{},"ai"," reporter ",[254,1248,1249],{},"[~0.85 ast]",[1236,1251,1252,1253,523,1256,1259,1260,1263,1264,1267,1268,660,1271,271],{},"JSON has ",[254,1254,1255],{},"\"kind\": \"similar\"",[254,1257,1258],{},"\"method\": \"ast\"",", the score as ",[254,1261,1262],{},"\"similarity\"",", and the size of each normalized tree as ",[254,1265,1266],{},"\"nodes\""," in ",[254,1269,1270],{},"firstFile",[254,1272,1273],{},"secondFile",[1236,1275,1276,1277,1280,1281,1283,1284,1287,1288,660,1291,271],{},"SARIF and Code Climate file the pairs under the rule ",[254,1278,1279],{},"jscpd\u002Fsimilar-function",", apart from the merges of ",[254,1282,1074],{}," under ",[254,1285,1286],{},"jscpd\u002Fsimilar-code",". SARIF adds ",[254,1289,1290],{},"similarity_method",[254,1292,1293],{},"nodes",[1236,1295,1296,1299],{},[254,1297,1298],{},"tokens"," is the token count of the smaller function, and the fragments span the whole functions.",[396,1301,1303],{"id":1302},"the-edn-report","The edn report",[250,1305,1306,1309,1310,1267,1312,1317,1318,1321,1322,1143],{},[254,1307,1308],{},"-r edn"," writes ",[254,1311,1095],{},[267,1313,1316],{"href":1314,"rel":1315},"https:\u002F\u002Fgithub.com\u002Fedn-format\u002Fedn",[381],"EDN",", the data notation of Clojure. It lists every pair the search found under ",[254,1319,1320],{},":candidates",", the pairs that a token clone covers too, most similar first, and the clones of the other passes under ",[254,1323,1324],{},":clones",[293,1326,1328],{"className":593,"code":1327,"filename":1095,"language":595,"meta":299,"style":299},"{:candidates [\n {:score 0.890909090909\n  :language \"python\"\n  :left {:file \"src\u002Fbilling\u002Finvoice.py\", :start-line 3, :end-line 13}\n  :right {:file \"src\u002Fbilling\u002Freceipt.py\", :start-line 3, :end-line 14}\n  :left-nodes 158\n  :right-nodes 166}\n]\n :clones [\n {:kind :exact\n  :format \"go\"\n  :left {:file \"src\u002Fa.go\", :start-line 1, :end-line 11}\n  :right {:file \"src\u002Fb.go\", :start-line 1, :end-line 11}\n  :tokens 55}\n]}\n",[254,1329,1330,1335,1340,1345,1350,1355,1360,1365,1370,1375,1381,1387,1393,1399,1405],{"__ignoreMap":299},[303,1331,1332],{"class":305,"line":306},[303,1333,1334],{},"{:candidates [\n",[303,1336,1337],{"class":305,"line":324},[303,1338,1339],{}," {:score 0.890909090909\n",[303,1341,1342],{"class":305,"line":340},[303,1343,1344],{},"  :language \"python\"\n",[303,1346,1347],{"class":305,"line":615},[303,1348,1349],{},"  :left {:file \"src\u002Fbilling\u002Finvoice.py\", :start-line 3, :end-line 13}\n",[303,1351,1352],{"class":305,"line":621},[303,1353,1354],{},"  :right {:file \"src\u002Fbilling\u002Freceipt.py\", :start-line 3, :end-line 14}\n",[303,1356,1357],{"class":305,"line":627},[303,1358,1359],{},"  :left-nodes 158\n",[303,1361,1362],{"class":305,"line":633},[303,1363,1364],{},"  :right-nodes 166}\n",[303,1366,1367],{"class":305,"line":639},[303,1368,1369],{},"]\n",[303,1371,1372],{"class":305,"line":645},[303,1373,1374],{}," :clones [\n",[303,1376,1378],{"class":305,"line":1377},10,[303,1379,1380],{}," {:kind :exact\n",[303,1382,1384],{"class":305,"line":1383},11,[303,1385,1386],{},"  :format \"go\"\n",[303,1388,1390],{"class":305,"line":1389},12,[303,1391,1392],{},"  :left {:file \"src\u002Fa.go\", :start-line 1, :end-line 11}\n",[303,1394,1396],{"class":305,"line":1395},13,[303,1397,1398],{},"  :right {:file \"src\u002Fb.go\", :start-line 1, :end-line 11}\n",[303,1400,1402],{"class":305,"line":1401},14,[303,1403,1404],{},"  :tokens 55}\n",[303,1406,1408],{"class":305,"line":1407},15,[303,1409,1410],{},"]}\n",[250,1412,1413,1416,1417,660,1420,1423,1424,1427,1428,1431,1432,1435,1436,1439],{},[254,1414,1415],{},":score"," is the Jaccard index with up to 12 decimals, and ",[254,1418,1419],{},":left-nodes",[254,1421,1422],{},":right-nodes"," are the sizes of the two normalized trees. Pairs with the same score are sorted by language, file and line, so two runs on the same code write the same file. A function in a Markdown code block or a component is named by its host file. A run with no pairs writes ",[254,1425,1426],{},"{:candidates []"," and an empty ",[254,1429,1430],{},":clones []",". ",[254,1433,1434],{},"jscpd --similarity -r console,edn -o .metrics"," puts the file at ",[254,1437,1438],{},".metrics\u002Fjscpd-report.edn"," and still prints the console report.",[288,1441,1443],{"id":1442},"in-editors-and-agents","In editors and agents",[1233,1445,1446,1472],{},[1236,1447,1448,1449,1452,1453,1455,1456,1431,1459,1462,1463,1465,1466,1468,1469,1471],{},"The ",[267,1450,1451],{"href":66},"language server"," shows the pairs with its ",[254,1454,264],{}," analysis, as one information diagnostic on the first line of each function: ",[254,1457,1458],{},"Same structure as the function at src\u002Fholds.js:13-20 (1.00)",[254,1460,1461],{},"ast.similarity"," sets the ratio, ",[254,1464,890],{}," unless the config's ",[254,1467,1000],{}," key sets another, and ",[254,1470,1023],{}," in the project's config sets the size limit.",[1236,1473,1448,1474,1477,1478,523,1480,534,1482,1485,1486,1488,1489,1491,1492,1495,1496,1498],{},[267,1475,1476],{"href":61},"MCP server"," finds the pairs when a tool call asks for the kinds ",[254,1479,264],{},[254,1481,260],{},[254,1483,1484],{},"type3",", at ",[254,1487,890],{}," unless ",[254,1490,256],{}," sets another ratio. Its ",[254,1493,1494],{},"check_duplication"," tool takes a ",[254,1497,1000],{}," argument for the functions of a snippet.",[288,1500,1502],{"id":1501},"what-it-does-not-do","What it does not do",[1233,1504,1505,1518,1525,1528],{},[1236,1506,1507,1508,534,1513,271],{},"It does not pair functions of two languages, except JavaScript with TypeScript and C with C++. For code ported from one language to another, use ",[267,1509,1510],{"href":86},[254,1511,1512],{},"--compare",[267,1514,1515],{"href":91},[254,1516,1517],{},"--semantic",[1236,1519,1520,1521,1524],{},"It does not find functions that do the same job with different code. The structure has to match; ",[267,1522,1523],{"href":91},"semantic clones"," cover the rest.",[1236,1526,1527],{},"It does not compare classes, modules or top-level code. A function nested in another one is part of it.",[1236,1529,1530],{},"It does not read test files.",[288,1532,1534],{"id":1533},"see-also","See also",[1233,1536,1537,1545,1550,1555],{},[1236,1538,1539,1541,1542,1544],{},[267,1540,105],{"href":106},", where ",[254,1543,260],{}," sits next to the other kinds.",[1236,1546,1547,1549],{},[267,1548,90],{"href":91},", for functions that do the same job with different code.",[1236,1551,1552,1554],{},[267,1553,125],{"href":126},", for every flag.",[1236,1556,1557,1562],{},[267,1558,1560],{"href":379,"rel":1559},[381],[254,1561,384],{},", a runnable pair in every language.",[1564,1565,1566],"style",{},"html pre.shiki code .sBMFI, html code.shiki .sBMFI{--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}html pre.shiki code .sHwdD, html code.shiki .sHwdD{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#546E7A;--shiki-default-font-style:italic;--shiki-dark:#676E95;--shiki-dark-font-style:italic}html pre.shiki code .sbssI, html code.shiki .sbssI{--shiki-light:#F76D47;--shiki-default:#F78C6C;--shiki-dark:#F78C6C}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html pre.shiki code .spNyl, html code.shiki .spNyl{--shiki-light:#9C3EDA;--shiki-default:#C792EA;--shiki-dark:#C792EA}",{"title":299,"searchDepth":324,"depth":324,"links":1568},[1569,1570,1577,1578,1579,1582,1583,1584],{"id":290,"depth":324,"text":291},{"id":388,"depth":324,"text":389,"children":1571},[1572,1573,1574,1575,1576],{"id":398,"depth":340,"text":399},{"id":553,"depth":340,"text":554},{"id":709,"depth":340,"text":710},{"id":815,"depth":340,"text":816},{"id":847,"depth":340,"text":848},{"id":877,"depth":324,"text":878},{"id":967,"depth":324,"text":968},{"id":1204,"depth":324,"text":1224,"children":1580},[1581],{"id":1302,"depth":340,"text":1303},{"id":1442,"depth":324,"text":1443},{"id":1501,"depth":324,"text":1502},{"id":1533,"depth":324,"text":1534},"Find functions with the same structure in 15 languages, renamed copies and lightly edited ones included, with jscpd --similarity.","md",null,{},{"icon":98},{"title":1591,"description":1592},"Find Similar Functions with jscpd --similarity","How jscpd --similarity normalizes syntax trees, scores two functions by the subtrees they share and finds every pair above a threshold, with all of its options and output formats.","iyYFRu9dLwt5HKKx1Rn8Aa-UVrch1yIFC3ZUH8C82wU",[1595,1597],{"title":90,"path":91,"stem":92,"description":1596,"icon":93,"children":-1},"Find functions that do the same job with different code, in one language or across languages, with jscpd --semantic and a code embedding model.",{"title":105,"path":106,"stem":107,"description":1598,"icon":108,"children":-1},"Exact, renamed, near-miss and semantic clones, and which of them jscpd finds.",1791389603977]