1
0
mirror of https://github.com/amix/vimrc synced 2025-07-07 08:45:00 +08:00

I don't know how to ignore autoformatting *.h.in files, so change into autoformat files manually.

This commit is contained in:
Kurtis Moxley
2022-06-05 22:09:27 +08:00
parent e371e16382
commit 27c01e54fa
23 changed files with 2776 additions and 1 deletions

View File

@ -0,0 +1,128 @@
" ==============================================================================
" Location: autoload/cmake/build.vim
" Description: Functions for building a project
" ==============================================================================
let s:build = {}
let s:buildsys = cmake#buildsys#Get()
let s:const = cmake#const#Get()
let s:logger = cmake#logger#Get()
let s:quickfix = cmake#quickfix#Get()
let s:system = cmake#system#Get()
let s:terminal = cmake#terminal#Get()
""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
" Private functions and callbacks
""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
" Get dictionary of build arguments from command-line string.
"
" Params:
" argstring : String
" command-line arguments, like target and additional build options
"
" Returns:
" Dictionary
" CMake build options, target and native options
"
" Example:
" argstring = --jobs 4 all -- VERBOSE=1
" return = {
" \ 'cmake_build_options': ['--jobs', '4'],
" \ 'target': ['--target', 'all'],
" \ 'native_build_options': ['VERBOSE=1']
" \ }
"
function! s:GetBuildArgs(argstring) abort
let l:argdict = {}
let l:arglist = split(a:argstring)
" Search arguments for one that matches the name of a target.
for l:t in s:buildsys.GetTargets()
let l:match_res = match(l:arglist, '\m\C^' . l:t)
if l:match_res != -1
" If found, get target and remove from list of arguments.
let l:target = l:arglist[l:match_res]
let l:argdict['target'] = ['--target', l:target]
call remove(l:arglist, l:match_res)
break
endif
endfor
" Search for command-line native build tool arguments.
let l:match_res = match(l:arglist, '\m\C^--$')
if l:match_res != -1
" Get command-line native build tool arguments and remove from list.
let l:argdict['native_build_options'] = l:arglist[l:match_res+1:]
" Remove from list of other arguments.
call remove(l:arglist, l:match_res, -1)
endif
" Get command-line CMake arguments.
let l:argdict['cmake_build_options'] = l:arglist
return l:argdict
endfunction
" Generate quickfix list after running build command.
"
function! s:GenerateQuickfix() abort
call s:quickfix.Generate(s:terminal.GetOutput())
endfunction
""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
" Public functions
""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
" Build a project using the generated buildsystem.
"
" Params:
" clean : Boolean
" whether to clean before building
" argstring : String
" build target and other options
"
function! s:build.Build(clean, argstring) abort
call s:logger.LogDebug('Invoked: build.Build(%s, %s)',
\ a:clean, string(a:argstring))
let l:path_to_current_config = s:buildsys.GetPathToCurrentConfig()
let l:build_dir = s:system.Path([l:path_to_current_config], v:true)
let l:command = [g:cmake_command, '--build', l:build_dir]
let l:options = {}
" Parse additional options.
let l:options = s:GetBuildArgs(a:argstring)
" Add CMake build options to the command.
let l:command += g:cmake_build_options
let l:command += get(l:options, 'cmake_build_options', [])
if a:clean
let l:command += ['--clean-first']
endif
" Add target to the command, if any was provided.
let l:command += get(l:options, 'target', [])
" Add native build tool options to the command.
if len(g:cmake_native_build_options) > 0 ||
\ len(get(l:options, 'native_build_options', [])) > 0
let l:command += ['--']
let l:command += g:cmake_native_build_options
let l:command += get(l:options, 'native_build_options', [])
endif
" Run build command.
call s:terminal.Run(l:command, 'build',
\ [function('s:GenerateQuickfix')],
\ [function('s:GenerateQuickfix')],
\ ['CMakeBuildSucceeded'], ['CMakeBuildFailed']
\ )
endfunction
" Install a project.
"
function! s:build.Install() abort
call s:logger.LogDebug('Invoked: build.Install()')
let l:path_to_current_config = s:buildsys.GetPathToCurrentConfig()
let l:build_dir = s:system.Path([l:path_to_current_config], v:true)
let l:command = [g:cmake_command, '--install', l:build_dir]
call s:terminal.Run(l:command, 'install', [], [], [], [])
endfunction
" Get build 'object'.
"
function! cmake#build#Get() abort
return s:build
endfunction

View File

