vim-denops / deno-denops-std

📚 Standard module for denops.vim
https://jsr.io/@denops/std
MIT License
57 stars 17 forks source link

option: "local to tab page" options has `getBuffer()` method #268

Open Milly opened 2 months ago

Milly commented 2 months ago

Describe the bug

Related #267.

"local to tab page" options has getBuffer() method. There is also getWindow() etc. methods.

import { cmdheight } from "jsr:@denops/std/option";

declare const denops: Denops;

await cmdheight.getBuffer(denops, 1);

Currently (Vim/Nvim target for v7) there are only these options:

Expected behavior

Milly commented 2 months ago

I'm working on this.

Milly commented 2 months ago

getTabPage() etc. methods cannot be implemented.

getBuffer() method uses getbufvar(), which allows to get buffer options:

getbufvar({buf}, {varname} [, {def}])               *getbufvar()*
        The result is the value of option or local buffer variable
        {varname} in buffer {buf}.  Note that the name without "b:"
        must be used.
        The {varname} argument is a string.
        When {varname} is empty returns a |Dictionary| with all the
        buffer-local variables.
        When {varname} is equal to "&" returns a |Dictionary| with all
        the buffer-local options.
        Otherwise, when {varname} starts with "&" returns the value of
        a buffer-local option.
        This also works for a global or buffer-local option, but it
        doesn't work for a global variable, window-local variable or
        window-local option.

But gettabvar() does not provide to get tab page options:

gettabvar({tabnr}, {varname} [, {def}])             *gettabvar()*
        Get the value of a tab-local variable {varname} in tab page
        {tabnr}. |t:var|
        Tabs are numbered starting with one.
        The {varname} argument is a string.  When {varname} is empty a
        dictionary with all tab-local variables is returned.
        Note that the name without "t:" must be used.
        When the tab or variable doesn't exist {def} or an empty
        string is returned, there is no error message.

Also same issues with settabvar().

lambdalisue commented 2 months ago

Not tested but I think we can use win_getid, win_execute like

function! s:gettabopt(tabnr, varname, def) abort
  let l:winid = win_getid(1, a:tabnr)
  let l:result = win_execute(l:winid, printf('echo &l:%s', a:varname))
  return empty(l:result) ? a:def : l:result
endfunction

function! s:settabopt(tabnr, varname, val) abort
  let l:winid = win_getid(1, a:tabnr)
  call win_execute(l:winid, printf('let &l:%s = %s', a:varname, string(a:val)))
endfunction
Milly commented 2 months ago

Not tested but I think we can use win_getid, win_execute like

These not work. It always returns the value of the current tab page. Currently Vim does not seem to have a way to get/set local tab options other than actually switching tab pages.

The following works:

function! s:gettabopt(tabnr, varname) abort
  let l:winid_save = win_getid()
  let l:lazyredraw_save = &lazyredraw
  try
    set lazyredraw
    execute a:tabnr 'tabnext'
    let l:result = a:varname->printf('&l:%s')->eval()
    return l:result
  finally
    call win_gotoid(l:winid_save)
    let &lazyredraw = l:lazyredraw_save
  endtry
endfunction

But has side effects (We can add more redundant state saving code):

setglobal cmdheight=1
tabnew
setlocal cmdheight=5
tabnew
1tabnext
echo tabpagenr('#')
" -> 3
echo s:gettabopt(2, 'cmdheight')
" -> 5
echo tabpagenr('#')
" -> 2

And causes an update of the GUI screen. (CUI screen updates seem to be blocked by 'lazyredraw'.)

Milly commented 2 months ago

https://github.com/vim/vim/pull/15713 fixes this.