/// <summary> /// Returns workspace edits for opening namespaces of unknown identifiers. /// </summary> private static IEnumerable <(string, WorkspaceEdit)> UnknownIdNamespaceSuggestions( this FileContentManager file, CompilationUnit compilation, int lineNr, IReadOnlyCollection <Diagnostic> diagnostics) { var idSuggestions = diagnostics .Where(DiagnosticTools.ErrorType(ErrorCode.UnknownIdentifier)) .SelectMany(d => file.IdNamespaceSuggestions(d.Range.Start.ToQSharp(), compilation, out _)); var typeSuggestions = diagnostics .Where(DiagnosticTools.ErrorType(ErrorCode.UnknownType)) .SelectMany(d => file.TypeNamespaceSuggestions(d.Range.Start.ToQSharp(), compilation, out _)); return(file .OpenDirectiveSuggestions(lineNr, idSuggestions.Concat(typeSuggestions)) .Select(edit => (edit.NewText.Trim().Trim(';'), file.GetWorkspaceEdit(edit)))); }
/// <summary> /// Returns a sequence of suggestions on how errors for unknown types and callable in the given diagnostics can be fixed, /// given the file for which those diagnostics were generated and the corresponding compilation. /// The given line number is used to determine the containing namespace. /// Returns an empty enumerable if any of the given arguments is null. /// </summary> internal static IEnumerable <(string, WorkspaceEdit)> SuggestionsForUnknownIdentifiers (this FileContentManager file, CompilationUnit compilation, int lineNr, IEnumerable <Diagnostic> diagnostics) { if (file == null || diagnostics == null) { return(Enumerable.Empty <(string, WorkspaceEdit)>()); } var unknownCallables = diagnostics.Where(DiagnosticTools.ErrorType(ErrorCode.UnknownIdentifier)); var unknownTypes = diagnostics.Where(DiagnosticTools.ErrorType(ErrorCode.UnknownType)); if (!unknownCallables.Any() && !unknownTypes.Any()) { return(Enumerable.Empty <(string, WorkspaceEdit)>()); } var suggestionsForIds = unknownCallables.Select(d => d.Range.Start) .SelectMany(pos => file.NamespaceSuggestionsForIdAtPosition(pos, compilation, out var _)); var suggestionsForTypes = unknownTypes.Select(d => d.Range.Start) .SelectMany(pos => file.NamespaceSuggestionsForTypeAtPosition(pos, compilation, out var _)); return(file.OpenDirectiveSuggestions(lineNr, suggestionsForIds.Concat(suggestionsForTypes).ToArray()) .Select(edit => (edit.NewText.Trim().Trim(';'), file.GetWorkspaceEdit(edit)))); }
/// <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())) }