Closed MuhammadSawalhy closed 1 year ago
This would be a nice addition. Even just showing vim.diff()
output in a buffer marked with diff filetype would be great.
This should show diff output in a floating window.
diff --git a/lua/competitest/init.lua b/lua/competitest/init.lua
index a0eebdc..b8b5474 100644
--- a/lua/competitest/init.lua
+++ b/lua/competitest/init.lua
@@ -46,6 +46,7 @@ local default_config = {
view_output = { "a", "A" },
view_stdout = { "o", "O" },
view_stderr = { "e", "E" },
+ view_diff = { "D", "<c-d>" },
close = { "q", "Q" },
},
viewer = { -- viewer window, to view in detail a stream (input, expected output, stdout or stderr)
diff --git a/lua/competitest/runner.lua b/lua/competitest/runner.lua
index 4212813..094b8cd 100644
--- a/lua/competitest/runner.lua
+++ b/lua/competitest/runner.lua
@@ -74,6 +74,7 @@ function TCRunner:run_testcases(tctbl, compile)
stdin = tc.input,
-- expout = expected output
expout = tc.output,
+ diff = '',
tcnum = tcnum,
timelimit = self.config.maximum_time,
})
@@ -230,6 +231,7 @@ function TCRunner:execute_testcase(tcindex, exec, args, dir, callback)
self:update_ui(true)
else
tc.stdout = tc.stdout .. string.gsub(data, "\r\n", "\n")
+ tc.diff = vim.diff(tc.expout, tc.stdout)
self:update_ui()
end
end)
diff --git a/lua/competitest/runner_ui/init.lua b/lua/competitest/runner_ui/init.lua
index f4e1439..8526f04 100644
--- a/lua/competitest/runner_ui/init.lua
+++ b/lua/competitest/runner_ui/init.lua
@@ -29,6 +29,7 @@ function RunnerUI:new(interface, restore_winid)
eo = nil, -- expected output
tc = nil, -- testcases selector
vw = nil, -- viewer popup
+ di = nil, -- diff viewer
},
tcdata = nil, -- table containing testcases data and results
}
@@ -73,6 +74,7 @@ function RunnerUI:show_ui()
se = "Errors", -- standard error
eo = "Expected Output", -- expected output
tc = "Testcases", -- testcases selector
+ di = "Diff", -- Diff
}
for n, w in pairs(self.windows) do
if n ~= "vw" then
@@ -170,6 +172,10 @@ function RunnerUI:show_ui()
for _, map in ipairs(self.runner.config.runner_ui.mappings.view_stderr) do
open_viewer(map, "se")
end
+ -- view diff in a bigger window keymaps
+ for _, map in ipairs(self.runner.config.runner_ui.mappings.view_diff) do
+ open_viewer(map, "di")
+ end
self.windows.tc:on(nui_event.CursorMoved, function()
local tcindex = get_testcase_index_by_line()
@@ -264,6 +270,9 @@ function RunnerUI:show_viewer_popup(window_name)
},
}
+ if window_name == 'di' then
+ viewer_popup_settings.buf_options = {syntax = 'diff'}
+ end
self.windows.vw = require("nui.popup")(viewer_popup_settings)
self.windows.vw:mount()
self.viewer_initialized = true
@@ -361,6 +370,7 @@ function RunnerUI:update_ui()
set_buf_content(self.windows.eo.bufnr, data.expout)
set_buf_content(self.windows.si.bufnr, data.stdin)
set_buf_content(self.windows.se.bufnr, data.stderr)
+ set_buf_content(self.windows.di.bufnr, data.diff)
end
if self.make_viewer_visible then
diff --git a/lua/competitest/runner_ui/popup.lua b/lua/competitest/runner_ui/popup.lua
index 1e42629..db94492 100644
--- a/lua/competitest/runner_ui/popup.lua
+++ b/lua/competitest/runner_ui/popup.lua
@@ -112,6 +112,10 @@ function M.init_ui(windows, config)
popup_settings.position = positions["se"]
windows.se = nui_popup(popup_settings)
+ -- diff popup
+ popup_settings.border.text.top = " Diff "
+ windows.di = nui_popup(popup_settings)
+
windows.so:mount()
windows.eo:mount()
windows.si:mount()
diff --git a/lua/competitest/runner_ui/split.lua b/lua/competitest/runner_ui/split.lua
index af71156..bb0a1da 100644
--- a/lua/competitest/runner_ui/split.lua
+++ b/lua/competitest/runner_ui/split.lua
@@ -32,6 +32,7 @@ function M.init_ui(windows, config, init_winid)
settings.so = vim.deepcopy(split_settings)
settings.se = vim.deepcopy(split_settings)
settings.eo = vim.deepcopy(split_settings)
+ settings.di = vim.deepcopy(split_settings)
---Get first windows in the given layout
---@param layout table: layout description
@@ -111,6 +112,10 @@ function M.init_ui(windows, config, init_winid)
vim.wo[windows[fw].winid]["winfixwidth"] = true
vim.wo[windows[fw].winid]["winfixheight"] = true
+ -- create diff window. but don't mount it
+ settings.di.top = ' Diff '
+ windows.di = nui_split(settings.di)
+
local old_equalalways = vim.o.equalalways
vim.o.equalalways = false
create_layout(current_layout, windows[fw].winid, is_split_vertical)
Awesome! But what about a diff view between Output and Expected output without extra windows like this?
But what about a diff view between Output and Expected output without extra windows like this?
Well we can just open the Output buffer and Expedcted Output buffer in two floats and run :diffthis
on them.
Result:
Patch:
diff --git a/lua/competitest/init.lua b/lua/competitest/init.lua
index a0eebdc..5a0eeaf 100644
--- a/lua/competitest/init.lua
+++ b/lua/competitest/init.lua
@@ -46,6 +46,7 @@ local default_config = {
view_output = { "a", "A" },
view_stdout = { "o", "O" },
view_stderr = { "e", "E" },
+ view_diff = { "D", "<C-d>" },
close = { "q", "Q" },
},
viewer = { -- viewer window, to view in detail a stream (input, expected output, stdout or stderr)
diff --git a/lua/competitest/runner_ui/init.lua b/lua/competitest/runner_ui/init.lua
index f4e1439..e25da2c 100644
--- a/lua/competitest/runner_ui/init.lua
+++ b/lua/competitest/runner_ui/init.lua
@@ -15,6 +15,8 @@ function RunnerUI:new(interface, restore_winid)
ui_initialized = false,
ui_visible = false,
viewer_initialized = false,
+ diff_viewer_initialized = false,
+ diff_viewer_visible = false,
viewer_visible = false,
viewer_content = nil,
restore_winid = restore_winid,
@@ -29,6 +31,8 @@ function RunnerUI:new(interface, restore_winid)
eo = nil, -- expected output
tc = nil, -- testcases selector
vw = nil, -- viewer popup
+ dvwl = nil, -- diff view left
+ dvwr = nil, -- diff view right
},
tcdata = nil, -- table containing testcases data and results
}
@@ -50,11 +54,13 @@ end
function RunnerUI:resize_ui()
local cursor_position = self.ui_visible and api.nvim_win_get_cursor(self.windows.tc.winid) -- restore cursor position later
local was_viewer_visible = self.viewer_visible -- make viewer visible later
+ local was_diff_viewer_visible = self.diff_viewer_visible -- make viewer visible later
self:delete()
if cursor_position then -- if cursor_position isn't nil ui was visible
self:show_ui()
vim.schedule(function()
self.make_viewer_visible = was_viewer_visible -- make update_ui() open viewer after updating details
+ self.make_diff_viewer_visible = was_diff_viewer_visible -- make update_ui() open diff viewer after updating details
api.nvim_win_set_cursor(self.windows.tc.winid, cursor_position)
end)
end
@@ -75,7 +81,7 @@ function RunnerUI:show_ui()
tc = "Testcases", -- testcases selector
}
for n, w in pairs(self.windows) do
- if n ~= "vw" then
+ if n ~= "vw" and n ~= 'dvwr' and n ~= 'dvwl' then
api.nvim_buf_set_var(w.bufnr, "competitest_title", windows_names[n])
api.nvim_buf_set_name(w.bufnr, "CompetiTest" .. string.gsub(windows_names[n], " ", "") .. w.bufnr)
end
@@ -93,6 +99,17 @@ function RunnerUI:show_ui()
self.windows.vw:hide()
api.nvim_set_current_win(self.windows.tc.winid)
self.viewer_visible = false
+ elseif self.diff_viewer_visible then
+ vim.api.nvim_win_call(self.windows.dvwr.winid, function ()
+ vim.cmd('diffoff')
+ end)
+ vim.api.nvim_win_call(self.windows.dvwl.winid, function ()
+ vim.cmd('diffoff')
+ end)
+ self.windows.dvwl:hide()
+ self.windows.dvwr:hide()
+ api.nvim_set_current_win(self.windows.tc.winid)
+ self.diff_viewer_visible = false
else
self:hide_ui()
end
@@ -101,7 +118,7 @@ function RunnerUI:show_ui()
-- close windows keymaps
for _, map in ipairs(self.runner.config.runner_ui.mappings.close) do
for n, w in pairs(self.windows) do
- if n ~= "vw" then
+ if n ~= "vw" and n ~= "dvwr" and n ~= "dvwl" then
w:map("n", map, hide_ui, { noremap = true })
end
end
@@ -170,6 +187,12 @@ function RunnerUI:show_ui()
for _, map in ipairs(self.runner.config.runner_ui.mappings.view_stderr) do
open_viewer(map, "se")
end
+ -- view diff in a bigger window keymaps
+ for _, map in ipairs(self.runner.config.runner_ui.mappings.view_diff) do
+ self.windows.tc:map("n", map, function()
+ self:show_diff_viewer_popup()
+ end, { noremap = true })
+ end
self.windows.tc:on(nui_event.CursorMoved, function()
local tcindex = get_testcase_index_by_line()
@@ -201,6 +224,7 @@ function RunnerUI:hide_ui()
end
self.ui_visible = false
self.viewer_visible = false
+ self.diff_viewer_visible = false
api.nvim_set_current_win(self.restore_winid or 0)
end
end
@@ -216,7 +240,9 @@ function RunnerUI:delete()
self.ui_initialized = false
self.ui_visible = false
self.viewer_initialized = false
+ self.diff_viewer_initialized = false
self.viewer_visible = false
+ self.diff_viewer_visible = false
api.nvim_set_current_win(self.restore_winid or 0)
end
@@ -277,6 +303,63 @@ function RunnerUI:show_viewer_popup(window_name)
api.nvim_set_current_win(self.windows.vw.winid)
end
+---Open diff popup
+function RunnerUI:show_diff_viewer_popup()
+ if not self.diff_viewer_initialized then
+ local vim_width, vim_height = utils.get_ui_size()
+ local diff_viewer_popup_settings = {
+ bufnr = self.windows.so.bufnr,
+ zindex = 55, -- popup ui has zindex 50
+ border = {
+ style = self.runner.config.floating_border,
+ highlight = self.runner.config.floating_border_highlight,
+ text = {
+ top = ' Output ',
+ top_align = "center",
+ },
+ },
+ relative = "editor",
+ size = {
+ width = math.floor(vim_width * self.runner.config.runner_ui.viewer.width/2 + 0.5),
+ height = math.floor(vim_height * self.runner.config.runner_ui.viewer.height + 0.5),
+ },
+ position = {col = ((vim_width - math.floor(vim_width * self.runner.config.runner_ui.viewer.width)) / 2) - 2, row = '50%'},
+ win_options = {
+ number = self.runner.config.runner_ui.viewer.show_nu,
+ relativenumber = self.runner.config.runner_ui.viewer.show_rnu,
+ },
+ }
+
+ self.windows.dvwl = require("nui.popup")(diff_viewer_popup_settings)
+ self.windows.dvwl:mount()
+
+ diff_viewer_popup_settings.bufnr = self.windows.eo.bufnr
+ diff_viewer_popup_settings.border.text.top = ' Expected Output '
+ diff_viewer_popup_settings.position.col = diff_viewer_popup_settings.position.col + math.floor(vim_width * self.runner.config.runner_ui.viewer.width/2 ) + 2
+ self.windows.dvwr = require("nui.popup")(diff_viewer_popup_settings)
+ self.windows.dvwr:mount()
+ self.diff_viewer_initialized = true
+ self.diff_viewer_visible = true
+ vim.api.nvim_win_call(self.windows.dvwr.winid, function ()
+ vim.cmd('diffthis')
+ end)
+ vim.api.nvim_win_call(self.windows.dvwl.winid, function ()
+ vim.cmd('diffthis')
+ end)
+ elseif not self.diff_viewer_visible then
+ self.windows.dvwl:show()
+ self.windows.dvwr:show()
+ self.diff_viewer_visible = true
+ vim.api.nvim_win_call(self.windows.dvwr.winid, function ()
+ vim.cmd('diffthis')
+ end)
+ vim.api.nvim_win_call(self.windows.dvwl.winid, function ()
+ vim.cmd('diffthis')
+ end)
+ end
+ api.nvim_set_current_win(self.windows.dvwl.winid)
+end
+
---Return a string of length len, starting with str.
---If str's length is greater than len, str will be truncated
---Otherwise the remaining space will be filled with a fill char (fchar)
@@ -367,6 +450,10 @@ function RunnerUI:update_ui()
self.make_viewer_visible = nil
self:show_viewer_popup()
end
+ if self.make_diff_viewer_visible then
+ self.make_diff_viewer_visible = nil
+ self:show_diff_viewer_popup()
+ end
end)
end
diff --git a/lua/competitest/runner_ui/popup.lua b/lua/competitest/runner_ui/popup.lua
index 1e42629..907b1a1 100644
--- a/lua/competitest/runner_ui/popup.lua
+++ b/lua/competitest/runner_ui/popup.lua
@@ -122,7 +122,7 @@ end
-- Show popup UI
function M.show_ui(windows)
for n, w in pairs(windows) do
- if n ~= "vw" then -- show ui but not viewer popup
+ if n ~= "vw" and n ~= "dvwl" and n ~= "dvwr" then -- show ui but not viewer popup
w:show()
end
end
I'm not very familiar with the codebase nor am I familiar with the apis of nui. So this probably could have been done better. @xeluxee what do you think about adding this feature to competitest ?
@xeluxee what do you think about adding this feature to competitest ?
I'm definitely interested in adding this feature to CompetiTest, though at the moment I'm working on cloning contests (issue 5).
@shadmansaleh your patch looks interesting, but what do you think about a keybind to toggle diff highlights in Output buffer? So we don't need to open two floating windows to see diff
but what do you think about a keybind to toggle diff highlights in Output buffer? So we don't need to open two floating windows to see diff
We could do that. I opened separate floats because I often find output/expected output windows to be quite tiny. especially when nvim isn't on full screen. I thought it'd be easier to look at if it was to be shown in a larger window.
Though now looking at direct diff on output and expected output windows I don't think it's necessary.
I often find output/expected output windows to be quite tiny
You can enlarge them, give a look at layout customization
You can enlarge them, give a look at layout customization
Ooo didn't know that was possible. Thanks.
I've opened a pr implementing this feature. Do take a look at it as you get time.
Sometimes there is a difference between the actual output and the expected one. So it is a good idea to enable a diff view with a keybinding or make it the default if a wrong answer is encountered.
We can do it with our terminals but the speed is the goal behind the plugin, we need to do it with just a key press.
Examples: