// 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))); } } }