/// <summary>
 /// Returns an IEnumerable with the indices of the closest preceding non-empty tokens with increasingly lower indentation level.
 /// </summary>
 internal static IEnumerable <CodeFragment.TokenIndex> GetNonEmptyParents(this CodeFragment.TokenIndex tIndex)
 {
     for (var current = tIndex.GetNonEmptyParent(); current != null; current = current.GetNonEmptyParent())
     {
         yield return(current);
     }
 }
        /// <summary>
        /// Returns the CodeFragment at the given position if such a fragment exists and null otherwise.
        /// If the given position is equal to the end of the fragment, that fragment is returned if includeEnd is set to true.
        /// If a fragment is determined for the given position, returns the corresponding token index as out parameter.
        /// Note that token indices are no longer valid as soon as the file is modified (possibly e.g. by queued background processing).
        /// Any query or attempt operation on the returned token index may result in an exception once it lost its validity.
        /// Returns null if the given file or the specified position is null,
        /// or if the specified position is not within the current Content range.
        /// </summary>
        public static CodeFragment TryGetFragmentAt(this FileContentManager file, Position pos,
                                                    out CodeFragment.TokenIndex tIndex, bool includeEnd = false)
        {
            tIndex = null;
            if (file == null || pos == null || !Utils.IsValidPosition(pos, file))
            {
                return(null);
            }
            var start    = pos.Line;
            var previous = file.GetTokenizedLine(start).Where(token => token.GetRange().Start.Character <= pos.Character).ToImmutableArray();

            while (!previous.Any() && --start >= 0)
            {
                previous = file.GetTokenizedLine(start);
            }
            if (!previous.Any())
            {
                return(null);
            }

            var lastPreceding = previous.Last().WithUpdatedLineNumber(start);
            var overlaps      = includeEnd
                ? pos.IsSmallerThanOrEqualTo(lastPreceding.GetRange().End)
                : pos.IsSmallerThan(lastPreceding.GetRange().End);

            tIndex = overlaps ? new CodeFragment.TokenIndex(file, start, previous.Length - 1) : null;
            return(overlaps ? lastPreceding : null);
        }
