Skip to content
Closed
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
9 changes: 9 additions & 0 deletions docs/content/style.css
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,15 @@ table.pre td.lines {
width:30px;
}

div.tip .signature {
white-space: pre;
font-family: Monaco, Menlo, Consolas, 'Courier New', Courier, monospace;
}

div.tip .full-usage {
font-family: Monaco, Menlo, Consolas, 'Courier New', Courier, monospace;
}

/*--------------------------------------------------------------------------
Formatting for page & standard document content
/*--------------------------------------------------------------------------*/
Expand Down
49 changes: 17 additions & 32 deletions src/FSharp.Formatting.ApiDocs/GenerateHtml.fs
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ let renderEntities (entities: Choice<ApiDocType, ApiDocModule> list) =
]
]
tbody [] [
for e in entities do
for e in entities do
tr [] [
td [Class "type-name"] [
let nm = match e with Choice1Of2 t -> t.Name | Choice2Of2 m -> m.Name
Expand Down Expand Up @@ -76,34 +76,19 @@ let renderMembers sigWidth header tableHeader (members: ApiDocMember list) =
for m in members do
tr [] [
td [Class "member-name"] [

renderWithToolTip [
// This adds #MemberName anchor. These may currently be ambiguous
a [Name m.Name] [!! HttpUtility.HtmlEncode(m.FormatUsage(sigWidth)) ]
]
[
strong [] [!! "Full Usage: "]
!! HttpUtility.HtmlEncode(m.UsageTooltip)
br []
if not m.ParameterTooltips.IsEmpty then
strong [] [!! "Parameters: "]
ul [] [
for (pname, ptyp) in m.ParameterTooltips do
li [] [
b [] [!! pname]
!! ":"
!! HttpUtility.HtmlEncode(ptyp)
]
]
p [Class "full-usage"] [
!! HttpUtility.HtmlEncode(m.UsageTooltip)
]
br []
match m.ReturnTooltip with
| None -> ()
| Some t ->
strong [] [!! "Returns: "]
!! HttpUtility.HtmlEncode(t)
br []
strong [] [!! "Signature: "]
!! HttpUtility.HtmlEncode(m.SignatureTooltip)
!!(sprintf "<p class='signature'>%s</p>"(HttpUtility.HtmlEncode(m.SignatureTooltip))) //Hack - workaround the fact our view engine implementation adds whitespaces to create human readable HTML. In this case we don't want additional whitespaces as it breaks formatting we've created for signature
br []
if not m.Modifiers.IsEmpty then
strong [] [!! "Modifiers: "]
Expand All @@ -114,7 +99,7 @@ let renderMembers sigWidth header tableHeader (members: ApiDocMember list) =
!!m.FormatTypeArguments
]
]

td [Class "xmldoc"] [
!!m.Comment.FullText
if m.IsObsolete then
Expand Down Expand Up @@ -146,7 +131,7 @@ let moduleContent sigWidth (info: ApiDocModuleInfo) =
// ...and can be used to categorize members in large modules or types
// (but if this is not used, then all members end up in just one category)
let byCategory =
members
members
|> List.groupBy(fun m -> m.Category)
|> List.sortBy (fun (key, _) -> if String.IsNullOrEmpty(key) then "ZZZ" else key)
|> List.mapi (fun n (key, elems) ->
Expand All @@ -172,12 +157,12 @@ let moduleContent sigWidth (info: ApiDocModuleInfo) =
// we print only those that belong to the <default>
for sec in comment.Sections do
if not (byCategory |> List.exists (fun (_, g, _, _) -> g = sec.Key)) then
if (sec.Key <> "<default>") then
if (sec.Key <> "<default>") then
h2 [] [!!sec.Key]
!! sec.Value
!! sec.Value
]
if (byCategory.Length > 1) then
// If there is more than 1 category in the type, generate TOC
// If there is more than 1 category in the type, generate TOC
h2 [] [!!"Table of contents"]
ul [] [
for (index, _, _, name) in byCategory do
Expand Down Expand Up @@ -228,7 +213,7 @@ let typeContent sigWidth (info: ApiDocTypeInfo) =
members
|> List.groupBy(fun m -> m.Category)
|> List.sortBy(fun (g, ms) -> if String.IsNullOrEmpty(g) then "ZZZ" else g)
|> List.mapi (fun n (g, ms) ->
|> List.mapi (fun n (g, ms) ->
let ms = ms |> List.sortBy(fun m -> if (m.Kind = ApiDocMemberKind.StaticParameter) then "" else m.Name)
let name = (if String.IsNullOrEmpty(g) then "Other type members" else g)
(n, g, ms, name))
Expand All @@ -251,12 +236,12 @@ let typeContent sigWidth (info: ApiDocTypeInfo) =
// we print only those that belong to the <default>
for sec in comment.Sections do
if not (byCategory |> List.exists (fun (_, g, _, _) -> g = sec.Key)) then
if (sec.Key <> "<default>") then
if (sec.Key <> "<default>") then
h2 [] [!!sec.Key]
!! sec.Value
!! sec.Value
]
if (byCategory.Length > 1) then
// If there is more than 1 category in the type, generate TOC
// If there is more than 1 category in the type, generate TOC
h2 [] [!!"Table of contents"]
ul [] [
for (index, _, _, name) in byCategory do
Expand All @@ -282,7 +267,7 @@ let typeContent sigWidth (info: ApiDocTypeInfo) =
div [] (renderMembers sigWidth "Instance members" "Instance member" (ms |> List.filter (fun m -> m.Kind = ApiDocMemberKind.InstanceMember)))
div [] (renderMembers sigWidth "Static members" "Static member" (ms |> List.filter (fun m -> m.Kind = ApiDocMemberKind.StaticMember)))
]

let namespacesContent (asm: ApiDocAssemblyGroup) =
[ h1 [] [!! asm.Name]
for (nsIndex, ns) in Seq.indexed asm.Namespaces do
Expand Down Expand Up @@ -324,7 +309,7 @@ let namespacesContent (asm: ApiDocAssemblyGroup) =
for (name, index, entities) in allByCategory do
li [] [a [Href ("#section" + index)] [!!name]]
]

for (name, index, entities) in allByCategory do
if (allByCategory.Length > 1) then
h3 [] [a [Class "anchor"; Name ("section" + index); Href ("#section" + index)] [!! name]]
Expand Down
109 changes: 89 additions & 20 deletions src/FSharp.Formatting.ApiDocs/GenerateModel.fs
Original file line number Diff line number Diff line change
Expand Up @@ -562,7 +562,7 @@ module internal ValueReader =
formatTypeWithPrec 5 typ

let isUnitType (ty: FSharpType) =
ty.HasTypeDefinition && ty.TypeDefinition.XmlDocSig = "T:Microsoft.FSharp.Core.unit"
ty.HasTypeDefinition && ty.TypeDefinition.XmlDocSig = "T:Microsoft.FSharp.Core.unit"

let formatArgNameAndType i (arg:FSharpParameter) =
let nm =
Expand All @@ -582,22 +582,91 @@ module internal ValueReader =
argType
argName, argType

// Format each argument, including its name and type
let formatArgUsage generateTypes i (arg:FSharpParameter) =
let internal (++) (a:string) (b:string) =
match String.IsNullOrEmpty a, String.IsNullOrEmpty b with
| true, true -> ""
| false, true -> a
| true, false -> b
| false, false -> a + " " + b

let formatParameter (p:FSharpParameter) =
try formatType p.Type
with :? InvalidOperationException -> p.DisplayName


//Formats argument list for "signature" information with padding
let formatSignature (v:FSharpMemberOrFunctionOrValue) (args: list<list<FSharpParameter>>) =

let ident = 3
let maxPadding = 20
let indent = String.replicate ident " "

let maxUnderThreshold nmax =
List.maxBy(fun n -> if n > nmax then 0 else n)

let padLength =
let allLengths =
args
|> List.concat
|> List.map (fun p -> let name = Option.defaultValue p.DisplayName p.Name
let normalisedName = PrettyNaming.QuoteIdentifierIfNeeded name
normalisedName.Length )
match allLengths with
| [] -> 0
| l -> l |> maxUnderThreshold maxPadding

let allParamsLengths =
args |> List.map (List.map (fun p -> (formatParameter p).Length) >> List.sum)
let maxLength = (allParamsLengths |> maxUnderThreshold maxPadding) + 1

let parameterTypeWithPadding (p: FSharpParameter) length =
(formatParameter p) + (String.replicate (if length >= maxLength then 1 else maxLength - length) " ")

let formatName indent padding (parameter:FSharpParameter) =
let name = Option.defaultValue parameter.DisplayName parameter.Name
let normalisedName = PrettyNaming.QuoteIdentifierIfNeeded name
indent + name.PadRight padding + ":"

let formatParameterPadded length p =
formatName indent padLength p ++ (parameterTypeWithPadding p length)

let allParams =
List.zip args allParamsLengths
|> List.map(fun (paramTypes, length) ->
paramTypes
|> List.map (formatParameterPadded length)
|> String.concat (" *\n"))
|> String.concat ("->\n")

allParams + "\n" + indent + (String.replicate (max (padLength-1) 0) " ") + "->"

//Formats argument list for "signature" information without padding
let formatSignatureNoPadding (v:FSharpMemberOrFunctionOrValue) (args: list<list<FSharpParameter>>) =
let isItemIndexer = (v.IsInstanceMember && v.DisplayName = "Item")
let counter = let n = ref 0 in fun () -> incr n; !n
let unit, argSep, tupSep = "unit", " -> ", " * "
args
|> List.map (List.map (fun x -> formatParameter x))
|> List.map (function
| [] -> unit
| [arg] when arg = unit -> unit
| [arg] when not v.IsMember || isItemIndexer -> arg
| args when isItemIndexer -> String.concat tupSep args
| args -> bracket (String.concat tupSep args))
|> String.concat argSep

// Format each argument, including its name and type for "usage"
let formatArgUsage i (arg:FSharpParameter) =
let argName, argType = formatArgNameAndType i arg
if generateTypes then
(match arg.Name with None -> "" | Some argName -> argName + ": ") +
formatTypeWithPrec 2 argType
else argName
argName

let formatArgsUsage generateTypes (v:FSharpMemberOrFunctionOrValue) args =
//Formats argument list for "usage"
let formatArgsUsage (v:FSharpMemberOrFunctionOrValue) args =
let isItemIndexer = (v.IsInstanceMember && v.DisplayName = "Item")
let counter = let n = ref 0 in fun () -> incr n; !n
let unit, argSep, tupSep =
if generateTypes then "unit", " -> ", " * "
else "()", " ", ", "
let unit, argSep, tupSep = "()", " ", ", "
args
|> List.map (List.map (fun x -> formatArgUsage generateTypes (counter()) x))
|> List.map (List.map (fun x -> formatArgUsage (counter()) x))
|> List.map (function
| [] -> unit
| [arg] when arg = unit -> unit
Expand Down Expand Up @@ -641,7 +710,7 @@ module internal ValueReader =
else sprintf "(%s)" s)

match v.IsMember, v.IsInstanceMember, v.LogicalName, v.DisplayName with
// Constructors
// Constructors
| _, _, ".ctor", _ -> v.ApparentEnclosingEntity.DisplayName + (defaultArg parArgs "(...)")
// Indexers
| _, true, _, "Item" -> "this.[" + (defaultArg args "...") + "]"
Expand Down Expand Up @@ -669,7 +738,7 @@ module internal ValueReader =
argInfos
|> List.concat
|> List.mapi (fun i p -> let nm, ty = formatArgNameAndType i p in nm, formatType ty)

// Extension members can have apparent parents which are not F# types.
// Hence getting the generic argument count if this is a little trickier
let numGenericParamsOfApparentParent =
Expand All @@ -689,18 +758,18 @@ module internal ValueReader =
let retTypeText = defaultArg (retType |> Option.map formatType) "unit"

let returnTooltip =
match retType with None -> None | Some retType -> if isUnitType retType then None else Some retTypeText
match retType with None -> None | Some retType -> if isUnitType retType then None else Some retTypeText

let signatureTooltip =
match argInfos with
| [] -> retTypeText
| [[x]] when (v.IsPropertyGetterMethod || v.HasGetterMethod) && x.Name.IsNone && isUnitType x.Type -> retTypeText
| _ -> (formatArgsUsage true v argInfos) + " -> " + retTypeText
| _ -> (formatSignature v argInfos) + " " + retTypeText

let fullArgUsage =
match argInfos with
| [[x]] when (v.IsPropertyGetterMethod || v.HasGetterMethod) && x.Name.IsNone && isUnitType x.Type -> ""
| _ -> formatArgsUsage false v argInfos
| _ -> formatArgsUsage v argInfos

let usageTooltip = buildUsage (Some fullArgUsage)

Expand Down Expand Up @@ -737,11 +806,11 @@ module internal ValueReader =
fields |> List.map (fun p -> p.Name, formatType p.FieldType)

let returnTooltip = None
//if isUnitType retType then None else Some retTypeText
//if isUnitType retType then None else Some retTypeText

let usage (maxLength:int) =
let nm = (if requireQualifiedAccess then typ.DisplayName + "." else "") + case.Name
nm + buildUsage (maxLength - nm.Length)
nm + buildUsage (maxLength - nm.Length)

let usageTooltip = usage Int32.MaxValue
let modifiers = List.empty
Expand All @@ -766,7 +835,7 @@ module internal ValueReader =
let paramTooltips = []

let returnTooltip = None
//if isUnitType retType then None else Some retTypeText
//if isUnitType retType then None else Some retTypeText
let loc = tryGetLocation field
let location = formatSourceLocation ctx.UrlRangeHighlight ctx.SourceFolderRepository loc
let usageTooltip = usage Int32.MaxValue
Expand Down