Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -3,26 +3,19 @@ namespace rec JetBrains.ReSharper.Plugins.FSharp.Psi.Features.CodeCompletion
open FSharp.Compiler.CodeAnalysis
open FSharp.Compiler.EditorServices
open FSharp.Compiler.Symbols
open JetBrains.Diagnostics
open JetBrains.ReSharper.Feature.Services.CodeCompletion.Infrastructure.LookupItems
open JetBrains.ReSharper.Feature.Services.CodeCompletion.Infrastructure.LookupItems.Impl
open JetBrains.ReSharper.Feature.Services.Lookup
open JetBrains.ReSharper.Feature.Services.ParameterInfo
open JetBrains.RdBackend.Common.Features.Completion
open JetBrains.ReSharper.Plugins.FSharp.Psi
open JetBrains.ReSharper.Plugins.FSharp.Psi.Features
open JetBrains.ReSharper.Plugins.FSharp.Psi.Features.CodeCompletion
open JetBrains.ReSharper.Plugins.FSharp.Psi.Features.Util
open JetBrains.ReSharper.Plugins.FSharp.Psi.Features.Util.FcsTypeUtil
open JetBrains.ReSharper.Plugins.FSharp.Psi.Impl
open JetBrains.ReSharper.Plugins.FSharp.Psi.Tree
open JetBrains.ReSharper.Plugins.FSharp.Psi.Util
open JetBrains.ReSharper.Plugins.FSharp.Util
open JetBrains.ReSharper.Plugins.FSharp.Util.FcsTaggedText
open JetBrains.ReSharper.Psi
open JetBrains.ReSharper.Psi.Modules
open JetBrains.ReSharper.Psi.Transactions
open JetBrains.ReSharper.Resources.Shell
open JetBrains.UI.RichText
open JetBrains.Util

Expand Down Expand Up @@ -77,7 +70,6 @@ type FcsErrorLookupItem(item: DeclarationListItem) =
type IFcsLookupItemInfo =
abstract FcsSymbol: FSharpSymbol
abstract FcsSymbolUse: FSharpSymbolUse
abstract NamespaceToOpen: string array

