forked from TypesettingTools/arch1t3cht-Aegisub-Scripts
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy patharch.RWTools.lua
497 lines (444 loc) · 20.6 KB
/
arch.RWTools.lua
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
script_name = "Rewriting Tools"
script_author = "arch1t3cht"
script_version = "1.3.1"
script_namespace = "arch.RWTools"
script_description = "Shortcuts for managing multiple rewrites of a line in one .ass event line."
haveDepCtrl, DependencyControl = pcall(require, "l0.DependencyControl")
default_config = {
default_signature = "OG",
personal_signature = "",
auto_fixes = true,
forbid_nested = true,
}
if haveDepCtrl then
depctrl = DependencyControl({
feed = "https://raw.githubusercontent.com/TypesettingTools/arch1t3cht-Aegisub-Scripts/main/DependencyControl.json",
})
config = depctrl:getConfigHandler(default_config, "config")
else
id = function() return nil end
config = {c = default_config, load = id, write = id}
end
-- This script automates the process of commenting and uncommenting subitle lines during rewrites,
-- using conventions used in some of the groups I'm working in.
--
-- Line format (formal-ish specification - scroll down for the functions and explicit examples):
-- Every line of a subtitle file is a sequence of sections of one of the following forms:
-- - Global styling tags: A brace block whose content starts with a backslash and ends with a pipe ('|').
-- These are styling overrides which should globally apply to any rewrite (e.g. position, alignment, font size, etc)
-- as opposed to local styling tags like those italicizing certain words.
--
-- Each (contiguous set) of global styling tags starts a new text section, each of which will have its self-contained set of rewrites.
-- To start such a section without changing styles, simply use an empty tag of this format like {\|}.
--
-- - Text sections: The text before, after, or in between two global styling tags.
-- Each of these sections reprents one bit of text, which is open for rewrites.
-- Such a section should be a sequence of blocks of one of the following forms:
-- - Inactive line: A brace block not started by a backslash or asterisk (Asterisks are allowed for compatibility with Dialog Swapper).
-- This can be any brace block matching this format (so also miscellaneous comments that aren't actual lines),
-- But in order for the script to interact with them, they should have the format
-- {<proposed line> - <author>} ,
-- where the <author> signature could potentially also contain other comments.
-- In the <proposed line>, should be escaped by replacing their braces with square brackets,
-- and the backslashes by harmless forward slashes.
--
-- - Active line: A block of the form
-- <proposed line>[{<author>}] ,
-- where the <proposed line> is allowed to contain (local) styling tags, and the <author> signature
-- must not begin with a backslash or asterisk and is only allowed to be omitted when this block is the last
-- in its section (since otherwise the following inactive line would be incorrectly recognized as the signature)
--
-- Only one of the blocks in a section should be an active line at any time.
--
-- In most cases, there will be only one non-empty text section.
--
-- The script's main function "Switch Active Lines" will, for each section:
-- - Deactivate any active lines, signing them with the default signature (defaults to "OG") if no signature is present.
-- - Activate any inactive lines where the " - " separating the line from the signature has been marked by replacing it with " !- ".
-- Thus, there should ideally be just one line per section marked this way.
-- - If there is a block that has been deactivated, but no block has been activated (suggesting that the user wants to write a new suggested line),
-- it will add a new signature at the end of the section, providing that one is specified in the configuration.
--
-- The function "Prepare Rewrite" will do all of the above, but also
-- - Copy the line that was just deactivated to the end of the section, before the added signature
-- That way, the user can quickly propose a small change in the line.
-- - Always add a signature, as long as one is configured.
--
-- The functions "Shift Line Break Forward" and "Shift Line Break Backward" will shift the line break in the currently active line(s)
-- forward or backward respectively, by one word (i.e. one contiguous sequence of non-space characters).
--
-- The function "Clean Up Styling Tag Escapes" will simply remove all the pipe ('|') characters from global styling tags.
--
-- Examples:
-- - Deactivating the line
-- {\i1}Hello{\i0}, world!{author}
-- Will turn it into
-- {[/i1]Hello[/i0], world! - author}
-- If a personal signature like "me" is set in the configuration, it will automatically be added:
-- {[/i1]Hello[/i0], world! - author}{me}
--
-- - Deactivating the line
-- {\an8|}Hello, {\i1}world{\i0}!
-- Will turn it into
-- {\an8|}{Hello,[/i1]world[/i0]! - OG}
-- where the default signature "OG" can be set to a different on in the configuration.
--
-- - Applying "Switch Lines" to the line
-- {Hello, [/i1]world[/i0]! !- OG}{foo - bar} .
-- Will turn it into
-- Hello, {\i1}world{\i0}!{OG}{foo - bar} .
-- Applying "Switch Lines" again will turn this line into
-- {Hello, [/i1]world[/i0]! - OG}{foo - bar} ,
-- which is just the beginning line without the marker.
-- On the other hand, applying "Prepare Rewrite" to this second line will turn it into
-- {Hello, [/i1]world[/i0]! - OG}{foo - bar}Hello, {\i1}world{\i0}!{me} ,
-- where "me" is the personal signature set in the configuration.
--
-- - A more complex example containing multiple sections: Consider a line where two people speak simultaneously,
-- which is represented using en dashes (represented as hyphens here):
-- - foo!\N- bar!
-- To make a rewrite for only one of these lines, add a separator after \N:
-- - foo!\N{\|}- bar!
-- Now these can be both be rewritten with "Prepare Rewrite":
-- {- foo!\N - OG}- foo!\N{me}{\|}{- bar! - OG}- bar!{me}
-- Say we want to rewrite the first line to "- foo2!\N", and don't rewrite the second line:
-- {- foo!\N - OG}- foo2!\N{me}{\|}{- bar! - OG}
-- Apply "Switch Lines" again:
-- {- foo!\N - OG}{- foo2!\N - me}{me}{\|}{- bar! - OG}
-- This will cause a duplicate signature, but preventing this would require a lot more macro options. Remove this duplicate, and mark both lines:
-- {- foo!\N - OG}{- foo2!\N !- me}{\|}{- bar! !- OG}
-- Finally, apply "Switch Lines":
-- {- foo!\N - OG}- foo2!{me}\N{\|}- bar!{OG}
-- This specific application is very tedious for small rewrites, but can still greatly speed up the process for longer sections with formatting.
--
-- It is strongly recommended to bind the macros for rewriting and shifting line breaks to keybinds.
-- I use Ctrl+K and Ctrl+Shift+K respectively for "Switch Active Lines" and "Prepare Rewrite",
-- as well as Ctrl+, and Ctrl+. respectively for "Shift Line Break Backward" and "Shift Line Break Forward".
function unreachable()
if not val then
aegisub.log("Incorrect line format! Aborting.")
aegisub.cancel()
end
end
-- These two functions are split up because signed deactivated lines like
-- {Upper line\N - author} would be broken by simply stripping spaces
-- surrounding \N tags everywhere.
-- An unintended but welcome side effect is that only those lines which the
-- user touches will have text fixes applied - this ensures a basic level
-- of reversibility.
function fix_text(line)
-- Applies some basic formatting fixes:
-- Removes spaces surrounding \N tags
while true do
local newline = line
:gsub(" *\\N *([^!])", "\\N%1")
:gsub(" *\\n *", "\\n")
if newline == line then
return line
end
line = newline
end
end
function fix_line_format(line)
-- Applies some basic formatting fixes:
-- Removes spaces surrounding or padding {} blocks
while true do
local newline = line
:gsub("{ *", "{")
:gsub("({[^}]-) *}", "%1}") -- make sure '}' is closing a tag here (still breaks if there's nested tags, but this is the best lua patterns can do.)
if newline == line then
return line
end
line = newline
end
end
function fix_text_checked(line)
if config.c.auto_fixes then
return fix_text(line)
else
return line
end
end
function fix_line_format_checked(line)
if config.c.auto_fixes then
return fix_line_format(line)
else
return line
end
end
function stripstart(intext, out)
local ws = intext:match("^ *")
intext = intext:sub(#ws + 1)
if not config.c.auto_fixes then
out = out .. ws
end
return intext, out
end
function appendstripend(text, out)
if config.c.auto_fixes then
local ws = text:match(" *$")
text = text:sub(1, #text - #ws)
end
return out .. text
end
function get_signature(sign)
if sign ~= "" then
return "{" .. sign .. "}"
end
return ""
end
-- Arguments
-- shift: True if shifting line breaks, false otherwise
-- shift_dir: If shifting line breaks, true if shifting forward, else false
-- rewrite: If not shifting line breaks, true if the line being deactivated should be copied for a rewrite.
function switch_lines_proper(subs, sel, rewrite, shift, shift_dir)
local insertion_point_marker = "{/|\\}"
local new_insertion_point = nil
for _, i in ipairs(sel) do
local line = subs[i]
if config.c.forbid_nested then
if line.text:match("{[^}]+{") then
aegisub.log("Nested braces detected! Please fix them before running the script. Aborting...")
return
end
end
-- local intext = fix_line_format_checked(line.text)
local intext = line.text
local out = ""
local deactivated = false
local reactivated = false
local newline = ""
while true do -- parse components of the line
intext, out = stripstart(intext, out)
local escaped_braceblock = intext:match("^{\\[^}]-|}")
local deactive_line = intext:match("^{[^\\*][^}]-}")
-- and some edge cases:
local empty_block = intext:match("^{}")
local unterminated_brace = intext:match("^{[^}]*$")
if escaped_braceblock ~= nil or intext == "" then
if rewrite then
newline, out = stripstart(newline, out)
newline = fix_text_checked(newline)
out = appendstripend(newline, out)
end
out = out .. insertion_point_marker
if (deactivated and not reactivated) or (rewrite and newline ~= "") then
out = out .. get_signature(config.c.personal_signature)
end
if intext == "" then
break
end
out = out .. escaped_braceblock
intext = intext:sub(#escaped_braceblock + 1)
deactivated = false
reactivated = false
newline = ""
elseif deactive_line ~= nil then
if shift or deactive_line:match(" !%- ") == nil then
out = out .. deactive_line
else
local reactivate_line = deactive_line
-- evil capture hacks to replace only those forward slashes
-- by backslashes, that are contained in square brackets.
while true do
local r = reactivate_line:gsub("(%[[^%]]-)/([^%]]-%])", "%1%\\%2")
if r == reactivate_line then
break
end
reactivate_line = r
end
if config.c.auto_fixes then
reactivate_line = reactivate_line:gsub(" * !%- ", " !- ")
end
out = out .. fix_text_checked(reactivate_line:gsub("^{", ""):gsub("%[", "{"):gsub("%]", "}"):gsub(" !%- ", "{"))
reactivated = true
end
intext = intext:sub(#deactive_line + 1)
elseif empty_block ~= nil then
-- just ignore it
out = out .. empty_block
intext = intext:sub(#empty_block + 1)
elseif unterminated_brace ~= nil then
-- terminate it and (effectively) abort
out = out .. unterminated_brace
intext = intext:sub(#unterminated_brace + 1)
else -- beginning of an active line
local linetext = ""
local linesignature = config.c.default_signature
-- this could be done with a bigger regex and some gsubs,
-- but in edge cases with broken nested braces this might hold up better
while intext ~= "" do
local escaped_braceblock = intext:match("^{[\\*][^}]-|}")
local styling_braceblock = intext:match("^{[\\*][^}]-}")
local cleartext = intext:match("^[^{]+")
local signature = intext:match("^{[^}]-}")
if escaped_braceblock ~= nil then
break
elseif styling_braceblock ~= nil then
newline = newline .. styling_braceblock
if not shift then
linetext = linetext .. styling_braceblock:gsub("{", "["):gsub("}", "]"):gsub("\\", "/")
else
linetext = linetext .. styling_braceblock
end
intext = intext:sub(#styling_braceblock + 1)
elseif cleartext ~= nil then
newline = newline .. cleartext
if not shift then
linetext = linetext .. cleartext
else
ctext = cleartext
if shift_dir then
if not ctext:match("\\N") then
ctext = "\\N" .. ctext
end
ctext = ctext
-- Shift a newline forward that's in somewhere in the middle of the line. Won't shift a new line to the very left.
:gsub("([^ ]+) *\\N *([^ ]+) *", "%1 %2\\N")
-- Shift a newline forward that's to the very left of the line.
:gsub("^ *\\N *([^ ]+) *", "%1\\N")
-- Remove a newline to the very right of the line.
:gsub(" *\\N *$", "")
else
if not ctext:match("\\N") then
ctext = ctext .. "\\N"
end
ctext = ctext
:gsub(" *([^ ]+) *\\N *([^ ]+)", "\\N%1 %2")
:gsub(" *([^ ]+) *\\N *$", "\\N%1")
:gsub("^ *\\N *", "")
end
linetext = linetext .. ctext
end
intext = intext:sub(#cleartext + 1)
elseif signature ~= nil then
if #signature >= 2 then
assert(signature[2] ~= "|" and signature[2] ~= "\\")
end
if not shift then
linesignature = signature:gsub("[{}]", "")
else
linetext = linetext .. signature
end
intext = intext:sub(#signature + 1)
break
else
assert(false)
end
end
if not shift then
out = out .. "{" .. fix_text_checked(linetext)
if linesignature ~= "" then
out = out .. " - " .. linesignature
end
out = out .. "}"
else
out = out .. linetext
end
deactivated = not shift
end
end
out = fix_line_format_checked(out)
new_insertion_point = out:find(insertion_point_marker)
out = out:gsub(insertion_point_marker, "")
line.text = out
subs[i] = line
end
if new_insertion_point ~= nil and aegisub.gui ~= nil then
aegisub.gui.set_cursor(new_insertion_point)
end
end
function clean_lines(subs, sel)
load_config()
for _, i in ipairs(sel) do
local line = subs[i]
while true do
local newtext = line.text:gsub("({\\[^}]-)|}", "%1}")
if newtext == line.text then
break
end
line.text = newtext
end
subs[i] = line
end
end
function switch_lines(subs, sel)
switch_lines_proper(subs, sel, false)
end
function rewrite_line(subs, sel)
switch_lines_proper(subs, sel, true)
end
function shift_forward(subs, sel)
switch_lines_proper(subs, sel, false, true, true)
end
function shift_backward(subs, sel)
switch_lines_proper(subs, sel, false, true, false)
end
function can_run(subs, sel)
return #sel == 1
end
function configure()
config:load()
local diag = {
{class = 'label', label = 'Default signature', x = 0, y = 0, width = 1, height = 1},
{
class = 'edit',
name = 'default_signature',
hint = "Signature to sign unsigned active lines with. Leave blank to deactivate.",
value = config.c.default_signature,
x = 1, y = 0, width = 1, height = 1,
},
{class = 'label', label = 'Your signature', x = 0, y = 1, width = 1, height = 1},
{
class = 'edit',
name = 'personal_signature',
hint = "Signature to automatically insert when deactivating a line. Leave blank to deactivate.",
value = config.c.personal_signature,
x = 1, y = 1, width = 1, height = 1,
},
{class = 'label', label = 'Apply auto fixes', x = 0, y = 2, width = 1, height = 1},
{
class = 'checkbox',
name = 'auto_fixes',
hint = "Whether to automatically remove unnecessary spaces. Deactivate if you know what you're doing (e.g. for special typesetting).",
value = config.c.auto_fixes,
x = 1, y = 2, width = 1, height = 1,
},
{class = 'label', label = 'Check for nested braces', x = 0, y = 3, width = 1, height = 1},
{
class = 'checkbox',
name = 'forbid_nested',
hint = "Whether to abort if nested braces are found. Deactivate if you know what you're doing.",
value = config.c.forbid_nested,
x = 1, y = 3, width = 1, height = 1,
},
}
local buttons = {'OK', 'Cancel'}
local button_ids = {ok = 'OK', cancel = 'Cancel'}
local button, results = aegisub.dialog.display(diag, buttons, button_ids)
if button == false then aegisub.cancel() end
for i,v in ipairs({"personal_signature", "default_signature", "auto_fixes", "forbid_nested"}) do
if results[v] ~= config.c[v] then
config.c[v] = results[v]
end
end
config:write()
return results
end
local mymacros = {}
function wrap_register_macro(name, ...)
if haveDepCtrl then
table.insert(mymacros, {name, ...})
else
aegisub.register_macro(script_name .. "/" .. name, ...)
end
end
wrap_register_macro("Switch Active Lines", "Deactivates the active line and activates any inactive lines marked with !- .", switch_lines, can_run)
wrap_register_macro("Prepare Rewrite", "Deactivates the active line and copies it to a new line for rewriting.", rewrite_line, can_run)
wrap_register_macro("Shift Line Break Forward", "Shifts the line break in the currently active line forward by one word.", shift_forward, can_run)
wrap_register_macro("Shift Line Break Backward", "Shifts the line break in the currently active line backward by one word.", shift_backward, can_run)
wrap_register_macro("Clean Up Styling Tag Escapes", "Removes all pipe ('|') characters from the end of styling blocks.", clean_lines)
wrap_register_macro("Configure", "Configure Rewriting Tools", configure)
if haveDepCtrl then
depctrl:registerMacros(mymacros)
end