" Copyright (C) 2010-2015 Hong Xu " This file is part of SingleCompile. " SingleCompile is free software: you can redistribute it and/or modify " it under the terms of the GNU General Public License as published by " the Free Software Foundation, either version 3 of the License, or " (at your option) any later version. " SingleCompile 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 General Public License for more details. " You should have received a copy of the GNU General Public License " along with SingleCompile. If not, see . " File: autoload/SingleCompile.vim " Version: 2.11.0 " check doc/SingleCompile.txt for more information let s:saved_cpo = &cpo set cpo&vim " varibles {{{1 " the dict to store the compiler template let s:CompilerTemplate = {} " is template initialize let s:Initialized = 0 " Chars to escape for ':lcd' command if has('win32') let s:CharsEscape = '" ' else let s:CharsEscape = '" \' endif " executable suffix if has('win32') let s:ExecutableSuffix = '.exe' else let s:ExecutableSuffix = '' endif function! SingleCompile#GetExecutableSuffix() return s:ExecutableSuffix endfunction " seperator in the environment varibles if has('win32') let s:EnvSeperator = ';' else let s:EnvSeperator = ':' endif if has('win32') let s:PathSeperator = '\' else let s:PathSeperator = '/' endif " the path of file where the output running result is stored let s:run_result_tempfile = '' function! SingleCompile#GetVersion() " get the script version {{{1 " Before 2.9.3, the return value is: major * 100 + minor * 10 + subminor " For example, 2.9.2 is corresponding to 292 " From 2.10.0, the return value is: major * 1000 + minor * 10 + subminor " For example, 2.10.1 is corresponding to 2101 return 2110 endfunction " util {{{1 function! SingleCompile#GetDefaultOpenCommand() " {{{2 " Get the default open command. It is "open" on Windows and Mac OS X, " "xdg-open" on Linux and other UNIX systems. if has('win32') return 'start' elseif has('macunix') return 'open' elseif has('unix') return 'xdg-open' else return 'open' " We guess 'open' for any other systems endif endfunction function! s:GetCurrentShell() " {{{2 " Get the name of the current shell according to &shell for UNIX. For example, " if &shell is '/bin/sh', the return value would be 'sh'. if has('unix') return strpart(&shell, strridx(&shell, '/') + 1) endif endfunction function! s:IsShellSh(shell_name) " is the shell Bourne shell? {{2 if a:shell_name =~ '^sh' || \a:shell_name =~ '^bash' || \a:shell_name =~ '^ksh' || \a:shell_name =~ '^mksh' || \a:shell_name =~ '^pdksh' || \a:shell_name =~ '^zsh' return 1 else return 0 endif endfunction function! s:IsShellCsh(shell_name) " is the shell C Shell? {{2 if a:shell_name =~ '^csh' || a:shell_name =~ '^tcsh' return 1 else return 0 endif endfunction function! s:IsShellFish(shell_name) " is the shell FISH shell {{2 return a:shell_name =~ '^fish' endfunction function! s:GetShellLastExitCodeVariable() " {{{2 " Get the variable that presents the exit code of last command. if has('unix') let l:cur_shell = s:GetCurrentShell() if s:IsShellCsh(l:cur_shell) || s:IsShellFish(l:cur_shell) return '$status' elseif s:IsShellSh(l:cur_shell) return '$?' else return '' endif elseif has('win32') return '$?' endif endfunction function! s:GetShellPipe(tee_used) " {{{2 " get the shell pipe command according to it's platform. If a:tee_used is " set to nonzero, then the shell pipe contains "tee", otherwise "tee" " wouldn't be contained in the return value. if has('unix') let l:cur_shell = s:GetCurrentShell() if s:IsShellCsh(l:cur_shell) if a:tee_used return '|& tee' else return '>&' endif elseif s:IsShellSh(l:cur_shell) || s:IsShellFish(l:cur_shell) if a:tee_used return '2>&1| tee' else return '>%s 2>&1' endif else if a:tee_used return '| tee' else return '>' endif endif elseif has('win32') if executable('tee') && a:tee_used && g:SingleCompile_usetee return '2>&1 | tee' else return '>%s 2>&1' endif endif endfunction function! s:Expand(str, ...) " expand the string{{{2 " the second argument is optional. If it is given and it is zero, then " we thought we don't need any quote; otherwise, we use double quotes on " Windows and single quotes on all other systems. let l:quote_needed = 1 if a:0 > 1 call s:ShowMessage('s:Expand argument error.') return '' elseif a:0 == 1 if !a:1 let l:quote_needed = 0 endif endif let l:rep_dict = { \'\$(FILE_NAME)\$': '%', \'\$(FILE_PATH)\$': '%:p', \'\$(FILE_TITLE)\$': '%:r', \'\$(FILE_EXEC)\$': '%:p:r', \'\$(FILE_RUN)\$': '%:p:r'} let l:rep_dict_prefix = { \'\$(FILE_NAME)\$': '', \'\$(FILE_PATH)\$': '', \'\$(FILE_TITLE)\$': '', \'\$(FILE_EXEC)\$': '', \'\$(FILE_RUN)\$': ''} let l:rep_dict_suffix = { \'\$(FILE_NAME)\$': '', \'\$(FILE_PATH)\$': '', \'\$(FILE_TITLE)\$': '', \'\$(FILE_EXEC)\$': '', \'\$(FILE_RUN)\$': ''} if has('win32') let l:rep_dict_prefix['\$(FILE_RUN)\$'] = '' let l:rep_dict_suffix['\$(FILE_EXEC)\$'] = '.exe' let l:rep_dict_suffix['\$(FILE_RUN)\$'] = '.exe' elseif has('unix') let l:rep_dict_prefix['\$(FILE_RUN)\$'] = './' let l:rep_dict_suffix['\$(FILE_EXEC)\$'] = '' let l:rep_dict_suffix['\$(FILE_RUN)\$'] = '' endif let l:str = a:str for one_key in keys(l:rep_dict) let l:rep_string = l:rep_dict_prefix[one_key] . \ expand(l:rep_dict[one_key]) . \ l:rep_dict_suffix[one_key] " on win32, replace the backslash with '/' if has('win32') let l:rep_string = substitute(l:rep_string, '/', '\\', 'g') endif let l:rep_string = escape(l:rep_string, '\') if l:quote_needed && match(l:rep_string, ' ') != -1 if has('win32') let l:rep_string = '"'.l:rep_string.'"' else let l:rep_string = "'".l:rep_string."'" endif endif let l:str = substitute(l:str, one_key, l:rep_string, 'g') endfor return l:str endfunction function! s:RunAsyncWithMessage(run_cmd) " {{{2 " run a command and display messages that we need to let l:async_run_res = SingleCompileAsync#Run(a:run_cmd) if l:async_run_res != 0 call s:ShowMessage( \"Fail to run the command '". \a:run_cmd."'. Error code: ".l:async_run_res) if l:async_run_res == 1 call s:ShowMessage( \'There is already an existing process '. \'running in background.') endif return 1 endif return 0 endfunction function! s:PushEnvironmentVaribles() " {{{2 " push environment varibles into stack. Win32 only. if !exists('s:environment_varibles_list') || \ type(s:environment_varibles_list) != type([]) unlet! s:environment_varibles_list let s:environment_varibles_list = [] endif let l:environment_varibles_dic = {} let l:environment_varibles_string = system('set') for line in split(l:environment_varibles_string, '\n') " find the '=' first. The left part is environment varible's name, the " right part is the value let l:eq_pos = match(line, '=') if l:eq_pos == -1 continue endif let l:key = strpart(line, 0, l:eq_pos) let l:val = strpart(line, l:eq_pos + 1) let l:environment_varibles_dic[l:key] = l:val endfor call add(s:environment_varibles_list, l:environment_varibles_dic) endfunction function! s:PopEnvironmentVaribles() " {{{2 " pop environment varibles out of the stack. Win32 only. if !exists('s:environment_varibles_list') || \ type(s:environment_varibles_list) != type([]) || \ empty(s:environment_varibles_list) return 0 endif let l:environment_varibles_dic = remove( \ s:environment_varibles_list, \ len(s:environment_varibles_list) - 1) for l:key in keys(l:environment_varibles_dic) silent! exec 'let $' . l:key . "='" . \ substitute(l:environment_varibles_dic[l:key], \ "'", "''", 'g') . "'" endfor endfunction " pre-do functions {{{1 function! s:AddLmIfMathH(compiling_info) " {{{2 " add -lm flag if math.h is included " if we find '#include ' in the file, then add '-lm' flag if match(getline(1, '$'), '^[ \t]*#[ \t]*include[ \t]*["<]math.h[">][ \t]*$') \!= -1 let l:new_comp_info = a:compiling_info let l:new_comp_info['args'] = l:new_comp_info['args'] . ' -lm' return l:new_comp_info endif return a:compiling_info endfunction function! SingleCompile#PredoWatcom(compiling_info) " watcom pre-do {{{2 let s:old_path = $PATH let $PATH = $WATCOM.s:PathSeperator.'binnt'.s:EnvSeperator. \$WATCOM.s:PathSeperator.'binw'.s:EnvSeperator. \$PATH return a:compiling_info endfunction function! SingleCompile#PredoGcc(compiling_info) " gcc pre-do {{{2 if has('unix') return s:AddLmIfMathH(a:compiling_info) else return a:compiling_info endif endfunction function! SingleCompile#PredoSolStudioC(compiling_info) " solaris studio C/C++ pre-do {{{2 return s:AddLmIfMathH(a:compiling_info) endfunction function! SingleCompile#PredoClang(compiling_info) " clang Predo {{{2 if has('unix') return s:AddLmIfMathH(a:compiling_info) else return a:compiling_info endif endfunction function! SingleCompile#PredoMicrosoftVC(compiling_info) " MSVC Predo {{{2 call s:PushEnvironmentVaribles() " to get the result environment varibles, we need to write a temp batch " file and call it. let l:tmpbat = tempname() . '.bat' call writefile(['@echo off', 'call "%VS'. \str2nr(strpart(a:compiling_info['command'], 2)). \'COMNTOOLS%..\..\VC\vcvarsall.bat"', 'set'], l:tmpbat) let l:environment_varibles_string = system(l:tmpbat) " Set the environment varibles for l:line in split(l:environment_varibles_string, '\n') let l:eq_pos = match(l:line, '=') if l:eq_pos == -1 continue endif silent! exec 'let $' . strpart(l:line, 0, l:eq_pos) . "='" . \ substitute(strpart(l:line, l:eq_pos + 1), \ "'", "''", 'g') . "'" endfor let l:new_compiling_info = a:compiling_info let l:new_compiling_info['command'] = 'cl' return l:new_compiling_info endfunction function! SingleCompile#PredoShell(compiling_info) " shell predo {{{2 let l:new_compiling_info = a:compiling_info let l:shebang = getline(1) if l:shebang !~ '#!/.*' " don't change anything if no shebang is found return a:compiling_info endif " Use the shell indicated by the shebang let l:new_compiling_info['command'] = matchstr(l:shebang, '#!\zs/.*') return l:new_compiling_info endfunction " post-do functions {{{1 function! SingleCompile#PostdoWatcom(compiling_info) " watcom pre-do {{{2 let $PATH = s:old_path return a:compiling_info endfunction function! SingleCompile#PostdoMicrosoftVC(not_used_arg) " MSVC post-do {{{2 call s:PopEnvironmentVaribles() endfunction " compiler detect functions {{{1 function! s:DetectCompilerGenerally(compiling_command) " {{{2 " the general function of compiler detection. The principle is to search " the environment varible PATH and some special directory if has('unix') let l:list_to_detect = [s:Expand(expand(a:compiling_command)), \s:Expand(expand('~/bin/'.a:compiling_command)), \s:Expand(expand('/usr/local/bin/'.a:compiling_command)), \s:Expand(expand('/usr/bin/'.a:compiling_command)), \s:Expand(expand('/bin/'.a:compiling_command)) \] else let l:list_to_detect = [s:Expand(expand(a:compiling_command))] endif for cmd in l:list_to_detect if executable(cmd) == 1 return cmd endif endfor return 0 endfunction function! SingleCompile#DetectCompilerGenerally(compiling_command) return s:DetectCompilerGenerally(a:compiling_command) endfunction function! SingleCompile#DetectWatcom(compiling_command) " {{{2 let l:watcom_command = \s:DetectCompilerGenerally(a:compiling_command) if l:watcom_command != 0 return l:watcom_command endif if $WATCOM != '' return $WATCOM.'\binnt\'.a:compiling_command endif endfunction function! SingleCompile#DetectMicrosoftVC(compiling_command) " {{{2 " the compiling_command should be cl_vcversion, such as cl80, cl100, etc. " return 0 if not starting with 'cl' if strpart(a:compiling_command, 0, 2) != 'cl' return 0 endif " get the version of vc let l:vc_version = strpart(a:compiling_command, 2) " if we have VSXXCOMNTOOLS environment variable, and " %VSXXCOMNTOOLS%\..\..\VC\BIN\cl.exe is executable, and " %VSXXCOMNTOOLS%\..\..\VC\vcvarsall.bat is executable, MSVC is detected exec 'let l:vs_common_tools = $VS'.l:vc_version.'COMNTOOLS' if !empty(l:vs_common_tools) && \ executable(l:vs_common_tools.'..\..\VC\BIN\cl.exe') && \ executable(l:vs_common_tools.'..\..\VC\vcvarsall.bat') return a:compiling_command else return 0 endif endfunction function! SingleCompile#DetectIe(not_used_arg) " {{{2 if executable('iexplore') return 'iexplore' endif if has('win32') let iepath = $PROGRAMFILES . '\Internet Explorer\iexplore.exe' if executable(iepath) return "\"".iepath."\"" endif endif endfunction function! s:Initialize() "{{{1 if s:Initialized == 0 let s:Initialized = 1 else return endif if !exists('g:SingleCompile_alwayscompile') || \type(g:SingleCompile_alwayscompile) != type(0) unlet! g:SingleCompile_alwayscompile let g:SingleCompile_alwayscompile = 1 endif if !exists('g:SingleCompile_asyncrunmode') || \type(g:SingleCompile_asyncrunmode) != type('') unlet! g:SingleCompile_asyncrunmode let g:SingleCompile_asyncrunmode = 'auto' endif if !exists('g:SingleCompile_autowrite') || \type(g:SingleCompile_autowrite) != type(0) unlet! g:SingleCompile_autowrite let g:SingleCompile_autowrite = 1 endif if !exists('g:SingleCompile_quickfixwindowposition') || \type(g:SingleCompile_quickfixwindowposition) != type('') unlet! g:SingleCompile_quickfixwindowposition let g:SingleCompile_quickfixwindowposition = 'botright' end if !exists('g:SingleCompile_resultsize') || \type(g:SingleCompile_resultsize) != type(0) || \g:SingleCompile_resultsize <= 0 unlet! g:SingleCompile_resultsize let g:SingleCompile_resultsize = 5 endif if !exists('g:SingleCompile_split') let g:SingleCompile_split = 'split' endif if !exists('g:SingleCompile_showquickfixiferror') || \type(g:SingleCompile_showquickfixiferror) != type(0) unlet! g:SingleCompile_showquickfixiferror let g:SingleCompile_showquickfixiferror = 0 endif if !exists('g:SingleCompile_showquickfixifwarning') || \type(g:SingleCompile_showquickfixifwarning) != type(0) unlet! g:SingleCompile_showquickfixifwarning let g:SingleCompile_showquickfixifwarning = \g:SingleCompile_showquickfixiferror endif if !exists('g:SingleCompile_showresultafterrun') || \type(g:SingleCompile_showresultafterrun) != type(0) unlet! g:SingleCompile_showresultafterrun let g:SingleCompile_showresultafterrun = 0 endif if !exists('g:SingleCompile_usedialog') || \type(g:SingleCompile_usedialog) != type(0) unlet! g:SingleCompile_usedialog let g:SingleCompile_usedialog = 0 endif if !exists('g:SingleCompile_usequickfix') || \type(g:SingleCompile_usequickfix) != type(0) unlet! g:SingleCompile_usequickfix let g:SingleCompile_usequickfix = 1 endif if !exists('g:SingleCompile_usetee') || \type(g:SingleCompile_usetee) != type(0) unlet! g:SingleCompile_usetee let g:SingleCompile_usetee = 1 endif if !exists('g:SingleCompile_silentcompileifshowquickfix') || \type(g:SingleCompile_silentcompileifshowquickfix) != type(0) unlet! g:SingleCompile_silentcompileifshowquickfix let g:SingleCompile_silentcompileifshowquickfix = 0 endif " Initialize async mode if g:SingleCompile_asyncrunmode !=? 'none' let l:async_init_res = SingleCompileAsync#Initialize( \g:SingleCompile_asyncrunmode) if type(l:async_init_res) == type('') call s:ShowMessage( \"Failed to initialize the async mode '". \g:SingleCompile_asyncrunmode."'.\n".l:async_init_res) elseif l:async_init_res == 3 call s:ShowMessage("The specified async mode '". \g:SingleCompile_asyncrunmode."' doesn't exist.") elseif l:async_init_res != 0 call s:ShowMessage( \"Failed to initialize the async mode'". \g:SingleCompile_asyncrunmode."'.") endif endif " terminate the background process before existing vim if has('autocmd') autocmd VimLeave * call SingleCompileAsync#Terminate() endif " initialize builtin templates if has('win32') let g:SingleCompile_common_run_command = '$(FILE_TITLE)$.exe' let g:SingleCompile_common_out_file = '$(FILE_TITLE)$.exe' else let g:SingleCompile_common_run_command = './$(FILE_TITLE)$' let g:SingleCompile_common_out_file = '$(FILE_TITLE)$' endif for builtin_filetype in [ \ 'ada', \ 'bash', \ 'c', \ 'cmake', \ 'cpp', \ 'coffee', \ 'cs', \ 'csh', \ 'd', \ 'dosbatch', \ 'elixir', \ 'erlang', \ 'fortran', \ 'go', \ 'haskell', \ 'html', \ 'idlang', \ 'java', \ 'javascript', \ 'ksh', \ 'lisp', \ 'ls', \ 'lua', \ 'make', \ 'markdown', \ 'matlab', \ 'ocaml', \ 'objc', \ 'pascal', \ 'perl', \ 'php', \ 'python', \ 'qml', \ 'r', \ 'rst', \ 'ruby', \ 'rust', \ 'scala', \ 'sh', \ 'tcl', \ 'tcsh', \ 'tex', \ 'vb', \ 'vim', \ 'xhtml', \ 'zsh'] exec 'call SingleCompile#templates#' . \ builtin_filetype . '#Initialize()' endfor endfunction function! s:SetVimCompiler(lang_name, compiler) " {{{1 " call the :compiler command let l:dict_compiler = s:CompilerTemplate[a:lang_name][a:compiler] if has_key(l:dict_compiler, 'vim-compiler') silent! exec 'compiler '.l:dict_compiler['vim-compiler'] else silent! exec 'compiler '.a:compiler endif endfunction function! s:SetGlobalVimCompiler(lang_name, compiler) " {{{1 "call the :compiler! command let l:dict_compiler = s:CompilerTemplate[a:lang_name][a:compiler] if has_key(l:dict_compiler, 'vim-compiler') silent! exec 'compiler! '.l:dict_compiler['vim-compiler'] else silent! exec 'compiler! '.a:compiler endif endfunction " SingleCompile#SetCompilerTemplate {{{1 function! SingleCompile#SetCompilerTemplate(lang_name, compiler, \compiler_name, detect_func_arg, flags, run_command, ...) " set compiler's template, including compiler, compiler's name, the " detecting function argument, compilation flags, run command and " a compiler detecting function which is optional. call s:Initialize() call s:SetCompilerSingleTemplate(a:lang_name, a:compiler, 'name', \a:compiler_name) call s:SetCompilerSingleTemplate(a:lang_name, a:compiler, \'detect_func_arg', a:detect_func_arg) call s:SetCompilerSingleTemplate(a:lang_name, a:compiler, 'flags', \a:flags) call s:SetCompilerSingleTemplate(a:lang_name, a:compiler, 'run', \a:run_command) if a:0 == 0 call SingleCompile#SetDetectFunc(a:lang_name, a:compiler, \function("s:DetectCompilerGenerally")) else call SingleCompile#SetDetectFunc(a:lang_name, a:compiler, a:1) endif endfunction " function! SingleCompile#SetCompilerTemplateByDict {{{1 function! SingleCompile#SetCompilerTemplateByDict( \lang_name, compiler, template_dict) " set templates by using a dict(template_dict), thus calling the template " settings functions below one by one is not needed. let l:key_list = ['name', 'detect_func_arg', 'flags', 'run', \'detect_func', 'pre-do', 'priority', 'post-do', 'out-file', \'vim-compiler'] for key in l:key_list if has_key(a:template_dict, key) call s:SetCompilerSingleTemplate(a:lang_name, a:compiler, key, \get(a:template_dict, key)) endif endfor endfunction " extra template settings functions {{{1 function! SingleCompile#SetDetectFunc(lang_name, compiler, detect_func) " {{{2 " set the detect_func function call s:SetCompilerSingleTemplate(a:lang_name, a:compiler, 'detect_func', \a:detect_func) endfunction function! SingleCompile#SetPredo(lang_name, compiler, predo_func) " {{{2 " set the pre-do function call s:SetCompilerSingleTemplate(a:lang_name, a:compiler, 'pre-do', \a:predo_func) endfunction function! SingleCompile#SetPostdo(lang_name, compiler, postdo_func) " {{{2 " set the post-do function call s:SetCompilerSingleTemplate(a:lang_name, a:compiler, \'post-do', a:postdo_func) endfunction function! SingleCompile#SetOutfile(lang_name, compiler, outfile) " {{{2 " set out-file call s:SetCompilerSingleTemplate(a:lang_name, a:compiler, \'out-file', a:outfile) endfunction fun! SingleCompile#SetVimCompiler(lang_name, compiler, vim_compiler) " {{{2 " set vim-compiler call s:SetCompilerSingleTemplate(a:lang_name, a:compiler, \'vim-compiler', a:vim_compiler) endfunction function! SingleCompile#SetPriority(lang_name, compiler, priority) " {{{2 " set priority call s:SetCompilerSingleTemplate(a:lang_name, a:compiler, \ 'priority', a:priority) endfunction function! s:GetCompilerSingleTemplate(lang_name, compiler_name, key) " {{{1 return s:CompilerTemplate[a:lang_name][a:compiler_name][a:key] endfunction " SetCompilerSingleTemplate {{{1 fun! s:SetCompilerSingleTemplate(lang_name, compiler, key, value, ...) " set the template. if the '...' is nonzero, this function will not " override the corresponding template if there is an existing template " Set the default template first so that we could let this override the " default compile template. call s:Initialize() if a:0 > 1 call s:ShowMessage( \'Too many argument for'. \' SingleCompile#SetCompilerSingleTemplate function!') return endif " if the key a:lang_name does not exist, create it if !has_key(s:CompilerTemplate,a:lang_name) let s:CompilerTemplate[a:lang_name] = {} elseif type(s:CompilerTemplate[a:lang_name]) != type({}) unlet! s:CompilerTemplate[a:lang_name] let s:CompilerTemplate[a:lang_name] = {} endif " if a:compiler does not exist, create it if !has_key(s:CompilerTemplate[a:lang_name],a:compiler) let s:CompilerTemplate[a:lang_name][a:compiler] = {} elseif type(s:CompilerTemplate[a:lang_name][a:compiler]) != type({}) unlet! s:CompilerTemplate[a:lang_name][a:compiler] let s:CompilerTemplate[a:lang_name][a:compiler] = {} endif " if a:key does not exist, create it " if the ... from the argument is 0 or the additional argument does " not exist, also override the original one if !has_key(s:CompilerTemplate[a:lang_name][a:compiler], a:key) || \a:0 == 0 || a:1 == 0 let s:CompilerTemplate[a:lang_name][a:compiler][a:key] = a:value endif endfunction function! SingleCompile#SetTemplate(lang_name, stype, string,...) " {{{1 " set the template. if the '...' is nonzero, this function will not " override the corresponding template if there is an existing template if a:0 > 1 call s:ShowMessage('Too many argument for '. \'SingleCompile#SetTemplate function') return endif if a:0 == 0 || a:1 == 0 let l:override = 0 else let l:override = 1 endif if a:stype == 'command' let l:stype = 'detect_func_arg' else let l:stype = a:stype endif " Set the name any way call s:SetCompilerSingleTemplate(a:lang_name, \'user_defined_compiler_using_old_style_function', \'name', 'user_defined_compiler_using_old_style_function', 1) let l:compiler_dict = s:CompilerTemplate[a:lang_name][ \ 'user_defined_compiler_using_old_style_function'] " Use general detect_func if !has_key(l:compiler_dict, 'detect_func') call SingleCompile#SetDetectFunc(a:lang_name, \'user_defined_compiler_using_old_style_function', \function("s:DetectCompilerGenerally")) endif " Set the template we want to set call s:SetCompilerSingleTemplate(a:lang_name, \'user_defined_compiler_using_old_style_function', \l:stype, a:string, l:override) if has_key(l:compiler_dict, 'name') && \ has_key(l:compiler_dict, 'detect_func') && \ has_key(l:compiler_dict, 'detect_func_arg') && \ has_key(l:compiler_dict, 'flags') && \ has_key(l:compiler_dict, 'run') call SingleCompile#ChooseCompiler(a:lang_name, \ 'user_defined_compiler_using_old_style_function') endif endfunction function! s:ShowMessage(message) "{{{1 if g:SingleCompile_usedialog == 0 || !((has('gui_running') && \has('dialog_gui')) || has('dialog_con')) echohl Error | echo 'SingleCompile: '.a:message | echohl None else call confirm('SingleCompile: '.a:message) endif endfunction function! s:IsLanguageInterpreting(filetype_name) "{{{1 "tell if a language is an interpreting language, reutrn 1 if yes, 0 if no let l:chosen_compiler = \s:CompilerTemplate[a:filetype_name]['chosen_compiler'] return (!has_key( \s:CompilerTemplate[a:filetype_name][l:chosen_compiler], \'run') \ || substitute( \s:CompilerTemplate[a:filetype_name][l:chosen_compiler] \['run'], \' ', '', "g") == '') endfunction function! s:ShouldQuickfixBeUsed() " tell whether quickfix sould be used{{{1 if g:SingleCompile_usequickfix == 0 \ || !has('quickfix') \ || ( s:IsLanguageInterpreting(&filetype) && !has('unix') ) return 0 else return 1 endif endfunction function! SingleCompile#Compile(...) " compile synchronously {{{1 return s:CompileInternal(a:000, 0) endfunction function! SingleCompile#CompileAsync(...) " compile asynchronously {{{1 return s:CompileInternal(a:000, 1) endfunction function! s:CompileInternal(arg_list, async) " compile only {{{1 " Return 0 for compiling language successfully compiled; " Return 1 for compiling language failed to be compiled; " Return 2 for interpreting language successfully run; " Return 3 for interpreting language failed to be run. call s:Initialize() let l:toret = 0 " whether we should run asynchronously if we are working with an " interpreting language let l:async = a:async && !empty(SingleCompileAsync#GetMode()) if !l:async && executable('tee') && g:SingleCompile_usetee \ && g:SingleCompile_showresultafterrun == 1 let l:show_result_after_run = 1 else let l:show_result_after_run = 0 endif " save current file type. Don't use &filetype directly because after " 'make' and quickfix is working and the error is in another file, " sometimes the value of &filetype may be incorrect. let l:cur_filetype = &filetype " Save the filetype to a script variable so it can be used by s:Run() let s:cur_filetype = l:cur_filetype " if current filetype is an empty string, show an error message and " return. if l:cur_filetype == '' call s:ShowMessage( \"Current buffer's filetype is not specified. ". \"Use \" :help 'filetype' \" command to see more details". \" if you don't know what filetype is.") return -1 endif " Check whether the language template is available if !(has_key(s:CompilerTemplate, l:cur_filetype) && \type(s:CompilerTemplate[l:cur_filetype]) == type({})) call s:ShowMessage('Language template for "'. \l:cur_filetype.'" is not defined on your system.') return -1 endif " if current buffer has no name (for example the buffer has never been " saved), don't compile if bufname('%') == '' call s:ShowMessage( \'Current buffer does not have a file name. '. \'Please save current buffer first. '. \'Compilation canceled.') return -1 endif " if autowrite is set and the buffer has been modified, then save if g:SingleCompile_autowrite != 0 && &modified != 0 write endif " detect compilers if !has_key(s:CompilerTemplate[l:cur_filetype], 'chosen_compiler') let l:detected_compilers = s:DetectCompiler(l:cur_filetype) " if l:detected_compilers is empty, then no compiler is detected if empty(l:detected_compilers) call s:ShowMessage( \'No compiler is detected on your system!') return -1 endif let s:CompilerTemplate[l:cur_filetype]['chosen_compiler'] = \get(l:detected_compilers, 0) endif let l:chosen_compiler = \s:CompilerTemplate[l:cur_filetype]['chosen_compiler'] let l:compile_cmd = s:GetCompilerSingleTemplate( \l:cur_filetype, l:chosen_compiler, 'command') " save current working directory let l:cwd = getcwd() " switch current work directory to the file's directory silent lcd %:p:h " If current language is not interpreting language, check the last " modification time of the file, whose name is the value of the 'out-file' " key. If the last modification time of that file is earlier than the last " modification time of current buffer's file, don't compile. if !g:SingleCompile_alwayscompile \&& !s:IsLanguageInterpreting(l:cur_filetype) \&& has_key( \s:CompilerTemplate[l:cur_filetype][l:chosen_compiler], \'out-file') \&& getftime(s:Expand( \s:CompilerTemplate[l:cur_filetype][l:chosen_compiler] \['out-file'], 0)) \> getftime(expand('%:p')) " switch back to the original directory exec 'lcd '.escape(l:cwd, s:CharsEscape) echo 'SingleCompile: '. \'No need to compile. '. \'No modification is detected.' return 0 endif if len(a:arg_list) == 1 " if there is only one argument, it means use this argument as the " compilation flag let l:compile_flags = a:arg_list[0] elseif len(a:arg_list) == 2 && has_key( \s:CompilerTemplate[l:cur_filetype][ \s:CompilerTemplate[l:cur_filetype]['chosen_compiler']], \'flags') " if there are two arguments, it means append the provided argument to " the flag defined in the template let l:compile_flags = s:GetCompilerSingleTemplate(l:cur_filetype, \s:CompilerTemplate[l:cur_filetype]['chosen_compiler'], \'flags').' '.a:arg_list[1] elseif len(a:arg_list) == 0 && has_key( \s:CompilerTemplate[l:cur_filetype][ \s:CompilerTemplate[l:cur_filetype]['chosen_compiler']], \'flags') let l:compile_flags = s:GetCompilerSingleTemplate(l:cur_filetype, \s:CompilerTemplate[l:cur_filetype]['chosen_compiler'], \'flags') else " if len(a:arg_list) is zero and 'flags' is not defined, assign '' to " let l:compile_flags let l:compile_flags = '' endif if match(l:compile_flags, '\$(FILE_PATH)\$') == -1 && \match(l:compile_flags, '\$(FILE_NAME)\$') == -1 let l:compile_flags = l:compile_flags.' $(FILE_PATH)$' endif let l:compile_args = s:Expand(l:compile_flags) " call the pre-do function if set if has_key(s:CompilerTemplate[l:cur_filetype][l:chosen_compiler], \ 'pre-do') let l:command_dic = \s:CompilerTemplate[l:cur_filetype][l:chosen_compiler][ \'pre-do']( \{ 'command': l:compile_cmd, \'args': l:compile_args}) let l:compile_cmd = l:command_dic['command'] let l:compile_args = l:command_dic['args'] endif if s:ShouldQuickfixBeUsed() == 0 " if quickfix is not enabled for this plugin or the language is an " interpreting language not in unix, then don't use quickfix if l:async && s:IsLanguageInterpreting(l:cur_filetype) call s:RunAsyncWithMessage(l:compile_cmd.' '.l:compile_args) else exec '!'.l:compile_cmd.' '.l:compile_args " check whether compiling is successful, if not, show the return value " with error message highlighting and set the return value to 1 if v:shell_error != 0 echo ' ' call s:ShowMessage('Error! Return value is '.v:shell_error) if s:IsLanguageInterpreting(l:cur_filetype) let l:toret = 3 else let l:toret = 1 endif endif endif elseif has('unix') && s:IsLanguageInterpreting(l:cur_filetype) " use quickfix for interpreting language in unix if l:async " run the interpreter asynchronously call s:RunAsyncWithMessage(l:compile_cmd.' '.l:compile_args) else " save old values of makeprg and errorformat which :compiler! " command may change let l:old_makeprg = &g:makeprg let l:old_errorformat = &g:errorformat " call :compiler! command to set vim compiler. We use :compiler! " but not :compiler because cgetexpr command uses global option " value of errorformat call s:SetGlobalVimCompiler(l:cur_filetype, l:chosen_compiler) let l:exit_code_tempfile = tempname() let s:run_result_tempfile = tempname() " The output is put into s:run_result_tempfile, and exit code is " written into l:exit_code_tempfile. The command should be " something like this on bash: " !(compiler_cmd compile_args; echo $? >tmp1) | tee tmp2 exec (l:show_result_after_run ? 'silent ' : ''). \ '!('.l:compile_cmd.' '.l:compile_args.'; '. \ 'echo '.s:GetShellLastExitCodeVariable().' >'. \ l:exit_code_tempfile.') '.s:GetShellPipe(1).' '. \ s:run_result_tempfile " if the exit code couldn't be obtained or the exit code is not 0, " l:toret is set to 3 (this return value is for interpreting " language only, means interpreting failed) let l:exit_code_str = readfile(l:exit_code_tempfile) if (len(l:exit_code_str) < 1) || \ (len(l:exit_code_str) >= 1 && \ str2nr(l:exit_code_str[0])) echo ' ' call s:ShowMessage( \ 'Interpreter exit code is '.l:exit_code_str[0]) let l:toret = 3 endif cgetexpr readfile(s:run_result_tempfile) " recover the old makeprg and errorformat value let &g:makeprg = l:old_makeprg let &g:errorformat = l:old_errorformat endif else " use quickfix for compiling language " change the makeprg and shellpipe temporarily let l:old_makeprg = &l:makeprg let l:old_shellpipe = &l:shellpipe let l:old_errorformat = &l:errorformat " call :compiler command to set vim compiler call s:SetVimCompiler(l:cur_filetype, l:chosen_compiler) let &l:makeprg = l:compile_cmd let &l:shellpipe = s:GetShellPipe(0) let l:prefix_args = '' let l:silentcompile = g:SingleCompile_silentcompileifshowquickfix && \ g:SingleCompile_showquickfixiferror && \ has("gui_running") if l:silentcompile let l:prefix_args = 'silent ' endif exec l:prefix_args.'make'.' '.l:compile_args " check whether compiling is successful, if not, show the return value " with error message highlighting and set the return value to 1 if v:shell_error != 0 if !l:silentcompile echo ' ' call s:ShowMessage( \ 'Compiler exit code is '.v:shell_error) endif let l:toret = 1 endif " set back makeprg and shellpipe let &l:makeprg = l:old_makeprg let &l:shellpipe = l:old_shellpipe let &l:errorformat = l:old_errorformat endif " if it's interpreting language, and l:toret has not been set, then return " 2 (means do not call run if user uses SCCompileRun command if l:toret == 0 && s:IsLanguageInterpreting(l:cur_filetype) let l:toret = 2 endif " call the post-do function if set if has_key(s:CompilerTemplate[l:cur_filetype][l:chosen_compiler], \'post-do') let l:TmpFunc = s:CompilerTemplate[l:cur_filetype][l:chosen_compiler][ \'post-do'] call l:TmpFunc({'command': l:compile_cmd, 'args': l:compile_args}) endif " switch back to the original directory exec 'lcd '.escape(l:cwd, s:CharsEscape) " show the quickfix window if error occurs, quickfix is used and " g:SingleCompile_showquickfixiferror is set to nonzero if g:SingleCompile_showquickfixiferror && s:ShouldQuickfixBeUsed() " We have error if l:toret == 1 || l:toret == 3 " workaround when the compiler file is broken exec g:SingleCompile_quickfixwindowposition . ' cope' " We may have warning elseif g:SingleCompile_showquickfixifwarning exec g:SingleCompile_quickfixwindowposition . ' cw' endif endif " if we are running an interpreting language source file, and we want to " show the result window right after the run, and we are not running it " asynchronously, then we show or hide the result if l:show_result_after_run if l:toret == 2 call SingleCompile#ViewResult(0) redraw! elseif l:toret == 1 || l:toret == 3 call SingleCompile#CloseViewResult() endif endif return l:toret endfunction function! s:CompareCompilerPriority(compiler1, compiler2) " {{{1 " Compare the priorities of two compiler. The lang_name is determinted by " s:lang_name_compare_compiler_priority (if it is presented) or &filetype if exists('s:lang_name_compare_compiler_priority') let l:lang_name = s:lang_name_compare_compiler_priority else let l:lang_name = &filetype endif if has_key(s:CompilerTemplate[l:lang_name][a:compiler1], 'priority') let l:compiler1_priority = \ s:CompilerTemplate[l:lang_name][a:compiler1]['priority'] else let l:compiler1_priority = 100 endif if has_key(s:CompilerTemplate[l:lang_name][a:compiler2], 'priority') let l:compiler2_priority = \ s:CompilerTemplate[l:lang_name][a:compiler2]['priority'] else let l:compiler2_priority = 100 endif return l:compiler1_priority == l:compiler2_priority ? 0 : ( \ l:compiler1_priority > l:compiler2_priority ? 1 : -1) endfunction function! s:DetectCompiler(lang_name) " {{{1 " to detect compilers for one language. Return available compilers sorted " by priority let l:toret = [] " call the compiler detection function to get the compilation command for some_compiler in keys(s:CompilerTemplate[a:lang_name]) if some_compiler == 'chosen_compiler' continue endif " ignore this compiler if things are not all available (name, " detect_func, detect_func_arg, flags, run) let l:compiler_dict = s:CompilerTemplate[a:lang_name][some_compiler] if !(has_key(l:compiler_dict, 'name') && \ has_key(l:compiler_dict, 'detect_func') && \ has_key(l:compiler_dict, 'detect_func_arg') && \ has_key(l:compiler_dict, 'flags') && \ has_key(l:compiler_dict, 'run')) continue endif let l:DetectFunc = s:CompilerTemplate[a:lang_name][some_compiler][ \'detect_func'] call s:SetCompilerSingleTemplate( \a:lang_name, \some_compiler, \'command', \l:DetectFunc( \s:CompilerTemplate[a:lang_name][some_compiler][ \'detect_func_arg'])) " if the type of s:CompilerTemplate[&filetype]['command'] returned " by the detection function is not a string, then we may think " that this compiler cannot be detected if type(s:CompilerTemplate[a:lang_name][some_compiler]['command']) == \type('') call add(l:toret, some_compiler) endif endfor " sort detected compilers by priority let s:lang_name_compare_compiler_priority = a:lang_name call sort(l:toret, 's:CompareCompilerPriority') return l:toret endfunction function! s:Run(async) " {{{1 " if async is non-zero, then run asynchronously " Get the current filetype. We do not use &filetype because quickfix may " open another file automatically, which may cause the filetype changed to " an incorrect value. let l:cur_filetype = s:cur_filetype let l:ret_val = 0 call s:Initialize() " whether we should use async mode let l:async = a:async && !empty(SingleCompileAsync#GetMode()) if !l:async && executable('tee') && g:SingleCompile_usetee \ && g:SingleCompile_showresultafterrun == 1 let l:show_result_after_run = 1 else let l:show_result_after_run = 0 endif if !(has_key(s:CompilerTemplate[l:cur_filetype][ \s:CompilerTemplate[l:cur_filetype]['chosen_compiler']], \'run')) call s:ShowMessage('Fail to run!') return 1 endif " save current working directory let l:cwd = getcwd() silent lcd %:p:h let l:run_cmd = s:Expand(s:GetCompilerSingleTemplate(l:cur_filetype, \ s:CompilerTemplate[l:cur_filetype]['chosen_compiler'], \ 'run'), 1) if l:async let l:ret_val = s:RunAsyncWithMessage(l:run_cmd) else if executable('tee') && g:SingleCompile_usetee " if tee is available, and we enabled the use of "tee", then " redirect the result to a temp file let s:run_result_tempfile = tempname() let l:run_cmd = l:run_cmd. \' '.s:GetShellPipe(1).' '.s:run_result_tempfile endif try exec (l:show_result_after_run ? 'silent ' : '').'!'.l:run_cmd catch call s:ShowMessage('Failed to execute "'.l:run_cmd.'"') endtry endif " switch back to the original directory exec 'lcd '.escape(l:cwd, s:CharsEscape) " if tee is available, and we are running synchronously, and we want to " show the result window right after the run, then we call " SingleCompile#ViewResult if l:show_result_after_run call SingleCompile#ViewResult(0) redraw! endif return l:ret_val endfunction function! s:CompileRunInternal(comp_param, async) " {{{1 " save the current path, thus if quickfix switches to another file, we can " still switch back to execute future code correctly let l:cur_filepath = expand('%:p') " if async run is not available, give an error message and stop here. if a:async && empty(SingleCompileAsync#GetMode()) call s:ShowMessage('Async mode is not available for your vim.') return endif " call different functions according to a:async if a:async let l:CompileFunc = function('SingleCompile#CompileAsync') else let l:CompileFunc = function('SingleCompile#Compile') endif if len(a:comp_param) == 1 let l:compile_result = l:CompileFunc(a:comp_param[0]) elseif len(a:comp_param) == 2 let l:compile_result = l:CompileFunc( \a:comp_param[0], a:comp_param[1]) else let l:compile_result = l:CompileFunc() endif if l:compile_result != 0 return endif let l:cur_filepath2 = expand('%:p') if l:cur_filepath != l:cur_filepath2 exec 'edit ' . l:cur_filepath endif " run the command and display the following messages only when the process " is successfully run. if !s:Run(a:async) && a:async echo 'SingleCompile: Now the program is running in background.' echo 'SingleCompile: you could use :SCViewResultAsync to see the ' \.'output if the program has terminated.' endif if l:cur_filepath != l:cur_filepath2 exec 'edit ' . l:cur_filepath2 endif endfunction function! SingleCompile#CompileRun(...) " compile and run {{{1 call s:CompileRunInternal(a:000, 0) endfunction function! SingleCompile#CompileRunAsync(...) " {{{1 " compile and run asynchronously call s:CompileRunInternal(a:000, 1) endfunction fun! SingleCompile#ChooseCompiler(lang_name, ...) " choose a compiler {{{1 call s:Initialize() if a:0 > 1 call s:ShowMessage( \'Too many argument for SingleCompile#ChooseCompiler!') return endif if a:0 == 1 " a:0 == 1 means the user has specified a compiler to choose if type(a:1) != type('') call s:ShowMessage( \'SingleCompile#ChooseCompiler argument error') return endif " If the current language template is set, then we check whether it is " available on the system; If not, then an error message is given. If " the current language template is not set, then we give an error " message directly. if has_key(s:CompilerTemplate, a:lang_name) && \has_key(s:CompilerTemplate[a:lang_name], a:1) " current language template is set let l:detected_compilers = s:DetectCompiler(a:lang_name) if count(l:detected_compilers, a:1) == 0 " if a:1 is not a detected compiler call s:ShowMessage('"'. \a:1.'" is not available on your system.') return endif let s:CompilerTemplate[a:lang_name]['chosen_compiler'] = a:1 else " current language template is not set call s:ShowMessage( \'The template of the compiler/interpreter "'. \a:1.'" is not set.') endif return endif " when no argument is provided, list all available compilers and " interpreters for user to choose if a:0 == 0 " if the language template is not defined for this language, show an " error message then return if !has_key(s:CompilerTemplate, a:lang_name) call s:ShowMessage('Language template for "'. \a:lang_name.'" is not defined on your system.') return endif let l:detected_compilers = s:DetectCompiler(a:lang_name) " used to store the detected compilers let l:choose_list = [] " used to filled with compiler names to be displayed in front of user let l:choose_list_display = [] let l:count = 1 if !has_key(s:CompilerTemplate[a:lang_name], 'chosen_compiler') let s:CompilerTemplate[a:lang_name]['chosen_compiler'] = \ get(l:detected_compilers, 0) endif for some_compiler in sort(keys(s:CompilerTemplate[a:lang_name])) if some_compiler == 'chosen_compiler' continue endif if count(l:detected_compilers, some_compiler) > 0 " if the compiler is detected, then add it to the choose_list, " which would be displayed then call add(l:choose_list, some_compiler) call add(l:choose_list_display, \l:count.'. '.some_compiler.'('. \s:CompilerTemplate[a:lang_name][some_compiler][ \'name']. \')') let l:count += 1 endif endfor " if l:choose_list is empty, it means no compiler is available for " this language if empty(l:choose_list) call s:ShowMessage( \'No compiler is available for this language!') return endif " display current compiler/interpreter echo "Current Compiler/Interpreter: " . \ s:CompilerTemplate[a:lang_name]['chosen_compiler'] let l:user_choose = inputlist( extend(['Detected compilers: '], \l:choose_list_display) ) " If user cancels the choosing, then return directly; if user chooses " a number which is too big, then echo an empty line first and then " show an error message, then return if l:user_choose <= 0 return elseif l:user_choose > len(l:choose_list_display) echo ' ' call s:ShowMessage( \'The number you have chosen is invalid.') return endif let s:CompilerTemplate[a:lang_name]['chosen_compiler'] = \get(l:choose_list, l:user_choose-1) return endif endfunction function! SingleCompile#ViewResult(async) " view the running result {{{1 " split a window below and put the result there " don't show result if s:run_result_tempfile is empty for synchronous run " result and the mode hasn't been set or the process is still running in " background if (!a:async && empty(s:run_result_tempfile)) \|| (a:async \&& (empty(SingleCompileAsync#GetMode()) \|| SingleCompileAsync#IsRunning())) return endif " if the async output cannot be obtained, give an error message and return " directly if a:async let l:async_out = SingleCompileAsync#GetOutput() if type(l:async_out) == type(0) call s:ShowMessage( \'Failed to get the output of the '. \'process running asynchronously.') return endif endif call s:Initialize() let l:result_bufnr = bufnr('__SINGLE_COMPILE_RUN_RESULT__') " if the __SINGLE_COMPILE_RUN_RESULT__ buffer doesn't exist, make one " else clear it, but leave it there to be refilled if l:result_bufnr == -1 exec 'keepalt rightbelow '.g:SingleCompile_resultsize. \g:SingleCompile_split.' __SINGLE_COMPILE_RUN_RESULT__' setl noswapfile buftype=nofile bufhidden=wipe foldcolumn=0 nobuflisted else let l:result_bufwinnr = bufwinnr(l:result_bufnr) exec l:result_bufwinnr.'wincmd w' let l:save_cursor = getpos(".") setl modifiable normal! ggdG setl nomodifiable endif setl modifiable if a:async call append(0, l:async_out) else call append(0, readfile(s:run_result_tempfile)) endif nnoremap q :q setl nomodifiable if l:result_bufnr != -1 call setpos('.', l:save_cursor) endif exec 'wincmd p' endfunction function! SingleCompile#CloseViewResult() " close the last result {{{1 let l:result_bufnr = bufnr('__SINGLE_COMPILE_RUN_RESULT__') if l:result_bufnr != -1 exec bufwinnr(l:result_bufnr).'wincmd w' if l:result_bufnr == bufnr('%') " if we got there quit endif endif endfunction call s:Initialize() " {{{1 call the initialize function " }}} let &cpo = s:saved_cpo unlet! s:saved_cpo " vim703: cc=78 " vim: fdm=marker et ts=4 tw=78 sw=4 fdc=3