@@ -3,9 +3,12 @@ local Context = require("core.context")
3
3
local streams = require (" core.streams" )
4
4
local fold = require (" core.fold" )
5
5
local utils = require (" core.utils" )
6
+ local validate = require (" crazywall.validate" )
7
+ local default_config = require (" core.defaults.config" )
6
8
local M = {}
7
9
8
10
local configs = {}
11
+ local current_config_name = " DEFAULT"
9
12
10
13
local output_options = { " both" , " planonly" , " textonly" }
11
14
local on_unsaved_options = { " warn" , " write" }
@@ -15,7 +18,7 @@ local err_buf = nil
15
18
16
19
--- @param path string ?
17
20
--- @return number ?
18
- local function find_buffer (path )
21
+ local find_buffer = function (path )
19
22
for _ , buf_id in ipairs (vim .api .nvim_list_bufs ()) do
20
23
local bufname = vim .api .nvim_buf_get_name (buf_id )
21
24
if bufname == path then
@@ -25,6 +28,90 @@ local function find_buffer(path)
25
28
return nil
26
29
end
27
30
31
+ --- @param path string
32
+ --- @param text string
33
+ --- @return string ? errmsg
34
+ local write = function (path , text )
35
+ local dir = vim .fn .fnamemodify (path , " :p:h" )
36
+ if vim .fn .isdirectory (dir ) == 0 then
37
+ vim .fn .mkdir (dir , " p" )
38
+ end
39
+
40
+ local file = io.open (path , " w" )
41
+ if file then
42
+ local _ , err = file :write (text )
43
+ file :close ()
44
+ if err then
45
+ return err
46
+ end
47
+ return
48
+ end
49
+ return " Unable to write to " .. path
50
+ end
51
+
52
+ --- @return string
53
+ local get_plan_path = function ()
54
+ return string.format (
55
+ " %s/crazywall/plan-%s.txt" ,
56
+ vim .fn .stdpath (" data" ),
57
+ os.time ()
58
+ )
59
+ end
60
+
61
+ --- @param lines string[]
62
+ --- @return number ? buf_id
63
+ --- @return string ? errmsg
64
+ local display_floating_window = function (lines )
65
+ local plan_path_prefix =
66
+ string.format (" %s/crazywall/plan-" , vim .fn .stdpath (" data" ))
67
+ for _ , buf_id in ipairs (vim .api .nvim_list_bufs ()) do
68
+ local buf_name = vim .api .nvim_buf_get_name (buf_id )
69
+ if utils .str .starts_with (buf_name , plan_path_prefix ) then
70
+ vim .api .nvim_buf_delete (buf_id , { force = true })
71
+ end
72
+ end
73
+ local buf_id = vim .api .nvim_create_buf (false , true )
74
+ if not buf_id then
75
+ return nil , " Failed to create buffer"
76
+ end
77
+ vim .api .nvim_buf_set_name (buf_id , get_plan_path ())
78
+ vim .api .nvim_buf_set_option (buf_id , " buftype" , " " )
79
+ vim .api .nvim_buf_set_lines (buf_id , 0 , - 1 , false , lines )
80
+ local editor_height = vim .api .nvim_get_option (" lines" )
81
+ local editor_width = vim .api .nvim_get_option (" columns" )
82
+ local win_height = math.min (math.floor (editor_height * 0.75 ), # lines )
83
+ local longest_line_length = (function ()
84
+ local res = 0
85
+ for _ , line in ipairs (lines ) do
86
+ res = math.max (res , # line )
87
+ end
88
+ return res
89
+ end )()
90
+ local win_width =
91
+ math.min (math.floor (editor_width * 0.75 ), longest_line_length )
92
+ local row = math.floor ((editor_height - win_height ) / 2 )
93
+ local col = math.floor ((editor_width - win_width ) / 2 )
94
+
95
+ local win = vim .api .nvim_open_win (buf_id , true , {
96
+ relative = " editor" ,
97
+ width = win_width ,
98
+ height = win_height ,
99
+ row = row ,
100
+ col = col ,
101
+ border = " rounded" ,
102
+ style = " minimal" ,
103
+ })
104
+ if not win then
105
+ return nil , " Failed to open floating window."
106
+ end
107
+ vim .api .nvim_buf_set_option (buf_id , " modifiable" , false )
108
+
109
+ local default_bg = vim .api .nvim_get_hl (0 , { name = ' Normal' }).background
110
+ vim .api .nvim_set_hl (0 , ' NormalFloat' , { bg = default_bg })
111
+
112
+ return buf_id
113
+ end
114
+
28
115
--- @param err_text string ?
29
116
local display_err = function (err_text )
30
117
local err = err_text and utils .str .split_lines_to_list (err_text or " " ) or {}
@@ -33,6 +120,8 @@ local display_err = function(err_text)
33
120
end
34
121
local total_height = vim .api .nvim_get_option (" lines" ) -- Total height of the Neovim window
35
122
local split_height = math.floor (total_height / 3 )
123
+ split_height = math.min (# err , split_height )
124
+ split_height = math.max (split_height , 3 )
36
125
vim .cmd (" botright new" )
37
126
local win_id = vim .api .nvim_get_current_win ()
38
127
err_buf = vim .api .nvim_get_current_buf ()
@@ -43,32 +132,91 @@ local display_err = function(err_text)
43
132
vim .api .nvim_buf_set_option (err_buf , " modifiable" , false )
44
133
end
45
134
135
+ --- @param buf_id integer ?
136
+ --- @param ctx Context
137
+ --- @return Plan ? plan
138
+ --- @return string ? errmsg
139
+ local do_fold = function (buf_id , ctx )
140
+ local root , err = fold .parse (ctx )
141
+ if not root then
142
+ return nil , err
143
+ end
144
+
145
+ _ , err = fold .prepare (root , ctx )
146
+ if err then
147
+ return nil , err
148
+ end
149
+
150
+ local plan
151
+ plan , err = fold .execute (root , ctx , true )
152
+ if not plan then
153
+ return nil , err
154
+ end
155
+
156
+ if ctx .is_dry_run then
157
+ return plan
158
+ end
159
+
160
+ plan , err = fold .execute (root , ctx , false )
161
+ if err then
162
+ error (err )
163
+ end
164
+
165
+ if buf_id then
166
+ vim .api .nvim_buf_call (buf_id , function ()
167
+ vim .cmd (" edit" )
168
+ end )
169
+ end
170
+
171
+ return plan
172
+ end
173
+
174
+ --- @param buf_id integer ?
175
+ --- @param write_on_unsaved boolean
176
+ --- @return boolean
177
+ local handle_unsaved = function (buf_id , write_on_unsaved )
178
+ if not buf_id then
179
+ return true
180
+ end
181
+ if not vim .api .nvim_buf_get_option (buf_id , " modified" ) then
182
+ return true
183
+ end
184
+ if write_on_unsaved then
185
+ vim .api .nvim_buf_call (buf_id , function ()
186
+ vim .cmd (" write" )
187
+ end )
188
+ return true
189
+ end
190
+ vim .api .nvim_err_writeln (" crazywall: Buffer has unsaved changes." )
191
+ return false
192
+ end
193
+
46
194
vim .api .nvim_create_user_command (" CrazywallQuick" , function (opts )
47
- local on_unsaved = opts .fargs [2 ] or " warn"
48
- local src_path = opts .fargs [3 ] or vim .fn .expand (" %" )
49
- local dest_path = opts .fargs [4 ] or vim .fn .expand (" %" )
195
+ local on_unsaved = opts .fargs [1 ] or " warn"
196
+ local err = validate .string_in_list (on_unsaved , on_unsaved_options )
197
+ if err then
198
+ vim .api .nvim_err_writeln (err )
199
+ return
200
+ end
50
201
51
- local buf_id = find_buffer (src_path )
202
+ local src_path = opts .fargs [2 ] or vim .fn .expand (" %" )
203
+ local dest_path = opts .fargs [3 ] or vim .fn .expand (" %" )
52
204
53
- if
54
- buf_id
55
- and on_unsaved == " warn"
56
- and vim .api .nvim_buf_get_option (buf_id , " modified" )
57
- then
58
- vim .api .nvim_err_writeln (" crazywall: Buffer has unsaved changes." )
205
+ local buf_id = find_buffer (src_path )
206
+ if not handle_unsaved (buf_id , on_unsaved == " write" ) then
59
207
return
60
- else
61
- if buf_id and vim .api .nvim_buf_get_option (buf_id , " modified" ) then
62
- vim .api .nvim_buf_call (buf_id , function ()
63
- vim .cmd (" write" )
64
- end )
65
- end
66
208
end
67
209
68
- local config , err = Config :new ({})
210
+ if configs [current_config_name ] == nil then
211
+ return vim .api .nvim_err_writeln (
212
+ " crazywall: Could not find config " .. current_config_name .. " ."
213
+ )
214
+ end
215
+
216
+ local config
217
+ config , err = Config :new (configs [current_config_name ])
69
218
if not config then
70
- display_err (err )
71
- return
219
+ return display_err (err )
72
220
end
73
221
74
222
local ctx
@@ -87,52 +235,120 @@ vim.api.nvim_create_user_command("CrazywallQuick", function(opts)
87
235
)
88
236
89
237
if not ctx then
90
- display_err (err )
91
- return
238
+ return display_err (err )
92
239
end
93
240
94
- local root
95
- root , err = fold .parse (ctx )
96
- if not root then
97
- display_err (err )
98
- return
241
+ local plan
242
+ plan , err = do_fold (buf_id , ctx )
243
+ if not plan then
244
+ return display_err (err )
99
245
end
100
246
101
- _ , err = fold .prepare (root , ctx )
247
+ local plan_path = get_plan_path ()
248
+ err = write (plan_path , tostring (plan ))
249
+ if err then
250
+ return display_err (err )
251
+ end
252
+
253
+ print (" crazywall: Logs written to " .. plan_path )
254
+ end , {
255
+ nargs = " *" ,
256
+ complete = function (_ , line )
257
+ local args = vim .split (line , " " )
258
+ if # args == 2 then
259
+ return on_unsaved_options
260
+ end
261
+ if # args == 3 then
262
+ return { vim .fn .expand (" %" ) }
263
+ end
264
+ if # args == 4 then
265
+ return { vim .fn .expand (" %" ) }
266
+ end
267
+ return {}
268
+ end ,
269
+ desc = " Folds a file with crazywall, skipping the confirmation window." ,
270
+ })
271
+
272
+ vim .api .nvim_create_user_command (" CrazywallDry" , function (opts )
273
+ local output_style = opts .fargs [1 ] or " both"
274
+ local on_unsaved = opts .fargs [2 ] or " warn"
275
+ local err = validate .string_in_list (on_unsaved , on_unsaved_options )
276
+ or validate .string_in_list (output_style , output_options )
102
277
if err then
103
- display_err (err )
278
+ vim . api . nvim_err_writeln (err )
104
279
return
105
280
end
106
281
107
- local plan
108
- plan , err = fold .execute (root , ctx , true )
109
- if not plan then
110
- display_err (err )
282
+ local src_path = opts .fargs [3 ] or vim .fn .expand (" %" )
283
+ local dest_path = opts .fargs [4 ] or vim .fn .expand (" %" )
284
+
285
+ local buf_id = find_buffer (src_path )
286
+ if not handle_unsaved (buf_id , on_unsaved == " write" ) then
111
287
return
112
288
end
113
289
114
- plan , err = fold .execute (root , ctx , false )
290
+ if configs [current_config_name ] == nil then
291
+ return vim .api .nvim_err_writeln (
292
+ " crazywall: Could not find config " .. current_config_name .. " ."
293
+ )
294
+ end
295
+
296
+ local config
297
+ config , err = Config :new (configs [current_config_name ])
298
+ if not config then
299
+ return display_err (err )
300
+ end
301
+
302
+ local ctx
303
+ ctx , err = Context :new (
304
+ config ,
305
+ src_path ,
306
+ dest_path ,
307
+ vim .fn .readfile (src_path ),
308
+ nil ,
309
+ true ,
310
+ false ,
311
+ (output_style == " plan-only" or output_style == " both" )
312
+ and streams .STDOUT
313
+ or streams .NONE ,
314
+ (output_style == " text-only" or output_style == " both" )
315
+ and streams .STDOUT
316
+ or streams .NONE ,
317
+ true ,
318
+ false
319
+ )
320
+
321
+ if not ctx then
322
+ return display_err (err )
323
+ end
324
+
325
+ local plan
326
+ plan , err = do_fold (buf_id , ctx )
115
327
if err then
116
- error (err )
328
+ return display_err (err )
117
329
end
118
330
119
- if buf_id then
120
- vim . api . nvim_buf_call ( buf_id , function ( )
121
- vim . cmd ( " edit " )
122
- end )
331
+ _ , err =
332
+ display_floating_window ( utils . str . split_lines_to_list ( tostring ( plan )) )
333
+ if err then
334
+ return nil , err
123
335
end
124
- print (" Done!" )
336
+
337
+ vim .cmd (' setlocal nowrap' )
125
338
end , {
126
- nargs = " *" , -- Expect exactly 2 arguments
339
+ nargs = " *" ,
127
340
complete = function (_ , line )
128
341
local args = vim .split (line , " " )
129
342
if # args == 2 then
130
343
return output_options
131
- elseif # args == 3 then
344
+ end
345
+ if # args == 3 then
132
346
return on_unsaved_options
133
- elseif # args == 4 then
347
+ end
348
+ if # args == 4 then
134
349
return { vim .fn .expand (" %" ) }
135
- elseif # args == 5 then
350
+ end
351
+ if # args == 5 then
136
352
return { vim .fn .expand (" %" ) }
137
353
end
138
354
return {}
@@ -142,7 +358,16 @@ end, {
142
358
143
359
M .setup = function (opts )
144
360
opts = opts or {}
361
+ local keys = { " configs" , " default_config_name" }
362
+ for key in pairs (opts ) do
363
+ local err = validate .string_in_list (key , keys )
364
+ if err then
365
+ error (err )
366
+ end
367
+ end
145
368
configs = opts .configs or configs
369
+ configs .DEFAULT = configs .DEFAULT or default_config
370
+ current_config_name = opts .default_config_name or current_config_name
146
371
end
147
372
148
373
M .foo = 42
0 commit comments