1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
local micro = import("micro")
local config = import("micro/config")
local strings = import("strings")
local filepath = import("filepath")
local shell = import("micro/shell")
local util = import("micro/util")
local os = import("os")

function runTvFiles(bp)
  -- Change to the project directory (or file's directory as fallback) before running tv
  local originalDir = nil
  local workingDir = nil
  if bp.Buf.Path and bp.Buf.Path ~= "" then
    originalDir, _ = os.Getwd()
    local fileDir = filepath.Dir(bp.Buf.Path)

    -- Try to find project directory first
    local projectDir = find_project_dir(fileDir)
    if projectDir then
      workingDir = projectDir
      os.Chdir(projectDir)
    else
      workingDir = fileDir
      os.Chdir(fileDir)
    end
  end

  -- Run tv files command
  local output, err = shell.RunInteractiveShell("tv files", false, true)

  -- Restore original directory
  if originalDir then
    os.Chdir(originalDir)
  end

  if err ~= nil then
    micro.InfoBar():Error("TV files command failed: " .. tostring(err))
    return
  end

  if output == "" or output == nil then
    micro.InfoBar():Message("tv files: no files selected")
    return
  end

  -- Split output by newlines and open each file
  local files = {}
  for line in output:gmatch("[^\r\n]+") do
    local trimmed = line:match("^%s*(.-)%s*$") -- trim whitespace
    if trimmed ~= "" then
      -- Convert path relative to working directory
      local finalPath = trimmed
      if workingDir then
        finalPath = filepath.Join(workingDir, trimmed)
      end
      table.insert(files, {path = finalPath, line = nil})
    end
  end

  if #files == 0 then
    micro.InfoBar():Message("tv files: no files selected")
    return
  end

  -- Open each file in a new tab
  local openedCount = 0
  for _, fileInfo in ipairs(files) do
    local newTab = OpenTabFromFile(bp, fileInfo.path, fileInfo.line)
    if newTab then
      openedCount = openedCount + 1
    else
      micro.InfoBar():Error("File not found: " .. fileInfo.path)
    end
  end

  if openedCount > 0 then
    micro.InfoBar():Message("Opened " .. openedCount .. " files from TV files")
  end
end

function runTvText(bp, args)
  local cursor = bp.Cursor
  local tvCmd

  -- Check if there's a selection
  if cursor:HasSelection() then
    -- Get the selected text as bytes and convert to string
    local selectionBytes = cursor:GetSelection()
    local selection = util.String(selectionBytes)

    if selection ~= "" then
      -- Run with selected text
      local escapedSelection = strings.Replace(selection, "'", "'\"'\"'", -1)
      tvCmd = "tv text --input '" .. escapedSelection .. "'"
    else
      -- Empty selection, run without input
      tvCmd = "tv text"
    end
  else
    -- No selection, run without input
    tvCmd = "tv text"
  end

  -- Change to the project directory (or file's directory as fallback) before running tv
  local originalDir = nil
  local workingDir = nil
  if bp.Buf.Path and bp.Buf.Path ~= "" then
    originalDir, _ = os.Getwd()
    local fileDir = filepath.Dir(bp.Buf.Path)

    -- Try to find project directory first
    local projectDir = find_project_dir(fileDir)
    if projectDir then
      workingDir = projectDir
      os.Chdir(projectDir)
      micro.InfoBar():Message("Running tv from project root: " .. filepath.Base(projectDir))
    else
      workingDir = fileDir
      os.Chdir(fileDir)
    end
  end

  -- Run the tv command
  local output, err = shell.RunInteractiveShell(tvCmd, false, true)

  -- Restore original directory
  if originalDir then
    os.Chdir(originalDir)
  end

  if err ~= nil then
    micro.InfoBar():Error("TV command failed: " .. tostring(err))
    return
  end

  if output == "" or output == nil then
    micro.InfoBar():Message("tv: nothing selected")
    return
  end

  -- Split output by newlines and parse file:line format
  local files = {}
  for line in output:gmatch("[^\r\n]+") do
    local trimmed = line:match("^%s*(.-)%s*$") -- trim whitespace
    if trimmed ~= "" then
      -- Parse file:line format by splitting on the last colon
      local filePath, lineNum = trimmed:match("^(.+):(%d+)$")
      if filePath and lineNum then
        -- Found file:line format, convert path relative to working directory
        if workingDir then
          filePath = filepath.Join(workingDir, filePath)
        end
        table.insert(files, {path = filePath, line = tonumber(lineNum)})
      else
        -- No line number, just file path, convert path relative to working directory
        local finalPath = trimmed
        if workingDir then
          finalPath = filepath.Join(workingDir, trimmed)
        end
        table.insert(files, {path = finalPath, line = nil})
      end
    end
  end

  if #files == 0 then
    micro.InfoBar():Message("tv: nothing selected")
    return
  end

  -- Open each file in a new tab
  local openedCount = 0
  for _, fileInfo in ipairs(files) do
    local newTab = OpenTabFromFile(bp, fileInfo.path, fileInfo.line)
    if newTab then
      openedCount = openedCount + 1
    else
      micro.InfoBar():Error("File not found: " .. fileInfo.path)
    end
  end

  if openedCount > 0 then
    micro.InfoBar():Message("Opened " .. openedCount .. " files from TV")
  end
end