Skip to content

Commit 29af4de

Browse files
feat: Reverse path transformation function for server config
1 parent 1839ff9 commit 29af4de

5 files changed

Lines changed: 103 additions & 8 deletions

File tree

README.md

Lines changed: 46 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -869,18 +869,57 @@ require('opencode').setup({
869869
})
870870
```
871871

872-
For WSL or complex mappings, use a function:
872+
### Auto-Spawning with WSL
873+
874+
Run opencode server inside WSL while using Neovim on Windows:
873875

874876
```lua
875877
require('opencode').setup({
876878
server = {
877879
url = 'localhost',
878-
port = 8080,
880+
port = 'auto', -- Random port for project isolation
881+
882+
-- Spawn opencode server inside WSL
883+
spawn_command = function(port, url)
884+
local cmd = string.format(
885+
'wsl.exe -e bash -c "opencode serve --hostname 127.0.0.1 --port %d"',
886+
port
887+
)
888+
print(string.format('[opencode.nvim] Starting WSL server on port %d', port))
889+
return vim.fn.jobstart(cmd, { detach = 1 })
890+
end,
891+
892+
-- Kill WSL opencode process
893+
kill_command = function(port, url)
894+
print(string.format('[opencode.nvim] Stopping WSL server on port %d', port))
895+
vim.fn.jobstart('wsl.exe -e pkill -f "opencode serve.*--port ' .. port .. '"')
896+
end,
897+
898+
-- Windows → WSL path translation (for requests)
879899
path_map = function(host_path)
880-
-- Convert Windows path to WSL path
881-
local wsl_path = host_path:gsub('^C:', '/mnt/c')
882-
return wsl_path
900+
if vim.fn.has('win32') == 1 then
901+
-- Convert C:\Users\... → /mnt/c/Users/...
902+
local drive, rest = host_path:match('^([A-Za-z]):(.*)$')
903+
if drive then
904+
local wsl_path = '/mnt/' .. drive:lower() .. rest:gsub('\\', '/')
905+
return wsl_path
906+
end
907+
end
908+
return host_path
909+
end,
910+
911+
-- WSL → Windows path translation (for responses)
912+
reverse_path_map = function(server_path)
913+
-- Convert /mnt/c/Users/... → C:\Users\...
914+
local drive, rest = server_path:match('^/mnt/([a-z])(.*)$')
915+
if drive then
916+
local windows_path = drive:upper() .. ':' .. rest:gsub('/', '\\')
917+
return windows_path
918+
end
919+
return server_path
883920
end,
921+
922+
auto_kill = true, -- Kill server when last nvim instance exits
884923
},
885924
})
886925
```
@@ -893,7 +932,8 @@ require('opencode').setup({
893932
- `spawn_command` (function | nil): Optional function to start server: `function(port, url) ... end`
894933
- `kill_command` (function | nil): Optional function to stop server when `auto_kill` triggers: `function(port, url) ... end`
895934
- `auto_kill` (boolean): Kill spawned servers when last nvim instance exits (default: true)
896-
- `path_map` (string | function | nil): Transform host paths to server paths
935+
- `path_map` (string | function | nil): Transform host paths to server paths (for outgoing requests)
936+
- `reverse_path_map` (function | nil): Transform server paths back to host paths (for incoming responses/events)
897937

898938
### Multi-Instance Support
899939

lua/opencode/api_client.lua

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ local state = require('opencode.state')
33
local config = require('opencode.config')
44
local url_encode = require('opencode.util').url_encode
55
local apply_path_map = require('opencode.util').apply_path_map
6+
local reverse_transform_paths_recursive = require('opencode.util').reverse_transform_paths_recursive
67

78
--- Transform file paths in API payloads using configured path_map
89
--- @param data any The data to transform (table, string, or other)
@@ -110,7 +111,10 @@ function OpencodeApiClient:_call(endpoint, method, body, query)
110111
body = transform_paths_recursive(body)
111112
end
112113

113-
return server_job.call_api(url, method, body)
114+
local Promise = require('opencode.promise')
115+
return server_job.call_api(url, method, body):and_then(function(result)
116+
return reverse_transform_paths_recursive(result)
117+
end)
114118
end
115119

116120
-- Project endpoints
@@ -471,7 +475,8 @@ function OpencodeApiClient:subscribe_to_events(directory, on_event)
471475
chunk = chunk:gsub('^data:%s*', '')
472476
local ok, event = pcall(vim.json.decode, vim.trim(chunk))
473477
if ok and event then
474-
on_event(event --[[@as table]])
478+
local transformed_event = reverse_transform_paths_recursive(event)
479+
on_event(transformed_event --[[@as table]])
475480
end
476481
end)
477482
end

lua/opencode/config.lua

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,9 @@ M.defaults = {
2525
path_map = function(path)
2626
return path
2727
end,
28+
reverse_path_map = function(path)
29+
return path
30+
end,
2831
},
2932
keymap = {
3033
editor = {

lua/opencode/types.lua

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,7 @@
120120
---@field kill_command fun(port: number, url: string): nil | nil -- Optional function to stop the server when auto_kill is true
121121
---@field auto_kill boolean -- Kill spawned servers when nvim exits (default: true)
122122
---@field path_map string | fun(host_path: string): string | nil -- Map host paths to server paths
123+
---@field reverse_path_map fun(server_path: string): string | nil -- Map server paths back to host paths
123124

124125
---@class OpencodeUIConfig
125126
---@field enable_treesitter_markdown boolean

lua/opencode/util.lua

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,52 @@ function M.apply_path_map(path)
138138
return path
139139
end
140140

141+
function M.apply_reverse_path_map(path)
142+
if not path then
143+
return path
144+
end
145+
146+
local config = require('opencode.config')
147+
local reverse_path_map = config.server.reverse_path_map
148+
149+
if type(reverse_path_map) == 'function' then
150+
local ok, result = pcall(reverse_path_map, path)
151+
if ok and result then
152+
return result
153+
end
154+
return path
155+
end
156+
157+
return path
158+
end
159+
160+
function M.reverse_transform_paths_recursive(data)
161+
if type(data) ~= 'table' then
162+
return data
163+
end
164+
165+
local result = {}
166+
for key, value in pairs(data) do
167+
if type(value) == 'string' and (
168+
key == 'filePath' or
169+
key == 'path' or
170+
key == 'file' or
171+
key == 'directory' or
172+
key == 'cwd' or
173+
key == 'root'
174+
) then
175+
result[key] = M.apply_reverse_path_map(value)
176+
elseif type(value) == 'table' and (key == 'files' or key == 'deleted_files') then
177+
result[key] = vim.tbl_map(M.apply_reverse_path_map, value)
178+
elseif type(value) == 'table' then
179+
result[key] = M.reverse_transform_paths_recursive(value)
180+
else
181+
result[key] = value
182+
end
183+
end
184+
return result
185+
end
186+
141187
--- Format a timestamp as time (e.g., "10:23 AM", "13 Oct 03:32 PM" "13 Oct 2025 03:32 PM")
142188
--- @param timestamp number
143189
--- @return string: Formatted time string

0 commit comments

Comments
 (0)