Skip to content

Commit 13c8d55

Browse files
[Repo Assist] Add /// doc comments to CrossReferenceResolver.fs, CodeFormatAgent.fs, and YaafFSharpScripting.fs (issue #1035) (#1076)
* Add `///` doc comments to CrossReferenceResolver.fs, CodeFormatAgent.fs, YaafFSharpScripting.fs (issue #1035) - CrossReferenceResolver.fs: docs for all private helpers and public member methods - CodeFormatAgent.fs: module-level doc plus docs for key functions - YaafFSharpScripting.fs: docs for Env, Log, CompilerServiceExtensions, output types Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * ci: trigger checks --------- Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com> Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent b942f68 commit 13c8d55

3 files changed

Lines changed: 102 additions & 18 deletions

File tree

src/FSharp.Formatting.ApiDocs/CrossReferenceResolver.fs

Lines changed: 38 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,8 @@ type internal CrossReferenceResolver(root, collectionName, qualify, extensions)
154154
let niceNameEntityLookup = Dictionary<_, _>()
155155
let extensions = extensions
156156

157+
/// Generates a unique, filesystem-safe URL base name for the given symbol name by
158+
/// replacing invalid characters and appending a numeric suffix when the name collides.
157159
let nameGen (name: string) =
158160
let nice =
159161
(toReplace
@@ -172,6 +174,7 @@ type internal CrossReferenceResolver(root, collectionName, qualify, extensions)
172174
usedNames.Add(found, true)
173175
found
174176

177+
/// Registers the XML doc signature for a member so it can be looked up during cross-reference resolution.
175178
let registerMember (memb: FSharpMemberOrFunctionOrValue) =
176179
let xmlsig = getXmlDocSigForMember memb
177180

@@ -184,6 +187,8 @@ type internal CrossReferenceResolver(root, collectionName, qualify, extensions)
184187

185188
xmlDocNameToSymbol.[xmlsig] <- memb
186189

190+
/// Recursively registers an entity and all its nested entities and members so they can
191+
/// be resolved as cross-references. Assigns a unique URL base name to each entity.
187192
let rec registerEntity (entity: FSharpEntity) =
188193
let newName = nameGen (sprintf "%s.%s" entity.AccessPath entity.CompiledName)
189194

@@ -205,12 +210,15 @@ type internal CrossReferenceResolver(root, collectionName, qualify, extensions)
205210
for memb in entity.TryGetMembersFunctionsAndValues() do
206211
registerMember memb
207212

213+
/// Returns the previously-assigned URL base name for a registered entity,
214+
/// raising an exception if the entity has not been registered.
208215
let getUrlBaseNameForRegisteredEntity (entity: FSharpEntity) =
209216
match registeredSymbolsToUrlBaseName.TryGetValue(entity) with
210217
| true, v -> v
211218
| _ ->
212219
failwithf "The entity %s was not registered before!" (sprintf "%s.%s" entity.AccessPath entity.CompiledName)
213220

221+
/// Strips a trailing parameter list "(…)" from a member name, returning just the base name.
214222
let removeParen (memberName: string) =
215223
let firstParen = memberName.IndexOf('(')
216224

@@ -219,7 +227,7 @@ type internal CrossReferenceResolver(root, collectionName, qualify, extensions)
219227
else
220228
memberName
221229

222-
// Strip generic parameters from a type name: "Map<K,V>" -> "Map", "List`1" -> "List"
230+
/// Strips generic parameters from a type name, e.g. <c>"Map&lt;K,V&gt;"</c> → <c>"Map"</c>, <c>"List`1"</c> → <c>"List"</c>.
223231
let stripGenericSuffix (name: string) =
224232
let angleIdx = name.IndexOf('<')
225233
let backtickIdx = name.IndexOf('`')
@@ -233,6 +241,8 @@ type internal CrossReferenceResolver(root, collectionName, qualify, extensions)
233241

234242
name.Substring(0, cutAt)
235243

244+
/// Extracts the containing type name from a fully-qualified member name, e.g.
245+
/// <c>"Ns.Type.Member(args)"</c> → <c>Some "Ns.Type"</c>; returns <c>None</c> if no period is found.
236246
let tryGetTypeFromMemberName (memberName: string) =
237247
let sub = removeParen memberName
238248
let lastPeriod = sub.LastIndexOf('.')
@@ -242,6 +252,8 @@ type internal CrossReferenceResolver(root, collectionName, qualify, extensions)
242252
else
243253
None
244254

255+
/// Extracts the short member name (without the containing type prefix) from a fully-qualified
256+
/// member name, e.g. <c>"Ns.Type.Member(args)"</c> → <c>Some "Member(args)"</c>.
245257
let tryGetShortMemberNameFromMemberName (memberName: string) =
246258
let sub = removeParen memberName
247259
let lastPeriod = sub.LastIndexOf('.')
@@ -251,6 +263,8 @@ type internal CrossReferenceResolver(root, collectionName, qualify, extensions)
251263
else
252264
None
253265

266+
/// Returns a display name for a member by retaining the last <paramref name="keepParts"/> dot-separated
267+
/// segments and optionally stripping a trailing "Module" suffix from the first segment.
254268
let getMemberName keepParts hasModuleSuffix (memberNameNoParen: string) =
255269
let splits = memberNameNoParen.Split('.') |> Array.toList
256270

@@ -278,6 +292,9 @@ type internal CrossReferenceResolver(root, collectionName, qualify, extensions)
278292

279293
noGenerics
280294

295+
/// Builds an external documentation link for an FSharp.Core or .NET symbol.
296+
/// FSharp.Core symbols are linked to <c>fsharp.github.io/fsharp-core-docs</c>;
297+
/// all other symbols are linked to <c>learn.microsoft.com/dotnet/api</c>.
281298
let externalDocsLink isMember simple (typeName: string) (fullName: string) =
282299
if
283300
fullName.StartsWith("FSharp.", StringComparison.Ordinal)
@@ -340,12 +357,16 @@ type internal CrossReferenceResolver(root, collectionName, qualify, extensions)
340357
ReferenceLink = link
341358
NiceName = simple }
342359

360+
/// Returns the URL for a registered entity within this documentation collection.
343361
let internalCrossReference urlBaseName =
344362
ApiDocEntity.GetUrl(urlBaseName, root, collectionName, qualify, extensions.InUrl)
345363

364+
/// Returns the URL for a specific member of a registered entity within this documentation collection.
346365
let internalCrossReferenceForMember entityUrlBaseName (memb: FSharpMemberOrFunctionOrValue) =
347366
ApiDocMember.GetUrl(entityUrlBaseName, memb.DisplayName, root, collectionName, qualify, extensions.InUrl)
348367

368+
/// Tries to resolve a cross-reference for an FSharpEntity; returns an internal link if the entity
369+
/// was registered, otherwise falls back to an external documentation link.
349370
let tryResolveCrossReferenceForEntity (entity: FSharpEntity) =
350371
match registeredSymbolsToUrlBaseName.TryGetValue(entity) with
351372
| true, _v ->
@@ -360,6 +381,8 @@ type internal CrossReferenceResolver(root, collectionName, qualify, extensions)
360381
| None -> None
361382
| Some nm -> Some(externalDocsLink false entity.DisplayName nm nm)
362383

384+
/// Resolves a type cross-reference given its XML doc signature (must start with <c>"T:"</c>).
385+
/// Checks the registered symbol map first, then falls back to the nice-name entity lookup.
363386
let resolveCrossReferenceForTypeByXmlSig (typeXmlSig: string) =
364387
assert (typeXmlSig.StartsWith("T:", StringComparison.Ordinal))
365388

@@ -428,6 +451,8 @@ type internal CrossReferenceResolver(root, collectionName, qualify, extensions)
428451
let simple = getMemberName 1 false typeName
429452
externalDocsLink false simple typeName typeName
430453

454+
/// Converts a registered <see cref="T:FSharp.Compiler.Symbols.FSharpMemberOrFunctionOrValue"/> to an
455+
/// internal <see cref="T:FSharp.Formatting.ApiDocs.CrefReference"/>.
431456
let mfvToCref (mfv: FSharpMemberOrFunctionOrValue) =
432457
match mfv.DeclaringEntity with
433458
| None -> failwith $"%s{mfv.DisplayName} does not have a DeclaringEntity"
@@ -438,6 +463,8 @@ type internal CrossReferenceResolver(root, collectionName, qualify, extensions)
438463
ReferenceLink = internalCrossReferenceForMember entityUrlBaseName mfv
439464
NiceName = declaringEntity.DisplayName + "." + mfv.DisplayName }
440465

