Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

🚀 Modern Task System for Project Building, Testing and Deploying !!

License

NotificationsYou must be signed in to change notification settings

skywind3000/asynctasks.vim

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

asynctasks.vim - modern task system

The generic way to handle building/running/testing/deploying tasks by imitating vscode'stask system.

GitHub licenseMaintenanceJoin the chat at https://gitter.im/skywind3000/asynctasks.vim

This readme is also available in:

Introduction

As vim 8.0 was released in 2017, we have got many wonderful plugins like LSP, DAP, and asynchronous linters. Even things likevimspector, which could only be imagined in emacs in the past, now become a reality in vim.

However, vim still lack an elegant task system to speed up your inner software development cycle (edit, compile, test). Many people are still dealing with those building, testing, and deploying tasks in such a primitive or flaky way. Therefore, I decided to create this plugin and introduce vscode's task-like mechanisms to vim.

Vscode creates a.vscode folder in your project root directory and uses a.vscode/tasks.json file to define project-specific tasks. Similar,asynctasks.vim uses a.tasks file in your project folders for local tasks and uses~/.vim/tasks.ini to define global tasks for generic projects.

This is very simple, but most good designs always start from simple concepts. You will benefit a lot from the productivity and possibility of this task system.

Get Started

Installation

Install withvim-plug:

Plug'skywind3000/asynctasks.vim'Plug'skywind3000/asyncrun.vim'

It requiresasyncrun.vim 2.4.0 or above. Don't forget to setup:

letg:asyncrun_open=6

And quickfix window can be opened automatically, otherwise you can't see the task output unless using:copen manually.

Build and run a single file

It's convenient for me to build and run a single file directly without creating a new project for that if I want to try some small and new ideas. In this circumstance, we can use:AsyncTaskEdit command to edit the.tasks configuration file in your current project root directory:

[file-build]# macros in the "$(...)" form will be substituted,# shell command, use quotation for filenames containing spacescommand=gcc -O2"$(VIM_FILEPATH)" -o"$(VIM_FILEDIR)/$(VIM_FILENOEXT)"# working directorycwd=$(VIM_FILEDIR)[file-run]command="$(VIM_FILEDIR)/$(VIM_FILENOEXT)"cwd=$(VIM_FILEDIR)# output mode: run in a terminaloutput=terminal

There are two tasksfile-build andfile-run defined in this.tasks file. Then from the directory where this.tasks reside and its child directories, you can use:

:AsyncTaskfile-build:AsyncTaskfile-run

To build and run the current file:

This is the result of:AsyncTask file-build, the command output displays in the quickfix window and errors are matched witherrorformat. You can navigate the command output in the quickfix window or use:cnext/:cprev to jump between errors.

There are many macros can be used in the command field and will be expanded and replaced when task starts. Having a fast, low-friction Edit/Build/Test cycle is one of the best and easiest ways to increase developer productivity, so we will map them to F5 and F9:

noremap<silent><f5> :AsyncTask file-run<cr>noremap<silent><f9> :AsyncTask file-build<cr>

Put the code above in yourvimrc and you can have F9 to compile current file and F5 to run it. And you may ask, this is for C/C++ only, what if you want to run a python script, should you create a new taskfile-run-python ? Totally unnecessary, commands can match with file types:

[file-run]command="$(VIM_FILEPATH)"command:c,cpp="$(VIM_PATHNOEXT)"command:go="$(VIM_PATHNOEXT)"command:python=python"$(VIM_FILENAME)"command:javascript=node"$(VIM_FILENAME)"command:sh=sh"$(VIM_FILENAME)"command:lua=lua"$(VIM_FILENAME)"command:perl=perl"$(VIM_FILENAME)"command:ruby=ruby"$(VIM_FILENAME)"output=terminalcwd=$(VIM_FILEDIR)save=2

Thecommand followed by a colon accepts file type list separated by comma. If the current file type cannot be matched, the default command will be used. The-save=2 represents to save all modified buffers before running the task.

At this point, you can have yourF5 to run all type of files. And plugins like quickrun can be obsoleted immediately, they can't do better than this. Then we continue to polishfile-build to support more file types:

