Skip to content

feat(explorer): Git stats + Explorer Keymaps + Explorer line renderer - #448

Closed
richardgill wants to merge 8 commits into
esmuellert:mainfrom
richardgill:review-deverts-commits
Closed

feat(explorer): Git stats + Explorer Keymaps + Explorer line renderer#448
richardgill wants to merge 8 commits into
esmuellert:mainfrom
richardgill:review-deverts-commits

Conversation

@richardgill

@richardgill richardgill commented Jul 17, 2026

Copy link
Copy Markdown
Collaborator

This PR adds three related explorer features. They can be split into separate PRs if preferred.

Git line statistics

Optionally show per-file and aggregate insertion/deletion counts:

explorer = {
  line_stats = {
    enabled = true,
    count_untracked = false,
    max_untracked_bytes = 1024 * 1024,
  },
}

Files display +12 -4 or bin; folders and groups show aggregate totals. Statistics are disabled by default because they require additional Git queries.

Explorer line formatters

Replace complete file, folder, or group rows:

explorer = {
  formatters = {
    -- file = function(ctx) return layout end,
    -- folder = function(ctx) return layout end,
    group = function(ctx)
      return {
        left = {{
          segments = {{ text = " " .. ctx.label, hl = "CodeDiffExplorerTreeGroup" }},
          truncate_priority = 1,
        }},
        right = {{
          segments = {{ text = ctx.file_count .. " files", hl = "Number" }},
        }},
      }
    end,
  },
}

Formatters receive metadata for the represented files and return styled, left/right-aligned regions with display-width-aware truncation.

Custom explorer keymaps

Add buffer-local explorer actions:

keymaps = {
  explorer = {
    custom = {{
      key = "gy",
      desc = "Copy explorer path",
      callback = function(ctx)
        if ctx.entry.path then
          vim.fn.setreg("+", ctx.entry.path)
        end
      end,
    }},
  },
}

Callbacks receive the selected file, directory, or group, plus redraw() and refresh(). Custom mappings can override built-in explorer mappings.

Fixes #181
Fixes #455
Fixes #456

@richardgill richardgill reopened this Jul 18, 2026
@richardgill richardgill changed the title feat(explorer): add optional line change statistics feat(explorer): Git stats + Explorer Keymaps + Explorer line renderer Jul 20, 2026
@richardgill
richardgill marked this pull request as ready for review July 20, 2026 16:20
@richardgill richardgill added the Size/XL Epic - Major feature, needs breakdown (multiple weeks) label Jul 22, 2026
@richardgill
richardgill requested a review from esmuellert as a code owner July 23, 2026 12:02
@esmuellert

Copy link
Copy Markdown
Owner

Thanks for the many fancy new features! I indeed would like it be multiple smaller PRs for easier review, and better risk management if there is a need for revert, thanks

@richardgill

Copy link
Copy Markdown
Collaborator Author

@esmuellert No problem 👍, here is a PR which just adds the explorer formatters: #472

esmuellert added a commit that referenced this pull request Jul 29, 2026
- Add `explorer.formatters.{file,folder,group}`callbacks.
- Support styled left/right regions with display-width-aware truncation.
- Add `explorer.ellipsis` to customize truncated-region markers,
defaulting to `…`.
- Provide row metadata for files, folders, and groups while preserving
selected-row backgrounds.
- Passing `nil` to the formatters uses the default formatters from
`formatters.lua`
  - New default formatter includes `...` truncation (this is a change).

This extracts only the Explorer line formatter functionality from #448.
Git line statistics and custom Explorer keymaps are intentionally
excluded.

## Demo

All three recordings use the same repository fixture, window dimensions,
cursor position, and inputs. The Explorer is resized to demonstrate
display-width-aware truncation and right-aligned regions.

### Before - upstream `main`

Default Explorer rendering:



https://github.com/user-attachments/assets/c106ba3e-e130-4265-8d32-95e8aa613b7e



### After - default formatters

- No formatter configuration is provided.
- Normal-width rendering remains unchanged;
- narrowing demonstrates the new built-in formatter truncation:


https://github.com/user-attachments/assets/7a1c8908-c2b2-4cae-be3c-98391dc88dbc




### After - custom formatters with `mini.icons`

