-
-
Notifications
You must be signed in to change notification settings - Fork 1.3k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[WIP] MVP support for expressions,
:let
, and :echo
There remain a mountain of bugs and TODOs, but this a big step toward much more substantial vimscript support. Refs #463 Fixes #7136, fixes #7155
- Loading branch information
Showing
12 changed files
with
1,541 additions
and
5 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,53 @@ | ||
import { optWhitespace, Parser, whitespace } from 'parsimmon'; | ||
import { VimState } from '../../state/vimState'; | ||
import { StatusBar } from '../../statusBar'; | ||
import { ExCommand } from '../../vimscript/exCommand'; | ||
import { EvaluationContext } from '../../vimscript/expression/evaluate'; | ||
import { expressionParser } from '../../vimscript/expression/parser'; | ||
import { Expression, Value } from '../../vimscript/expression/types'; | ||
|
||
export function displayValue(value: Value, topLevel = true): string { | ||
switch (value.type) { | ||
case 'number': | ||
case 'float': | ||
// TODO: this is incorrect for float with exponent | ||
return value.value.toString(); | ||
case 'string': | ||
return topLevel ? value.value : `'${value.value.replace("'", "''")}'`; | ||
case 'list': | ||
return `[${value.items.map((v) => displayValue(v, false)).join(', ')}]`; | ||
case 'dict_val': | ||
return `{${[...value.items] | ||
.map(([k, v]) => `'${k}': ${displayValue(v, false)}`) | ||
.join(', ')}}`; | ||
} | ||
} | ||
|
||
export class EchoCommand extends ExCommand { | ||
public static argParser(echoArgs: { sep: string; error: boolean }): Parser<EchoCommand> { | ||
return optWhitespace | ||
.then(expressionParser.sepBy(whitespace)) | ||
.map((expressions) => new EchoCommand(echoArgs, expressions)); | ||
} | ||
|
||
private sep: string; | ||
private error: boolean; | ||
private expressions: Expression[]; | ||
private constructor(args: { sep: string; error: boolean }, expressions: Expression[]) { | ||
super(); | ||
this.sep = args.sep; | ||
this.error = args.error; | ||
this.expressions = expressions; | ||
} | ||
|
||
public override neovimCapable(): boolean { | ||
return true; | ||
} | ||
|
||
public async execute(vimState: VimState): Promise<void> { | ||
const ctx = new EvaluationContext(); | ||
const values = this.expressions.map((x) => ctx.evaluate(x)); | ||
const message = values.map((v) => displayValue(v)).join(this.sep); | ||
StatusBar.setText(vimState, message, this.error); | ||
} | ||
} |
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,139 @@ | ||
import { alt, optWhitespace, Parser, seq, string, whitespace } from 'parsimmon'; | ||
import { env } from 'process'; | ||
import { VimState } from '../../state/vimState'; | ||
import { StatusBar } from '../../statusBar'; | ||
import { ExCommand } from '../../vimscript/exCommand'; | ||
import { | ||
add, | ||
concat, | ||
divide, | ||
modulo, | ||
multiply, | ||
str, | ||
subtract, | ||
} from '../../vimscript/expression/build'; | ||
import { EvaluationContext } from '../../vimscript/expression/evaluate'; | ||
import { | ||
envVariableParser, | ||
expressionParser, | ||
optionParser, | ||
registerParser, | ||
variableParser, | ||
} from '../../vimscript/expression/parser'; | ||
import { | ||
EnvVariableExpression, | ||
Expression, | ||
OptionExpression, | ||
RegisterExpression, | ||
VariableExpression, | ||
} from '../../vimscript/expression/types'; | ||
import { displayValue } from './echo'; | ||
|
||
export type LetCommandOperation = '=' | '+=' | '-=' | '*=' | '/=' | '%=' | '.=' | '..='; | ||
export type LetCommandVariable = | ||
| VariableExpression | ||
| OptionExpression | ||
| RegisterExpression | ||
| EnvVariableExpression; | ||
export type LetCommandArgs = | ||
| { | ||
operation: LetCommandOperation; | ||
variable: LetCommandVariable; | ||
expression: Expression; | ||
} | ||
| { | ||
operation: 'print'; | ||
variables: LetCommandVariable[]; | ||
}; | ||
|
||
const operationParser: Parser<LetCommandOperation> = alt( | ||
string('='), | ||
string('+='), | ||
string('-='), | ||
string('*='), | ||
string('/='), | ||
string('%='), | ||
string('.='), | ||
string('..=') | ||
); | ||
|
||
const letVarParser: Parser<LetCommandVariable> = alt( | ||
variableParser, | ||
optionParser, | ||
envVariableParser, | ||
registerParser | ||
); | ||
|
||
export class LetCommand extends ExCommand { | ||
// TODO: Support unpacking | ||
// TODO: Support indexing | ||
// TODO: Support slicing | ||
public static readonly argParser: Parser<LetCommand> = alt( | ||
// `:let {var} = {expr}` | ||
// `:let {var} += {expr}` | ||
// `:let {var} -= {expr}` | ||
// `:let {var} .= {expr}` | ||
whitespace.then( | ||
seq(letVarParser, operationParser.wrap(optWhitespace, optWhitespace), expressionParser).map( | ||
([variable, operation, expression]) => | ||
new LetCommand({ | ||
operation, | ||
variable, | ||
expression, | ||
}) | ||
) | ||
), | ||
// `:let` | ||
// `:let {var-name} ...` | ||
optWhitespace | ||
.then(letVarParser.sepBy(whitespace)) | ||
.map((variables) => new LetCommand({ operation: 'print', variables })) | ||
); | ||
|
||
private args: LetCommandArgs; | ||
constructor(args: LetCommandArgs) { | ||
super(); | ||
this.args = args; | ||
} | ||
|
||
async execute(vimState: VimState): Promise<void> { | ||
const context = new EvaluationContext(); | ||
if (this.args.operation === 'print') { | ||
if (this.args.variables.length === 0) { | ||
// TODO | ||
} else { | ||
const variable = this.args.variables[this.args.variables.length - 1]; | ||
const value = context.evaluate(variable); | ||
// TODO: If number, should include # sign | ||
StatusBar.setText(vimState, `${variable.name} ${displayValue(value)}`); | ||
} | ||
} else { | ||
const variable = this.args.variable; | ||
let value = context.evaluate(this.args.expression); | ||
if (variable.type === 'variable') { | ||
if (this.args.operation === '+=') { | ||
value = context.evaluate(add(variable, value)); | ||
} else if (this.args.operation === '-=') { | ||
value = context.evaluate(subtract(variable, value)); | ||
} else if (this.args.operation === '*=') { | ||
value = context.evaluate(multiply(variable, value)); | ||
} else if (this.args.operation === '/=') { | ||
value = context.evaluate(divide(variable, value)); | ||
} else if (this.args.operation === '%=') { | ||
value = context.evaluate(modulo(variable, value)); | ||
} else if (this.args.operation === '.=') { | ||
value = context.evaluate(concat(variable, value)); | ||
} else if (this.args.operation === '..=') { | ||
value = context.evaluate(concat(variable, value)); | ||
} | ||
context.setVariable(variable, value); | ||
} else if (variable.type === 'register') { | ||
// TODO | ||
} else if (variable.type === 'option') { | ||
// TODO | ||
} else if (variable.type === 'env_variable') { | ||
value = str(env[variable.name] ?? ''); | ||
} | ||
} | ||
} | ||
} |
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
Oops, something went wrong.