Exemple #3
0
        // routines related to "reconstructing" the syntax tree from the saved tokens to do context checks

        /// <summary>
        /// Returns the context object for the given token index, ignoring empty fragments.
        /// Throws an ArgumentNullException if the given token index is null.
        /// </summary>
        private static Context.SyntaxTokenContext GetContext(this CodeFragment.TokenIndex tokenIndex)
        {
            if (tokenIndex == null)
            {
                throw new ArgumentNullException(nameof(tokenIndex));
            }
            QsNullable <QsFragmentKind> Nullable(CodeFragment fragment) =>
            fragment?.Kind == null
                ? QsNullable <QsFragmentKind> .Null
                : fragment.IncludeInCompilation
                ? QsNullable <QsFragmentKind> .NewValue(fragment.Kind)
                : QsNullable <QsFragmentKind> .NewValue(QsFragmentKind.InvalidFragment);

            var self         = tokenIndex.GetFragment();
            var previous     = tokenIndex.PreviousOnScope()?.GetFragment(); // excludes empty tokens
            var next         = tokenIndex.NextOnScope()?.GetFragment();     // excludes empty tokens
            var parents      = tokenIndex.GetNonEmptyParents().Select(tIndex => Nullable(tIndex.GetFragment())).ToArray();
            var nullableSelf = self?.Kind == null                           // special treatment such that errors for fragments excluded from compilation still get logged...
                ? QsNullable <QsFragmentKind> .Null
                : QsNullable <QsFragmentKind> .NewValue(self.Kind);

            var headerRange = self?.HeaderRange ?? QsCompilerDiagnostic.DefaultRange;

            return(new Context.SyntaxTokenContext(headerRange, nullableSelf, Nullable(previous), Nullable(next), parents));
        }
 /// <summary>
 /// Returns an IEnumerable with the indices of the closest preceding non-empty tokens with increasingly lower indentation level.
 /// Throws an ArgumentNullException if tIndex is null.
 /// </summary>
 private static IEnumerable <CodeFragment.TokenIndex> GetNonEmptyParents(this CodeFragment.TokenIndex tIndex)
 {
     if (tIndex == null)
     {
         throw new ArgumentNullException(nameof(tIndex));
     }
     for (var current = tIndex.GetNonEmptyParent(); current != null; current = current.GetNonEmptyParent())
     {
         yield return(current);
     }
 }
        /// <summary>
        /// Returnes an IEnumerable with the indices of all children of the given token.
        /// If deep is set to true (default value), then the returned children are all following tokens
        /// with a higher indentation level than the token corresponding to tIndex
        /// up to the point where we are at the same indentation level again.
        /// If deep is set to false, then of those only the tokens with an indentation level that is precisely
        /// one larger than the one of the parent token are returned.
        /// </summary>
        internal static IEnumerable <CodeFragment.TokenIndex> GetChildren(this CodeFragment.TokenIndex tIndex, bool deep = true)
        {
            var current     = tIndex;
            var indentation = current.GetFragment().Indentation;

            while ((current = current.Next()) != null && current.GetFragment().Indentation > indentation)
            {
                if (deep || current.GetFragment().Indentation == indentation + 1)
                {
                    yield return(current);
                }
            }
        }
        /// <summary>
        /// Returns the index of the next non-empty token on the same indenation level, or null if no such token exists.
        /// Includes empty tokens if includeEmpty is set to true.
        /// </summary>
        internal static CodeFragment.TokenIndex?NextOnScope(this CodeFragment.TokenIndex tIndex, bool includeEmpty = false)
        {
            var current     = tIndex;
            var indentation = current.GetFragment().Indentation;

            while ((current = current.Next()) != null)
            {
                var fragment = current.GetFragment();
                if (fragment.Indentation <= indentation && (fragment.Kind != null || includeEmpty))
                {
                    break;
                }
            }
            return(current != null && current.GetFragment().Indentation == indentation ? current : null);
        }
        /// <summary>
        /// Returns the index of the closest preceding non-empty token with the next lower indentation level.
        /// Returns null if no such token exists.
        /// </summary>
        internal static CodeFragment.TokenIndex?GetNonEmptyParent(this CodeFragment.TokenIndex tIndex)
        {
            var current     = tIndex;
            var indentation = current.GetFragment().Indentation;

            while ((current = current.Previous()) != null)
            {
                var fragment = current.GetFragment();
                if (fragment.Kind != null && fragment.Indentation < indentation)
                {
                    break; // ignore empty fragments
                }
            }
            return(current != null && current.GetFragment().Indentation == indentation - 1 ? current : null);
        }
        /// <summary>
        /// Returnes an IEnumerable with the indices of all children of the given token.
        /// If deep is set to true (default value), then the returned children are all following tokens
        /// with a higher indentation level than the token corresponding to tIndex
        /// up to the point where we are at the same indentation level again.
        /// If deep is set to false, then of those only the tokens with an indentation level that is precisely
        /// one larger than the one of the parent token are returned.
        /// Throws an ArgumentNullException if tIndex is null.
        /// </summary>
        internal static IEnumerable <CodeFragment.TokenIndex> GetChildren(this CodeFragment.TokenIndex tIndex, bool deep = true)
        {
            if (tIndex == null)
            {
                throw new ArgumentNullException(nameof(tIndex));
            }
            var tokenIndex  = new CodeFragment.TokenIndex(tIndex);
            var indentation = tokenIndex.GetFragment().Indentation;

            while (++tokenIndex != null && tokenIndex.GetFragment().Indentation > indentation)
            {
                if (deep || tokenIndex.GetFragment().Indentation == indentation + 1)
                {
                    yield return(tokenIndex);
                }
            }
        }
        // routines related to "reconstructing" the syntax tree from the saved tokens to do context checks

        /// <summary>
        /// Returns the context object for the given token index, ignoring empty fragments.
        /// </summary>
        private static Context.SyntaxTokenContext GetContext(this CodeFragment.TokenIndex tokenIndex)
        {
            QsNullable <QsFragmentKind> Nullable(CodeFragment?token, bool precedesSelf) =>
            token?.Kind == null
                ? QsNullable <QsFragmentKind> .Null
                : precedesSelf && !token.IncludeInCompilation // fragments that *follow * self need to be re-evaluated first
                    ? QsNullable <QsFragmentKind> .NewValue(QsFragmentKind.InvalidFragment)
                    : QsNullable <QsFragmentKind> .NewValue(token.Kind);

            var fragment    = tokenIndex.GetFragment();
            var headerRange = fragment?.HeaderRange ?? Range.Zero;

            var self     = Nullable(fragment, false);                                   // making sure that errors for fragments excluded from compilation still get logged
            var previous = Nullable(tokenIndex.PreviousOnScope()?.GetFragment(), true); // excludes empty tokens
            var next     = Nullable(tokenIndex.NextOnScope()?.GetFragment(), false);    // excludes empty tokens
            var parents  = tokenIndex.GetNonEmptyParents().Select(tIndex => Nullable(tIndex.GetFragment(), true)).ToArray();

            return(new Context.SyntaxTokenContext(headerRange, self, previous, next, parents));
        }
        /// <summary>
        /// Returns the index of the next non-empty token on the same indenation level, or null if no such token exists.
        /// Includes empty tokens if includeEmpty is set to true.
        /// Throws an ArgumentNullException if tIndex is null.
        /// </summary>
        internal static CodeFragment.TokenIndex NextOnScope(this CodeFragment.TokenIndex tIndex, bool includeEmpty = false)
        {
            if (tIndex == null)
            {
                throw new ArgumentNullException(nameof(tIndex));
            }

            var tokenIndex  = new CodeFragment.TokenIndex(tIndex);
            var indentation = tokenIndex.GetFragment().Indentation;

            while (++tokenIndex != null)
            {
                var fragment = tokenIndex.GetFragment();
                if (fragment.Indentation <= indentation && (fragment.Kind != null || includeEmpty))
                {
                    break;
                }
            }
            return(tokenIndex != null && tokenIndex.GetFragment().Indentation == indentation ? tokenIndex : null);
        }
        /// <summary>
        /// Returns the index of the closest preceding non-empty token with the next lower indentation level.
        /// Returns null if no such token exists.
        /// Throws an ArgumentNullException if tIndex is null.
        /// </summary>
        private static CodeFragment.TokenIndex GetNonEmptyParent(this CodeFragment.TokenIndex tIndex)
        {
            if (tIndex == null)
            {
                throw new ArgumentNullException(nameof(tIndex));
            }

            var tokenIndex  = new CodeFragment.TokenIndex(tIndex);
            var indentation = tokenIndex.GetFragment().Indentation;

            while (--tokenIndex != null)
            {
                var fragment = tokenIndex.GetFragment();
                if (fragment.Kind != null && fragment.Indentation < indentation)
                {
                    break;                                                              // ignore empty fragments
                }
            }
            return(tokenIndex != null && tokenIndex.GetFragment().Indentation == indentation - 1 ? tokenIndex : null);
        }