Skip to content

Commit 81c0709

Browse files
authored
Improvements on executing lua code from python + code simplifications (#48)
* funcs.luaeval -> exec_lua Uses 'native' lua instead of parsed/indirection throught viml function * Restructure connections + rplugin This makes python code call lua without going through viml, Which might make things faster and remove some indirections. * Don't need to get that again * Remove 'math.random' calls This avoids port number collisions and callback session overwrites * Split responsabilities of core.send * Fix API doc attribute name mismatch * Add LRU to session * Don't fire autocmd multiple times * Add fn to call from viml * Allow running auto-require for tests * Simplify handler fn * Thin Session * ease random * Avoid random by creating id * Use ulids (but make uuids available) * Simplify some more stuff * Add missing dep + remove fn * Add back function for compatibility * Update ulid doc * Simplify some more * Fix viml register callback * Make connections use keys instead of indexes This allows to pin things to connections * Drop comments from ulid
1 parent 515702b commit 81c0709

File tree

14 files changed

+370
-187
lines changed

14 files changed

+370
-187
lines changed

API.md

Lines changed: 19 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,42 +1,42 @@
11
# acid.connections
22
low-level connection handler
33

4-
## `acid.connections.add(this, addr)`
4+
## `acid.connections.add(addr)`
55
Stores connection for reuse later
66

7-
this **(table)**: Connections object.
8-
97
addr **({string,string})**: Address tuple with ip and port.
108

119

12-
## `acid.connections.select(this, pwd, ix)`
10+
## `acid.connections.select(pwd, ix)`
1311
Elects selected connection as primary (thus default) for a certain address
1412

15-
this **(table)**: Connections object.
16-
1713
pwd **(string)**: path (usually project root).
1814
Assumed to be neovim's `pwd`.
1915

2016
ix **(int)**: index of the stored connection
2117

2218

23-
## `acid.connections.unselect(this, pwd)`
19+
## `acid.connections.unselect(pwd)`
2420
Dissociates the connection for the given path
2521

26-
this **(table)**: Connections object.
27-
2822
pwd **(string)**: path (usually project root).
2923

3024

31-
## `acid.connections.get(this, pwd)`
25+
## `acid.connections.get(pwd)`
3226
Return active connection for the given path
3327

34-
this **(table)**: Connections object.
35-
3628
pwd **(string)**: path (usually project root).
3729

3830

39-
**({string,string})** Connection tuple with ip and port.
31+
**({string,string})** Connection tuple with ip and port or nil.
32+
33+
34+
## `acid.connections.set(pwd, addr)`
35+
Add and select the given connection for given path.
36+
37+
pwd **(string)**: path (usually project root).
38+
39+
addr **({string,string})**: tuple with ip and port or nil.
4040

4141

4242
---
@@ -54,6 +54,11 @@ obj **(table)**: Payload to be sent to the nrepl.
5454
handler **(function)**: Handler function to deal with the response.
5555

5656

57+
---
58+
59+
# extra.ulid
60+
generates the time-based part of a `ulid`.
61+
5762
---
5863

5964
# acid.features
@@ -214,11 +219,9 @@ cmd: A command (op + payload + handler) to be executed.
214219
conn: A connection where this command will be run.
215220

216221

217-
## `acid.callback(session, ret)`
222+
## `acid.callback(ret)`
218223
Callback proxy for handling command responses
219224

220-
session: Session ID for matching response with request
221-
222225
ret: The response from nrepl
223226

224227

lua/acid/connections.lua

Lines changed: 63 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -2,91 +2,112 @@
22

33
--- low-level connection handler
44
-- @module acid.connections
5-
local nvim = vim.api
65
local utils = require("acid.utils")
76

87
local connections = {
98
store = {},
109
current = {},
1110
}
1211

12+
local pwd_to_key = function(pwd)
13+
if not utils.ends_with(pwd, "/") then
14+
return pwd .. "/"
15+
end
16+
return pwd
17+
end
18+
1319
--- Stores connection for reuse later
14-
-- @tparam table this Connections object.
1520
-- @tparam {string,string} addr Address tuple with ip and port.
16-
connections.add = function(this, addr)
17-
table.insert(this.store, addr)
18-
return #this.store
21+
connections.add = function(addr)
22+
local uuid = utils.uuid()
23+
connections.store[uuid] = addr
24+
return uuid
1925
end
2026

21-
connections.remove = function(this, addr)
27+
connections.remove = function(addr)
2228
local ix
23-
for i, v in ipairs(this.store) do
29+
30+
-- Remove current connections first
31+
-- Then remove remaining
32+
for pwd, v in pairs(connections.current) do
2433
if v[2] == addr[2] and v[1] == addr[1] then
25-
ix = i
26-
break
34+
ix = connections.current[pwd]
35+
connections.store[ix] = nil
36+
connections.current[pwd] = nil
2737
end
2838
end
29-
table.remove(connections.store, ix)
3039

31-
for pwd, v in pairs(this.current) do
40+
for i, v in ipairs(connections.store) do
3241
if v[2] == addr[2] and v[1] == addr[1] then
33-
this.current[pwd] = nil
42+
connections.store[i] = nil
3443
end
3544
end
3645
end
3746

3847
--- Elects selected connection as primary (thus default) for a certain address
39-
-- @tparam table this Connections object.
4048
-- @tparam string pwd path (usually project root).
4149
-- Assumed to be neovim's `pwd`.
4250
-- @tparam int ix index of the stored connection
43-
connections.select = function(this, pwd, ix)
44-
if not utils.ends_with(pwd, "/") then
45-
pwd = pwd .. "/"
46-
end
51+
connections.select = function(pwd, ix)
52+
pwd = pwd_to_key(pwd)
4753

48-
this.current[pwd] = ix
54+
connections.current[pwd] = ix
4955
end
5056

5157
--- Dissociates the connection for the given path
52-
-- @tparam table this Connections object.
5358
-- @tparam string pwd path (usually project root).
54-
connections.unselect = function(this, pwd)
55-
if not utils.ends_with(pwd, "/") then
56-
pwd = pwd .. "/"
57-
end
59+
connections.unselect = function(pwd)
60+
pwd = pwd_to_key(pwd)
5861

5962
-- TODO Potentially wrong
60-
this.current[pwd] = nil
63+
connections.current[pwd] = nil
6164
end
6265

6366
--- Return active connection for the given path
64-
-- @tparam table this Connections object.
6567
-- @tparam string pwd path (usually project root).
66-
-- @treturn {string,string} Connection tuple with ip and port.
67-
connections.get = function(this, pwd)
68-
if not utils.ends_with(pwd, "/") then
69-
pwd = pwd .. "/"
70-
end
68+
-- @treturn {string,string} Connection tuple with ip and port or nil.
69+
connections.get = function(pwd)
70+
pwd = pwd_to_key(pwd)
7171

72-
local ix = tonumber(this.current[pwd])
72+
local ix = connections.current[pwd]
7373

7474
if ix == nil then
75-
local port_file = pwd .. '.nrepl-port'
76-
if nvim.nvim_call_function('filereadable', {port_file}) == 1 then
77-
local port = nvim.nvim_call_function('readfile', {port_file})
78-
ix = this:add({'127.0.0.1', port[1]})
79-
this:select(pwd, ix)
75+
return nil
76+
end
77+
78+
return connections.store[ix]
79+
end
80+
81+
connections.search = function(pwd)
82+
pwd = pwd_to_key(pwd)
83+
local fpath = vim.api.nvim_call_function("findfile", {pwd .. ".nrepl-port"})
84+
if fpath ~= "" then
85+
local portno = table.concat(vim.api.nvim_call_function("readfile", {fpath}), "")
86+
local conn = {"127.0.0.1", utils.trim(portno)}
87+
return connections.add(conn)
88+
end
89+
return nil
90+
end
91+
92+
connections.attempt_get = function(pwd)
93+
local conn = connections.get(pwd)
94+
if conn == nil then
95+
local ix = connections.search(pwd)
96+
if ix ~= nil then
97+
connections.select(pwd, ix)
98+
conn = connections.store[ix]
8099
else
81-
if #this.store >= 1 then
82-
ix = #this.store
83-
else
84-
return
85-
end
100+
return nil
86101
end
87102
end
103+
return conn
104+
end
88105

89-
return this.store[ix]
106+
--- Add and select the given connection for given path.
107+
-- @tparam string pwd path (usually project root).
108+
-- @tparam {string,string} addr tuple with ip and port or nil.
109+
connections.set = function(pwd, addr)
110+
connections.select(pwd, connections.add(addr))
90111
end
91112

92113
return connections

lua/acid/core.lua

Lines changed: 15 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ local utils = require("acid.utils")
88
local log = require("acid.log")
99

1010
local core = {
11-
indirection = {}
11+
indirection = setmetatable({}, utils.LRU(10))
1212
}
1313

1414
--- Forward messages to the nrepl and registers the handler.
@@ -21,37 +21,30 @@ core.send = function(conn, obj, handler)
2121
return
2222
end
2323

24-
local session = math.random(10000, 99999)
2524
local pwd = vim.api.nvim_call_function("getcwd", {})
2625

27-
conn = conn or connections:get(pwd)
28-
local new_conn
26+
conn = conn or connections.attempt_get(pwd)
2927

3028
if conn == nil then
31-
local fpath = vim.api.nvim_call_function("findfile", {".nrepl-port"})
32-
if fpath == "" then
33-
log.msg("No active connection to a nrepl session. Aborting")
34-
return
35-
end
36-
local portno = table.concat(vim.api.nvim_call_function("readfile", {fpath}), "")
37-
conn = {"127.0.0.1", utils.trim(portno)}
38-
new_conn = true
29+
log.msg("No active connection to a nrepl session. Aborting")
3930
end
4031

32+
if obj.id == nil then
33+
obj.id = utils.ulid()
34+
end
35+
36+
core.register_callback(conn, handler, obj.id)
37+
38+
vim.api.nvim_call_function("AcidSendNrepl", {obj, conn})
39+
40+
end
41+
42+
core.register_callback = function(conn, handler, session)
4143
core.indirection[session] = {
4244
fn = handler,
4345
conn = conn
4446
}
45-
46-
vim.api.nvim_call_function("AcidSendNrepl", {obj,
47-
"require('acid').callback(" .. session .. ", _A)",
48-
conn,
49-
"lua"
50-
})
51-
52-
if new_conn then
53-
connections:select(pwd, connections:add(new_conn))
54-
end
47+
return session
5548
end
5649

5750
return core

lua/acid/extra/ulid.lua

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
-- luacheck: globals vim
2+
---
3+
-- Module for creating Universally Unique Lexicographically Sortable Identifiers.
4+
--
5+
-- Modeled after the [ulid implementation by alizain](https://github.com/alizain/ulid). Please checkout the
6+
-- documentation there for the design and characteristics of ulid.
7+
--
8+
-- **IMPORTANT**: the standard Lua versions, based on the standard C library are
9+
-- unfortunately very weak regarding time functions and randomizers.
10+
-- So make sure to set it up properly!
11+
--
12+
--see https://github.com/Tieske/ulid.lua
13+
--copyright Copyright 2016-2017 Thijs Schreijer
14+
--license [mit](https://opensource.org/licenses/MIT)
15+
--author Thijs Schreijer
16+
17+
18+
-- Crockford's Base32 https://en.wikipedia.org/wiki/Base32
19+
local ENCODING = {
20+
"0", "1", "2", "3", "4", "5", "6", "7", "8", "9",
21+
"A", "B", "C", "D", "E", "F", "G", "H", "J", "K", "M",
22+
"N", "P", "Q", "R", "S", "T", "V", "W", "X", "Y", "Z"
23+
}
24+
local ENCODING_LEN = #ENCODING
25+
local TIME_LEN = 10
26+
local RANDOM_LEN = 16
27+
28+
local floor = math.floor
29+
local concat = table.concat
30+
local random = math.random
31+
local now
32+
33+
if package.loaded["socket"] and package.loaded["socket"].gettime then
34+
-- LuaSocket
35+
now = package.loaded["socket"].gettime
36+
else
37+
now = function() return vim.api.nvim_call_function("localtime", {}) end
38+
end
39+
40+
math.randomseed(now())
41+
42+
--- generates the time-based part of a `ulid`.
43+
-- @param[opt] time time to generate the string from, in seconds since
44+
-- unix epoch, with millisecond precision (defaults to now)
45+
-- @param[opt] len the length of the time-based string to return (defaults to 10)
46+
-- @return time-based part of `ulid` string
47+
local function encode_time(time, len)
48+
time = floor((time or now()) * 1000)
49+
len = len or TIME_LEN
50+
51+
local result = {}
52+
for i = len, 1, -1 do
53+
local mod = time % ENCODING_LEN
54+
result[i] = ENCODING[mod + 1]
55+
time = (time - mod) / ENCODING_LEN
56+
end
57+
return concat(result)
58+
end
59+
60+
--- generates the random part of a `ulid`.
61+
-- @param[opt] len the length of the random string to return (defaults to 16)
62+
-- @return random part of `ulid` string
63+
local function encode_random(len)
64+
len = len or RANDOM_LEN
65+
local result = {}
66+
for i = 1, len do
67+
result[i] = ENCODING[floor(random() * ENCODING_LEN) + 1]
68+
end
69+
return concat(result)
70+
end
71+
72+
local function ulid(time)
73+
return encode_time(time) .. encode_random()
74+
end
75+
76+
local _M = {
77+
ulid = ulid,
78+
encode_time = encode_time,
79+
encode_random = encode_random,
80+
set_time_func = set_time_func,
81+
set_random_func = set_random_func,
82+
}
83+
84+
return setmetatable(_M, {
85+
__call = function(_, ...)
86+
return ulid(...)
87+
end
88+
})

lua/acid/forms.lua

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ forms.get_form_boundaries = function()
3131
return {
3232
from = from,
3333
to = to,
34-
bufnr = vim.api.nvim_get_current_buf()
34+
bufnr = curpos[1]
3535
}
3636
end
3737

0 commit comments

Comments
 (0)