[file-build]command:c,cpp=gcc -O2 -Wall"$(VIM_FILEPATH)" -o"$(VIM_PATHNOEXT)" -lstdc++ -lm -msse3command:go=go build -o"$(VIM_PATHNOEXT)""$(VIM_FILEPATH)"command:make=make -f"$(VIM_FILEPATH)"output=quickfixcwd=$(VIM_FILEDIR)save=2

Again, F9 can be used to compile many file types, same keybind, different command. This two tasks can be defined in local.tasks and work for the project scope or in the~/.vim/tasks.ini and work for all project. Much more elegant than using the old&makeprg or callingasyncrun/neomake with a lotif/else in yourvimrc.

Tasks for running compilers or grep may setoutput=quickfix (default), because the output can use errorformat to match errors in the quickfix window, while tasks for running your file/project may setoutput=terminal.

When you setoutput toterminal, you can further specify what type of terminal do you want to use exactly, like: a simulated terminal in quickfix window (without matching the errorformat)? the triditional! command in vim? the internal terminal ? an external terminal window ? or in a tmux split window ?? The detail will be discussed later.

Build and run a project

If you want to do something with a project, you must figure out where the project locates.asynctasks.vim and its backendasyncrun.vim choose a widely used method calledroot markers to indentify the project root directory. The project root is one of the nearest parent directory containing these markers:

letg:asyncrun_rootmarks= ['.git','.svn','.root','.project','.hg']

If none of the parent directories contains these root markers, the directory of the current file is used as the project root.

There is a corner case: if current buffer is not a normal file buffer (eg. a tool window) or is an unnamed new buffer, vim's current working directory (which:pwd returns) will be used as the project root.

Once we got the project location, the macro$(VIM_ROOT), or its alias<root>, can be used to represent the project root:

What if your current project is not in anygit/subversion repository ? How to find out where is my project root ? The solution is very simple, just put an empty.root file in your project root, it has been defined ing:asyncrun_rootmarks before.

Tasks related to projects can be defined by using this:

[project-build]command=make# set the working directory to the project root.cwd=$(VIM_ROOT)[project-run]command=make run# <root> is an alias to `$(VIM_ROOT)`, a little easier to type.cwd=<root>output=terminal

We assign F6 and F7 for them:

noremap<silent><f6> :AsyncTask project-run<cr>noremap<silent><f7> :AsyncTask project-build<cr>

Now, F7 can be used to build your project and F6 can be used run your project. You may ask again, this is forgnu-make only, but there are a lot of build tools like cmake, ninja and bazel, should you define new tasks asproject-build-cmake orproject-build-ninja and assign different keymaps for them ?

Task priority

No, you don't have to. The easiest way is to put previousproject-build andproject-run in your~/.vim/tasks.ini as the default and global tasks, you can use them directly for generic projects usingmake.

For other type of projects, for example, I am usingmsbuild in my projectA. And I can define a newproject-build task in the local.tasks file residing in projectA:

[project-build]command=vcvars32 > nul && msbuild build/StreamNet.vcxproj /property:Configuration=Debug /nologo /verbosity:quietcwd=<root>errorformat=%f(%l):%m[project-run]command=build/Debug/StreamNet.execwd=<root>output=terminal

The.tasks configuration file are read top to bottom and the most recent tasks found take precedence. and local tasks always have higher priority than the global tasks.

Task defined in.tasks will always override the task with the same name in~/.vim/tasks.ini. So, in projectA, our two old friendsproject-build andproject-run have been replaced with the local methods.

Firstly, the newproject-build task will callvcvars32.bat to setup environment variables, then, use a&& to concatenatemsbuild command.errorformat is initialized to%f(%l):%m for matchingVisual C++ errors in this task.

We can still useF7 to build this project andF6 to run it. We don't have to change our habit if we are working in a different type of project. Unified workflow can be used in different type of projects. This is the power of local/global tasks combination.

Bonus: use:AsyncTaskEdit to edit local tasks, and:AsyncTaskEdit! to edit global tasks.

