current commit
260502.1 · feat: port fzf's config parser in lua
-- lua version of shellwords https://github.com/junegunn/fzf/blob/f7835825617bd38eb50349bd89fa2f39569c3068/src/options.go#L3854-L3857
local M = {}

local function isSpace(c)
  return c == " " or c == "\t" or c == "\r" or c == "\n"
end

local argNo = 0
local argSingle = 1
local argQuoted = 2

--- Parses a shell string into arguments similar to Go's shellwords.
--- @param line string
--- @return string[]? args
--- @return string? err
M.shellparse = function(line)
  if not line or type(line) ~= "string" then
    return {}
  end

  local args = {}
  local buf = ""
  local escaped, doubleQuoted, singleQuoted, backQuote, dollarQuote, comment =
      false, false, false, false, false, false

  local got = argNo

  local i = 1
  while i <= #line do
    local r = line:sub(i, i)
    local continue = false

    if comment then
      if r == "\n" then
        comment = false
      end
      continue = true
    end

    if not continue and escaped then
      if r == "t" then r = "\t" end
      if r == "n" then r = "\n" end
      buf = buf .. r
      escaped = false
      got = argSingle
      continue = true
    end

    if not continue and r == "\\" then
      if singleQuoted then
        buf = buf .. r
      else
        escaped = true
      end
      continue = true
    end

    if not continue and isSpace(r) then
      if singleQuoted or doubleQuoted or backQuote or dollarQuote then
        buf = buf .. r
      elseif got ~= argNo then
        args[#args + 1] = buf
        buf = ""
        got = argNo
      end
      continue = true
    end

    if not continue then
      if r == "`" then
        if not singleQuoted and not doubleQuoted and not dollarQuote then
          backQuote = not backQuote
        end
      elseif r == ")" then
        if not singleQuoted and not doubleQuoted and not backQuote then
          dollarQuote = not dollarQuote
        end
      elseif r == "(" then
        if not singleQuoted and not doubleQuoted and not backQuote then
          if not dollarQuote and buf:sub(-1) == "$" then
            dollarQuote = true
            buf = buf .. "("
            continue = true
          else
            return nil, "invalid command line string"
          end
        end
      elseif r == '"' then
        if not singleQuoted and not dollarQuote then
          if doubleQuoted then got = argQuoted end
          doubleQuoted = not doubleQuoted
          continue = true
        end
      elseif r == "'" then
        if not doubleQuoted and not dollarQuote then
          if singleQuoted then got = argQuoted end
          singleQuoted = not singleQuoted
          continue = true
        end
      elseif r == ";" or r == "&" or r == "|" or r == "<" or r == ">" then
        if not (escaped or singleQuoted or doubleQuoted or backQuote or dollarQuote) then
          if r == ">" and #buf > 0 then
            local c = buf:sub(1, 1)
            if c >= "0" and c <= "9" then
              got = argNo
            end
          end
          break
        end
      elseif r == "#" then
        -- ParseComment is true
        if #buf == 0 and not (escaped or singleQuoted or doubleQuoted) then
          comment = true
          continue = true
        end
      end
    end

    if not continue then
      got = argSingle
      buf = buf .. r
    end

    i = i + 1
  end

  if got ~= argNo then
    args[#args + 1] = buf
  end

  if escaped or singleQuoted or doubleQuoted or backQuote or dollarQuote then
    return nil, "invalid command line string"
  end

  return args
end

---@alias fzf-lua.FzfOpts { [string]: string }
---@param line string
---@return fzf-lua.FzfOpts
M.parse = function(line)
  local words = M.shellparse(line)
  if not words then return {} end
  local args = {}
  -- https://github.com/junegunn/fzf/blob/f7835825617bd38eb50349bd89fa2f39569c3068/src/options.go#L2618-L2622
  local i = 1
  while i <= #words do
    local word = words[i] ---@as string
    if word:sub(1, 2) == "--" then
      local field, value = word:match("^(.-)=(.*)$")
      if field then
        args[field] = value
      else -- space-separated: --key value (next token doesn't start with '-')
        local nxt = words[i + 1]
        if nxt and nxt:sub(1, 1) ~= "-" then
          args[word] = nxt
          i = i + 1
        else
          args[word] = true -- bare flag
        end
      end
    end
    i = i + 1
  end
  return args
end


local parsed ---@type fzf-lua.FzfOpts?
---@return fzf-lua.FzfOpts
M.get = function()
  if parsed then return parsed end
  local default_opts = os.getenv("FZF_DEFAULT_OPTS")
  local file = os.getenv("FZF_DEFAULT_OPTS_FILE")
  local content = file and require("fzf-lua.utils").read_file(file) or nil
  local opts1 = default_opts and M.parse(default_opts) or {}
  local opts2 = content and M.parse(content) or {}
  parsed = vim.tbl_deep_extend("keep", opts1, opts2)
  return parsed
end

return M