1
1
// eslint-disable-next-line id-denylist
2
- import { alt , optWhitespace , Parser , seq , string , whitespace } from 'parsimmon' ;
2
+ import { alt , optWhitespace , Parser , sepBy , seq , string , whitespace } from 'parsimmon' ;
3
3
import { env } from 'process' ;
4
4
import { VimState } from '../../state/vimState' ;
5
5
import { StatusBar } from '../../statusBar' ;
@@ -20,17 +20,24 @@ import {
20
20
optionParser ,
21
21
registerParser ,
22
22
variableParser ,
23
+ varNameParser ,
23
24
} from '../../vimscript/expression/parser' ;
24
25
import {
25
26
EnvVariableExpression ,
26
27
Expression ,
27
28
OptionExpression ,
28
29
RegisterExpression ,
30
+ Value ,
29
31
VariableExpression ,
30
32
} from '../../vimscript/expression/types' ;
31
33
import { displayValue } from '../../vimscript/expression/displayValue' ;
32
34
import { ErrorCode , VimError } from '../../error' ;
33
35
36
+ type Unpack = {
37
+ type : 'unpack' ;
38
+ names : string [ ] ;
39
+ } ;
40
+
34
41
export type LetCommandOperation = '=' | '+=' | '-=' | '*=' | '/=' | '%=' | '.=' | '..=' ;
35
42
export type LetCommandVariable =
36
43
| VariableExpression
@@ -40,7 +47,7 @@ export type LetCommandVariable =
40
47
export type LetCommandArgs =
41
48
| {
42
49
operation : LetCommandOperation ;
43
- variable : LetCommandVariable ;
50
+ variable : LetCommandVariable | Unpack ;
44
51
expression : Expression ;
45
52
lock : boolean ;
46
53
}
@@ -67,8 +74,14 @@ const letVarParser = alt<LetCommandVariable>(
67
74
registerParser ,
68
75
) ;
69
76
77
+ const unpackParser : Parser < Unpack > = sepBy ( varNameParser , string ( ',' ) . trim ( optWhitespace ) )
78
+ . wrap ( string ( '[' ) . then ( optWhitespace ) , optWhitespace . then ( string ( ']' ) ) )
79
+ . map ( ( names ) => ( {
80
+ type : 'unpack' ,
81
+ names,
82
+ } ) ) ;
83
+
70
84
export class LetCommand extends ExCommand {
71
- // TODO: Support unpacking
72
85
// TODO: Support indexing
73
86
// TODO: Support slicing
74
87
public static readonly argParser = ( lock : boolean ) =>
@@ -78,7 +91,11 @@ export class LetCommand extends ExCommand {
78
91
// `:let {var} -= {expr}`
79
92
// `:let {var} .= {expr}`
80
93
whitespace . then (
81
- seq ( letVarParser , operationParser . wrap ( optWhitespace , optWhitespace ) , expressionParser ) . map (
94
+ seq (
95
+ alt < LetCommandVariable | Unpack > ( letVarParser , unpackParser ) ,
96
+ operationParser . trim ( optWhitespace ) ,
97
+ expressionParser ,
98
+ ) . map (
82
99
( [ variable , operation , expression ] ) =>
83
100
new LetCommand ( {
84
101
operation,
@@ -107,7 +124,7 @@ export class LetCommand extends ExCommand {
107
124
if ( this . args . variables . length === 0 ) {
108
125
// TODO
109
126
} else {
110
- const variable = this . args . variables [ this . args . variables . length - 1 ] ;
127
+ const variable = this . args . variables . at ( - 1 ) ! ;
111
128
const value = context . evaluate ( variable ) ;
112
129
const prefix = value . type === 'number' ? '#' : value . type === 'funcref' ? '*' : '' ;
113
130
StatusBar . setText ( vimState , `${ variable . name } ${ prefix } ${ displayValue ( value ) } ` ) ;
@@ -118,7 +135,7 @@ export class LetCommand extends ExCommand {
118
135
if ( this . args . lock ) {
119
136
if ( this . args . operation !== '=' ) {
120
137
throw VimError . fromCode ( ErrorCode . CannotModifyExistingVariable ) ;
121
- } else if ( this . args . variable . type !== 'variable' ) {
138
+ } else if ( variable . type !== 'variable' ) {
122
139
// TODO: this error message should vary by type
123
140
throw VimError . fromCode ( ErrorCode . CannotLockARegister ) ;
124
141
}
@@ -148,6 +165,25 @@ export class LetCommand extends ExCommand {
148
165
// TODO
149
166
} else if ( variable . type === 'env_variable' ) {
150
167
value = str ( env [ variable . name ] ?? '' ) ;
168
+ } else if ( variable . type === 'unpack' ) {
169
+ // TODO: Support :let [a, b; rest] = ["aval", "bval", 3, 4]
170
+ if ( value . type !== 'list' ) {
171
+ throw VimError . fromCode ( ErrorCode . ListRequired ) ;
172
+ }
173
+ if ( variable . names . length < value . items . length ) {
174
+ throw VimError . fromCode ( ErrorCode . LessTargetsThanListItems ) ;
175
+ }
176
+ if ( variable . names . length > value . items . length ) {
177
+ throw VimError . fromCode ( ErrorCode . MoreTargetsThanListItems ) ;
178
+ }
179
+ for ( let i = 0 ; i < variable . names . length ; i ++ ) {
180
+ await new LetCommand ( {
181
+ operation : this . args . operation ,
182
+ variable : { type : 'variable' , namespace : undefined , name : variable . names [ i ] } ,
183
+ expression : value . items [ i ] ,
184
+ lock : this . args . lock ,
185
+ } ) . execute ( vimState ) ;
186
+ }
151
187
}
152
188
}
153
189
}
0 commit comments