Query available tasks

What tasks do you have in current project ? Where are they defined ? Has one global task been overrided by a local one ? We use:AsyncTaskList command to answer these questions:

It will display task name, command and where it has been defined.

Bonus: tasks starting with a dot "." will be hidden (eg. ".file-test-temp1"), use:AsyncTaskList! to see them all.

Macro variable substitution

asynctasks.vim supports macro variable substitution incommand andcwd fileds, available macros are:

$(VIM_FILEPATH)# File name of current buffer with full path.$(VIM_FILENAME)# File name of current buffer without path.$(VIM_FILEDIR)# Full path of current buffer without the file name.$(VIM_FILEEXT)# File extension of current buffer.$(VIM_FILETYPE)# File type (value of &ft in vim)$(VIM_FILENOEXT)# File name of current buffer without path and extension.$(VIM_PATHNOEXT)# Current file name with full path but without extension.$(VIM_CWD)# Current directory (which :pwd returns).$(VIM_RELDIR)# File path relativize to current directory.$(VIM_RELNAME)# File name relativize to current directory.$(VIM_ROOT)# Project root directory.$(VIM_CWORD)# Word under cursor.$(VIM_CFILE)# File name under cursor.$(VIM_CLINE)# Cursor line number in current buffer$(VIM_GUI)# has('gui_runnin')?$(VIM_VERSION)# Value of v:version.$(VIM_COLUMNS)# Current screen width.$(VIM_LINES)# Current screen height.$(VIM_SVRNAME)# Value of v:servername.$(VIM_PRONAME)# Name of current project root directory$(VIM_DIRNAME)# Name of current directory$(VIM_INIFILE)# Full path name of current ini (.tasks) file.$(VIM_INIHOME)# Where the ini file locates.

They will be expanded and replaced in thecommand andcwd fields. System environment variables with same names are also initialized as the same value. If one of your task has many complex shell commands, you can put the commands in a shell script and execute it in the task:

[project-build]command=build/my-build-task.shcwd=<root>

In this case, you don't have to pass any argument tomy-build-task.sh, because the shell script can read environment variable$VIM_FILENAME to access current file name. By utilizing system environment variables with external script file, you can describe many complex tasks in your project. And of course, much more powerful than defining some keymaps for! command in yourvimrc.

There is a:AsyncTaskMacro command for you to display macro help:

From left to right, is the macro name, what does it stand for and current value. You don't have to check the documentation when you are editing your task configuration.

Task running modes

There is anoutput field in each task's configuration, it can be one of:

  • quickfix: default mode, output to the quickfix window and match witherrorformat.
  • terminal: run in a terminal.

Nothing to talk aboutoutput=quickfix, and if you setoutput toterminal your can further specify the terminal type by setting:

letg:asynctasks_term_pos='xxx'

to specify what terminal do you want to use, available options are:

NameTypeDescription
quickfixsimulationDefault, simulate a terminal in quickfix window (output will not match the errorformat)
vim-Use the old! command to run your task, some people still like it
tabinternal terminalopen a new internal terminal in a new tab
TABinternal terminalsimilar totab but open in the left side (easy to return to the previous tab)
topinternal terminalopen a reusable internal terminal above current window
bottominternal terminalopen a reusable internal terminal under current window
leftinternal terminalopen a reusable internal terminal on the left
rightinternal terminalopen a reusable internal terminal on the right
externalexternal terminaluse a new system terminal to run your task
hidehiddenrun in the background

You can set apos field in a task to override globalg:asynctasks_term_pos value in the given task.

Almost all the possible methods are here, choose your favorite one.

Whenoutput isterminal, and if you set:

letg:asynctasks_term_pos='bottom'

Command:AsyncTask file-run will open an internal terminal under your current window:

If the previous terminal session has finished, the window will be resused. When you setg:asynctasks_term_pos to one oftop,bottom,left andright, these two options below can allow you change the terminal size:

letg:asynctasks_term_rows=10" set height for the horizontal terminal splitletg:asynctasks_term_cols=80" set width for vertical terminal split

If a terminal split window is too small for you, you can setup:

