Add drop, sort commands, start tail command; implement support for destructuredOrLines match type; misc fixes
This commit is contained in:
93
src/vm/commands/drop.ts
Normal file
93
src/vm/commands/drop.ts
Normal file
@@ -0,0 +1,93 @@
|
||||
import {Command, ParseContext, StrTerm} from "./command.js";
|
||||
import {LexInput} from "../lexer.js";
|
||||
import {StrVM} from "../vm.js";
|
||||
import {Lines} from "./lines.js";
|
||||
import {Words} from "./words.js";
|
||||
import {Join} from "./join.js";
|
||||
|
||||
export type DropData = {
|
||||
type: 'line'|'word'|'index',
|
||||
specific: StrTerm,
|
||||
}
|
||||
|
||||
/**
|
||||
* Like `on`, this command has a few forms:
|
||||
*
|
||||
* drop word 3 | drop line 3
|
||||
* Assume the subject is a string. Split into words|lines respectively, remove the 3rd index, and rejoin.
|
||||
*
|
||||
* drop 3 | drop index 3
|
||||
* Assume the subject is a destructured. Drop the 3rd element.
|
||||
*/
|
||||
export class Drop extends Command<DropData> {
|
||||
async attemptParse(context: ParseContext): Promise<DropData> {
|
||||
const next = context.peekTerm()
|
||||
if ( next?.term === 'int' || next?.term === 'variable' ) {
|
||||
return {
|
||||
type: 'index',
|
||||
specific: context.popTerm(),
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
type: context.popKeywordInSet(['line', 'word', 'index']).value,
|
||||
specific: context.popTerm(),
|
||||
}
|
||||
}
|
||||
|
||||
getDisplayName(): string {
|
||||
return 'drop'
|
||||
}
|
||||
|
||||
isParseCandidate(token: LexInput): boolean {
|
||||
return this.isKeyword(token, 'drop')
|
||||
}
|
||||
|
||||
async execute(vm: StrVM, data: DropData): Promise<StrVM> {
|
||||
// If the type is line|word, first destructure the subject accordingly:
|
||||
let rejoin = false
|
||||
if ( data.type === 'line' ) {
|
||||
vm = await (new Lines).execute(vm)
|
||||
rejoin = true
|
||||
} else if ( data.type === 'word' ) {
|
||||
vm = await (new Words).execute(vm)
|
||||
rejoin = true
|
||||
}
|
||||
|
||||
// Then, drop the item at the given index in the destructured subject:
|
||||
vm = await vm.replaceContextMatchingTerm(ctx => ({
|
||||
destructured: async sub => {
|
||||
const idx = ctx.resolveInt(data.specific)
|
||||
sub = [...sub]
|
||||
|
||||
// In word mode, all whitespace is preserved. So, we need to take the prefix
|
||||
// of the element that's about to be deleted and add it to the prefix of the
|
||||
// word right after it (example: "a\nb c" `drop word 1` should become "a\n c", not "a c"
|
||||
if ( data.type === 'word' && sub[idx]?.prefix && sub[idx + 1] ) {
|
||||
sub[idx + 1].prefix = `${sub[idx].prefix}${sub[idx + 1].prefix || ''}`
|
||||
}
|
||||
|
||||
// Remove the nth element.
|
||||
sub.splice(idx, 1)
|
||||
|
||||
// In line mode, if we delete a line, we *don't* want the newline
|
||||
// prefixes to be preserved, as that would clear the contents of the line,
|
||||
// but leave an empty line in-place. The only edge case we need to account
|
||||
// for is if we removed the first line, we should remove the newline prefix
|
||||
// from the *new* first line:
|
||||
if ( data.type === 'line' && idx === 0 && sub.length > 0 ) {
|
||||
sub[0].prefix = undefined
|
||||
}
|
||||
|
||||
return sub
|
||||
},
|
||||
}))
|
||||
|
||||
// If we previously split the value (i.e. for type = line|word), rejoin it:
|
||||
if ( rejoin ) {
|
||||
vm = await (new Join).execute(vm, {})
|
||||
}
|
||||
|
||||
return vm
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user