466+
/// Tries to resolve a cross-reference for a member given its XML doc signature
467+
/// (must start with <c>"M:"</c>, <c>"P:"</c>, <c>"F:"</c>, or <c>"E:"</c>).
441468
let tryResolveCrossReferenceForMemberByXmlSig (memberXmlSig: string) =
442469
assert
443470
(memberXmlSig.StartsWith("M:", StringComparison.Ordinal)
@@ -519,7 +546,8 @@ type internal CrossReferenceResolver(root, collectionName, qualify, extensions)
519546
None
520547

521548

522-
// Try to resolve a cref that has no XML doc prefix (e.g. "MyType", "MyType.MyMember", "Map<K,V>")
549+
/// Tries to resolve a cref that has no XML doc prefix (e.g. <c>"MyType"</c>, <c>"MyType.MyMember"</c>).
550+
/// Attempts a "Type.Member" split first, then a bare type name lookup.
523551
let tryResolveUnqualifiedCref (cref: string) =
524552
let baseName = stripGenericSuffix cref
525553

@@ -562,6 +590,8 @@ type internal CrossReferenceResolver(root, collectionName, qualify, extensions)
562590
| _ -> None
563591
| _ -> None
564592

593+
/// Resolves a <c>cref</c> string (with or without XML doc prefix such as <c>"T:"</c> or <c>"M:"</c>)
594+
/// to a <see cref="T:FSharp.Formatting.ApiDocs.CrefReference"/>, or <c>None</c> if unresolvable.
565595
member _.ResolveCref(cref: string) =
566596
if (cref.Length < 2) then
567597
invalidArg "cref" (sprintf "the given cref: '%s' is invalid!" cref)
@@ -583,19 +613,25 @@ type internal CrossReferenceResolver(root, collectionName, qualify, extensions)
583613
Log.warnf "Unresolved reference '%s'!" cref
584614
None
585615

616+
/// Registers an entity (and all its nested entities and members) so that cross-references to it can be resolved.
586617
member _.RegisterEntity entity = registerEntity entity
587618

619+
/// Returns the URL base name that was assigned to the given registered entity.
588620
member _.ResolveUrlBaseNameForEntity entity =
589621
getUrlBaseNameForRegisteredEntity entity
590622

623+
/// Returns the URL base name for the entity if it has been registered, or <c>None</c>.
591624
member _.TryResolveUrlBaseNameForEntity(entity: FSharpEntity) =
592625
match registeredSymbolsToUrlBaseName.TryGetValue(entity) with
593626
| true, v -> Some v
594627
| _ -> None
595628

629+
/// Tries to resolve an <see cref="T:FSharp.Compiler.Symbols.FSharpEntity"/> to a cross-reference,
630+
/// returning an internal reference if registered or an external link otherwise.
596631
member _.TryResolveEntity entity =
597632
tryResolveCrossReferenceForEntity entity
598633

634+
/// Returns <c>true</c> if the given cref resolves to an entity within this documentation collection.
599635
member x.IsLocal cref =
600636
match x.ResolveCref(cref) with
601637
| None -> false

src/FSharp.Formatting.CodeFormat/CodeFormatAgent.fs

Lines changed: 19 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,8 @@ open FSharp.Compiler.EditorServices
1717
open FSharp.Compiler.CodeAnalysis
1818
open FSharp.Compiler.Diagnostics
1919

20-
// --------------------------------------------------------------------------------------
21-
// ?
22-
// --------------------------------------------------------------------------------------
23-
20+
/// Internal helpers for tokenising, type-checking and annotating F# source code
21+
/// using the F# Compiler Service, producing coloured and tooltip-annotated token spans.
2422
module private Helpers =
2523

2624
/// Mapping table that translates F# compiler representation to our union
@@ -40,8 +38,7 @@ module private Helpers =
4038
| FSharpTokenColorKind.Default
4139
| _ -> TokenKind.Default
4240

43-
// Parse command line options - split string by space, but if there is something
44-
// enclosed in double quotes "..." then ignore spaces in the quoted text
41+
/// Parses compiler command-line options by splitting on spaces while respecting quoted strings.
4542
let parseOptions (str: string) =
4643
let rec loop i opts current =
4744
let opts =
@@ -107,8 +104,8 @@ module private Helpers =
107104

108105
indexedSnippetLines
109106

110-
// Count the minimal number of spaces at the beginning of lines
111-
// (so that we can remove spaces for indented text)
107+
/// Returns the number of leading spaces on the least-indented non-empty line in the snippet,
108+
/// used to strip common indentation when formatting indented code blocks.
112109
let countStartingSpaces (lines: Snippet) =
113110
[ for _, toks: _ list in lines do
114111
match toks with
@@ -118,6 +115,7 @@ module private Helpers =
118115
| _ -> yield 0 ]
119116
|> List.fold min 0
120117

118+
/// A column range within a single source line, used to track string literal extents during tokenisation.
121119
[<Struct>]
122120
type internal Range =
123121
{ LeftCol: int
@@ -127,15 +125,19 @@ type internal Range =
127125
{ LeftCol = leftCol
128126
RightCol = rightCol }
129127

130-
/// Uses agent to handle formatting requests
128+
/// Formats F# source code snippets into annotated token sequences by invoking the
129+
/// F# Compiler Service for tokenisation, type-checking, and semantic classification.
131130
module CodeFormatter =
132-
// Create keys for query tooltips for double-backtick identifiers
131+
/// Converts a double-backtick identifier like <c>``my ident``</c> into the form
132+
/// <c>( my ident )</c> that the compiler uses for tooltip lookup.
133133
let processDoubleBackticks (body: string) =
134134
if body.StartsWith("``", StringComparison.Ordinal) then
135135
sprintf "( %s )" <| body.Trim '`'
136136
else
137137
body
138138

139+
/// Maps an FCS <see cref="T:FSharp.Compiler.EditorServices.SemanticClassificationType"/> to our
140+
/// <see cref="T:FSharp.Formatting.CodeFormat.TokenKind"/>, or <c>None</c> for unrecognised categories.
139141
let categoryToTokenKind =
140142
function
141143
| SemanticClassificationType.Enumeration -> Some TokenKind.Enumeration
@@ -178,7 +180,8 @@ module CodeFormatter =
178180
| _ -> None
179181

180182

181-
// Processes a single line of the snippet
183+
/// Processes a single source line: walks the token stream and enriches each token with
184+
/// semantic classification (colour kind and tooltip) from the FCS type-check results.
182185
let processSnippetLine
183186
(checkResults: FSharpCheckFileResults)
184187
(semanticRanges: SemanticClassificationItem array)
@@ -319,7 +322,8 @@ module CodeFormatter =
319322
// Process the current line & return info about it
320323
Line(lineStr, loop [] (List.ofSeq lineTokens) None |> List.ofSeq)
321324

322-
/// Process snippet
325+
/// Processes all lines of a snippet using the FCS check results and semantic classification ranges,
326+
/// returning an annotated list of <see cref="T:FSharp.Formatting.CodeFormat.Line"/> values.
323327
let processSnippet checkResults categorizedRanges lines (snippet: Snippet) =
324328
snippet
325329
|> List.map (fun snippetLine ->
@@ -336,12 +340,11 @@ module CodeFormatter =
336340

337341
// --------------------------------------------------------------------------------------
338342

339-
// Create an instance of an InteractiveChecker (which does background analysis
340-
// in a typical IntelliSense editor integration for F#)
343+
/// Shared <see cref="T:FSharp.Compiler.CodeAnalysis.FSharpChecker"/> instance used for all source-code processing.
341344
let fsChecker = FSharpAssemblyHelper.checker // FSharpChecker.Create()
342345

343-
// ------------------------------------------------------------------------------------
344-
346+
/// Asynchronously parses, type-checks, and annotates a single F# source file or script,
347+
/// returning an array of named, token-annotated snippets and any compilation diagnostics.
345348
let processSourceCode (filePath, source, options, defines, onError) =
346349
async {
347350
Log.verbf "starting to process source code from '%s'" filePath

0 commit comments

Comments
 (0)