Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

bug: First invocation of Telescope aerial/AerialNavOpen shows incomplete symbol list #432

Open
pidgeon777 opened this issue Dec 14, 2024 · 1 comment
Labels
bug Something isn't working P2 Not a priority. PRs welcome

Comments

@pidgeon777
Copy link

Neovim version (nvim -v)

NVIM v0.10.2

Operating system/version

Windows 11 Pro 23H2

Output of :AerialInfo

Aerial Info
-----------
Filetype: c
Configured backends:
lsp (supported)
treesitter (supported)
markdown (not supported) [Filetype is not markdown]
man (not supported) [Filetype is not man]
Show symbols: Class, Constructor, Enum, Function, Interface, Module, Method, Struct

Describe the bug

When opening a C file buffer with clangd LSP running, the first invocation of Telescope aerial or AerialNavOpen shows a significantly reduced list of symbols. Subsequent invocations of the same commands show the complete list of symbols as expected.

What is the severity of this bug?

minor (annoyance)

Steps To Reproduce

  1. nvim -u repro.lua
  2. Open a C source file
  3. Wait for clangd LSP to attach
  4. Run :Telescope aerial or :AerialNavOpen
  5. Observe the limited number of symbols
  6. Run the same command again
  7. Observe that now much more symbols are properly displayed

Expected Behavior

The first invocation of Telescope aerial or AerialNavOpen should show the complete list of symbols immediately, without requiring a second invocation.

Minimal example file

test.c:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

// Costanti
#define MAX_STUDENTS 100
#define MAX_NAME_LENGTH 50
#define MIN_GRADE 0
#define MAX_GRADE 30

// Enumerazioni
typedef enum { FRESHMAN, SOPHOMORE, JUNIOR, SENIOR } StudentYear;

typedef enum { COMPUTER_SCIENCE, ENGINEERING, MATHEMATICS, PHYSICS } Department;

// Strutture
typedef struct {
  int day;
  int month;
  int year;
} Date;

typedef struct {
  char firstName[MAX_NAME_LENGTH];
  char lastName[MAX_NAME_LENGTH];
  int id;
  float gpa;
  StudentYear year;
  Department dept;
  Date birthDate;
} Student;

// Variabili globali
static Student studentDatabase[MAX_STUDENTS];
static int totalStudents = 0;
int lastAssignedId = 1000;

// Prototipi delle funzioni
void initializeDatabase(void);
Student *createStudent(const char *firstName, const char *lastName,
                       Date birthDate);
void deleteStudent(int id);
float calculateAverageGPA(Department dept);
void printStudentInfo(const Student *student);
StudentYear advanceYear(StudentYear currentYear);
int isValidDate(Date date);

// Implementazione delle funzioni
void initializeDatabase(void) {
  totalStudents = 0;
  memset(studentDatabase, 0, sizeof(studentDatabase));
  printf("Database initialized successfully\n");
}

Student *createStudent(const char *firstName, const char *lastName,
                       Date birthDate) {
  if (totalStudents >= MAX_STUDENTS) {
    printf("Error: Database is full\n");
    return NULL;
  }

  Student *newStudent = &studentDatabase[totalStudents];
  strncpy(newStudent->firstName, firstName, MAX_NAME_LENGTH - 1);
  strncpy(newStudent->lastName, lastName, MAX_NAME_LENGTH - 1);
  newStudent->id = lastAssignedId++;
  newStudent->gpa = 0.0f;
  newStudent->year = FRESHMAN;
  newStudent->birthDate = birthDate;

  totalStudents++;
  return newStudent;
}

void deleteStudent(int id) {
  int found = 0;
  for (int i = 0; i < totalStudents; i++) {
    if (studentDatabase[i].id == id) {
      found = 1;
      for (int j = i; j < totalStudents - 1; j++) {
        studentDatabase[j] = studentDatabase[j + 1];
      }
      totalStudents--;
      break;
    }
  }

  if (!found) {
    printf("Error: Student not found\n");
  }
}

float calculateAverageGPA(Department dept) {
  float totalGPA = 0.0f;
  int count = 0;

  for (int i = 0; i < totalStudents; i++) {
    if (studentDatabase[i].dept == dept) {
      totalGPA += studentDatabase[i].gpa;
      count++;
    }
  }

  return count > 0 ? totalGPA / count : 0.0f;
}

void printStudentInfo(const Student *student) {
  if (student == NULL) {
    printf("Error: Invalid student pointer\n");
    return;
  }

  printf("Student ID: %d\n", student->id);
  printf("Name: %s %s\n", student->firstName, student->lastName);
  printf("GPA: %.2f\n", student->gpa);
  printf("Birth Date: %d/%d/%d\n", student->birthDate.day,
         student->birthDate.month, student->birthDate.year);
}

