[{"data":1,"prerenderedAt":2033},["ShallowReactive",2],{"navigation_docs":3,"-guides-editors":239,"-guides-editors-surround":2028},[4,29,94,114,184],{"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],{"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,"icon":6,"path":96,"stem":97,"children":98,"page":6},"Concepts","\u002Fconcepts","3.concepts",[99,104,109],{"title":100,"path":101,"stem":102,"icon":103},"Types of code clones","\u002Fconcepts\u002Fclone-types","3.concepts\u002F1.clone-types","i-lucide-copy",{"title":105,"path":106,"stem":107,"icon":108},"How detection works","\u002Fconcepts\u002Fhow-detection-works","3.concepts\u002F2.how-detection-works","i-lucide-scan-search",{"title":110,"path":111,"stem":112,"icon":113},"The health score","\u002Fconcepts\u002Fhealth-score","3.concepts\u002F3.health-score","i-lucide-heart-pulse",{"title":115,"icon":6,"path":116,"stem":117,"children":118,"page":6},"Reference","\u002Freference","4.reference",[119,124,129,134,171,176,180],{"title":120,"path":121,"stem":122,"icon":123},"CLI options","\u002Freference\u002Fcli","4.reference\u002F1.cli","i-lucide-terminal",{"title":125,"path":126,"stem":127,"icon":128},"The config file","\u002Freference\u002Fconfig-file","4.reference\u002F2.config-file","i-lucide-settings",{"title":130,"path":131,"stem":132,"icon":133},"Exit codes","\u002Freference\u002Fexit-codes","4.reference\u002F3.exit-codes","i-lucide-log-out",{"title":135,"icon":6,"path":136,"stem":137,"children":138},"Reporters","\u002Freference\u002Freporters","4.reference\u002F4.reporters\u002Findex",[139,141,146,151,156,161,166],{"title":135,"path":136,"stem":137,"icon":140},"i-lucide-file-chart-column",{"title":142,"path":143,"stem":144,"icon":145},"HTML reporter","\u002Freference\u002Freporters\u002Fhtml","4.reference\u002F4.reporters\u002F2.html","i-lucide-file-code",{"title":147,"path":148,"stem":149,"icon":150},"JSON reporter","\u002Freference\u002Freporters\u002Fjson","4.reference\u002F4.reporters\u002F3.json","i-lucide-braces",{"title":152,"path":153,"stem":154,"icon":155},"Badge reporter","\u002Freference\u002Freporters\u002Fbadge","4.reference\u002F4.reporters\u002F4.badge","i-lucide-award",{"title":157,"path":158,"stem":159,"icon":160},"SARIF reporter","\u002Freference\u002Freporters\u002Fsarif","4.reference\u002F4.reporters\u002F5.sarif","i-lucide-shield-check",{"title":162,"path":163,"stem":164,"icon":165},"CodeClimate reporter","\u002Freference\u002Freporters\u002Fcodeclimate","4.reference\u002F4.reporters\u002F6.codeclimate","i-lucide-flag-triangle-right",{"title":167,"path":168,"stem":169,"icon":170},"OpenMetrics reporter","\u002Freference\u002Freporters\u002Fopenmetrics","4.reference\u002F4.reporters\u002F7.openmetrics","i-lucide-activity",{"title":172,"path":173,"stem":174,"icon":175},"Supported formats","\u002Freference\u002Fsupported-formats","4.reference\u002F5.supported-formats","i-lucide-languages",{"title":177,"path":178,"stem":179,"icon":38},"GitHub Action","\u002Freference\u002Fgithub-action","4.reference\u002F6.github-action",{"title":181,"path":182,"stem":183,"icon":68},"Rust crates","\u002Freference\u002Frust-crates","4.reference\u002F7.rust-crates",{"title":185,"icon":6,"path":186,"stem":187,"children":188,"page":6},"Project","\u002Fproject","5.project",[189,194,199,204,229,234],{"title":190,"path":191,"stem":192,"icon":193},"Changelog","\u002Fproject\u002Fchangelog","5.project\u002F1.changelog","i-lucide-scroll-text",{"title":195,"path":196,"stem":197,"icon":198},"Migrating from v4 to v5","\u002Fproject\u002Fmigration","5.project\u002F2.migration","i-lucide-arrow-right-left",{"title":200,"path":201,"stem":202,"icon":203},"jscpd v4 (TypeScript)","\u002Fproject\u002Fv4","5.project\u002F3.v4","i-simple-icons-typescript",{"title":205,"icon":6,"path":206,"stem":207,"children":208},"Benchmarks","\u002Fproject\u002Fbenchmarks","5.project\u002F4.benchmarks\u002Findex",[209,211,216,220,225],{"title":205,"path":206,"stem":207,"icon":210},"i-lucide-gauge",{"title":212,"path":213,"stem":214,"icon":215},"Detection Speed","\u002Fproject\u002Fbenchmarks\u002Fdetection-speed","5.project\u002F4.benchmarks\u002F2.detection-speed","i-lucide-zap",{"title":217,"path":218,"stem":219,"icon":58},"Multi-Format Detection","\u002Fproject\u002Fbenchmarks\u002Fcross-format","5.project\u002F4.benchmarks\u002F3.cross-format",{"title":221,"path":222,"stem":223,"icon":224},"AI Token Efficiency","\u002Fproject\u002Fbenchmarks\u002Fai-token-efficiency","5.project\u002F4.benchmarks\u002F4.ai-token-efficiency","i-lucide-sparkles",{"title":226,"path":227,"stem":228,"icon":93},"Embedding Models","\u002Fproject\u002Fbenchmarks\u002Fembedding-models","5.project\u002F4.benchmarks\u002F5.embedding-models",{"title":230,"path":231,"stem":232,"icon":233},"Research","\u002Fproject\u002Fresearch","5.project\u002F5.research","i-lucide-graduation-cap",{"title":235,"path":236,"stem":237,"icon":238},"Articles","\u002Fproject\u002Farticles","5.project\u002F6.articles","i-lucide-newspaper",{"id":240,"title":65,"body":241,"description":2019,"extension":2020,"links":2021,"meta":2022,"navigation":2023,"path":66,"seo":2024,"stem":67,"__hash__":2027},"docs\u002F2.guides\u002F07.editors.md",{"type":242,"value":243,"toc":1989},"minimark",[244,269,290,295,300,303,345,364,367,426,430,474,492,496,506,637,643,647,650,693,696,700,712,716,752,756,765,769,772,921,927,941,1009,1017,1027,1033,1039,1050,1054,1058,1061,1103,1133,1139,1148,1152,1157,1175,1208,1221,1225,1235,1241,1446,1537,1563,1567,1569,1584,1601,1611,1614,1627,1630,1643,1653,1669,1671,1691,1701,1703,1738,1765,1769,1783,1880,1897,1901,1911,1919,1923,1951,1955,1985],[245,246,247,248,252,253,256,257,260,261,264,265,268],"p",{},"An editor that speaks the Language Server Protocol can start ",[249,250,251],"code",{},"jscpd --lsp"," for a workspace and get what jscpd finds as diagnostics in the files you edit. The diagnostics follow the text in the editor, saved or not: after you stop typing for 300 ms, the server tokenizes the file again from the buffer and searches its pool again. Files that change outside the editor, such as in a ",[249,254,255],{},"git checkout",", reach the server when the editor watches files for it; the server reads them again and searches each pool they touch once. A folder deleted or moved in one piece counts for the files under it, and files the scan skips (ignored by ",[249,258,259],{},".gitignore"," or ",[249,262,263],{},".ignore",", or over ",[249,266,267],{},"maxSize",") stay out, whether they appear on disk or open in the editor.",[270,271,272,273,277,278,281,282,285,286,289],"note",{},"Available from jscpd ",[274,275,276],"strong",{},"5.4.0",". The editor needs ",[249,279,280],{},"jscpd"," on its ",[249,283,284],{},"PATH"," (",[287,288,15],"a",{"href":16},"). In each setup below jscpd runs next to the language's own server.",[291,292,294],"h2",{"id":293},"minimal-setup","Minimal setup",[296,297,299],"h3",{"id":298},"neovim","Neovim",[245,301,302],{},"Neovim 0.11 and later:",[304,305,311],"pre",{"className":306,"code":307,"filename":308,"language":309,"meta":310,"style":310},"language-lua shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","vim.lsp.config('jscpd', {\n  cmd = { 'jscpd', '--lsp' },\n  root_markers = { '.jscpd.json', '.git' },\n})\nvim.lsp.enable('jscpd')\n","init.lua","lua","",[249,312,313,321,327,333,339],{"__ignoreMap":310},[314,315,318],"span",{"class":316,"line":317},"line",1,[314,319,320],{},"vim.lsp.config('jscpd', {\n",[314,322,324],{"class":316,"line":323},2,[314,325,326],{},"  cmd = { 'jscpd', '--lsp' },\n",[314,328,330],{"class":316,"line":329},3,[314,331,332],{},"  root_markers = { '.jscpd.json', '.git' },\n",[314,334,336],{"class":316,"line":335},4,[314,337,338],{},"})\n",[314,340,342],{"class":316,"line":341},5,[314,343,344],{},"vim.lsp.enable('jscpd')\n",[245,346,347,348,351,352,355,356,359,360,363],{},"Without ",[249,349,350],{},"filetypes",", Neovim starts the server for every file you open. To keep it to some languages, add them: ",[249,353,354],{},"filetypes = { 'javascript', 'typescript', 'python' }",". The editor's settings go in ",[249,357,358],{},"init_options",", for example ",[249,361,362],{},"init_options = { lsp = { deadCode = { enabled = true } } }",".",[245,365,366],{},"Neovim 0.10:",[304,368,370],{"className":306,"code":369,"filename":308,"language":309,"meta":310,"style":310},"vim.api.nvim_create_autocmd('FileType', {\n  pattern = { 'javascript', 'typescript', 'python' },\n  callback = function(args)\n    vim.lsp.start({\n      name = 'jscpd',\n      cmd = { 'jscpd', '--lsp' },\n      root_dir = vim.fs.root(args.buf, { '.jscpd.json', '.git' }),\n    })\n  end,\n})\n",[249,371,372,377,382,387,392,397,403,409,415,421],{"__ignoreMap":310},[314,373,374],{"class":316,"line":317},[314,375,376],{},"vim.api.nvim_create_autocmd('FileType', {\n",[314,378,379],{"class":316,"line":323},[314,380,381],{},"  pattern = { 'javascript', 'typescript', 'python' },\n",[314,383,384],{"class":316,"line":329},[314,385,386],{},"  callback = function(args)\n",[314,388,389],{"class":316,"line":335},[314,390,391],{},"    vim.lsp.start({\n",[314,393,394],{"class":316,"line":341},[314,395,396],{},"      name = 'jscpd',\n",[314,398,400],{"class":316,"line":399},6,[314,401,402],{},"      cmd = { 'jscpd', '--lsp' },\n",[314,404,406],{"class":316,"line":405},7,[314,407,408],{},"      root_dir = vim.fs.root(args.buf, { '.jscpd.json', '.git' }),\n",[314,410,412],{"class":316,"line":411},8,[314,413,414],{},"    })\n",[314,416,418],{"class":316,"line":417},9,[314,419,420],{},"  end,\n",[314,422,424],{"class":316,"line":423},10,[314,425,338],{},[296,427,429],{"id":428},"helix","Helix",[304,431,436],{"className":432,"code":433,"filename":434,"language":435,"meta":310,"style":310},"language-toml shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","[language-server.jscpd]\ncommand = \"jscpd\"\nargs = [\"--lsp\"]\n\n[[language]]\nname = \"typescript\"\nlanguage-servers = [\"typescript-language-server\", \"jscpd\"]\n","languages.toml","toml",[249,437,438,443,448,453,459,464,469],{"__ignoreMap":310},[314,439,440],{"class":316,"line":317},[314,441,442],{},"[language-server.jscpd]\n",[314,444,445],{"class":316,"line":323},[314,446,447],{},"command = \"jscpd\"\n",[314,449,450],{"class":316,"line":329},[314,451,452],{},"args = [\"--lsp\"]\n",[314,454,455],{"class":316,"line":335},[314,456,458],{"emptyLinePlaceholder":457},true,"\n",[314,460,461],{"class":316,"line":341},[314,462,463],{},"[[language]]\n",[314,465,466],{"class":316,"line":399},[314,467,468],{},"name = \"typescript\"\n",[314,470,471],{"class":316,"line":405},[314,472,473],{},"language-servers = [\"typescript-language-server\", \"jscpd\"]\n",[245,475,476,477,480,481,484,485,488,489,363],{},"A language gets the servers its ",[249,478,479],{},"language-servers"," list names, so the list has to keep the language's own server. Add a ",[249,482,483],{},"[[language]]"," table for every language you want jscpd in. The editor's settings go in the ",[249,486,487],{},"config"," key of the server: ",[249,490,491],{},"config = { lsp = { deadCode = { enabled = true } } }",[296,493,495],{"id":494},"sublime-text","Sublime Text",[245,497,498,499,505],{},"With the ",[287,500,504],{"href":501,"rel":502},"https:\u002F\u002Fpackagecontrol.io\u002Fpackages\u002FLSP",[503],"nofollow","LSP"," package, in Preferences > Package Settings > LSP > Settings:",[304,507,512],{"className":508,"code":509,"filename":510,"language":511,"meta":310,"style":310},"language-json shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","{\n  \"clients\": {\n    \"jscpd\": {\n      \"enabled\": true,\n      \"command\": [\"jscpd\", \"--lsp\"],\n      \"selector\": \"source.js | source.ts | source.python\"\n    }\n  }\n}\n","LSP.sublime-settings","json",[249,513,514,520,538,552,568,603,622,627,632],{"__ignoreMap":310},[314,515,516],{"class":316,"line":317},[314,517,519],{"class":518},"sMK4o","{\n",[314,521,522,525,529,532,535],{"class":316,"line":323},[314,523,524],{"class":518},"  \"",[314,526,528],{"class":527},"spNyl","clients",[314,530,531],{"class":518},"\"",[314,533,534],{"class":518},":",[314,536,537],{"class":518}," {\n",[314,539,540,543,546,548,550],{"class":316,"line":329},[314,541,542],{"class":518},"    \"",[314,544,280],{"class":545},"sBMFI",[314,547,531],{"class":518},[314,549,534],{"class":518},[314,551,537],{"class":518},[314,553,554,557,561,563,565],{"class":316,"line":335},[314,555,556],{"class":518},"      \"",[314,558,560],{"class":559},"sbssI","enabled",[314,562,531],{"class":518},[314,564,534],{"class":518},[314,566,567],{"class":518}," true,\n",[314,569,570,572,575,577,579,582,584,587,589,592,595,598,600],{"class":316,"line":341},[314,571,556],{"class":518},[314,573,574],{"class":559},"command",[314,576,531],{"class":518},[314,578,534],{"class":518},[314,580,581],{"class":518}," [",[314,583,531],{"class":518},[314,585,280],{"class":586},"sfazB",[314,588,531],{"class":518},[314,590,591],{"class":518},",",[314,593,594],{"class":518}," \"",[314,596,597],{"class":586},"--lsp",[314,599,531],{"class":518},[314,601,602],{"class":518},"],\n",[314,604,605,607,610,612,614,616,619],{"class":316,"line":399},[314,606,556],{"class":518},[314,608,609],{"class":559},"selector",[314,611,531],{"class":518},[314,613,534],{"class":518},[314,615,594],{"class":518},[314,617,618],{"class":586},"source.js | source.ts | source.python",[314,620,621],{"class":518},"\"\n",[314,623,624],{"class":316,"line":405},[314,625,626],{"class":518},"    }\n",[314,628,629],{"class":316,"line":411},[314,630,631],{"class":518},"  }\n",[314,633,634],{"class":316,"line":417},[314,635,636],{"class":518},"}\n",[245,638,639,640,363],{},"The editor's settings go in ",[249,641,642],{},"initialization_options",[296,644,646],{"id":645},"emacs","Emacs",[245,648,649],{},"lsp-mode runs a client marked as an add-on next to the language's own server:",[304,651,656],{"className":652,"code":653,"filename":654,"language":655,"meta":310,"style":310},"language-elisp shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","(with-eval-after-load 'lsp-mode\n  (lsp-register-client\n   (make-lsp-client\n    :new-connection (lsp-stdio-connection '(\"jscpd\" \"--lsp\"))\n    :activation-fn (lsp-activate-on \"javascript\" \"typescript\" \"python\")\n    :add-on? t\n    :server-id 'jscpd)))\n","init.el","elisp",[249,657,658,663,668,673,678,683,688],{"__ignoreMap":310},[314,659,660],{"class":316,"line":317},[314,661,662],{},"(with-eval-after-load 'lsp-mode\n",[314,664,665],{"class":316,"line":323},[314,666,667],{},"  (lsp-register-client\n",[314,669,670],{"class":316,"line":329},[314,671,672],{},"   (make-lsp-client\n",[314,674,675],{"class":316,"line":335},[314,676,677],{},"    :new-connection (lsp-stdio-connection '(\"jscpd\" \"--lsp\"))\n",[314,679,680],{"class":316,"line":341},[314,681,682],{},"    :activation-fn (lsp-activate-on \"javascript\" \"typescript\" \"python\")\n",[314,684,685],{"class":316,"line":399},[314,686,687],{},"    :add-on? t\n",[314,689,690],{"class":316,"line":405},[314,691,692],{},"    :server-id 'jscpd)))\n",[245,694,695],{},"Eglot connects one server to a buffer, so there jscpd would take the place of the language's own server.",[296,697,699],{"id":698},"jetbrains-ides","JetBrains IDEs",[245,701,702,703,708,709,711],{},"The ",[287,704,707],{"href":705,"rel":706},"https:\u002F\u002Fgithub.com\u002Fredhat-developer\u002Flsp4ij",[503],"LSP4IJ"," plugin starts a language server you define. In Settings > Languages & Frameworks > Language Servers, add a server with the command ",[249,710,251],{}," and map it to the file types you want.",[296,713,715],{"id":714},"vs-code","VS Code",[245,717,718,719,722,723,728,729,734,735,737,738,722,740,742,743,746,747,363],{},"VS Code starts language servers from extensions. The jscpd extension, ",[249,720,721],{},"kucherenko.jscpd"," on the ",[287,724,727],{"href":725,"rel":726},"https:\u002F\u002Fmarketplace.visualstudio.com\u002Fitems?itemName=kucherenko.jscpd",[503],"Visual Studio Marketplace"," and on ",[287,730,733],{"href":731,"rel":732},"https:\u002F\u002Fopen-vsx.org\u002Fextension\u002Fkucherenko\u002Fjscpd",[503],"Open VSX",", starts ",[249,736,251],{}," for the workspace. It looks for ",[249,739,280],{},[249,741,284],{}," and offers to download a release build when there is none, and its settings map to the ",[249,744,745],{},"lsp"," section described below. The source lives in ",[287,748,751],{"href":749,"rel":750},"https:\u002F\u002Fgithub.com\u002Fkucherenko\u002Fjscpd-ide",[503],"kucherenko\u002Fjscpd-ide",[296,753,755],{"id":754},"zed","Zed",[245,757,758,759,764],{},"Zed starts language servers from extensions too. A jscpd extension is in the same repository and is waiting for review in Zed's extension registry (",[287,760,763],{"href":761,"rel":762},"https:\u002F\u002Fgithub.com\u002Fzed-industries\u002Fextensions\u002Fpull\u002F7858",[503],"zed-industries\u002Fextensions#7858",").",[291,766,768],{"id":767},"reading-the-output","Reading the output",[245,770,771],{},"The server runs five analyses. Only clones are on unless you turn the others on:",[773,774,775,797],"table",{},[776,777,778],"thead",{},[779,780,781,785,791,794],"tr",{},[782,783,784],"th",{},"Analysis",[782,786,787,788],{},"Name in ",[249,789,790],{},"--lsp-analyses",[782,792,793],{},"What it reports",[782,795,796],{},"Rules",[798,799,800,826,848,868,900],"tbody",{},[779,801,802,806,811,814],{},[803,804,805],"td",{},"Clones",[803,807,808],{},[249,809,810],{},"clones",[803,812,813],{},"exact, renamed and near-miss copies",[803,815,816,819,820,819,823],{},[249,817,818],{},"jscpd\u002Fduplicate-code",", ",[249,821,822],{},"jscpd\u002Frenamed-code",[249,824,825],{},"jscpd\u002Fsimilar-code",[779,827,828,831,836,843],{},[803,829,830],{},"Similar functions",[803,832,833],{},[249,834,835],{},"ast",[803,837,838,839,842],{},"functions whose syntax trees have the same shape, as ",[249,840,841],{},"--similarity"," finds them",[803,844,845],{},[249,846,847],{},"jscpd\u002Fsimilar-function",[779,849,850,852,857,863],{},[803,851,90],{},[803,853,854],{},[249,855,856],{},"semantic",[803,858,859,860,842],{},"functions that do the same job, as ",[249,861,862],{},"--semantic",[803,864,865],{},[249,866,867],{},"jscpd\u002Fsemantic-code",[779,869,870,872,877,883],{},[803,871,80],{},[803,873,874],{},[249,875,876],{},"dead-code",[803,878,879,880,842],{},"unused files, exports, symbols, imports and members, as ",[249,881,882],{},"--dead-code",[803,884,885,819,888,819,891,819,894,819,897],{},[249,886,887],{},"unused-file",[249,889,890],{},"unused-export",[249,892,893],{},"unused-symbol",[249,895,896],{},"unused-import",[249,898,899],{},"unused-member",[779,901,902,905,910,913],{},[803,903,904],{},"Complexity",[803,906,907],{},[249,908,909],{},"complexity",[803,911,912],{},"functions and files above a complexity limit",[803,914,915,819,918],{},[249,916,917],{},"jscpd\u002Fcomplex-function",[249,919,920],{},"jscpd\u002Fcomplex-file",[245,922,923,924,926],{},"Every diagnostic has the source ",[249,925,280],{},", and its code is the id of its rule, which an editor can filter by. For clones and dead code, the SARIF reporters write the same ids, so a finding has one name in the editor and in code scanning.",[245,928,929,936,937,940],{},[287,930,933],{"href":931,"rel":932},"https:\u002F\u002Fgithub.com\u002Fkucherenko\u002Fjscpd\u002Ftree\u002Fmaster\u002Ffixtures\u002Flsp-demo",[503],[249,934,935],{},"fixtures\u002Flsp-demo"," in the jscpd repository is a small library-loans module whose ",[249,938,939],{},".jscpd.json"," turns on similar functions, dead code and complexity. Headless Neovim prints what the server publishes for one of its files:",[304,942,947],{"className":943,"code":944,"filename":945,"language":946,"meta":310,"style":310},"language-bash shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","cd fixtures\u002Flsp-demo\nnvim --headless --clean src\u002Floans.js -c \"lua vim.lsp.start({ name = 'jscpd', cmd = { 'jscpd', '--lsp' }, root_dir = vim.fn.getcwd() })\" -c \"lua vim.wait(4000)\" -c \"lua for _, d in ipairs(vim.diagnostic.get(0)) do print(d.lnum + 1, d.code, d.message) end\" -c \"qa!\"\n","Terminal","bash",[249,948,949,958],{"__ignoreMap":310},[314,950,951,955],{"class":316,"line":317},[314,952,954],{"class":953},"s2Zo4","cd",[314,956,957],{"class":586}," fixtures\u002Flsp-demo\n",[314,959,960,963,966,969,972,975,977,980,982,984,986,989,991,993,995,998,1000,1002,1004,1007],{"class":316,"line":323},[314,961,962],{"class":545},"nvim",[314,964,965],{"class":586}," --headless",[314,967,968],{"class":586}," --clean",[314,970,971],{"class":586}," src\u002Floans.js",[314,973,974],{"class":586}," -c",[314,976,594],{"class":518},[314,978,979],{"class":586},"lua vim.lsp.start({ name = 'jscpd', cmd = { 'jscpd', '--lsp' }, root_dir = vim.fn.getcwd() })",[314,981,531],{"class":518},[314,983,974],{"class":586},[314,985,594],{"class":518},[314,987,988],{"class":586},"lua vim.wait(4000)",[314,990,531],{"class":518},[314,992,974],{"class":586},[314,994,594],{"class":518},[314,996,997],{"class":586},"lua for _, d in ipairs(vim.diagnostic.get(0)) do print(d.lnum + 1, d.code, d.message) end",[314,999,531],{"class":518},[314,1001,974],{"class":586},[314,1003,594],{"class":518},[314,1005,1006],{"class":586},"qa!",[314,1008,621],{"class":518},[304,1010,1015],{"className":1011,"code":1013,"language":1014,"meta":310},[1012],"language-text","4 jscpd\u002Fduplicate-code Duplicated in src\u002Fholds.js:4-13 (80 tokens)\n13 jscpd\u002Fsimilar-function Same structure as the function at src\u002Fholds.js:13-20 (1.00)\n22 jscpd\u002Fcomplex-function Complexity 20 in loanStatus, over the limit of 15\n","text",[249,1016,1013],{"__ignoreMap":310},[245,1018,1019,1020,728,1023,1026],{},"The same command on ",[249,1021,1022],{},"src\u002Findex.js",[249,1024,1025],{},"src\u002Fformat.js"," prints the dead code, faded in an editor instead of underlined:",[304,1028,1031],{"className":1029,"code":1030,"language":1014,"meta":310},[1012],"3 unused-import `padLeft` is imported but never used (100%)\n",[249,1032,1030],{"__ignoreMap":310},[304,1034,1037],{"className":1035,"code":1036,"language":1014,"meta":310},[1012],"7 unused-export exported function `formatFine` is never imported (85%)\n",[249,1038,1036],{"__ignoreMap":310},[245,1040,1041,1042,1045,1046,1049],{},"Each clone comes with two code actions. \"Go to the other copy in src\u002Fholds.js:4-13\" opens that copy, with one action per copy. \"Ignore this clone\" puts ",[249,1043,1044],{},"jscpd:ignore-start"," and ",[249,1047,1048],{},"jscpd:ignore-end"," comments around the fragment, in the comment syntax of the file's language. A hover over a clone shows the message and the first lines of the other copy. Edit the copied function in one of the files, and the clone diagnostic goes away before you save.",[291,1051,1053],{"id":1052},"make-it-useful","Make it useful",[296,1055,1057],{"id":1056},"turn-on-more-analyses","Turn on more analyses",[245,1059,1060],{},"Options come from three places, and each wins over the one before it:",[1062,1063,1064,1081,1086],"ol",{},[1065,1066,1067,1068,819,1071,260,1074,1077,1078,1080],"li",{},"The command line that starts the server. Detection options such as ",[249,1069,1070],{},"--min-tokens",[249,1072,1073],{},"--format",[249,1075,1076],{},"--ignore",", and ",[249,1079,790],{},", are the defaults for every project.",[1065,1082,1083,1084,363],{},"The project's ",[249,1085,939],{},[1065,1087,1088,1089,1092,1093,1096,1097,1099,1100,1102],{},"The editor's settings, which the editor sends when it starts the server (",[249,1090,1091],{},"initializationOptions",") and when they change (",[249,1094,1095],{},"workspace\u002FdidChangeConfiguration","). They take the keys of ",[249,1098,939],{},", at the top level or under a ",[249,1101,280],{}," key, and apply to every project on top of its config.",[245,1104,1105,1106,260,1109,1112,1113,1115,1116,819,1118,1045,1120,1123,1124,1077,1126,1129,1130,1132],{},"A change to a config file or to the editor's settings applies at once, without a restart. ",[249,1107,1108],{},"--lsp-analyses clones,ast,dead-code,complexity",[249,1110,1111],{},"--lsp-analyses all"," turns analyses on for every project; the ",[249,1114,745],{}," section below does it per project; the editor's settings do it for the whole editor. jscpd refuses ",[249,1117,862],{},[249,1119,882],{},[249,1121,1122],{},"--complexity"," together with ",[249,1125,597],{},[249,1127,1128],{},"--config"," and paths too, because each project reads its own ",[249,1131,939],{}," and the folders come from the editor:",[304,1134,1137],{"className":1135,"code":1136,"language":1014,"meta":310},[1012],"Error: the argument '--lsp' cannot be used with '--dead-code': the server runs it as an analysis, turn it on with --lsp-analyses dead-code\n",[249,1138,1136],{"__ignoreMap":310},[245,1140,1141,1142,1144,1145,1147],{},"A mistake in the ",[249,1143,745],{}," settings of the editor, such as an unknown key, is shown to the user, and those settings are left out while the ",[249,1146,745],{}," section of each config still applies.",[296,1149,1151],{"id":1150},"split-a-workspace-into-projects","Split a workspace into projects",[245,1153,702,1154,1156],{},[249,1155,939],{}," files split the workspace into projects, and the server looks for clones within a project, never across two:",[1158,1159,1160,1166,1172],"ul",{},[1065,1161,1162,1163,1165],{},"With no ",[249,1164,939],{}," in the workspace, the workspace is one project, whatever the number of its folders, and a clone between two folders counts.",[1065,1167,1168,1169,1171],{},"Each ",[249,1170,939],{}," makes its folder a project with that config. A config in a subfolder of another project splits that subfolder out, so a file belongs to the project of the nearest config above it.",[1065,1173,1174],{},"The files under no config form one more project, with the defaults.",[245,1176,1177,1178,1180,1181,819,1184,1187,1188,1190,1191,1193,1194,1196,1197,1200,1201,1203,1204,1207],{},"When it starts, the server looks for ",[249,1179,939],{}," files, skipping ",[249,1182,1183],{},".git",[249,1185,1186],{},"node_modules"," and what git ignores. It scans the workspace again when a config is saved in the editor, and when the editor reports that a config, a ",[249,1189,259],{}," or an ",[249,1192,263],{}," file appeared, changed or went away. Only ",[249,1195,939],{}," makes a project: the server does not read ",[249,1198,1199],{},".config\u002Fjscpd.json"," or the ",[249,1202,280],{}," key of ",[249,1205,1206],{},"package.json",", which the CLI also reads.",[245,1209,1210,1211,1213,1214,1216,1217,1220],{},"A ",[249,1212,939],{}," that is not valid JSON leaves its project on the defaults, and the editor shows the parse error. A key in the ",[249,1215,856],{}," section that looks like a secret, such as ",[249,1218,1219],{},"apiKey",", stops the project: the editor shows why, and the project gets no diagnostics until the key is gone. Other warnings about a config, such as an unknown key, go to the editor's log.",[296,1222,1224],{"id":1223},"list-the-whole-project-in-the-problems-panel","List the whole project in the problems panel",[245,1226,1227,1228,1231,1232,1234],{},"By default a file gets diagnostics while it is open. ",[249,1229,1230],{},"allFiles: true"," in the ",[249,1233,745],{}," section also publishes the diagnostics of closed files that have clones, semantic pairs or dead code, so a problems panel lists the whole project. A closed file whose only finding is its complexity stays out, since listing it would mean parsing every file of the project after each change.",[291,1236,702,1238,1240],{"id":1237},"the-lsp-section",[249,1239,745],{}," section",[304,1242,1244],{"className":508,"code":1243,"filename":939,"language":511,"meta":310,"style":310},"{\n  \"lsp\": {\n    \"clones\": { \"enabled\": true, \"warningTokens\": 100 },\n    \"ast\": { \"enabled\": true, \"similarity\": 0.85 },\n    \"semantic\": { \"enabled\": false },\n    \"deadCode\": { \"enabled\": true },\n    \"complexity\": { \"enabled\": true, \"functionLimit\": 15 },\n    \"allFiles\": false\n  }\n}\n",[249,1245,1246,1250,1262,1301,1337,1362,1388,1424,1438,1442],{"__ignoreMap":310},[314,1247,1248],{"class":316,"line":317},[314,1249,519],{"class":518},[314,1251,1252,1254,1256,1258,1260],{"class":316,"line":323},[314,1253,524],{"class":518},[314,1255,745],{"class":527},[314,1257,531],{"class":518},[314,1259,534],{"class":518},[314,1261,537],{"class":518},[314,1263,1264,1266,1268,1270,1272,1275,1277,1279,1281,1283,1286,1288,1291,1293,1295,1298],{"class":316,"line":329},[314,1265,542],{"class":518},[314,1267,810],{"class":545},[314,1269,531],{"class":518},[314,1271,534],{"class":518},[314,1273,1274],{"class":518}," {",[314,1276,594],{"class":518},[314,1278,560],{"class":559},[314,1280,531],{"class":518},[314,1282,534],{"class":518},[314,1284,1285],{"class":518}," true,",[314,1287,594],{"class":518},[314,1289,1290],{"class":559},"warningTokens",[314,1292,531],{"class":518},[314,1294,534],{"class":518},[314,1296,1297],{"class":559}," 100",[314,1299,1300],{"class":518}," },\n",[314,1302,1303,1305,1307,1309,1311,1313,1315,1317,1319,1321,1323,1325,1328,1330,1332,1335],{"class":316,"line":335},[314,1304,542],{"class":518},[314,1306,835],{"class":545},[314,1308,531],{"class":518},[314,1310,534],{"class":518},[314,1312,1274],{"class":518},[314,1314,594],{"class":518},[314,1316,560],{"class":559},[314,1318,531],{"class":518},[314,1320,534],{"class":518},[314,1322,1285],{"class":518},[314,1324,594],{"class":518},[314,1326,1327],{"class":559},"similarity",[314,1329,531],{"class":518},[314,1331,534],{"class":518},[314,1333,1334],{"class":559}," 0.85",[314,1336,1300],{"class":518},[314,1338,1339,1341,1343,1345,1347,1349,1351,1353,1355,1357,1360],{"class":316,"line":341},[314,1340,542],{"class":518},[314,1342,856],{"class":545},[314,1344,531],{"class":518},[314,1346,534],{"class":518},[314,1348,1274],{"class":518},[314,1350,594],{"class":518},[314,1352,560],{"class":559},[314,1354,531],{"class":518},[314,1356,534],{"class":518},[314,1358,1359],{"class":518}," false",[314,1361,1300],{"class":518},[314,1363,1364,1366,1369,1371,1373,1375,1377,1379,1381,1383,1386],{"class":316,"line":399},[314,1365,542],{"class":518},[314,1367,1368],{"class":545},"deadCode",[314,1370,531],{"class":518},[314,1372,534],{"class":518},[314,1374,1274],{"class":518},[314,1376,594],{"class":518},[314,1378,560],{"class":559},[314,1380,531],{"class":518},[314,1382,534],{"class":518},[314,1384,1385],{"class":518}," true",[314,1387,1300],{"class":518},[314,1389,1390,1392,1394,1396,1398,1400,1402,1404,1406,1408,1410,1412,1415,1417,1419,1422],{"class":316,"line":405},[314,1391,542],{"class":518},[314,1393,909],{"class":545},[314,1395,531],{"class":518},[314,1397,534],{"class":518},[314,1399,1274],{"class":518},[314,1401,594],{"class":518},[314,1403,560],{"class":559},[314,1405,531],{"class":518},[314,1407,534],{"class":518},[314,1409,1285],{"class":518},[314,1411,594],{"class":518},[314,1413,1414],{"class":559},"functionLimit",[314,1416,531],{"class":518},[314,1418,534],{"class":518},[314,1420,1421],{"class":559}," 15",[314,1423,1300],{"class":518},[314,1425,1426,1428,1431,1433,1435],{"class":316,"line":411},[314,1427,542],{"class":518},[314,1429,1430],{"class":545},"allFiles",[314,1432,531],{"class":518},[314,1434,534],{"class":518},[314,1436,1437],{"class":518}," false\n",[314,1439,1440],{"class":316,"line":417},[314,1441,631],{"class":518},[314,1443,1444],{"class":316,"line":423},[314,1445,636],{"class":518},[773,1447,1448,1461],{},[776,1449,1450],{},[779,1451,1452,1455,1458],{},[782,1453,1454],{},"Key",[782,1456,1457],{},"What it does",[782,1459,1460],{},"Default",[798,1462,1463,1481,1494,1510,1523],{},[779,1464,1465,1470,1475],{},[803,1466,1467],{},[249,1468,1469],{},"\u003Canalysis>.enabled",[803,1471,1472,1473],{},"turns the analysis on or off, over ",[249,1474,790],{},[803,1476,1477,1478,1480],{},"on for ",[249,1479,810],{},", off for the rest",[779,1482,1483,1488,1491],{},[803,1484,1485],{},[249,1486,1487],{},"clones.warningTokens",[803,1489,1490],{},"clones of at least this many tokens are warnings, and smaller ones information",[803,1492,1493],{},"every clone is a warning",[779,1495,1496,1501,1504],{},[803,1497,1498],{},[249,1499,1500],{},"ast.similarity",[803,1502,1503],{},"how much of their syntax-tree shape two functions must share, from 0 to 1",[803,1505,1506,1507,1509],{},"the ",[249,1508,1327],{}," key of the config when it is below 1, else 0.85",[779,1511,1512,1517,1520],{},[803,1513,1514],{},[249,1515,1516],{},"complexity.functionLimit",[803,1518,1519],{},"a function above this complexity gets a diagnostic",[803,1521,1522],{},"15",[779,1524,1525,1529,1532],{},[803,1526,1527],{},[249,1528,1430],{},[803,1530,1531],{},"also publish the diagnostics of closed files that have clones, semantic pairs or dead code",[803,1533,1534],{},[249,1535,1536],{},"false",[245,1538,1539,1540,1542,1543,1545,1546,1548,1549,1551,1552,1554,1555,1558,1559,1562],{},"The other options of each analysis stay where the CLI reads them: detection options at the top level of ",[249,1541,939],{},", the model and thresholds in its ",[249,1544,856],{}," section, and entry points, categories and the confidence floor in its ",[249,1547,1368],{}," section. The ",[249,1550,560],{}," key of the top-level ",[249,1553,1368],{}," section picks the CLI's mode and does not turn the analysis on in the server; the one in ",[249,1556,1557],{},"lsp.deadCode"," does. ",[287,1560,1561],{"href":126},"Configuration file"," lists the keys.",[291,1564,1566],{"id":1565},"what-each-analysis-reports","What each analysis reports",[296,1568,805],{"id":810},[245,1570,1571,1572,1575,1576,1579,1580,1583],{},"Each fragment of a clone in an open file gets a warning over its range, as in SARIF, and a clone within one file gets one on each of its ranges. The message names the other copy, such as ",[249,1573,1574],{},"Duplicated in src\u002Fholds.js:4-13 (80 tokens)",", or all of them when a block has several: ",[249,1577,1578],{},"Duplicated in 3 places: ...",". In a project with several folders, a path starts with the name of its folder. Editors that support ",[249,1581,1582],{},"relatedInformation"," also list each copy as a link.",[245,1585,1586,1587,819,1590,260,1593,1596,1597,1600],{},"Renamed and near-miss copies need the options that find them on the command line: ",[249,1588,1589],{},"ignoreIdentifiers",[249,1591,1592],{},"ignoreLiterals",[249,1594,1595],{},"ignoreAnnotations"," for renamed code, and ",[249,1598,1599],{},"maxGapLines"," for copies with a few changed lines between them.",[245,1602,1603,1604,260,1607,1610],{},"The \"Ignore this clone\" action writes the ignore comments on lines of their own where the fragment starts and ends its lines, and as block comments beside it where other code shares a line with it. The first line of a file that starts with ",[249,1605,1606],{},"#!",[249,1608,1609],{},"\u003C?"," stays first.",[296,1612,830],{"id":1613},"similar-functions",[245,1615,1616,1617,1619,1620,1622,1623,1626],{},"The server runs the pass of ",[249,1618,841],{}," on JavaScript, TypeScript, JSX and TSX: it summarizes the syntax tree of every function, and two functions pair when their summaries share at least the ratio ",[249,1621,1500],{}," asks for. A pair gets one diagnostic of severity information on the first line of each function: ",[249,1624,1625],{},"Same structure as the function at src\u002Fholds.js:13-20 (1.00)",". A pair that a clone already covers is not reported again. \"Go to the similar function\" and the hover work as they do for clones.",[296,1628,90],{"id":1629},"semantic-clones",[245,1631,1632,1633,1635,1636,1638,1639,1642],{},"Semantic clones come from the pass of ",[249,1634,862],{},", with its languages and thresholds. The server runs it in the background when it starts and after each save, and the editor shows its progress. The first run embeds every function; the vectors go to the cache on disk that ",[249,1637,862],{}," keeps, so later runs embed only the functions that changed. A pair gets one diagnostic of severity information on the first line of each function: ",[249,1640,1641],{},"Does the same job as the function at src\u002Fmoney.ts:3-9 (0.87)",", with \"Go to the similar function\" and a hover as for clones. The pairs come from the files on disk, so while either file differs from what the last run read, as after an unsaved edit, its pairs are hidden until the next run.",[245,1644,1645,1646,1649,1650,1652],{},"The server never downloads the model on its own. When the model is missing, it asks first, and it shows the download as progress. ",[249,1647,1648],{},"jscpd --semantic-download"," downloads it from the command line instead. ",[287,1651,90],{"href":91}," covers the model and its thresholds.",[245,1654,1210,1655,1658,1659,1661,1662,1665,1666,363],{},[249,1656,1657],{},"semantic.url"," in ",[249,1660,939],{}," or in the editor's settings is used only when it points to this machine, since a config can arrive with the code. To use a remote embeddings API, give it with ",[249,1663,1664],{},"--semantic-url"," in the command that starts the server, and the key in ",[249,1667,1668],{},"JSCPD_SEMANTIC_API_KEY",[296,1670,80],{"id":876},[245,1672,1673,1674,1678,1679,1682,1683,1686,1687,1690],{},"Dead code comes from basta, the analysis behind ",[287,1675,1676],{"href":81},[249,1677,882],{},", which reads JavaScript, TypeScript, JSX, TSX, Vue, Svelte, Astro and Python. The import graph spans the project, so the server runs basta in the background when it starts and after each save, on the files on disk, and the editor shows its progress. A finding at or above the confidence floor (",[249,1680,1681],{},"minConfidence",", 60 by default) gets a diagnostic of severity hint with the ",[249,1684,1685],{},"Unnecessary"," tag, so editors fade the code. The message carries the confidence, such as ",[249,1688,1689],{},"exported function `formatFine` is never imported (85%)",", and the hover lists the reasons behind a score below 100. An unused file gets one diagnostic on its first line. The findings come from the files on disk, so while a file differs from what the last run read, as after an unsaved edit, its findings are hidden until the run after the next save.",[245,1692,1693,1694,1696,1697,1700],{},"Mistakes in the ",[249,1695,1368],{}," section, such as an unknown category, are shown to the user, and dead code stays off for that project. The server leaves Rust to rust-analyzer, so ",[249,1698,1699],{},"deadCode.rustDiagnostics"," has no effect here. The server offers no code action that deletes code.",[296,1702,904],{"id":909},[245,1704,1705,1706,1045,1709,1711,1712,819,1715,819,1718,819,1721,819,1724,819,1727,819,1730,1733,1734,1737],{},"The measure is the cyclomatic estimate of ",[249,1707,1708],{},"--summary",[249,1710,1122],{},": one path, plus one for every branch token (",[249,1713,1714],{},"if",[249,1716,1717],{},"for",[249,1719,1720],{},"while",[249,1722,1723],{},"case",[249,1725,1726],{},"catch",[249,1728,1729],{},"&&",[249,1731,1732],{},"||",", a ternary ",[249,1735,1736],{},"?"," and the rest). The server counts it per function too, over the range of each function it finds in JavaScript, TypeScript, JSX, TSX, Vue, Svelte, Astro, Python, Rust, Go, Java, Kotlin, C#, C, C++, PHP, Ruby, Scala and Swift. It recounts a file from the buffer after each edit.",[1158,1739,1740,1752],{},[1065,1741,1742,1743,1745,1746,1748,1749,363],{},"A function above ",[249,1744,1516],{}," gets a ",[249,1747,917],{}," diagnostic of severity information on its first line: ",[249,1750,1751],{},"Complexity 20 in loanStatus, over the limit of 15",[1065,1753,1754,1755,285,1758,1761,1762,1764],{},"A file at or above the complex-file bar of the ",[287,1756,1757],{"href":111},"health score",[249,1759,1760],{},"health.complexFile",", 50 by default) gets a ",[249,1763,920],{}," diagnostic on its first line.",[291,1766,1768],{"id":1767},"requests-for-editor-clients","Requests for editor clients",[245,1770,1771,1772,1775,1776,1779,1780,1782],{},"Clients with views of their own, such as a tree of clones, need data that diagnostics do not carry. The server answers these requests. Each answer holds one entry per project in ",[249,1773,1774],{},"projects",", with its ",[249,1777,1778],{},"roots"," and its ",[249,1781,487],{}," file:",[773,1784,1785,1795],{},[776,1786,1787],{},[779,1788,1789,1792],{},[782,1790,1791],{},"Request",[782,1793,1794],{},"Each project's entry",[798,1796,1797,1816,1828,1844,1864],{},[779,1798,1799,1804],{},[803,1800,1801],{},[249,1802,1803],{},"jscpd\u002Fclones",[803,1805,1806,1045,1809,1812,1813],{},[249,1807,1808],{},"statistics",[249,1810,1811],{},"duplicates",", as in ",[249,1814,1815],{},"jscpd-report.json",[779,1817,1818,1823],{},[803,1819,1820],{},[249,1821,1822],{},"jscpd\u002Fsemantic",[803,1824,1825,1827],{},[249,1826,1811],{}," of the semantic pairs, for projects with the analysis on",[779,1829,1830,1835],{},[803,1831,1832],{},[249,1833,1834],{},"jscpd\u002FdeadCode",[803,1836,1837,1812,1840,1843],{},[249,1838,1839],{},"findings",[249,1841,1842],{},"basta-report.json"," but with absolute paths, for projects with the analysis on",[779,1845,1846,1851],{},[803,1847,1848],{},[249,1849,1850],{},"jscpd\u002Fcomplexity",[803,1852,1853,1812,1856,1859,1860,1863],{},[249,1854,1855],{},"summary",[249,1857,1858],{},"jscpd-complexity.json"," with ",[249,1861,1862],{},"--absolute",", with the duplication columns filled in",[779,1865,1866,1871],{},[803,1867,1868],{},[249,1869,1870],{},"jscpd\u002Fstatistics",[803,1872,1873,1874,1877,1878],{},"the number of ",[249,1875,1876],{},"files"," and the ",[249,1879,1808],{},[245,1881,1882,1885,1886,1889,1890,1893,1894,363],{},[249,1883,1884],{},"jscpd\u002Frescan"," scans every project again and answers ",[249,1887,1888],{},"null",". The command ",[249,1891,1892],{},"jscpd.showLocation",", with a URI and a range as its arguments, is the one behind \"Go to the other copy\". The server answers it with ",[249,1895,1896],{},"window\u002FshowDocument",[291,1898,1900],{"id":1899},"positions-and-logs","Positions and logs",[245,1902,1903,1904,260,1907,1910],{},"The progress of a scan and of each background run ends with what it found, such as ",[249,1905,1906],{},"94 files, 13 clones",[249,1908,1909],{},"4 found",", so an editor that shows progress also shows that the server runs in a project with nothing to report in the open file.",[245,1912,1913,1914,260,1916,1918],{},"Lines count from 0, and columns count UTF-16 code units unless the editor offers UTF-8. stdout carries protocol messages only. The server shows the user what changes what runs, such as a config that does not parse or a wrong ",[249,1915,745],{},[249,1917,1368],{}," section, and writes other warnings to the editor's log; it writes to stderr only when it cannot start or loses the connection.",[291,1920,1922],{"id":1921},"limits","Limits",[1158,1924,1925,1928,1935,1944],{},[1065,1926,1927],{},"Semantic clones and dead code update after a save; clones, similar functions and complexity update as you type.",[1065,1929,1930,1931,1934],{},"The server pushes diagnostics and does not answer ",[249,1932,1933],{},"textDocument\u002Fdiagnostic"," requests.",[1065,1936,1937,1938,1943],{},"An edit searches the whole detection pool of its file again, on the thread that answers the editor. In a pool of about 12,700 JavaScript files that takes several seconds; looking up only the changed file (",[287,1939,1942],{"href":1940,"rel":1941},"https:\u002F\u002Fgithub.com\u002Fkucherenko\u002Fjscpd\u002Fissues\u002F1001",[503],"#1001",") would fix that.",[1065,1945,1946,1947,1950],{},"With ",[249,1948,1949],{},"followSymlinks",", files reached through a symlinked folder are scanned but get no diagnostics of their own.",[291,1952,1954],{"id":1953},"related","Related",[1158,1956,1957,1962,1970,1978],{},[1065,1958,1959,1961],{},[287,1960,60],{"href":61}," gives an AI assistant the same project through the MCP server.",[1065,1963,1964,1966,1967,1969],{},[287,1965,80],{"href":81}," explains the findings, their confidence and the ",[249,1968,1368],{}," section the server reads.",[1065,1971,1972,1974,1975,1977],{},[287,1973,90],{"href":91}," covers the model behind the ",[249,1976,856],{}," analysis.",[1065,1979,1980,1982,1983,363],{},[287,1981,1561],{"href":126}," lists every key of ",[249,1984,939],{},[1986,1987,1988],"style",{},"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}html pre.shiki code .sBMFI, html code.shiki .sBMFI{--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B}html pre.shiki code .sbssI, html code.shiki .sbssI{--shiki-light:#F76D47;--shiki-default:#F78C6C;--shiki-dark:#F78C6C}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}html pre.shiki code .s2Zo4, html code.shiki .s2Zo4{--shiki-light:#6182B8;--shiki-default:#82AAFF;--shiki-dark:#82AAFF}",{"title":310,"searchDepth":323,"depth":323,"links":1990},[1991,2000,2001,2006,2008,2015,2016,2017,2018],{"id":293,"depth":323,"text":294,"children":1992},[1993,1994,1995,1996,1997,1998,1999],{"id":298,"depth":329,"text":299},{"id":428,"depth":329,"text":429},{"id":494,"depth":329,"text":495},{"id":645,"depth":329,"text":646},{"id":698,"depth":329,"text":699},{"id":714,"depth":329,"text":715},{"id":754,"depth":329,"text":755},{"id":767,"depth":323,"text":768},{"id":1052,"depth":323,"text":1053,"children":2002},[2003,2004,2005],{"id":1056,"depth":329,"text":1057},{"id":1150,"depth":329,"text":1151},{"id":1223,"depth":329,"text":1224},{"id":1237,"depth":323,"text":2007},"The lsp section",{"id":1565,"depth":323,"text":1566,"children":2009},[2010,2011,2012,2013,2014],{"id":810,"depth":329,"text":805},{"id":1613,"depth":329,"text":830},{"id":1629,"depth":329,"text":90},{"id":876,"depth":329,"text":80},{"id":909,"depth":329,"text":904},{"id":1767,"depth":323,"text":1768},{"id":1899,"depth":323,"text":1900},{"id":1921,"depth":323,"text":1922},{"id":1953,"depth":323,"text":1954},"Run jscpd --lsp as a language server so Neovim, Helix, Sublime Text, Emacs, JetBrains IDEs and VS Code show clones, dead code and complexity as diagnostics while you type.","md",null,{},{"icon":68},{"title":2025,"description":2026},"jscpd in your editor with --lsp","Setup for Neovim, Helix, Sublime Text, Emacs, JetBrains IDEs and VS Code: jscpd --lsp reports clones, similar functions, semantic clones, dead code and complexity as diagnostics that follow the text.","boANwUnmuHblN_OUOiVRxmOUxK0zCNJwg_lP4m7Cj5A",[2029,2031],{"title":60,"path":61,"stem":62,"description":2030,"icon":63,"children":-1},"Give Claude, Cursor, Copilot and other coding agents jscpd through the MCP server, five installable skills and the ai reporter.",{"title":70,"path":71,"stem":72,"description":2032,"icon":73,"children":-1},"Scan every commit in a range with --history and see whether duplication is going up or down, as a chart and a table in the terminal.",1791302983880]