Skip to content

Commit 85731c6

Browse files
committed
feat(highlights): split modified states into staged and unstaged groups
1 parent 6c92acd commit 85731c6

9 files changed

Lines changed: 116 additions & 22 deletions

File tree

README.md

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,8 @@ require("oil-git").setup({
111111
-- Colors (only applied if highlight groups don't exist)
112112
highlights = {
113113
OilGitAdded = { fg = "#a6e3a1" },
114-
OilGitModified = { fg = "#f9e2af" },
114+
OilGitModifiedStaged = { fg = "#f9e2af" },
115+
OilGitModifiedUnstaged = { fg = "#e5c890" },
115116
OilGitRenamed = { fg = "#cba6f7" },
116117
OilGitDeleted = { fg = "#f38ba8" },
117118
OilGitCopied = { fg = "#cba6f7" },
@@ -138,7 +139,8 @@ When `symbol_position = "signcolumn"`, you can optionally provide
138139
| Status | File Symbol | Color | Description |
139140
|--------|-------------|-------|-------------|
140141
| Added | `+` | Green | Staged new file |
141-
| Modified | `~` | Yellow | Changed (staged/unstaged) |
142+
| Modified (staged) | `~` | Yellow | Changes staged in the index |
143+
| Modified (unstaged) | `~` | Gold | Changes only in the worktree |
142144
| Renamed | `->` | Purple | Renamed file |
143145
| Deleted | `D` | Red | Deleted file |
144146
| Copied | `C` | Purple | Copied file |
@@ -152,8 +154,9 @@ Directories show the highest-priority status among their contents:
152154

153155
| Priority | Status | Description |
154156
|----------|--------|-------------|
155-
| 7 | Conflict | Merge conflicts need immediate attention |
156-
| 6 | Modified | Staged or unstaged changes |
157+
| 8 | Conflict | Merge conflicts need immediate attention |
158+
| 7 | Modified (staged) | Staged changes take precedence in directories |
159+
| 6 | Modified (unstaged) | Worktree-only changes |
157160
| 5 | Deleted | Deleted files |
158161
| 4 | Added | New staged files |
159162
| 3 | Renamed/Copied | Renamed or copied files |

lua/oil-git/config.lua

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,8 @@ local default_config = {
3939
highlights = {
4040
OilGitAdded = { fg = "#a6e3a1" },
4141
OilGitModified = { fg = "#f9e2af" },
42+
OilGitModifiedStaged = { fg = "#f9e2af" },
43+
OilGitModifiedUnstaged = { fg = "#e5c890" },
4244
OilGitRenamed = { fg = "#cba6f7" },
4345
OilGitUntracked = { fg = "#89b4fa" },
4446
OilGitIgnored = { fg = "#6c7086" },
@@ -50,6 +52,27 @@ local default_config = {
5052

5153
local config = {}
5254

55+
local function apply_legacy_modified_highlights(opts, merged_config)
56+
local user_highlights = opts.highlights or {}
57+
local legacy_modified = user_highlights.OilGitModified
58+
59+
if not legacy_modified then
60+
return
61+
end
62+
63+
if not user_highlights.OilGitModifiedStaged then
64+
merged_config.highlights.OilGitModifiedStaged =
65+
vim.deepcopy(legacy_modified)
66+
end
67+
68+
if not user_highlights.OilGitModifiedUnstaged then
69+
merged_config.highlights.OilGitModifiedUnstaged =
70+
vim.deepcopy(legacy_modified)
71+
end
72+
73+
merged_config.highlights.OilGitModified = vim.deepcopy(legacy_modified)
74+
end
75+
5376
local function make_readonly(t)
5477
if type(t) ~= "table" then
5578
return t
@@ -83,6 +106,7 @@ end
83106
function M.setup(opts)
84107
opts = opts or {}
85108
config = vim.tbl_deep_extend("force", default_config, opts)
109+
apply_legacy_modified_highlights(opts, config)
86110
end
87111

88112
function M.get()

lua/oil-git/constants.lua

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ M.DEFAULTS = {
1111
M.HIGHLIGHT_GROUPS = {
1212
ADDED = "OilGitAdded",
1313
MODIFIED = "OilGitModified",
14+
MODIFIED_STAGED = "OilGitModifiedStaged",
15+
MODIFIED_UNSTAGED = "OilGitModifiedUnstaged",
1416
RENAMED = "OilGitRenamed",
1517
DELETED = "OilGitDeleted",
1618
COPIED = "OilGitCopied",
@@ -43,8 +45,10 @@ M.PRIORITY = {
4345
COPIED = 3,
4446
ADDED = 4,
4547
DELETED = 5,
46-
MODIFIED = 6,
47-
CONFLICT = 7,
48+
MODIFIED_UNSTAGED = 6,
49+
MODIFIED_STAGED = 7,
50+
MODIFIED = 7,
51+
CONFLICT = 8,
4852
}
4953

5054
return M

lua/oil-git/status_mapper.lua

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,9 @@ local constants = require("oil-git.constants")
55
local STATUS_MAP = {
66
A = { constants.HIGHLIGHT_GROUPS.ADDED, "added", constants.PRIORITY.ADDED },
77
M = {
8-
constants.HIGHLIGHT_GROUPS.MODIFIED,
8+
constants.HIGHLIGHT_GROUPS.MODIFIED_STAGED,
99
"modified",
10-
constants.PRIORITY.MODIFIED,
10+
constants.PRIORITY.MODIFIED_STAGED,
1111
},
1212
R = {
1313
constants.HIGHLIGHT_GROUPS.RENAMED,
@@ -33,9 +33,9 @@ local STATUS_MAP = {
3333

3434
local WORKTREE_STATUS_MAP = {
3535
M = {
36-
constants.HIGHLIGHT_GROUPS.MODIFIED,
36+
constants.HIGHLIGHT_GROUPS.MODIFIED_UNSTAGED,
3737
"modified",
38-
constants.PRIORITY.MODIFIED,
38+
constants.PRIORITY.MODIFIED_UNSTAGED,
3939
},
4040
D = {
4141
constants.HIGHLIGHT_GROUPS.DELETED,

tests/plenary/config_spec.lua

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,8 +60,29 @@ describe("config", function()
6060
assert.equals("*", cfg.symbols.directory.added)
6161
assert.equals("#ffffff", cfg.highlights.OilGitAdded.fg)
6262
assert.is_not_nil(cfg.highlights.OilGitModified)
63+
assert.is_not_nil(cfg.highlights.OilGitModifiedStaged)
64+
assert.is_not_nil(cfg.highlights.OilGitModifiedUnstaged)
6365
end)
6466

67+
it(
68+
"should apply legacy modified highlight to both new groups",
69+
function()
70+
config.setup({
71+
highlights = {
72+
OilGitModified = { fg = "#111111" },
73+
},
74+
})
75+
local cfg = config.get()
76+
77+
assert.equals("#111111", cfg.highlights.OilGitModified.fg)
78+
assert.equals("#111111", cfg.highlights.OilGitModifiedStaged.fg)
79+
assert.equals(
80+
"#111111",
81+
cfg.highlights.OilGitModifiedUnstaged.fg
82+
)
83+
end
84+
)
85+
6586
it("should handle debug option", function()
6687
config.setup({ debug = "verbose" })
6788
local cfg = config.get()
@@ -172,6 +193,8 @@ describe("config", function()
172193
local expected_groups = {
173194
"OilGitAdded",
174195
"OilGitModified",
196+
"OilGitModifiedStaged",
197+
"OilGitModifiedUnstaged",
175198
"OilGitRenamed",
176199
"OilGitDeleted",
177200
"OilGitCopied",

tests/plenary/highlights_spec.lua

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,13 +34,17 @@ describe("highlights", function()
3434
it("should create highlight groups that don't exist", function()
3535
vim.cmd("highlight clear OilGitAdded")
3636
vim.cmd("highlight clear OilGitModified")
37+
vim.cmd("highlight clear OilGitModifiedStaged")
38+
vim.cmd("highlight clear OilGitModifiedUnstaged")
3739
vim.cmd("highlight clear OilGitDeleted")
3840

3941
highlights.setup()
4042

4143
local groups = {
4244
"OilGitAdded",
4345
"OilGitModified",
46+
"OilGitModifiedStaged",
47+
"OilGitModifiedUnstaged",
4448
"OilGitDeleted",
4549
"OilGitRenamed",
4650
"OilGitUntracked",

tests/plenary/init_spec.lua

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,8 @@ describe("init", function()
155155
local groups = {
156156
"OilGitAdded",
157157
"OilGitModified",
158+
"OilGitModifiedStaged",
159+
"OilGitModifiedUnstaged",
158160
"OilGitDeleted",
159161
"OilGitRenamed",
160162
"OilGitUntracked",

tests/plenary/status_mapper_spec.lua

Lines changed: 15 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -142,9 +142,9 @@ describe("status_mapper", function()
142142
assert.equals("+", sym)
143143
end)
144144

145-
it("should map 'M ' to modified", function()
145+
it("should map 'M ' to staged modified", function()
146146
local hl, sym = status_mapper.map("M ", test_symbols)
147-
assert.equals(constants.HIGHLIGHT_GROUPS.MODIFIED, hl)
147+
assert.equals(constants.HIGHLIGHT_GROUPS.MODIFIED_STAGED, hl)
148148
assert.equals("~", sym)
149149
end)
150150

@@ -168,9 +168,9 @@ describe("status_mapper", function()
168168
end)
169169

170170
describe("worktree status - second character", function()
171-
it("should map ' M' to modified", function()
171+
it("should map ' M' to unstaged modified", function()
172172
local hl, sym = status_mapper.map(" M", test_symbols)
173-
assert.equals(constants.HIGHLIGHT_GROUPS.MODIFIED, hl)
173+
assert.equals(constants.HIGHLIGHT_GROUPS.MODIFIED_UNSTAGED, hl)
174174
assert.equals("~", sym)
175175
end)
176176

@@ -190,7 +190,13 @@ describe("status_mapper", function()
190190

191191
it("should prioritize index status for 'MD'", function()
192192
local hl, sym = status_mapper.map("MD", test_symbols)
193-
assert.equals(constants.HIGHLIGHT_GROUPS.MODIFIED, hl)
193+
assert.equals(constants.HIGHLIGHT_GROUPS.MODIFIED_STAGED, hl)
194+
assert.equals("~", sym)
195+
end)
196+
197+
it("should prioritize staged modified for 'MM'", function()
198+
local hl, sym = status_mapper.map("MM", test_symbols)
199+
assert.equals(constants.HIGHLIGHT_GROUPS.MODIFIED_STAGED, hl)
194200
assert.equals("~", sym)
195201
end)
196202

@@ -271,9 +277,9 @@ describe("status_mapper", function()
271277
assert.equals(constants.PRIORITY.ADDED, priority)
272278
end)
273279

274-
it("should return MODIFIED priority for 'M '", function()
280+
it("should return staged modified priority for 'M '", function()
275281
local priority = status_mapper.get_priority("M ")
276-
assert.equals(constants.PRIORITY.MODIFIED, priority)
282+
assert.equals(constants.PRIORITY.MODIFIED_STAGED, priority)
277283
end)
278284

279285
it("should return DELETED priority for 'D '", function()
@@ -293,9 +299,9 @@ describe("status_mapper", function()
293299
end)
294300

295301
describe("worktree statuses", function()
296-
it("should return MODIFIED priority for ' M'", function()
302+
it("should return unstaged modified priority for ' M'", function()
297303
local priority = status_mapper.get_priority(" M")
298-
assert.equals(constants.PRIORITY.MODIFIED, priority)
304+
assert.equals(constants.PRIORITY.MODIFIED_UNSTAGED, priority)
299305
end)
300306

301307
it("should return DELETED priority for ' D'", function()

tests/plenary/trie_spec.lua

Lines changed: 31 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ describe("trie", function()
5858
root.children["src"].children["file.lua"].status
5959
)
6060
assert.equals(
61-
6,
61+
7,
6262
root.children["src"].children["file.lua"].priority
6363
)
6464
end
@@ -69,7 +69,7 @@ describe("trie", function()
6969
local git_root = "/repo"
7070

7171
trie.insert(root, "/repo/src/a.lua", "A ", git_root) -- ADDED = 4
72-
trie.insert(root, "/repo/src/b.lua", "M ", git_root) -- MODIFIED = 6
72+
trie.insert(root, "/repo/src/b.lua", "M ", git_root) -- MODIFIED_STAGED = 7
7373
trie.insert(root, "/repo/src/c.lua", "??", git_root) -- UNTRACKED = 2
7474

7575
assert.is_nil(root.children["src"].status)
@@ -204,7 +204,35 @@ describe("trie", function()
204204
local root = trie.create_node()
205205
local git_root = "/repo"
206206
trie.insert(root, "/repo/src/a.lua", "??", git_root) -- UNTRACKED = 2
207-
trie.insert(root, "/repo/src/b.lua", "M ", git_root) -- MODIFIED = 6
207+
trie.insert(root, "/repo/src/b.lua", "M ", git_root) -- MODIFIED_STAGED = 7
208+
209+
local result = trie.lookup(root, "/repo/src", git_root)
210+
assert.equals("M ", result)
211+
end
212+
)
213+
214+
it(
215+
"should compute unstaged modified directory status from children",
216+
function()
217+
local root = trie.create_node()
218+
local git_root = "/repo"
219+
220+
trie.insert(root, "/repo/src/a.lua", " M", git_root)
221+
trie.insert(root, "/repo/src/b.lua", "??", git_root)
222+
223+
local result = trie.lookup(root, "/repo/src", git_root)
224+
assert.equals(" M", result)
225+
end
226+
)
227+
228+
it(
229+
"should prefer staged modified over unstaged for directories",
230+
function()
231+
local root = trie.create_node()
232+
local git_root = "/repo"
233+
234+
trie.insert(root, "/repo/src/a.lua", " M", git_root)
235+
trie.insert(root, "/repo/src/b.lua", "M ", git_root)
208236

209237
local result = trie.lookup(root, "/repo/src", git_root)
210238
assert.equals("M ", result)

0 commit comments

Comments
 (0)