StudentYear advanceYear(StudentYear currentYear) {
  switch (currentYear) {
  case FRESHMAN:
    return SOPHOMORE;
  case SOPHOMORE:
    return JUNIOR;
  case JUNIOR:
    return SENIOR;
  case SENIOR:
  default:
    return SENIOR;
  }
}

int isValidDate(Date date) {
  int daysInMonth[] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};

  if (date.year < 1900 || date.year > 2024)
    return 0;
  if (date.month < 1 || date.month > 12)
    return 0;
  if (date.day < 1)
    return 0;

  // Controllo anno bisestile
  if (date.month == 2) {
    if ((date.year % 4 == 0 && date.year % 100 != 0) || date.year % 400 == 0) {
      if (date.day > 29)
        return 0;
    }
  }

  if (date.day > daysInMonth[date.month - 1])
    return 0;

  return 1;
}

// Funzione principale per test
int main(void) {
  initializeDatabase();

  Date birthDate1 = {15, 5, 2000};
  Date birthDate2 = {22, 7, 1999};

  Student *student1 = createStudent("John", "Doe", birthDate1);
  Student *student2 = createStudent("Jane", "Smith", birthDate2);

  if (student1) {
    student1->dept = COMPUTER_SCIENCE;
    student1->gpa = 28.5f;
    printStudentInfo(student1);
  }

  if (student2) {
    student2->dept = ENGINEERING;
    student2->gpa = 29.0f;
    printStudentInfo(student2);
  }

  float csAverage = calculateAverageGPA(COMPUTER_SCIENCE);
  printf("Computer Science Average GPA: %.2f\n", csAverage);

  deleteStudent(1000); // Elimina il primo studente

  return 0;
}

Minimal init.lua

-- DO NOT change the paths and don't remove the colorscheme
local root = vim.fn.fnamemodify("./.repro", ":p")

-- set stdpaths to use .repro
for _, name in ipairs({ "config", "data", "state", "cache" }) do
  vim.env[("XDG_%s_HOME"):format(name:upper())] = root .. "/" .. name
end

-- bootstrap lazy
local lazypath = root .. "/plugins/lazy.nvim"
if not vim.loop.fs_stat(lazypath) then
  vim.fn.system({
    "git",
    "clone",
    "--filter=blob:none",
    "--single-branch",
    "https://github.com/folke/lazy.nvim.git",
    lazypath,
  })
end
vim.opt.runtimepath:prepend(lazypath)

-- install plugins
local plugins = {
  "folke/tokyonight.nvim",
  {
    "stevearc/aerial.nvim",
    config = function()
      require("aerial").setup({
        attach_mode = "global",
        backends = { "lsp", "treesitter", "markdown", "man" },
        show_guides = true,
        layout = {
          resize_to_content = false,
          win_opts = {
            winhl = "Normal:NormalFloat,FloatBorder:NormalFloat,SignColumn:SignColumnSB",
            signcolumn = "yes",
            statuscolumn = " ",
          },
        },
      })
    end,
  },
  {
    "nvim-telescope/telescope.nvim",
    dependencies = {
      "nvim-lua/plenary.nvim",
    },
    config = function()
      require("telescope").load_extension("aerial")
    end,
  },
  {
    "nvim-treesitter/nvim-treesitter",
    build = ":TSUpdate",
    config = function()
      require("nvim-treesitter.configs").setup({
        ensure_installed = { "c", "lua" },
        auto_install = true,
        highlight = { enable = true },
      })
    end,
  },
  {
    "neovim/nvim-lspconfig",
    config = function()
      require("lspconfig").clangd.setup({})
    end,
  },
}

require("lazy").setup(plugins, {
  root = root .. "/plugins",
})

vim.cmd.colorscheme("tokyonight")

Additional context

AerialOpen properly shows the full symbol list even at the first command execution.

@pidgeon777 pidgeon777 added the bug Something isn't working label Dec 14, 2024
@stevearc
Copy link
Owner

Aerial lazily attaches to the buffer, so even if the LSP is already attached, we don't fetch symbols until you do something with aerial. Fetching LSP symbols requires a RPC request, so there's a bit of a time delay while waiting for it. Since LSP symbols on first attach aren't immediate, we display the treesitter symbols first because those are available nearly immediately. When opening the normal aerial buffer, it will display the treesitter symbols first, then update to the LSP symbols. When using AerialNav or the telescope extension, there is no live-update when the LSP symbols come in.

As a workaround, I'd recommend disabling the treesitter backend for C.

@stevearc stevearc added the P2 Not a priority. PRs welcome label Dec 21, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working P2 Not a priority. PRs welcome
Projects
None yet
Development

No branches or pull requests

2 participants