Automatically append current date to file name when saving in Vim - vim

I'm using vim to take notes while reading academic articles. I prefer to have a new text file for each note I've taken, but organizing them becomes tedious.
What I would like to do is set an autocommand to detect if I'm in a certain directory, writing to a newfile and then appened the current date and time to whatever filename I write.
So if I have:
:pwd
/path/to/projects
When I type
:w Notes
I would like vim instead to save the file as
"Notes - <CURRENT DATE - TIME >.txt"
I believe it involves declaring something like the following in my vimrc:
autocmd BufNewFile,BufWrite "/path/to/projects/*" <command involving strftime("%Y-%m-%d_%H-%M")>
But I can't figure out what. Any Ideas? I'm using Vim 7.3 on Debian Linux.

You're very close. I think it's best to rename the file as it is created; messing with the file name during writes makes this more difficult (e.g. what if you re-open an existing note, or just write the buffer again?)
The :file command can be used to rename the current file; the current filename is in the % special identifier. Triggered when a new file is created, this does the job:
autocmd BufNewFile /path/to/projects/* execute 'file' fnameescape(expand('%') . strftime(" - %Y-%m-%d_%H-%M.txt"))
If you don't want to consider the original filename, this becomes even easier:
autocmd BufNewFile /path/to/projects/* execute 'file' fnameescape(strftime("Notes - %Y-%m-%d_%H-%M.txt"))

You may be looking for something along the lines of:
function! SaveWithTS(filename) range
execute "save '" . a:filename . strftime(" - %Y-%m-%d_%H-%M.txt'")
endfunction
command! -nargs=1 SWT call SaveWithTS( <q-args> )
With the above in your .vimrc, executing :SWT Note will save your file as Note - YYYY-MM-DD_HH-MM.txt. This has the disadvantage of not happening automatically, so you have to remember to use :SWT instead of :w the first time your write your file, but it does let you wait until you are ready to save to decide what your filename should be (i.e. you aren't stuck with Note every time).
Edit: The above version of SaveWithTS actually saves to the filename with single quotes around it (bad testing on my part). Below is a version that should fix that and also lets you specify an extension to your file (but will default to .txt)
function! SaveWithTS(filename) range
let l:extension = '.' . fnamemodify( a:filename, ':e' )
if len(l:extension) == 1
let l:extension = '.txt'
endif
let l:filename = escape( fnamemodify(a:filename, ':r') . strftime(" - %Y-%m-%d_%H-%M") . l:extension, ' ' )
execute "write " . l:filename
endfunction

Related

Vim: Avoid having to press ENTER after successful make?

$ ls
Makefile html-page/ page-generator.m4
Run includes/
Alongside the Makefile, I have a script Run that is executed only when make completes without errors. This I've managed to implement with the following in my .vimrc file, which also looks for the Makefile in parent directories if needed.
" Before the 'make' quickfix command, run my quickfix pre-commands
autocmd QuickfixCmdPre make call MyQuickfixCmdPre()
" After the 'make' quickfix command, run my quickfix post-commands
autocmd QuickfixCmdPost make call MyQuickfixCmdPost()
and
function! MyQuickfixCmdPre()
" Save current buffer, but only if it's been modified
update
" (h)ead of (p)ath of % (current buffer), i.e. path of current file
let l:dir = expand('%:p:h')
" Remove final / and smack a /Makefile on the end, glob gives empty if file doesn't exist
while empty(glob(substitute(l:dir, '/$', '', '') . '/Makefile'))
" There's no Makefile here. Are we at the root dir?
if l:dir ==# "/"
" Just use dir of current file then
let l:dir = '.'
break
else
" Try the parent dir. Get (h)ead of dir, i.e. remove rightmost dir name from it
let l:dir = fnamemodify(l:dir, ':h')
endif
endwhile
" Makefile is in this dir, so local-cd (only this window) to the dir
execute "lcd " . l:dir
endfunction
function! MyQuickfixCmdPost()
" Get number of valid quickfix entries, i.e. number of errors reported,
" using filter to check the 'valid' flag
let l:err_count = len(filter(getqflist(), 'v:val.valid'))
if l:err_count ==# 0
" The make succeeded. Execute the Run script expected in the same dir as Makefile
call system('./Run')
redraw!
endif
endfunction
With this in place, after typing :mak in vim, the code is made and run... There are two possible results:
If there are errors during make, vim will present these errors with a Press ENTER or type command to continue afterwards, which is all good.
If make succeeds without errors, however, my Run script is executed, for testing my code (in this case an html file shown in a browser), but then when I switch back to vim, I have to press enter to get rid of a message from vim that I don't need to read because it doesn't tell me about errors. This message used to look like this:
"includes/m4includes/subs.m4" 34L, 759B written
:!make 2>&1| tee /var/folders/zk/0bsgbxne3pe5c86jsbgdt27f3333yd/T/vkbxFyd/255
m4 -I includes/m4includes page-generator.m4 >html-page/mypage.html
(1 of 1): m4 -I includes/m4includes page-generator.m4 >html-page/mypage.html
Press ENTER or type command to continue
but after introducing the redraw! in MyQuickfixCmdPost() is now reduced to:
(1 of 1): m4 -I includes/m4includes page-generator.m4 >html-page/mypage.html
Press ENTER or type command to continue
yet still with the need to press enter.
How do we avoid having to press enter every single time we return to vim after a successful compilation? Any ideas?
Note: vim has a -silent command-line option, but as far as I can see this would silence all the Press ENTERs, and the goal here is to only avoid them after a successful make.
Just add call feedkeys("\<CR>") afterwards. There are not many places you need feedkeys() (often normal! or similar commands will do), and there are subtle effects (look at the flags it takes carefully). Fortunately this is one place it is useful.

Using SpaceVim on neovim and MacVim and can't get location of plugin script

I've tried all the solutionshttps://stackoverflow.com/questions/4976776/how-to-get-path-to-the-current-vimscript-being-executed/53274039#53274039 which was posted 7 years ago and none are working for me in the current (2018) MacVim (8.1) or neovim (0.3.1). Since my question there keeps being deleted, I decided to ask in a new question.
Has something changed in Vim since the last answer 7 years ago? All of these solutions are giving me the current file's location and not the location of the script. Is SpaceVim effecting how these functions work?
This is the code I'm trying to fix:
function! TeaCodeExpand()
<<some code>>
echom fnamemodify(resolve(expand('<sfile>:p')), ':h')
<<other code>>
endfunction
I have an echom to show the path that should be the current script file, but it always returns the currently edited file. The next line is to execute an AppleScript in the directory above the vimscript file. I can hard code the path and everything works fine. But, I can't get it to work as is. The full code is here: github.com/raguay/TeaCode-Vim-Extension
To have the <sfile> expand to the plugin's filespec, the expansion has to happen when the script is sourced. See :help :<sfile>:
<sfile> When executing a ":source" command, is replaced with the
file name of the sourced file.
When executing a function, is replaced with:
"function {function-name}[{lnum}]"
If you need the filespec later in a function, you need to store it in a (script-local) variable, and reference that from the function:
let s:scriptPath = fnamemodify(resolve(expand('<sfile>:p')), ':h')
function! TeaCodeExpand()
<<some code>>
echom s:scriptPath
<<other code>>
endfunction

How can I source files relative to file?

I'm trying to split my vimrc up into multiple files - init.vim, keybindings.vim, ui.vim, etc. - but I can't get Vim to source files relative to init.vim (it instead sources relative to where I launch Vim from.
This is what I've got at the top of init.vim:
source keybindings.vim
source ui.vim
If I run vim from the same directory as those files, it works fine; if I run it from any other directory, I get the following errors:
Error detected while processing /path/to/vimrc:
line 1:
E484: Can't open file keybindings.vim
line 2:
E484: Can't open file ui.vim
Press ENTER or type command to continue
Edit: It's worth noting that I'm using NixOS, so I don't know what the absolute paths will be, nor if they would be constant if I found out.
I think you can use
runtime keybindings.vim
Source needs the full path, you can however simplify it using something like this :
let path = expand('%:p:h')
exec 'source' path . '/keybindings.vim'
You can have a look at mine here - https://github.com/dhruvasagar/dotfiles/blob/master/vim/vimrc for reference.
If the order is not important, you can just put your scripts into ~/.vim/plugin/, and they will be sourced after ~/.vimrc. You can check :scriptnames output to see what gets sourced when.
You can influence the ordering somewhat via the plugin filenames. For example, I have a ~/.vim/plugin/00plugin-configuration.vim that configures Vim plugins; the 00... ensures this is sourced first.
To get finer control, I would instead put the scripts into ~/.vim/. Vim will ignore them there, but they can easily be addressed via :runtime, which looks in all runtimepaths, and ~/.vim/ typically is included in 'runtimepath':
# .vimrc
runtime init.vim
runtime keybindings.vim
...
Relevant help pages: :help .vimrc and :help load-plugins.
Building on Dhruva's answer, you can make a function to help out with this
function! SourceLocal(relativePath)
let root = expand('%:p:h')
let fullPath = root . '/'. a:relativePath
exec 'source ' . fullPath
endfunction
You then use it like
call SourceLocal ("yourScript.vim")
I have met exactly the same issue with you in Neovim. I split my large init.vim file into several small vim scripts and I want to source them inside init.vim.
This is what I get finally based on #Dhruva Sagar's links:
let g:nvim_config_root = stdpath('config')
let g:config_file_list = ['variables.vim',
\ 'options.vim',
\ 'autocommands.vim',
\ 'mappings.vim',
\ 'plugins.vim',
\ 'ui.vim'
\ ]
for f in g:config_file_list
execute 'source ' . g:nvim_config_root . '/' . f
endfor
As none of the solutions works as a real substitute for source working globally (on any script, even sourced from vimrc), I ended up with this solution and decided to share here, which can be used as a substitute for source with relative support, as simple as:
Rsource /home/me/.vim/your/file/path
Rsource $HOME/.vim/your/file/path
Rsource your/file/path
Rsource ../your/file/path
To use it, this must be defined on your vimrc or any file sourced by it before you can use Rsource:
if !exists('g:RelativeSource')
function! g:RelativeSource(file)
let file = expand(a:file)
" if file is a root path, just source it
if stridx(file, '/') == 0
exec 'source ' . file
return
endif
let sfile = expand('<sfile>:p:h')
" If this is called outside this script, it will contains this script
" name, this function name, a script_marker then the executing script name
" In this case we extract just the last part, the script name which called
" the this function
let script_marker = '..script '
let path_index = strridx(sfile, script_marker)
if path_index == -1
let path_index = 0
else
let path_index += len(script_marker)
endif
let path = strpart(sfile,path_index)
let absolute_path = resolve(path . '/'. file)
exec 'source ' . absolute_path
endfunction
command! -nargs=1 Rsource :call g:RelativeSource(<q-args>)
endif
This is safe to be used in any script or plugin.
These are all great solutions, and this is what I ended up using.
let home = expand('~')
exec 'source' home . '/.config/nvim/prettierConfig.vim'

Vim: cannot see :echomsg messages in message history list

I'm working on a vim script and want to see the value of a variable that is generated in the script for debugging purposes.
I use something like:
echomsg 'My variable = ' . b:variable
reload the source with
:source %
and then look the message history list with
:messages
But there's nothing there and I'm pretty sure that the command would be executed.
What am I doing wrong here?
EDIT:
I'm trying to make a change to vim-cucumber and so I wanted to see the value of a variable. In the ftplugin/cucumber.vim file, I have the following:
let b:cucumber_root = expand('%:p:h:s?.*[\/]\%(features\|stories\)/step_definitions/mobile_website\zs[\/].*??')
echomsg 'cucumber_root = ' . b:cucumber_root
If you look at the top of file you will find
if (exists("b:did_ftplugin"))
finish
endif
Which stops the file from being sourced again after the first time. You need to unlet b:did_ftplugin before sourcing the file again.
Other options would be to use another tpope plugin to do this for you called scripttease with the :Runtime command.

How to make Syntastic search for javac config file in project

I'm using Syntastic with vim and I've added external libraries to its classpath (using SyntasticJavacEditClasspath). This creates a file in the current working directory (which was my project folder). This is all fine. However, whenever I restart vim, Syntastic seems to search for the .syntastic_javac_config file in the current working directory only, and the cwd is, of course, randomly whereever I left it in my last operation. So it doesn't find the file and I get a bunch of incorrect import errors. Can Syntastic be told to search the file's parents for the config file? If not, is there a way of using vim where this typically works? (I've only been using vim for a couple of months so I might be clueless.)
According to the official manual, the snippet should solve the problem. Put it in your .vimrc, change the javascript, jscs, etc. to required values
function! FindConfig(prefix, what, where)
let cfg = findfile(a:what, escape(a:where, ' ') . ';')
return cfg !=# '' ? ' ' . a:prefix . ' ' . shellescape(cfg) : ''
endfunction
autocmd FileType javascript let b:syntastic_javascript_jscs_args =
\ get(g:, 'syntastic_javascript_jscs_args', '') .
\ FindConfig('-c', '.jscs', expand('<afile>:p:h', 1))

Resources