From 483ee3e1de9c4396e1111be40e502504572c0580 Mon Sep 17 00:00:00 2001 From: Will Smith Date: Thu, 1 Apr 2021 16:07:06 -0700 Subject: [PATCH] Better handling of external symbols. Formatting module or namespace entities. --- src/fsharp/NicePrint.fs | 104 +++++++++++++++++- .../Common/CodeAnalysisExtensions.fs | 5 + .../Navigation/GoToDefinition.fs | 4 +- 3 files changed, 105 insertions(+), 8 deletions(-) diff --git a/src/fsharp/NicePrint.fs b/src/fsharp/NicePrint.fs index b8f49aa4553..a6b56b4ee2b 100755 --- a/src/fsharp/NicePrint.fs +++ b/src/fsharp/NicePrint.fs @@ -1783,12 +1783,104 @@ module private TastDefinitionPrinting = let xs = List.map (layoutTycon denv infoReader ad m false (wordL (tagKeyword "and"))) t aboveListL (x :: xs) - let layoutEntity (denv: DisplayEnv) (infoReader: InfoReader) ad m (entity: Entity) = - if entity.IsModule then - // TODO: Implementation of layoutTycon isn't correct for module. - layoutTycon denv infoReader ad m false (wordL (tagKeyword "module")) entity - elif entity.IsNamespace then - emptyL + let rec layoutModuleOrNamespace (denv: DisplayEnv) (infoReader: InfoReader) ad m isFirstTopLevel (mspec: ModuleOrNamespace) = + let rec fullPath (mspec: ModuleOrNamespace) acc = + if mspec.IsNamespace then + match mspec.ModuleOrNamespaceType.ModuleAndNamespaceDefinitions |> List.tryHead with + | Some next when next.IsNamespace -> + fullPath next (acc @ [next.DemangledModuleOrNamespaceName]) + | _ -> + acc, mspec + else + acc, mspec + + let outerPath = mspec.CompilationPath.AccessPath + + let path, mspec = fullPath mspec [mspec.DemangledModuleOrNamespaceName] + + let denv = denv.AddOpenPath path + + let headerL = + if mspec.IsNamespace then + // This is a container namespace. We print the header when we get to the first concrete module. + wordL (tagKeyword "namespace") ^^ sepListL SepL.dot (List.map (tagNamespace >> wordL) path) + else + // This is a module + let nmL = + match path with + | [nm] -> wordL (tagModule nm) + | _ -> + let nm = path |> List.last + let innerPath = path.[..path.Length - 2] + sepListL SepL.dot (List.map (tagNamespace >> wordL) innerPath) ^^ SepL.dot ^^ wordL (tagModule nm) + // Check if its an outer module or a nested module + if (outerPath |> List.forall (fun (_, istype) -> istype = Namespace)) then + // Check if this is an outer module with no namespace + if isNil outerPath then + // If so print a "module" declaration + (wordL (tagKeyword "module") ^^ nmL) + else + if mspec.ModuleOrNamespaceType.AllEntities |> Seq.isEmpty && mspec.ModuleOrNamespaceType.AllValsAndMembers |> Seq.isEmpty then + (wordL (tagKeyword "module") ^^ nmL ^^ WordL.equals ^^ wordL (tagKeyword "begin") ^^ wordL (tagKeyword "end")) + else + // Otherwise this is an outer module contained immediately in a namespace + // We already printed the namespace declaration earlier. So just print the + // module now. + (wordL (tagKeyword "module") ^^ nmL ^^ WordL.equals) + else + if mspec.ModuleOrNamespaceType.AllEntities |> Seq.isEmpty && mspec.ModuleOrNamespaceType.AllValsAndMembers |> Seq.isEmpty then + (wordL (tagKeyword "module") ^^ nmL ^^ WordL.equals ^^ wordL (tagKeyword "begin") ^^ wordL (tagKeyword "end")) + else + // OK, this is a nested module + (wordL (tagKeyword "module") ^^ nmL ^^ WordL.equals) + + let headerL = + PrintTypes.layoutAttribs denv false (generalizedTyconRef(mkLocalEntityRef mspec)) mspec.TypeOrMeasureKind mspec.Attribs headerL + + let shouldShow (v: Val) = + (denv.showObsoleteMembers || not (CheckFSharpAttributesForObsolete denv.g v.Attribs)) && + (denv.showHiddenMembers || not (CheckFSharpAttributesForHidden denv.g v.Attribs)) + + let entityLs = + if mspec.IsNamespace then [] + else + mspec.ModuleOrNamespaceType.AllEntities + |> QueueList.toList + |> List.map (fun entity -> layoutEntity denv infoReader ad m entity) + + let valLs = + if mspec.IsNamespace then [] + else + mspec.ModuleOrNamespaceType.AllValsAndMembers + |> QueueList.toList + |> List.filter shouldShow + |> List.sortBy (fun v -> v.DisplayName) + |> List.map (PrintTastMemberOrVals.prettyLayoutOfValOrMemberNoInst denv) + + if List.isEmpty entityLs && List.isEmpty valLs then + headerL + else + let entitiesL = + entityLs + |> aboveListL + + let valsL = + valLs + |> aboveListL + + if isFirstTopLevel then + aboveListL + [ + headerL + entitiesL + valsL + ] + else + headerL @@- (aboveL entitiesL valsL) + + and layoutEntity (denv: DisplayEnv) (infoReader: InfoReader) ad m (entity: Entity) = + if entity.IsModuleOrNamespace then + layoutModuleOrNamespace denv infoReader ad m false entity elif entity.IsExceptionDecl then layoutExnDefn denv entity else diff --git a/vsintegration/src/FSharp.Editor/Common/CodeAnalysisExtensions.fs b/vsintegration/src/FSharp.Editor/Common/CodeAnalysisExtensions.fs index 4a1212d8658..8f40f743507 100644 --- a/vsintegration/src/FSharp.Editor/Common/CodeAnalysisExtensions.fs +++ b/vsintegration/src/FSharp.Editor/Common/CodeAnalysisExtensions.fs @@ -30,6 +30,11 @@ type Project with type Solution with + /// Checks if the file path is associated with a document in the solution. + member self.ContainsDocumentWithFilePath filePath = + self.GetDocumentIdsWithFilePath(filePath).IsEmpty + |> not + /// Try to get a document inside the solution using the document's name member self.TryGetDocumentNamed docName = self.Projects |> Seq.tryPick (fun proj -> diff --git a/vsintegration/src/FSharp.Editor/Navigation/GoToDefinition.fs b/vsintegration/src/FSharp.Editor/Navigation/GoToDefinition.fs index 5be4f843ef8..fad9dfa00c7 100644 --- a/vsintegration/src/FSharp.Editor/Navigation/GoToDefinition.fs +++ b/vsintegration/src/FSharp.Editor/Navigation/GoToDefinition.fs @@ -276,8 +276,8 @@ type internal GoToDefinition(checker: FSharpChecker, projectInfoManager: FSharpP return (FSharpGoToDefinitionResult.ExternalAssembly(targetSymbolUse, metadataReferences), idRange) | FindDeclResult.DeclFound targetRange -> - // If the file does not actually exist, it's external considered external. - if not (File.Exists targetRange.FileName) then + // If the file is not associated with a document, it's considered external. + if not (originDocument.Project.Solution.ContainsDocumentWithFilePath(targetRange.FileName)) then let metadataReferences = originDocument.Project.MetadataReferences return (FSharpGoToDefinitionResult.ExternalAssembly(targetSymbolUse, metadataReferences), idRange) else