dotfiles/.aegisub/automation/include/a-mo/LineCollection.moon

273 lines
7.1 KiB
Plaintext

local log, Line
version = '1.3.0'
haveDepCtrl, DependencyControl = pcall require, 'l0.DependencyControl'
if haveDepCtrl
version = DependencyControl {
name: 'LineCollection'
:version
description: 'A class for handling collections of lines.'
author: 'torque'
url: 'https://github.com/TypesettingTools/Aegisub-Motion'
moduleName: 'a-mo.LineCollection'
feed: 'https://raw.githubusercontent.com/TypesettingTools/Aegisub-Motion/DepCtrl/DependencyControl.json'
{
{ 'a-mo.Log', version: '1.0.0' }
{ 'a-mo.Line', version: '1.5.3' }
}
}
log, Line = version\requireModules!
else
log = require 'a-mo.Log'
Line = require 'a-mo.Line'
frameFromMs = aegisub.frame_from_ms
class LineCollection
@version: version
@fromAllLines: ( sub, validationCb, selectLines ) =>
sel = { }
for i = 1, #@sub
table.insert( sel, i ) if @sub[i].class == "dialogue"
@ sub, sel, validationCb, selectLines
new: ( @sub, sel, validationCb, selectLines = true ) =>
@lines = { }
meta = getmetatable @
if 'function' != type meta.__index
metaIndex = meta.__index
meta.__index = ( index ) =>
if 'number' == type index
@lines[index]
else
metaIndex[index]
if type( sel ) == "table" and #sel > 0
@collectLines sel, validationCb, selectLines
if frameFromMs 0
@getFrameInfo!
else
for i = #@sub, 1, -1
if @sub[i].class != "dialogue" then
@firstLineNumber = i + 1
@lastLineNumber = i + 1
break
-- This method should update various properties such as
-- (start|end)(Time|Frame).
addLine: ( line, validationCb = (-> return true), selectLine = true, index = false ) =>
if validationCb line
line.parentCollection = @
line.inserted = false
line.selected = selectLine
line.number = index == true and line.number or index or nil
-- if @startTime is unset, @endTime should damn well be too.
if @startTime
if @startTime > line.start_time
@startTime = line.start_time
if @endTime < line.end_time
@endTime = line.end_time
else
@startTime = line.start_time
@endTime = line.end_time
if @hasMetaStyles
line.styleRef = @styles[line.style]
if @hasFrameInfo
line.startFrame = frameFromMs line.start_time
line.endFrame = frameFromMs line.end_time
@startFrame = frameFromMs @startTime
@endFrame = frameFromMs @endTime
@totalFrames = @endFrame - @startFrame
table.insert @lines, line
generateMetaAndStyles: =>
@styles = { }
@meta = { }
for i = 1, #@sub
line = @sub[i]
if line.class == "style"
@styles[line.name] = line
-- not going to bother porting all the special-case bullshit over
-- from karaskel.
elseif line.class == "info"
@meta[line.key] = line.value
elseif line.class == "dialogue"
break
unless next @styles
log.windowError "No styles could be found and I guarantee that's gonna break something."
@hasMetaStyles = true
collectLines: ( sel, validationCb = (( line ) -> return not line.comment), selectLines = true ) =>
unless @hasMetaStyles
@generateMetaAndStyles!
dialogueStart = 0
for x = 1, #@sub
if @sub[x].class == "dialogue"
dialogueStart = x - 1 -- start line of dialogue subs
break
@startTime = @sub[sel[1]].start_time
@endTime = @sub[sel[1]].end_time
@lastLineNumber = 0
for i = #sel, 1, -1
with line = Line @sub[sel[i]], @
if validationCb line
.number = sel[i]
@firstLineNumber = math.min .number, @firstLineNumber or .number
@lastLineNumber = math.max .number, @lastLineNumber
.inserted = true
.hasBeenDeleted = false
.selected = selectLines
.humanizedNumber = .number - dialogueStart
.styleRef = @styles[.style]
if .start_time < @startTime
@startTime = .start_time
if .end_time > @endTime
@endTime = .end_time
table.insert @lines, line
getFrameInfo: =>
for line in *@lines
line.startFrame = frameFromMs line.start_time
line.endFrame = frameFromMs line.end_time
@startFrame = frameFromMs @startTime
@endFrame = frameFromMs @endTime
@totalFrames = @endFrame - @startFrame
@hasFrameInfo = true
callMethodOnAllLines: ( methodName, ... ) =>
for line in *@lines
line[methodName] line, ...
combineIdenticalLines: =>
lastLine = @lines[1]
linesToSkip = { }
for i = 2, #@lines
log.checkCancellation!
if lastLine\combineWithLine @lines[i]
linesToSkip[#linesToSkip+1] = @lines[i]
@shouldInsertLines = true
continue
else lastLine = @lines[i]
@deleteLines linesToSkip
-- The third value passed to the callback is for progress reporting only,
-- and the fourth is the actual index.
runCallback: ( callback, reverse ) =>
lineCount = #@lines
if reverse
for index = lineCount, 1, -1
callback @, @lines[index], lineCount - index + 1, index
else
for index = 1, lineCount
callback @, @lines[index], index, index
deleteLines: ( lines = @lines, doShift = true ) =>
if lines.__class == Line
lines = { lines }
lineSet = {line,true for _,line in pairs lines when not line.hasBeenDeleted}
-- make sure all lines are unique and have not actually been already removed
lines = [k for k,v in pairs lineSet]
@sub.delete [line.number for line in *lines when line.inserted]
@lastLineNumber = @firstLineNumber
shift = #lines or 0
for line in *@lines
if lineSet[line]
line.hasBeenDeleted = true
shift -= line.inserted and 1 or 0
elseif not line.hasBeenDeleted and line.inserted
line.number -= doShift and shift or 0
@lastLineNumber = math.max(line.number, @lastLineNumber)
insertLines: =>
toInsert = [line for line in *@lines when not (line.inserted or line.hasBeenDeleted)]
tailLines, numberedLines = {}, {}
for i = 1, #toInsert
line = toInsert[i]
if line.number
numberedLines[#numberedLines + 1] = line
line.i = i
else
tailLines[#tailLines + 1] = line
line.number = @lastLineNumber + i
line.inserted = true
table.sort numberedLines, ( a, b ) ->
return (a.number < b.number) or (a.number == b.number) and (a.i < b.i)
for line in *numberedLines
@sub.insert line.number, line
line.inserted = true
@lastLineNumber = math.max @lastLineNumber, line.number
tailLineCnt, chunkSize = #tailLines, 1000
if tailLineCnt > 0
for i = 1, tailLineCnt, chunkSize
chunkSize = math.min chunkSize, tailLineCnt - i + 1
@sub.insert @lastLineNumber + i, unpack tailLines, i, i+chunkSize-1
@lastLineNumber = math.max @lastLineNumber, tailLines[tailLineCnt].number
replaceLines: =>
if @shouldInsertLines
@insertLines!
else
for line in *@lines
if line.inserted and not line.hasBeenDeleted
@sub[line.number] = line
getSelection: =>
sel = [line.number for line in *@lines when line.selected and line.inserted and not line.hasBeenDeleted]
return sel, sel[#sel]
__newindex: ( index, value ) =>
if 'number' == type index
@lines[index] = value
else
rawset @, index, value
__len: =>
#@lines
__ipairs: =>
iterator = ( tbl, i ) ->
i += 1
value = tbl[i]
if value
i, value
iterator, @lines, 0
-- There's no real reason to use pairs, but I've preserved it anyways
__pairs: =>
next, @lines, nil
if haveDepCtrl
return version\register LineCollection
else
return LineCollection