letg:asynctasks_term_pos='tab'

A whole tab can be used to display the internal terminal:

Almost all the vim screen are occupied, is it big enough to fit your need ? This is my most favorite one.

The defaultquickfix can also be used to run your task, but it is not capable to handle user input, and if your program will interact with user, you may choose a real terminal.

Bonus:

  • tab terminal can also be reusable if you setg:asynctasks_term_reuse to1.
  • you can prevent focus changing if you setg:asynctasks_term_focus to0 (split terminals only).

(When you are using internal terminal,asynctasks.vim encourage you to setupALT+HJKL to jump around windows andALT+q to exit to terminal normal mode).

Run in an external terminal

Many desktop developer using Visual Studio on Windows prefer to run their programs in a new cmd window, we can useexternal for this:

letg:asynctasks_term_pos='external'

Then, every task withoutput=terminal will open a newcmd window:

Familiar feeling like you are working in Visual Studio.

asynctasks.vim provide you all the possible ways to run a command in vim with no compromise. Choose one you like.

Extra runners

Powered by AsyncRun'scustomizable runners, tasks can be executed in any way you want. Here is a list of pre-included runners:

RunnerDescriptionRequirementLink
gnomerun in a new gnome terminalGNOMEgnome.vim
gnome_tabrun in a new gnome terminal tabGNOMEgnome_tab.vim
xtermrun in a xterm windowxtermxterm.vim
tmuxrun in a separated tmux paneVimuxtmux.vim
floatermrun in a new floaterm windowfloatermfloaterm.vim
floaterm_reuserun in a reusable floaterm windowfloatermfloaterm_reuse.vim
quickuirun in a quickui windowvim-quickuiquickui.vim
toggletermrun in a toggleterm windowtoggleterm.nvimtoggleterm.vim
termhelprun in terminal helpvim-terminal-helptermhelp.vim
xfcerun in a new xfce terminalxfce4-terminalxfce.vim
konsolerun in a new konsole terminalKDEkonsole.vim
macosrun in a macOS system terminalmacosmacos.vim
itermrun in a new iterm2 tabmacos + iTerm2iterm.vim

When a runner is defined for AsyncRun, it can be used by providing apos option:

[file-run]command=python"$(VIM_FILEPATH)"cwd=$(VIM_FILEDIR)output=terminalpos=gnome

Then use:

:AsyncTaskfile-run

The task will be executed in thegnome-terminal:

If you have many tasks need thispos option, no need to specify them one-by-one, the global settings may be helpful:

letg:asynctasks_term_pos='gnome'

After that, every task withoutput=terminal option could be executed in thegnome-terminal.

Remember, theoutput option must beterminal and the local optionpos has higher priority and can override global optiong:asynctasks_term_pos.

It is quite easy to create a new runner, see thecustomize-runner.

Advanced Topics

Continue hacking inasynctasks.vim:

Ask for user input

Some tasks, eg finding strings in current project, may need to ask user to input some keywords before start.

Ifcommand field contains macros in the$(-...) pattern:

[task1]command=echo hello $(-name), you are a $(-gender).output=terminal

When you start the task by:

:AsyncTask task1

You are required to input the values of$(-name) and$(-gender) in the prompt area:

There are two variable you need to provide, input them one by one, pressESC to give up andENTER to confirm. The task will start when you finished:

As you see,$(-name) has been substituted with the value you just provided.

Input value can also be provided as command arguments ofAsyncTask {name}:

:AsyncTask task1 -name=Batman -gender=boy

If the value is present in the arguments, AsyncTask will not ask you repeatly.

Hint: use$(-prompt:default) to provide a default value,$(-prompt:) to remember input history. and$(-gender:&male,&female) to provide multiple choices.

Real example used by myself:

[grep]command=rg -n --no-heading --color never"$(-word)""<root>" -tcpp -tc -tpy -tvim -tgo -tasmcwd=$(VIM_ROOT)errorformat=%f:%l:%m

Here is my globalgrep task. Each time I use:AsyncTask grep in any of my project, it prompts me to inputword before searching, I can use<C-r><C-w> to pickup word under cursor or input something new.

