forked from vincens2005/lite-xl-gitdiff-highlight
-
Notifications
You must be signed in to change notification settings - Fork 0
/
init.lua
220 lines (182 loc) · 5.6 KB
/
init.lua
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
-- mod-version:2
local core = require "core"
local config = require "core.config"
local DocView = require "core.docview"
local Doc = require "core.doc"
local common = require "core.common"
local command = require "core.command"
local style = require "core.style"
local gitdiff = require "plugins.gitdiff_highlight.gitdiff"
local _, MiniMap = pcall(require, "plugins.minimap")
-- vscode defaults
style.gitdiff_addition = {common.color "#587c0c"}
style.gitdiff_modification = {common.color "#0c7d9d"}
style.gitdiff_deletion = {common.color "#94151b"}
local function color_for_diff(diff)
if diff == "addition" then
return style.gitdiff_addition
elseif diff == "modification" then
return style.gitdiff_modification
else
return style.gitdiff_deletion
end
end
style.gitdiff_width = 3
local last_doc_lines = 0
-- maximum size of git diff to read, multiplied by current filesize
config.max_diff_size = 2
local diffs = setmetatable({}, { __mode = "k" })
local function get_diff(doc)
return diffs[doc] or {is_in_repo = false}
end
local function init_diff(doc)
diffs[doc] = {is_in_repo = true}
end
local function update_diff(doc)
if doc == nil or doc.filename == nil then return end
local current_file
if system.get_file_info(doc.filename) then
current_file = system.absolute_path(doc.filename)
else
current_file = doc.filename
end
core.log_quiet("updating diff for " .. current_file)
if not get_diff(doc).is_in_repo then
local is_in_repo = process.start({"git", "ls-files", "--error-unmatch", current_file})
while is_in_repo:running() do
coroutine.yield(0.1)
end
is_in_repo = is_in_repo:returncode()
is_in_repo = is_in_repo == 0
if is_in_repo then
init_diff(doc)
else
core.log_quiet("file ".. current_file .." is not in a git repository")
return
end
end
local max_diff_size = system.get_file_info(current_file).size * config.max_diff_size
local diff_proc = process.start({"git", "diff", "HEAD", "--word-diff", "--unified=1", "--no-color", current_file})
while diff_proc:running() do
coroutine.yield(0.1)
end
local raw_diff = diff_proc:read_stdout(max_diff_size)
local parsed_diff = gitdiff.changed_lines(raw_diff)
diffs[doc] = parsed_diff
diffs[doc].is_in_repo = true
end
local function gitdiff_padding(dv)
return style.padding.x * 1.5 + dv:get_font():get_width(#dv.doc.lines)
end
local old_docview_gutter = DocView.draw_line_gutter
local old_gutter_width = DocView.get_gutter_width
function DocView:draw_line_gutter(idx, x, y, width)
if not get_diff(self.doc).is_in_repo then
return old_docview_gutter(self, idx, x, y, width)
end
local gw, gpad = old_gutter_width(self)
old_docview_gutter(self, idx, x, y, gpad and gw - gpad or gw)
if diffs[self.doc][idx] == nil then
return
end
local color = color_for_diff(diffs[self.doc][idx])
-- add margin in between highlight and text
x = x + gitdiff_padding(self)
local yoffset = self:get_line_text_y_offset()
if diffs[self.doc][idx] ~= "deletion" then
renderer.draw_rect(x, y + yoffset, style.gitdiff_width, self:get_line_height(), color)
return
end
renderer.draw_rect(x, y + (yoffset * 4), style.gitdiff_width, self:get_line_height() / 2, color)
end
function DocView:get_gutter_width()
if not get_diff(self.doc).is_in_repo then return old_gutter_width(self) end
return old_gutter_width(self) + style.padding.x * style.gitdiff_width / 12
end
local old_text_change = Doc.on_text_change
function Doc:on_text_change(type)
local line
if not get_diff(self).is_in_repo then goto end_of_function end
line = self:get_selection()
if diffs[self][line] == "addition" then goto end_of_function end
-- TODO figure out how to detect an addition
if type == "insert" or (type == "remove" and #self.lines == last_doc_lines) then
diffs[self][line] = "modification"
elseif type == "remove" then
diffs[self][line] = "deletion"
end
::end_of_function::
last_doc_lines = #self.lines
return old_text_change(self, type)
end
local old_doc_save = Doc.save
function Doc:save(...)
old_doc_save(self, ...)
core.add_thread(function()
update_diff(self)
end)
end
local old_docview_new = DocView.new
function DocView:new(...)
old_docview_new(self, ...)
core.add_thread(function()
update_diff(self.doc)
end)
end
local old_doc_load = Doc.load
function Doc:load(...)
old_doc_load(self, ...)
core.add_thread(function()
update_diff(self)
end)
end
if type(MiniMap) == "table" then
-- Override MiniMap's line_highlight_color, but first
-- stash the old one (using [] in case it is not there at all)
local old_line_highlight_color = MiniMap["line_highlight_color"]
function MiniMap:line_highlight_color(line_index)
local diff = get_diff(core.active_view.doc)
if diff.is_in_repo and diff[line_index] then
return color_for_diff(diff[line_index])
end
return old_line_highlight_color(line_index)
end
end
local function jump_to_next_change()
local doc = core.active_view.doc
local line, col = doc:get_selection()
if not get_diff(doc).is_in_repo then return end
while diffs[doc][line] do
line = line + 1
end
while line < #doc.lines do
if diffs[doc][line] then
doc:set_selection(line, col, line, col)
return
end
line = line + 1
end
end
local function jump_to_previous_change()
local doc = core.active_view.doc
local line, col = doc:get_selection()
if not get_diff(doc).is_in_repo then return end
while diffs[doc][line] do
line = line - 1
end
while line > 0 do
if diffs[doc][line] then
doc:set_selection(line, col, line, col)
return
end
line = line - 1
end
end
command.add("core.docview", {
["gitdiff:previous-change"] = function()
jump_to_previous_change()
end,
["gitdiff:next-change"] = function()
jump_to_next_change()
end,
})