-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
5 changed files
with
206 additions
and
16 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
|
||
/** | ||
* return first case[1] where case[0] is true | ||
*/ | ||
function condShort(...cases) { | ||
for (const c in cases) { | ||
if (cases[c][0] === true) { | ||
return cases[c][1] | ||
} | ||
} | ||
throw new Error("No default was provided") | ||
} | ||
|
||
export default { condShort } |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,146 @@ | ||
|
||
import fn from "@/fn.js"; | ||
|
||
|
||
const ACTION = { tag: "action" } | ||
const VALUE = { tag: "value" } | ||
|
||
class ActionHistory { | ||
/* | ||
Capture calls of an action, link them in a tree | ||
For call-level options granularity: | ||
action.trackWith({...options})(...args) | ||
*/ | ||
constructor(target, func) { | ||
this.root = { | ||
prevs: [], | ||
nexts: [], | ||
} | ||
this.func = func | ||
this.target = target | ||
this.pointer = this.root | ||
} | ||
|
||
record(args) { | ||
var node = { | ||
nexts: [], | ||
prevs: [this.pointer], | ||
args: args | ||
} | ||
this.pointer.nexts.push(node) | ||
this.pointer = node | ||
} | ||
|
||
recordInPlace(args) { | ||
this.pointer.args = args | ||
} | ||
|
||
peekNext() { | ||
/* | ||
return the next node | ||
picks the latest branch of nexts | ||
*/ | ||
return this.pointer.nexts.at(-1) | ||
} | ||
|
||
peekPrev() { | ||
/* | ||
return the next node | ||
picks the latest branch of nexts | ||
*/ | ||
const res = this.pointer.prevs.at(-1) | ||
if (res === this.root) return undefined | ||
return res | ||
} | ||
|
||
tryNext() { | ||
/* | ||
moves pointer one step forward, if possible | ||
*/ | ||
const potentialNext = this.peekNext() | ||
this.pointer = potentialNext ?? this.pointer | ||
return this | ||
} | ||
|
||
tryPrev() { | ||
/* | ||
moves pointer one step back, if possible | ||
*/ | ||
const potentialPrev = this.peekPrev() | ||
this.pointer = potentialPrev ?? this.pointer | ||
return this | ||
} | ||
|
||
call() { | ||
if (this.pointer.args === undefined) { | ||
throw new Error('calling ActionHistory.call() on root') | ||
} | ||
|
||
this.func.call(this.target, ...this.pointer.args) | ||
} | ||
|
||
} | ||
|
||
function hsTrack(target, props) { | ||
/* | ||
props.keys() -> { propKey: History(target, target[propKey]), ... } | ||
+ auto record on call | ||
*/ | ||
var history = {} | ||
|
||
for (const key in props) { | ||
if (props[key] === ACTION) { | ||
const func = target[key] | ||
|
||
history[key] = new ActionHistory(target, func) | ||
|
||
function wrapper(...args) { | ||
history[key].record(args) | ||
return func.call(target, ...args) | ||
} | ||
|
||
/* | ||
(options, default): | ||
isTrack: true \ -- record the arguments | ||
isOverwrite: false / -- replace arguments at pointer | ||
isCall: true -- call the action | ||
isArgsFromPrev: false -- use previous arguments instead, if possible | ||
*/ | ||
wrapper.trackWith = (options) => { | ||
|
||
const tracking = fn.condShort( | ||
[options.isTrack === false, (args) => { }], | ||
[options.isOverwrite, (args) => history[key].recordInPlace(args)], | ||
[true, (args) => history[key].record(args)] | ||
) | ||
|
||
const calling = fn.condShort( | ||
[options.isCall === false, (args) => { }], | ||
[true, (args) => func.apply(target, args)] | ||
) | ||
|
||
const argumenting = fn.condShort( | ||
[options.isArgsFromPrev, (args) => history[key].peekPrev()?.args ?? args], | ||
[true, (args) => args] | ||
) | ||
|
||
return (...args) => { | ||
const currentArgs = argumenting(args) | ||
tracking(currentArgs) | ||
return calling(currentArgs) | ||
} | ||
} | ||
|
||
target[key] = wrapper | ||
|
||
} else { | ||
throw new Error('Not Implemented') | ||
} | ||
} | ||
|
||
return history | ||
} | ||
|
||
export { ACTION, VALUE, hsTrack } |