Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Enable "let" to assign registers #9313

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 22 additions & 4 deletions src/cmd_line/commands/let.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
// eslint-disable-next-line id-denylist
import { alt, optWhitespace, Parser, seq, string, whitespace } from 'parsimmon';
import { env } from 'process';
import { ErrorCode, VimError } from '../../error';
import { Register, RegisterMode } from '../../register/register';
import { VimState } from '../../state/vimState';
import { StatusBar } from '../../statusBar';
import { ExCommand } from '../../vimscript/exCommand';
Expand All @@ -13,23 +15,23 @@ import {
str,
subtract,
} from '../../vimscript/expression/build';
import { EvaluationContext } from '../../vimscript/expression/evaluate';
import { displayValue } from '../../vimscript/expression/displayValue';
import { EvaluationContext, toString } from '../../vimscript/expression/evaluate';
import {
envVariableParser,
expressionParser,
optionParser,
registerParser,
variableParser,
} from '../../vimscript/expression/parser';
import { stringToRegisterMode } from '../../vimscript/expression/registerUtils';
import {
EnvVariableExpression,
Expression,
OptionExpression,
RegisterExpression,
VariableExpression,
} from '../../vimscript/expression/types';
import { displayValue } from '../../vimscript/expression/displayValue';
import { ErrorCode, VimError } from '../../error';

