Skip to content

Commit 7215be1

Browse files
committed
chore: save progress before exploring VSCode like API structure
1 parent 10d33b3 commit 7215be1

File tree

9 files changed

+534
-189
lines changed

9 files changed

+534
-189
lines changed

.editorconfig

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,10 @@ fsharp_max_array_or_list_width=120
1616
fsharp_max_infix_operator_expression=80
1717
fsharp_max_value_binding_width=120
1818

19-
[{GenerateHtml.fs,tests/FSharp.ApiDocs.Tests/files/ReferenceProject/**/*.fs}]
19+
[{
20+
src/FSharp.Formatting.ApiDocs/GenerateHtml.fs
21+
src/FSharp.Formatting.ApiDocs/Generate/**.fs
22+
,tests/FSharp.ApiDocs.Tests/files/ReferenceProject/**/*.fs}]
2023
# Specific settings for "View/HTML" related files
2124
# It makes the code more consistent in term of spacing and indentation
2225
fsharp_experimental_elmish = true

docs/content/fsdocs-default.css

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1292,7 +1292,8 @@ dialog {
12921292
a.record-field-name,
12931293
a.union-case-property,
12941294
a.property {
1295-
/* color: darken($primary, 4%); */
1295+
color: var(--code-property-color);
1296+
text-decoration: none;
12961297

12971298
&:hover {
12981299
text-decoration: underline;
@@ -1392,4 +1393,5 @@ dialog {
13921393
justify-content: flex-start;
13931394
align-items: center;
13941395
}
1396+
13951397
}

src/FSharp.Formatting.ApiDocs/FSharp.Formatting.ApiDocs.fsproj

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,9 @@
1515
<Compile Include="GenerateModel.fs" />
1616
<Compile Include="Categorise.fs" />
1717
<Compile Include="GenerateSignature.fs" />
18+
<Compile Include="Generate/Common.fs" />
19+
<Compile Include="Generate/Module.fs" />
20+
<Compile Include="Generate/Record.fs" />
1821
<Compile Include="GenerateHtml.fs" />
1922
<Compile Include="GenerateMarkdown.fs" />
2023
<Compile Include="GenerateSearchIndex.fs" />
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
module internal FSharp.Formatting.ApiDocs.Generate.Common
2+
3+
open FSharp.Formatting.ApiDocs
4+
open FSharp.Formatting.HtmlModel
5+
open System.Xml.Linq
6+
open System.Text.RegularExpressions
7+
8+
let formatXmlComment (commentOpt: XElement option) : string =
9+
10+
match commentOpt with
11+
| Some comment ->
12+
let docComment = comment.ToString()
13+
14+
let pattern = $"""<member name=".*">((?'xml_doc'(?:(?!<member>)(?!<\/member>)[\s\S])*)<\/member\s*>)"""
15+
16+
let m = Regex.Match(docComment, pattern)
17+
18+
// Remove the <member> and </member> tags
19+
if m.Success then
20+
let xmlDoc = m.Groups.["xml_doc"].Value
21+
22+
let lines = xmlDoc |> String.splitLines |> Array.toList
23+
24+
// Remove the non meaning full indentation
25+
let content =
26+
lines
27+
|> List.map (fun line ->
28+
// Add a small protection in case the user didn't align all it's tags
29+
if line.StartsWith(" ") then
30+
line.Substring(1)
31+
else
32+
line
33+
)
34+
|> String.concat "\n"
35+
36+
CommentFormatter.format content
37+
else
38+
CommentFormatter.format docComment
39+
40+
| None -> ""
Lines changed: 243 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,243 @@
1+
module internal FSharp.Formatting.ApiDocs.Generate.Module
2+
3+
open FSharp.Formatting.HtmlModel
4+
open FSharp.Formatting.HtmlModel.Html
5+
open FSharp.Formatting.ApiDocs.GenerateSignature
6+
open FSharp.Formatting.ApiDocs
7+
8+
let sectionTitle (title: string) =
9+
strong [] [
10+
!!title
11+
]
12+
13+
let private renderSection (linkGenerator: ApiDocEntity -> string) (title: string) (entities: ApiDocEntity list) =
14+
[
15+
if not entities.IsEmpty then
16+
sectionTitle title
17+
18+
p [] [
19+
table [] [
20+
thead [] [
21+
tr [] [
22+
th [
23+
Width "25%"
24+
] [
25+
!! "Type"
26+
]
27+
th [
28+
Width "75%"
29+
] [
30+
!! "Description"
31+
]
32+
]
33+
]
34+
tbody [] [
35+
for entity in entities do
36+
tr [] [
37+
td [] [
38+
a [
39+
Href(linkGenerator entity)
40+
] [
41+
!!entity.Name
42+
]
43+
]
44+
td [] [
45+
!!(Common.formatXmlComment entity.Comment.Xml)
46+
]
47+
]
48+
]
49+
]
50+
]
51+
]
52+
53+
let private renderDeclaredTypes (entities: ApiDocEntity list) (linkGenerator: ApiDocEntity -> string) =
54+
entities
55+
|> List.filter (fun entity -> entity.IsTypeDefinition)
56+
|> renderSection linkGenerator "Declared types"
57+
58+
let private renderDeclaredModules (entities: ApiDocEntity list) (linkGenerator: ApiDocEntity -> string) =
59+
entities
60+
|> List.filter (fun entity -> entity.Symbol.IsFSharpModule)
61+
|> renderSection linkGenerator "Declared modules"
62+
63+
let private renderValueOrFunctions (entities: ApiDocMember list) (linkGenerator: ApiDocEntity -> string) =
64+
if entities.IsEmpty then
65+
[]
66+
else
67+
68+
[
69+
sectionTitle "Functions and values"
70+
71+
for entity in entities do
72+
let (ApiDocMemberDetails(usageHtml,
73+
paramTypes,
74+
returnType,
75+
modifiers,
76+
typars,
77+
baseType,
78+
location,
79+
compiledName)) =
80+
entity.Details
81+
82+
let returnHtml =
83+
// TODO: Parse the return type information from
84+
// let x = entity.Symbol :?> FSharpMemberOrFunctionOrValue
85+
// x.FullType <-- Here we have access to all the type including the argument for the function that we should ignore... (making the processing complex)
86+
// For now, we are just using returnType.HtmlText to have something ready as parsing from
87+
// FSharpMemberOrFunctionOrValue seems to be quite complex
88+
match returnType with
89+
| Some(_, returnType) ->
90+
// Remove the starting <code> and ending </code>
91+
returnType.HtmlText.[6 .. returnType.HtmlText.Length - 8]
92+
// Adapt the text to have basic syntax highlighting
93+
|> fun text -> text.Replace("&lt;", Html.lessThan.ToMinifiedHtml())
94+
|> fun text -> text.Replace("&gt;", Html.greaterThan.ToMinifiedHtml())
95+
|> fun text -> text.Replace(",", Html.comma.ToMinifiedHtml())
96+
97+
| None -> "unit"
98+
99+
let initial = Signature.ParamTypesInformation.Init entity.Name
100+
101+
let paramTypesInfo = Signature.extractParamTypesInformation initial paramTypes
102+
103+
div [
104+
Class "fsdocs-block"
105+
] [
106+
107+
div [
108+
Class "actions-buttons"
109+
] [
110+
// yield! sourceLink entity.SourceLocation
111+
// yield! copyXmlSigIconForSymbol entity.Symbol
112+
// yield! copyXmlSigIconForSymbolMarkdown entity.Symbol
113+
]
114+
115+
// This is a value
116+
if paramTypesInfo.Infos.IsEmpty then
117+
div [
118+
Class "fsdocs-api-code"
119+
] [
120+
div [] [
121+
Html.val'
122+
Html.space
123+
!!entity.Name
124+
Html.space
125+
Html.colon
126+
!!returnHtml
127+
]
128+
]
129+
130+
// This is a function
131+
else
132+
133+
div [
134+
Class "fsdocs-api-code"
135+
] [
136+
[
137+
TextNode.Div [
138+
TextNode.Keyword "val"
139+
TextNode.Space
140+
TextNode.AnchorWithId($"#{entity.Name}", entity.Name, entity.Name)
141+
TextNode.Space
142+
TextNode.Colon
143+
]
144+
]
145+
|> TextNode.Node
146+
|> TextNode.ToHtmlElement
147+
148+
for index in 0 .. paramTypesInfo.Infos.Length - 1 do
149+
let (name, returnType) = paramTypesInfo.Infos.[index]
150+
151+
div [] [
152+
Html.spaces 4 // Equivalent to 'val '
153+
!!name
154+
Html.spaces (paramTypesInfo.MaxNameLength - name.Length + 1) // Complete with space to align ':'
155+
Html.colon
156+
Html.space
157+
!! returnType.HtmlElement.ToMinifiedHtml()
158+
159+
Html.spaces (paramTypesInfo.MaxReturnTypeLength - returnType.Length + 1) // Complete with space to align '->'
160+
161+
// Don't add the arrow for the last parameter
162+
if index <> paramTypesInfo.Infos.Length - 1 then
163+
Html.arrow
164+
]
165+
|> Html.minify
166+
167+
div [] [
168+
Html.spaces (4 + paramTypesInfo.MaxNameLength + 1) // Equivalent to 'val ' + the max length of parameter name + ':'
169+
Html.arrow
170+
Html.space
171+
!!returnHtml
172+
]
173+
|> Html.minify
174+
]
175+
176+
match entity.Comment.Xml with
177+
| Some xmlComment ->
178+
let comment = xmlComment.ToString()
179+
!!(CommentFormatter.formatSummaryOnly comment)
180+
181+
if not paramTypesInfo.Infos.IsEmpty then
182+
p [] [
183+
strong [] [
184+
!! "Parameters"
185+
]
186+
]
187+
188+
for (name, returnType) in paramTypesInfo.Infos do
189+
let paramDoc =
190+
CommentFormatter.tryFormatParam name comment
191+
|> Option.map (fun paramDoc -> !!paramDoc)
192+
|> Option.defaultValue Html.nothing
193+
194+
div [
195+
Class "fsdocs-doc-parameter"
196+
] [
197+
[
198+
TextNode.DivWithClass(
199+
"fsdocs-api-code",
200+
[
201+
TextNode.Property name
202+
TextNode.Space
203+
TextNode.Colon
204+
TextNode.Space
205+
returnType
206+
]
207+
)
208+
]
209+
|> TextNode.Node
210+
|> TextNode.ToHtmlElement
211+
212+
paramDoc
213+
]
214+
215+
match CommentFormatter.tryFormatReturnsOnly comment with
216+
| Some returnDoc ->
217+
p [] [
218+
strong [] [
219+
!! "Returns"
220+
]
221+
]
222+
223+
!!returnDoc
224+
225+
| None -> ()
226+
227+
// TODO: Should we render a minimal documentation here with the information we have?
228+
// For example, we can render the list of parameters and the return type
229+
// This is to make the documentation more consistent
230+
// However, these minimal information will be rondontant with the information displayed in the signature
231+
| None -> ()
232+
]
233+
234+
// hr []
235+
236+
]
237+
238+
let renderModule (entityInfo: ApiDocEntityInfo) (linkGenerator: ApiDocEntity -> string) : HtmlElement list =
239+
[
240+
yield! renderDeclaredTypes entityInfo.Entity.NestedEntities linkGenerator
241+
yield! renderDeclaredModules entityInfo.Entity.NestedEntities linkGenerator
242+
yield! renderValueOrFunctions entityInfo.Entity.ValuesAndFuncs linkGenerator
243+
]

0 commit comments

Comments
 (0)