Esempio n. 1
0
        // 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)));
                }
            }
        }