Skip to content

Commit 1a16bb2

Browse files
committed
First pass assigning registers
1 parent 2398110 commit 1a16bb2

File tree

8 files changed

+172
-39
lines changed

8 files changed

+172
-39
lines changed

src/cmd_line/commands/let.ts

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
// eslint-disable-next-line id-denylist
22
import { alt, optWhitespace, Parser, seq, string, whitespace } from 'parsimmon';
33
import { env } from 'process';
4+
import { ErrorCode, VimError } from '../../error';
5+
import { Register, RegisterMode } from '../../register/register';
46
import { VimState } from '../../state/vimState';
57
import { StatusBar } from '../../statusBar';
68
import { ExCommand } from '../../vimscript/exCommand';
@@ -13,23 +15,23 @@ import {
1315
str,
1416
subtract,
1517
} from '../../vimscript/expression/build';
16-
import { EvaluationContext } from '../../vimscript/expression/evaluate';
18+
import { displayValue } from '../../vimscript/expression/displayValue';
19+
import { EvaluationContext, toString } from '../../vimscript/expression/evaluate';
1720
import {
1821
envVariableParser,
1922
expressionParser,
2023
optionParser,
2124
registerParser,
2225
variableParser,
2326
} from '../../vimscript/expression/parser';
27+
import { stringToRegisterMode } from '../../vimscript/expression/registerUtils';
2428
import {
2529
EnvVariableExpression,
2630
Expression,
2731
OptionExpression,
2832
RegisterExpression,
2933
VariableExpression,
3034
} from '../../vimscript/expression/types';
31-
import { displayValue } from '../../vimscript/expression/displayValue';
32-
import { ErrorCode, VimError } from '../../error';
3335

