Skip to content

Commit 500004f

Browse files
troigantotroiganto
and
troiganto
authored
feat(utils): add optional flag to utils.writefile() for exclusive writes (#893)
* refactor(tests): add minimal test for folding * refactor(tests): add tests for `readfile()` and `writefile()` * feat(utils): add optional parameter `opts` to `writefile()` --------- Co-authored-by: troiganto <[email protected]>
1 parent 8cdfc8d commit 500004f

File tree

3 files changed

+118
-2
lines changed

3 files changed

+118
-2
lines changed

Diff for: lua/orgmode/utils/init.lua

+4-2
Original file line numberDiff line numberDiff line change
@@ -55,10 +55,12 @@ end
5555

5656
---@param file string
5757
---@param data string|string[]
58+
---@param opts? {excl?: boolean}
5859
---@return OrgPromise<integer> bytes
59-
function utils.writefile(file, data)
60+
function utils.writefile(file, data, opts)
6061
return Promise.new(function(resolve, reject)
61-
uv.fs_open(file, 'w', 438, function(err1, fd)
62+
local flags = opts and opts.excl and 'wx' or 'w'
63+
uv.fs_open(file, flags, 438, function(err1, fd)
6264
if err1 then
6365
return reject(err1)
6466
end

Diff for: tests/plenary/org/fold_spec.lua

+17
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
local helpers = require('tests.plenary.helpers')
2+
3+
describe('folding', function()
4+
it('works', function()
5+
helpers.create_file({
6+
'* First',
7+
'** Second',
8+
'*** Third',
9+
'**** Fourth',
10+
'***** Fifth',
11+
'text',
12+
})
13+
vim.cmd.normal({ 'GzM', bang = true })
14+
local foldlevel = vim.fn.foldlevel(6)
15+
assert.are.same(5, foldlevel)
16+
end)
17+
end)

Diff for: tests/plenary/utils_spec.lua

+97
Original file line numberDiff line numberDiff line change
@@ -38,4 +38,101 @@ describe('Util', function()
3838
end)
3939
end)
4040
end)
41+
42+
describe('readfile', function()
43+
---@type OrgFile
44+
local file
45+
before_each(function()
46+
if not file then
47+
file = helpers.create_file({
48+
'First line',
49+
'',
50+
'* Headline',
51+
'Contents',
52+
})
53+
end
54+
end)
55+
56+
it('returns lines', function()
57+
local contents = utils.readfile(file.filename):wait()
58+
assert.are.same(contents, {
59+
'First line',
60+
'',
61+
'* Headline',
62+
'Contents',
63+
})
64+
end)
65+
66+
it('returns raw contents', function()
67+
local contents = utils.readfile(file.filename, { raw = true }):wait()
68+
assert.are.equal(contents, 'First line\n\n* Headline\nContents\n')
69+
end)
70+
71+
it('schedules its results for later', function()
72+
utils
73+
.readfile(file.filename, { schedule = true })
74+
:next(function(contents)
75+
-- Without `schedule = true`, this line would run inside `fast-api`
76+
-- and thus fail.
77+
vim.fn.setreg('', contents)
78+
end)
79+
:wait()
80+
local contents = vim.fn.getreg('')
81+
assert.are.equal(contents, 'First line\n\n* Headline\nContents\n')
82+
end)
83+
end)
84+
85+
describe('writefile', function()
86+
---@type string
87+
local filename
88+
before_each(function()
89+
if not filename then
90+
filename = vim.fn.tempname()
91+
end
92+
end)
93+
94+
local contents = {
95+
'First line',
96+
'',
97+
'* Headline',
98+
'Contents',
99+
}
100+
101+
it('writes bare strings', function()
102+
local bytes = utils.writefile(filename, table.concat(contents, '\n')):wait()
103+
assert.are.equal(bytes, 31)
104+
local reread = vim.fn.readfile(filename)
105+
assert.are.same(reread, contents)
106+
end)
107+
108+
it('writes lists of strings by concatenation', function()
109+
local bytes = utils.writefile(filename, contents):wait()
110+
assert.are.equal(bytes, 28)
111+
local reread = vim.fn.readfile(filename)
112+
assert.are.same(reread, { 'First line* HeadlineContents' })
113+
end)
114+
115+
it('does not schedule its results', function()
116+
local promise = utils.writefile(filename, contents):next(function(bytes)
117+
return vim.fn.setreg('', bytes)
118+
end)
119+
---@type boolean, string?
120+
local ok, err = pcall(promise.wait, promise)
121+
assert.is.False(ok)
122+
assert(err)
123+
local expected = 'E5560: Vimscript function must not be called in a lua loop callback'
124+
local msg = err:sub(#err - #expected)
125+
assert.are.equal(expected, msg)
126+
end)
127+
128+
it('allows no-clobber writes', function()
129+
local promise = utils.writefile(filename, contents, { excl = true })
130+
---@type boolean, string?
131+
local ok, err = pcall(promise.wait, promise)
132+
assert.is.False(ok)
133+
assert(err)
134+
local expected = 'EEXIST: file already exists: ' .. filename
135+
assert.are.equal(expected, err)
136+
end)
137+
end)
41138
end)

0 commit comments

Comments
 (0)