@ -0,0 +1,443 @@
" ==============================================================================
" Location: autoload/cmake/buildsys.vim
" Description: Functions for generating the buildsystem
" ==============================================================================
let s:buildsys = {}
let s:buildsys.cmake_version = 0
let s:buildsys.project_root = ''
let s:buildsys.current_config = ''
let s:buildsys.path_to_current_config = ''
let s:buildsys.configs = []
let s:buildsys.targets = []
let s:logger = cmake#logger#Get()
let s:statusline = cmake#statusline#Get()
let s:system = cmake#system#Get()
let s:terminal = cmake#terminal#Get()
""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
" Private functions
""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
" Find project root by looking for g:cmake_root_markers upwards.
"
" Returns:
" String
" escaped path to the root of the project
"
function! s:FindProjectRoot() abort
let l:root = getcwd()
let l:escaped_cwd = fnameescape(getcwd())
for l:marker in g:cmake_root_markers
" Search CWD upward for l:marker, assuming it is a file.
let l:marker_path = findfile(l:marker, l:escaped_cwd . ';' . $HOME)
if len(l:marker_path) > 0
" If found, strip l:marker from it.
let l:root = fnamemodify(l:marker_path, ':h')
break
endif
" Search CWD upward for l:marker, assuming it is a directory.
let l:marker_path = finddir(l:marker, l:escaped_cwd . ';' . $HOME)
if len(l:marker_path) > 0
" If found, strip l:marker from it.
let l:root = fnamemodify(l:marker_path, ':h')
break
endif
endfor
return l:root
endfunction
" Get absolute path to location where the build directory is located.
"
" Returns:
" String
" path to build directory location
"
function! s:GetBuildDirLocation() abort
return s:system.Path(
\ [s:buildsys.project_root, g:cmake_build_dir_location], v:false)
endfunction
" Find CMake variable in list of options.
"
" Params:
" opts : List
" list of options
" variable : String
" variable to find
"
" Returns:
" String
" value of the CMake variable, or an empty string if the variable was
" not found
"
" Example:
" to find the variable 'CMAKE_BUILD_TYPE', which would be passed by the user
" as '-D CMAKE_BUILD_TYPE=<value>', call
" s:FindVarInOpts(opts, 'CMAKE_BUILD_TYPE')
"
function! s:FindVarInOpts(opts, variable) abort
if len(a:opts) > 0
" Search the list of command-line options for an entry matching
" '-D <variable>=<value>' or '-D <variable>:<type>=<value>' or
" '-D<variable>=<value>' or '-D<variable>:<type>=<value>'.
let l:opt = matchstr(a:opts, '\m\C-D\s*' . a:variable)
" If found, return the value, otherwise return an empty string.
if len(l:opt) > 0
return split(l:opt, '=')[1]
else
return ''
endif
endif
endfunction
" Process build configuration.
"
" Params:
" opts : List
" list of options
"
function! s:ProcessBuildConfig(opts) abort
let l:config = s:buildsys.current_config
" Check if the first entry of the list of command-line options starts with a
" letter (and not with a dash), in which case the user will have passed the
" name of the build configuration as the first option.
if (len(a:opts) > 0) && (match(a:opts[0], '\m\C^\w') >= 0)
" Update build config name and remove from list of options.
let l:config = a:opts[0]
call s:SetCurrentConfig(l:config)
call remove(a:opts, 0)
endif
" If the list of command-line options does not contain an explicit value for
" the 'CMAKE_BUILD_TYPE' variable, add it.
if s:FindVarInOpts(a:opts, 'CMAKE_BUILD_TYPE') ==# ''
call add(a:opts, '-D CMAKE_BUILD_TYPE=' . l:config)
endif
endfunction
" Get list of command-line options from string of arguments.
"
" Params:
" argstring : String
" string containing command-line arguments
"
" Returns:
" List
" list of unprocessed command-line options
"
" Example:
" an argument string like the following
" 'Debug -D VAR_A=1 -DVAR_B=0 -Wdev -U VAR_C'
" results in a list of options like the following
" ['Debug', '-D VAR_A=1', '-DVAR_B=0', '-Wdev', '-U VAR_C']
"
function! s:ArgStringToOptList(argstring) abort
let l:opts = []
for l:arg in split(a:argstring)
" If list of options is empty, append first argument.
if len(l:opts) == 0
call add(l:opts, l:arg)
" If argument starts with a dash, append it to the list of options.
elseif match(l:arg, '\m\C^-') >= 0
call add(l:opts, l:arg)
" If argument does not start with a dash, it must belong to the last
" option that was added to the list, thus extend that option.
else
let l:opts[-1] = join([l:opts[-1], l:arg])
endif
endfor
return l:opts
endfunction
" Process string of arguments and return parsed options.
"
" Params:
" argstring : String
" string containing command-line arguments
"
" Returns:
" Dictionary
" opts : List
" list of options
" source_dir : String
" path to source directory
" build_dir : String
" path to build directory
"
function! s:ProcessArgString(argstring) abort
let l:opts = s:ArgStringToOptList(a:argstring)
call s:ProcessBuildConfig(l:opts)
" If compile commands are to be exported, and the
" 'CMAKE_EXPORT_COMPILE_COMMANDS' variable is not set, set it.
if g:cmake_link_compile_commands
if s:FindVarInOpts(l:opts, 'CMAKE_EXPORT_COMPILE_COMMANDS') ==# ''
call add(l:opts, '-D CMAKE_EXPORT_COMPILE_COMMANDS=ON')
endif
endif
" Set source and build directories. Must be done after processing the build
" configuration so that the current build configuration is up to date before
" setting the build directory.
let l:source_dir = s:system.Path([s:buildsys.project_root], v:true)
let l:build_dir = s:system.Path([s:buildsys.path_to_current_config], v:true)
" Return dictionary of options.
let l:optdict = {}
let l:optdict.opts = l:opts
let l:optdict.source_dir = l:source_dir
let l:optdict.build_dir = l:build_dir
return l:optdict
endfunction
" Refresh list of build configuration directories.
"
function! s:RefreshConfigs() abort
" List of directories inside of which a CMakeCache file is found.
let l:cache_dirs = findfile(
\ 'CMakeCache.txt',
\ s:GetBuildDirLocation() . '/**1',
\ -1)
" Transform paths to just names of directories. These will be the names of
" existing configuration directories.
call map(l:cache_dirs, {_, val -> fnamemodify(val, ':h:t')})
let s:buildsys.configs = l:cache_dirs
call s:logger.LogDebug('Build configs: %s', s:buildsys.configs)
endfunction
" Callback for RefreshTargets().
"
function! s:RefreshTargetsCb(...) abort
let l:data = s:system.ExtractStdoutCallbackData(a:000)
for l:line in l:data
if match(l:line, '\m\C\.\.\.\s') == 0
let l:target = split(l:line)[1]
let s:buildsys.targets += [l:target]
endif
endfor
endfunction
" Refresh list of available CMake targets.
"
function! s:RefreshTargets() abort
let s:buildsys.targets = []
let l:build_dir = s:buildsys.path_to_current_config
let l:command = [g:cmake_command,
\ '--build', l:build_dir,
\ '--target', 'help'
\ ]
call s:system.JobRun(
\ l:command, v:true, function('s:RefreshTargetsCb'), v:null, v:false)
endfunction
" Check if build configuration directory exists.
"
" Params:
" config : String
" configuration to check
"
" Returns:
" Boolean
" v:true if the build configuration exists, v:false otherwise
"
function! s:ConfigExists(config) abort
return index(s:buildsys.configs, a:config) >= 0
endfunction
" Set current build configuration.
"
" Params:
" config : String
" build configuration name
"
function! s:SetCurrentConfig(config) abort
let s:buildsys.current_config = a:config
let l:path = s:system.Path([s:GetBuildDirLocation(), a:config], v:false)
let s:buildsys.path_to_current_config = l:path
call s:logger.LogInfo('Current config: %s (%s)',
\ s:buildsys.current_config,
\ s:buildsys.path_to_current_config
\ )
call s:statusline.SetBuildInfo(s:buildsys.current_config)
endfunction
" Link compile commands from source directory to build directory.
"
function! s:LinkCompileCommands() abort
if !g:cmake_link_compile_commands
return
endif
let l:target = s:system.Path(
\ [s:buildsys.path_to_current_config, 'compile_commands.json'],
\ v:true
\ )
let l:link = s:system.Path(
\ [s:buildsys.project_root, 'compile_commands.json'],
\ v:true,
\ )
let l:command = [g:cmake_command, '-E', 'create_symlink', l:target, l:link]
call s:system.JobRun(l:command, v:true, v:null, v:null, v:false)
endfunction
""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
" Public functions
""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
" Generate a buildsystem for the project using CMake.
"
" Params:
" clean : Boolean
" whether to clean before generating
" argstring : String
" build configuration and additional CMake options
"
function! s:buildsys.Generate(clean, argstring) abort
call s:logger.LogDebug('Invoked: buildsys.Generate(%s, %s)',
\ a:clean, string(a:argstring))
let l:command = [g:cmake_command]
let l:optdict = s:ProcessArgString(a:argstring)
" Construct command.
call extend(l:command, g:cmake_generate_options)
call extend(l:command, l:optdict.opts)
if l:self.cmake_version < 313
call add(l:command, '-H' . l:optdict.source_dir)
call add(l:command, '-B' . l:optdict.build_dir)
else
call add(l:command, '-S ' . l:optdict.source_dir)
call add(l:command, '-B ' . l:optdict.build_dir)
endif
" Clean project buildsystem, if requested.
if a:clean
call l:self.Clean()
endif
" Run generate command.
call s:terminal.Run(
\ l:command, 'generate',
\ [
\ function('s:RefreshConfigs'),
\ function('s:RefreshTargets'),
\ function('s:LinkCompileCommands')
\ ],
\ [function('s:RefreshConfigs')],
\ [], []
\ )
endfunction
" Clean buildsystem.
"
function! s:buildsys.Clean() abort
call s:logger.LogDebug('Invoked: buildsys.Clean()')
if isdirectory(l:self.path_to_current_config)
call delete(l:self.path_to_current_config, 'rf')
endif
call s:RefreshConfigs()
endfunction
" Set current build configuration after checking that the configuration exists.
"
" Params:
" config : String
" build configuration name
"
function! s:buildsys.Switch(config) abort
call s:logger.LogDebug('Invoked: buildsys.Switch(%s)', a:config)
" Check that config exists.
if !s:ConfigExists(a:config)
call s:logger.EchoError(
\ "Build configuration '%s' not found, run ':CMakeGenerate %s'",
\ a:config, a:config)
call s:logger.LogError(
\ "Build configuration '%s' not found, run ':CMakeGenerate %s'",
\ a:config, a:config)
return
endif
call s:SetCurrentConfig(a:config)
call s:LinkCompileCommands()
endfunction
" Get list of configuration directories (containing a buildsystem).
"
" Returns:
" List
" list of existing configuration directories
"
function! s:buildsys.GetConfigs() abort
return l:self.configs
endfunction
" Get list of available build targets.
"
" Returns:
" List
" list of available build targets
"
function! s:buildsys.GetTargets() abort
if len(l:self.targets) == 0
call s:RefreshTargets()
endif
return l:self.targets
endfunction
" Get current build configuration.
"
" Returns:
" String
" build configuration
"
function! s:buildsys.GetCurrentConfig() abort
return l:self.current_config
endfunction
" Get path to CMake source directory of current project.
"
" Returns:
" String
" path to CMake source directory
"
function! s:buildsys.GetSourceDir() abort
return l:self.project_root
endfunction
" Get path to current build configuration.
"
" Returns:
" String
" path to build configuration
"
function! s:buildsys.GetPathToCurrentConfig() abort
return l:self.path_to_current_config
endfunction
" Get buildsys 'object'.
"
function! cmake#buildsys#Get() abort
return s:buildsys
endfunction
""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
" Initialization
""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
function! s:GetCMakeVersionCb(...) abort
let l:data = s:system.ExtractStdoutCallbackData(a:000)
for l:line in l:data
if match(l:line, '\m\C^cmake\S* version') == 0
let l:version_str = split(split(l:line)[2], '\.')
let l:major = str2nr(l:version_str[0])
let l:minor = str2nr(l:version_str[1])
let s:buildsys.cmake_version = l:major * 100 + l:minor
break
endif
endfor
endfunction
" Get CMake version. The version is stored as MAJOR * 100 + MINOR (e.g., version
" 3.13.3 would result in 313).
let s:command = [g:cmake_command, '--version']
call s:system.JobRun(
\ s:command, v:true, function('s:GetCMakeVersionCb'), v:null, v:false)
" Must be done before any other initial configuration.
let s:buildsys.project_root = s:system.Path([s:FindProjectRoot()], v:false)
call s:logger.LogInfo('Project root: %s', s:buildsys.project_root)
call s:SetCurrentConfig(g:cmake_default_config)
call s:RefreshConfigs()

