Improve the run program command in vim

This commit is contained in:
Michael Campagnaro 2023-12-10 18:37:18 -05:00
parent bc21059ea6
commit 05c46303c8

247
.vimrc
View File

@ -689,8 +689,7 @@ fu! CreateCtags()
" First determine if we're in a root drive directory. If we are then
" we bail because we don't want to recurse across the entire drive!
let l:path = expand('%:p:h')
let l:path_without_slashes = substitute(l:path, "/", "", "g")
if (strchars(l:path) - strchars(l:path_without_slashes)) <= 1
if IsRootDrive(l:path)
call PrintError("Not going to run ctags because the file is in a root drive directory")
return
endif
@ -1037,6 +1036,34 @@ command -nargs=0 EditColorScheme call EditColorScheme()
"|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
"|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
fu! IsRootDrive(path) abort
let l:path_without_slashes = substitute(a:path, "/", "", "g")
return strchars(l:path_without_slashes) <= 1
endfu
fu! IsPathContained(path1, path2) abort
let l:normalized_path1 = substitute(fnamemodify(a:path1, ':p'), '\', '/', 'g')
let l:normalized_path2 = substitute(fnamemodify(a:path2, ':p'), '\', '/', 'g')
" Ensure paths end with a directory separator
if l:normalized_path1[-1:] != '/'
let l:normalized_path1 .= '/'
endif
if l:normalized_path2[-1:] != '/'
let l:normalized_path2 .= '/'
endif
echo l:normalized_path1
echo l:normalized_path2
return match(l:normalized_path1, '^' . escape(l:normalized_path2, '\')) != -1
endfu
fu! CountChar(str, char) abort
" Remove all characters that are not the target character.
let l:filtered = substitute(a:str, '[^' . a:char . ']', '', 'g')
return strlen(l:filtered)
endfu
"##################################################################################
" COMPILING CODE
@ -1242,68 +1269,172 @@ fu! Build(optimized=0, silent=0)
exec l:async_cmd . l:cmd
endfu
fu! RunProgram()
let l:ran = 0
let l:ext = tolower(expand('%:e'))
let l:path_to_use = ""
if l:ext == "jai"
" Maybe the current file has an exe, i.e. wasn't compiled with a build script.
if filereadable(expand('%:p:r') . '.exe')
let l:ran = 1
exec "AsyncRun! " . expand('%:p:r') . ".exe"
elseif tolower(expand('%:h:t')) == "modules" || tolower(expand('%:h:h:t')) == "modules"
" This is likely a jai module inside a project. We will want to do the exe/run script checks in the parent project folder.
" The :h:h tests for a module inside a folder, e.g. modules/Basic/module.jai
echo "module"
if tolower(expand('%:h:t')) == "modules"
let l:path_to_use = "/.."
else
let l:path_to_use = "/../.."
endif
endif
elseif l:ext == "py"
let l:ran = 1
fu! RunProgram() abort
if tolower(expand('%:e')) == "py"
exec "AsyncRun! python %"
return
endif
if l:ran == 0
" First check the current file's directory (and one directory back)
" for a run script, falling back to the current working directory (and
" one directory back) of the editor.
if filereadable(expand('%:h') . '/run')
echo "file here"
exec "AsyncRun! " . expand('%:h') . "/run"
elseif filereadable(expand('%:h') . '/../run')
" Handles editing a file in a code/ or src/ and there's a run script one directory back.
echo "file one back"
exec "AsyncRun! " . expand('%:h') . "/../run"
elseif filereadable("run")
echo "cwd here"
exec "AsyncRun! ./run"
elseif filereadable("../run")
" Handles editing a file in a code/ or src/ and there's a run script one directory back.
echo "cwd one back"
exec "AsyncRun! ../run"
else
" Final attempt is to run any exe that's found nearby.
" We start with an exe relative to the open file's path.
let l:files = systemlist('ls '.expand('%:h').''.l:path_to_use.'/*.exe 2>/dev/null')
if len(l:files) > 0
exec "AsyncRun! " . l:files[0]
else
" Last attempt is any exe in the current working directory.
let l:files = systemlist('ls *.exe 2>/dev/null')
if len(l:files) > 0
exec "AsyncRun! " . l:files[0]
else
call PrintError("No exe or run script found!")
endif
endif
let l:file_exe_name = expand('%:t:r').'.exe' " Just the filename without the path
" Easy case is the current file has an exe with the same name in the
" same directory. We run that if found.
if filereadable(expand('%:p:h') . '/' . l:file_exe_name)
exec 'AsyncRun! ' . expand('%:p:h') . '/' . l:file_exe_name
return
endif
" We start by looking for something to run relative to the current file's
" path. If nothing is found then we start over looking in the current
" working directory (the directory vim was opened in).
let l:current_path = expand('%:p:h')
if !RunProgramInDirectory(l:current_path, l:file_exe_name)
let l:cwd = getcwd()
if !RunProgramInDirectory(l:cwd, l:file_exe_name)
call PrintError("No exe or run script found in current file path '" . l:current_path . "' or working directory '". l:cwd ."'")
endif
endif
endfu
fu! RunProgramInDirectory(starting_path, file_exe_name) abort
let l:search_path = a:starting_path
if tolower(expand('%:e')) == "jai"
" Check if we're editing inside a modules file and if so then get a
" path that takes us outside of it. We'll use that path for finding an
" exe or run script.
let l:pos = match(l:search_path, "modules")
if l:pos != -1
" e.g. if the current path is
" /z/jai/examples/wasm/modules/Blah/blah.jai then this is /z/jai/examples/wasm/
let l:module_root_path = l:search_path[:l:pos-1]
" We don't want to proceed if the path is in the jai compiler's modules folder.
let l:jai_path = tolower(g:campo_jai_path)
if l:jai_path[-1:] != '/'
let l:jai_path .= '/'
endif
"echo 'modules root path: ' . l:module_root_path . ' | jai path: ' . l:jai_path
if tolower(l:module_root_path) == l:jai_path
"echo 'inside jai modules. Aborting run'
return 0
endif
let l:search_path = l:module_root_path
endif
endif
let l:search_path = fnamemodify(l:search_path, ':p') " Normalize the path just in case.
" Search the current path for a run script, an exe with the current
" filename, a bin/filename exe, a run_tree/filename exe - if nothing is
" found then repeat one directory back when the current folder doesn't
" contain a .git/ or it's not a root directory, e.g. /z/.
"
" If nothing is found after exhausting the search then we start over and
" find the first exe with any name.
let l:current_path = l:search_path
while 1
" Run script has higher priority over an exe named after the current file.
let l:search = l:current_path . 'run'
if filereadable(l:search)
exec "AsyncRun! " . l:search
return 1
endif
" filename exe
let l:search = l:current_path . a:file_exe_name
if filereadable(l:search)
exec "AsyncRun! " . l:search
return 1
endif
" bin/filename exe
let l:search = l:current_path . 'bin/' . a:file_exe_name
if filereadable(l:search)
exec "AsyncRun! " . l:search
return 1
endif
" run_tree/filename exe
let l:search = l:current_path . 'run_tree/' . a:file_exe_name
if filereadable(l:search)
exec "AsyncRun! " . l:search
return 1
endif
" Only go back a directory if the current path doesn't have a .git folder or we're not in a root drive path.
if isdirectory(l:current_path . '.git') || IsRootDrive(l:current_path)
break
endif
let l:current_path = fnamemodify(l:current_path."../", ':p')
endwhile
"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
" Start over but look for the first exe. The user will confirm if they
" want to run it.
"
" @improve maybe provide the first 4 or 5 exes found and let them input
" which one they want?
fu! GetFirstExePath(path)
let l:files = systemlist('ls '.a:path.'*.exe 2>/dev/null')
if len(l:files) > 0
return l:files[0]
endif
return ""
endfu
let l:current_path = l:search_path
let l:exe_to_confirm = ""
while 1
let l:exe = GetFirstExePath(l:current_path)
if l:exe != ""
let l:exe_to_confirm = l:exe
break
endif
" bin/filename exe
let l:exe = GetFirstExePath(l:current_path."bin/")
if l:exe != ""
let l:exe_to_confirm = l:exe
break
endif
" run_tree/filename exe
let l:exe = GetFirstExePath(l:current_path."run_tree/")
if l:exe != ""
let l:exe_to_confirm = l:exe
break
endif
" Only go back a directory if the current path doesn't have a .git folder or we're not in a root drive path.
if isdirectory(l:current_path . '.git') || IsRootDrive(l:current_path)
break
endif
let l:current_path = fnamemodify(l:current_path."../", ':p')
endwhile
if l:exe_to_confirm != ""
let l:confirm = confirm("Found exe ".l:exe_to_confirm." - run this?", "&Yes\n&No")
if l:confirm == 1
exec "AsyncRun! " . l:exe_to_confirm
return 1
endif
redraw!
endif
return 0
endfu
" Show results window the moment the async job starts
augroup asyncPluginCmds
autocmd!
@ -1528,9 +1659,9 @@ fu! RenameFile()
if l:confirm == 1
exec 'saveas' l:new_name
exec 'silent! !rm' l:old_name
redraw!
endif
endif
redraw!
endfu
noremap <leader>n :call RenameFile()<cr>