0

I am attempting to write a vim function that I would like to be loaded automatically when vim starts, thus I am putting it in my vimrc (but maybe that is wrong?). When called during normal editing, it should write a modified version of the current buffer's contents to another file, and then return me to my original file to continue editing it. My version of vim is 8.1.5126.

So far I've attempted the following:

function! Write_new_file() " Hold the original and new file names in variables. let l:fn_no_ext = expand('%:p:r') let l:fn_ext = expand('%:e') let l:original_name = l:fn_no_ext.'.'.l:fn_ext let l:new_name = l:fn_no_ext.'_x.'.l:fn_ext " Save the state of the current buffer to the original file. write " Assign a new file name to the current buffer. exe 'file '.l:new_name " Make all the modifications to the current buffer. call Many_Modifications() " Save the modified buffer to the new file. write! " Return to editing the original file. exe 'edit! '.l:original_name endfunction 

The problem:

If this function is defined in a script other than my vimrc, it works as expected. However, if the function is defined in my vimrc, the edit command at the very end causes error E127. The output looks like this:

Error detected while processing /home/edward/.vim/vimrc: line 111: E127: Cannot redefine function Write_new_file: It is in use Press ENTER or type command to continue 

Based on the fact that, when I comment out the edit in the function, the error disappears (of course, then I don't get the behavior I want), I am guessing that the edit is the source of the problem.

Based on the fact that the error message says that the error was detected while processing my vimrc, I am guessing that something about executing the edit statement makes vim re-source my vimrc, and since my vimrc contains the definition of Write_new_file(), the error is triggered.

Of course, these are just guesses. BTW, this behavior happens regardless of whether the --noplugins flag is present or not when vim is started.

Is there a way to use the edit command in a function defined in my vimrc that does not throw an error, or alternatively is there a different way to achieve my goal that does not involve manually sourcing a script once during each session?

For anyone who is wondering what my use case is: I am writing text that has to contain many instances of different long sequences of characters. To ease the typing, for each such long sequence, I've come up with a unique shorter one that I type instead. Obviously I have to replace my sequences with the expected ones, before the files are usable. The process of creating any such file is iterative, ie: write, check validity, keep writing. Therefore I need a script that creates a new file with the appropriate replacements and returns me to where I left off in my text.

I suppose I could accomplish my goal with sed, however, I've already invested considerable effort writing the replacement scripts in vimscript, and I'd hate to have to re-implement. Also, I have a secondary goal to become more proficient in the use of vim.

18
  • So what are you actually trying to achieve? Commented Apr 30, 2024 at 17:55
  • I have files that contain shorthand sequences (to make them easy to type) that then need to be replaced by much longer sequences to make the files interpretable as some computer language. Commented Apr 30, 2024 at 18:01
  • 2
    If :edit or another command is sourcing your vimrc, we're missing code that would cause that. See How to debug my vimrc. Commented Apr 30, 2024 at 21:26
  • 1
    Aside: you can just use write, no need for exec 'w'. Using execute is for interpolation of expressions into commands and controlling where | delimits command end. Commented Apr 30, 2024 at 21:27
  • 1
    This is an XY problem. Your initial problem can be solved with :help abbreviations. Commented May 2, 2024 at 6:40

3 Answers 3

1

Beside the XY Problem-ness of your question, I think that the main problem we have with it is that your function is fine as-is and there is no obvious reason for it to have the effect that you describe.

Actually, adding it to my own vimrc (as well as a dummy Many_Modifications()):

function! Many_Modifications() %s/\n/ endfunction function! Write_new_file() let l:fn_no_ext = expand('%:p:r') let l:fn_ext = expand('%:e') let l:original_name = l:fn_no_ext.'.'.l:fn_ext let l:new_name = l:fn_no_ext.'_x.'.l:fn_ext write exe 'file '.l:new_name call Many_Modifications() write! exe 'edit! '.l:original_name endfunction 

and calling it repeatedly on any file I throw at it does exactly what you want, without any error.

$ vim foo.try (some editing) :call Write_new_file() :!ls foo.try foo_x.try 

So I'm tempted to say that the problem is either:

  • in Many_Modifications(), which you didn't disclose,
  • or in another part of your setup that is unknown to us,
  • or in how you call Write_new_file(),
  • or in how you handle your vimrc.
1
  • I agree with you. Specifically, I think it is part of my setup. It do not think the problem is in Many_Modifications - just for testing I just put a function in my vimrc that contains plain edit as its only command, and guess what, it also causes my vimrc to be re-sourced and fails the same way as the script in my question. As for how I call Write_new_file(), I do it like this :call Write_new_file(), so I don't think that's it either. If I find out what is wrong with my setup, I'll add an additional answer. Commented May 4, 2024 at 1:51
1

If your goal is to replace short commands with longer commands in the file you're just editing, the solution is to use abbreviations, as commented by romainl. In that case, do something like

:iabbrev cmd a_very_long_command 

This is documented in :help abbreviations. The iabbrev command works only in insert mode, which is what we usually want.

If you want to write to another file, I'd recommend to make use of Vim's make support. See :help :make for more information. Text transformations are a kind of compilation. Only we don't compile to a binary artifact but to another text file. Nevertheless, we can use the same tools.

Your "compiler" is an external tool. Since you already created your Many_Modifications() function, let's recycle it and write a quick and dirty Vim-based text compiler. Not because it's incredibly useful, but because I felt like doing it.

First, let's create a file which contains the function you already wrote. I'll call it many_modifications.vim:

function Many_Modifications() abort %s/\<cmd\>/a_very_long_command/g endfunction 

And we also need a "compiler" shell script my_compiler.sh:

#!/bin/bash vim -es -u many_modifications.vim -c 'call Many_Modifications()' -c "saveas! $1.redundant" -c'q!' "$1" 

Note that the script above lacks all error checking and relies on relative paths. It's the bare minimum you need to get it working.

It starts Vim in script mode as explained in :help -s-ex (some severe cases of puerile humor in Vim's documentation), sources your many_modifications.vim, opens the file and executes the three commands:

call Many_Modifications() saveas! <your-file>.redundant q! 

Finally, configure Vim to use your compiler with

:set makeprg=./my_compiler.sh\ % 

and run :make to create a file <your-file>.redundant from <your-file>.

And that's it: Vim running Vim to transform your source code.

2
  • Thanks for taking the time to write this answer. This bypasses the problem and is an acceptable solution for the current state of my system. If we call my activity compilation, then this may be the more appropriate approach. It is worth saying, though, that as romainl points out, my system has to be misconfigured in some way. Commented May 3, 2024 at 23:54
  • I had fun writing the answer. My point is that it doesn't matter what it's called. On the abstract level, you take an input, transform it in some way to produce an artifact. This is ubiquitous in our trade. The whole family of "make" programs does this and so does Vim's :make command. Commented May 4, 2024 at 8:06
1

The root cause of the error is that some time ago, thinking I was clever, I created an "after" script for my file-type. In this "after" script I sourced my vimrc.

For anyone who doesn't know, if vim can determine your file-type, eg python, html, lisp, etc, it will source a variety file-type specific scripts to configure such things as the appropriate syntax highlighting. Some of these scripts (perhaps all of them) are sourced after your vimrc. Some of them do annoying things, like overwriting your indentation and wrapping behavior. These scripts are shipped with vim and there is no point in trying to edit them, they are complex and are overwritten anyway when vim is updated.

With a little research I found out that vim does provide a way to modify its default file-type configurations. If say, vim's c file-type configuration was annoying to me, I could create $HOME/.vim/after/ftplugin/c.vim and in it reset the configuration to my liking.

The least-effort fix, I thought, would be to simply source my vimrc in my "after" script. As it turns out, while edit does not cause vim to re-source vimrc, it does cause vim to source all appropriate file-type specific scripts, including the appropriate "after" scripts (one of which, in my case, sourced vimrc).

The obvious solution is to:

  1. Not source vimrc in any "after" scripts.
  2. More specifically, do not put function definitions containing edit within any scripts that will directly or indirectly be sourced by vim's file-type system. Doing so has a high likelihood of resulting in an E127 error.
  3. Generally, be mindful that your functions never cause the re-sourcing of their enclosing scripts while they are running, as that will always cause an E127 error.

Unless you are overwriting function definitions shipped with vim for a particular file-type, you should usually not need to define a function in "after" scripts, thus limiting your exposure to this kind of problem. Function definitions containing edit can generally be safely placed in vimrc.

In my particular case, I solved the problem by removing the line that sourced my vimrc from my specific "after" script.

5
  • I found all this good stuff out by looking at the log file produced with vim -Vlog_file my_file. Commented May 4, 2024 at 6:53
  • You shouldn't source your vimrc, manually or automatically, ever. Commented May 4, 2024 at 7:34
  • 1
    A classic step on the journey: I used to put indent configuration in my vimrc, too. Don’t: instead, put it in after/ftplugin or after/indent scripts, as you’ve learned. Commented May 4, 2024 at 11:19
  • As it’s currently written, your answer is unclear. Please edit to add additional details that will help others understand how this addresses the question asked. You can find more information on how to write good answers in the help center. Commented May 5, 2024 at 9:11
  • I'd love to do that. Could you be a bit more specific about what you don't understand? Commented May 7, 2024 at 2:07

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.