Skip to content

Commit b48b429

Browse files
committed
Add ability to start neovim server from a fresh terminal (fix #51), move fsock/netsock path logic to core
1 parent 49542e5 commit b48b429

8 files changed

Lines changed: 198 additions & 53 deletions

File tree

crates/bridge/src/launcher.rs

Lines changed: 11 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,18 @@
1+
use crate::{
2+
neovide,
3+
plugin_config::{LauncherType, PluginConfig, SocketType},
4+
utils::is_port_in_use,
5+
};
6+
use anyhow::{Context, Result, bail};
7+
use defold_nvim_core::{focus::focus_neovim, nvim_server, utils::classname};
18
use std::{
29
fs::{self},
310
path::{Path, PathBuf},
411
process::{Child, Command},
512
};
6-
7-
use anyhow::{Context, Result, bail};
8-
use defold_nvim_core::{focus::focus_neovim, utils::classname};
913
use termlauncher::{Application, CustomTerminal, Terminal};
1014
use which::which;
1115

12-
use crate::{
13-
neovide,
14-
plugin_config::{LauncherType, PluginConfig, SocketType},
15-
utils::{self, is_port_in_use},
16-
};
17-
1816
const ERR_NEOVIDE_NOT_FOUND: &str = "Could not find Neovide, have you installed it?";
1917
const ERR_TERMINAL_NOT_FOUND: &str = "Could not find any suitable terminal";
2018

@@ -190,12 +188,7 @@ fn run_fsock(
190188
file: &str,
191189
line: Option<usize>,
192190
) -> Result<()> {
193-
let socket_file = utils::runtime_dir(
194-
root_dir
195-
.to_str()
196-
.context("could not convert path to string")?,
197-
)?
198-
.join("neovim.sock");
191+
let socket_file = nvim_server::fsock_path(root_dir)?;
199192

200193
tracing::debug!("Using fsock at {socket_file:?}");
201194

@@ -241,18 +234,8 @@ fn run_netsock(
241234
file: &str,
242235
line: Option<usize>,
243236
) -> Result<()> {
244-
let port_file = utils::runtime_dir(
245-
root_dir
246-
.to_str()
247-
.context("could not convert path to string")?,
248-
)?
249-
.join("port");
250-
251-
let port: u16 = if port_file.exists() {
252-
fs::read_to_string(&port_file)?.parse()?
253-
} else {
254-
utils::find_free_port()
255-
};
237+
let port_file = nvim_server::netsock_port_file(root_dir)?;
238+
let port = nvim_server::read_or_allocate_netsock_port(root_dir)?;
256239

257240
let socket = format!("127.0.0.1:{port}");
258241

@@ -264,10 +247,8 @@ fn run_netsock(
264247
if let Err(err) = nvim_open_file_remote(nvim, &socket, file, line) {
265248
tracing::error!("Failed to communicate with neovim server: {err:?}");
266249

267-
let new_port = utils::find_free_port();
268-
let socket = format!("127.0.0.1:{new_port}");
250+
let socket = nvim_server::allocate_new_netsock_addr(root_dir)?;
269251
tracing::debug!("Trying to use netsock with port {socket}");
270-
fs::write(port_file, new_port.to_string())?;
271252

272253
app.args = apply_vars(&app.args, VAR_ADDRESS, &socket);
273254
report_process_errors(app.launch_with(launcher)?)?;

crates/bridge/src/utils.rs

Lines changed: 0 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,5 @@
1-
use std::{fs, net::TcpListener, path::PathBuf};
2-
3-
use anyhow::{Context, Result};
4-
use defold_nvim_core::utils::project_id;
51
use netstat2::{AddressFamilyFlags, ProtocolFlags, get_sockets_info};
62

7-
pub fn runtime_dir(root_dir: &str) -> Result<PathBuf> {
8-
let dir = dirs::cache_dir()
9-
.context("could not get cache dir")?
10-
.join("defold.nvim")
11-
.join("runtime")
12-
.join(project_id(root_dir)?);
13-
fs::create_dir_all(&dir)?;
14-
Ok(dir)
15-
}
16-
173
pub fn is_port_in_use(port: u16) -> bool {
184
let af_flags = AddressFamilyFlags::IPV4 | AddressFamilyFlags::IPV6;
195
let proto_flags = ProtocolFlags::TCP | ProtocolFlags::UDP;
@@ -35,11 +21,3 @@ pub fn is_port_in_use(port: u16) -> bool {
3521
}
3622
false
3723
}
38-
39-
pub fn find_free_port() -> u16 {
40-
TcpListener::bind("127.0.0.1:0")
41-
.expect("Failed to bind")
42-
.local_addr()
43-
.expect("Failed to get local addr")
44-
.port()
45-
}

crates/core/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ pub mod game_project;
88
pub mod github;
99
pub mod mobdap;
1010
pub mod neovide;
11+
pub mod nvim_server;
1112
pub mod project;
1213
mod release_downloader;
1314
pub mod script_api;

crates/core/src/nvim_server.rs

Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
use crate::utils::project_id;
2+
use anyhow::{Context, Result, bail};
3+
use std::{
4+
fs,
5+
net::TcpListener,
6+
path::{Path, PathBuf},
7+
};
8+
9+
#[derive(Debug, Clone, Copy)]
10+
pub enum SocketType {
11+
Fsock,
12+
Netsock,
13+
}
14+
15+
impl SocketType {
16+
pub fn parse(value: &str) -> Result<Self> {
17+
match value {
18+
"fsock" => Ok(Self::Fsock),
19+
"netsock" => Ok(Self::Netsock),
20+
_ => bail!("Invalid socket type '{value}', expected 'fsock' or 'netsock'"),
21+
}
22+
}
23+
}
24+
25+
#[must_use]
26+
pub fn default_socket_type() -> SocketType {
27+
if cfg!(target_os = "linux") || cfg!(target_os = "macos") {
28+
SocketType::Fsock
29+
} else {
30+
SocketType::Netsock
31+
}
32+
}
33+
34+
pub fn runtime_dir(root_dir: &Path) -> Result<PathBuf> {
35+
let root_dir = root_dir
36+
.to_str()
37+
.context("could not convert root dir to string")?;
38+
39+
let dir = dirs::cache_dir()
40+
.context("could not get cache dir")?
41+
.join("defold.nvim")
42+
.join("runtime")
43+
.join(project_id(root_dir)?);
44+
45+
fs::create_dir_all(&dir)?;
46+
Ok(dir)
47+
}
48+
49+
pub fn fsock_path(root_dir: &Path) -> Result<PathBuf> {
50+
Ok(runtime_dir(root_dir)?.join("neovim.sock"))
51+
}
52+
53+
pub fn netsock_port_file(root_dir: &Path) -> Result<PathBuf> {
54+
Ok(runtime_dir(root_dir)?.join("port"))
55+
}
56+
57+
#[must_use]
58+
pub fn find_free_port() -> Result<u16> {
59+
let listener = TcpListener::bind("127.0.0.1:0")?;
60+
let port = listener.local_addr()?.port();
61+
Ok(port)
62+
}
63+
64+
pub fn read_or_allocate_netsock_port(root_dir: &Path) -> Result<u16> {
65+
let port_file = netsock_port_file(root_dir)?;
66+
67+
if port_file.exists() {
68+
match fs::read_to_string(&port_file) {
69+
Ok(content) => {
70+
if let Ok(port) = content.trim().parse::<u16>() {
71+
return Ok(port);
72+
}
73+
}
74+
Err(err) => {
75+
tracing::warn!(
76+
"Could not read netsock port file '{}': {err}",
77+
port_file.display()
78+
);
79+
}
80+
}
81+
}
82+
83+
allocate_new_netsock_port(root_dir)
84+
}
85+
86+
pub fn allocate_new_netsock_port(root_dir: &Path) -> Result<u16> {
87+
let port_file = netsock_port_file(root_dir)?;
88+
let port = find_free_port()?;
89+
fs::write(port_file, port.to_string())?;
90+
Ok(port)
91+
}
92+
93+
pub fn allocate_new_netsock_addr(root_dir: &Path) -> Result<String> {
94+
let port = allocate_new_netsock_port(root_dir)?;
95+
Ok(format!("127.0.0.1:{port}"))
96+
}
97+
98+
pub fn resolve_server_addr(root_dir: &Path, socket_type: Option<SocketType>) -> Result<String> {
99+
match socket_type.unwrap_or_else(default_socket_type) {
100+
SocketType::Fsock => Ok(fsock_path(root_dir)?
101+
.to_str()
102+
.context("could not convert socket file to string")?
103+
.to_string()),
104+
SocketType::Netsock => {
105+
let port = read_or_allocate_netsock_port(root_dir)?;
106+
Ok(format!("127.0.0.1:{port}"))
107+
}
108+
}
109+
}

crates/sidecar/src/lib.rs

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
use anyhow::Context;
2-
use defold_nvim_core::{bridge, editor, editor_config, mobdap, project, utils};
2+
use defold_nvim_core::{bridge, editor, editor_config, mobdap, nvim_server, project, utils};
33
use defold_nvim_core::{focus, game_project::GameProject};
44
use mlua::Value;
55
use mlua::prelude::*;
@@ -91,6 +91,10 @@ fn register_exports(lua: &Lua) -> LuaResult<LuaTable> {
9191
lua.create_function(set_default_editor)?,
9292
)?;
9393
exports.set("find_bridge_path", lua.create_function(find_bridge_path)?)?;
94+
exports.set(
95+
"resolve_nvim_server_addr",
96+
lua.create_function(resolve_nvim_server_addr)?,
97+
)?;
9498
exports.set("focus_neovim", lua.create_function(focus_neovim)?)?;
9599
exports.set("focus_game", lua.create_function(focus_game)?)?;
96100
exports.set("mobdap_install", lua.create_function(mobdap_install)?)?;
@@ -180,6 +184,21 @@ fn find_bridge_path(_lua: &Lua, plugin_root: Option<String>) -> LuaResult<String
180184
.to_string())
181185
}
182186

187+
#[instrument(level = "debug", err(Debug), skip_all)]
188+
fn resolve_nvim_server_addr(
189+
_lua: &Lua,
190+
(game_root, socket_type): (String, Option<String>),
191+
) -> LuaResult<String> {
192+
let root = absolute(game_root)?;
193+
let socket_type = socket_type
194+
.as_deref()
195+
.map(nvim_server::SocketType::parse)
196+
.transpose()?;
197+
198+
let addr = nvim_server::resolve_server_addr(&root, socket_type)?;
199+
Ok(addr)
200+
}
201+
183202
#[instrument(level = "debug", err(Debug), skip_all)]
184203
fn focus_neovim(_lua: &Lua, game_root: String) -> LuaResult<()> {
185204
focus::focus_neovim(absolute(game_root)?)?;

lua/defold/init.lua

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -171,6 +171,11 @@ function M.setup(opts)
171171

172172
vim.defer_fn(function()
173173
local project = require "defold.project"
174+
175+
if project.is_defold_project() then
176+
project.ensure_nvim_server(M.config.launcher.socket_type)
177+
end
178+
174179
local port = project.editor_port()
175180

176181
if M.config.defold.set_default_editor and port then

lua/defold/project.lua

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,57 @@ function M.is_defold_project()
3131
return vim.fn.filereadable(root_dir .. "/game.project") == 1
3232
end
3333

34+
---@return boolean
35+
local function is_server_registered(server_addr)
36+
for _, addr in ipairs(vim.fn.serverlist()) do
37+
if addr == server_addr then
38+
return true
39+
end
40+
end
41+
42+
return false
43+
end
44+
45+
---@param socket_type "fsock"|"netsock"|nil
46+
---@return string|nil
47+
function M.ensure_nvim_server(socket_type)
48+
local log = require "defold.service.logger"
49+
50+
for _, arg in ipairs(vim.v.argv) do
51+
-- if neovim was started with `--listen` flag we don't need to try to start a new server
52+
if arg == "--listen" then
53+
log.debug "Neovim was started with `--listen`, no need to start a server"
54+
return nil
55+
end
56+
end
57+
58+
local root = M.project_root()
59+
if not root then
60+
return nil
61+
end
62+
63+
local sidecar = require "defold.sidecar"
64+
65+
local ok_addr, addr = pcall(sidecar.resolve_nvim_server_addr, root, socket_type)
66+
if not ok_addr then
67+
log.error(string.format("Could not resolve Neovim server address because: %s", addr))
68+
return nil
69+
end
70+
71+
if is_server_registered(addr) then
72+
log.debug(string.format("Neovim is running with server: %s", addr))
73+
return addr
74+
end
75+
76+
local ok_server, server = pcall(vim.fn.serverstart, addr)
77+
if ok_server then
78+
log.debug(string.format("Started new Neovim server at: %s", server))
79+
return server
80+
end
81+
82+
return nil
83+
end
84+
3485
M._editor_port = nil
3586

3687
---Find defold editor port for current project

lua/defold/sidecar.lua

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -185,6 +185,7 @@ package.cpath = package.cpath
185185
---@field send_command function(port: integer, cmd: string)
186186
---@field set_default_editor function(port: integer, plugin_root: string, launcher_config: LauncherSettings)
187187
---@field find_bridge_path function(plugin_root: string|nil): string
188+
---@field resolve_nvim_server_addr function(game_root: string, socket_type: "fsock"|"netsock"|nil): string
188189
---@field focus_neovim function(game_root: string)
189190
---@field focus_game function(game_root: string)
190191
---@field mobdap_install function(): string

0 commit comments

Comments
 (0)