-
Notifications
You must be signed in to change notification settings - Fork 234
Fix crash where lines appended to end of script file causes out of bounds exception #730
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
Changes from 1 commit
4eed1bb
08647af
6a17b4a
7bdbc2d
e1cff3d
2bd9a30
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -114,7 +114,7 @@ public Token[] ScriptTokens | |
} | ||
|
||
/// <summary> | ||
/// Gets the array of filepaths dot sourced in this ScriptFile | ||
/// Gets the array of filepaths dot sourced in this ScriptFile | ||
/// </summary> | ||
public string[] ReferencedFiles | ||
{ | ||
|
@@ -227,7 +227,7 @@ public static IList<string> GetLines(string text) | |
} | ||
|
||
/// <summary> | ||
/// Deterines whether the supplied path indicates the file is an "untitled:Unitled-X" | ||
/// Deterines whether the supplied path indicates the file is an "untitled:Unitled-X" | ||
/// which has not been saved to file. | ||
/// </summary> | ||
/// <param name="path">The path to check.</param> | ||
|
@@ -309,14 +309,24 @@ public void ValidatePosition(BufferPosition bufferPosition) | |
/// </summary> | ||
/// <param name="line">The 1-based line to be validated.</param> | ||
/// <param name="column">The 1-based column to be validated.</param> | ||
public void ValidatePosition(int line, int column) | ||
public void ValidatePosition(int line, int column, bool isInsertion = false) | ||
{ | ||
int maxLine = this.FileLines.Count; | ||
int maxLine = isInsertion ? this.FileLines.Count + 1 : this.FileLines.Count; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I would add a code comment above this to make sure someone else doesn't think they see an off-by-one error with the |
||
if (line < 1 || line > maxLine) | ||
{ | ||
throw new ArgumentOutOfRangeException($"Position {line}:{column} is outside of the line range of 1 to {maxLine}."); | ||
} | ||
|
||
// If we are inserting at the end of the file, the column should be 1 | ||
if (isInsertion && line == maxLine) | ||
{ | ||
if (column != 1) | ||
{ | ||
throw new ArgumentOutOfRangeException($"Insertion at the end of a file must occur at column 1"); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 1 indexed columns???!?!?!? omg There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @tylerl0706 yep $sb = [scriptblock]::Create('')
$sb.Ast.Extent.StartColumnNumber
# 1
$sb.Ast.Extent.StartLineNumber
# 1
$sb.Ast.Extent.StartOffset
# 0 |
||
} | ||
return; | ||
} | ||
|
||
// The maximum column is either **one past** the length of the string | ||
// or 1 if the string is empty. | ||
string lineString = this.FileLines[line - 1]; | ||
|
@@ -347,51 +357,63 @@ public void ApplyChange(FileChange fileChange) | |
} | ||
else | ||
{ | ||
this.ValidatePosition(fileChange.Line, fileChange.Offset); | ||
this.ValidatePosition(fileChange.EndLine, fileChange.EndOffset); | ||
|
||
// Get the first fragment of the first line | ||
string firstLineFragment = | ||
this.FileLines[fileChange.Line - 1] | ||
.Substring(0, fileChange.Offset - 1); | ||
|
||
// Get the last fragment of the last line | ||
string endLine = this.FileLines[fileChange.EndLine - 1]; | ||
string lastLineFragment = | ||
endLine.Substring( | ||
fileChange.EndOffset - 1, | ||
(this.FileLines[fileChange.EndLine - 1].Length - fileChange.EndOffset) + 1); | ||
|
||
// Remove the old lines | ||
for (int i = 0; i <= fileChange.EndLine - fileChange.Line; i++) | ||
{ | ||
this.FileLines.RemoveAt(fileChange.Line - 1); | ||
} | ||
this.ValidatePosition(fileChange.Line, fileChange.Offset, isInsertion: true); | ||
this.ValidatePosition(fileChange.EndLine, fileChange.EndOffset, isInsertion: true); | ||
|
||
// Build and insert the new lines | ||
int currentLineNumber = fileChange.Line; | ||
for (int changeIndex = 0; changeIndex < changeLines.Length; changeIndex++) | ||
// If the change is a pure append to the file, we just need to add the new lines on the end | ||
if (fileChange.EndLine == this.FileLines.Count + 1) | ||
{ | ||
// Since we split the lines above using \n, make sure to | ||
// trim the ending \r's off as well. | ||
string finalLine = changeLines[changeIndex].TrimEnd('\r'); | ||
|
||
// Should we add first or last line fragments? | ||
if (changeIndex == 0) | ||
foreach (string addedLine in changeLines) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Would there be a scenario where there's a changed line AND an added line? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If so, the foreach might also need to handle the "between" logic as well. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I guess a better question is... are the changed lines always consecutive |
||
{ | ||
// Append the first line fragment | ||
finalLine = firstLineFragment + finalLine; | ||
string finalLine = addedLine.TrimEnd('\r'); | ||
this.FileLines.Add(finalLine); | ||
} | ||
if (changeIndex == changeLines.Length - 1) | ||
} | ||
// Otherwise, the change needs to go between existing content | ||
else | ||
{ | ||
// Get the first fragment of the first line | ||
string firstLineFragment = | ||
this.FileLines[fileChange.Line - 1] | ||
.Substring(0, fileChange.Offset - 1); | ||
|
||
// Get the last fragment of the last line | ||
string endLine = this.FileLines[fileChange.EndLine - 1]; | ||
string lastLineFragment = | ||
endLine.Substring( | ||
fileChange.EndOffset - 1, | ||
(this.FileLines[fileChange.EndLine - 1].Length - fileChange.EndOffset) + 1); | ||
|
||
// Remove the old lines | ||
for (int i = 0; i <= fileChange.EndLine - fileChange.Line; i++) | ||
{ | ||
// Append the last line fragment | ||
finalLine = finalLine + lastLineFragment; | ||
this.FileLines.RemoveAt(fileChange.Line - 1); | ||
} | ||
|
||
this.FileLines.Insert(currentLineNumber - 1, finalLine); | ||
currentLineNumber++; | ||
} | ||
// Build and insert the new lines | ||
int currentLineNumber = fileChange.Line; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. maybe consider doing the -1 here instead of line 431. I think it's a little clearer. |
||
for (int changeIndex = 0; changeIndex < changeLines.Length; changeIndex++) | ||
{ | ||
// Since we split the lines above using \n, make sure to | ||
// trim the ending \r's off as well. | ||
string finalLine = changeLines[changeIndex].TrimEnd('\r'); | ||
|
||
// Should we add first or last line fragments? | ||
if (changeIndex == 0) | ||
{ | ||
// Append the first line fragment | ||
finalLine = firstLineFragment + finalLine; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. do you think you could add a bit more comment? Like... "append the first line fragment to the front of the change" the code says that but the logic here is a tad complex |
||
} | ||
if (changeIndex == changeLines.Length - 1) | ||
{ | ||
// Append the last line fragment | ||
finalLine = finalLine + lastLineFragment; | ||
} | ||
|
||
this.FileLines.Insert(currentLineNumber - 1, finalLine); | ||
currentLineNumber++; | ||
} | ||
} | ||
} | ||
|
||
// Parse the script again to be up-to-date | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Adding an optional parameter is a breaking change, can you add an overload instead?