type FcsLookupItem(items: RiderDeclarationListItems, context: FSharpCodeCompletionContext) =
inherit TextLookupItemBase()
Expand All @@ -95,7 +87,6 @@ type FcsLookupItem(items: RiderDeclarationListItems, context: FSharpCodeCompleti

interface IFcsLookupItemInfo with
member this.FcsSymbol = this.FcsSymbol
member this.NamespaceToOpen = this.NamespaceToOpen
member this.FcsSymbolUse = this.FcsSymbolUse

member x.Candidates =
Expand All @@ -121,72 +112,11 @@ type FcsLookupItem(items: RiderDeclarationListItems, context: FSharpCodeCompleti
| _ -> null
with _ -> null

override this.Accept(textControl, nameRange, insertType, suffix, solution, keepCaretStill) =
use pinCheckResultsCookie =
Assertion.Assert(context.ParseAndCheckResults.IsValueCreated)
textControl.GetFSharpFile(solution).PinTypeCheckResults(context.ParseAndCheckResults.Value)

base.Accept(textControl, nameRange, insertType, suffix, solution, keepCaretStill)

override x.OnAfterComplete(textControl, nameRange, decorationRange, tailType, suffix, caretPositionRangeMarker) =
base.OnAfterComplete(textControl, &nameRange, &decorationRange, tailType, &suffix, &caretPositionRangeMarker)

// todo: 213: exit early if there's no need in additional binding
context.BasicContext.Solution.GetPsiServices().Files.CommitAllDocuments()

let fsFile = context.BasicContext.SourceFile.FSharpFile
let psiServices = fsFile.GetPsiServices()

use writeCookie = WriteLockCookie.Create(fsFile.IsPhysical())
use transactionCookie = PsiTransactionCookie.CreateAutoCommitCookieWithCachesUpdate(psiServices, "Add open")

let declaredElement = x.FcsSymbol.GetDeclaredElement(context.PsiModule)

let typeElement =
// todo: other declared elements
match declaredElement with
| :? ITypeElement as typeElement -> typeElement
| :? IField as field when (field.ContainingType :? IEnum) -> field.ContainingType
| _ -> null

let ns = items.NamespaceToOpen
let moduleToImport =
if isNotNull typeElement then
let moduleToOpen = getModuleToOpen typeElement
ModuleToImport.DeclaredElement(moduleToOpen) else

let ns = ns |> Array.map FSharpNamingService.normalizeBackticks |> String.concat "."
ModuleToImport.FullName(ns)

let offset = context.Ranges.InsertRange.StartOffset
// todo: getting reference owner in parse errors, e.g. unfinished `if`

let referenceOwner = fsFile.GetNode<IFSharpReferenceOwner>(offset)
if isNotNull referenceOwner && isNotNull typeElement && not referenceOwner.Reference.IsQualified then
let clrDeclaredElement: IClrDeclaredElement =
// todo: other elements: union cases
match declaredElement with
| :? ITypeElement as typeElement -> typeElement :> _
| :? IField as field when (field.ContainingType :? IEnum) -> field :> _
| _ -> null

if isNotNull clrDeclaredElement then
referenceOwner.Reference.SetRequiredQualifiers(clrDeclaredElement, referenceOwner)

if not (ns.IsEmpty()) then
addOpen offset fsFile moduleToImport

override x.GetDisplayName() =
let name = LookupUtil.FormatLookupString(items.Name, x.TextColor)

if emphasize then
LookupUtil.AddEmphasize(name, TextRange(0, name.Length))

let ns = items.NamespaceToOpen
if not (ns.IsEmpty()) then
let ns = String.concat "." ns
LookupUtil.AddInformationText(name, $"(in {ns})")

name

interface IParameterInfoCandidatesProvider with
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,11 +52,7 @@ type FSharpRelevanceRule() =
match info.FcsSymbol with
| :? FSharpEntity ->
markRelevance item CLRLookupItemRelevance.TypesAndNamespaces

if not (Array.isEmpty info.NamespaceToOpen) then
markRelevance item CLRLookupItemRelevance.NotImportedType
else
markRelevance item CLRLookupItemRelevance.ImportedType
markRelevance item CLRLookupItemRelevance.ImportedType

| :? FSharpMemberOrFunctionOrValue as mfv ->
if not mfv.IsModuleValueOrMember then
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ open JetBrains.ReSharper.Plugins.FSharp.Psi.Services.Util.FSharpCompletionUtil
open JetBrains.ReSharper.Plugins.FSharp.Psi.Tree
open JetBrains.ReSharper.Psi
open JetBrains.UI.RichText
open JetBrains.Util.Extension

[<Language(typeof<FSharpLanguage>)>]
type ImportExtensionMemberRule() =
Expand All @@ -43,7 +42,7 @@ type ImportExtensionMemberRule() =
override this.AddLookupItems(context, collector) =
let refExpr = getRefExpr context
let members =
FSharpExtensionMemberUtil.getNonImportedExtensionMembers None refExpr
FSharpExtensionMemberUtil.getNonImportedExtensionMembers context.NodeInFile None refExpr
|> FSharpExtensionMemberUtil.groupByNameAndNs

let iconManager = context.BasicContext.Solution.GetComponent<PsiIconManager>()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,6 @@ type FcsSymbolInfo(text, symbolUse: FSharpSymbolUse) =
interface IFcsLookupItemInfo with
member this.FcsSymbol = if isNotNull symbolUse then symbolUse.Symbol else Unchecked.defaultof<_>
member this.FcsSymbolUse = symbolUse
member this.NamespaceToOpen = [||]

[<Language(typeof<FSharpLanguage>)>]
type LocalValuesRule() =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,7 @@ type FSharpImportExtensionMemberFix(reference: IReference) =
if isNull refExpr then [] else

let name = reference.GetName()
FSharpExtensionMemberUtil.getNonImportedExtensionMembers (Some name) refExpr
FSharpExtensionMemberUtil.getNonImportedExtensionMembers refExpr (Some name) refExpr
|> FSharpExtensionMemberUtil.groupByNameAndNs
|> Seq.map (snd >> Seq.tryHead)
|> Seq.choose id
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,13 @@ open JetBrains.ReSharper.Plugins.FSharp.Psi
open JetBrains.ReSharper.Plugins.FSharp.Psi.Features.Util
open JetBrains.ReSharper.Plugins.FSharp.Psi.Impl
open JetBrains.ReSharper.Plugins.FSharp.Psi.Impl.Cache2.Compiled
open JetBrains.ReSharper.Plugins.FSharp.Psi.Resolve
open JetBrains.ReSharper.Plugins.FSharp.Psi.Tree
open JetBrains.ReSharper.Plugins.FSharp.Psi.Util
open JetBrains.ReSharper.Psi
open JetBrains.ReSharper.Psi.ExtensionsAPI.Caches2.ExtensionMethods.Queries
open JetBrains.ReSharper.Psi.Modules
open JetBrains.ReSharper.Psi.Resolve.TypeInference
open JetBrains.ReSharper.Psi.Tree
open JetBrains.ReSharper.Psi.Util
open JetBrains.Util
open JetBrains.Util.Extension
Expand Down Expand Up @@ -109,7 +109,7 @@ let (|FSharpExtensionMember|_|) (typeMember: ITypeMember) =
| FSharpCompiledExtensionMember _ -> ValueSome()
| _ -> ValueNone

let getExtensionMembersForType (context: IFSharpTreeNode) (fcsType: FSharpType) isStaticContext (nameOpt: string option) =
let getExtensionMembersForType (context: ITreeNode) (fcsType: FSharpType) isStaticContext (nameOpt: string option) =
if isNull fcsType then EmptyList.InstanceList else

let psiModule = context.GetPsiModule()
Expand All @@ -134,7 +134,6 @@ let getExtensionMembersForType (context: IFSharpTreeNode) (fcsType: FSharpType)
typeElements.AsReadOnly()

let openedModulesProvider = OpenedModulesProvider(context)
let accessContext = FSharpAccessContext(context)

let matchesType (typeMember: ITypeMember) : bool =
match typeMember with
Expand All @@ -158,26 +157,22 @@ let getExtensionMembersForType (context: IFSharpTreeNode) (fcsType: FSharpType)
let parameters = method.Parameters
if parameters.Count = 0 then false else

let consumer = RecursiveConsumer(method.TypeParameters.ToIReadOnlyList())
let typeInferenceMatcher = CLRTypeInferenceMatcher.Instance
typeInferenceMatcher.Match(TypeInferenceKind.LowerBound, exprType, parameters[0].Type, consumer)
let thisParameter = parameters[0]
let parameterType = thisParameter.Type
let parameterKind = thisParameter.Kind
let typeParameters = method.TypeParameters.ToIReadOnlyList()
let conversionRule = ClrPredefinedTypeConversionRule.INSTANCE
if typeParameters.Count > 0 then
let substitution = TypeInferenceUtil.InferTypes(FSharpLanguage.Instance, psiModule, exprType, parameterType, typeParameters, conversionRule)
isNotNull substitution &&

let substitutedType = substitution.Apply(parameterType)
conversionRule.HasExtensionMethodThisArgumentConversion(exprType, substitutedType, parameterKind, false)
else
conversionRule.HasExtensionMethodThisArgumentConversion(exprType, parameterType, parameterKind, false)

| _ -> false

let isAccessible (typeMember: ITypeMember) =
let isTypeAccessible =
let containingType = typeMember.ContainingType
let accessRightsOwner = containingType :?> IAccessRightsOwner
match accessRightsOwner.GetAccessRights() with
| AccessRights.PUBLIC -> true
| _ -> containingType.Module.AreInternalsVisibleTo(psiModule)

isTypeAccessible &&

match typeMember with
| FSharpExtensionMember _ -> true
| _ -> AccessUtil.IsSymbolAccessible(typeMember, accessContext)

let matchesName (typeMember: ITypeMember) =
match nameOpt with
| None -> true
Expand Down Expand Up @@ -211,27 +206,35 @@ let getExtensionMembersForType (context: IFSharpTreeNode) (fcsType: FSharpType)

| _ -> not isStaticContext

let isInScope (typeMember: ITypeMember) : bool =
match typeMember with
| FSharpExtensionMember -> FSharpImportUtil.areMembersInScope openedModulesProvider typeMember.ContainingType
| _ -> FSharpImportUtil.isExtensionMemberInScope openedModulesProvider typeMember

let isApplicable (typeMember: ITypeMember) =
resolvesAsExtensionMember typeMember &&
matchesName typeMember &&
not (FSharpImportUtil.isTypeMemberInScope openedModulesProvider typeMember) &&
isAccessible typeMember &&
not (isInScope typeMember) &&
FSharpAccessRightUtil.IsAccessible(typeMember.ContainingType, context) &&
matchesCallingConvention typeMember &&
matchesType typeMember

let query = ExtensionMembersQuery(solution.GetPsiServices(), FSharpRequest(psiModule, exprType, nameOpt))
let methods = query.EnumerateMembers() |> List.ofSeq
let query = ExtensionMembersQuery(solution.GetPsiServices(), FSharpRequest(psiModule, exprType, nameOpt)) //.MaybeForReceiverType(exprType)

let result = List()
for typeMember in query.EnumerateMembers() do
Interruption.Current.CheckAndThrow()
if isApplicable typeMember then
result.Add(typeMember)

methods
|> Seq.filter isApplicable
|> List :> _
result

let getNonImportedExtensionMembers (nameOpt: string option) (refExpr: IReferenceExpr) : IList<ITypeMember> =
let getNonImportedExtensionMembers (context: ITreeNode) (nameOpt: string option) (refExpr: IReferenceExpr) : IList<ITypeMember> =
if isNull refExpr then EmptyList.InstanceList else

let isStaticContext = FSharpExpressionUtil.isStaticContext refExpr.Qualifier
let fcsType = getQualifierFcsType refExpr
getExtensionMembersForType refExpr fcsType isStaticContext nameOpt
getExtensionMembersForType context fcsType isStaticContext nameOpt

let groupByNameAndNs members =
members |> Seq.groupBy (fun (typeMember: ITypeMember) ->
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,12 @@
module JetBrains.ReSharper.Plugins.FSharp.Psi.Services.Util.FSharpImportUtil

open JetBrains.ReSharper.Plugins.FSharp.Psi
open JetBrains.ReSharper.Plugins.FSharp.Psi.Impl
open JetBrains.ReSharper.Plugins.FSharp.Psi.Util
open JetBrains.ReSharper.Psi

let private isTypeOrNsInScope (openedModulesProvider: OpenedModulesProvider) (declaredElement: IClrDeclaredElement) =
let name = getQualifiedName declaredElement
let name = getClrName declaredElement
let scopes = openedModulesProvider.OpenedModuleScopes.GetValuesSafe(name)
OpenScope.inAnyScope openedModulesProvider.Context scopes

Expand All @@ -15,22 +16,27 @@ let areMembersInScope (openedModulesProvider: OpenedModulesProvider) (typeElemen
| :? IFSharpModule as fsModule ->
isTypeOrNsInScope openedModulesProvider fsModule

| containingType ->
let ns = containingType.GetContainingNamespace()
| typeElement ->
let ns = typeElement.GetContainingNamespace()
isTypeOrNsInScope openedModulesProvider ns

let isTypeMemberInScope (openedModulesProvider: OpenedModulesProvider) (typeMember: ITypeMember) =
areMembersInScope openedModulesProvider typeMember.ContainingType
let isExtensionMemberInScope (openedModulesProvider: OpenedModulesProvider) (typeMember: ITypeMember) =
let containingTypeOrNs: IClrDeclaredElement =
let typeElement = typeMember.ContainingType
match typeElement.GetContainingType() with
| null -> typeElement.GetNamespace()
| containingType -> containingType

let isTypeElementInScope (openedModulesProvider: OpenedModulesProvider) (typeElement: ITypeElement) =
isTypeOrNsInScope openedModulesProvider typeElement ||
isTypeOrNsInScope openedModulesProvider containingTypeOrNs

let isTypeElementInScope (openedModulesProvider: OpenedModulesProvider) (typeElement: ITypeElement) =
match typeElement.GetContainingType() with
| :? IFSharpModule as fsModule ->
isTypeOrNsInScope openedModulesProvider fsModule

| null ->
let ns = typeElement.GetContainingNamespace()
isTypeOrNsInScope openedModulesProvider ns

| _ -> false
| :? IFSharpModule as fsModule ->
isTypeOrNsInScope openedModulesProvider fsModule

| typeElement ->
areMembersInScope openedModulesProvider typeElement
Original file line number Diff line number Diff line change
Expand Up @@ -400,6 +400,12 @@ type OpenedModulesProvider(context: ITreeNode) =
let scope = OpenScope.Range(moduleDecl.GetTreeTextRange())
importQualifiedName scope moduleDecl.ClrName

let fsModule = moduleDecl.DeclaredElement.As<IFSharpModule>()
if isNotNull fsModule && fsModule.IsAutoOpen then
let parent = moduleDecl.Parent
let scope = OpenScope.Range(TreeTextRange(moduleDecl.GetTreeEndOffset(), parent.GetTreeEndOffset()))
importQualifiedName scope moduleDecl.ClrName

let namedModuleDecl = moduleDecl.As<INamedModuleDeclaration>()
if isNotNull namedModuleDecl then
importQualifiedName scope namedModuleDecl.NamespaceName
Expand Down
Loading
Loading