2

So, here's my problem. I want to be able to run make build whenever I save a source code file and see it in a split pane.

If I manually run :split | :te! make build, I get a split pane, my makefile runs, it's focused and by just pressing Enter I can close it. All fine.

But, if I do :autocmd BufWritePost src/*.elm :split | :te! make build, the command does run whenever I "write" the buffer ( so that part is fine ), and I get the terminal running in a split pane. BUT the focus remains on the source code file pane, and it somehow also turns it into INSERT mode. I cannot understand why.

( I use neovim, with some plugins, let me know if you need info on that )

3 Answers 3

1

This is actually a Vim bug (inherited in Nvim), which wasn't noticeable until the use-case you describe became common.

Bug report: https://github.com/neovim/neovim/issues/7429

Pull request: https://github.com/neovim/neovim/pull/7431

0

The correct way to do this in neovim is autocmd! BufWritePost src/*.elm make build

So actually much simpler...

2
  • If I do that, I use the "!" to run the command, not a terminal, right? The problem with that is that I don't get colored output. The color codes are printed directly and it's much more difficult to read. ( example : [38;5;81m---------------------[ [38;5;208m bundle Elm [38;5;81m ] [0;39;49m )
    – AntouanK
    Apr 20, 2017 at 21:24
  • Oh, you didn't mention the colorcodes.
    – mike
    Apr 21, 2017 at 17:28
0

It sounds like you would benefit from a custom quickfix window. I too tried many different ways to use the built-in make command, and while it's a great feature, it can't do everything. It really isn't customizable like a normal window is, or even the preview window. I found the preview window far more effective at displaying output (though my solution doesn't have colored output). If the preview window doesn't give you the output you want, you could try just using a normal split pane.

The following code assumes the output file (in C at least) is a *.cmp file in a directory like C:\TEMP or some other directory indicated by your $TMP environment variable.

Now you can just call CompileModule in your autocommand. The provided code also maps double-click to the double click handler so that double-clicking in this custom window takes you to the exact file, line, and column where the compiler choked. I realize the quickfix window does this automatically for you, but as stated earlier, it is very difficult to customize.

let s:vimdbg_file = $VIMRUNTIME . "\\vimdebug.out"
let s:show_err_type = 0 " set to 1 to include error indicator in output
let s:output_window_height = 12 " maximum size of output window, uses less space if there is less output

map <silent> <2-LeftMouse> :call HandleDoubleLeftClick()<CR>

function! CompileModule()
    if expand('%:t') == 'vimdebug.out' | exe "wincmd k" | endif

    " adjust compile below to your actual command line build tool
    " (this executes a shell command to change to the directory of the current file and then build)
    exe "silent !( cd %:p:h ; compile %:r )"

    let s:last_built = tolower(expand('%:t'))

    call ShowOutFile()
endf

function! ShowOutFile()
    let tmp_dir = $TMPDIR
    if tmp_dir == ""
        let tmp_dir = $TEMP
    endif
    if tmp_dir == ""
        let tmp_dir = $TMP
    endif
    let outfile = tmp_dir . '/' . expand( '%:t:r' ) . '.cmp'

    if getfsize( outfile ) == 0 || !filereadable( outfile )
        let outfile = expand( '%:r' ) . '.out'
        " stash this in msg history and hide so it's not disruptive
        echom ".cmp not found, using .out"
        redraw
    endif

    if getfsize( outfile ) > 0 && filereadable( outfile )
        let outbuff = readfile( outfile )
        let dispbuff = []
        let index = 0
        let num_errors = 0
        for errline in outbuff
            if match( errline, ' error \| warning\|informational\|severe' ) >= 0
                if match( errline, ' error ' ) >= 0
                    let num_errors += 1
                endif
                let index = stridx( errline, "(" )
                let index2 = stridx( errline, ")", index )
                let index3 = stridx( errline, ":", index2 )
                let index4 = stridx( errline, ":", index3 + 1 )

                " get the entire filename
                let search_idx = index
                while ( errline[search_idx] != '\' && errline[search_idx] != '/' && search_idx >= 0 )
                    let search_idx -= 1
                endw

                " if filename is different, include it in output
                if ( ( tolower(expand('%:t')) == strpart( errline, search_idx + 1, index - (search_idx + 1) ) ) || ( s:always_include_filename == 1 ) )
                    let startidx = index
                    let startlen = (index2 + 1) - index
                else
                    let startidx = search_idx + 1
                    let startlen = index2 - search_idx
                endif

                " include error type indicator in output
                if s:show_err_type == 1
                    if match( errline, 'error' ) >= 0
                        let c_err_ind = " E"
                    elseif match( errline, 'warning' ) >= 0
                        let c_err_ind = " W"
                    elseif match( errline, 'informational' ) >= 0
                        let c_err_ind = " I"
                    else
                        let c_err_ind = " "
                    endif
                else
                    let c_err_ind = " "
                endif

                let startline = strpart( errline, startidx, startlen )
                let endline = strpart( errline, index4 )

                if c_err_ind == " "
                    call add( dispbuff, startline . endline )
                else
                    call add( dispbuff, startline . c_err_ind . endline )
                endif
            endif
        endfor

        if dispbuff == [] || len(dispbuff) == 0
            redraw " Erase any existing messages - gets rid of ENTER prompt
            echom expand('%:t') . ' compiled successfully!'

            " Close preview window if it's open
            try
                sil exe "pc"
            catch /^Vim(\a\+):E441:/
            endtry

            return
        else
            redraw " Erase any existing messages - gets rid of ENTER prompt

            if num_errors > 0
                echohl ErrorMsg
                echom num_errors . ' compile errors found!'
                echohl Normal
            else
                echohl WarningMsg
                echom 'Compile successful, see warnings...'
                echohl Normal
            endif
        endif

        " Write to a special output file
        call writefile( dispbuff, s:vimdbg_file )

        " Load (or re-load) the output file
        sil exe "ped " . s:vimdbg_file

        " Move preview window to the bottom
        try
            exe "wincmd P"
            exe "wincmd J"
        catch /^Vim(\a\+):E441:/ " no preview window open
        endtry

        " Determine size of output (preview) window
        set previewheight=1
        let i = 1
        let owh = s:output_window_height
        let ph = ( owh < len(dispbuff) ) ? owh : len(dispbuff)
        while i < ph
            set previewheight+=1
            let i += 1
        endw

        " Resize window
        try
            exe "wincmd P"
        catch /^Vim(\a\+):E441:/
        endtry
        exe "res " ph
    else
        redraw " Erase any existing messages - gets rid of ENTER prompt
        echohl WarningMsg
        " (no way to tell a difference between the two)
        echom "Build successful (or output file not found)."
        echohl Normal

        " Close preview window if it's open
        try
            sil exe "pc"
        catch /^Vim(\a\+):E441:/
        endtry
    endif
endf

function! HandleDoubleLeftClick()
    let err_line = 0
    let err_col = 0

    if expand('%:t') == "vimdebug.out"
        let curr_line = getline('.')
        let index1 = stridx( curr_line, '(' )
        let index2 = stridx( curr_line, ':', index1 )
        let index1 += 1
        let int1len = index2 - index1
        let index3 = stridx( curr_line, ')', index2 )
        let index2 += 1
        let int2len = index3 - index2

        let err_line = str2nr( strpart( curr_line, index1, int1len ) )
        let err_col = str2nr( strpart( curr_line, index2, int2len ) )

        " if there are characters in front of the first '(' then
        " we have a filename to parse first
        if index1 > 1
            let buf_name = strpart( curr_line, 0, index1 - 1 )
        else
            if exists( s:last_built )
                let buf_name = s:last_built
            else
                " set buf_name to buffer name of other file (upper window?)
                wincmd k

                let buf_name = tolower(expand('%:t'))
            endif
        endif
        redraw
        " switches to the appropriate file if open; if not, opens it
        exe "dr " . buf_name

        " now go to the located line and column
        exe "normal " . err_line . "G"
        exe "normal 0"
        if err_col > 1
            exe "normal " . (err_col - 1) . "l"
        endif

        redraw
    else
        " put code here to do whatever you'd like double click to normally do
    endif
endf
1
  • thanks for the response. This might be to much for me. The Elm compiler has great messages, with coloured output, so I want to be able to see the terminal output. :te works fine, it's just that there are those quirks on how to open/close it, and I'm struggling with that.
    – AntouanK
    Apr 26, 2017 at 11:52

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

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