jj-plus 0.10.10
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.
- package/CHANGELOG.md +30 -0
- package/README.md +102 -0
- package/assets/jj-plus-icon.png +0 -0
- package/dist/_virtual/_rolldown/runtime.cjs +23 -0
- package/dist/bin.cjs +112 -0
- package/dist/bin.cjs.map +1 -0
- package/dist/cli/options.cjs +376 -0
- package/dist/cli/options.cjs.map +1 -0
- package/dist/cli/timeline-json.cjs +29 -0
- package/dist/cli/timeline-json.cjs.map +1 -0
- package/dist/extension/blame-codelens.cjs +228 -0
- package/dist/extension/blame-codelens.cjs.map +1 -0
- package/dist/extension/command-runner.cjs +99 -0
- package/dist/extension/command-runner.cjs.map +1 -0
- package/dist/extension/constants.cjs +56 -0
- package/dist/extension/constants.cjs.map +1 -0
- package/dist/extension/editor-extra-features.cjs +464 -0
- package/dist/extension/editor-extra-features.cjs.map +1 -0
- package/dist/extension/history-adapters.cjs +702 -0
- package/dist/extension/history-adapters.cjs.map +1 -0
- package/dist/extension/index.cjs +657 -0
- package/dist/extension/index.cjs.map +1 -0
- package/dist/extension/repo-timeline-panel.cjs +402 -0
- package/dist/extension/repo-timeline-panel.cjs.map +1 -0
- package/dist/extension/resolve-file-path.cjs +16 -0
- package/dist/extension/resolve-file-path.cjs.map +1 -0
- package/dist/extension/revision-diff-nav.cjs +169 -0
- package/dist/extension/revision-diff-nav.cjs.map +1 -0
- package/dist/extension/settings.cjs +41 -0
- package/dist/extension/settings.cjs.map +1 -0
- package/dist/extension/timeline-at-line.cjs +72 -0
- package/dist/extension/timeline-at-line.cjs.map +1 -0
- package/dist/extension/timeline-panel.cjs +1113 -0
- package/dist/extension/timeline-panel.cjs.map +1 -0
- package/dist/extension/timeline-service.cjs +1154 -0
- package/dist/extension/timeline-service.cjs.map +1 -0
- package/dist/extension/uri-utils.cjs +75 -0
- package/dist/extension/uri-utils.cjs.map +1 -0
- package/dist/package.cjs +11 -0
- package/dist/package.cjs.map +1 -0
- package/dist/shared/blame.cjs +211 -0
- package/dist/shared/blame.cjs.map +1 -0
- package/dist/shared/diff-content-search.cjs +23 -0
- package/dist/shared/diff-content-search.cjs.map +1 -0
- package/dist/shared/diff-helpers.cjs +12 -0
- package/dist/shared/diff-helpers.cjs.map +1 -0
- package/dist/shared/diff-stats.cjs +129 -0
- package/dist/shared/diff-stats.cjs.map +1 -0
- package/dist/shared/file-oplog.cjs +82 -0
- package/dist/shared/file-oplog.cjs.map +1 -0
- package/dist/shared/history-helpers.cjs +257 -0
- package/dist/shared/history-helpers.cjs.map +1 -0
- package/dist/shared/history-search.cjs +85 -0
- package/dist/shared/history-search.cjs.map +1 -0
- package/dist/shared/jj-revset-suggestions.cjs +41 -0
- package/dist/shared/jj-revset-suggestions.cjs.map +1 -0
- package/dist/shared/line-history.cjs +181 -0
- package/dist/shared/line-history.cjs.map +1 -0
- package/dist/shared/list-file-revisions.cjs +36 -0
- package/dist/shared/list-file-revisions.cjs.map +1 -0
- package/dist/shared/non-textual-details.cjs +43 -0
- package/dist/shared/non-textual-details.cjs.map +1 -0
- package/dist/shared/resolve-file-path.cjs +14 -0
- package/dist/shared/resolve-file-path.cjs.map +1 -0
- package/dist/shared/revision-diff-availability.cjs +18 -0
- package/dist/shared/revision-diff-availability.cjs.map +1 -0
- package/dist/shared/revision-diff-pair.cjs +42 -0
- package/dist/shared/revision-diff-pair.cjs.map +1 -0
- package/dist/shared/revset.cjs +16 -0
- package/dist/shared/revset.cjs.map +1 -0
- package/dist/shared/timeline-model.cjs +29 -0
- package/dist/shared/timeline-model.cjs.map +1 -0
- package/dist/standalone/server.cjs +1733 -0
- package/dist/standalone/server.cjs.map +1 -0
- package/dist/webview/timeline-template.cjs +214 -0
- package/dist/webview/timeline-template.cjs.map +1 -0
- package/package.json +320 -0
- package/webview-dist/assets/worker-DuV13XDT.js +161 -0
- package/webview-dist/chunks/abap-CLvhMVsD.js +1 -0
- package/webview-dist/chunks/actionscript-3-Cs6JJXT5.js +1 -0
- package/webview-dist/chunks/ada-C5qYipkI.js +1 -0
- package/webview-dist/chunks/ahk-CA9gdOdF.js +1 -0
- package/webview-dist/chunks/ahk2-_6rzW9BS.js +1 -0
- package/webview-dist/chunks/andromeeda-vGVdxbeo.js +1 -0
- package/webview-dist/chunks/angular-html-DTpR04n4.js +1 -0
- package/webview-dist/chunks/angular-ts-BCtbMvbl.js +1 -0
- package/webview-dist/chunks/apache-U0d_L8uA.js +1 -0
- package/webview-dist/chunks/apex-KFGFyKwG.js +1 -0
- package/webview-dist/chunks/apl-BMZuN1Ql.js +1 -0
- package/webview-dist/chunks/applescript-CCn79oCD.js +1 -0
- package/webview-dist/chunks/ara-4CJ0cIlV.js +1 -0
- package/webview-dist/chunks/asciidoc-SCjQUq34.js +1 -0
- package/webview-dist/chunks/asm-Cmm7eHzH.js +1 -0
- package/webview-dist/chunks/astro-DrbV4Z7r.js +1 -0
- package/webview-dist/chunks/aurora-x-CDeNXAV0.js +1 -0
- package/webview-dist/chunks/awk-BWXHIvNe.js +1 -0
- package/webview-dist/chunks/ayu-dark-DluEY0Gj.js +1 -0
- package/webview-dist/chunks/ayu-light-C3h-C4tm.js +1 -0
- package/webview-dist/chunks/ayu-mirage-Bqwy1Gya.js +1 -0
- package/webview-dist/chunks/ballerina-B7ZEbQpA.js +1 -0
- package/webview-dist/chunks/bat-qdxAdBWQ.js +1 -0
- package/webview-dist/chunks/beancount-D-usSTwE.js +1 -0
- package/webview-dist/chunks/berry-DKpUyyne.js +1 -0
- package/webview-dist/chunks/bibtex-Ci_nEsc7.js +1 -0
- package/webview-dist/chunks/bicep-CUHmPFLl.js +1 -0
- package/webview-dist/chunks/bird2-CYIUgYqB.js +1 -0
- package/webview-dist/chunks/blade-oPV8S6rR.js +1 -0
- package/webview-dist/chunks/bsl-_VjlB9mv.js +1 -0
- package/webview-dist/chunks/c-DYf-3tob.js +1 -0
- package/webview-dist/chunks/c3-D0apG41V.js +1 -0
- package/webview-dist/chunks/cadence-CQ2zXKGN.js +1 -0
- package/webview-dist/chunks/cairo-DLTphjLi.js +1 -0
- package/webview-dist/chunks/catppuccin-frappe-C9Q5zVZY.js +1 -0
- package/webview-dist/chunks/catppuccin-latte-CtsyrtPq.js +1 -0
- package/webview-dist/chunks/catppuccin-macchiato-R8nAf1vR.js +1 -0
- package/webview-dist/chunks/catppuccin-mocha-DjuaAJKi.js +1 -0
- package/webview-dist/chunks/chapel-DY-WYB1D.js +1 -0
- package/webview-dist/chunks/clarity-CtfBBspg.js +1 -0
- package/webview-dist/chunks/clojure-DqKBuwfJ.js +1 -0
- package/webview-dist/chunks/cmake-Bj61d0ZC.js +1 -0
- package/webview-dist/chunks/cobol-DvdX_6CO.js +1 -0
- package/webview-dist/chunks/codeowners-C8r90Shi.js +1 -0
- package/webview-dist/chunks/codeql-oeQT6MSM.js +1 -0
- package/webview-dist/chunks/coffee-C1kO7jr_.js +1 -0
- package/webview-dist/chunks/common-lisp-Cv5bFMCO.js +1 -0
- package/webview-dist/chunks/coq-DuBRPxYg.js +1 -0
- package/webview-dist/chunks/cpp-CoSarUrI.js +1 -0
- package/webview-dist/chunks/crystal-SLJeZ5EP.js +1 -0
- package/webview-dist/chunks/csharp-oqKa8noW.js +1 -0
- package/webview-dist/chunks/css-cfScyrSP.js +1 -0
- package/webview-dist/chunks/csv-Dx-8-gkx.js +1 -0
- package/webview-dist/chunks/cue-CE9AQfxI.js +1 -0
- package/webview-dist/chunks/cypher-ClKdZ_lG.js +1 -0
- package/webview-dist/chunks/d-qD-0Kul2.js +1 -0
- package/webview-dist/chunks/dark-plus-Cs2F2srj.js +1 -0
- package/webview-dist/chunks/dart-CnvKMtbv.js +1 -0
- package/webview-dist/chunks/dax-BkyTk9wS.js +1 -0
- package/webview-dist/chunks/desktop-Dlh5hvp9.js +1 -0
- package/webview-dist/chunks/diff-woXpYk--.js +1 -0
- package/webview-dist/chunks/docker-IyjqRm3v.js +1 -0
- package/webview-dist/chunks/dotenv-_5a1GRtc.js +1 -0
- package/webview-dist/chunks/dracula-BHWKrbxM.js +1 -0
- package/webview-dist/chunks/dracula-soft-5eyTD99u.js +1 -0
- package/webview-dist/chunks/dream-maker-DW3nJb8Q.js +1 -0
- package/webview-dist/chunks/edge-lvBr3t3s.js +1 -0
- package/webview-dist/chunks/elixir-BLoPe0KG.js +1 -0
- package/webview-dist/chunks/elm-BveBINBn.js +1 -0
- package/webview-dist/chunks/emacs-lisp-c_oH4hRZ.js +1 -0
- package/webview-dist/chunks/erb-Bmf8z4ph.js +1 -0
- package/webview-dist/chunks/erlang-Cphh6RMH.js +1 -0
- package/webview-dist/chunks/everforest-dark-sB-x3p7T.js +1 -0
- package/webview-dist/chunks/everforest-light-Df2xbC6M.js +1 -0
- package/webview-dist/chunks/fennel-DQxkIbk2.js +1 -0
- package/webview-dist/chunks/fish-BJitypiv.js +1 -0
- package/webview-dist/chunks/fluent-C03EYrpw.js +1 -0
- package/webview-dist/chunks/fortran-fixed-form-DEKoE2YW.js +1 -0
- package/webview-dist/chunks/fortran-free-form-CYNrtFtB.js +1 -0
- package/webview-dist/chunks/fsharp-D13ZGOAj.js +1 -0
- package/webview-dist/chunks/gdresource-xMC7jr7x.js +1 -0
- package/webview-dist/chunks/gdscript-DYvRD3hg.js +1 -0
- package/webview-dist/chunks/gdshader-CBce3t8t.js +1 -0
- package/webview-dist/chunks/genie-CV2tkWYe.js +1 -0
- package/webview-dist/chunks/gherkin-DExj1W_8.js +1 -0
- package/webview-dist/chunks/git-commit-BSykSTBG.js +1 -0
- package/webview-dist/chunks/git-rebase-BBii7GBM.js +1 -0
- package/webview-dist/chunks/github-dark-C-LZuMrd.js +1 -0
- package/webview-dist/chunks/github-dark-default-DXG-b-1a.js +1 -0
- package/webview-dist/chunks/github-dark-dimmed-Bx1FflLF.js +1 -0
- package/webview-dist/chunks/github-dark-high-contrast-B_tTalzw.js +1 -0
- package/webview-dist/chunks/github-light-EUqPIrTm.js +1 -0
- package/webview-dist/chunks/github-light-default-BXViO-2h.js +1 -0
- package/webview-dist/chunks/github-light-high-contrast-B68TUdTA.js +1 -0
- package/webview-dist/chunks/gleam-CSRkHgEL.js +1 -0
- package/webview-dist/chunks/glimmer-js-CuCPna-X.js +1 -0
- package/webview-dist/chunks/glimmer-ts-DNAjZNDK.js +1 -0
- package/webview-dist/chunks/glsl-COQvRxdz.js +1 -0
- package/webview-dist/chunks/gn-ilITqXS6.js +1 -0
- package/webview-dist/chunks/gnuplot-7GGW24-e.js +1 -0
- package/webview-dist/chunks/go-rLFTqkRN.js +1 -0
- package/webview-dist/chunks/graphql-1cKCVreZ.js +1 -0
- package/webview-dist/chunks/groovy-CacY0gHj.js +1 -0
- package/webview-dist/chunks/gruvbox-dark-hard-C820rvS2.js +1 -0
- package/webview-dist/chunks/gruvbox-dark-medium-BPjhmG05.js +1 -0
- package/webview-dist/chunks/gruvbox-dark-soft-MrdJrrXF.js +1 -0
- package/webview-dist/chunks/gruvbox-light-hard-BC_s9l72.js +1 -0
- package/webview-dist/chunks/gruvbox-light-medium-BAWPOn9u.js +1 -0
- package/webview-dist/chunks/gruvbox-light-soft-BSMLrYjP.js +1 -0
- package/webview-dist/chunks/hack-7CXYxWGO.js +1 -0
- package/webview-dist/chunks/haml-CeE16iG9.js +1 -0
- package/webview-dist/chunks/handlebars-CLm7Miue.js +1 -0
- package/webview-dist/chunks/haskell-D8IpX4py.js +1 -0
- package/webview-dist/chunks/haxe-C8oCnwJJ.js +1 -0
- package/webview-dist/chunks/hcl-Dh228itO.js +1 -0
- package/webview-dist/chunks/hjson-CxZEssPk.js +1 -0
- package/webview-dist/chunks/hlsl-Cvrh5tZx.js +1 -0
- package/webview-dist/chunks/horizon-CE9ld1lL.js +1 -0
- package/webview-dist/chunks/horizon-bright-DSNQnXHK.js +1 -0
- package/webview-dist/chunks/houston-CsvMBhTu.js +1 -0
- package/webview-dist/chunks/html-EbCri-_j.js +1 -0
- package/webview-dist/chunks/html-derivative-DGcE2kQi.js +1 -0
- package/webview-dist/chunks/http-DpnqgCg2.js +1 -0
- package/webview-dist/chunks/hurl-B6NGpamo.js +1 -0
- package/webview-dist/chunks/hxml-Dcm88srW.js +1 -0
- package/webview-dist/chunks/hy-CZbG8q4J.js +1 -0
- package/webview-dist/chunks/imba-DsUTQ-LC.js +1 -0
- package/webview-dist/chunks/ini-B5eOa1yu.js +1 -0
- package/webview-dist/chunks/java-ZkUoUSJy.js +1 -0
- package/webview-dist/chunks/javascript-DLd0EIw7.js +1 -0
- package/webview-dist/chunks/jinja-WhIqNp3q.js +1 -0
- package/webview-dist/chunks/jison-CIn1t7S8.js +1 -0
- package/webview-dist/chunks/json-AHG_JuKN.js +1 -0
- package/webview-dist/chunks/json5-BR5RXkoi.js +1 -0
- package/webview-dist/chunks/jsonc-CYpm1nAK.js +1 -0
- package/webview-dist/chunks/jsonl-CmCQp5Yx.js +1 -0
- package/webview-dist/chunks/jsonnet-CJTPZ8u_.js +1 -0
- package/webview-dist/chunks/jssm-DXw9l8Rf.js +1 -0
- package/webview-dist/chunks/jsx-BLy2KGb2.js +1 -0
- package/webview-dist/chunks/julia-XrhdGMY0.js +1 -0
- package/webview-dist/chunks/just-CiO9rOBq.js +1 -0
- package/webview-dist/chunks/kanagawa-dragon-Bozd7klA.js +1 -0
- package/webview-dist/chunks/kanagawa-lotus-BmuOG9ZW.js +1 -0
- package/webview-dist/chunks/kanagawa-wave-BML5mF2J.js +1 -0
- package/webview-dist/chunks/kdl-CsD5j6eV.js +1 -0
- package/webview-dist/chunks/kotlin-DhhofPvG.js +1 -0
- package/webview-dist/chunks/kusto-C7mF5XQf.js +1 -0
- package/webview-dist/chunks/laserwave-C_8bwKvT.js +1 -0
- package/webview-dist/chunks/latex-4tqdYtbu.js +1 -0
- package/webview-dist/chunks/lean-CewbzKMR.js +1 -0
- package/webview-dist/chunks/less-DVTAwKKz.js +1 -0
- package/webview-dist/chunks/light-plus-DVQuIRkW.js +1 -0
- package/webview-dist/chunks/liquid-BiiWRCLG.js +1 -0
- package/webview-dist/chunks/llvm-DqHOMS-s.js +1 -0
- package/webview-dist/chunks/log-BNLmms1o.js +1 -0
- package/webview-dist/chunks/logo-Cluzi2Zq.js +1 -0
- package/webview-dist/chunks/lua-BPFdS2Mc.js +1 -0
- package/webview-dist/chunks/luau-C2pBCSgt.js +1 -0
- package/webview-dist/chunks/make-Dixweg8N.js +1 -0
- package/webview-dist/chunks/markdown-BYOwaDjH.js +1 -0
- package/webview-dist/chunks/marko-BLi1Yy1V.js +1 -0
- package/webview-dist/chunks/material-theme-Bm3Qr25_.js +1 -0
- package/webview-dist/chunks/material-theme-darker-2IIEA8gg.js +1 -0
- package/webview-dist/chunks/material-theme-lighter-uhdI0v04.js +1 -0
- package/webview-dist/chunks/material-theme-ocean-CHQ94UKr.js +1 -0
- package/webview-dist/chunks/material-theme-palenight-B5W6OYN7.js +1 -0
- package/webview-dist/chunks/matlab-D7qyCx1q.js +1 -0
- package/webview-dist/chunks/mdc-Dx-qyM1x.js +1 -0
- package/webview-dist/chunks/mdx-DQZ5AkYe.js +1 -0
- package/webview-dist/chunks/mermaid-BxpfTIzt.js +1 -0
- package/webview-dist/chunks/min-dark-BSWPekZh.js +1 -0
- package/webview-dist/chunks/min-light-DDpmG2fV.js +1 -0
- package/webview-dist/chunks/mipsasm-BMqwQI7S.js +1 -0
- package/webview-dist/chunks/mojo-fg40_ntQ.js +1 -0
- package/webview-dist/chunks/monokai-B_bV9PQ9.js +1 -0
- package/webview-dist/chunks/moonbit-DgjMoLWr.js +1 -0
- package/webview-dist/chunks/move-BYnGhZoM.js +1 -0
- package/webview-dist/chunks/narrat-_X_XdTYD.js +1 -0
- package/webview-dist/chunks/nextflow-BJtWHP5T.js +1 -0
- package/webview-dist/chunks/nextflow-groovy-DJMQeKeT.js +1 -0
- package/webview-dist/chunks/nginx-rnpjAIVq.js +1 -0
- package/webview-dist/chunks/night-owl-DhmEMT88.js +1 -0
- package/webview-dist/chunks/night-owl-light-eJ-hLW7d.js +1 -0
- package/webview-dist/chunks/nim-kKTJ2fIs.js +1 -0
- package/webview-dist/chunks/nix-IvuFDN5E.js +1 -0
- package/webview-dist/chunks/nord-Cb4Vim4T.js +1 -0
- package/webview-dist/chunks/nsis-CjNLYCLg.js +1 -0
- package/webview-dist/chunks/nushell-DHYfO66s.js +1 -0
- package/webview-dist/chunks/objective-c-D1A_Heim.js +1 -0
- package/webview-dist/chunks/objective-cpp-BsSzOQcm.js +1 -0
- package/webview-dist/chunks/ocaml-O90oeIOV.js +1 -0
- package/webview-dist/chunks/odin-B1RWQWA5.js +1 -0
- package/webview-dist/chunks/one-dark-pro-CLwyXe_n.js +1 -0
- package/webview-dist/chunks/one-light-D7Lr4KcI.js +1 -0
- package/webview-dist/chunks/openscad-BUDT5pXO.js +1 -0
- package/webview-dist/chunks/org-BGWCjkJX.js +1 -0
- package/webview-dist/chunks/pascal-4ZHwLPI5.js +1 -0
- package/webview-dist/chunks/perl-DEimDILQ.js +1 -0
- package/webview-dist/chunks/php-QnaNN--1.js +1 -0
- package/webview-dist/chunks/pierre-dark-CpLgRqie.js +1 -0
- package/webview-dist/chunks/pierre-dark-protanopia-deuteranopia-B35FxJx-.js +1 -0
- package/webview-dist/chunks/pierre-dark-soft-kZQmAZld.js +1 -0
- package/webview-dist/chunks/pierre-dark-tritanopia-CpjhbsIL.js +1 -0
- package/webview-dist/chunks/pierre-dark-vibrant-CpQYzh95.js +1 -0
- package/webview-dist/chunks/pierre-light-CoaEpmwp.js +1 -0
- package/webview-dist/chunks/pierre-light-protanopia-deuteranopia-0fSaH845.js +1 -0
- package/webview-dist/chunks/pierre-light-soft-lWLdNTOI.js +1 -0
- package/webview-dist/chunks/pierre-light-tritanopia-CEbqgOJL.js +1 -0
- package/webview-dist/chunks/pierre-light-vibrant-D80Fkn33.js +1 -0
- package/webview-dist/chunks/pkl-ot-7Btpt.js +1 -0
- package/webview-dist/chunks/plastic-DQwYfKfQ.js +1 -0
- package/webview-dist/chunks/plsql-DGHpHOYJ.js +1 -0
- package/webview-dist/chunks/po-BiJDBrnU.js +1 -0
- package/webview-dist/chunks/poimandres-DRFjx7u4.js +1 -0
- package/webview-dist/chunks/polar-C7UOKdEL.js +1 -0
- package/webview-dist/chunks/postcss-BXeXVLqQ.js +1 -0
- package/webview-dist/chunks/powerquery-DNMTfnFr.js +1 -0
- package/webview-dist/chunks/powershell-CCSX75yM.js +1 -0
- package/webview-dist/chunks/prisma-DMgNhnYS.js +1 -0
- package/webview-dist/chunks/prolog-iXnhIJG7.js +1 -0
- package/webview-dist/chunks/proto-DB4EqR-F.js +1 -0
- package/webview-dist/chunks/pug-D5rsKmMX.js +1 -0
- package/webview-dist/chunks/puppet-CDv2pdJW.js +1 -0
- package/webview-dist/chunks/purescript-9MfHhQsQ.js +1 -0
- package/webview-dist/chunks/python-gzcpVVnB.js +1 -0
- package/webview-dist/chunks/qml-wvbZb8UE.js +1 -0
- package/webview-dist/chunks/qmldir-DCQb3MpD.js +1 -0
- package/webview-dist/chunks/qss-Fe1Jh2GI.js +1 -0
- package/webview-dist/chunks/r-BNyBFaU1.js +1 -0
- package/webview-dist/chunks/racket-DcIDlBhZ.js +1 -0
- package/webview-dist/chunks/raku-B3gFvitq.js +1 -0
- package/webview-dist/chunks/razor-bKaHS6Kz.js +1 -0
- package/webview-dist/chunks/rbs-C0MpOrj4.js +1 -0
- package/webview-dist/chunks/red-DX1BQOs8.js +1 -0
- package/webview-dist/chunks/reg-CRGYupPL.js +1 -0
- package/webview-dist/chunks/regexp-B-mZuWUh.js +1 -0
- package/webview-dist/chunks/rel-BtDbiS_P.js +1 -0
- package/webview-dist/chunks/riscv-Ckw8ddFX.js +1 -0
- package/webview-dist/chunks/ron-VUp2lXgN.js +1 -0
- package/webview-dist/chunks/rose-pine-aMsaMD1D.js +1 -0
- package/webview-dist/chunks/rose-pine-dawn-8PwUyIlP.js +1 -0
- package/webview-dist/chunks/rose-pine-moon-DO-qXg-I.js +1 -0
- package/webview-dist/chunks/rosmsg-CAekHB0j.js +1 -0
- package/webview-dist/chunks/rst-Qq8ybXHe.js +1 -0
- package/webview-dist/chunks/ruby-U659LEwU.js +1 -0
- package/webview-dist/chunks/rust-Cfkwpbl8.js +1 -0
- package/webview-dist/chunks/sas-C4NtLApe.js +1 -0
- package/webview-dist/chunks/sass-DXrisJhu.js +1 -0
- package/webview-dist/chunks/scala-BiFt_7mu.js +1 -0
- package/webview-dist/chunks/scheme-DQCgrYNe.js +1 -0
- package/webview-dist/chunks/scss-KAaA4sHk.js +1 -0
- package/webview-dist/chunks/sdbl-bTVj8UrX.js +1 -0
- package/webview-dist/chunks/shaderlab-TOUzSsQk.js +1 -0
- package/webview-dist/chunks/shellscript-_7bOVsRC.js +1 -0
- package/webview-dist/chunks/shellsession-Bs_5k0qk.js +1 -0
- package/webview-dist/chunks/slack-dark-DnToyrRv.js +1 -0
- package/webview-dist/chunks/slack-ochin-B2OO5cIa.js +1 -0
- package/webview-dist/chunks/smalltalk-BHpQb5nM.js +1 -0
- package/webview-dist/chunks/smithy-Dgaz7aur.js +1 -0
- package/webview-dist/chunks/snazzy-light-4G7pJPwS.js +1 -0
- package/webview-dist/chunks/solarized-dark-UdsmMDCL.js +1 -0
- package/webview-dist/chunks/solarized-light-C1IUL_tW.js +1 -0
- package/webview-dist/chunks/solidity-Bj7E1mRn.js +1 -0
- package/webview-dist/chunks/soy-C5iuoFCh.js +1 -0
- package/webview-dist/chunks/sparql-D_iOobhT.js +1 -0
- package/webview-dist/chunks/splunk-BC2Px7Mm.js +1 -0
- package/webview-dist/chunks/sql-JX305rZh.js +1 -0
- package/webview-dist/chunks/ssh-config-BgfXC-Er.js +1 -0
- package/webview-dist/chunks/stata-2Rtik3TV.js +1 -0
- package/webview-dist/chunks/stylus-B6D30XZt.js +1 -0
- package/webview-dist/chunks/surrealql-IvpiLAIH.js +1 -0
- package/webview-dist/chunks/svelte-DeZcV_1u.js +1 -0
- package/webview-dist/chunks/swift-CyEgAFGc.js +1 -0
- package/webview-dist/chunks/synthwave-84-nFMaYfgc.js +1 -0
- package/webview-dist/chunks/system-verilog-Dh5ihWbf.js +1 -0
- package/webview-dist/chunks/systemd-BxMlprV5.js +1 -0
- package/webview-dist/chunks/talonscript-CohzipZa.js +1 -0
- package/webview-dist/chunks/tasl-DMoTqEGO.js +1 -0
- package/webview-dist/chunks/tcl-CZd0xW_V.js +1 -0
- package/webview-dist/chunks/templ-CcwjPXJr.js +1 -0
- package/webview-dist/chunks/terraform-DswuEJGm.js +1 -0
- package/webview-dist/chunks/tex-CZYHgHl8.js +1 -0
- package/webview-dist/chunks/tokyo-night-oM2G3aXe.js +1 -0
- package/webview-dist/chunks/toml-CcmNWLt0.js +1 -0
- package/webview-dist/chunks/ts-tags-DPINfEO_.js +1 -0
- package/webview-dist/chunks/tsv-sltzmVWM.js +1 -0
- package/webview-dist/chunks/tsx-Dq7pSIyE.js +1 -0
- package/webview-dist/chunks/turtle-ByJddavk.js +1 -0
- package/webview-dist/chunks/twig-DFpC8yDL.js +1 -0
- package/webview-dist/chunks/typescript-CN-guYmK.js +1 -0
- package/webview-dist/chunks/typespec-BRdr0IET.js +1 -0
- package/webview-dist/chunks/typst-hgW4q6mN.js +1 -0
- package/webview-dist/chunks/v-DBj4K9d_.js +1 -0
- package/webview-dist/chunks/vala-zf12oZj6.js +1 -0
- package/webview-dist/chunks/vb-DOwzaHZ0.js +1 -0
- package/webview-dist/chunks/verilog-B2gwz-sJ.js +1 -0
- package/webview-dist/chunks/vesper-DdrHHSXu.js +1 -0
- package/webview-dist/chunks/vhdl-BroJfC0k.js +1 -0
- package/webview-dist/chunks/viml-DvXPmvsu.js +1 -0
- package/webview-dist/chunks/vitesse-black-fwtXNY1n.js +1 -0
- package/webview-dist/chunks/vitesse-dark-BZCL-v6S.js +1 -0
- package/webview-dist/chunks/vitesse-light-VbXTXTou.js +1 -0
- package/webview-dist/chunks/vue-Dkz9ntel.js +1 -0
- package/webview-dist/chunks/vue-html-Dg8LQ5v3.js +1 -0
- package/webview-dist/chunks/vue-vine-CIEXICEN.js +1 -0
- package/webview-dist/chunks/vyper-CgoNMtux.js +1 -0
- package/webview-dist/chunks/wasm-BnjxR4X6.js +1 -0
- package/webview-dist/chunks/wasm-ByWQv1Qj.js +1 -0
- package/webview-dist/chunks/wenyan-C8pVoKbM.js +1 -0
- package/webview-dist/chunks/wgsl-BsKzXJz4.js +1 -0
- package/webview-dist/chunks/wikitext-ClFFjSW2.js +1 -0
- package/webview-dist/chunks/wit-DdvCle-K.js +1 -0
- package/webview-dist/chunks/wolfram-DLL8P-h_.js +1 -0
- package/webview-dist/chunks/worker-D52zl71O.js +1 -0
- package/webview-dist/chunks/worker-pool-lOjh8Q2A.js +1543 -0
- package/webview-dist/chunks/xml-X7YOen_c.js +1 -0
- package/webview-dist/chunks/xsl-h4d_Px-X.js +1 -0
- package/webview-dist/chunks/yaml-BRDpsfaj.js +1 -0
- package/webview-dist/chunks/zenscript-BnlCZFoB.js +1 -0
- package/webview-dist/chunks/zig-CMLA9XwU.js +1 -0
- package/webview-dist/index.html +18 -0
- package/webview-dist/pierre-worker-portable.js +13633 -0
- package/webview-dist/repo-timeline-app.js +7 -0
- package/webview-dist/repo-timeline.html +14 -0
- package/webview-dist/timeline-app.css +2 -0
- package/webview-dist/timeline-app.js +85 -0
|
@@ -0,0 +1,1154 @@
|
|
|
1
|
+
const require_runtime = require("../_virtual/_rolldown/runtime.cjs");
|
|
2
|
+
require("./constants.cjs");
|
|
3
|
+
const require_blame = require("../shared/blame.cjs");
|
|
4
|
+
const require_history_adapters = require("./history-adapters.cjs");
|
|
5
|
+
const require_line_history = require("../shared/line-history.cjs");
|
|
6
|
+
const require_non_textual_details = require("../shared/non-textual-details.cjs");
|
|
7
|
+
const require_diff_stats = require("../shared/diff-stats.cjs");
|
|
8
|
+
const require_history_search = require("../shared/history-search.cjs");
|
|
9
|
+
const require_file_oplog = require("../shared/file-oplog.cjs");
|
|
10
|
+
let node_path = require("node:path");
|
|
11
|
+
node_path = require_runtime.__toESM(node_path, 1);
|
|
12
|
+
let node_fs_promises = require("node:fs/promises");
|
|
13
|
+
//#region src/extension/timeline-service.ts
|
|
14
|
+
const DEFAULT_TIMELINE_PREFERENCES = {
|
|
15
|
+
sidebarWidth: 276,
|
|
16
|
+
sidebarCollapsed: false,
|
|
17
|
+
timelinePaneHeight: 220,
|
|
18
|
+
timelinePaneCollapsed: false,
|
|
19
|
+
layoutMode: "split",
|
|
20
|
+
contentMode: "diffs",
|
|
21
|
+
comparisonMode: "range",
|
|
22
|
+
comparisonSource: "revision",
|
|
23
|
+
showIntermediateRevisions: false,
|
|
24
|
+
preset: "year",
|
|
25
|
+
customRevset: "",
|
|
26
|
+
themePreference: "auto",
|
|
27
|
+
heatmapOpen: false,
|
|
28
|
+
lineDiffType: "word-alt"
|
|
29
|
+
};
|
|
30
|
+
const IGNORED_WORKSPACE_DIRECTORIES = /* @__PURE__ */ new Set([
|
|
31
|
+
".git",
|
|
32
|
+
".jj",
|
|
33
|
+
"node_modules",
|
|
34
|
+
"dist",
|
|
35
|
+
"build",
|
|
36
|
+
"out",
|
|
37
|
+
"coverage"
|
|
38
|
+
]);
|
|
39
|
+
function getComparisonEntries(request) {
|
|
40
|
+
const normalizedFromIndex = Math.max(0, Math.min(request.fromIndex, request.toIndex));
|
|
41
|
+
const normalizedToIndex = Math.max(normalizedFromIndex, Math.max(request.fromIndex, request.toIndex));
|
|
42
|
+
const fromEntry = request.entries[normalizedFromIndex];
|
|
43
|
+
const toEntry = request.entries[normalizedToIndex];
|
|
44
|
+
if (!fromEntry || !toEntry) return;
|
|
45
|
+
return {
|
|
46
|
+
fromEntry,
|
|
47
|
+
toEntry
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
function syncSession(request) {
|
|
51
|
+
request.target.adapter = request.source.adapter;
|
|
52
|
+
request.target.backend = request.source.backend;
|
|
53
|
+
request.target.workspacePath = request.source.workspacePath;
|
|
54
|
+
request.target.relativePath = request.source.relativePath;
|
|
55
|
+
request.target.absolutePath = request.source.absolutePath;
|
|
56
|
+
request.target.fileName = request.source.fileName;
|
|
57
|
+
request.target.entries = request.source.entries;
|
|
58
|
+
request.target.snapshotEntries = request.source.snapshotEntries;
|
|
59
|
+
request.target.snapshotLoadedChangeIds = request.source.snapshotLoadedChangeIds;
|
|
60
|
+
request.target.snapshotPendingChangeIds = request.source.snapshotPendingChangeIds;
|
|
61
|
+
request.target.workspaceFiles = request.source.workspaceFiles;
|
|
62
|
+
request.target.workspaceFilesLoaded = request.source.workspaceFilesLoaded;
|
|
63
|
+
request.target.historyLimit = request.source.historyLimit;
|
|
64
|
+
request.target.intermediateRevisionsLoaded = request.source.intermediateRevisionsLoaded;
|
|
65
|
+
request.target.contentCache = request.source.contentCache;
|
|
66
|
+
request.target.previewCache = request.source.previewCache;
|
|
67
|
+
request.target.rangeOverviewCache = request.source.rangeOverviewCache;
|
|
68
|
+
request.target.entryDiffCountCache = request.source.entryDiffCountCache;
|
|
69
|
+
request.target.pathCache = request.source.pathCache;
|
|
70
|
+
request.target.activeActionAbortController = request.source.activeActionAbortController;
|
|
71
|
+
request.target.lineHistory = request.source.lineHistory;
|
|
72
|
+
request.target.customRevset = request.source.customRevset;
|
|
73
|
+
}
|
|
74
|
+
async function getContentForRevset(request) {
|
|
75
|
+
const cacheKey = `revset:${request.revset}:${request.filePath}`;
|
|
76
|
+
const cached = request.session.contentCache.get(cacheKey);
|
|
77
|
+
if (cached !== void 0) return cached;
|
|
78
|
+
const content = await request.session.adapter.showFileAtRevision({
|
|
79
|
+
workspacePath: request.session.workspacePath,
|
|
80
|
+
revset: request.revset,
|
|
81
|
+
filePath: request.filePath
|
|
82
|
+
});
|
|
83
|
+
request.session.contentCache.set(cacheKey, content);
|
|
84
|
+
return content;
|
|
85
|
+
}
|
|
86
|
+
async function filterSessionEntriesForLineHistory(args) {
|
|
87
|
+
if (args.adapter.backend === "git") try {
|
|
88
|
+
const { stdout } = await args.runner.runGit({
|
|
89
|
+
workspacePath: args.workspacePath,
|
|
90
|
+
args: require_line_history.buildGitLineHistoryArgs({
|
|
91
|
+
relativePath: args.relativePath,
|
|
92
|
+
range: args.range,
|
|
93
|
+
limit: args.limit
|
|
94
|
+
})
|
|
95
|
+
});
|
|
96
|
+
const revisions = new Set(require_line_history.parseGitLineHistoryRevisions(stdout));
|
|
97
|
+
const filtered = args.entries.filter((entry) => entry.isWorkingTree || revisions.has(entry.revision));
|
|
98
|
+
if (filtered.length) return filtered;
|
|
99
|
+
} catch {}
|
|
100
|
+
const contentByRevision = /* @__PURE__ */ new Map();
|
|
101
|
+
for (const entry of args.entries) {
|
|
102
|
+
const filePath = entry.filePath || args.relativePath;
|
|
103
|
+
const revset = entry.isWorkingTree ? "@" : entry.revision;
|
|
104
|
+
try {
|
|
105
|
+
const content = await args.adapter.showFileAtRevision({
|
|
106
|
+
workspacePath: args.workspacePath,
|
|
107
|
+
revset: entry.isWorkingTree && args.adapter.backend === "git" ? "HEAD" : revset,
|
|
108
|
+
filePath
|
|
109
|
+
});
|
|
110
|
+
contentByRevision.set(entry.revision, content);
|
|
111
|
+
} catch {
|
|
112
|
+
contentByRevision.set(entry.revision, "");
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
if (args.adapter.backend === "git") {
|
|
116
|
+
const tip = args.entries.find((entry) => entry.isWorkingTree);
|
|
117
|
+
if (tip) try {
|
|
118
|
+
const diskPath = node_path.default.join(args.workspacePath, tip.filePath || args.relativePath);
|
|
119
|
+
contentByRevision.set(tip.revision, await (0, node_fs_promises.readFile)(diskPath, "utf8"));
|
|
120
|
+
} catch {}
|
|
121
|
+
}
|
|
122
|
+
return require_line_history.filterEntriesTouchingLineRange({
|
|
123
|
+
entries: args.entries,
|
|
124
|
+
range: args.range,
|
|
125
|
+
getContent: (entry) => contentByRevision.get(entry.revision) || ""
|
|
126
|
+
});
|
|
127
|
+
}
|
|
128
|
+
async function hydrateEntryFilePaths(args) {
|
|
129
|
+
if (!args.entries.length) return;
|
|
130
|
+
args.entries[args.entries.length - 1].filePath = args.relativePath;
|
|
131
|
+
let currentPath = args.relativePath;
|
|
132
|
+
for (let index = args.entries.length - 1; index > 0; index -= 1) {
|
|
133
|
+
const currentEntry = args.entries[index];
|
|
134
|
+
const previousEntry = args.entries[index - 1];
|
|
135
|
+
if (previousEntry.filePath) {
|
|
136
|
+
currentPath = previousEntry.filePath;
|
|
137
|
+
continue;
|
|
138
|
+
}
|
|
139
|
+
let previousPath = currentPath;
|
|
140
|
+
if (currentEntry.touchesFile && !currentEntry.isWorkingTree) previousPath = await args.adapter.resolvePreviousPath({
|
|
141
|
+
workspacePath: args.workspacePath,
|
|
142
|
+
revision: currentEntry.revision,
|
|
143
|
+
currentPath
|
|
144
|
+
});
|
|
145
|
+
previousEntry.filePath = previousPath;
|
|
146
|
+
currentPath = previousPath;
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
async function resolvePreviousPathAcrossRevision(request) {
|
|
150
|
+
const cacheKey = `${request.entry.revision}:${request.currentPath}`;
|
|
151
|
+
const cached = request.session.pathCache.get(cacheKey);
|
|
152
|
+
if (cached) return cached;
|
|
153
|
+
const previousPath = await request.session.adapter.resolvePreviousPath({
|
|
154
|
+
workspacePath: request.session.workspacePath,
|
|
155
|
+
revision: request.entry.revision,
|
|
156
|
+
currentPath: request.currentPath
|
|
157
|
+
});
|
|
158
|
+
request.session.pathCache.set(cacheKey, previousPath);
|
|
159
|
+
return previousPath;
|
|
160
|
+
}
|
|
161
|
+
async function loadRevisionTreeFiles(request) {
|
|
162
|
+
const revision = request.revision.trim() || "@";
|
|
163
|
+
try {
|
|
164
|
+
return await request.session.adapter.listRevisionTreeFiles({
|
|
165
|
+
workspacePath: request.session.workspacePath,
|
|
166
|
+
revision
|
|
167
|
+
});
|
|
168
|
+
} catch {
|
|
169
|
+
return [];
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
function createTimelineService(args) {
|
|
173
|
+
const { runner } = args;
|
|
174
|
+
return {
|
|
175
|
+
buildSession,
|
|
176
|
+
buildPayload,
|
|
177
|
+
mapEntriesForPayload,
|
|
178
|
+
ensureWorkspaceFiles,
|
|
179
|
+
loadRevisionTreeFiles,
|
|
180
|
+
ensureIntermediateRevisions,
|
|
181
|
+
expandFileHistory,
|
|
182
|
+
findNearestNonEmptyVisibleRange,
|
|
183
|
+
getComparisonEntries,
|
|
184
|
+
getDiffPreview,
|
|
185
|
+
getDiffBlame,
|
|
186
|
+
getFileOpLog,
|
|
187
|
+
searchFileHistory,
|
|
188
|
+
getEntryDiffCounts,
|
|
189
|
+
getEntriesForSource,
|
|
190
|
+
getRangeOverview,
|
|
191
|
+
getRangeStackPreviews,
|
|
192
|
+
hydrateSnapshotEntries,
|
|
193
|
+
resolveBlameRevisionForLine,
|
|
194
|
+
runSessionAction,
|
|
195
|
+
resolveEntryFilePath,
|
|
196
|
+
syncSession
|
|
197
|
+
};
|
|
198
|
+
async function buildSession(request) {
|
|
199
|
+
const historyWorkspacePath = await require_history_adapters.resolveHistoryWorkspacePath({
|
|
200
|
+
workspacePath: request.workspacePath,
|
|
201
|
+
runner
|
|
202
|
+
});
|
|
203
|
+
const relativePath = node_path.default.relative(historyWorkspacePath, request.absolutePath).replace(/\\/g, "/");
|
|
204
|
+
const adapter = await require_history_adapters.resolveHistoryAdapter({
|
|
205
|
+
workspacePath: historyWorkspacePath,
|
|
206
|
+
runner
|
|
207
|
+
});
|
|
208
|
+
const entryLimit = request.options?.entryLimit ?? 50;
|
|
209
|
+
const includeWorkspaceFiles = request.options?.includeWorkspaceFiles === true;
|
|
210
|
+
const includeIntermediateRevisions = request.options?.includeIntermediateRevisions === true;
|
|
211
|
+
const lineHistory = request.options?.lineHistory ? require_line_history.normalizeLineHistoryRange(request.options.lineHistory.startLine, request.options.lineHistory.endLine) : null;
|
|
212
|
+
const customRevset = (request.options?.customRevset || "").trim() || void 0;
|
|
213
|
+
const workspaceFiles = includeWorkspaceFiles ? await listWorkspaceFiles({
|
|
214
|
+
adapter,
|
|
215
|
+
workspacePath: historyWorkspacePath
|
|
216
|
+
}) : [relativePath];
|
|
217
|
+
const fileEntries = await adapter.getFileRevisionHistory({
|
|
218
|
+
workspacePath: historyWorkspacePath,
|
|
219
|
+
relativePath,
|
|
220
|
+
limit: entryLimit,
|
|
221
|
+
customRevset
|
|
222
|
+
});
|
|
223
|
+
let entries = await buildTimelineEntries({
|
|
224
|
+
adapter,
|
|
225
|
+
workspacePath: historyWorkspacePath,
|
|
226
|
+
absolutePath: request.absolutePath,
|
|
227
|
+
relativePath,
|
|
228
|
+
fileEntries,
|
|
229
|
+
includeIntermediateRevisions,
|
|
230
|
+
entryLimit
|
|
231
|
+
});
|
|
232
|
+
if (lineHistory) entries = await filterSessionEntriesForLineHistory({
|
|
233
|
+
adapter,
|
|
234
|
+
runner,
|
|
235
|
+
workspacePath: historyWorkspacePath,
|
|
236
|
+
relativePath,
|
|
237
|
+
entries,
|
|
238
|
+
range: lineHistory,
|
|
239
|
+
limit: entryLimit
|
|
240
|
+
});
|
|
241
|
+
const snapshotEntries = adapter.backend === "jj" ? [] : [...entries];
|
|
242
|
+
const remoteBaseUrl = await adapter.getRemoteBaseUrl({ workspacePath: historyWorkspacePath });
|
|
243
|
+
if (remoteBaseUrl) {
|
|
244
|
+
for (const entry of entries) if (!entry.isWorkingTree) entry.remoteUrl = `${remoteBaseUrl}/commit/${entry.revision}`;
|
|
245
|
+
}
|
|
246
|
+
await hydrateEntryFilePaths({
|
|
247
|
+
adapter,
|
|
248
|
+
workspacePath: historyWorkspacePath,
|
|
249
|
+
relativePath,
|
|
250
|
+
entries
|
|
251
|
+
});
|
|
252
|
+
return {
|
|
253
|
+
adapter,
|
|
254
|
+
backend: adapter.backend,
|
|
255
|
+
workspacePath: historyWorkspacePath,
|
|
256
|
+
absolutePath: request.absolutePath,
|
|
257
|
+
relativePath,
|
|
258
|
+
fileName: node_path.default.basename(request.absolutePath),
|
|
259
|
+
entries,
|
|
260
|
+
snapshotEntries,
|
|
261
|
+
snapshotLoadedChangeIds: /* @__PURE__ */ new Set(),
|
|
262
|
+
snapshotPendingChangeIds: /* @__PURE__ */ new Set(),
|
|
263
|
+
workspaceFiles,
|
|
264
|
+
workspaceFilesLoaded: includeWorkspaceFiles,
|
|
265
|
+
historyLimit: entryLimit,
|
|
266
|
+
intermediateRevisionsLoaded: includeIntermediateRevisions,
|
|
267
|
+
lineHistory: lineHistory ?? void 0,
|
|
268
|
+
customRevset,
|
|
269
|
+
contentCache: /* @__PURE__ */ new Map(),
|
|
270
|
+
previewCache: /* @__PURE__ */ new Map(),
|
|
271
|
+
rangeOverviewCache: /* @__PURE__ */ new Map(),
|
|
272
|
+
entryDiffCountCache: /* @__PURE__ */ new Map(),
|
|
273
|
+
pathCache: /* @__PURE__ */ new Map(),
|
|
274
|
+
activeActionAbortController: void 0
|
|
275
|
+
};
|
|
276
|
+
}
|
|
277
|
+
function mapEntriesForPayload(entries) {
|
|
278
|
+
return entries.map((entry, index) => ({
|
|
279
|
+
...entry,
|
|
280
|
+
index,
|
|
281
|
+
hasPreviousEntry: index > 0,
|
|
282
|
+
monthLabel: formatEntryMonthLabel(entry.authorDate),
|
|
283
|
+
shortDate: formatEntryShortDate(entry.authorDate),
|
|
284
|
+
relativeDate: formatRelativeTime(entry.timestamp)
|
|
285
|
+
}));
|
|
286
|
+
}
|
|
287
|
+
function buildPayload(request) {
|
|
288
|
+
return {
|
|
289
|
+
backend: request.session.backend,
|
|
290
|
+
workspacePath: request.session.workspacePath,
|
|
291
|
+
relativePath: request.session.relativePath,
|
|
292
|
+
fileName: request.session.fileName,
|
|
293
|
+
version: request.version,
|
|
294
|
+
presets: request.presets,
|
|
295
|
+
defaultIndex: Math.max(0, request.session.entries.length - 1),
|
|
296
|
+
latestIndex: Math.max(0, request.session.entries.length - 1),
|
|
297
|
+
preferences: normalizeTimelinePreferences(request.preferences),
|
|
298
|
+
workspaceFiles: request.session.workspaceFiles,
|
|
299
|
+
workspaceFilesLoaded: request.session.workspaceFilesLoaded,
|
|
300
|
+
hasIntermediateRevisions: request.session.entries.some((entry) => !entry.touchesFile),
|
|
301
|
+
entries: mapEntriesForPayload(request.session.entries),
|
|
302
|
+
snapshotEntries: mapEntriesForPayload(request.session.snapshotEntries),
|
|
303
|
+
snapshotState: { loadedChangeIds: [...request.session.snapshotLoadedChangeIds] },
|
|
304
|
+
...request.session.lineHistory ? { lineHistory: request.session.lineHistory } : {},
|
|
305
|
+
...request.session.customRevset ? { customRevset: request.session.customRevset } : {}
|
|
306
|
+
};
|
|
307
|
+
}
|
|
308
|
+
async function ensureWorkspaceFiles(request) {
|
|
309
|
+
if (request.session.workspaceFilesLoaded) return false;
|
|
310
|
+
const workspaceFiles = await listWorkspaceFiles({
|
|
311
|
+
adapter: request.session.adapter,
|
|
312
|
+
workspacePath: request.session.workspacePath
|
|
313
|
+
});
|
|
314
|
+
request.session.workspaceFiles = workspaceFiles;
|
|
315
|
+
request.session.workspaceFilesLoaded = true;
|
|
316
|
+
request.session.rangeOverviewCache.clear();
|
|
317
|
+
return true;
|
|
318
|
+
}
|
|
319
|
+
async function ensureIntermediateRevisions(request) {
|
|
320
|
+
if (request.session.intermediateRevisionsLoaded) return false;
|
|
321
|
+
const touchingEntries = request.session.entries.filter((entry) => entry.touchesFile && !entry.isWorkingTree);
|
|
322
|
+
if (touchingEntries.length < 2) {
|
|
323
|
+
request.session.intermediateRevisionsLoaded = true;
|
|
324
|
+
return false;
|
|
325
|
+
}
|
|
326
|
+
try {
|
|
327
|
+
const repositoryEntries = await request.session.adapter.getRepositoryRevisionHistory({
|
|
328
|
+
workspacePath: request.session.workspacePath,
|
|
329
|
+
limit: 200
|
|
330
|
+
});
|
|
331
|
+
const workingTreeEntry = request.session.entries.find((entry) => entry.isWorkingTree);
|
|
332
|
+
let entries = mergeTimelineEntries({
|
|
333
|
+
repositoryEntries,
|
|
334
|
+
fileEntries: touchingEntries
|
|
335
|
+
});
|
|
336
|
+
if (request.session.backend === "jj") entries = dedupeAdjacentTimelineEntries(entries);
|
|
337
|
+
if (workingTreeEntry) entries = [...entries, workingTreeEntry];
|
|
338
|
+
request.session.entries = entries;
|
|
339
|
+
request.session.intermediateRevisionsLoaded = true;
|
|
340
|
+
request.session.previewCache.clear();
|
|
341
|
+
request.session.rangeOverviewCache.clear();
|
|
342
|
+
request.session.entryDiffCountCache.clear();
|
|
343
|
+
return true;
|
|
344
|
+
} catch {
|
|
345
|
+
request.session.intermediateRevisionsLoaded = true;
|
|
346
|
+
return false;
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
async function expandFileHistory(request) {
|
|
350
|
+
if (request.session.historyLimit >= 200) return false;
|
|
351
|
+
const previousTouchingCount = request.session.entries.filter((entry) => entry.touchesFile && !entry.isWorkingTree).length;
|
|
352
|
+
if (previousTouchingCount < request.session.historyLimit) {
|
|
353
|
+
request.session.historyLimit = 200;
|
|
354
|
+
return false;
|
|
355
|
+
}
|
|
356
|
+
const fileEntries = await request.session.adapter.getFileRevisionHistory({
|
|
357
|
+
workspacePath: request.session.workspacePath,
|
|
358
|
+
relativePath: request.session.relativePath,
|
|
359
|
+
limit: 200
|
|
360
|
+
});
|
|
361
|
+
const entries = await buildTimelineEntries({
|
|
362
|
+
adapter: request.session.adapter,
|
|
363
|
+
workspacePath: request.session.workspacePath,
|
|
364
|
+
absolutePath: request.session.absolutePath,
|
|
365
|
+
relativePath: request.session.relativePath,
|
|
366
|
+
fileEntries,
|
|
367
|
+
includeIntermediateRevisions: request.session.intermediateRevisionsLoaded,
|
|
368
|
+
entryLimit: 200
|
|
369
|
+
});
|
|
370
|
+
const nextTouchingCount = entries.filter((entry) => entry.touchesFile && !entry.isWorkingTree).length;
|
|
371
|
+
request.session.entries = entries;
|
|
372
|
+
request.session.historyLimit = 200;
|
|
373
|
+
request.session.previewCache.clear();
|
|
374
|
+
request.session.rangeOverviewCache.clear();
|
|
375
|
+
request.session.entryDiffCountCache.clear();
|
|
376
|
+
return nextTouchingCount > previousTouchingCount;
|
|
377
|
+
}
|
|
378
|
+
function getEntriesForSource(request) {
|
|
379
|
+
if (request.session.backend !== "jj" || request.comparisonSource !== "snapshot") return request.session.entries;
|
|
380
|
+
return composeSnapshotEntries({
|
|
381
|
+
revisionEntries: request.session.entries,
|
|
382
|
+
snapshotEntries: request.session.snapshotEntries,
|
|
383
|
+
loadedChangeIds: request.session.snapshotLoadedChangeIds
|
|
384
|
+
});
|
|
385
|
+
}
|
|
386
|
+
async function getDiffPreview(request) {
|
|
387
|
+
const comparisonSource = request.comparisonSource || "revision";
|
|
388
|
+
const sourceEntries = getEntriesForSource({
|
|
389
|
+
session: request.session,
|
|
390
|
+
comparisonSource
|
|
391
|
+
});
|
|
392
|
+
const normalizedFromIndex = Math.max(0, Math.min(request.fromIndex, request.toIndex));
|
|
393
|
+
const normalizedToIndex = Math.max(normalizedFromIndex, Math.max(request.fromIndex, request.toIndex));
|
|
394
|
+
const cacheKey = `${comparisonSource}:${normalizedFromIndex}:${normalizedToIndex}`;
|
|
395
|
+
const cached = request.session.previewCache.get(cacheKey);
|
|
396
|
+
if (cached) return cached;
|
|
397
|
+
if (request.session.backend === "jj" && comparisonSource === "snapshot") {
|
|
398
|
+
const preview = await getJjSnapshotPreview({
|
|
399
|
+
session: request.session,
|
|
400
|
+
fromIndex: normalizedFromIndex,
|
|
401
|
+
toIndex: normalizedToIndex
|
|
402
|
+
});
|
|
403
|
+
request.session.previewCache.set(cacheKey, preview);
|
|
404
|
+
return preview;
|
|
405
|
+
}
|
|
406
|
+
const comparison = getComparisonEntries({
|
|
407
|
+
entries: sourceEntries,
|
|
408
|
+
fromIndex: normalizedFromIndex,
|
|
409
|
+
toIndex: normalizedToIndex
|
|
410
|
+
});
|
|
411
|
+
if (!comparison) return createEmptyPreview({
|
|
412
|
+
index: normalizedToIndex,
|
|
413
|
+
fromIndex: normalizedFromIndex,
|
|
414
|
+
toIndex: normalizedToIndex,
|
|
415
|
+
comparisonSource,
|
|
416
|
+
title: "No revision selected"
|
|
417
|
+
});
|
|
418
|
+
const beforePath = comparison.fromEntry ? await resolveEntryFilePath({
|
|
419
|
+
session: request.session,
|
|
420
|
+
entry: comparison.fromEntry,
|
|
421
|
+
entryIndex: normalizedFromIndex
|
|
422
|
+
}) : "";
|
|
423
|
+
const afterPath = await resolveEntryFilePath({
|
|
424
|
+
session: request.session,
|
|
425
|
+
entry: comparison.toEntry,
|
|
426
|
+
entryIndex: normalizedToIndex
|
|
427
|
+
});
|
|
428
|
+
const beforeText = comparison.fromEntry ? await getRevisionContent({
|
|
429
|
+
session: request.session,
|
|
430
|
+
entry: comparison.fromEntry,
|
|
431
|
+
entryIndex: normalizedFromIndex
|
|
432
|
+
}) : "";
|
|
433
|
+
const afterText = await getRevisionContent({
|
|
434
|
+
session: request.session,
|
|
435
|
+
entry: comparison.toEntry,
|
|
436
|
+
entryIndex: normalizedToIndex
|
|
437
|
+
});
|
|
438
|
+
const preview = buildDiffPreview({
|
|
439
|
+
index: normalizedToIndex,
|
|
440
|
+
previousEntry: comparison.fromEntry,
|
|
441
|
+
currentEntry: comparison.toEntry,
|
|
442
|
+
beforeText,
|
|
443
|
+
afterText,
|
|
444
|
+
fromIndex: normalizedFromIndex,
|
|
445
|
+
toIndex: normalizedToIndex,
|
|
446
|
+
beforePath,
|
|
447
|
+
afterPath,
|
|
448
|
+
comparisonSource
|
|
449
|
+
});
|
|
450
|
+
request.session.previewCache.set(cacheKey, preview);
|
|
451
|
+
return preview;
|
|
452
|
+
}
|
|
453
|
+
async function hydrateSnapshotEntries(request) {
|
|
454
|
+
if (request.session.backend !== "jj") return false;
|
|
455
|
+
const uniqueTargets = request.revisionIndexes.map((index) => request.session.entries[index]).filter((entry) => Boolean(entry && !entry.isWorkingTree && entry.touchesFile && entry.changeId && !request.session.snapshotLoadedChangeIds.has(entry.changeId) && !request.session.snapshotPendingChangeIds.has(entry.changeId))).reduce((entries, entry) => {
|
|
456
|
+
if (!entry.changeId || entries.some((candidate) => candidate.changeId === entry.changeId)) return entries;
|
|
457
|
+
entries.push(entry);
|
|
458
|
+
return entries;
|
|
459
|
+
}, []).slice(-8);
|
|
460
|
+
if (!uniqueTargets.length) return false;
|
|
461
|
+
let didUpdate = false;
|
|
462
|
+
for (const entry of uniqueTargets) {
|
|
463
|
+
const changeId = entry.changeId;
|
|
464
|
+
if (!changeId) continue;
|
|
465
|
+
request.session.snapshotPendingChangeIds.add(changeId);
|
|
466
|
+
const expandedEntries = await request.session.adapter.getSnapshotEntriesForFile({
|
|
467
|
+
workspacePath: request.session.workspacePath,
|
|
468
|
+
relativePath: request.session.relativePath,
|
|
469
|
+
entry
|
|
470
|
+
});
|
|
471
|
+
request.session.snapshotPendingChangeIds.delete(changeId);
|
|
472
|
+
request.session.snapshotLoadedChangeIds.add(changeId);
|
|
473
|
+
if (expandedEntries.length) {
|
|
474
|
+
request.session.snapshotEntries.push(...expandedEntries);
|
|
475
|
+
didUpdate = true;
|
|
476
|
+
}
|
|
477
|
+
}
|
|
478
|
+
if (!didUpdate) return true;
|
|
479
|
+
request.session.snapshotEntries = request.session.snapshotEntries.toSorted((left, right) => left.timestamp - right.timestamp || left.revision.localeCompare(right.revision));
|
|
480
|
+
for (const cacheKey of request.session.previewCache.keys()) if (cacheKey.startsWith("snapshot:")) request.session.previewCache.delete(cacheKey);
|
|
481
|
+
for (const cacheKey of request.session.rangeOverviewCache.keys()) if (cacheKey.startsWith("snapshot:")) request.session.rangeOverviewCache.delete(cacheKey);
|
|
482
|
+
for (const cacheKey of request.session.entryDiffCountCache.keys()) if (cacheKey.startsWith("snapshot:")) request.session.entryDiffCountCache.delete(cacheKey);
|
|
483
|
+
return true;
|
|
484
|
+
}
|
|
485
|
+
async function findNearestNonEmptyVisibleRange(request) {
|
|
486
|
+
if (request.candidateIndexes.length < 2) return null;
|
|
487
|
+
for (let index = request.candidateIndexes.length - 1; index > 0; index -= 1) {
|
|
488
|
+
const fromIndex = request.candidateIndexes[index - 1];
|
|
489
|
+
const toIndex = request.candidateIndexes[index];
|
|
490
|
+
if ((await getDiffPreview({
|
|
491
|
+
session: request.session,
|
|
492
|
+
fromIndex,
|
|
493
|
+
toIndex
|
|
494
|
+
})).hasChanges) return {
|
|
495
|
+
fromIndex,
|
|
496
|
+
toIndex
|
|
497
|
+
};
|
|
498
|
+
}
|
|
499
|
+
return null;
|
|
500
|
+
}
|
|
501
|
+
async function getRangeOverview(request) {
|
|
502
|
+
const comparisonSource = request.comparisonSource || "revision";
|
|
503
|
+
const normalizedFromIndex = Math.max(0, Math.min(request.fromIndex, request.toIndex));
|
|
504
|
+
const normalizedToIndex = Math.max(normalizedFromIndex, Math.max(request.fromIndex, request.toIndex));
|
|
505
|
+
const selectedEntryIndexes = (request.selectedEntryIndexes || []).filter((value) => Number.isInteger(value));
|
|
506
|
+
const cacheKey = `${comparisonSource}:${normalizedFromIndex}:${normalizedToIndex}:${selectedEntryIndexes.length ? selectedEntryIndexes.join(",") : "all"}`;
|
|
507
|
+
const cached = request.session.rangeOverviewCache.get(cacheKey);
|
|
508
|
+
if (cached) return cached;
|
|
509
|
+
const sourceEntries = getEntriesForSource({
|
|
510
|
+
session: request.session,
|
|
511
|
+
comparisonSource
|
|
512
|
+
});
|
|
513
|
+
const countedPaths = /* @__PURE__ */ new Map();
|
|
514
|
+
if (!request.session.workspaceFilesLoaded) await ensureWorkspaceFiles({ session: request.session });
|
|
515
|
+
const workspaceFileSet = new Set(request.session.workspaceFiles);
|
|
516
|
+
const entryIndexesToScan = selectedEntryIndexes.length ? selectedEntryIndexes : Array.from({ length: normalizedToIndex - normalizedFromIndex }, (_, index) => normalizedFromIndex + index + 1);
|
|
517
|
+
for (const index of entryIndexesToScan) {
|
|
518
|
+
const entry = sourceEntries[index];
|
|
519
|
+
if (!entry) continue;
|
|
520
|
+
const changedFiles = entry.isWorkingTree ? await request.session.adapter.listWorkingTreeFiles({ workspacePath: request.session.workspacePath }) : await request.session.adapter.listRevisionFiles({
|
|
521
|
+
workspacePath: request.session.workspacePath,
|
|
522
|
+
revision: entry.revision
|
|
523
|
+
});
|
|
524
|
+
for (const relativePath of changedFiles) {
|
|
525
|
+
if (!relativePath || !workspaceFileSet.has(relativePath) && relativePath !== request.session.relativePath) continue;
|
|
526
|
+
countedPaths.set(relativePath, (countedPaths.get(relativePath) || 0) + 1);
|
|
527
|
+
}
|
|
528
|
+
}
|
|
529
|
+
const items = [...countedPaths.entries()].map(([relativePath, changeCount]) => ({
|
|
530
|
+
relativePath,
|
|
531
|
+
changeCount,
|
|
532
|
+
isCurrentFile: relativePath === request.session.relativePath
|
|
533
|
+
})).toSorted((left, right) => {
|
|
534
|
+
if (left.changeCount !== right.changeCount) return right.changeCount - left.changeCount;
|
|
535
|
+
if (left.isCurrentFile !== right.isCurrentFile) return Number(right.isCurrentFile) - Number(left.isCurrentFile);
|
|
536
|
+
return left.relativePath.localeCompare(right.relativePath);
|
|
537
|
+
}).slice(0, 150);
|
|
538
|
+
request.session.rangeOverviewCache.set(cacheKey, items);
|
|
539
|
+
return items;
|
|
540
|
+
}
|
|
541
|
+
async function getRangeStackPreviews(request) {
|
|
542
|
+
const comparisonSource = request.comparisonSource || "revision";
|
|
543
|
+
const normalizedFromIndex = Math.max(0, Math.min(request.fromIndex, request.toIndex));
|
|
544
|
+
const normalizedToIndex = Math.max(normalizedFromIndex, Math.max(request.fromIndex, request.toIndex));
|
|
545
|
+
const comparison = getComparisonEntries({
|
|
546
|
+
entries: getEntriesForSource({
|
|
547
|
+
session: request.session,
|
|
548
|
+
comparisonSource
|
|
549
|
+
}),
|
|
550
|
+
fromIndex: normalizedFromIndex,
|
|
551
|
+
toIndex: normalizedToIndex
|
|
552
|
+
});
|
|
553
|
+
if (!comparison) return [];
|
|
554
|
+
const items = [];
|
|
555
|
+
for (const relativePath of request.relativePaths) {
|
|
556
|
+
const trimmed = relativePath.trim();
|
|
557
|
+
if (!trimmed) continue;
|
|
558
|
+
if (trimmed === request.session.relativePath) {
|
|
559
|
+
items.push({
|
|
560
|
+
relativePath: trimmed,
|
|
561
|
+
preview: await getDiffPreview({
|
|
562
|
+
session: request.session,
|
|
563
|
+
fromIndex: normalizedFromIndex,
|
|
564
|
+
toIndex: normalizedToIndex,
|
|
565
|
+
comparisonSource
|
|
566
|
+
})
|
|
567
|
+
});
|
|
568
|
+
continue;
|
|
569
|
+
}
|
|
570
|
+
const beforeText = comparison.fromEntry ? await readPathAtEntry({
|
|
571
|
+
session: request.session,
|
|
572
|
+
entry: comparison.fromEntry,
|
|
573
|
+
relativePath: trimmed
|
|
574
|
+
}) : "";
|
|
575
|
+
const afterText = await readPathAtEntry({
|
|
576
|
+
session: request.session,
|
|
577
|
+
entry: comparison.toEntry,
|
|
578
|
+
relativePath: trimmed
|
|
579
|
+
});
|
|
580
|
+
items.push({
|
|
581
|
+
relativePath: trimmed,
|
|
582
|
+
preview: buildDiffPreview({
|
|
583
|
+
index: normalizedToIndex,
|
|
584
|
+
previousEntry: comparison.fromEntry,
|
|
585
|
+
currentEntry: comparison.toEntry,
|
|
586
|
+
beforeText,
|
|
587
|
+
afterText,
|
|
588
|
+
fromIndex: normalizedFromIndex,
|
|
589
|
+
toIndex: normalizedToIndex,
|
|
590
|
+
beforePath: trimmed,
|
|
591
|
+
afterPath: trimmed,
|
|
592
|
+
comparisonSource
|
|
593
|
+
})
|
|
594
|
+
});
|
|
595
|
+
}
|
|
596
|
+
return items;
|
|
597
|
+
}
|
|
598
|
+
async function readPathAtEntry(request) {
|
|
599
|
+
const cacheKey = `${request.entry.id}:stack:${request.relativePath}`;
|
|
600
|
+
const cached = request.session.contentCache.get(cacheKey);
|
|
601
|
+
if (cached !== void 0) return cached;
|
|
602
|
+
try {
|
|
603
|
+
const content = request.entry.isWorkingTree ? await (0, node_fs_promises.readFile)(node_path.default.join(request.session.workspacePath, request.relativePath), "utf8") : await request.session.adapter.showFileAtRevision({
|
|
604
|
+
workspacePath: request.session.workspacePath,
|
|
605
|
+
revset: request.entry.revision,
|
|
606
|
+
filePath: request.relativePath
|
|
607
|
+
});
|
|
608
|
+
request.session.contentCache.set(cacheKey, content);
|
|
609
|
+
return content;
|
|
610
|
+
} catch {
|
|
611
|
+
request.session.contentCache.set(cacheKey, "");
|
|
612
|
+
return "";
|
|
613
|
+
}
|
|
614
|
+
}
|
|
615
|
+
async function getEntryDiffCounts(request) {
|
|
616
|
+
const comparisonSource = request.comparisonSource || "revision";
|
|
617
|
+
const sourceEntries = getEntriesForSource({
|
|
618
|
+
session: request.session,
|
|
619
|
+
comparisonSource
|
|
620
|
+
});
|
|
621
|
+
return Promise.all(request.entryIndexes.map(async (entryIndex) => {
|
|
622
|
+
const cacheKey = `${comparisonSource}:${entryIndex}`;
|
|
623
|
+
const cached = request.session.entryDiffCountCache.get(cacheKey);
|
|
624
|
+
if (cached !== void 0) return {
|
|
625
|
+
entryIndex,
|
|
626
|
+
diffCount: cached
|
|
627
|
+
};
|
|
628
|
+
const entry = sourceEntries[entryIndex];
|
|
629
|
+
if (!entry || entryIndex <= 0) {
|
|
630
|
+
request.session.entryDiffCountCache.set(cacheKey, 0);
|
|
631
|
+
return {
|
|
632
|
+
entryIndex,
|
|
633
|
+
diffCount: 0
|
|
634
|
+
};
|
|
635
|
+
}
|
|
636
|
+
let diffCount = 0;
|
|
637
|
+
if (entry.isWorkingTree) {
|
|
638
|
+
const previousEntry = sourceEntries[entryIndex - 1];
|
|
639
|
+
if (previousEntry) diffCount = (await request.session.adapter.buildRangeMultiDiffPlan({
|
|
640
|
+
workspacePath: request.session.workspacePath,
|
|
641
|
+
fromEntry: previousEntry,
|
|
642
|
+
toEntry: entry,
|
|
643
|
+
comparisonSource,
|
|
644
|
+
sourceEntries
|
|
645
|
+
})).files.length;
|
|
646
|
+
} else diffCount = (await request.session.adapter.buildRevisionMultiDiffPlan({
|
|
647
|
+
workspacePath: request.session.workspacePath,
|
|
648
|
+
entry,
|
|
649
|
+
entryIndex,
|
|
650
|
+
comparisonSource,
|
|
651
|
+
sourceEntries
|
|
652
|
+
})).files.length;
|
|
653
|
+
request.session.entryDiffCountCache.set(cacheKey, diffCount);
|
|
654
|
+
return {
|
|
655
|
+
entryIndex,
|
|
656
|
+
diffCount
|
|
657
|
+
};
|
|
658
|
+
}));
|
|
659
|
+
}
|
|
660
|
+
async function resolveEntryFilePath(request) {
|
|
661
|
+
if (request.entry.isWorkingTree) return request.session.relativePath;
|
|
662
|
+
if (request.entry.filePath) return request.entry.filePath;
|
|
663
|
+
await ensureEntryFilePath(request);
|
|
664
|
+
return request.entry.filePath || request.session.relativePath;
|
|
665
|
+
}
|
|
666
|
+
async function runSessionAction(request) {
|
|
667
|
+
request.session.activeActionAbortController?.abort();
|
|
668
|
+
const controller = new AbortController();
|
|
669
|
+
request.session.activeActionAbortController = controller;
|
|
670
|
+
try {
|
|
671
|
+
await request.action(controller.signal);
|
|
672
|
+
} catch (error) {
|
|
673
|
+
if (isAbortError(error) || controller.signal.aborted) return;
|
|
674
|
+
throw error;
|
|
675
|
+
} finally {
|
|
676
|
+
if (request.session.activeActionAbortController === controller) request.session.activeActionAbortController = void 0;
|
|
677
|
+
}
|
|
678
|
+
}
|
|
679
|
+
async function resolveBlameRevisionForLine(request) {
|
|
680
|
+
try {
|
|
681
|
+
if (request.backend === "git") {
|
|
682
|
+
const { stdout } = await runner.runGit({
|
|
683
|
+
workspacePath: request.workspacePath,
|
|
684
|
+
args: require_blame.buildGitBlameArgs({
|
|
685
|
+
relativePath: request.relativePath,
|
|
686
|
+
line: request.line
|
|
687
|
+
})
|
|
688
|
+
});
|
|
689
|
+
return require_blame.findBlameForLine(require_blame.parseGitBlamePorcelain(stdout), request.line)?.revision;
|
|
690
|
+
}
|
|
691
|
+
const { stdout } = await runner.runJj({
|
|
692
|
+
workspacePath: request.workspacePath,
|
|
693
|
+
args: require_blame.buildJjAnnotateArgs({ relativePath: request.relativePath })
|
|
694
|
+
});
|
|
695
|
+
return require_blame.findBlameForLine(require_blame.parseJjFileAnnotate(stdout), request.line)?.revision;
|
|
696
|
+
} catch {
|
|
697
|
+
return;
|
|
698
|
+
}
|
|
699
|
+
}
|
|
700
|
+
async function getDiffBlame(request) {
|
|
701
|
+
const comparisonSource = request.comparisonSource || "revision";
|
|
702
|
+
const normalizedFromIndex = Math.max(0, Math.min(request.fromIndex, request.toIndex));
|
|
703
|
+
const normalizedToIndex = Math.max(normalizedFromIndex, Math.max(request.fromIndex, request.toIndex));
|
|
704
|
+
const toEntry = getEntriesForSource({
|
|
705
|
+
session: request.session,
|
|
706
|
+
comparisonSource
|
|
707
|
+
})[normalizedToIndex];
|
|
708
|
+
const relativePath = toEntry?.filePath || request.session.relativePath;
|
|
709
|
+
if (!toEntry) return {
|
|
710
|
+
relativePath,
|
|
711
|
+
lines: []
|
|
712
|
+
};
|
|
713
|
+
try {
|
|
714
|
+
if (request.session.backend === "git") {
|
|
715
|
+
const { stdout } = await runner.runGit({
|
|
716
|
+
workspacePath: request.session.workspacePath,
|
|
717
|
+
args: require_blame.buildGitBlameFileArgs({
|
|
718
|
+
relativePath,
|
|
719
|
+
revision: toEntry.isWorkingTree ? void 0 : toEntry.revision
|
|
720
|
+
})
|
|
721
|
+
});
|
|
722
|
+
return {
|
|
723
|
+
relativePath,
|
|
724
|
+
lines: require_blame.parseGitBlamePorcelain(stdout)
|
|
725
|
+
};
|
|
726
|
+
}
|
|
727
|
+
const { stdout } = await runner.runJj({
|
|
728
|
+
workspacePath: request.session.workspacePath,
|
|
729
|
+
args: require_blame.buildJjAnnotateArgs({
|
|
730
|
+
relativePath,
|
|
731
|
+
revision: toEntry.isWorkingTree ? void 0 : toEntry.revision
|
|
732
|
+
})
|
|
733
|
+
});
|
|
734
|
+
return {
|
|
735
|
+
relativePath,
|
|
736
|
+
lines: require_blame.parseJjFileAnnotate(stdout)
|
|
737
|
+
};
|
|
738
|
+
} catch {
|
|
739
|
+
return {
|
|
740
|
+
relativePath,
|
|
741
|
+
lines: []
|
|
742
|
+
};
|
|
743
|
+
}
|
|
744
|
+
}
|
|
745
|
+
async function getFileOpLog(request) {
|
|
746
|
+
const relativePath = request.session.relativePath;
|
|
747
|
+
if (request.session.backend !== "jj") return {
|
|
748
|
+
relativePath,
|
|
749
|
+
entries: []
|
|
750
|
+
};
|
|
751
|
+
try {
|
|
752
|
+
const { stdout } = await runner.runJj({
|
|
753
|
+
workspacePath: request.session.workspacePath,
|
|
754
|
+
args: [
|
|
755
|
+
"op",
|
|
756
|
+
"log",
|
|
757
|
+
"--limit",
|
|
758
|
+
"80",
|
|
759
|
+
"-T",
|
|
760
|
+
require_file_oplog.JJ_OP_LOG_TEMPLATE
|
|
761
|
+
]
|
|
762
|
+
});
|
|
763
|
+
const fileEntries = [...request.session.snapshotEntries, ...request.session.entries.filter((entry) => Boolean(entry.operationId))];
|
|
764
|
+
return {
|
|
765
|
+
relativePath,
|
|
766
|
+
entries: require_file_oplog.filterFileOpLogPeek({
|
|
767
|
+
opLog: require_file_oplog.parseJjOpLogOutput(stdout),
|
|
768
|
+
fileEntries,
|
|
769
|
+
relativePath,
|
|
770
|
+
limit: 12
|
|
771
|
+
})
|
|
772
|
+
};
|
|
773
|
+
} catch {
|
|
774
|
+
return {
|
|
775
|
+
relativePath,
|
|
776
|
+
entries: []
|
|
777
|
+
};
|
|
778
|
+
}
|
|
779
|
+
}
|
|
780
|
+
async function searchFileHistory(request) {
|
|
781
|
+
const maxEntries = request.maxEntries ?? 40;
|
|
782
|
+
const touching = request.session.entries.map((entry, index) => ({
|
|
783
|
+
entry,
|
|
784
|
+
index
|
|
785
|
+
})).filter(({ entry }) => entry.touchesFile && !entry.isWorkingTree).slice(-maxEntries);
|
|
786
|
+
const contentsByIndex = /* @__PURE__ */ new Map();
|
|
787
|
+
for (const { entry, index } of touching) try {
|
|
788
|
+
const content = await getRevisionContent({
|
|
789
|
+
session: request.session,
|
|
790
|
+
entry,
|
|
791
|
+
entryIndex: index
|
|
792
|
+
});
|
|
793
|
+
contentsByIndex.set(index, content);
|
|
794
|
+
} catch {
|
|
795
|
+
contentsByIndex.set(index, "");
|
|
796
|
+
}
|
|
797
|
+
const orderedEntryIndexes = touching.map(({ index }) => index);
|
|
798
|
+
if (request.purpose === "sidebar") return require_history_search.searchHistoryDiffContents({
|
|
799
|
+
query: request.query,
|
|
800
|
+
orderedEntryIndexes,
|
|
801
|
+
contentsByIndex
|
|
802
|
+
});
|
|
803
|
+
return require_history_search.searchHistoryContents({
|
|
804
|
+
query: request.query,
|
|
805
|
+
orderedEntryIndexes,
|
|
806
|
+
contentsByIndex
|
|
807
|
+
});
|
|
808
|
+
}
|
|
809
|
+
async function buildTimelineEntries(request) {
|
|
810
|
+
const touchingEntries = request.fileEntries.map((entry) => ({
|
|
811
|
+
...entry,
|
|
812
|
+
touchesFile: true
|
|
813
|
+
}));
|
|
814
|
+
if (touchingEntries.length) touchingEntries[touchingEntries.length - 1].filePath = request.relativePath;
|
|
815
|
+
let entries = touchingEntries;
|
|
816
|
+
if (request.includeIntermediateRevisions && touchingEntries.length >= 2) try {
|
|
817
|
+
entries = mergeTimelineEntries({
|
|
818
|
+
repositoryEntries: await request.adapter.getRepositoryRevisionHistory({
|
|
819
|
+
workspacePath: request.workspacePath,
|
|
820
|
+
limit: Math.max(request.entryLimit, 200)
|
|
821
|
+
}),
|
|
822
|
+
fileEntries: touchingEntries
|
|
823
|
+
});
|
|
824
|
+
} catch {
|
|
825
|
+
entries = touchingEntries;
|
|
826
|
+
}
|
|
827
|
+
if (request.adapter.backend === "jj") entries = dedupeAdjacentTimelineEntries(entries);
|
|
828
|
+
return appendWorkingTreeEntry({
|
|
829
|
+
adapter: request.adapter,
|
|
830
|
+
workspacePath: request.workspacePath,
|
|
831
|
+
absolutePath: request.absolutePath,
|
|
832
|
+
relativePath: request.relativePath,
|
|
833
|
+
entries
|
|
834
|
+
});
|
|
835
|
+
}
|
|
836
|
+
async function listWorkspaceFiles(request) {
|
|
837
|
+
if (request.adapter.backend === "git") try {
|
|
838
|
+
const { stdout } = await runner.runGit({
|
|
839
|
+
workspacePath: request.workspacePath,
|
|
840
|
+
args: [
|
|
841
|
+
"ls-files",
|
|
842
|
+
"--cached",
|
|
843
|
+
"-z"
|
|
844
|
+
]
|
|
845
|
+
});
|
|
846
|
+
const files = stdout.split("\0").map((value) => value.trim()).filter(Boolean).toSorted((left, right) => left.localeCompare(right));
|
|
847
|
+
if (files.length) return files;
|
|
848
|
+
} catch {}
|
|
849
|
+
if (request.adapter.backend === "jj") try {
|
|
850
|
+
const { stdout } = await runner.runJj({
|
|
851
|
+
workspacePath: request.workspacePath,
|
|
852
|
+
args: ["file", "list"]
|
|
853
|
+
});
|
|
854
|
+
const files = stdout.split(/\r?\n/u).map((value) => value.trim()).filter(Boolean).toSorted((left, right) => left.localeCompare(right));
|
|
855
|
+
if (files.length) return files;
|
|
856
|
+
} catch {}
|
|
857
|
+
const files = [];
|
|
858
|
+
await collectWorkspaceFiles({
|
|
859
|
+
workspacePath: request.workspacePath,
|
|
860
|
+
currentDirectory: request.workspacePath,
|
|
861
|
+
files
|
|
862
|
+
});
|
|
863
|
+
return files.toSorted((left, right) => left.localeCompare(right));
|
|
864
|
+
}
|
|
865
|
+
async function collectWorkspaceFiles(request) {
|
|
866
|
+
const directoryEntries = await (0, node_fs_promises.readdir)(request.currentDirectory, { withFileTypes: true });
|
|
867
|
+
for (const directoryEntry of directoryEntries) {
|
|
868
|
+
if (directoryEntry.isDirectory() && IGNORED_WORKSPACE_DIRECTORIES.has(directoryEntry.name)) continue;
|
|
869
|
+
const absolutePath = node_path.default.join(request.currentDirectory, directoryEntry.name);
|
|
870
|
+
if (directoryEntry.isDirectory()) {
|
|
871
|
+
await collectWorkspaceFiles({
|
|
872
|
+
workspacePath: request.workspacePath,
|
|
873
|
+
currentDirectory: absolutePath,
|
|
874
|
+
files: request.files
|
|
875
|
+
});
|
|
876
|
+
continue;
|
|
877
|
+
}
|
|
878
|
+
if (!directoryEntry.isFile()) continue;
|
|
879
|
+
request.files.push(node_path.default.relative(request.workspacePath, absolutePath).replace(/\\/g, "/"));
|
|
880
|
+
}
|
|
881
|
+
}
|
|
882
|
+
async function appendWorkingTreeEntry(request) {
|
|
883
|
+
if (!await runner.fileExists({ filePath: request.absolutePath })) return request.entries;
|
|
884
|
+
const lastEntry = request.entries.at(-1);
|
|
885
|
+
const lastTouchingEntry = request.entries.filter((entry) => entry.touchesFile && !entry.isWorkingTree).at(-1);
|
|
886
|
+
if (lastEntry?.isWorkingTree) return request.entries;
|
|
887
|
+
if (lastTouchingEntry) {
|
|
888
|
+
if (await (0, node_fs_promises.readFile)(request.absolutePath, "utf8") === await request.adapter.showFileAtRevision({
|
|
889
|
+
workspacePath: request.workspacePath,
|
|
890
|
+
revset: lastTouchingEntry.revision,
|
|
891
|
+
filePath: lastTouchingEntry.filePath || request.relativePath
|
|
892
|
+
}) && (lastTouchingEntry.filePath || request.relativePath) === request.relativePath) return request.entries;
|
|
893
|
+
}
|
|
894
|
+
const now = /* @__PURE__ */ new Date();
|
|
895
|
+
return [...request.entries, {
|
|
896
|
+
id: "working-tree",
|
|
897
|
+
revision: "WORKTREE",
|
|
898
|
+
shortRevision: "Current",
|
|
899
|
+
changeId: void 0,
|
|
900
|
+
authorDate: now.toISOString(),
|
|
901
|
+
authorName: "",
|
|
902
|
+
description: "Working tree",
|
|
903
|
+
isWorkingTree: true,
|
|
904
|
+
touchesFile: true,
|
|
905
|
+
timestamp: now.getTime(),
|
|
906
|
+
filePath: request.relativePath
|
|
907
|
+
}];
|
|
908
|
+
}
|
|
909
|
+
function composeSnapshotEntries(request) {
|
|
910
|
+
if (!request.loadedChangeIds.size) return request.revisionEntries;
|
|
911
|
+
const snapshotEntriesByChangeId = request.snapshotEntries.reduce((groups, entry) => {
|
|
912
|
+
if (!entry.changeId) return groups;
|
|
913
|
+
const existing = groups.get(entry.changeId) || [];
|
|
914
|
+
existing.push(entry);
|
|
915
|
+
groups.set(entry.changeId, existing);
|
|
916
|
+
return groups;
|
|
917
|
+
}, /* @__PURE__ */ new Map());
|
|
918
|
+
return request.revisionEntries.flatMap((entry) => {
|
|
919
|
+
if (entry.isWorkingTree || !entry.touchesFile || !entry.changeId || !request.loadedChangeIds.has(entry.changeId)) return [entry];
|
|
920
|
+
return snapshotEntriesByChangeId.get(entry.changeId) || [entry];
|
|
921
|
+
});
|
|
922
|
+
}
|
|
923
|
+
async function getJjSnapshotPreview(request) {
|
|
924
|
+
const sourceEntries = getEntriesForSource({
|
|
925
|
+
session: request.session,
|
|
926
|
+
comparisonSource: "snapshot"
|
|
927
|
+
});
|
|
928
|
+
const normalizedFromIndex = Math.max(0, Math.min(request.fromIndex, request.toIndex));
|
|
929
|
+
const normalizedToIndex = Math.max(normalizedFromIndex, Math.max(request.fromIndex, request.toIndex));
|
|
930
|
+
const entry = sourceEntries[normalizedToIndex];
|
|
931
|
+
if (!entry) return createEmptyPreview({
|
|
932
|
+
index: normalizedToIndex,
|
|
933
|
+
fromIndex: normalizedFromIndex,
|
|
934
|
+
toIndex: normalizedToIndex,
|
|
935
|
+
comparisonSource: "snapshot",
|
|
936
|
+
title: "No revision selected"
|
|
937
|
+
});
|
|
938
|
+
const afterPath = await resolveEntryFilePath({
|
|
939
|
+
session: request.session,
|
|
940
|
+
entry,
|
|
941
|
+
entryIndex: normalizedToIndex
|
|
942
|
+
});
|
|
943
|
+
const previousEntryIndex = normalizedFromIndex === normalizedToIndex ? normalizedToIndex - 1 : normalizedFromIndex;
|
|
944
|
+
const previousEntry = previousEntryIndex >= 0 ? sourceEntries[previousEntryIndex] : void 0;
|
|
945
|
+
const previousPath = previousEntry ? await resolveEntryFilePath({
|
|
946
|
+
session: request.session,
|
|
947
|
+
entry: previousEntry,
|
|
948
|
+
entryIndex: previousEntryIndex
|
|
949
|
+
}) : entry.isWorkingTree ? request.session.relativePath : await resolvePreviousPathAcrossRevision({
|
|
950
|
+
session: request.session,
|
|
951
|
+
entry,
|
|
952
|
+
currentPath: afterPath
|
|
953
|
+
});
|
|
954
|
+
return {
|
|
955
|
+
...buildDiffPreview({
|
|
956
|
+
index: normalizedToIndex,
|
|
957
|
+
previousEntry,
|
|
958
|
+
currentEntry: entry,
|
|
959
|
+
beforeText: previousEntry ? await getRevisionContent({
|
|
960
|
+
session: request.session,
|
|
961
|
+
entry: previousEntry,
|
|
962
|
+
entryIndex: previousEntryIndex
|
|
963
|
+
}) : entry.isWorkingTree ? "" : await getContentForRevset({
|
|
964
|
+
session: request.session,
|
|
965
|
+
revset: `${entry.revision}-`,
|
|
966
|
+
filePath: previousPath
|
|
967
|
+
}),
|
|
968
|
+
afterText: await getRevisionContent({
|
|
969
|
+
session: request.session,
|
|
970
|
+
entry,
|
|
971
|
+
entryIndex: normalizedToIndex
|
|
972
|
+
}),
|
|
973
|
+
fromIndex: normalizedFromIndex,
|
|
974
|
+
toIndex: normalizedToIndex,
|
|
975
|
+
beforePath: previousPath,
|
|
976
|
+
afterPath,
|
|
977
|
+
comparisonSource: "snapshot"
|
|
978
|
+
}),
|
|
979
|
+
title: entry.isWorkingTree ? `Snapshot @ ${entry.shortRevision}` : `Snapshot ${entry.shortRevision}`
|
|
980
|
+
};
|
|
981
|
+
}
|
|
982
|
+
async function getRevisionContent(request) {
|
|
983
|
+
const resolvedPath = request.entry.isWorkingTree ? request.session.relativePath : await resolveEntryFilePath(request);
|
|
984
|
+
const cacheKey = `${request.entry.id}:${resolvedPath}`;
|
|
985
|
+
const cached = request.session.contentCache.get(cacheKey);
|
|
986
|
+
if (cached !== void 0) return cached;
|
|
987
|
+
const content = request.entry.isWorkingTree ? await (0, node_fs_promises.readFile)(request.session.absolutePath, "utf8") : await request.session.adapter.showFileAtRevision({
|
|
988
|
+
workspacePath: request.session.workspacePath,
|
|
989
|
+
revset: request.entry.revision,
|
|
990
|
+
filePath: resolvedPath
|
|
991
|
+
});
|
|
992
|
+
request.session.contentCache.set(cacheKey, content);
|
|
993
|
+
return content;
|
|
994
|
+
}
|
|
995
|
+
async function ensureEntryFilePath(request) {
|
|
996
|
+
const targetEntry = request.session.entries[request.entryIndex];
|
|
997
|
+
if (!targetEntry || targetEntry.filePath || targetEntry.isWorkingTree) return;
|
|
998
|
+
let knownIndex = -1;
|
|
999
|
+
for (let index = request.entryIndex + 1; index < request.session.entries.length; index += 1) if (request.session.entries[index]?.filePath) {
|
|
1000
|
+
knownIndex = index;
|
|
1001
|
+
break;
|
|
1002
|
+
}
|
|
1003
|
+
if (knownIndex < 0) {
|
|
1004
|
+
targetEntry.filePath = request.session.relativePath;
|
|
1005
|
+
return;
|
|
1006
|
+
}
|
|
1007
|
+
let currentPath = request.session.entries[knownIndex].filePath || request.session.relativePath;
|
|
1008
|
+
for (let index = knownIndex; index > request.entryIndex; index -= 1) {
|
|
1009
|
+
const currentEntry = request.session.entries[index];
|
|
1010
|
+
const previousEntry = request.session.entries[index - 1];
|
|
1011
|
+
if (!previousEntry) break;
|
|
1012
|
+
if (!previousEntry.filePath) {
|
|
1013
|
+
let previousPath = currentPath;
|
|
1014
|
+
if (currentEntry.touchesFile && !currentEntry.isWorkingTree) previousPath = await resolvePreviousPathAcrossRevision({
|
|
1015
|
+
session: request.session,
|
|
1016
|
+
entry: currentEntry,
|
|
1017
|
+
currentPath
|
|
1018
|
+
});
|
|
1019
|
+
previousEntry.filePath = previousPath;
|
|
1020
|
+
}
|
|
1021
|
+
currentPath = previousEntry.filePath || currentPath;
|
|
1022
|
+
}
|
|
1023
|
+
}
|
|
1024
|
+
}
|
|
1025
|
+
function normalizeTimelinePreferences(preferences) {
|
|
1026
|
+
if (!preferences) return { ...DEFAULT_TIMELINE_PREFERENCES };
|
|
1027
|
+
return {
|
|
1028
|
+
sidebarWidth: typeof preferences.sidebarWidth === "number" ? preferences.sidebarWidth : DEFAULT_TIMELINE_PREFERENCES.sidebarWidth,
|
|
1029
|
+
sidebarCollapsed: preferences.sidebarCollapsed === true,
|
|
1030
|
+
timelinePaneHeight: typeof preferences.timelinePaneHeight === "number" ? preferences.timelinePaneHeight : DEFAULT_TIMELINE_PREFERENCES.timelinePaneHeight,
|
|
1031
|
+
timelinePaneCollapsed: preferences.timelinePaneCollapsed === true,
|
|
1032
|
+
layoutMode: preferences.layoutMode === "unified" ? "unified" : DEFAULT_TIMELINE_PREFERENCES.layoutMode,
|
|
1033
|
+
contentMode: preferences.contentMode === "full" ? "full" : DEFAULT_TIMELINE_PREFERENCES.contentMode,
|
|
1034
|
+
comparisonMode: preferences.comparisonMode === "step" ? "step" : DEFAULT_TIMELINE_PREFERENCES.comparisonMode,
|
|
1035
|
+
comparisonSource: preferences.comparisonSource === "snapshot" ? "snapshot" : DEFAULT_TIMELINE_PREFERENCES.comparisonSource,
|
|
1036
|
+
showIntermediateRevisions: preferences.showIntermediateRevisions === true,
|
|
1037
|
+
preset: preferences.preset === "7d" || preferences.preset === "30d" || preferences.preset === "90d" || preferences.preset === "all" || preferences.preset === "year" ? preferences.preset : DEFAULT_TIMELINE_PREFERENCES.preset,
|
|
1038
|
+
customRevset: typeof preferences.customRevset === "string" ? preferences.customRevset.trim() : "",
|
|
1039
|
+
themePreference: preferences.themePreference === "light" || preferences.themePreference === "dark" ? preferences.themePreference : DEFAULT_TIMELINE_PREFERENCES.themePreference,
|
|
1040
|
+
heatmapOpen: preferences.heatmapOpen === true,
|
|
1041
|
+
lineDiffType: preferences.lineDiffType === "word" || preferences.lineDiffType === "char" || preferences.lineDiffType === "none" || preferences.lineDiffType === "word-alt" ? preferences.lineDiffType : DEFAULT_TIMELINE_PREFERENCES.lineDiffType
|
|
1042
|
+
};
|
|
1043
|
+
}
|
|
1044
|
+
function createEmptyPreview(args) {
|
|
1045
|
+
return {
|
|
1046
|
+
index: args.index,
|
|
1047
|
+
title: args.title,
|
|
1048
|
+
subtitle: "",
|
|
1049
|
+
diffCount: 0,
|
|
1050
|
+
additions: 0,
|
|
1051
|
+
deletions: 0,
|
|
1052
|
+
hunkCount: 0,
|
|
1053
|
+
hasChanges: false,
|
|
1054
|
+
fromIndex: args.fromIndex,
|
|
1055
|
+
toIndex: args.toIndex,
|
|
1056
|
+
comparisonSource: args.comparisonSource,
|
|
1057
|
+
beforePath: "",
|
|
1058
|
+
afterPath: "",
|
|
1059
|
+
beforeText: "",
|
|
1060
|
+
afterText: "",
|
|
1061
|
+
nonTextualDetails: []
|
|
1062
|
+
};
|
|
1063
|
+
}
|
|
1064
|
+
function buildDiffPreview(args) {
|
|
1065
|
+
const stats = require_diff_stats.computeDiffStats(args.beforeText, args.afterText);
|
|
1066
|
+
const title = args.previousEntry ? `${args.previousEntry.shortRevision} -> ${args.currentEntry.shortRevision}` : `Initial revision -> ${args.currentEntry.shortRevision}`;
|
|
1067
|
+
const subtitle = args.currentEntry.isWorkingTree ? "Working tree" : `${formatEntryDateTime(args.currentEntry.authorDate)} · ${args.currentEntry.description}`;
|
|
1068
|
+
return {
|
|
1069
|
+
index: args.index,
|
|
1070
|
+
title,
|
|
1071
|
+
subtitle,
|
|
1072
|
+
diffCount: 0,
|
|
1073
|
+
additions: stats.additions,
|
|
1074
|
+
deletions: stats.deletions,
|
|
1075
|
+
hunkCount: stats.hunkCount,
|
|
1076
|
+
hasChanges: stats.hasChanges,
|
|
1077
|
+
fromIndex: args.fromIndex,
|
|
1078
|
+
toIndex: args.toIndex,
|
|
1079
|
+
comparisonSource: args.comparisonSource,
|
|
1080
|
+
beforePath: args.beforePath,
|
|
1081
|
+
afterPath: args.afterPath,
|
|
1082
|
+
beforeText: args.beforeText,
|
|
1083
|
+
afterText: args.afterText,
|
|
1084
|
+
nonTextualDetails: stats.hasChanges ? [] : require_non_textual_details.buildNonTextualDetails({
|
|
1085
|
+
beforeText: args.beforeText,
|
|
1086
|
+
afterText: args.afterText,
|
|
1087
|
+
beforePath: args.beforePath,
|
|
1088
|
+
afterPath: args.afterPath,
|
|
1089
|
+
isWorkingTree: args.currentEntry.isWorkingTree
|
|
1090
|
+
})
|
|
1091
|
+
};
|
|
1092
|
+
}
|
|
1093
|
+
function mergeTimelineEntries(args) {
|
|
1094
|
+
const touchingByRevision = new Map(args.fileEntries.map((entry) => [entry.revision, entry]));
|
|
1095
|
+
const repoIndexes = args.fileEntries.map((entry) => args.repositoryEntries.findIndex((candidate) => candidate.revision === entry.revision)).filter((index) => index >= 0);
|
|
1096
|
+
if (!repoIndexes.length) return args.fileEntries;
|
|
1097
|
+
const startIndex = Math.min(...repoIndexes);
|
|
1098
|
+
const endIndex = Math.max(...repoIndexes);
|
|
1099
|
+
const merged = args.repositoryEntries.slice(startIndex, endIndex + 1).map((entry) => touchingByRevision.get(entry.revision) || entry);
|
|
1100
|
+
for (const entry of args.fileEntries) if (!merged.some((candidate) => candidate.revision === entry.revision)) merged.push(entry);
|
|
1101
|
+
return merged.toSorted((left, right) => left.timestamp - right.timestamp || left.revision.localeCompare(right.revision));
|
|
1102
|
+
}
|
|
1103
|
+
function dedupeAdjacentTimelineEntries(entries) {
|
|
1104
|
+
return entries.reduce((deduped, entry) => {
|
|
1105
|
+
const previousEntry = deduped.at(-1);
|
|
1106
|
+
if (!previousEntry?.changeId || previousEntry.changeId !== entry.changeId) {
|
|
1107
|
+
deduped.push(entry);
|
|
1108
|
+
return deduped;
|
|
1109
|
+
}
|
|
1110
|
+
if (previousEntry.touchesFile && entry.touchesFile) {
|
|
1111
|
+
deduped.push(entry);
|
|
1112
|
+
return deduped;
|
|
1113
|
+
}
|
|
1114
|
+
if (!previousEntry.touchesFile && entry.touchesFile) deduped[deduped.length - 1] = entry;
|
|
1115
|
+
return deduped;
|
|
1116
|
+
}, []);
|
|
1117
|
+
}
|
|
1118
|
+
function formatEntryMonthLabel(authorDate) {
|
|
1119
|
+
const date = new Date(authorDate);
|
|
1120
|
+
if (Number.isNaN(date.getTime())) return "Unknown month";
|
|
1121
|
+
return new Intl.DateTimeFormat("en", { month: "long" }).format(date);
|
|
1122
|
+
}
|
|
1123
|
+
function formatEntryShortDate(authorDate) {
|
|
1124
|
+
const date = new Date(authorDate);
|
|
1125
|
+
if (Number.isNaN(date.getTime())) return "Unknown date";
|
|
1126
|
+
return new Intl.DateTimeFormat("en", {
|
|
1127
|
+
month: "short",
|
|
1128
|
+
day: "numeric",
|
|
1129
|
+
year: "numeric"
|
|
1130
|
+
}).format(date);
|
|
1131
|
+
}
|
|
1132
|
+
function formatRelativeTime(timestamp) {
|
|
1133
|
+
const deltaSeconds = Math.round((timestamp - Date.now()) / 1e3);
|
|
1134
|
+
const formatter = new Intl.RelativeTimeFormat("en", { numeric: "auto" });
|
|
1135
|
+
for (const [unit, amount] of [
|
|
1136
|
+
["day", 86400],
|
|
1137
|
+
["hour", 3600],
|
|
1138
|
+
["minute", 60]
|
|
1139
|
+
]) if (Math.abs(deltaSeconds) >= amount || unit === "minute") return formatter.format(Math.round(deltaSeconds / amount), unit);
|
|
1140
|
+
return formatter.format(deltaSeconds, "second");
|
|
1141
|
+
}
|
|
1142
|
+
function formatEntryDateTime(authorDate) {
|
|
1143
|
+
const date = new Date(authorDate);
|
|
1144
|
+
if (Number.isNaN(date.getTime())) return "Unknown time";
|
|
1145
|
+
return date.toLocaleString();
|
|
1146
|
+
}
|
|
1147
|
+
function isAbortError(error) {
|
|
1148
|
+
return error instanceof Error && (error.name === "AbortError" || /aborted/i.test(error.message));
|
|
1149
|
+
}
|
|
1150
|
+
//#endregion
|
|
1151
|
+
exports.createTimelineService = createTimelineService;
|
|
1152
|
+
exports.normalizeTimelinePreferences = normalizeTimelinePreferences;
|
|
1153
|
+
|
|
1154
|
+
//# sourceMappingURL=timeline-service.cjs.map
|