2

I'm using Processing. I've created a Python script that enables me to compile a sketch from the directory that the processing file is in.

I'm currently running as follows

:!./make.py 

This works, but when It's running the Vim window (that I was in) is taken over by the output of the programme. I think that It would be nicer if I could see the code whilst the programme is running.

I think this means that I want to run the script as a background process? I have tried

:!./make.py & 

But that didn't work. But then again that doesn't work if I do it directly from the shell either, so I'm not sure why it should really work from within Vim...

Is there a way that I could do this?

Ideally I would be able to map whatever it is to

Thanks

  • 1
    You could try setting makeprg and using Vim's :make command. See vimhelp.appspot.com/quickfix.txt.html#%3Amake_makeprg.. Hmm. No, that doesn't stop the output either. – muru Mar 21 '15 at 18:11
  • Thanks I've had a look at that, It makes things a bit simpler than having to type :!./make.py, but not much really. It would be nice to have it set to something such as <F2> (or whatever), and so that I could still see the code whilst it was running the programme (not sure if that's possible or not). Thanks – baxx Mar 21 '15 at 18:39
  • I tried :silent make, but that seemed to screw up rendering for some reason. By the way, a mapping shouldn't be difficult: nnoremap <F2> :silent make<cr>. – muru Mar 21 '15 at 18:41
  • yeah I've done the mapping, that was me thinking aloud a bit there (bit distracting!). It would be nice to have it so that it ran either silently or there was some other way around. In the processing IDE pressing Ctrl+r runs the sketch, it can be nice to be able to view the code whilst the programme is running. I'm not sure how this would be done though (in Vim). Cheers – baxx Mar 21 '15 at 18:43
  • You might also want to look into github.com/tpope/vim-dispatch – Rich Mar 22 '15 at 10:10
3

One way, which I don't like all that much, could be to use system(). system() saves the output and returns it, so unless you capture it in a variable or echo it, the output won't show up:

:call system('./make.py')

system doesn't seem to have any indication of whether the command was successful or not, and aside from a blinking cursor (or lack thereof), there's not much visual indication as to whether the command has finished execution. You'd still want to save the output in a variable or something, just to know if the command failed.

  • ooo that works :) I feel like I'm playing with fire a bit though, the mailing list link seemed to imply that the problem was trying to use it without the quotations. Any dangers that you know of? Why don't you like it? cheers @muru – baxx Mar 21 '15 at 19:12
  • @user3130747 it isn't all that obvious when a command finishes or fails - I don't think the exit code is checked at all. So unless your command is guaranteed to succeed, you probably might miss crucial errors. – muru Mar 21 '15 at 19:15
  • This is a good point! I didn't think about that... Will it close the command though? I'm assuming that if there was an error the process would end? Or would that make it just keep running? If there was an error in the code the processing sketch wouldn't run, meaning that there wouldn't be a sketch window to close (and end the process). So I'm wondering about that now. Other wise it's ace – baxx Mar 21 '15 at 19:18
  • @user3130747 normally, does the process end if there's an error? system closes stdin, I think, so unless your command expects something else, it should close. – muru Mar 21 '15 at 19:21
2

The best idea it would be to define your own :make by setting makeprg option and then you can run:

:make | copen

It'll run the command and open a window to show the current list of errors. However it doesn't run in the background.

So probably you should use screen for that, e.g.:

:!screen -dm "!./make.py"

Or use :execute "!./foo.py &" or :call system("./foo.py &") instead.

See also:

  • cheers @kenorb, :call system('./make.py') did the trick though :) – baxx Mar 21 '15 at 19:13
0

Vim 8 has released for 2 years and shipped with all the mean stream Linux distributions and Mac OS X. But a lot of vim tutorials are still teaching people something ten years ago.

You can compile your C++/Java programs in vim as convenience as Sublime Text or NotePad++ with some dedicated plugins for Vim 8 or NeoVim.

For example, the AsyncRun plugin will allow you run shell commands in background and read output from quickfix window in realtime. See the screen capture.

Just like compiling programs in IDEs, the compilation errors will be matched by errorformat and be highlighted and become selectable. You can navigate errors in the quickfix window or continue editing while compiling.

Quick setup

Copy & paste the lines below to your vimrc:

Plug 'skywind3000/asyncrun.vim'

" open quickfix window automatically when AsyncRun is executed
" set the quickfix window 6 lines height.
let g:asyncrun_open = 6

" ring the bell to notify you job finished
let g:asyncrun_bell = 1

" F10 to toggle quickfix window
nnoremap <F10> :call asyncrun#quickfix_toggle(6)<cr>

When you input “:AsyncRun echo hello ” in the command line:

see the capture here

You will see the realtime command output in the open quickfix window.

Compile and run a single file

Compiling a single file with AsyncRun is much simpler than Sublime Text’s build system. We can setup F9 for this:

noremap <silent> <F9> :AsyncRun gcc -Wall -O2 "$(VIM_FILEPATH)" -o "$(VIM_FILEDIR)/$(VIM_FILENOEXT)" <cr>

The macros in $(..) form will be expanded as the real file name or directory, and then we will have F5 to run the executable:

noremap <silent> <F5> :AsyncRun -raw -cwd=$(VIM_FILEDIR) "$(VIM_FILEDIR)/$(VIM_FILENOEXT)" <cr>

