competitest.nvim
is a testcase manager and checker. It lets you save time in competitive programming contests by automating common tasks related to testcase management. It can compile, run and test your solutions across all the available testcases, displaying results in a nice interactive user interface.
:CompetiTestAdd
:CompetiTestEdit
:CompetiTestDelete
:CompetiTestRun
, showing results and execution data in a nice interactive window:CompetiTestReceive
NOTE: this plugins requires Neovim ≥ 0.5
Install with vim-plug
:
Plug 'MunifTanjim/nui.nvim' " it's a dependency
Plug 'xeluxee/competitest.nvim'
Install with packer.nvim
:
use {
'xeluxee/competitest.nvim',
requires = 'MunifTanjim/nui.nvim',
config = function() require'competitest'.setup() end
}
If you are using another package manager note that this plugin depends on nui.nvim
, hence it should be installed as a dependency.
To load this plugin call setup()
:
require('competitest').setup() -- to use default configuration
require('competitest').setup { -- to customize settings
-- put here configuration
}
To see all the available settings see configuration.
stdin
and print to stdout
. If stderr
is used its content will be displayedtestcases_use_single_file
boolean option in in configuration. By default it's false, so multiple files are usedtestcases_use_single_file
to falsetask-A.cpp
. If using the default configuration testcases associated with that file will be named task-A_input0.txt
, task-A_output0.txt
, task-A_input1.txt
, task-A_output1.txt
and so on. The counting starts from 0testcases_files_format
in configurationtestcases_directory
in configuration).testcases_use_single_file
to truetask-A.cpp
. If using the default configuration testcases file will be named task-A.testcases
.
Of course single file naming can be configured: see testcases_single_file_format
in configurationtestcases_directory
in configuration).Anyway you can forget about these rules if you use :CompetiTestAdd
and :CompetiTestEdit
, that handle these things for you.
When launching the following commands make sure the focused buffer is the one containing the source code file.
Launch :CompetiTestAdd
to add a new testcase.
Launch :CompetiTestEdit
to edit an existing testcase. If you want to specify testcase number directly in the command line you can use :CompetiTestEdit x
, where x
is a number representing the testcase you want to edit.
To jump between input and output windows press either <C-h>
, <C-l>
, or <C-i>
. To save and close testcase editor press <C-s>
.
Of course these keybindings can be customized: see editor_ui
➤ normal_mode_mappings
and editor_ui
➤ insert_mode_mappings
in configuration
Launch :CompetiTestDelete
. If you want to specify testcase number directly in the command line you can use :CompetiTestDelete x
, where x
is a number representing the testcase you want to remove.
Testcases can be stored in multiple text files or in a single msgpack encoded file.
Launch :CompetiTestConvert
to change testcases storage method: you can convert a single file into multiple files or vice versa.
One of the following arguments is needed:
singlefile_to_files
: convert a single file into multiple text filesfiles_to_singlefile
: convert multiple text files into a single fileauto
: if there's a single file convert it into multiple files, otherwise convert multiple files into a single fileNOTE: this command only converts already existing testcases files without changing CompetiTest configuration. To choose the storage method to use you have to configure testcases_use_single_file
option, that is false by default.
Launch :CompetiTestRun
. CompetiTest's interface will appear and you'll be able to view details about a testcase by moving the cursor over its entry. You can close the UI by pressing q
or Q
.
If you're using a compiled language and you don't want to recompile your program launch :CompetiTestRunNC
, where "NC" means "No Compile".
If you have previously closed the UI and you want to re-open it without re-executing testcases or recompiling launch :CompetiTestRunNE
, where "NE" means "No Execute".
R
<C-r>
K
<C-k>
i
or I
a
or A
o
or O
e
or E
Of course all these keybindings can be customized: see runner_ui
➤ mappings
in configuration
NOTE: to get this feature working you need to install competitive-companion extension in your browser.
Launch :CompetiTestReceive
to fetch testcases from competitive programming platforms. After launching this command click on the green plus button in your browser to download testcases: they will be received and stored automatically by CompetiTest.
For further customization see companion_port
and receive_print_message
in configuration
Here you can find CompetiTest default configuration
require('competitest').setup {
local_config_file_name = ".competitest.lua",
floating_border = "rounded",
floating_border_highlight = "FloatBorder",
picker_ui = {
width = 0.2,
height = 0.3,
mappings = {
focus_next = { "j", "<down>", "<Tab>" },
focus_prev = { "k", "<up>", "<S-Tab>" },
close = { "<esc>", "<C-c>", "q", "Q" },
submit = { "<cr>" },
},
},
editor_ui = {
popup_width = 0.4,
popup_height = 0.6,
show_nu = true,
show_rnu = false,
normal_mode_mappings = {
switch_window = { "<C-h>", "<C-l>", "<C-i>" },
save_and_close = "<C-s>",
cancel = { "q", "Q" },
},
insert_mode_mappings = {
switch_window = { "<C-h>", "<C-l>", "<C-i>" },
save_and_close = "<C-s>",
cancel = "<C-q>",
},
},
runner_ui = {
total_width = 0.8,
total_height = 0.8,
selector_width = 0.3,
selector_show_nu = false,
selector_show_rnu = false,
show_nu = true,
show_rnu = false,
mappings = {
run_again = "R",
run_all_again = "<C-r>",
kill = "K",
kill_all = "<C-k>",
view_input = { "i", "I" },
view_output = { "a", "A" },
view_stdout = { "o", "O" },
view_stderr = { "e", "E" },
close = { "q", "Q" },
},
viewer = {
width = 0.5,
height = 0.5,
show_nu = true,
show_rnu = false,
close_mappings = { "q", "Q" },
},
},
save_current_file = true,
save_all_files = false,
compile_directory = ".",
compile_command = {
c = { exec = "gcc", args = { "-Wall", "$(FNAME)", "-o", "$(FNOEXT)" } },
cpp = { exec = "g++", args = { "-Wall", "$(FNAME)", "-o", "$(FNOEXT)" } },
rust = { exec = "rustc", args = { "$(FNAME)" } },
java = { exec = "javac", args = { "$(FNAME)" } },
},
running_directory = ".",
run_command = {
c = { exec = "./$(FNOEXT)" },
cpp = { exec = "./$(FNOEXT)" },
rust = { exec = "./$(FNOEXT)" },
python = { exec = "python", args = { "$(FNAME)" } },
java = { exec = "java", args = { "$(FNOEXT)" } },
},
multiple_testing = -1,
maximum_time = 5000,
output_compare_method = "squish",
testcases_directory = ".",
input_name = "input",
output_name = "output",
testcases_files_format = "$(FNOEXT)_$(INOUT)$(TCNUM).txt",
testcases_use_single_file = false,
testcases_single_file_format = "$(FNOEXT).testcases",
companion_port = 27121,
receive_print_message = true,
}
local_config_file_name
: you can use a different configuration for every different folder. See local configuration
floating_border
: for details see here
floating_border_highlight
: the highlight group used for popups border
picker_ui
: settings related to the testcase picker
width
: a value from 0 to 1, representing the ratio between picker width and Neovim widthheight
: a value from 0 to 1, representing the ratio between picker height and Neovim heightmappings
: keyboard mappings to interact with pickereditor_ui
: settings related to the testcase editor
popup_width
: a value from 0 to 1, representing the ratio between editor width and Neovim widthpopup_height
: a value from 0 to 1, representing the ratio between editor height and Neovim heightshow_nu
: whether to show line numbers or notshow_rnu
: whether to show relative line numbers or notswitch_window
: keyboard mappings to switch between input window and output windowsave_and_close
: keyboard mappings to save testcase contentcancel
: keyboard mappings to quit testcase editor without savingrunner_ui
: settings related to testcase runner user interface
total_width
: a value from 0 to 1, representing the ratio between total runner width and Neovim widthtotal_height
: a value from 0 to 1, representing the ratio between total runner height and Neovim heightselector_width
: a value from 0 to 1, representing the ratio between testcase selector popup width and the total widthselector_show_nu
: whether to show line numbers or not in testcase selectorselector_show_rnu
: whether to show relative line numbers or not in testcase selectorshow_nu
: whether to show line numbers or not in details popupsshow_rnu
: whether to show relative line numbers or not in details popupsmappings
: keyboard mappings used in testcase selector popuprun_again
: keymaps to run again a testcaserun_all_again
: keymaps to run again all testcaseskill
: keymaps to kill a testcasekill_all
: keymaps to kill all testcasesview_input
: keymaps to view input (stdin) in a bigger windowview_output
: keymaps to view expected output in a bigger windowview_stdout
: keymaps to view programs's output (stdout) in a bigger windowview_stderr
: keymaps to view programs's errors (stderr) in a bigger windowclose
: keymaps to close runner user interfaceviewer
: keyboard mappings used in viewer windowwidth
: a value from 0 to 1, representing the ratio between viewer window width and Neovim widthheight
: a value from 0 to 1, representing the ratio between viewer window height and Neovim heightshow_nu
: whether to show line numbers or not in viewer windowshow_rnu
: whether to show relative line numbers or not in viewer windowclose_mappings
: keymaps to close viewer windowsave_current_file
: if true save current file before running testcases
save_all_files
: if true save all the opened files before running testcases
compile_directory
: execution directory of compiler, relatively to current file's path
compile_command
: configure the command used to compile code for every different language, see here
running_directory
: execution directory of your solutions, relatively to current file's path
run_command
: configure the command used to run your solutions for every different language, see here
multiple_testing
: how many testcases to run at the same time
-1
if you want to run as many testcases as the number of available CPU cores at the same time0
if you want to run all the testcases togethermaximum_time
: maximum time, in milliseconds, given to processes. If it's exceeded process will be killed
testcases_directory
: where testcases files are located, relatively to current file's path
input_name
: the string substituted to $(INOUT)
(see modifiers), used to name input files
output_name
: the string substituted to $(INOUT)
(see modifiers), used to name output files
testcases_files_format
: string representing how testcases files should be named (see modifiers)
testcases_use_single_file
: if true testcases will be stored in a single file instead of using multiple text files. If you want to change the way already existing testcases are stored see conversion
testcases_single_file_format
: string representing how single testcases files should be named (see modifiers)
output_compare_method
: how given output (stdout) and expected output should be compared. It can be a string, representing the method to use, or a custom function. Available options follows:
"exact"
: character by character comparison"squish"
: compare stripping extra white spaces and newlinesrequire('competitest').setup {
output_compare_method = function(output, expected_output)
if output == expected_output then
return true
else
return false
end
end
}
companion_port
: competitive companion port number
receive_print_message
: if true notify user that plugin is ready to receive testcases or that testcases have just been received
You can use a different configuration for every different folder by creating a file called .competitest.lua
(this name can be changed configuring the option local_config_file_name
). It will affect every file contained in that folder and in subfolders. A table containing valid options must be returned, see the following example.
-- .competitest.lua content
return {
multiple_testing = 3,
maximum_time = 2500,
input_name = 'in',
output_name = 'ans',
testcases_files_format = "$(INOUT)$(TCNUM).txt",
}
Modifiers are strings that will be replaced by something else. You can use them to define commands or to customize testcases_files_format
.
Modifier | Meaning |
---|---|
$() |
insert a dollar |
$(HOME) |
user home directory |
$(FNAME) |
file name |
$(FNOEXT) |
file name without extension |
$(FEXT) |
file extension |
$(FTYPE) |
file type |
$(FABSPATH) |
absolute path of current file |
$(FRELPATH) |
file path, relative to Neovim's current working directory |
$(ABSDIR) |
absolute path of folder that contains file |
$(RELDIR) |
path of folder that contains file, relative to Neovim's current working directory |
$(TCNUM) |
testcase number |
$(INOUT) |
it's substituted with input_name or output_name (see configuration), to distinguish testcases input files from testcases output files |
Languages as C, C++, Rust, Java and Python are supported by default.
Of course you can customize commands used for compiling and for running your programs. You can also add languages that aren't supported by default.
require('competitest').setup {
compile_command = {
cpp = { exec = 'g++', args = {'$(FNAME)', '-o', '$(FNOEXT)'} },
some_lang = { exec = 'some_compiler', args = {'$(FNAME)'} },
},
run_command = {
cpp = { exec = './$(FNOEXT)' },
some_lang = { exec = 'some_interpreter', args = {'$(FNAME)'} },
},
}
See available modifiers to understand better how dollar notation works.
NOTE: if your language isn't compiled you can ignore compile_command
section.
Feel free to open a PR or an issue if you think it's worth adding a new language among default ones.
You can customize CompetiTest highlight groups. Their default values are:
hi CompetiTestRunning cterm=bold gui=bold
hi CompetiTestDone cterm=none gui=none
hi CompetiTestCorrect ctermfg=green guifg=#00ff00
hi CompetiTestWarning ctermfg=yellow guifg=orange
hi CompetiTestWrong ctermfg=red guifg=#ff0000
If you have any suggestion to give or if you encounter any trouble don't hesitate to open a new issue.
Pull Requests are welcome! 🎉
GNU Lesser General Public License version 3 (LGPL v3) or, at your option, any later version
Copyright © 2021, 2022 xeluxee
CompetiTest.nvim is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
CompetiTest.nvim is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License along with CompetiTest.nvim. If not, see https://www.gnu.org/licenses/.