Re: function definition for only one file, not for other simultaneously opened files

17 views
Skip to first unread message

Manas

unread,
May 30, 2020, 6:06:54 PM5/30/20
to v...@vim.org

I am struggling to find a way to deal with the following problem.

Suppose, I open 3 markdown files at once in separate tabs. Now I want some particular function for one of those files only. Like, I want to add time and date in front of each new line for that file. So I define a function to do so. But, that function is affecting other files too which I don't want.

Is there some way to achieve that?

--
Manas
CSAM Undergraduate 2022

Gary Johnson

unread,
May 30, 2020, 8:49:23 PM5/30/20
to vim...@googlegroups.com
From ":help local-function":

There are only script-local functions, no buffer-local or
window-local functions.

However, you _can_ control how and when your function is executed.
For example, mappings and autocommands can be local to a buffer.
You can also have your function look at the current buffer name or
some other property of the buffer and decide whether to continue
executing or to return immediately.

How are you executing your function and what is it doing to affect
files or buffers that you don't want it to?

Regards,
Gary

Tony Mechelynck

unread,
May 31, 2020, 4:44:00 AM5/31/20
to vim_use
Sidestepping for the moment the question of :def functions which are
still "under development":
- a function can be global (:function foo() ), script-local (:function
s:bar() ) or defined in an autoload-script (:function tiddly#Wink()
defined in e.g. ~/.vim/autoload/tiddly.vim ). Script-local functions
are only visible from the same script, including autocommands defined
there. To invoke them from mappings, replace s: by <SID>
- a function can be assigned to a Funcref which is a variable (global,
script-local, buffer-local, window-local, etc.) of a special type;
that Funcref can then be used both as a variable (when not followed by
() ) or as a function (followed by () ).
- a function can also be invoked from a user-command (defined by the
:command command)

I have a kind of hunch that there is something for you (Manas) in the
above but I'm not sure what.


Best regards,
Tony.

BPJ

unread,
May 31, 2020, 6:19:13 AM5/31/20
to vim_use
I believe that as Gary hinted the best bet in this case is to inspect the file extension (".md" I guess) with the help of 

if matchstr(bufname(), '\.md$')
  " do something
endif


which should return a true value (the string '.md') if the extension matches and a false value otherwise. This might make the function a little more complicated, so that you may want to load it from an (autoload) file.

I actually have a small function which takes an extension (not a regex!) with or without a leading dot and a file name and returns true of false according to whether the extension is present on the filename or not.

" e.g. let bool =                  bpj#util#ext_is('md',              bufname())                       
fun! bpj#util#ext_is (ext,         filename) abort                  
    " prepend dot to ext if none   
  let wanted = substitute(a:       ext, '\v^[^\.]+$', '.&','')      
    " get ext incl dot from fn     
   let actual = matchstr(a:         filename, '\v\.[^\.]+$')         
   " return result of comparison     return actual ==# wanted        
 endfun

You may want to make that a case insensitive comparison (use `==?` instead of `==#`). I prefer not to. I can always pass the lowercased version of the file name if I want to ignore case.

--
Better --help|less than helpless

Manas

unread,
May 31, 2020, 7:55:05 PM5/31/20
to vim...@googlegroups.com

On 31/05/20 6:18 am, Gary Johnson wrote:

From ":help local-function":

    There are only script-local functions, no buffer-local or
    window-local functions.

However, you _can_ control how and when your function is executed.
For example, mappings and autocommands can be local to a buffer.
I looked up this. While reading the manual, I realized I wanted a buffer-local function but as it is not available so I think I would just copy that function to my .vimrc and as you suggested, invoke a mapping anytime I want to use that function in some buffer.
You can also have your function look at the current buffer name or
some other property of the buffer and decide whether to continue
executing or to return immediately.
Though, this can be done as I am working with some markdown files for note-taking etc. but I wanted to make the feature less hard coded. As in, I have many markdown files for notes on different topics (viz. current project, academics, reminders, etc.) but I wanted the timestamp feature to only apply in one of those files.
How are you executing your function and what is it doing to affect
files or buffers that you don't want it to?

