// private utils related to extracting file content /// <summary> /// Checks that the given range is a valid range in file, and returns the text in the given range in concatenated form /// stripping (only) end of line comments (and not removing excess brackets). /// Note: the End position of the given range is *not* part of the returned string. /// </summary> private static string GetCodeSnippet(this FileContentManager file, Range range) { if (!file.ContainsRange(range)) { throw new ArgumentException($"cannot extract code snippet for the given range \n range: {range.DiagnosticString()}"); } string CodeLine(CodeLine line) => line.WithoutEnding + line.LineEnding; var start = range.Start.Line; var count = range.End.Line - start + 1; var firstLine = CodeLine(file.GetLine(start)); if (count == 1) { return(firstLine.Substring(range.Start.Column, range.End.Column - range.Start.Column)); } var lastLine = CodeLine(file.GetLine(range.End.Line)); var prepend = firstLine.Substring(range.Start.Column); var append = lastLine.Substring(0, range.End.Column); var middle = file.GetLines(start + 1, count - 2).Select(CodeLine).ToArray(); if (middle.Length == 0) { return(Utils.JoinLines(new string[] { prepend, append })); } else { return(Utils.JoinLines(new string[] { prepend, Utils.JoinLines(middle), append })); // Note: use JoinLines here to get accurate position infos for errors } }
/// <summary> /// Computes excess bracket errors for the given range of lines in file based on the corresponding CodeLine. /// Throws an ArgumentOutOfRangeException if the range [start, start + count) is not within file. /// </summary> private static IEnumerable <Diagnostic> ComputeScopeDiagnostics(this FileContentManager file, int start, int count) { foreach (var line in file.GetLines(start, count)) { foreach (var pos in line.ExcessBracketPositions) { yield return(Errors.ExcessBracketError(file.FileName, Position.Create(start, pos))); } ++start; } }
/// <summary> /// Computes excess bracket errors for the given range of lines in file based on the corresponding CodeLine. /// Throws an ArgumentNullException if file is null. /// Throws an ArgumentOutOfRangeException if the range [start, start + count) is not within file. /// </summary> private static IEnumerable <Diagnostic> ComputeScopeDiagnostics(this FileContentManager file, int start, int count) { if (file == null) { throw new ArgumentNullException(nameof(file)); } foreach (var line in file.GetLines(start, count)) { foreach (var pos in line.ExcessBracketPositions) { yield return(Errors.ExcessBracketError(file.FileName.Value, new Position(start, pos))); } ++start; } }
/// <summary> /// Computes excess bracket errors for the given range of lines in file based on the corresponding CodeLine. /// </summary> /// <exception cref="ArgumentOutOfRangeException">The range [<paramref name="start"/>, <paramref name="start"/> + <paramref name="count"/>) is not within <paramref name="file"/>.</exception> private static IEnumerable <Diagnostic> ComputeScopeDiagnostics(this FileContentManager file, int start, int count) { foreach (var line in file.GetLines(start, count)) { foreach (var pos in line.ExcessBracketPositions) { yield return(Errors.ExcessBracketError(file.FileName, Position.Create(start, pos))); } foreach (var pos in line.ErrorDelimiterPositions) { yield return(Errors.InvalidCharacterInInterpolatedArgument(file.FileName, Position.Create(start, pos), file.GetLine(start).Text[pos])); } ++start; } }
/// <summary> /// Computes the excess closing and scope error updates for the given replacements at the position specified by start and count in the given file. /// Returns a sequence of CodeLines for the remaining file, if the made replacements require updating the remaining file as well, and null otherwise. /// Throws an ArgumentNullException if file or replacements is null. /// Throws an ArgumentException if replacements does not at least contain one CodeLine. /// Throws an ArgumentOutOfRangeException if the range defined by start and count is not within the given file, where count needs to be at least one. /// </summary> private static IEnumerable <CodeLine> ComputeUpdates(FileContentManager file, int start, int count, CodeLine[] replacements) { if (file == null) { throw new ArgumentNullException(nameof(file)); } if (replacements == null) { throw new ArgumentNullException(nameof(replacements)); } if (start < 0 || start >= file.NrLines()) { throw new ArgumentOutOfRangeException(nameof(start)); } if (count < 1 || start + count > file.NrLines()) { throw new ArgumentOutOfRangeException(nameof(count)); } if (replacements.Length == 0) { throw new ArgumentException("replacements cannot be empty"); } var continueAtInFile = start + count; var remainingLines = file.GetLines(continueAtInFile, file.NrLines() - continueAtInFile); // how much the effective indentation (i.e. absolute indentation plus nr of excess closings up to that point) changed determines how much an what we need to update: var indentationChange = GetIndentationChange(file, continueAtInFile, replacements.Last()); var requiresStringDelimiterUpdate = ContinueString(file.GetLine(continueAtInFile - 1)) ^ ContinueString(replacements.Last()); if (requiresStringDelimiterUpdate) // we need to recompute everything if the interpretation of what is code and what is a string changes... // since the interpretation of the remaining file changed, we need to update the entire file from start onwards { return(ComputeCodeLines(remainingLines.Select(line => line.Text), replacements.Last()).ToList()); } else if (indentationChange != 0) // if the replacements has more effective closing brackets (not just excess closings!) than the current part that will be replaced has, // then we need check the text of the remaining file as well in order to compute the correct update // if it has less (indentationChange > 0), then we could in principle simplify things somewhat by simply discarding the corresponding number of excess closing brackets { return(remainingLines.GetUpdatedLines(remainingLines.First().Indentation + indentationChange)); } else { return(null); } }