The double quotation mark is used to handle path names containing spaces. The option -cwd=$(VIM_FILEDIR) means running the file in the file's directory. The absolute path name $(VIM_FILEDIR)/$(VIM_FILENOEXT) is used because linux needs a ./ prefix to running executables in current directory, but windows doesn't . Using the absolute path name of the binary file can handle this crossing platform issue.

Another option -raw means the output will not be matched by vim's errorformat, and will be displayed in quickfix as what it is. Now you can compile your file with F9, check the compilation errors in quickfix window and press F5 to run the binary.

Build C/C++ Projects

No matter what build tool you are using, make or cmake, project building means acting to a group of files. It requires locating the project root directory. AsyncRun uses a simple method called root markers to identify the project root. The Project Root is identified as the nearest ancestor directory of the current file which contains one of these directories or files:

let g:asyncrun_rootmarks = ['.svn', '.git', '.root', '_darcs'] 

If none of the parent directories contains these root markers, the directory of the current file is used as the project root. This enables us to use either <root> or $(VIM_ROOT) to represent the project root. and F7 can be setup to build the current project:

noremap <silent> <F7> :AsyncRun -cwd=<root> make <cr>

What if your current project is not in any git or 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 will be located easily.

Let’s move on, setup F8 to run the current project:

noremap <silent> <F8> :AsyncRun -cwd=<root> -raw make run <cr>

The project will run in its root directory. Of course, you need define the run rule in your own makefile. then remap F6 to test:

noremap <silent> <F6> :AsyncRun -cwd=<root> -raw make test <cr>

If you are using cmake, F4 can be map to update your Makefile:

nnoremap <silent> <F4> :AsyncRun -cwd=<root> cmake . <cr>

Due to the implementation of c runtime, if the process is running is a non-tty environment, all the data in stdout will be buffered until process exits. So, there must be a fflush(stdout) after your printf statement if you want to see the real-time output. or you can close the stdout buffer at the beginning by

setbuf(stdout, NULL);

At the mean time, if you are writing C++ code, a std::endl can be appended to the end of std::cout. It can force flush the stdout buffer. If you are developing on windows, AsyncRun can open a new cmd window for the child process:

nnoremap <silent> <F5> :AsyncRun -cwd=$(VIM_FILEDIR) -mode=4 "$(VIM_FILEDIR)/$(VIM_FILENOEXT)" <cr>
nnoremap <silent> <F8> :AsyncRun -cwd=<root> -mode=4 make run <cr>

Using the option -mode=4 on windows will open a new prompt window to run the command, just like running command line programs in Visual Studio. Finally, we have these key mappings below:

  • F4: update Makefile with cmake.
  • F5: run the single file
  • F6: run project test
  • F7: build project
  • F8: run project
  • F9: compile the single file
  • F10: toggle quickfix window

It is more like build system in NotePad++ and GEdit. If you are using cmake heavily, you can write a simple shell script located in ~/.vim/script/build.sh to combine F4 and F7 together: it will update Makefile if CMakeList.txt has been changed, then exectute make.

Advanced usage

You can also define shell scripts in your dotfiles repository and execute the script with F3:

nnoremap <F3> :AsyncRun -cwd=<root> sh /path/to/your/dotfiles/script/build_advanced.sh <cr>

The following shell environment variables are defined by AsyncRun:

$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_FILENOEXT - File name of current buffer without path and extension
$VIM_CWD       - Current directory
$VIM_RELDIR    - File path relativize to current directory
$VIM_RELNAME   - File name relativize to current directory 
$VIM_ROOT      - Project root directory
$VIM_CWORD     - Current word under cursor
$VIM_CFILE     - Current filename under cursor
$VIM_GUI       - Is running under gui ?
$VIM_VERSION   - Value of v:version
$VIM_COLUMNS   - How many columns in vim's screen
$VIM_LINES     - How many lines in vim's screen
$VIM_SVRNAME   - Value of v:servername for +clientserver usage

All the above environment variables can be used in your build_advanced.sh. Using the external shell script file can do more complex work then a single command.

Grep symbols

Sometimes, If you don't have a well setup environment in you remote linux box, grep is the most cheap way to search symbol definition and references among sources. Now we will have F2 to search keyword under cursor:

if has('win32') || has('win64')
    noremap <F2> :AsyncRun! -cwd=<root> grep -n -s -R <C-R><C-W> --include='*.h' --include='*.c*' '<root>' <cr>
else
    noremap <F2> :AsyncRun! -cwd=<root> findstr /n /s /C:"<C-R><C-W>" "\%CD\%\*.h" "\%CD\%\*.c*" <cr>
endif

The above script will run grep or findstr in your project root directory, and find symbols in only .c, .cpp and .h files. Now we move around the cursor and press F2, the symbol references in current project will be displayed in the quickfix window immediately.

This simple keymap is enough for most time. And you can improve this script to support more file types or other grep tools in your vimrc .

That’s the practical way to build/run C/C++ projects in Vim 8 or NeoVim. Just like Sublime Text’s build system and NotePad++’s NppExec.

No more outdated vim tutorials again, try something new.

  • thanks for the update, hope it's useful to others finding this thread in future – baxx May 10 '18 at 9:03

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.