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
|
local micro = import("micro")
local buffer = import("micro/buffer")
local filepath = import("filepath")
local os = import("os")
function findWordStart(line, cursorX)
local pos = cursorX
-- First, find the basic word boundary
while pos > 1 and not line:sub(pos-1, pos-1):match("%s") do
pos = pos - 1
end
-- Check if this word contains a path starting with /
local wordEnd = cursorX
while wordEnd <= #line and not line:sub(wordEnd, wordEnd):match("%s") do
wordEnd = wordEnd + 1
end
local word = line:sub(pos, wordEnd - 1)
-- If word contains /, find the / and expand backwards past non-letters
local slashPos = word:find("/")
if slashPos then
local actualStart = pos + slashPos - 1
-- Go backwards from the / past any non-letter characters
while actualStart > 1 and not line:sub(actualStart-1, actualStart-1):match("%a") and not line:sub(actualStart-1, actualStart-1):match("%s") do
actualStart = actualStart - 1
end
return actualStart
end
return pos
end
function findWordEnd(line, cursorX)
local pos = cursorX
while pos <= #line and not line:sub(pos, pos):match("%s") do
pos = pos + 1
end
return pos - 1
end
function OpenTabFromBuffer(bp, name, buf)
if not buf then
micro.InfoBar():Error("Buffer is nil")
return nil
end
buf:SetName(name)
-- Create a new empty tab
bp:NewTabCmd(nil)
-- Get the newly created tab (it's the last one and now active)
local tabs = micro.Tabs()
local newTab = tabs.List[#tabs.List]
-- Replace the buffer in the new tab's first pane with our buffer
if newTab and newTab.Panes and newTab.Panes[1] then
newTab.Panes[1]:OpenBuffer(buf)
end
return newTab
end
function OpenTabFromFile(bp, filePath, lineNum)
-- TODO: Check whether Micro supports multiple views on the same buffer
-- If it does, we might want to allow opening the same file in multiple tabs/splits
-- First check if this file is already open in an existing tab
local tabs = micro.Tabs()
for i = 0, #tabs.List - 1 do
local tab = tabs.List[i + 1] -- Lua is 1-indexed
if tab and tab.Panes and tab.Panes[1] and tab.Panes[1].Buf then
local buf = tab.Panes[1].Buf
if buf.Path == filePath then
-- File is already open, reload it from disk before jumping to position
-- This ensures we see the latest content if Claude edited the file
buf:ReOpen()
-- Switch to that tab and position cursor
tabs:SetActive(i)
if lineNum and lineNum > 0 then
local pane = tab.Panes[1]
if pane and pane.Cursor then
pane.Cursor:GotoLoc(buffer.Loc(0, lineNum - 1)) -- Convert to 0-based
-- Relocate the view to center the cursor
pane:Relocate()
end
end
return tab
end
end
end
-- File not open, create new buffer and tab
local newBuf, err = buffer.NewBufferFromFile(filePath)
if err ~= nil then
return nil
end
local newTab = OpenTabFromBuffer(bp, filePath, newBuf)
-- Position cursor at specified line if provided
if newTab and lineNum and lineNum > 0 then
local pane = newTab.Panes[1]
if pane and pane.Cursor then
pane.Cursor:GotoLoc(buffer.Loc(0, lineNum - 1)) -- Convert to 0-based
-- Relocate the view to center the cursor
pane:Relocate()
end
end
return newTab
end
function find_project_dir(startPath)
local currentDir = startPath
while currentDir ~= "/" do
local gitPath = filepath.Join(currentDir, ".git")
if fileExists(gitPath) then
return currentDir
end
-- Go up one directory
local parentDir = filepath.Dir(currentDir)
if parentDir == currentDir then
-- Reached root, stop
break
end
currentDir = parentDir
end
return nil
end
function fileExists(path)
local file = io.open(path, "r")
if file then
file:close()
return true
end
return false
end
|