Skip to content

Conversation

@Uanela
Copy link
Collaborator

@Uanela Uanela commented Dec 27, 2025

This refactor moves directory change operations from the actions.root module into the Explorer class, improving code organization and encapsulation.

Changes:

  • Moved change_dir, force_dirchange, and dir_up methods to Explorer class
  • Deleted actions/root/change-dir.lua and actions/root/dir-up.lua files
  • Removed actions.root module from actions/init.lua
  • Added helper methods: is_window_event, clean_input_cwd, prevent_cwd_change,
    add_profiling_to, should_change_dir, and cd
  • Introduced explorer_fn helper in nvim-tree.lua for safe explorer method calls
  • Updated all references to use explorer methods instead of actions.root
  • Renamed M.current_tab to self.current_tab for proper class encapsulation
  • Renamed internal _foldername parameter to folder_name for consistency

Resolves #2988

@Uanela
Copy link
Collaborator Author

Uanela commented Dec 27, 2025

@alex-courtis this one was really interesting to refactor, I was able to really strengthen my knowledge in lua classes even though the problems weren't that hard.

I will get into the next ones.

@Uanela Uanela removed the request for review from Akmadan23 December 27, 2025 12:15
@alex-courtis
Copy link
Member

even though the problems weren't that hard.

You underestimate yourself. This was not an easy change.

FYI you now have permissions to push branches directly to this repo, so you don't need to use your fork next time.

@Uanela Uanela requested a review from alex-courtis December 28, 2025 10:50
@Uanela
Copy link
Collaborator Author

Uanela commented Dec 28, 2025

  • Prev changes made and tested.
  • Moved Api.tree.change_root_to_node into Explorer

Just a question

what is the profiling for?

@alex-courtis
Copy link
Member

alex-courtis commented Jan 2, 2026

Base Test Case Failure

Setup

cd /tmp
git clone git@github.com:rvaiya/keyd.git

change_root_to_node - fail

Open nvim in /tmp/keyd

:NvimTreeOpen

<C-]> data

Expected: data is new root
Actual: root has not changed

After R the correct root is shown

Analysis

core.init(foldername) destroys the explorer and creates a new one, hence the call toself.renderer:draw() fails as self has been destroyed.

We can debug this by adding lines that note the explorer's unique id:

-- in constructor
  self.uid_explorer = vim.loop.hrtime()
  log.line("dev", "Explorer:new uid_explorer=%d", self.uid_explorer)

-- destructor
function Explorer:destroy()
  log.line("dev", "Explorer:destroy uid_explorer=%d", self.uid_explorer)

-- change
function Explorer:force_dirchange(foldername, should_open_view)
  log.line("dev", "Explorer:force_dirchange uid_explorer=%d foldername=%s", self.uid_explorer, foldername)
  ---
    else
      log.line("dev", "Explorer:change_dir uid_explorer=%d calling renderer:draw()")
    self.renderer:draw()
   ---

You can see that the old explorer instance is destroyed and a new on created, but draw is called on the old one:

tail: '/home/alex/.local/state/nvim/nvim-tree.log' has appeared;  following new file
[2026-01-02 14:23:41] [dev] Explorer:new uid_explorer=5572366830148
[2026-01-02 14:23:45] [dev] Explorer:change_dir uid_explorer=5572366830148 input_cwd=/tmp/keyd/data
[2026-01-02 14:23:45] [dev] Explorer:force_dirchange uid_explorer=5572366830148 foldername=/tmp/keyd/data
[2026-01-02 14:23:45] [dev] Explorer:destroy uid_explorer=5572366830148
[2026-01-02 14:23:45] [dev] Explorer:new uid_explorer=5576582353809
[2026-01-02 14:23:45] [dev] Explorer:change_dir uid_explorer=5572366830148 calling renderer:draw()

Recommendation

Revert to old behaviour of getting the new explorer before drawing:

    if should_open_view then
      require("nvim-tree.lib").open()
    else
      -- TODO #2255
      -- The call to core.init destroyed this Explorer instance hence we need to fetch the new instance.
      local explorer = core.get_explorer()
      if explorer then
        explorer.renderer:draw()
      end
    end

