1

I am writing a vim plugin for the fortran filetype.

In an autoload script, I have done:

" File Name: autoload/makes.vim
let s:Compiler = get(g:, "fortran_compiler", "gfortran")
let s:ObjExt    =   '.o'
let s:ModExt    =   '.mod'
let s:ExeExt    =   ''
let s:VimComp   =   'gfortran'
let s:FCFlags   =   '-Wall -g -O0 -c'
let s:FLFlags   =   '-Wall -g -O0'
let s:OutputGvim=   'vim'

function! makes#Fcompile()
  exe ":cclose"
  exe ":update"

  let sou = expand("%:p")
  let obj = expand("%:p:r").s:ObjExt

  if !filereadable(obj) || (filereadable(obj) && (getftime(obj)<getftime(sou)))
    let makeprg_saved = '"'.&makeprg.'"'
    exe "setlocal makeprg=".s:Compiler
    let v:statusmsg = ''
    exe "make ".s:FCFlags. " " .sou. " -o " .obj
    if empty(v:statusmsg)
      :let &statusline = "'".obj."':Compiled successfully"
    endif
    if v:shell_error !=0
      :let &statusline = v:shell_error
    endif
    ":redraw!
    exe ":botright copen"
  else
    :let &statusline = "'".obj."':is up to date"
  endif
endfunction

I use it from ftplugin/fortran_mk.vim like this:

function! Compile()
  :call makes#Fcompile()
endfunction

Now, I compile a program, say, foo.f90 as:

Program foo
   Implicit None
   write (*, *) "Hello"+i
End Program foo

The problem is that when compilation succeeds (:exe Compile()), a buffer opens named "<Integer>: -- NO NAME --". If there is an error the buffer does not open and shows the error in quickfix.

I want no buffer to open when compilation succeeds and a oneline error when it failed.

8
  • 1
    From what I see in the main function you're always opening the quickfix window except in the case of the obj file being "up to date". Why not just return after the "Compiled succesfully" message?
    – B Layer
    Jul 27 '20 at 6:50
  • 1
    BTW, not related to your question but just from a good coding convention standpoint it would be good to change that first if to check the opposite condition and if true set status line to "up to date" and return. Then the rest of the code wouldn't have to all be indented in a large if block.
    – B Layer
    Jul 27 '20 at 6:58
  • 1
    @BLayer from a good coding convention standpoint i would recommend not setting the statusline... most users have their own, and I would not be happy if a plugin overrode mine. There are other ways to message the user. (Also the statusline may not be visible)
    – D. Ben Knoble
    Jul 27 '20 at 13:33
  • Other code comment: when you save the old makeprg, you dont restore it. And the double quotes around it arent necessary—just save it directly.
    – D. Ben Knoble
    Jul 27 '20 at 13:34
  • @D.BenKnoble That really has nothing to do with coding conventions. I was talking about refactoring the conditionals. I mentioned their use of statusline...for which I rendered no judgement...just to identify the code in question.
    – B Layer
    Jul 27 '20 at 16:11
1

So what opens this additional buffer is your command exe ":botright copen", :h :copen is used to open the quickfix window. As you use it no matter how the compilation went, when the compilation is successful the quickfix doesn't contain anything and the buffer looks open.

So the first thing to do is to remove the useless usages of :exe: In exe ":cclose", exe ":update" and exe ":botright copen" you don't need an execute command since the strings to pass to execute are already complete normal commands. So replace these lines by cclose, update and botright open.

Now to prevent the buffer from opening on failed compilation you probably want to move botright copen to a specific condition. It could be like this:

if empty(v:statusmsg)
    let &statusline = "'".obj."':Compiled successfully"
else
    botright copen    
endif

Also a few different ideas:

  • As B.Layer pointed out in the comments you can reduce the cyclomatic complexity of your function by refactoring your if conditions
  • You don't need to use : in front of normal commands in a vimscript
  • I think there is no benefit using the short name of commands in a script
  • And I think you don't need to surround your save of makeprg between quotes you should be able to do let save = &makeprg and then execute 'set makeprg = ' . save

So here is how I would refactor your code:

function! makes#Fcompile()
    cclose
    update

    let sou = expand("%:p")
    let obj = expand("%:p:r").s:ObjExt

    " Don't process any further if the compilation is up to date
    if filereadable(obj) && (getftime(obj)>getftime(sou)))
        let &statusline = "'" . obj . "':is up to date"
        return
    endif

    let makeprg_saved = &makeprg
    execute "setlocal makeprg=" . s:Compiler
    let v:statusmsg = ''
    execute "make " . s:FCFlags . " " . sou . " -o " . obj
    execute 'set makeprg=' . makeprg_saved

    " Don'r process any further if the compilation was sucessful
    if empty(v:statusmsg)
        let &statusline = "'" . obj . "':Compiled successfully"
        return
    endif

    if v:shell_error !=0
        let &statusline = v:shell_error
    endif
    botright copen
endfunction
2
  • You also forgot to restore the makeprg :)
    – D. Ben Knoble
    Jul 27 '20 at 13:35
  • Good catch Ben, thanks :)
    – statox
    Jul 27 '20 at 14:22

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.