ext/ext-editor/embed/lite/data/plugins/fsutils.lua
-- mod-version:1 -- lite-xl 1.16
local core = require "core"
local common = require "core.common"
local command = require "core.command"

local fsutils = {}

function fsutils.iterdir(dir)
  local stack = { dir }
  return function()
    local path = table.remove(stack)
    if not path then return end

    for _, file in ipairs(system.list_dir(path) or {}) do
      stack[#stack + 1] = path .. PATHSEP .. file
    end

    return path, system.get_file_info(path)
  end
end

function fsutils.delete(dir, yield)
  local dirs = {}
  local n = 0
  for filename, stat in fsutils.iterdir(dir) do
    if stat.type == "dir" then
      -- this will later allow us to delete the dirs in correct sequence
      table.insert(dirs, filename)
    else
      os.remove(filename)
      if yield then
        n = n + 1
        coroutine.yield(n)
      end
    end
  end
  for i = #dirs, 1, -1 do
    os.remove(dirs[i])
    if yield then
      n = n + 1
      coroutine.yield(n)
    end
  end
end

function fsutils.move(oldname, newname)
  os.rename(oldname, newname)
end

function fsutils.split(path)
  local segments = {}
  local pos = 1
  while true do
    local s, e = string.find(path, "[/\\]+", pos)
    if not s then break end
    table.insert(segments, string.sub(path, pos, s - 1))
    pos = e + 1
  end
  table.insert(list, string.sub(path, pos))

  if segments[#segments] == '' then
    table.remove(segments)
  end

  return segments
end

function fsutils.normalize(path)
  return table.concat(fsutils.split(path), PATHSEP)
end

function fsutils.normalize_posix(path)
  return table.concat(fsutils.split(path), '/')
end

function fsutils.mkdir(path)
  local segments = fsutils.split(path)
  if system.mkdir then
    local p = ""
    for i = 1, #segments do
      p = table.concat(segments, PATHSEP, 1, i)
    end

    if p == "" then
      return nil, "path empty", p
    end

    local stat = system.get_file_info(p)
    if stat and stat.type == "file" then
      return nil, "path exists as a file", p
    end
    local ok, err = system.mkdir(p)
    if not ok then
      return nil, err, p
    end
  else
    -- just wing it lol
    system.exec(string.format(PLATFORM == "Windows" and "setlocal enableextensions & mkdir %q" or "mkdir -p %q", fsutils.normalize(path)))
  end
end

local function async_exec(f, cb)
  cb = cb or function() end
  local co = coroutine.create(f)
  local function resolve(...)
    local ok, exec_body = coroutine.resume(co, ...)
    if not ok then
      error(debug.traceback(co, exec_body))
    end
    if coroutine.status(co) ~= "dead" then
      exec_body(resolve)
    end
  end
  resolve(cb)
end

local function prompt(text, suggest)
  return coroutine.yield(function(resolve)
    core.command_view:enter(text, resolve, suggest)
  end)
end

command.add(nil, {
  ["files:delete"] = function()
    async_exec(function()
      local path = prompt("Delete", common.path_suggest)

      core.add_thread(function()
        -- we use a wrapping coroutine to get status
        local function delete()
          return coroutine.wrap(function() fsutils.delete(path, true) end)
        end

        for n in delete() do
          if n % 100 == 0 then
            core.log("Deleted %d items...", n)
            coroutine.yield()
          end
        end
        core.log("%q deleted.", path)
      end)
    end)
  end,
  ["files:move"] = function()
    async_exec(function()
      local oldname = prompt("Move", common.path_suggest)
      local newname = prompt("To", common.path_suggest)

      fsutils.move(oldname, newname)
      core.log("Moved %q to %q", oldname, newname)
    end)
  end
})

if not command.map["files:create-directory"] then
  command.add(nil, {
    ["files:create-directory"] = function()
      async_exec(function()
        local path = prompt("Name", common.path_suggest)
        fsutils.mkdir(path)
        core.log("%q created.", path)
      end)
    end
  })
end

return fsutils