Пример #1
0
        /// <summary>
        /// Returns a sequence of suggestions for replacing ranges over array indices with the corresponding library call,
        /// provided the corresponding library is referenced.
        /// Returns an empty enumerable if this is not the case or any of the given arguments is null.
        /// </summary>
        internal static IEnumerable <(string, WorkspaceEdit)> SuggestionsForIndexRange
            (this FileContentManager file, CompilationUnit compilation, LSP.Range range)
        {
            if (file == null || compilation == null || range?.Start == null)
            {
                return(Enumerable.Empty <(string, WorkspaceEdit)>());
            }
            var indexRangeNamespaces = compilation.GlobalSymbols.NamespacesContainingCallable(BuiltIn.IndexRange.Name);

            if (!indexRangeNamespaces.Contains(BuiltIn.IndexRange.Namespace))
            {
                return(Enumerable.Empty <(string, WorkspaceEdit)>());
            }
            var suggestedOpenDir = file.OpenDirectiveSuggestions(range.Start.Line, BuiltIn.IndexRange.Namespace);

            /// Returns true the given expression is of the form "0 .. Length(args) - 1",
            /// as well as the range of the entire expression and the argument tuple "(args)" as out parameters.
            bool IsIndexRange(QsExpression iterExpr, Position offset, out LSP.Range exprRange, out LSP.Range argRange)
            {
                if (iterExpr.Expression is QsExpressionKind <QsExpression, QsSymbol, QsType> .RangeLiteral rangeExpression && iterExpr.Range.IsValue &&                               // iterable expression is a valid range literal
                    rangeExpression.Item1.Expression is QsExpressionKind <QsExpression, QsSymbol, QsType> .IntLiteral intLiteralExpression && intLiteralExpression.Item == 0L &&      // .. starting at 0 ..
                    rangeExpression.Item2.Expression is QsExpressionKind <QsExpression, QsSymbol, QsType> .SUB SUBExpression &&                                                       // .. and ending in subracting ..
                    SUBExpression.Item2.Expression is QsExpressionKind <QsExpression, QsSymbol, QsType> .IntLiteral subIntLiteralExpression && subIntLiteralExpression.Item == 1L &&  // .. 1 from ..
                    SUBExpression.Item1.Expression is QsExpressionKind <QsExpression, QsSymbol, QsType> .CallLikeExpression callLikeExression &&                                      // .. a call ..
                    callLikeExression.Item1.Expression is QsExpressionKind <QsExpression, QsSymbol, QsType> .Identifier identifier &&                                                 // .. to and identifier ..
                    identifier.Item1.Symbol is QsSymbolKind <QsSymbol> .Symbol symName && symName.Item.Value == BuiltIn.Length.Name.Value &&                                          // .. "Length" called with ..
                    callLikeExression.Item2.Expression is QsExpressionKind <QsExpression, QsSymbol, QsType> .ValueTuple valueTuple && callLikeExression.Item2.Range.IsValue)          // .. a valid argument tuple
                {
                    exprRange = DiagnosticTools.GetAbsoluteRange(offset, iterExpr.Range.Item);
                    argRange  = DiagnosticTools.GetAbsoluteRange(offset, callLikeExression.Item2.Range.Item);
                    return(true);
                }

                (exprRange, argRange) = (null, null);
                return(false);
            }

            /// Returns the text edits for replacing an range over the indices with the corresponding library call if the given code fragment is a suitable for-loop intro.
            /// The returned edits do *not* include an edit for adding the corresponding open-directive if necessary.
            IEnumerable <TextEdit> IndexRangeEdits(CodeFragment fragment)
            {
                if (fragment.Kind is QsFragmentKind.ForLoopIntro forLoopIntro && // todo: in principle we could give these suggestions for any index range
                    IsIndexRange(forLoopIntro.Item2, fragment.GetRange().Start, out var iterExprRange, out var argTupleRange))
                {
                    yield return(new TextEdit()
                    {
                        Range = new LSP.Range()
                        {
                            Start = iterExprRange.Start, End = argTupleRange.Start
                        },
                        NewText = BuiltIn.IndexRange.Name.Value
                    });

                    yield return(new TextEdit()
                    {
                        Range = new LSP.Range()
                        {
                            Start = argTupleRange.End, End = iterExprRange.End
                        },
                        NewText = ""
                    });
                }
            }

            var fragments = file.FragmentsOverlappingWithRange(range);
            var edits     = fragments.SelectMany(IndexRangeEdits);

            return(edits.Any()
                ? new[] { ("Use IndexRange to iterate over indices.", file.GetWorkspaceEdit(suggestedOpenDir.Concat(edits).ToArray())) }