/// <summary> /// Returns true if the given file contains any tokens overlapping with the given fragment. /// The range of the tokens in the file is assumed to be relative to their start line (the index at which they are listed), /// whereas the range of the given fragment is assumed to be the absolute range. /// </summary> /// <exception cref="ArgumentOutOfRangeException"><paramref name="range"/> is not a valid range within <paramref name="file"/>.</exception> internal static bool ContainsTokensOverlappingWith(this FileContentManager file, Range range) { if (!file.ContainsRange(range)) { throw new ArgumentOutOfRangeException(nameof(range)); } var(start, end) = (range.Start.Line, range.End.Line); if (start != end && file.GetTokenizedLines(start + 1, end - start - 1).SelectMany(x => x).Any()) { return(true); } var inRange = file.GetTokenizedLine(start).Where(TokensAfter(Position.Create(0, range.Start.Column))); // checking tokens overlapping with range.Start below inRange = start == end ? inRange.Where(TokensStartingBefore(Position.Create(0, range.End.Column))) : inRange.Concat(file.GetTokenizedLine(end).Where(TokensStartingBefore(Position.Create(0, range.End.Column)))); if (inRange.Any()) { QsCompilerError.Raise($"{range.DiagnosticString()} overlaps for start = {start}, end = {end}, \n\n" + $"{string.Join("\n", file.GetTokenizedLine(start).Select(x => $"{x.Range.DiagnosticString()}"))},\n\n " + $"{string.Join("\n", file.GetTokenizedLine(end).Select(x => $"{x.Range.DiagnosticString()}"))},"); return(true); } var overlapsWithStart = file.TryGetFragmentAt(range.Start, out _); return(overlapsWithStart != null); }
// 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> /// Return a string with the new content of the (entire) lines in the range [start, end] where start and end are /// the start and end line of the given change. /// </summary> /// <exception cref="ArgumentException">The range is invalid.</exception> /// <exception cref="ArgumentOutOfRangeException">The range is not contained in <paramref name="file"/>.</exception> internal static string GetTextChangedLines(FileContentManager file, TextDocumentContentChangeEvent change) { if (!file.ContainsRange(change.Range.ToQSharp())) { throw new ArgumentOutOfRangeException(nameof(change)); // range can be empty } var first = file.GetLine(change.Range.Start.Line).Text; var last = file.GetLine(change.Range.End.Line).Text; var prepend = first.Substring(0, change.Range.Start.Character); var append = last.Substring(change.Range.End.Character); return(string.Concat(prepend, change.Text, append)); }
/// <summary> /// Returns a look-up of workspace edits suggested by the compiler for the given location and context. /// The key of the look-up is a suitable title for the corresponding edits that can be presented to the user. /// Returns null if any of the given arguments is null or if suitable edits cannot be determined. /// </summary> public static ILookup <string, WorkspaceEdit>?CodeActions(this FileContentManager file, CompilationUnit compilation, Range?range, CodeActionContext?context) { if (range?.Start is null || range.End is null || !file.ContainsRange(range)) { return(null); } var diagnostics = context?.Diagnostics ?? Array.Empty <Diagnostic>(); return(file.UnknownIdSuggestions(compilation, range.Start.Line, diagnostics) .Concat(file.AmbiguousIdSuggestions(compilation, diagnostics)) .Concat(file.DeprecatedSyntaxSuggestions(diagnostics)) .Concat(file.UpdateReassignStatementSuggestions(diagnostics)) .Concat(file.IndexRangeSuggestions(compilation, range)) .Concat(file.UnreachableCodeSuggestions(diagnostics)) .Concat(file.DocCommentSuggestions(range)) .ToLookup(s => s.Item1, s => s.Item2)); }