" Pattern to detect real-looking shebang to an absolute path let s:shebang = '^#!\s*/[^/]\+' " If the buffer looks shebanged, and the file being saved to doesn't exist " yet, set up a hook to make it executable after the write is done done function! shebang_create_exec#(filename) abort " If the first line isn't a shebang, or the file exists, do nothing if getline(1) !~# s:shebang || filereadable(a:filename) return endif " Set a buffer variable to the target filename let b:shebang_create_exec_filename = a:filename " Set up a hook to run and clean up after the write autocmd shebang_create_exec BufWritePost \ call s:Run(expand(':p')) endfunction " Clear away the hook that called us and make the file executable function! s:Run(filename) abort " Clear away the hook that called us autocmd! shebang_create_exec BufWritePost " Check that the save filename was set by BufWritePre if !exists('b:shebang_create_exec_filename') return endif " Check that it matches the file we just saved, and if so, make that file " executable if a:filename ==# b:shebang_create_exec_filename call s:MakeExecutable(b:shebang_create_exec_filename) endif " Clear away the save filename, even if we didn't change any permissions unlet b:shebang_create_exec_filename endfunction " Make a given filename executable function! s:MakeExecutable(filename) abort " Get filename into local variable let filename = a:filename " How we do this depends on whether we have native file permissions " functions (Vim >=8.0) if exists('*setfperm') " We have setfperm(), so we can make the file executable without a fork to " chmod(1), which should be quick and safe let cperm = getfperm(filename) " Replace every third character of the permissions string with an 'x' let nperm \ = strpart(cperm, 0, 2).'x' \ . strpart(cperm, 3, 2).'x' \ . strpart(cperm, 6, 2).'x' " If our new permissions string differs from the current one, apply it to " the file if nperm !=# cperm call setfperm(filename, nperm) endif else " We'll need to fork to chmod(1); escape the filename safely, using " shellescape() if we've got it let l:filename_escaped = exists('*shellescape') \ ? shellescape(filename) \ : '''' . escape(filename, '''') . '''' " Try to make the file executable with a fork to chmod(1) call system('chmod +x '.filename_escaped) endif endfunction