The value ofword can also be provided in the arguments:

:AsyncTaskgrep -word=hello

If I need other filetypes to search in certain project, I can redifine a newgrep with different parameters for this project.

But most of time, a globalgrep task is enough, rg supports.ignore files for different files, I can use them to prevent searching in unnecessary files. Check rg documentation for--ignore-file.

Internal variables

Internal variables can be used in many ways, e.g., to manage multiple building targets. They are defined in the[+] section of.tasks files:

[+]build_target=build_x86test_target=test_x86[project-build]command=make $(+build_target)cwd=<root>[project-test]command=make $(+test_target)cwd=<root>

Patterns which match$(+var_name) in thecommand field will be substituted with the corresponding value defined in the[+] section.

Which means, the new command in "project-build" will become:

make build_x86

It is a efficient way to switch current building target by changing the variable values in the[+] section without modifying thecommand option every time.

Internal variables can be provided in the argument list as+varname=value:

:AsyncTask project-test+test_target=mytest

Default values can be defined as$(+varname:default) form, it will be used if variables are absent in both[+] section and:AsyncTask xxx arguments.

[project-test]command=make $(+test_target:testall)cwd=<root>

The global dictionaryg:asynctasks_environ is the third way to define a variable, it's a convenient place for vimscript:

let g:asynctasks_environ = {'foo': '100', 'bar': '200' }

Same variable can be defined in the different places, priorities are:

  • Low priority: global[+] section.
  • Normal priority: local[+] section.
  • High priority: vimscript objectg:asynctasks_environ.
  • The highest priority:+varname=value arguments of:AsyncTask command.

The one with higher priority will overshadow the lower one. By utilizing this feature, we can simplify most similar tasks.

e.g. we have two tasksfile-build andproject-find in the global config~/.vim/tasks.ini:

[file-build]command=gcc -O2 -Wall"$(VIM_FILEPATH)" -o"$(VIM_PATHNOEXT)" $(+cflags:)cwd=$(VIM_FILEDIR)[project-find]command=rg -n --no-heading --color never"$(-word)""<root>" $(+findargs:)cwd=$(VIM_ROOT)errorformat=%f:%l:%m

Both of them have introduced a variable with a default value of empty string. Sometimes, we don't need to redefine the tasks, just init the two variables in the local.tasks:

[+]clags=-g -gproffindargs=-tcpp

It's more flexable if we have the same local tasks with similar arguments.

Task with different profiles

One task can have many differentprofiles:

[task1:release]command=gcc -O2"$(VIM_FILEPATH)" -o"$(VIM_PATHNOEXT)"cwd=$(VIM_FILEDIR)[task1:debug]command=gcc -g"$(VIM_FILEPATH)" -o"$(VIM_PATHNOEXT)"cwd=$(VIM_FILEDIR)

Here we havetask1 with two different profilesrelease anddebug. The default profile isdebug, change it torelease by:

:AsyncTaskProfile release

or

letg:asynctasks_profile='release'

Then,:AsyncTask task1 will runtasks1 with profilerelease.

Bonus: When usingAsyncTaskProfile command with more than one arguments:

:AsyncTaskProfiledebug release

A dialog will popup to allow you pick betweendebug andrelease, and previous selected item is remembered.

Different system with different commands

This plugin can select command for given system:

[task1]command=echo defaultcommand/win32=echo win32 defaultcommand/linux=echo linux defaultcommand:c,cpp/win32=echo c/c++ for win32command:c,cpp/linux=echo c/c++ for linux

Bothfiletype andsystem can be used as filter. Default command (the first one) will be chosen if mismatch.

Change this option to specify your system:

letg:asynctasks_system='macos'

Then command ending with/macos will be selected.

Data source for fuzzy finders

A fuzzy finder can help you pick a task easily:

This plugin have some apis to fetch task information, which makes integration very easy:

let current_tasks=asynctasks#list("")

It returns a list of items, each item represents a task. And it can be used as the data source for fuzzy finders likefzf.vim orLeaderf.