Configuration: [custom Explorer formatters using
`mini.icons`](https://gist.github.com/richardgill/050a30e1c253dd8a20413f614b563ed0)

- This example replaces file, folder, and group rows with custom
function.
- Uses metadata such as file counts and Git groups
- Demonstrates using `mini.icons` for file and directory icons.



https://github.com/user-attachments/assets/d3766882-7233-43e2-a66c-f040285228b8
@esmuellert
esmuellert requested a review from yanuoma as a code owner July 29, 2026 03:16
esmuellert added a commit that referenced this pull request Jul 29, 2026
- Add `explorer.formatters.{file,folder,group}`callbacks.
- Support styled left/right regions with display-width-aware truncation.
- Add `explorer.ellipsis` to customize truncated-region markers,
defaulting to `…`.
- Provide row metadata for files, folders, and groups while preserving
selected-row backgrounds.
- Passing `nil` to the formatters uses the default formatters from
`formatters.lua`
  - New default formatter includes `...` truncation (this is a change).

This extracts only the Explorer line formatter functionality from #448.
Git line statistics and custom Explorer keymaps are intentionally
excluded.

## Demo

All three recordings use the same repository fixture, window dimensions,
cursor position, and inputs. The Explorer is resized to demonstrate
display-width-aware truncation and right-aligned regions.

### Before - upstream `main`

Default Explorer rendering:



https://github.com/user-attachments/assets/c106ba3e-e130-4265-8d32-95e8aa613b7e



### After - default formatters

- No formatter configuration is provided.
- Normal-width rendering remains unchanged;
- narrowing demonstrates the new built-in formatter truncation:


https://github.com/user-attachments/assets/7a1c8908-c2b2-4cae-be3c-98391dc88dbc




### After - custom formatters with `mini.icons`

Configuration: [custom Explorer formatters using
`mini.icons`](https://gist.github.com/richardgill/050a30e1c253dd8a20413f614b563ed0)

- This example replaces file, folder, and group rows with custom
function.
- Uses metadata such as file counts and Git groups
- Demonstrates using `mini.icons` for file and directory icons.



https://github.com/user-attachments/assets/d3766882-7233-43e2-a66c-f040285228b8
@richardgill

Copy link
Copy Markdown
Collaborator Author

This has now been split into three focused PRs:

Closing this combined PR in favor of those replacements.

esmuellert added a commit that referenced this pull request Aug 1, 2026
Adds opt-in Git line statistics to the Explorer.

- works across all Explorer comparison modes:
  - working tree status
  - a revision against the working tree
  - two revisions against each other
- handles staged, unstaged, renamed, deleted, binary, conflicting, and
optionally bounded untracked files
- preserves path filtering when calculating statistics
- exposes per-file and aggregate folder/group statistics to the `file`,
`folder`, and `group` formatter functions
- includes sensible default formatter functions:
  - right-aligned, truncatable file statistics
  - aggregate group totals

### Screenshots

#### Before

```lua
require("codediff").setup({
  explorer = {
    width = 56,
    view_mode = "tree",
    flatten_dirs = false,
  },
})
```


![Before](https://gist.githubusercontent.com/richardgill/976220c4918b767279ce6fd04ea0deba/raw/before.png)

#### After - disabled by default

```lua
require("codediff").setup({
  explorer = {
    width = 56,
    view_mode = "tree",
    flatten_dirs = false,
    -- line_stats is omitted because it is disabled by default.
  },
})
```

![After
disabled](https://gist.githubusercontent.com/richardgill/976220c4918b767279ce6fd04ea0deba/raw/after-disabled.png)

#### After - enabled with default formatters

```lua
require("codediff").setup({
  explorer = {
    width = 56,
    view_mode = "tree",
    flatten_dirs = false,
    line_stats = {
      enabled = true,
      count_untracked = true,
      max_untracked_bytes = 1024 * 1024,
    },
  },
})
```

![After
enabled](https://gist.githubusercontent.com/richardgill/976220c4918b767279ce6fd04ea0deba/raw/after-enabled.png)

#### After - custom file, folder, and group formatters

```lua
local default_formatters = require("codediff.ui.explorer.formatters")

require("codediff").setup({
  explorer = {
    width = 56,
    view_mode = "tree",
    flatten_dirs = false,
    line_stats = {
      enabled = true,
      count_untracked = true,
      max_untracked_bytes = 1024 * 1024,
    },
    formatters = {
      file = function(ctx)
        local layout = default_formatters.file(ctx)
        local right = {}

        if ctx.stats then
          local segments = ctx.stats.binary and {
            { text = "binary ", hl = "CodeDiffExplorerStatBinary" },
          } or {
            { text = "↑" .. ctx.stats.insertions, hl = "CodeDiffExplorerStatInsertions" },
            { text = " ↓" .. ctx.stats.deletions .. " ", hl = "CodeDiffExplorerStatDeletions" },
          }
          right[#right + 1] = {
            segments = segments,
            truncate_priority = 3,
          }
        end

        right[#right + 1] = {
          segments = {
            { text = ctx.status, hl = ctx.status_hl },
            { text = string.rep(" ", ctx.status_right_margin), hl = "Normal" },
          },
        }
        layout.right = right
        return layout
      end,

      folder = function(ctx)
        local layout = default_formatters.folder(ctx)
        if not ctx.stats then
          return layout
        end

        layout.right = {
          {
            segments = {
              { text = "↑" .. ctx.stats.insertions, hl = "CodeDiffExplorerStatInsertions" },
              { text = " ↓" .. ctx.stats.deletions, hl = "CodeDiffExplorerStatDeletions" },
            },
          },
        }
        return layout
      end,

      group = function(ctx)
        return {
          left = {
            {
              segments = { { text = " " .. ctx.label, hl = "CodeDiffExplorerTreeGroup" } },
              truncate_priority = 1,
            },
          },
          right = {
            {
              segments = {
                { text = ctx.file_count .. " files · ", hl = "CodeDiffExplorerStatFiles" },
                { text = "↑" .. ctx.stats.insertions, hl = "CodeDiffExplorerStatInsertions" },
                { text = " ↓" .. ctx.stats.deletions, hl = "CodeDiffExplorerStatDeletions" },
              },
            },
          },
          min_gap = 2,
        }
      end,
    },
  },
})
```

![After custom
formatters](https://gist.githubusercontent.com/richardgill/976220c4918b767279ce6fd04ea0deba/raw/after-custom.png)

Supercedes part of #448.

Fixes #181
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Size/XL Epic - Major feature, needs breakdown (multiple weeks)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Feature: support custom explorer keymaps Feature: support custom explorer line formatting Feature: Show diff stats (+/-) next to files in explorer

2 participants