View File

@ -0,0 +1,47 @@
" ==============================================================================
" Location: autoload/cmake/const.vim
" Description: Constants and definitions
" ==============================================================================
let s:const = {}
let s:const.plugin_version = '0.7.1'
let s:const.plugin_news = {
\ '0.2.0': ['Vim-CMake has a new feature, run `:help cmake-switch`'],
\ '0.3.0': ['Vim-CMake has a new feature, run `:help cmake-quickfix`'],
\ '0.4.0': ['Vim-CMake has a new config option `g:cmake_generate_options`'],
\ '0.5.0': ['Vim-CMake has a new feature, run `:help cmake-events`'],
\ '0.6.0': [
\ 'Vim-CMake has a new config option `g:cmake_build_dir_location`',
\ 'Vim-CMake has improved :CMakeGenerate, run `:help cmake-generate`'
\ ],
\ '0.7.0': [
\ 'Vim-CMake has new command `:CMakeStop`, run `:help cmake-stop`',
\ 'Vim-CMake has a new config option `g:cmake_console_echo_cmd`'
\ ],
\ }
let s:const.config_vars = {
\ 'cmake_command' : 'cmake',
\ 'cmake_default_config' : 'Debug',
\ 'cmake_build_dir_location' : '.',
\ 'cmake_generate_options' : [],
\ 'cmake_build_options' : [],
\ 'cmake_native_build_options' : [],
\ 'cmake_console_size' : 15,
\ 'cmake_console_position' : 'botright',
\ 'cmake_console_echo_cmd' : 1,
\ 'cmake_jump' : 0,
\ 'cmake_jump_on_completion' : 0,
\ 'cmake_jump_on_error' : 1,
\ 'cmake_link_compile_commands' : 0,
\ 'cmake_root_markers' : ['.git', '.svn'],
\ 'cmake_log_file' : '',
\ }
" Get const 'object'.
"
function! cmake#const#Get() abort
return s:const
endfunction

View File