Here is aninstruction to integrate withfzf,leaderf,coc-list andfzf-lua.

Extensions

Existing UI extensions for fuzzy-finders:

ExtensionAuthorDescription
fzf-lua-asynctasksYaroslav Mazurykfzf-lua integration
telescope-asynctasks.nvimGustavo Sampaiotelescope integration
coc-tasksvoldiksscoc integration

Run last task

There is a command to run last task without typing its name again:

:AsyncTaskLast

Can be binded to a hotkey for repeatedly running task.

Options

Theg:asynctasks_config_name option

Don't like the.tasks file name ? Rename it as you want:

letg:asynctasks_config_name='.asynctask'letg:asynctasks_config_name='.git/tasks.ini'

When you get multiple local configurations to load, a comma separated list (or just a list) can also be accepted:

letg:asynctasks_config_name='.tasks,.git/tasks.ini,.svn/tasks.ini'letg:asynctasks_config_name= ['.tasks','.git/tasks.ini','.svn/tasks.ini']
Theg:asynctasks_rtp_config option

Don't like the globaltasks.ini file name in your~/.vim ? Change it to:

letg:asynctasks_rtp_config="asynctasks.ini"
Theg:asynctasks_extra_config option

A list of additional global task configuration files, you can indicate other global configurations:

letg:asynctasks_extra_config= [\'~/github/my_dotfiles/my_tasks.ini',\'~/.config/tasks/local_tasks.ini',\]

Then, these two additional globla configurations will be loaded after reading~/.vim/tasks.ini.

Theg:asynctasks_term_pos option

What terminal do you want to run your task. seeTask running modes.

Theg:asynctasks_term_cols option

Internal terminal width when using vertical split.

Theg:asynctasks_term_rows option

Internal terminal height when using horizontal split.

Theg:asynctasks_term_focus option

Set to zero to keep focus when using an internal terminal in a new split.

Theg:asynctasks_term_reuse option

Set to1 to reuse internal terminal when open it in a new tab.

Theg:asynctasks_term_hidden option

If it is set to1, the internal terminal buffers will setbufhidden tohide.

Theg:asynctasks_term_listed option

Set to zero to hide terminal buffer from buffer list (set nolisted).

Theg:asynctasks_term_close option

Set to1 to close the terminal window when task finished.

Theg:asynctasks_confirm option

Set to zero to skip filename confirmation in:AsyncTaskEdit.

Theg:asynctasks_filetype option

The filetype of the task configuration file, default to "taskini".

Theg:asynctasks_template option

Command:AsyncTaskEdit accept a template file name, the content of template will be used if you are creating a new task config file:

letg:asynctask_template='~/.vim/task_template.ini'

And templates can be defined in your~/.vim/task_template.ini like:

{cmake}[project-init]command=mkdir build && cd build && cmake ..cwd=<root>[project-build]command=cmake --build buildcwd=<root>errorformat=%. %#--> %f:%l:%c[project-run]command=build/$(VIM_PRONAME)cwd=<root>output=terminal{cargo}[project-init]command=cargo updatecwd=<root>[project-build]command=cargo buildcwd=<root>[project-run]command=cargo runcwd=<root>output=terminal

Command:

:AsyncTaskEdit cargo

Will create a new file with the template "cargo", if the file doesn't exist.

Specification

A full configuration specification can be found here:

And there are many examples about: cmake, grep, ninja, wsl and more:

Command Line Tool

This plugin provides you anasynctask.py script (in thebin folder) when you want to run tasks right in your shell:

# run tasks in any child directory of your project# no need to go back to project root directory, because "cwd=<root>".$ asynctask project-build# compile a file$ asynctask file-build hello.c# run a file$ asynctask file-run hello.c

Usefzf to select task:

For more information, please visit:

Frequently Asked Questions

See the:

Credit

Likeasynctasks.vim ? Star this repository onGitHub, this is really helpful. And if you're feeling especially charitable, follow skywind3000 onTwitter andGitHub.

Releases

No releases published

Packages

No packages published

Contributors7

Languages


[8]ページ先頭

©2009-2025 Movatter.jp