export type LetCommandOperation = '=' | '+=' | '-=' | '*=' | '/=' | '%=' | '.=' | '..=';
export type LetCommandVariable =
Expand Down Expand Up @@ -143,7 +145,23 @@ export class LetCommand extends ExCommand {
}
context.setVariable(variable, value, this.args.lock);
} else if (variable.type === 'register') {
// TODO
if (this.args.operation !== '=') {
throw VimError.fromCode(ErrorCode.InvalidOperationForRegister, this.args.operation);
}
let registerIndex = 0;
const items = value.type === 'list' ? value.items : [value];
for (const item of items) {
const registerMode =
item.type === 'register_val'
? stringToRegisterMode(item.registerMode)
: RegisterMode.CharacterWise;
Register.put(vimState, toString(value), registerIndex, /* copyToUnamed */ false, {
registerName: variable.name,
registerMode,
forceOverwrite: true,
});
registerIndex++;
}
} else if (variable.type === 'option') {
// TODO
} else if (variable.type === 'env_variable') {
Expand Down
8 changes: 8 additions & 0 deletions src/error.ts
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,10 @@ export enum ErrorCode {
UsingABlobAsANumber = 974,
CannotModifyExistingVariable = 995,
CannotLockARegister = 996,
CannotAccessClipboardRegister = 997,
UsingARegisterAsANumber = 998,
CannotAccessRecordedStateRegister = 999,
InvalidOperationForRegister = 1000,
}

export const ErrorMessage: IErrorMessage = {
Expand Down Expand Up @@ -162,6 +166,10 @@ export const ErrorMessage: IErrorMessage = {
974: 'Using a Blob as a Number',
995: 'Cannot modify existing variable',
996: 'Cannot lock a register',
997: 'Cannot access clipboard register',
998: 'Using a register as a Number',
999: 'Cannot access recorded state register',
1000: 'Invalid operation for register',
};

export class VimError extends Error {
Expand Down
60 changes: 41 additions & 19 deletions src/register/register.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,9 +55,10 @@ export class Register {
vimState: VimState,
content: RegisterContent,
multicursorIndex?: number,
copyToUnnamed?: boolean,
copyToUnnamed?: boolean, // TODO: This should be a part of the options object
options: { registerName?: string; registerMode?: RegisterMode; forceOverwrite?: boolean } = {},
): void {
const register = vimState.recordedState.registerName;
const register = options.registerName || vimState.recordedState.registerName;

if (!Register.isValidRegister(register)) {
throw new Error(`Invalid register ${register}`);
Expand All @@ -67,17 +68,33 @@ export class Register {
return;
}

if (Register.isValidUppercaseRegister(register)) {
Register.appendToRegister(vimState, register.toLowerCase(), content, multicursorIndex ?? 0);
if (Register.isValidUppercaseRegister(register) && !options.forceOverwrite) {
Register.appendToRegister(
vimState,
Register.decapitalize(register),
content,
multicursorIndex ?? 0,
options.registerMode,
);
} else {
Register.overwriteRegister(vimState, register, content, multicursorIndex ?? 0);
Register.overwriteRegister(
vimState,
Register.decapitalize(register),
content,
multicursorIndex ?? 0,
options.registerMode,
);
}

if (copyToUnnamed && register !== '"') {
Register.registers.set('"', Register.registers.get(register)!);
}
}

private static decapitalize(register: string): string {
return Register.isValidUppercaseRegister(register) ? register.toLowerCase() : register;
}

public static isValidRegister(register: string): boolean {
return (
Register.isValidLowercaseRegister(register) ||
Expand All @@ -95,7 +112,7 @@ export class Register {
return registerName === '_';
}

private static isClipboardRegister(registerName: string): boolean {
public static isClipboardRegister(registerName: string): boolean {
return registerName === '*' || registerName === '+';
}

Expand All @@ -120,13 +137,14 @@ export class Register {
register: string,
content: RegisterContent,
multicursorIndex: number,
registerMode?: RegisterMode,
): void {
if (multicursorIndex === 0 || !Register.registers.has(register)) {
Register.registers.set(register, []);
}

Register.registers.get(register)![multicursorIndex] = {
registerMode: vimState.currentRegisterMode,
registerMode: registerMode || vimState.currentRegisterMode,
text: content,
};

Expand All @@ -149,6 +167,7 @@ export class Register {
register: string,
content: RegisterContent,
multicursorIndex: number,
registerMode?: RegisterMode,
): void {
if (!Register.registers.has(register)) {
Register.registers.set(register, []);
Expand All @@ -162,11 +181,13 @@ export class Register {
text: content,
};
} else {
// Line-wise trumps other RegisterModes
const registerMode =
vimState.currentRegisterMode === RegisterMode.LineWise
? RegisterMode.LineWise
: oldContent.registerMode;
if (!registerMode) {
// Line-wise trumps other RegisterModes
registerMode =
vimState.currentRegisterMode === RegisterMode.LineWise
? RegisterMode.LineWise
: oldContent.registerMode;
}
let newText: RegisterContent;
if (oldContent.text instanceof RecordedState || content instanceof RecordedState) {
newText = oldContent.text;
Expand Down Expand Up @@ -271,13 +292,7 @@ export class Register {
register: string,
multicursorIndex = 0,
): Promise<IRegisterContent | undefined> {
if (!Register.isValidRegister(register)) {
throw new Error(`Invalid register ${register}`);
}

register = register.toLowerCase();

const contentByCursor = Register.registers.get(register);
const contentByCursor = Register.getRegisterArray(register);

if (Register.isClipboardRegister(register)) {
const clipboardContent = (await Clipboard.Paste()).replace(/\r\n/g, '\n');
Expand All @@ -302,6 +317,13 @@ export class Register {
return contentByCursor?.[multicursorIndex];
}

public static getRegisterArray(register: string): IRegisterContent[] | undefined {
if (!Register.isValidRegister(register)) {
throw new Error(`Invalid register ${register}`);
}
return Register.registers.get(register.toLowerCase());
}

public static has(register: string): boolean {
return Register.registers.has(register);
}
Expand Down
36 changes: 24 additions & 12 deletions src/vimscript/expression/build.ts
Original file line number Diff line number Diff line change
@@ -1,21 +1,22 @@
import {
NumberValue,
Expression,
ListExpression,
UnaryExpression,
BinaryOp,
BinaryExpression,
FunctionCallExpression,
StringValue,
LambdaExpression,
VariableExpression,
Namespace,
BinaryOp,
BlobValue,
DictionaryValue,
Expression,
FloatValue,
FuncRefValue,
FunctionCallExpression,
LambdaExpression,
ListExpression,
ListValue,
DictionaryValue,
Namespace,
NumberValue,
RegisterValue,
StringValue,
UnaryExpression,
Value,
BlobValue,
VariableExpression,
} from './types';

export function int(value: number): NumberValue {
Expand Down Expand Up @@ -66,6 +67,17 @@ export function blob(data: ArrayBuffer): BlobValue {
};
}

export function register(
content: string,
registerMode: 'character' | 'line' | 'block',
): RegisterValue {
return {
type: 'register_val',
content,
registerMode,
};
}

export function listExpr(items: Expression[]): ListExpression {
return {
type: 'list',
Expand Down
2 changes: 2 additions & 0 deletions src/vimscript/expression/displayValue.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,5 +42,7 @@ export function displayValue(value: Value, topLevel = true): string {
.join('')
.toUpperCase()
);
case 'register_val':
return `register('${value.content}', ${value.registerMode})`;
}
}
23 changes: 20 additions & 3 deletions src/vimscript/expression/evaluate.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import { all } from 'parsimmon';
import { displayValue } from './displayValue';
import { configuration } from '../../configuration/configuration';
import { ErrorCode, VimError } from '../../error';
import { globalState } from '../../state/globalState';
import { bool, float, funcref, listExpr, int, str, list, funcCall, blob } from './build';
import { blob, bool, float, funcCall, funcref, int, list, listExpr, str } from './build';
import { displayValue } from './displayValue';
import { expressionParser, numberParser } from './parser';
import { lookupRegister } from './registerUtils';
import {
BinaryOp,
ComparisonOp,
Expand Down Expand Up @@ -43,6 +44,8 @@ function toInt(value: Value): number {
throw VimError.fromCode(ErrorCode.UsingAFuncrefAsANumber);
case 'blob':
throw VimError.fromCode(ErrorCode.UsingABlobAsANumber);
case 'register_val':
throw VimError.fromCode(ErrorCode.UsingARegisterAsANumber);
}
}

Expand All @@ -57,6 +60,7 @@ function toFloat(value: Value): number {
case 'dict_val':
case 'funcref':
case 'blob':
case 'register_val':
throw VimError.fromCode(ErrorCode.NumberOrFloatRequired);
}
}
Expand All @@ -77,6 +81,8 @@ export function toString(value: Value): string {
throw VimError.fromCode(ErrorCode.UsingFuncrefAsAString);
case 'blob':
return displayValue(value);
case 'register_val':
return value.content;
}
}

Expand All @@ -88,6 +94,7 @@ function toList(value: Value): ListValue {
case 'funcref':
case 'dict_val':
case 'blob':
case 'register_val':
throw VimError.fromCode(ErrorCode.ListRequired);
case 'list':
return value;
Expand All @@ -102,6 +109,7 @@ function toDict(value: Value): DictionaryValue {
case 'list':
case 'funcref':
case 'blob':
case 'register_val':
throw VimError.fromCode(ErrorCode.DictionaryRequired);
case 'dict_val':
return value;
Expand Down Expand Up @@ -147,6 +155,7 @@ export class EvaluationContext {
case 'dict_val':
case 'funcref':
case 'blob':
case 'register_val':
return expression;
case 'list':
return list(expression.items.map((x) => this.evaluate(x)));
Expand All @@ -168,7 +177,7 @@ export class EvaluationContext {
case 'variable':
return this.evaluateVariable(expression);
case 'register':
return str(''); // TODO
return lookupRegister(expression.name);
case 'option':
return str(''); // TODO
case 'env_variable':
Expand Down Expand Up @@ -395,6 +404,9 @@ export class EvaluationContext {
const bytes = new Uint8Array(sequence.data);
return int(bytes[toInt(index)]);
}
case 'register_val': {
return this.evaluateIndex(str(toString(sequence)), index);
}
}
}

Expand Down Expand Up @@ -438,6 +450,9 @@ export class EvaluationContext {
case 'blob': {
return blob(new Uint8Array(sequence.data).slice(_start, _end + 1));
}
case 'register_val': {
return this.evaluateSlice(str(toString(sequence)), start, end);
}
}
}

Expand Down Expand Up @@ -1281,6 +1296,8 @@ export class EvaluationContext {
// return int(7);
case 'blob':
return int(8);
case 'register_val':
return int(9);
default:
const guard: never = x;
throw new Error('type() got unexpected type');
Expand Down
Loading