@ -0,0 +1,132 @@
" ==============================================================================
" Location: autoload/cmake/logger.vim
" Description: Logger
" ==============================================================================
let s:logger = {}
function! s:Echo(fmt, arglist) abort
" Trick to convert list (a:arglist) into arguments for printf().
let l:PrintfPartial = function('printf', [a:fmt] + a:arglist)
echomsg '[Vim-CMake] ' . l:PrintfPartial()
endfunction
function! s:Log(fmt, level, arglist) abort
" Trick to convert list (a:arglist) into arguments for printf().
let l:PrintfPartial = function('printf', [a:fmt] + a:arglist)
let l:logstring = printf(
\ '[%s] [%5s] %s',
\ strftime('%Y-%m-%d %T'),
\ a:level,
\ l:PrintfPartial()
\ )
call writefile([l:logstring], g:cmake_log_file, 'a')
endfunction
""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
" Public functions
""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
" Log a debug message.
"
" Params:
" fmt : String
" printf-like format string (see :help printf())
" ... :
" list of arguments to replace placeholders in format string
"
function! s:logger.LogDebug(fmt, ...) abort
if g:cmake_log_file !=# ''
call s:Log(a:fmt, 'DEBUG', a:000)
end
endfunction
" Log an information message.
"
" Params:
" fmt : String
" printf-like format string (see :help printf())
" ... :
" list of arguments to replace placeholders in format string
"
function! s:logger.LogInfo(fmt, ...) abort
if g:cmake_log_file !=# ''
call s:Log(a:fmt, 'INFO', a:000)
end
endfunction
" Log a warning message.
"
" Params:
" fmt : String
" printf-like format string (see :help printf())
" ... :
" list of arguments to replace placeholders in format string
"
function! s:logger.LogWarn(fmt, ...) abort
if g:cmake_log_file !=# ''
call s:Log(a:fmt, 'WARN', a:000)
end
endfunction
" Log an error message.
"
" Params:
" fmt : String
" printf-like format string (see :help printf())
" ... :
" list of arguments to replace placeholders in format string
"
function! s:logger.LogError(fmt, ...) abort
if g:cmake_log_file !=# ''
call s:Log(a:fmt, 'ERROR', a:000)
end
endfunction
" Echo an information message.
"
" Params:
" fmt : String
" printf-like format string (see :help printf())
" ... :
" list of arguments to replace placeholders in format string
"
function! s:logger.EchoInfo(fmt, ...) abort
echohl MoreMsg
call s:Echo(a:fmt, a:000)
echohl None
endfunction
" Echo a warning message.
"
" Params:
" fmt : String
" printf-like format string (see :help printf())
" ... :
" list of arguments to replace placeholders in format string
"
function! s:logger.EchoWarn(fmt, ...) abort
echohl WarningMsg
call s:Echo(a:fmt, a:000)
echohl None
endfunction
" Echo an error message.
"
" Params:
" fmt : String
" printf-like format string (see :help printf())
" ... :
" list of arguments to replace placeholders in format string
"
function! s:logger.EchoError(fmt, ...) abort
echohl Error
call s:Echo(a:fmt, a:000)
echohl None
endfunction
" Get logger 'object'
"
function! cmake#logger#Get() abort
return s:logger
endfunction

View File

@ -0,0 +1,56 @@
" ==============================================================================
" Location: autoload/cmake/quickfix.vim
" Description: Functions for populating the quickfix window
" ==============================================================================
let s:quickfix = {}
let s:quickfix.list = {}
let s:quickfix.list.items = []
let s:quickfix.list.title = 'CMakeBuild'
let s:quickfix.id = -1
let s:filters = [
\ 'v:val.valid == 1',
\ 'filereadable(bufname(v:val.bufnr))',
\ ]
let s:logger = cmake#logger#Get()
""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
" Public functions
""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
" Generate Quickfix list from lines
"
" Params:
" lines_to_parse : List
" list of lines to parse to generate Quickfix list
"
function! s:quickfix.Generate(lines_to_parse) abort
call s:logger.LogDebug('Invoked: s:quickfix.Generate()')
" Create a list of quickfix items from the output of the last command.
let l:list = getqflist({'lines': a:lines_to_parse})
let l:self.list.items = filter(l:list.items, join(s:filters, ' && '))
" If a quickfix list for Vim-CMake exists, make that list active and replace
" its items with the new ones.
if getqflist({'id': l:self.id}).id == l:self.id
let l:current = getqflist({'nr': 0}).nr
let l:target = getqflist({'id': l:self.id, 'nr': 0}).nr
if l:current > l:target
execute 'silent colder ' . (l:current - l:target)
elseif l:current < l:target
execute 'silent cnewer ' . (l:target - l:current)
endif
call setqflist([], 'r', {'items': l:self.list.items})
call s:logger.LogDebug('Replaced existing Quickfix list')
" Otherwise, create a new quickfix list.
else
call setqflist([], ' ', l:self.list)
call s:logger.LogDebug('Created new Quickfix list')
endif
let l:self.id = getqflist({'nr': 0, 'id': 0}).id
endfunction
function! cmake#quickfix#Get() abort
return s:quickfix
endfunction

View File

@ -0,0 +1,84 @@
" ==============================================================================
" Location: autoload/cmake/statusline.vim
" Description: Functions for handling statusline information
" ==============================================================================
let s:statusline = {}
let s:statusline.build_info = ''
let s:statusline.cmd_info = ''
let s:logger = cmake#logger#Get()
""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
" Public functions
""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
" Set build info string for statusline/airline.
"
" Params:
" build_info : String
" statusline build info
"
function! s:statusline.SetBuildInfo(build_info) abort
call s:logger.LogDebug('Invoked: statusline.SetBuildInfo(%s)', a:build_info)
let l:self.build_info = a:build_info
endfunction
" Set command info string for statusline/airline.
"
" Params:
" cmd_info : String
" statusline command info
"
function! s:statusline.SetCmdInfo(cmd_info) abort
call s:logger.LogDebug('Invoked: statusline.SetCmdInfo(%s)', a:cmd_info)
let l:self.cmd_info = a:cmd_info
endfunction
" Force a refresh of the statusline/airline.
"
function! s:statusline.Refresh() abort
if exists('g:loaded_airline') && g:loaded_airline
execute 'AirlineRefresh!'
else
execute 'redrawstatus!'
endif
endfunction
" Get build info string for statusline/airline.
"
" Params:
" active : Number
" whether called for the statusline of an active window
"
" Returns:
" String
" statusline build info
"
function! cmake#statusline#GetBuildInfo(active) abort
if a:active
return s:statusline.build_info
else
return '[' . s:statusline.build_info . ']'
endif
endfunction
" Get command info string for statusline/airline.
"
" Returns:
" String
" statusline command info (command currently running)
"
function! cmake#statusline#GetCmdInfo() abort
if len(s:statusline.cmd_info) > 0
return s:statusline.cmd_info
else
return ' '
endif
endfunction
" Get statusline 'object'.
"
function! cmake#statusline#Get() abort
return s:statusline
endfunction

