/// <summary> /// Note that the only thing that may be set to null is the fragment kind - all other properties need to be set upon initialization /// </summary> private CodeFragment(int indent, Range r, string text, char next, QsComments comments, QsFragmentKind kind, bool include) { if (!Utils.IsValidRange(r)) { throw new ArgumentException("invalid range for code fragment"); } if (!DelimitingChars.Contains(next) && next != MissingDelimiter) { throw new ArgumentException("a CodeFragment needs to be followed by a DelimitingChar"); } this.Indentation = indent < 0 ? throw new ArgumentException("indentation needs to be positive") : indent; this.Text = text?.TrimEnd() ?? throw new ArgumentNullException(nameof(text)); this.FollowedBy = next; this.Comments = comments ?? QsComments.Empty; this.Kind = kind; // nothing here should be modifiable this.FragmentRange = r.Copy(); this.HeaderRange = GetHeaderRange(this.Text, this.Kind); this.IncludeInCompilation = include; }
internal CodeFragment SetKind(QsFragmentKind kind) => new CodeFragment(this.Indentation, this.Range, this.Text, this.FollowedBy, this.Comments, kind, this.IncludeInCompilation);
/// <summary> /// Returns the completion environment at the given position in the file or null if the environment cannot be /// determined. Stores the code fragment found at or before the given position into an out parameter. /// </summary> /// <exception cref="ArgumentNullException">Thrown when any argument is null.</exception> /// <exception cref="ArgumentException">Thrown when the position is invalid.</exception> private static (CompletionScope, QsFragmentKind) GetCompletionEnvironment( FileContentManager file, Position position, out CodeFragment fragment) { if (file == null) { throw new ArgumentNullException(nameof(file)); } if (!Utils.IsValidPosition(position)) { throw new ArgumentException(nameof(position)); } if (!Utils.IsValidPosition(position, file)) { // FileContentManager.IndentationAt will fail if the position is not within the file. fragment = null; return(null, null); } var token = GetTokenAtOrBefore(file, position); if (token == null) { fragment = null; return(null, null); } fragment = token.GetFragment(); var relativeIndentation = fragment.Indentation - file.IndentationAt(position); QsCompilerError.Verify(Math.Abs(relativeIndentation) <= 1); var parents = new[] { token }.Concat(token.GetNonEmptyParents()) .Skip(relativeIndentation + 1) .Select(t => t.GetFragment()); CompletionScope scope = null; if (!parents.Any()) { scope = CompletionScope.TopLevel; } else if (parents.Any() && parents.First().Kind.IsNamespaceDeclaration) { scope = CompletionScope.NamespaceTopLevel; } else if (parents.Where(parent => parent.Kind.IsFunctionDeclaration).Any()) { scope = CompletionScope.Function; } else if (parents.Any() && parents.First().Kind.IsOperationDeclaration) { scope = CompletionScope.OperationTopLevel; } else if (parents.Where(parent => parent.Kind.IsOperationDeclaration).Any()) { scope = CompletionScope.Operation; } QsFragmentKind previous = null; if (relativeIndentation == 0 && IsPositionAfterDelimiter(file, fragment, position)) { previous = fragment.Kind; } else if (relativeIndentation == 0) { previous = token.PreviousOnScope()?.GetFragment().Kind; } else if (relativeIndentation == 1) { previous = token.GetNonEmptyParent()?.GetFragment().Kind; } return(scope, previous); }
internal CodeFragment(int indent, Range r, string text, char next, QsFragmentKind kind = null) : this(indent, r, text, next, null, kind, true) { }
private static Tuple <QsPositionInfo, QsPositionInfo> GetHeaderRange(string text, QsFragmentKind kind) => kind == null ? QsCompilerDiagnostic.DefaultRange : kind.IsControlledAdjointDeclaration ? Parsing.HeaderDelimiters(2).Invoke(text ?? "") : Parsing.HeaderDelimiters(1).Invoke(text ?? "");