// routines called by the file content manager upon updating a file /// <summary> /// Given the start line of a change, and how many lines have been updated from there, /// computes the position where the syntax check will start and end. /// Throws an ArgumentNullException if file is null. /// Throws an ArgumentOutOfRangeException if the range [start, start + count) is not a valid range within the current file content. /// </summary> internal static Range GetSyntaxCheckDelimiters(this FileContentManager file, int start, int count) { if (file == null) { throw new ArgumentNullException(nameof(file)); } if (start < 0 || start >= file.NrLines()) { throw new ArgumentOutOfRangeException(nameof(start)); } if (count < 0 || start + count > file.NrLines()) { throw new ArgumentOutOfRangeException(nameof(count)); } // if no piece of code exists before the start of the modifications, then the check effectively starts at the beginning of the file var syntaxCheckStart = file.PositionAfterPrevious(new Position(start, 0)); // position (0,0) if there is no previous fragment // if the modification goes past what is currently the last piece of code, then the effectively the check extends to the end of the file var firstAfterModified = new Position(start + count, 0); var lastInFile = LastInFile(file); var syntaxCheckEnd = firstAfterModified.IsSmallerThan(lastInFile) ? file.FragmentEnd(ref firstAfterModified) : file.End(); return(new Range { Start = syntaxCheckStart, End = lastInFile.IsSmallerThanOrEqualTo(syntaxCheckEnd) ? file.End() : syntaxCheckEnd }); }
// external routines for context verification /// <summary> /// Given the line number of the lines that contain tokens that (possibly) have been modified, /// checks which callable declaration they can potentially belong to and returns the fully qualified name of those callables. /// Throws an ArgumentNullException if the given file or the collection of changed lines is null. /// </summary> internal static IEnumerable <(Range, QsQualifiedName)> CallablesWithContentModifications(this FileContentManager file, IEnumerable <int> changedLines) { if (file == null) { throw new ArgumentNullException(nameof(file)); } if (changedLines == null) { throw new ArgumentNullException(nameof(changedLines)); } var lastInFile = file.LastToken()?.GetFragment()?.GetRange()?.End ?? file.End(); var callables = file.GetCallableDeclarations().Select(tuple => // these are sorted according to their line number { var ns = file.TryGetNamespaceAt(tuple.Item2.Start); QsCompilerError.Verify(ns != null, "namespace for callable declaration should not be null"); // invalid namespace names default to an unknown namespace name, but remain included in the compilation return(tuple.Item2.Start, new QsQualifiedName(NonNullable <string> .New(ns), tuple.Item1)); }).ToList(); // NOTE: The range of modifications that has to trigger an update of the syntax tree for a callable // does need to go up to and include modifications to the line containing the next callable! // Otherwise inserting a callable declaration in the middle of an existing callable does not trigger the right behavior! (Range, QsQualifiedName) TypeCheckingRange((Position, QsQualifiedName) lastPreceding, IEnumerable <(Position, QsQualifiedName)> next) { var callableStart = lastPreceding.Item1; var callableEnd = next.Any() ? next.First().Item1 : lastInFile; return(new Range { Start = callableStart, End = callableEnd }, lastPreceding.Item2); } foreach (var lineNr in changedLines) { bool precedes((Position, QsQualifiedName) tuple) => tuple.Item1.Line < lineNr; var preceding = callables.TakeWhile(precedes); var following = callables.SkipWhile(precedes); if (preceding.Any()) { yield return(TypeCheckingRange(preceding.Last(), following)); } if (following.Any() && following.First().Item1.Line == lineNr) { yield return(TypeCheckingRange(following.First(), following.Skip(1))); } } }