HiPhish / rainbow-delimiters.nvim

Rainbow delimiters for Neovim with Tree-sitter
https://gitlab.com/HiPhish/rainbow-delimiters.nvim
Apache License 2.0
490 stars 37 forks source link

[Bug]: open big file very slow #12

Closed HATTER-LONG closed 1 year ago

HATTER-LONG commented 1 year ago

Neovim version

0.9.1

Language affected

Lua

Query

No response

Strategy

No response

Description

open a big file such as this will very slow.

image

When I disable the plugin and restore it to normal:

image
HiPhish commented 1 year ago

Do you have the comment grammar installed? There are a lot of of comments interspersed into the code and each one creates a separate regions which needs to be parsed individually. I need to add a condition to prevent parsing child trees for which there is no query defined.

HATTER-LONG commented 1 year ago

I didn't install this. Do I need to install this and try again?

image
HiPhish commented 1 year ago

No, don't install the comment grammar. I just thought that maybe if you have it installed that it might be the source of your problem.

Anyway, I think I have found the issue: the file has 10797 Tree-sitter errors. This breaks it up into 10797 individual regions which need to be each parsed individually, which causes the massive slowdown. This many errors in a source file are just unreasonable and I cannot make it faster.

Some errors are always to be expected, your buffer will be in an invalid state most of the time, but this is too much. I recommend adjusting your C++ strategy to a function that gives up after a reasonable number of errors. Something like this:

local rbd = require 'rainbow-delimiters'

-- Loops through the tree and counts the number or errors.
local function give_up()
    local parser = vim.treesitter.get_parser()
    local errors = 0
    parser:for_each_tree(function(tree)
        if tree:root():has_error() then
            errors = errors + 1
        end
    end)
    if errors > 50 then return nil end
    return rbd.strategy.global
end

vim.g.treesitter = {
    strategy = {
        cpp = give_up
    }
}

If the strategy is a function it will be evaluated and the result will be used as the function. If the result is nil Rainbow Delimiters will abort. This is done on a per-file basis, so other files will work fine.

HATTER-LONG commented 1 year ago

Very thx for that, I think what you're referring to is vim.g.rainbow_delimiters, I made the following attempts, it's looks like working well, from 50s+ to 2s+:

    local rbd = require("rainbow-delimiters")

    -- Loops through the tree and counts the number or errors.
    local function give_up()
        print("give_up")
        local parser = vim.treesitter.get_parser()
        local errors = 0
        parser:for_each_tree(function(tree)
            if tree:root():has_error() then
                errors = errors + 1
            end
        end)
        if errors > 10 then
            return nil
        end
        return rbd.strategy.global
    end

    vim.g.rainbow_delimiters = {
        strategy = {
            [""] = require("rainbow-delimiters").strategy["global"],
            cpp = give_up,
            c = give_up,
        },
image

By the way I want ask what does the Tree-sitter errors refer? I am not very familiar with this aspect. Does it refer to errors in Tree-sitter analysis? This source file compiles normally in the project。

HiPhish commented 1 year ago

I think what you're referring to is vim.g.rainbow_delimiters

Yes, my mistake.

By the way I want ask what does the Tree-sitter errors refer?

It means that Tree-sitter encountered an error while parsing the file. This could be because the file does indeed contain syntax errors, but it could also be a bug in the C++ grammar or in the Tree-sitter implementation.

Tree-sitter is designed to be fault tolerant. A compiler will abort on the first syntax error it encounters, but Tree-sitter will keep chugging along with whatever sense it can make of the rest of your source code. This is because normally while you are editing a file it will be in an invalid state for some small region most of the time. You still want syntax highlighting to work at least on the correct portion of the code, you don't want syntax highlighting to only work when your code is valid.

This source file compiles normally in the project

That does not necessarily mean much when it comes to the C family. The C preprocessor can cause the compiler to skip sections of the file, and if these sections contain invalid code the compiler will never notice. However, the parser parses the entire file and it will notice syntax errors.