3436
export type LetCommandOperation = '=' | '+=' | '-=' | '*=' | '/=' | '%=' | '.=' | '..=';
3537
export type LetCommandVariable =
@@ -143,7 +145,23 @@ export class LetCommand extends ExCommand {
143145
}
144146
context.setVariable(variable, value, this.args.lock);
145147
} else if (variable.type === 'register') {
146-
// TODO
148+
if (this.args.operation !== '=') {
149+
throw VimError.fromCode(ErrorCode.InvalidOperationForRegister, this.args.operation);
150+
}
151+
let registerIndex = 0;
152+
const items = value.type === 'list' ? value.items : [value];
153+
for (const item of items) {
154+
const registerMode =
155+
item.type === 'register_val'
156+
? stringToRegisterMode(item.registerMode)
157+
: RegisterMode.CharacterWise;
158+
Register.put(vimState, toString(value), registerIndex, /* copyToUnamed */ false, {
159+
registerName: variable.name,
160+
registerMode,
161+
forceOverwrite: true,
162+
});
163+
registerIndex++;
164+
}
147165
} else if (variable.type === 'option') {
148166
// TODO
149167
} else if (variable.type === 'env_variable') {

src/error.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,10 @@ export enum ErrorCode {
8181
UsingABlobAsANumber = 974,
8282
CannotModifyExistingVariable = 995,
8383
CannotLockARegister = 996,
84+
CannotAccessClipboardRegister = 997,
85+
UsingARegisterAsANumber = 998,
86+
CannotAccessRecordedStateRegister = 999,
87+
InvalidOperationForRegister = 1000,
8488
}
8589

8690
export const ErrorMessage: IErrorMessage = {
@@ -162,6 +166,10 @@ export const ErrorMessage: IErrorMessage = {
162166
974: 'Using a Blob as a Number',
163167
995: 'Cannot modify existing variable',
164168
996: 'Cannot lock a register',
169+
997: 'Cannot access clipboard register',
170+
998: 'Using a register as a Number',
171+
999: 'Cannot access recorded state register',
172+
1000: 'Invalid operation for register',
165173
};
166174

167175
export class VimError extends Error {

src/register/register.ts

Lines changed: 41 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -55,9 +55,10 @@ export class Register {
5555
vimState: VimState,
5656
content: RegisterContent,
5757
multicursorIndex?: number,
58-
copyToUnnamed?: boolean,
58+
copyToUnnamed?: boolean, // TODO: This should be a part of the options object
59+
options: { registerName?: string; registerMode?: RegisterMode; forceOverwrite?: boolean } = {},
5960
): void {
60-
const register = vimState.recordedState.registerName;
61+
const register = options.registerName || vimState.recordedState.registerName;
6162

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

70-
if (Register.isValidUppercaseRegister(register)) {
71-
Register.appendToRegister(vimState, register.toLowerCase(), content, multicursorIndex ?? 0);
71+
if (Register.isValidUppercaseRegister(register) && !options.forceOverwrite) {
72+
Register.appendToRegister(
73+
vimState,
74+
Register.decapitalize(register),
75+
content,
76+
multicursorIndex ?? 0,
77+
options.registerMode,
78+
);
7279
} else {
73-
Register.overwriteRegister(vimState, register, content, multicursorIndex ?? 0);
80+
Register.overwriteRegister(
81+
vimState,
82+
Register.decapitalize(register),
83+
content,
84+
multicursorIndex ?? 0,
85+
options.registerMode,
86+
);
7487
}
7588

7689
if (copyToUnnamed && register !== '"') {
7790
Register.registers.set('"', Register.registers.get(register)!);
7891
}
7992
}
8093

94+
private static decapitalize(register: string): string {
95+
return Register.isValidUppercaseRegister(register) ? register.toLowerCase() : register;
96+
}
97+
8198
public static isValidRegister(register: string): boolean {
8299
return (
83100
Register.isValidLowercaseRegister(register) ||
@@ -95,7 +112,7 @@ export class Register {
95112
return registerName === '_';
96113
}
97114

98-
private static isClipboardRegister(registerName: string): boolean {
115+
public static isClipboardRegister(registerName: string): boolean {
99116
return registerName === '*' || registerName === '+';
100117
}
101118

@@ -120,13 +137,14 @@ export class Register {
120137
register: string,
121138
content: RegisterContent,
122139
multicursorIndex: number,
140+
registerMode?: RegisterMode,
123141
): void {
124142
if (multicursorIndex === 0 || !Register.registers.has(register)) {
125143
Register.registers.set(register, []);
126144
}
127145

128146
Register.registers.get(register)![multicursorIndex] = {
129-
registerMode: vimState.currentRegisterMode,
147+
registerMode: registerMode || vimState.currentRegisterMode,
130148
text: content,
131149
};
132150

@@ -149,6 +167,7 @@ export class Register {
149167
register: string,
150168
content: RegisterContent,
151169
multicursorIndex: number,
170+
registerMode?: RegisterMode,
152171
): void {
153172
if (!Register.registers.has(register)) {
154173
Register.registers.set(register, []);
@@ -162,11 +181,13 @@ export class Register {
162181
text: content,
163182
};
164183
} else {
165-
// Line-wise trumps other RegisterModes
166-
const registerMode =
167-
vimState.currentRegisterMode === RegisterMode.LineWise
168-
? RegisterMode.LineWise
169-
: oldContent.registerMode;
184+
if (!registerMode) {
185+
// Line-wise trumps other RegisterModes
186+
registerMode =
187+
vimState.currentRegisterMode === RegisterMode.LineWise
188+
? RegisterMode.LineWise
189+
: oldContent.registerMode;
190+
}
170191
let newText: RegisterContent;
171192
if (oldContent.text instanceof RecordedState || content instanceof RecordedState) {
172193
newText = oldContent.text;
@@ -271,13 +292,7 @@ export class Register {
271292
register: string,
272293
multicursorIndex = 0,
273294
): Promise<IRegisterContent | undefined> {
274-
if (!Register.isValidRegister(register)) {
275-
throw new Error(`Invalid register ${register}`);
276-
}
277-
278-
register = register.toLowerCase();
279-
280-
const contentByCursor = Register.registers.get(register);
295+
const contentByCursor = Register.getRegisterArray(register);
281296

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

320+
public static getRegisterArray(register: string): IRegisterContent[] | undefined {
321+
if (!Register.isValidRegister(register)) {
322+
throw new Error(`Invalid register ${register}`);
323+
}
324+
return Register.registers.get(register.toLowerCase());
325+
}
326+
305327
public static has(register: string): boolean {
306328
return Register.registers.has(register);
307329
}

src/vimscript/expression/build.ts

Lines changed: 24 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,22 @@
11
import {
2-
NumberValue,
3-
Expression,
4-
ListExpression,
5-
UnaryExpression,
6-
BinaryOp,
72
BinaryExpression,
8-
FunctionCallExpression,
9-
StringValue,
10-
LambdaExpression,
11-
VariableExpression,
12-
Namespace,
3+
BinaryOp,
4+
BlobValue,
5+
DictionaryValue,
6+
Expression,
137
FloatValue,
148
FuncRefValue,
9+
FunctionCallExpression,
10+
LambdaExpression,
11+
ListExpression,
1512
ListValue,
16-
DictionaryValue,
13+
Namespace,
14+
NumberValue,
15+
RegisterValue,
16+
StringValue,
17+
UnaryExpression,
1718
Value,
18-
BlobValue,
19+
VariableExpression,
1920
} from './types';
2021

2122
export function int(value: number): NumberValue {
@@ -66,6 +67,17 @@ export function blob(data: ArrayBuffer): BlobValue {
6667
};
6768
}
6869

70+
export function register(
71+
content: string,
72+
registerMode: 'character' | 'line' | 'block',
73+
): RegisterValue {
74+
return {
75+
type: 'register_val',
76+
content,
77+
registerMode,
78+
};
79+
}
80+
6981
export function listExpr(items: Expression[]): ListExpression {
7082
return {
7183
type: 'list',

src/vimscript/expression/displayValue.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,5 +42,7 @@ export function displayValue(value: Value, topLevel = true): string {
4242
.join('')
4343
.toUpperCase()
4444
);
45+
case 'register_val':
46+
return `register('${value.content}', ${value.registerMode})`;
4547
}
4648
}

src/vimscript/expression/evaluate.ts

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
import { all } from 'parsimmon';
2-
import { displayValue } from './displayValue';
32
import { configuration } from '../../configuration/configuration';
43
import { ErrorCode, VimError } from '../../error';
54
import { globalState } from '../../state/globalState';
6-
import { bool, float, funcref, listExpr, int, str, list, funcCall, blob } from './build';
5+
import { blob, bool, float, funcCall, funcref, int, list, listExpr, str } from './build';
6+
import { displayValue } from './displayValue';
77
import { expressionParser, numberParser } from './parser';
8+
import { lookupRegister } from './registerUtils';
89
import {
910
BinaryOp,
1011
ComparisonOp,
@@ -43,6 +44,8 @@ function toInt(value: Value): number {
4344
throw VimError.fromCode(ErrorCode.UsingAFuncrefAsANumber);
4445
case 'blob':
4546
throw VimError.fromCode(ErrorCode.UsingABlobAsANumber);
47+
case 'register_val':
48+
throw VimError.fromCode(ErrorCode.UsingARegisterAsANumber);
4649
}
4750
}
4851

@@ -57,6 +60,7 @@ function toFloat(value: Value): number {
5760
case 'dict_val':
5861
case 'funcref':
5962
case 'blob':
63+
case 'register_val':
6064
throw VimError.fromCode(ErrorCode.NumberOrFloatRequired);
6165
}
6266
}
@@ -77,6 +81,8 @@ export function toString(value: Value): string {
7781
throw VimError.fromCode(ErrorCode.UsingFuncrefAsAString);
7882
case 'blob':
7983
return displayValue(value);
84+
case 'register_val':
85+
return value.content;
8086
}
8187
}
8288

@@ -88,6 +94,7 @@ function toList(value: Value): ListValue {
8894
case 'funcref':
8995
case 'dict_val':
9096
case 'blob':
97+
case 'register_val':
9198
throw VimError.fromCode(ErrorCode.ListRequired);
9299
case 'list':
93100
return value;
@@ -102,6 +109,7 @@ function toDict(value: Value): DictionaryValue {
102109
case 'list':
103110
case 'funcref':
104111
case 'blob':
112+
case 'register_val':
105113
throw VimError.fromCode(ErrorCode.DictionaryRequired);
106114
case 'dict_val':
107115
return value;
@@ -147,6 +155,7 @@ export class EvaluationContext {
147155
case 'dict_val':
148156
case 'funcref':
149157
case 'blob':
158+
case 'register_val':
150159
return expression;
151160
case 'list':
152161
return list(expression.items.map((x) => this.evaluate(x)));
@@ -168,7 +177,7 @@ export class EvaluationContext {
168177
case 'variable':
169178
return this.evaluateVariable(expression);
170179
case 'register':
171-
return str(''); // TODO
180+
return lookupRegister(expression.name);
172181
case 'option':
173182
return str(''); // TODO
174183
case 'env_variable':
@@ -395,6 +404,9 @@ export class EvaluationContext {
395404
const bytes = new Uint8Array(sequence.data);
396405
return int(bytes[toInt(index)]);
397406
}
407+
case 'register_val': {
408+
return this.evaluateIndex(str(toString(sequence)), index);
409+
}
398410
}
399411
}
400412

@@ -438,6 +450,9 @@ export class EvaluationContext {
438450
case 'blob': {
439451
return blob(new Uint8Array(sequence.data).slice(_start, _end + 1));
440452
}
453+
case 'register_val': {
454+
return this.evaluateSlice(str(toString(sequence)), start, end);
455+
}
441456
}
442457
}
443458

@@ -1281,6 +1296,8 @@ export class EvaluationContext {
12811296
// return int(7);
12821297
case 'blob':
12831298
return int(8);
1299+
case 'register_val':
1300+
return int(9);
12841301
default:
12851302
const guard: never = x;
12861303
throw new Error('type() got unexpected type');

0 commit comments

Comments
 (0)