neovim

Neovim text editor
git clone https://git.dasho.dev/neovim.git
Log | Files | Refs | README

fold_spec.lua (26191B)


      1 local t = require('test.testutil')
      2 local n = require('test.functional.testnvim')()
      3 local Screen = require('test.functional.ui.screen')
      4 
      5 local clear = n.clear
      6 local eq = t.eq
      7 local insert = n.insert
      8 local write_file = t.write_file
      9 local exec_lua = n.exec_lua
     10 local command = n.command
     11 local feed = n.feed
     12 local poke_eventloop = n.poke_eventloop
     13 
     14 before_each(clear)
     15 
     16 describe('treesitter foldexpr', function()
     17  before_each(function()
     18    -- open folds to avoid deleting entire folded region
     19    exec_lua([[vim.opt.foldlevel = 9]])
     20  end)
     21 
     22  local test_text = [[
     23 void ui_refresh(void)
     24 {
     25  int width = INT_MAX, height = INT_MAX;
     26  bool ext_widgets[kUIExtCount];
     27  for (UIExtension i = 0; (int)i < kUIExtCount; i++) {
     28    ext_widgets[i] = true;
     29  }
     30 
     31  bool inclusive = ui_override();
     32  for (size_t i = 0; i < ui_count; i++) {
     33    UI *ui = uis[i];
     34    width = MIN(ui->width, width);
     35    height = MIN(ui->height, height);
     36    foo = BAR(ui->bazaar, bazaar);
     37    for (UIExtension j = 0; (int)j < kUIExtCount; j++) {
     38      ext_widgets[j] &= (ui->ui_ext[j] || inclusive);
     39    }
     40  }
     41 }]]
     42 
     43  local function parse(lang)
     44    exec_lua(
     45      ([[vim.treesitter.get_parser(0, %s):parse()]]):format(lang and '"' .. lang .. '"' or 'nil')
     46    )
     47  end
     48 
     49  local function get_fold_levels()
     50    return exec_lua(function()
     51      local res = {}
     52      for i = 1, vim.api.nvim_buf_line_count(0) do
     53        res[i] = vim.treesitter.foldexpr(i)
     54      end
     55      return res
     56    end)
     57  end
     58 
     59  it('can compute fold levels', function()
     60    insert(test_text)
     61 
     62    parse('c')
     63 
     64    eq({
     65      [1] = '>1',
     66      [2] = '1',
     67      [3] = '1',
     68      [4] = '1',
     69      [5] = '>2',
     70      [6] = '2',
     71      [7] = '2',
     72      [8] = '1',
     73      [9] = '1',
     74      [10] = '>2',
     75      [11] = '2',
     76      [12] = '2',
     77      [13] = '2',
     78      [14] = '2',
     79      [15] = '>3',
     80      [16] = '3',
     81      [17] = '3',
     82      [18] = '2',
     83      [19] = '1',
     84    }, get_fold_levels())
     85  end)
     86 
     87  it('recomputes fold levels after lines are added/removed', function()
     88    insert(test_text)
     89 
     90    parse('c')
     91 
     92    command('1,2d')
     93    poke_eventloop()
     94 
     95    eq({
     96      [1] = '0',
     97      [2] = '0',
     98      [3] = '>1',
     99      [4] = '1',
    100      [5] = '1',
    101      [6] = '0',
    102      [7] = '0',
    103      [8] = '>1',
    104      [9] = '1',
    105      [10] = '1',
    106      [11] = '1',
    107      [12] = '1',
    108      [13] = '>2',
    109      [14] = '2',
    110      [15] = '2',
    111      [16] = '1',
    112      [17] = '0',
    113    }, get_fold_levels())
    114 
    115    command('1put!')
    116    poke_eventloop()
    117 
    118    eq({
    119      [1] = '>1',
    120      [2] = '1',
    121      [3] = '1',
    122      [4] = '1',
    123      [5] = '>2',
    124      [6] = '2',
    125      [7] = '2',
    126      [8] = '1',
    127      [9] = '1',
    128      [10] = '>2',
    129      [11] = '2',
    130      [12] = '2',
    131      [13] = '2',
    132      [14] = '2',
    133      [15] = '>3',
    134      [16] = '3',
    135      [17] = '3',
    136      [18] = '2',
    137      [19] = '1',
    138    }, get_fold_levels())
    139  end)
    140 
    141  it('handles changes close to start/end of folds', function()
    142    insert([[
    143 # h1
    144 t1
    145 # h2
    146 t2]])
    147 
    148    exec_lua([[vim.treesitter.query.set('markdown', 'folds', '(section) @fold')]])
    149    parse('markdown')
    150 
    151    eq({
    152      [1] = '>1',
    153      [2] = '1',
    154      [3] = '>1',
    155      [4] = '1',
    156    }, get_fold_levels())
    157 
    158    feed('2ggo<Esc>')
    159    poke_eventloop()
    160 
    161    eq({
    162      [1] = '>1',
    163      [2] = '1',
    164      [3] = '1',
    165      [4] = '>1',
    166      [5] = '1',
    167    }, get_fold_levels())
    168 
    169    feed('dd')
    170    poke_eventloop()
    171 
    172    eq({
    173      [1] = '>1',
    174      [2] = '1',
    175      [3] = '>1',
    176      [4] = '1',
    177    }, get_fold_levels())
    178 
    179    feed('2ggdd')
    180    poke_eventloop()
    181 
    182    eq({
    183      [1] = '0',
    184      [2] = '>1',
    185      [3] = '1',
    186    }, get_fold_levels())
    187 
    188    feed('u')
    189    poke_eventloop()
    190 
    191    eq({
    192      [1] = '>1',
    193      [2] = '1',
    194      [3] = '>1',
    195      [4] = '1',
    196    }, get_fold_levels())
    197 
    198    feed('3ggdd')
    199    poke_eventloop()
    200 
    201    eq({
    202      [1] = '>1',
    203      [2] = '1',
    204      [3] = '1',
    205    }, get_fold_levels())
    206 
    207    feed('u')
    208    poke_eventloop()
    209 
    210    eq({
    211      [1] = '>1',
    212      [2] = '1',
    213      [3] = '>1',
    214      [4] = '1',
    215    }, get_fold_levels())
    216 
    217    feed('3ggI#<Esc>')
    218    parse()
    219    poke_eventloop()
    220 
    221    eq({
    222      [1] = '>1',
    223      [2] = '1',
    224      [3] = '>2',
    225      [4] = '2',
    226    }, get_fold_levels())
    227 
    228    feed('x')
    229    parse()
    230    poke_eventloop()
    231 
    232    eq({
    233      [1] = '>1',
    234      [2] = '1',
    235      [3] = '>1',
    236      [4] = '1',
    237    }, get_fold_levels())
    238  end)
    239 
    240  it('handles changes that trigger multiple on_bytes', function()
    241    insert([[
    242 function f()
    243  asdf()
    244  asdf()
    245 end
    246 -- comment]])
    247 
    248    exec_lua(function()
    249      vim.treesitter.query.set(
    250        'lua',
    251        'folds',
    252        '[(function_declaration) (parameters) (arguments)] @fold'
    253      )
    254    end)
    255    parse('lua')
    256 
    257    eq({
    258      [1] = '>1',
    259      [2] = '1',
    260      [3] = '1',
    261      [4] = '1',
    262      [5] = '0',
    263    }, get_fold_levels())
    264 
    265    command('1,4join')
    266    poke_eventloop()
    267 
    268    eq({
    269      [1] = '0',
    270      [2] = '0',
    271    }, get_fold_levels())
    272 
    273    feed('u')
    274    poke_eventloop()
    275 
    276    eq({
    277      [1] = '>1',
    278      [2] = '1',
    279      [3] = '1',
    280      [4] = '1',
    281      [5] = '0',
    282    }, get_fold_levels())
    283  end)
    284 
    285  it('handles multiple folds that overlap at the end and start', function()
    286    insert([[
    287 function f()
    288  g(
    289    function()
    290      asdf()
    291    end, function()
    292    end
    293  )
    294 end]])
    295 
    296    exec_lua(function()
    297      vim.treesitter.query.set(
    298        'lua',
    299        'folds',
    300        '[(function_declaration) (function_definition) (parameters) (arguments)] @fold'
    301      )
    302    end)
    303    parse('lua')
    304 
    305    -- If fold1.stop = fold2.start, then move fold1's stop up so that fold2.start gets proper level.
    306    eq({
    307      [1] = '>1',
    308      [2] = '>2',
    309      [3] = '>3',
    310      [4] = '3',
    311      [5] = '>3',
    312      [6] = '3',
    313      [7] = '2',
    314      [8] = '1',
    315    }, get_fold_levels())
    316 
    317    command('1,8join')
    318    feed('u')
    319    poke_eventloop()
    320 
    321    eq({
    322      [1] = '>1',
    323      [2] = '>2',
    324      [3] = '>3',
    325      [4] = '3',
    326      [5] = '>3',
    327      [6] = '3',
    328      [7] = '2',
    329      [8] = '1',
    330    }, get_fold_levels())
    331  end)
    332 
    333  it('handles multiple folds that start at the same line', function()
    334    insert([[
    335 function f(a)
    336  if #(g({
    337    k = v,
    338  })) > 0 then
    339    return
    340  end
    341 end]])
    342 
    343    exec_lua(function()
    344      vim.treesitter.query.set(
    345        'lua',
    346        'folds',
    347        '[(if_statement) (function_declaration) (parameters) (arguments) (table_constructor)] @fold'
    348      )
    349    end)
    350    parse('lua')
    351 
    352    eq({
    353      [1] = '>1',
    354      [2] = '>3',
    355      [3] = '3',
    356      [4] = '3',
    357      [5] = '2',
    358      [6] = '2',
    359      [7] = '1',
    360    }, get_fold_levels())
    361 
    362    command('2,6join')
    363    poke_eventloop()
    364 
    365    eq({
    366      [1] = '>1',
    367      [2] = '1',
    368      [3] = '1',
    369    }, get_fold_levels())
    370 
    371    feed('u')
    372    poke_eventloop()
    373 
    374    eq({
    375      [1] = '>1',
    376      [2] = '>3',
    377      [3] = '3',
    378      [4] = '3',
    379      [5] = '2',
    380      [6] = '2',
    381      [7] = '1',
    382    }, get_fold_levels())
    383  end)
    384 
    385  it('takes account of relevant options', function()
    386    insert([[
    387 # h1
    388 t1
    389 ## h2
    390 t2
    391 ### h3
    392 t3]])
    393 
    394    exec_lua([[vim.treesitter.query.set('markdown', 'folds', '(section) @fold')]])
    395    parse('markdown')
    396 
    397    command([[set foldminlines=2]])
    398 
    399    eq({
    400      [1] = '>1',
    401      [2] = '1',
    402      [3] = '>2',
    403      [4] = '2',
    404      [5] = '2',
    405      [6] = '2',
    406    }, get_fold_levels())
    407 
    408    command([[set foldminlines=1 foldnestmax=1]])
    409 
    410    eq({
    411      [1] = '>1',
    412      [2] = '1',
    413      [3] = '1',
    414      [4] = '1',
    415      [5] = '1',
    416      [6] = '1',
    417    }, get_fold_levels())
    418  end)
    419 
    420  it('handles quantified patterns', function()
    421    insert([[
    422 -- hello
    423 -- hello
    424 -- hello
    425 -- hello
    426 -- hello
    427 -- hello]])
    428 
    429    exec_lua([[vim.treesitter.query.set('lua', 'folds', '(comment)+ @fold')]])
    430    parse('lua')
    431 
    432    eq({
    433      [1] = '>1',
    434      [2] = '1',
    435      [3] = '1',
    436      [4] = '1',
    437      [5] = '1',
    438      [6] = '1',
    439    }, get_fold_levels())
    440  end)
    441 
    442  it('updates folds in all windows', function()
    443    local screen = Screen.new(60, 48)
    444    screen:set_default_attr_ids({
    445      [1] = { background = Screen.colors.Grey, foreground = Screen.colors.DarkBlue },
    446      [2] = { bold = true, foreground = Screen.colors.Blue1 },
    447      [3] = { bold = true, reverse = true },
    448      [4] = { reverse = true },
    449    })
    450 
    451    parse('c')
    452    command([[set foldmethod=expr foldexpr=v:lua.vim.treesitter.foldexpr() foldcolumn=1]])
    453    command('split')
    454 
    455    insert(test_text)
    456 
    457    screen:expect {
    458      grid = [[
    459      {1:-}void ui_refresh(void)                                      |
    460      {1:│}{                                                          |
    461      {1:│}  int width = INT_MAX, height = INT_MAX;                   |
    462      {1:│}  bool ext_widgets[kUIExtCount];                           |
    463      {1:-}  for (UIExtension i = 0; (int)i < kUIExtCount; i++) {     |
    464      {1:2}    ext_widgets[i] = true;                                 |
    465      {1:2}  }                                                        |
    466      {1:│}                                                           |
    467      {1:│}  bool inclusive = ui_override();                          |
    468      {1:-}  for (size_t i = 0; i < ui_count; i++) {                  |
    469      {1:2}    UI *ui = uis[i];                                       |
    470      {1:2}    width = MIN(ui->width, width);                         |
    471      {1:2}    height = MIN(ui->height, height);                      |
    472      {1:2}    foo = BAR(ui->bazaar, bazaar);                         |
    473      {1:-}    for (UIExtension j = 0; (int)j < kUIExtCount; j++) {   |
    474      {1:3}      ext_widgets[j] &= (ui->ui_ext[j] || inclusive);      |
    475      {1:3}    }                                                      |
    476      {1:2}  }                                                        |
    477      {1:│}^}                                                          |
    478      {2:~                                                           }|*4
    479      {3:[No Name] [+]                                               }|
    480      {1:-}void ui_refresh(void)                                      |
    481      {1:│}{                                                          |
    482      {1:│}  int width = INT_MAX, height = INT_MAX;                   |
    483      {1:│}  bool ext_widgets[kUIExtCount];                           |
    484      {1:-}  for (UIExtension i = 0; (int)i < kUIExtCount; i++) {     |
    485      {1:2}    ext_widgets[i] = true;                                 |
    486      {1:2}  }                                                        |
    487      {1:│}                                                           |
    488      {1:│}  bool inclusive = ui_override();                          |
    489      {1:-}  for (size_t i = 0; i < ui_count; i++) {                  |
    490      {1:2}    UI *ui = uis[i];                                       |
    491      {1:2}    width = MIN(ui->width, width);                         |
    492      {1:2}    height = MIN(ui->height, height);                      |
    493      {1:2}    foo = BAR(ui->bazaar, bazaar);                         |
    494      {1:-}    for (UIExtension j = 0; (int)j < kUIExtCount; j++) {   |
    495      {1:3}      ext_widgets[j] &= (ui->ui_ext[j] || inclusive);      |
    496      {1:3}    }                                                      |
    497      {1:2}  }                                                        |
    498      {1:│}}                                                          |
    499      {2:~                                                           }|*3
    500      {4:[No Name] [+]                                               }|
    501                                                                  |
    502    ]],
    503    }
    504 
    505    command('1,2d')
    506 
    507    screen:expect {
    508      grid = [[
    509      {1: }  ^int width = INT_MAX, height = INT_MAX;                   |
    510      {1: }  bool ext_widgets[kUIExtCount];                           |
    511      {1:-}  for (UIExtension i = 0; (int)i < kUIExtCount; i++) {     |
    512      {1:│}    ext_widgets[i] = true;                                 |
    513      {1:│}  }                                                        |
    514      {1: }                                                           |
    515      {1: }  bool inclusive = ui_override();                          |
    516      {1:-}  for (size_t i = 0; i < ui_count; i++) {                  |
    517      {1:│}    UI *ui = uis[i];                                       |
    518      {1:│}    width = MIN(ui->width, width);                         |
    519      {1:│}    height = MIN(ui->height, height);                      |
    520      {1:│}    foo = BAR(ui->bazaar, bazaar);                         |
    521      {1:-}    for (UIExtension j = 0; (int)j < kUIExtCount; j++) {   |
    522      {1:2}      ext_widgets[j] &= (ui->ui_ext[j] || inclusive);      |
    523      {1:2}    }                                                      |
    524      {1:│}  }                                                        |
    525      {1: }}                                                          |
    526      {2:~                                                           }|*6
    527      {3:[No Name] [+]                                               }|
    528      {1: }  int width = INT_MAX, height = INT_MAX;                   |
    529      {1: }  bool ext_widgets[kUIExtCount];                           |
    530      {1:-}  for (UIExtension i = 0; (int)i < kUIExtCount; i++) {     |
    531      {1:│}    ext_widgets[i] = true;                                 |
    532      {1:│}  }                                                        |
    533      {1: }                                                           |
    534      {1: }  bool inclusive = ui_override();                          |
    535      {1:-}  for (size_t i = 0; i < ui_count; i++) {                  |
    536      {1:│}    UI *ui = uis[i];                                       |
    537      {1:│}    width = MIN(ui->width, width);                         |
    538      {1:│}    height = MIN(ui->height, height);                      |
    539      {1:│}    foo = BAR(ui->bazaar, bazaar);                         |
    540      {1:-}    for (UIExtension j = 0; (int)j < kUIExtCount; j++) {   |
    541      {1:2}      ext_widgets[j] &= (ui->ui_ext[j] || inclusive);      |
    542      {1:2}    }                                                      |
    543      {1:│}  }                                                        |
    544      {1: }}                                                          |
    545      {2:~                                                           }|*5
    546      {4:[No Name] [+]                                               }|
    547                                                                  |
    548    ]],
    549    }
    550 
    551    feed([[O<C-u><C-r>"<BS><Esc>]])
    552 
    553    screen:expect {
    554      grid = [[
    555      {1:-}void ui_refresh(void)                                      |
    556      {1:│}^{                                                          |
    557      {1:│}  int width = INT_MAX, height = INT_MAX;                   |
    558      {1:│}  bool ext_widgets[kUIExtCount];                           |
    559      {1:-}  for (UIExtension i = 0; (int)i < kUIExtCount; i++) {     |
    560      {1:2}    ext_widgets[i] = true;                                 |
    561      {1:2}  }                                                        |
    562      {1:│}                                                           |
    563      {1:│}  bool inclusive = ui_override();                          |
    564      {1:-}  for (size_t i = 0; i < ui_count; i++) {                  |
    565      {1:2}    UI *ui = uis[i];                                       |
    566      {1:2}    width = MIN(ui->width, width);                         |
    567      {1:2}    height = MIN(ui->height, height);                      |
    568      {1:2}    foo = BAR(ui->bazaar, bazaar);                         |
    569      {1:-}    for (UIExtension j = 0; (int)j < kUIExtCount; j++) {   |
    570      {1:3}      ext_widgets[j] &= (ui->ui_ext[j] || inclusive);      |
    571      {1:3}    }                                                      |
    572      {1:2}  }                                                        |
    573      {1:│}}                                                          |
    574      {2:~                                                           }|*4
    575      {3:[No Name] [+]                                               }|
    576      {1:-}void ui_refresh(void)                                      |
    577      {1:│}{                                                          |
    578      {1:│}  int width = INT_MAX, height = INT_MAX;                   |
    579      {1:│}  bool ext_widgets[kUIExtCount];                           |
    580      {1:-}  for (UIExtension i = 0; (int)i < kUIExtCount; i++) {     |
    581      {1:2}    ext_widgets[i] = true;                                 |
    582      {1:2}  }                                                        |
    583      {1:│}                                                           |
    584      {1:│}  bool inclusive = ui_override();                          |
    585      {1:-}  for (size_t i = 0; i < ui_count; i++) {                  |
    586      {1:2}    UI *ui = uis[i];                                       |
    587      {1:2}    width = MIN(ui->width, width);                         |
    588      {1:2}    height = MIN(ui->height, height);                      |
    589      {1:2}    foo = BAR(ui->bazaar, bazaar);                         |
    590      {1:-}    for (UIExtension j = 0; (int)j < kUIExtCount; j++) {   |
    591      {1:3}      ext_widgets[j] &= (ui->ui_ext[j] || inclusive);      |
    592      {1:3}    }                                                      |
    593      {1:2}  }                                                        |
    594      {1:│}}                                                          |
    595      {2:~                                                           }|*3
    596      {4:[No Name] [+]                                               }|
    597                                                                  |
    598    ]],
    599    }
    600  end)
    601 
    602  it("doesn't open folds in diff mode", function()
    603    local screen = Screen.new(60, 36)
    604 
    605    parse('c')
    606    command(
    607      [[set foldmethod=expr foldexpr=v:lua.vim.treesitter.foldexpr() foldcolumn=1 foldlevel=9]]
    608    )
    609    insert(test_text)
    610    command('16d')
    611 
    612    command('new')
    613    insert(test_text)
    614 
    615    command('windo diffthis')
    616    feed('do')
    617 
    618    screen:expect([[
    619      {7:+ }{13:+-- 19 lines: void ui_refresh(void)·······················}|
    620      {1:~                                                           }|*16
    621      {2:[No Name] [+]                                               }|
    622      {7:+ }{13:^+-- 19 lines: void ui_refresh(void)·······················}|
    623      {1:~                                                           }|*15
    624      {3:[No Name] [+]                                               }|
    625                                                                  |
    626    ]])
    627  end)
    628 
    629  it('does not extend closed fold with `o`/`O`', function()
    630    local screen = Screen.new(60, 24)
    631 
    632    insert(test_text)
    633    parse('c')
    634    command([[set foldmethod=expr foldexpr=v:lua.vim.treesitter.foldexpr() foldcolumn=1]])
    635 
    636    feed('5ggzco')
    637    screen:expect({
    638      grid = [[
    639        {7:-}void ui_refresh(void)                                      |
    640        {7:│}{                                                          |
    641        {7:│}  int width = INT_MAX, height = INT_MAX;                   |
    642        {7:│}  bool ext_widgets[kUIExtCount];                           |
    643        {7:+}{13:+---  3 lines: for (UIExtension i = 0; (int)i < kUIExtCount}|
    644        {7:│}^                                                           |
    645        {7:│}                                                           |
    646        {7:│}  bool inclusive = ui_override();                          |
    647        {7:-}  for (size_t i = 0; i < ui_count; i++) {                  |
    648        {7:2}    UI *ui = uis[i];                                       |
    649        {7:2}    width = MIN(ui->width, width);                         |
    650        {7:2}    height = MIN(ui->height, height);                      |
    651        {7:2}    foo = BAR(ui->bazaar, bazaar);                         |
    652        {7:-}    for (UIExtension j = 0; (int)j < kUIExtCount; j++) {   |
    653        {7:3}      ext_widgets[j] &= (ui->ui_ext[j] || inclusive);      |
    654        {7:3}    }                                                      |
    655        {7:2}  }                                                        |
    656        {7:│}}                                                          |
    657        {1:~                                                           }|*5
    658        {5:-- INSERT --}                                                |
    659      ]],
    660    })
    661 
    662    feed('<Esc>O')
    663    screen:expect({
    664      grid = [[
    665        {7:-}void ui_refresh(void)                                      |
    666        {7:│}{                                                          |
    667        {7:│}  int width = INT_MAX, height = INT_MAX;                   |
    668        {7:│}  bool ext_widgets[kUIExtCount];                           |
    669        {7:+}{13:+---  3 lines: for (UIExtension i = 0; (int)i < kUIExtCount}|
    670        {7:│}^                                                           |
    671        {7:│}                                                           |*2
    672        {7:│}  bool inclusive = ui_override();                          |
    673        {7:-}  for (size_t i = 0; i < ui_count; i++) {                  |
    674        {7:2}    UI *ui = uis[i];                                       |
    675        {7:2}    width = MIN(ui->width, width);                         |
    676        {7:2}    height = MIN(ui->height, height);                      |
    677        {7:2}    foo = BAR(ui->bazaar, bazaar);                         |
    678        {7:-}    for (UIExtension j = 0; (int)j < kUIExtCount; j++) {   |
    679        {7:3}      ext_widgets[j] &= (ui->ui_ext[j] || inclusive);      |
    680        {7:3}    }                                                      |
    681        {7:2}  }                                                        |
    682        {7:│}}                                                          |
    683        {1:~                                                           }|*4
    684        {5:-- INSERT --}                                                |
    685      ]],
    686    })
    687  end)
    688 
    689  it("doesn't open folds that are not touched", function()
    690    local screen = Screen.new(40, 8)
    691    screen:set_default_attr_ids({
    692      [1] = { foreground = Screen.colors.DarkBlue, background = Screen.colors.Gray },
    693      [2] = { foreground = Screen.colors.DarkBlue, background = Screen.colors.LightGray },
    694      [3] = { foreground = Screen.colors.Blue1, bold = true },
    695      [4] = { bold = true },
    696    })
    697 
    698    insert([[
    699 # h1
    700 t1
    701 # h2
    702 t2]])
    703    exec_lua([[vim.treesitter.query.set('markdown', 'folds', '(section) @fold')]])
    704    parse('markdown')
    705    command(
    706      [[set foldmethod=expr foldexpr=v:lua.vim.treesitter.foldexpr() foldcolumn=1 foldlevel=0]]
    707    )
    708 
    709    feed('ggzojo')
    710    screen:expect([[
    711      {1:-}# h1                                   |
    712      {1:│}t1                                     |
    713      {1:-}^                                       |
    714      {1:+}{2:+--  2 lines: # h2·····················}|
    715      {3:~                                       }|*3
    716      {4:-- INSERT --}                            |
    717    ]])
    718 
    719    -- TODO(tomtomjhj): `u` spuriously opens the fold (#26499).
    720    feed('<Esc>uzMggzodd')
    721    screen:expect([[
    722      {1:-}^t1                                     |
    723      {1:-}# h2                                   |
    724      {1:│}t2                                     |
    725      {3:~                                       }|*4
    726      1 line less; before #2  {MATCH:.*}|
    727    ]])
    728  end)
    729 
    730  it("doesn't call get_parser too often when parser is not available", function()
    731    -- spy on vim.treesitter.get_parser() to keep track of how many times it is called
    732    exec_lua(function()
    733      _G.count = 0
    734      vim.treesitter.get_parser = (function(wrapped)
    735        return function(...)
    736          _G.count = _G.count + 1
    737          return wrapped(...)
    738        end
    739      end)(vim.treesitter.get_parser)
    740    end)
    741 
    742    insert(test_text)
    743    command [[
    744      set filetype=some_filetype_without_treesitter_parser
    745      set foldmethod=expr foldexpr=v:lua.vim.treesitter.foldexpr() foldcolumn=1 foldlevel=0
    746    ]]
    747 
    748    -- foldexpr will return '0' for all lines
    749    local levels = get_fold_levels() ---@type integer[]
    750    eq(19, #levels)
    751    for lnum, level in ipairs(levels) do
    752      eq('0', level, string.format("foldlevel[%d] == %s; expected '0'", lnum, level))
    753    end
    754 
    755    eq(
    756      1,
    757      exec_lua [[ return _G.count ]],
    758      'count should not be as high as the # of lines; actually only once for the buffer.'
    759    )
    760  end)
    761 
    762  it('can detect a new parser and refresh folds accordingly', function()
    763    local name = t.tmpname()
    764    write_file(name, test_text)
    765    command('edit ' .. name)
    766    command [[
    767      set filetype=some_filetype_without_treesitter_parser
    768      set foldmethod=expr foldexpr=v:lua.vim.treesitter.foldexpr() foldcolumn=1 foldlevel=0
    769    ]]
    770 
    771    -- foldexpr will return '0' for all lines
    772    local function expect_no_folds()
    773      local levels = get_fold_levels() ---@type integer[]
    774      eq(19, #levels)
    775      for lnum, level in ipairs(levels) do
    776        eq('0', level, string.format("foldlevel[%d] == %s; expected '0'", lnum, level))
    777      end
    778    end
    779    expect_no_folds()
    780 
    781    -- reload buffer as c filetype to simulate new parser being found
    782    feed('GA// vim: ft=c<Esc>')
    783    command([[write | edit]])
    784    local foldlevels = {
    785      [1] = '>1',
    786      [2] = '1',
    787      [3] = '1',
    788      [4] = '1',
    789      [5] = '>2',
    790      [6] = '2',
    791      [7] = '2',
    792      [8] = '1',
    793      [9] = '1',
    794      [10] = '>2',
    795      [11] = '2',
    796      [12] = '2',
    797      [13] = '2',
    798      [14] = '2',
    799      [15] = '>3',
    800      [16] = '3',
    801      [17] = '3',
    802      [18] = '2',
    803      [19] = '1',
    804    }
    805    eq(foldlevels, get_fold_levels())
    806 
    807    -- only changing filetype should change the parser again
    808    command('set ft=some_filetype_without_treesitter_parser')
    809    expect_no_folds()
    810 
    811    command('set ft=c')
    812    eq(foldlevels, get_fold_levels())
    813  end)
    814 
    815  it('no error when deleting lines at end of buffer with fml=0', function()
    816    local screen = Screen.new(40, 2)
    817    insert('hello')
    818    parse('markdown')
    819    command('set foldmethod=expr foldexpr=v:lua.vim.treesitter.foldexpr() foldminlines=0')
    820    feed('o<Esc>dd')
    821    screen:expect([[
    822      ^hello                                   |
    823                                              |
    824    ]])
    825  end)
    826 end)