Skip to content

Commit 2e12332

Browse files
authored
Fix GenerateRender to not throw 'IndexOutOfRangeException' and 'NullReferenceException' (#1049)
1 parent 809c287 commit 2e12332

File tree

2 files changed

+36
-6
lines changed

2 files changed

+36
-6
lines changed

PSReadLine/Render.cs

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -226,26 +226,32 @@ void MaybeEmphasize(int i, string currColor)
226226
var token = state.Tokens[state.Index];
227227
while (i == token.Extent.EndOffset)
228228
{
229-
if (token == state.Tokens[state.Tokens.Length - 1])
229+
if (state.Index == state.Tokens.Length - 1)
230230
{
231231
tokenStack.Pop();
232232
if (tokenStack.Count == 0)
233233
{
234234
afterLastToken = true;
235235
token = null;
236236
color = defaultColor;
237+
break;
237238
}
238239
else
239240
{
240241
state = tokenStack.Peek();
242+
243+
// It's possible that a 'StringExpandableToken' is the last available token, for example:
244+
// 'begin $a\abc def', 'process $a\abc | blah' and 'end $a\abc; hello'
245+
// due to the special handling of the keywords 'begin', 'process' and 'end', all the above 3 script inputs
246+
// generate only 2 tokens by the parser -- A KeywordToken, and a StringExpandableToken '$a\abc'. Text after
247+
// '$a\abc' is not tokenized at all.
248+
// We repeat the test to see if we fall into this case ('token' is the final one in the stack).
249+
continue;
241250
}
242251
}
243252

244-
if (!afterLastToken)
245-
{
246-
color = state.Color;
247-
token = state.Tokens[++state.Index];
248-
}
253+
color = state.Color;
254+
token = state.Tokens[++state.Index];
249255
}
250256

251257
if (!afterLastToken && i == token.Extent.StartOffset)

test/RenderTest.cs

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,30 @@ public void Render()
9494
InputAcceptedNow
9595
));
9696

97+
// test that rendering doesn't cause an exception in a potential missing "EOS" token case.
98+
// this case could be a moving target, if the PowerShell parser is changed such as to eliminate the case.
99+
Test("", Keys(
100+
"process $abc\\name | def",
101+
CheckThat(() =>
102+
AssertScreenIs(1,
103+
TokenClassification.Keyword, "process",
104+
TokenClassification.None, " ",
105+
TokenClassification.Variable, "$abc",
106+
TokenClassification.None, "\\name | def")),
107+
_.Ctrl_c,
108+
InputAcceptedNow
109+
));
110+
111+
Test("", Keys(
112+
"process out put",
113+
CheckThat(() =>
114+
AssertScreenIs(1,
115+
TokenClassification.Keyword, "process",
116+
TokenClassification.None, " out put")),
117+
_.Ctrl_c,
118+
InputAcceptedNow
119+
));
120+
97121
Test("", Keys(
98122
"\"$([int];\"_$(1+2)\")\"",
99123
CheckThat(() =>

0 commit comments

Comments
 (0)