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