My function executes a bash command to get the current date and time, whenever I hit <ENTER> and prepends it to the line.


On 31/05/20 2:13 pm, Tony Mechelynck wrote:
- a function can be assigned to a Funcref which is a variable (global,
script-local, buffer-local, window-local, etc.) of a special type;
that Funcref can then be used both as a variable (when not followed by
() ) or as a function (followed by () ).

I have a kind of hunch that there is something for you (Manas) in the
above but I'm not sure what.

I found this the most optimum. What I did was:

1. define the function

2. create a buffer-local function reference pointing to the function:

> let b:Fn = function("PrependTime")

`PrependTime` is my function name.

3. And now `:echo b:Fn()` in current buffer will prepend the timestamp and in other buffers, it will throw error (E117: Unknown function). Now I wanted to somehow suppress the error message but I could not find much.


On 31/05/20 3:48 pm, BPJ wrote:
I believe that as Gary hinted the best bet in this case is to inspect the file extension (".md" I guess) with the help of 

if matchstr(bufname(), '\.md$')
  " do something
endif
But again it will become hard-coded for that particular file. As I open multiple markdown files simultaneously, so I will need to go with the filename.


PS: Sorry for delayed response.

Gary Johnson

unread,
Jun 1, 2020, 12:43:49 AM6/1/20
to vim...@googlegroups.com
On 2020-06-01, Manas wrote:
> On 31/05/20 6:18 am, Gary Johnson wrote:

> How are you executing your function and what is it doing to affect
> files or buffers that you don't want it to?
>
> My function executes a bash command to get the current date and time, whenever
> I hit <ENTER> and prepends it to the line.
>
>
> On 31/05/20 2:13 pm, Tony Mechelynck wrote:
>
> - a function can be assigned to a Funcref which is a variable (global,
> script-local, buffer-local, window-local, etc.) of a special type;
> that Funcref can then be used both as a variable (when not followed by
> () ) or as a function (followed by () ).
>
> I have a kind of hunch that there is something for you (Manas) in the
> above but I'm not sure what.
>
> I found this the most optimum. What I did was:
>
> 1. define the function
>
> 2. create a buffer-local function reference pointing to the function:
>
> > let b:Fn = function("PrependTime")
>
> `PrependTime` is my function name.
>
> 3. And now `:echo b:Fn()` in current buffer will prepend the timestamp and in
> other buffers, it will throw error (E117: Unknown function). Now I wanted to
> somehow suppress the error message but I could not find much.

You wrote that you want to have <Enter> insert the date at the start
of the next line, but only in one buffer. Wouldn't making that
mapping buffer-local solve your problem? Something like this?

inoremap <buffer> <CR> <CR><C-R>=PrependTime()<CR>

function! PrependTime()
return strftime("%c")
endfunction

See

:help map-<buffer>

You would just need to execute that mapping only in the one buffer
where you wanted that behavior.

If other buffers are throwing error E117, then you have <Enter>
(a.k.a. <CR>) mapped in buffers where it shouldn't be mapped.

Regards,
Gary

Manas

unread,
Jun 1, 2020, 6:20:59 AM6/1/20
to vim...@googlegroups.com


On 01/06/20 10:13 am, Gary Johnson wrote:
You wrote that you want to have <Enter> insert the date at the start
of the next line, but only in one buffer.  Wouldn't making that
mapping buffer-local solve your problem?  Something like this?

    inoremap <buffer> <CR> <CR><C-R>=PrependTime()<CR>

    function! PrependTime()
        return strftime("%c")
    endfunction    

See

    :help map-<buffer>

You would just need to execute that mapping only in the one buffer
where you wanted that behavior.
Yes, that's what I wanted. Thanks a lot. Somehow I missed this.
If other buffers are throwing error E117, then you have <Enter>
(a.k.a. <CR>) mapped in buffers where it shouldn't be mapped.

Regards,
Gary

After unmapping and remapping I checked everything works fine.

Thanks a lot everyone.

Reply all
Reply to author
Forward
0 new messages