This is not ideal at all, as an Explorer method from a destroyed instance is running.
This is a pragmatic, temporary solution. We don't have much option as core owns the singleton Explorer.
Once #2255 is complete and core is gone, it will be resolved.
There should be no issues with resource leaks as Explorer:destroy is called.
There should be no memory leaks as the old explorer will be garbage collected when this method ends.

@alex-courtis
Copy link
Member

what is the profiling for?

It's used to report performance of costly operations. We ask users to enable it when reporting Performance Issues

@alex-courtis
Copy link
Member

alex-courtis commented Jan 2, 2026

Base Test Case Plan

Change To Directory Node

Open nvim in /tmp/keyd
:NvimTreeOpen

<C-]> data

Expected: tree root is data

Change To File Node

Open nvim in /tmp/keyd
:NvimTreeOpen

Navigate to data/unicode.txt

<C-]> unicode.txt

Expected: tree root changes to data

Change Up Root Node

Open nvim in /tmp/keyd
:NvimTreeOpen

<C-]> on root

Expected: tree root changes /tmp

is_window_event

We must create a test cases that results in is_window_event returning false when calling Explorer:change_dir

Option Test Case Plan

sync_root_with_cwd = true

Open nvim in /tmp/keyd

:NvimTreeOpen

:cd data

Expected: tree root should change to data

nvim-tree.lua change_root

Open nvim in home

:NvimTreeOpen
:e /tmp/keyd/README.md
:NvimTreeFindFile!

Expected: tree root change to /tmp/keyd

nvim-tree.lua open_on_directory

Set hijack_directories = true

Open nvim in home
:NvimTreeOpen
change to previous win
:e /tmp

Expected: tree with root /tmp is opened

config.actions.change_dir.enable = true

Open nvim in /tmp/keyd

:NvimTreeOpen

<C-]> data
:pwd
Expected: /tmp/keyd/data

-
:pwd
Expected: /tmp/keyd

-
:pwd
Expected: /tmp

config.actions.change_dir.enable = false

Open nvim in /tmp/keyd

:NvimTreeOpen

<C-]> data
:pwd
Expected: /tmp/keyd

-
:pwd
Expected: /tmp/keyd

-
:pwd
Expected: /tmp/keyd

config.actions.change_dir.restrict_above_cwd = true

Set config.actions.change_dir.enable = true

Open nvim in /tmp/keyd

:NvimTreeOpen

-
Expected: no change dir

:pwd
Expected: /tmp/keyd

config.actions.change_dir.global = true

Set config.actions.change_dir.enable = true

Open nvim in /tmp/keyd
:NvimTreeOpen

<C-]> data
:pwd
Expected: /tmp/keyd/data

Switch to editor window
:pwd
Expected: /tmp/keyd

Copy link
Member

@alex-courtis alex-courtis left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This has turned out to be much harder than anticipated... there were a lot of landmines. We are close, please:

---@private
---@return boolean
function Explorer:should_change_dir()
return config.enable and vim.tbl_isempty(vim.v.event)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
return config.enable and vim.tbl_isempty(vim.v.event)
return config.actions.change_dir.enable and vim.tbl_isempty(vim.v.event)

local valid_dir = vim.fn.isdirectory(foldername) == 1 -- prevent problems on non existing dirs
if valid_dir then
if self:should_change_dir() then
self:cd(config.global, foldername)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
self:cd(config.global, foldername)
self:cd(config.actions.change_dir.global, foldername)

Comment on lines +777 to +788
function Explorer:change_dir_to_node(node)
if node.name == ".." or node:is(RootNode) then
self:change_dir("..")
elseif node:is(FileNode) and node.parent ~= nil then
self:change_dir(node.parent:last_group_node().absolute_path)
else
node = node:as(DirectoryNode)
if node then
self:change_dir(node:last_group_node().absolute_path)
end
end
end
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Many thanks for going the extra mile here - this looks so much better as this functionality should be owned by the Explorer instance.

Please do remember to add parameter annotations.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Multi Instance: Refactor: move root actions to Explorer

2 participants