diff --git a/.vimrc b/.vimrc index 0f21efe..7db2edc 100644 --- a/.vimrc +++ b/.vimrc @@ -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 n :call RenameFile()