View File

@ -0,0 +1,256 @@
" ==============================================================================
" Location: autoload/cmake/system.vim
" Description: System abstraction layer
" ==============================================================================
let s:system = {}
let s:stdout_partial_line = {}
""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
" Private functions
""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
function! s:ManipulateCommand(command) abort
let l:ret_command = []
for l:arg in a:command
" Remove double quotes around argument that are quoted. For instance,
" '-G "Unix Makefiles"' results in '-G Unix Makefiles'.
let l:quotes_regex = '\m\C\(^\|[^"\\]\)"\([^"]\|$\)'
let l:arg = substitute(l:arg, l:quotes_regex, '\1\2', 'g')
" Split arguments that are composed of an option (short '-O' or long
" '--option') and a follow-up string, where the option and the string
" are separated by a space.
let l:split_regex = '\m\C^\(-\w\|--\w\+\)\s\(.\+\)'
let l:match_list = matchlist(l:arg, l:split_regex)
if len(l:match_list) > 0
call add(l:ret_command, l:match_list[1])
call add(l:ret_command, l:match_list[2])
else
call add(l:ret_command, l:arg)
endif
endfor
return l:ret_command
endfunction
""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
" Public functions
""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
" Generate escaped path string from list of components.
"
" Params:
" components : List
" list of path components (strings)
" relative : Boolean
" whether to have the path relative to the current directory or absolute
"
" Returns:
" String
" escaped path string with appropriate path separators
"
function! s:system.Path(components, relative) abort
let l:components = a:components
let l:separator = has('win32') ? '\' : '/'
" Join path components and get absolute path.
let l:path = join(l:components, l:separator)
let l:path = simplify(l:path)
let l:path = fnamemodify(l:path, ':p')
" If path ends with separator, remove separator from path.
if match(l:path, '\m\C\' . l:separator . '$') != -1
let l:path = fnamemodify(l:path, ':h')
endif
" Reduce to relative path if requested.
if a:relative
" For some reason, reducing the path to relative returns an empty string
" if the path happens to be the same as CWD. Thus, only reduce the path
" to relative when it is not CWD, otherwise just return '.'.
if l:path ==# getcwd()
let l:path = '.'
else
let l:path = fnamemodify(l:path, ':.')
endif
endif
" Simplify and escape path.
let l:path = simplify(l:path)
let l:path = fnameescape(l:path)
return l:path
endfunction
" Run arbitrary job in the background.
"
" Params:
" command : List
" the command to be run, as a list of command and arguments
" wait : Boolean
" whether to wait for completion
" stdout_cb : Funcref
" stdout callback (can be v:null), which should take a variable number
" of arguments, and from which s:system.ExtractStdoutCallbackData(a:000)
" can be called to retrieve the stdout string
" exit_cb : Funcref
" exit callback (can be v:null), which should take a variable number of
" arguments, and from which s:system.ExtractExitCallbackData(a:000) can
" be called to retrieve the exit code
" pty : Boolean
" whether to allocate a pseudo terminal for the job
"
" Return:
" Number
" job id
"
function! s:system.JobRun(command, wait, stdout_cb, exit_cb, pty) abort
let l:options = {}
let l:options['pty'] = a:pty
let l:command = s:ManipulateCommand(a:command)
if has('nvim')
if a:stdout_cb isnot# v:null
let l:options['on_stdout'] = a:stdout_cb
endif
if a:exit_cb isnot# v:null
let l:options['on_exit'] = a:exit_cb
endif
" In some cases, the PTY in MS-Windows (ConPTY) uses ANSI escape
" sequences to move the cursor position (ESC[<n>;<m>H) rather than
" inseting newline characters. Setting the width of the PTY to be very
" large and the height to be as small as possible (but larger than 1)
" seems to circumvent this problem. Hacky, but it seems to work.
if has('win32')
let l:options['width'] = 10000
let l:options['height'] = 2
endif
let l:job_id = jobstart(l:command, l:options)
else
if a:stdout_cb isnot# v:null
let l:options['out_cb'] = a:stdout_cb
endif
if a:exit_cb isnot# v:null
let l:options['exit_cb'] = a:exit_cb
endif
let l:job_id = job_start(l:command, l:options)
endif
" Wait for job to complete, if requested.
if a:wait
call l:self.JobWait(l:job_id)
endif
return l:job_id
endfunction
" Wait for job to complete.
"
" Params:
" job_id : Number
" job id
"
function! s:system.JobWait(job_id) abort
if has('nvim')
call jobwait([a:job_id])
else
while job_status(a:job_id) ==# 'run'
execute 'sleep 5m'
endwhile
endif
endfunction
" Wait for job's channel to be closed.
"
" Params:
" job_id : Number
" job id
"
function! s:system.ChannelWait(job_id) abort
" Only makes sense in Vim currently.
if !has('nvim')
let l:chan_id = job_getchannel(a:job_id)
while ch_status(l:chan_id, {'part': 'out'}) !=# 'closed'
execute 'sleep 5m'
endwhile
endif
endfunction
" Stop job.
"
" Params:
" job_id : Number
" job id
"
function! s:system.JobStop(job_id) abort
try
if has('nvim')
call jobstop(a:job_id)
else
call job_stop(a:job_id)
endif
catch /.*/
endtry
endfunction
" Extract data from a job's stdout callback.
"
" Params:
" cb_arglist : List
" variable-size list of arguments as passed to the callback, which will
" differ between Neovim and Vim
"
" Returns:
" List
" stdout data, as a list of strings
"
function! s:system.ExtractStdoutCallbackData(cb_arglist) abort
let l:channel = a:cb_arglist[0]
let l:data = a:cb_arglist[1]
if has('nvim')
let l:eof = (l:data == [''])
" In Neovim, remove all the CR characters, which are returned when a
" pseudo terminal is allocated for the job.
call map(l:data, {_, val -> substitute(val, '\m\C\r', '', 'g')})
" The first and the last lines may be partial lines, thus they need to
" be joined on consecutive iterations. See :help channel-lines.
" When this function is called for the first time for a particular
" channel, allocate an empty partial line for that channel.
if !has_key(s:stdout_partial_line, l:channel)
let s:stdout_partial_line[l:channel] = ''
endif
" Append first entry of output list to partial line.
let s:stdout_partial_line[l:channel] .= remove(l:data, 0)
" If output list contains more entries, they are all complete lines
" except for the last entry. Return the saved partial line (which is now
" complete) and all the complete lines from the list, and save a new
" partial line (the last entry of the list).
if len(l:data) > 0
call insert(l:data, s:stdout_partial_line[l:channel])
let s:stdout_partial_line[l:channel] = remove(l:data, -1)
endif
" At the end of the stream of a channel, remove the dictionary entry for
" that channel.
if l:eof
call remove(s:stdout_partial_line, l:channel)
endif
else
" In Vim, l:data is a string, so we transform it to a list (consisting
" of a single element).
let l:data = [l:data]
endif
return l:data
endfunction
" Extract data from a system's exit callback.
"
" Params:
" cb_arglist : List
" variable-size list of arguments as passed to the callback, which will
" differ between Neovim and Vim
"
" Returns:
" Number
" exit code
"
function! s:system.ExtractExitCallbackData(cb_arglist) abort
return a:cb_arglist[1]
endfunction
" Get system 'object'.
"
function! cmake#system#Get() abort
return s:system
endfunction

View File

@ -0,0 +1,472 @@
" ==============================================================================
" Location: autoload/cmake/terminal.vim
" Description: Terminal abstraction layer
" ==============================================================================
let s:terminal = {}
let s:terminal.console_buffer = -1
let s:terminal.console_cmd_info = {
\ 'generate': 'Generating buildsystem...',
\ 'build': 'Building...',
\ 'install': 'Installing...',
\ 'NONE': '',
\ }
let s:terminal.console_cmd = {
\ 'id': -1,
\ 'running': v:false,
\ 'callbacks': [],
\ 'callbacks_err': [],
\ 'autocmds': [],
\ 'autocmds_err': [],
\ }
let s:terminal.console_cmd_output = []
let s:term_tty = ''
let s:term_id = -1
let s:term_chan_id = -1
let s:exit_term_mode = 0
let s:logger = cmake#logger#Get()
let s:statusline = cmake#statusline#Get()
let s:system = cmake#system#Get()
""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
" ANSI sequence filters
""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
" https://en.wikipedia.org/wiki/ANSI_escape_code
" https://docs.microsoft.com/en-us/windows/console/console-virtual-terminal-sequences
let s:ansi_esc = '\e'
let s:ansi_csi = s:ansi_esc . '\['
let s:ansi_st = '\(\%x07\|\\\)'
let s:pre_echo_filter = ''
let s:post_echo_filter = ''
let s:post_echo_rep_pat = ''
let s:post_echo_rep_sub = ''
" In ConPTY (MS-Windows), remove ANSI sequences that mess up the terminal before
" echoing data to the terminal:
" - 'erase screen' sequences
" - 'move cursor' sequences
if has('win32')
let s:pre_echo_filter = s:pre_echo_filter
\ . '\(' . s:ansi_csi . '\d*J' . '\)'
\ . '\|\(' . s:ansi_csi . '\(\d\+;\)*\d*H' . '\)'
endif
" Remove ANSI sequences for coloring and style after echoing to the terminal.
let s:post_echo_filter .= '\(' . s:ansi_csi . '\(\d\+;\)*\d*m' . '\)'
" Remove additional ANSI sequences returened by ConPTY (MS-Windows) after
" echoing to the terminal:
" - 'erase from cursor' sequences
" - 'erase from cursor to EOL' sequences
" - 'hide/show cursor' sequences
" - 'console title' sequences
if has('win32')
let s:post_echo_filter = s:post_echo_filter
\ . '\|\(' . s:ansi_csi . '\d*X' . '\)'
\ . '\|\(' . s:ansi_csi . 'K' . '\)'
\ . '\|\(' . s:ansi_csi . '?25[hl]' . '\)'
\ . '\|\(' . s:ansi_esc . '\]' . '0;.*' . s:ansi_st . '\)'
endif
" Replace 'move forward' sequences with spaces in ConPTY (MS-Windows) after
" echoing to the terminal
if has('win32')
let s:post_echo_rep_pat .= s:ansi_csi . '\(\d*\)C'
let s:post_echo_rep_sub .= '\=repeat('' '', submatch(1))'
endif
""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
" Private functions
""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
" Callback for the stdout of the command running in the Vim-CMake console.
"
function! s:ConsoleCmdStdoutCb(...) abort
let l:data = s:system.ExtractStdoutCallbackData(a:000)
call s:FilterStdoutPreEcho(l:data)
call s:TermEcho(l:data)
call s:FilterStdoutPostEcho(l:data)
" Save console output to list.
let s:terminal.console_cmd_output += l:data
endfunction
" Callback for the end of the command running in the Vim-CMake console.
"
function! s:ConsoleCmdExitCb(...) abort
call s:logger.LogDebug('Invoked console exit callback')
" Waiting for the job's channel to be closed ensures that all output has
" been processed. This is useful in Vim, where buffered stdout may still
" come in after entering this function.
call s:system.ChannelWait(s:terminal.console_cmd.id)
let l:error = s:system.ExtractExitCallbackData(a:000)
call s:OnCompleteCommand(l:error, v:false)
endfunction
" Enter terminal mode.
"
function! s:EnterTermMode() abort
if mode() !=# 't'
execute 'normal! i'
endif
endfunction
" Exit terminal mode.
"
function! s:ExitTermMode() abort
if mode() ==# 't'
call feedkeys("\<C-\>\<C-N>", 'n')
endif
endfunction
" Define actions to perform when completing/stopping a command.
"
function! s:OnCompleteCommand(error, stopped) abort
if a:error == 0
let l:callbacks = s:terminal.console_cmd.callbacks
let l:autocmds = s:terminal.console_cmd.autocmds
else
let l:callbacks = s:terminal.console_cmd.callbacks_err
let l:autocmds = s:terminal.console_cmd.autocmds_err
endif
" Reset state
let s:terminal.console_cmd.id = -1
let s:terminal.console_cmd.running = v:false
let s:terminal.console_cmd.callbacks = []
let s:terminal.console_cmd.callbacks_err = []
let s:terminal.console_cmd.autocmds = []
let s:terminal.console_cmd.autocmds_err = []
" Append empty line to terminal.
call s:TermEcho([''])
" Exit terminal mode if inside the Vim-CMake console window (useful for
" Vim). Otherwise the terminal mode is exited after WinEnter event.
if win_getid() == bufwinid(s:terminal.console_buffer)
call s:ExitTermMode()
else
let s:exit_term_mode = 1
endif
" Update statusline.
call s:statusline.SetCmdInfo(s:terminal.console_cmd_info['NONE'])
call s:statusline.Refresh()
" The rest of the tasks are not to be carried out if the running command was
" stopped by the user.
if a:stopped
return
endif
" Focus Vim-CMake console window, if requested.
if g:cmake_jump_on_completion
call s:terminal.Focus()
else
if a:error != 0 && g:cmake_jump_on_error
call s:terminal.Focus()
endif
endif
" Handle callbacks and autocmds.
" Note: Funcref variable names must start with a capital.
for l:Callback in l:callbacks
call s:logger.LogDebug('Callback invoked: %s()', l:Callback)
call l:Callback()
endfor
for l:autocmd in l:autocmds
call s:logger.LogDebug('Executing autocmd %s', l:autocmd)
execute 'doautocmd <nomodeline> User ' . l:autocmd
endfor
endfunction
" Define actions to perform when entering the Vim-CMake console window.
"
function! s:OnEnterConsoleWindow() abort
if winnr() == bufwinnr(s:terminal.console_buffer) && s:exit_term_mode
let s:exit_term_mode = 0
call s:ExitTermMode()
endif
endfunction
" Start arbitrary command with output to be displayed in Vim-CMake console.
"
" Params:
" command : List
" the command to be run, as a list of command and arguments
"
" Return:
" Number
" job id
"
function! s:ConsoleCmdStart(command) abort
let l:console_win_id = bufwinid(s:terminal.console_buffer)
" For Vim, must go back into Terminal-Job mode for the command's output to
" be appended to the buffer.
if !has('nvim')
call win_execute(l:console_win_id, 'call s:EnterTermMode()', '')
endif
" Run command.
let l:job_id = s:system.JobRun(
\ a:command, v:false, function('s:ConsoleCmdStdoutCb'),
\ function('s:ConsoleCmdExitCb'), v:true)
" For Neovim, scroll manually to the end of the terminal buffer while the
" command's output is being appended.
if has('nvim')
let l:buffer_length = nvim_buf_line_count(s:terminal.console_buffer)
call nvim_win_set_cursor(l:console_win_id, [l:buffer_length, 0])
endif
return l:job_id
endfunction
" Create Vim-CMake window.
"
" Returns:
" Number
" number of the created window
"
function! s:CreateConsoleWindow() abort
execute join([g:cmake_console_position, g:cmake_console_size . 'split'])
setlocal winfixheight
setlocal winfixwidth
call s:logger.LogDebug('Created console window')
endfunction
" Create Vim-CMake buffer and apply local settings.
"
" Returns:
" Number
" number of the created buffer
"
function! s:CreateConsoleBuffer() abort
execute 'enew'
call s:TermSetup()
nnoremap <buffer> <silent> cg :CMakeGenerate<CR>
nnoremap <buffer> <silent> cb :CMakeBuild<CR>
nnoremap <buffer> <silent> ci :CMakeInstall<CR>
nnoremap <buffer> <silent> cq :CMakeClose<CR>
nnoremap <buffer> <silent> <C-C> :CMakeStop<CR>
setlocal nonumber
setlocal norelativenumber
setlocal signcolumn=auto
setlocal nobuflisted
setlocal filetype=vimcmake
setlocal statusline=[CMake]
setlocal statusline+=\ %{cmake#statusline#GetBuildInfo(0)}
setlocal statusline+=\ %{cmake#statusline#GetCmdInfo()}
" Avoid error E37 on :CMakeClose in some Vim instances.
setlocal bufhidden=hide
augroup cmake
autocmd WinEnter <buffer> call s:OnEnterConsoleWindow()
augroup END
return bufnr()
call s:logger.LogDebug('Created console buffer')
endfunction
" Setup Vim-CMake console terminal.
"
function! s:TermSetup() abort
" Open job-less terminal to echo command outputs to.
let l:options = {}
if has('nvim')
let s:term_chan_id = nvim_open_term(bufnr(''), l:options)
else
let l:options['curwin'] = 1
let l:term = term_start('NONE', l:options)
let s:term_id = term_getjob(l:term)
let s:term_tty = job_info(s:term_id)['tty_in']
call term_setkill(l:term, 'term')
endif
endfunction
" Echo strings to terminal.
"
" Params:
" data : List
" list of strings to echo
"
function! s:TermEcho(data) abort
if len(a:data) == 0
return
endif
if has('nvim')
call chansend(s:term_chan_id, join(a:data, "\r\n") . "\r\n")
else
call writefile(a:data, s:term_tty)
endif
endfunction
" Filter stdout data to remove ANSI sequences that should not be sent to the
" console terminal.
"
" Params:
" data : List
" list of stdout strings to filter (filtering is done in-place)
"
function! s:FilterStdoutPreEcho(data) abort
if s:pre_echo_filter !=# ''
call map(a:data, {_, val -> substitute(
\ val, s:pre_echo_filter, '', 'g')})
endif
endfunction
" Filter stdout data to remove remaining ANSI sequences after sending the data
" to the console terminal.
"
" Params:
" data : List
" list of stdout strings to filter (filtering is done in-place)
"
function! s:FilterStdoutPostEcho(data) abort
if s:post_echo_filter !=# ''
call map(a:data, {_, val -> substitute(
\ val, s:post_echo_filter, '', 'g')})
endif
if s:post_echo_rep_pat !=# ''
call map(a:data, {_, val -> substitute(
\ val, s:post_echo_rep_pat, s:post_echo_rep_sub, 'g')})
endif
endfunction
""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
" Public functions
""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
" Open Vim-CMake console window.
"
" Params:
" clear : Boolean
" if set, a new buffer is created and the old one is deleted
"
function! s:terminal.Open(clear) abort
call s:logger.LogDebug('Invoked: terminal.Open(%s)', a:clear)
let l:original_win_id = win_getid()
let l:cmake_win_id = bufwinid(l:self.console_buffer)
if l:cmake_win_id == -1
" If a Vim-CMake window does not exist, create it.
call s:CreateConsoleWindow()
if bufexists(l:self.console_buffer)
" If a Vim-CMake buffer exists, open it in the Vim-CMake window, or
" delete it if a:clear is set.
if !a:clear
execute 'b ' . l:self.console_buffer
call win_gotoid(l:original_win_id)
return
else
execute 'bd! ' . l:self.console_buffer
endif
endif
" Create Vim-CMake buffer if none exist, or if the old one was deleted.
let l:self.console_buffer = s:CreateConsoleBuffer()
else
" If a Vim-CMake window exists, and a:clear is set, create a new
" Vim-CMake buffer and delete the old one.
if a:clear
let l:old_buffer = l:self.console_buffer
call l:self.Focus()
let l:self.console_buffer = s:CreateConsoleBuffer()
if bufexists(l:old_buffer) && l:old_buffer != l:self.console_buffer
execute 'bd! ' . l:old_buffer
endif
endif
endif
if l:original_win_id != win_getid()
call win_gotoid(l:original_win_id)
endif
endfunction
" Focus Vim-CMake console window.
"
function! s:terminal.Focus() abort
call s:logger.LogDebug('Invoked: terminal.Focus()')
call win_gotoid(bufwinid(l:self.console_buffer))
endfunction
" Close Vim-CMake console window.
"
function! s:terminal.Close() abort
call s:logger.LogDebug('Invoked: terminal.Close()')
if bufexists(l:self.console_buffer)
let l:cmake_win_id = bufwinid(l:self.console_buffer)
if l:cmake_win_id != -1
execute win_id2win(l:cmake_win_id) . 'wincmd q'
endif
endif
endfunction
" Run arbitrary command in the Vim-CMake console.
"
" Params:
" command : List
" the command to be run, as a list of command and arguments
" tag : String
" command tag, must be an item of keys(l:self.console_cmd_info)
" cbs : List
" list of callbacks (Funcref) to be invoked upon successful completion
" of the command
" cbs_err : List
" list of callbacks (Funcref) to be invoked upon unsuccessful completion
" of the command
" aus : List
" list of autocmds (String) to be invoked upon successful completion of
" the command
" aus_err : List
" list of autocmds (String) to be invoked upon unsuccessful completion
" of the command
"
function! s:terminal.Run(command, tag, cbs, cbs_err, aus, aus_err) abort
call s:logger.LogDebug('Invoked: terminal.Run(%s, %s, %s, %s, %s, %s)',
\ a:command, string(a:tag), a:cbs, a:cbs_err, a:aus, a:aus_err)
call assert_notequal(index(keys(l:self.console_cmd_info), a:tag), -1)
" Prevent executing this function when a command is already running
if l:self.console_cmd.running
call s:logger.EchoError('Another CMake command is already running')
call s:logger.LogError('Another CMake command is already running')
return
endif
let l:self.console_cmd.running = v:true
let l:self.console_cmd.callbacks = a:cbs
let l:self.console_cmd.callbacks_err = a:cbs_err
let l:self.console_cmd.autocmds = a:aus
let l:self.console_cmd.autocmds_err = a:aus_err
let l:self.console_cmd_output = []
" Open Vim-CMake console window.
call l:self.Open(v:false)
" Echo start message to terminal.
if g:cmake_console_echo_cmd
call s:TermEcho([printf(
\ '%sRunning command: %s%s',
\ "\e[1;35m",
\ join(a:command),
\ "\e[0m")
\ ])
endif
" Run command.
call s:statusline.SetCmdInfo(l:self.console_cmd_info[a:tag])
let l:self.console_cmd.id = s:ConsoleCmdStart(a:command)
" Jump to Vim-CMake console window if requested.
if g:cmake_jump
call l:self.Focus()
endif
endfunction
" Stop command currently running in the Vim-CMake console.
"
function! s:terminal.Stop() abort
call s:logger.LogDebug('Invoked: terminal.Stop()')
call s:system.JobStop(l:self.console_cmd.id)
call s:OnCompleteCommand(0, v:true)
endfunction
" Get output from the last command run.
"
" Returns
" List:
" output from the last command, as a list of strings
"
function! s:terminal.GetOutput() abort
return l:self.console_cmd_output
endfunction
" Get terminal 'object'.
"
function! cmake#terminal#Get() abort
return s:terminal
endfunction

View File

@ -0,0 +1,81 @@
" ==============================================================================
" Location: autoload/cmake/util.vim
" Description: Utility functions
" ==============================================================================
let s:logger = cmake#logger#Get()
let s:system = cmake#system#Get()
let s:repo_dir = expand('<sfile>:p:h:h:h')
let s:data_dir = s:system.Path([s:repo_dir, '.data'], v:false)
let s:data_file = s:system.Path([s:data_dir, 'previous-version.bin'], v:false)
""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
" Private functions
""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
function! s:VersionToNumber(_, version) abort
let l:version = split(a:version, '\.')
let l:major = str2nr(l:version[0])
let l:minor = str2nr(l:version[1])
let l:patch = str2nr(l:version[2])
let l:number = l:major * 10000 + l:minor * 100 + l:patch
return l:number
endfunction
function! s:NumberToVersion(number) abort
let l:major = a:number / 10000
let l:minor = (a:number - l:major * 10000) / 100
let l:patch = a:number - l:major * 10000 - l:minor * 100
let l:version = l:major . '.' . l:minor . '.' . l:patch
return l:version
endfunction
""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
" Public functions
""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
" Print news of newer Vim-CMake versions.
"
" Params:
" current_version : String
" current version of the plugin (in the format <major>.<minor>.<patch>)
" news : Dictionary
" dictionary of news, where a key identifies a version (in the format
" <major>.<minor>.<patch>), and a value is a string containing the news
" to print for a version
"
function! cmake#util#PrintNews(current_version, news) abort
" Make a list of all version numbers, transform to integers, and sort.
let l:all_version_numbers = keys(a:news)
call map(l:all_version_numbers, function('s:VersionToNumber'))
call sort(l:all_version_numbers)
try
" Try to read previous version number from file.
let l:line = readfile(s:data_file, 'b')
catch /.*/
" Store current version number.
try
call mkdir(s:data_dir, 'p')
call writefile([a:current_version], s:data_file, 'b')
catch /.*/
endtry
return
endtry
" Get previous version number from file, then write current version number.
let l:previous_version_number = s:VersionToNumber('', l:line[0])
if l:previous_version_number < s:VersionToNumber('', a:current_version)
try
call writefile([a:current_version], s:data_file, 'b')
catch /.*/
endtry
endif
" Print updates for newer versions.
for l:number in l:all_version_numbers
if l:number > l:previous_version_number
for l:news_item in a:news[s:NumberToVersion(l:number)]
call s:logger.EchoInfo(l:news_item)
endfor
endif
endfor
endfunction