Skip to content

Commit 61dd560

Browse files
authored
Insert doc comments when typing via LSP (#46301)
1 parent 3e5decb commit 61dd560

File tree

12 files changed

+1364
-920
lines changed

12 files changed

+1364
-920
lines changed

src/EditorFeatures/CSharp/DocumentationComments/DocumentationCommentCommandHandler.cs

Lines changed: 2 additions & 250 deletions
Original file line numberDiff line numberDiff line change
@@ -2,17 +2,13 @@
22
// The .NET Foundation licenses this file to you under the MIT license.
33
// See the LICENSE file in the project root for more information.
44

5-
using System.Collections.Generic;
5+
#nullable enable
6+
67
using System.ComponentModel.Composition;
78
using System.Diagnostics.CodeAnalysis;
8-
using System.Linq;
9-
using System.Threading;
10-
using Microsoft.CodeAnalysis.CSharp;
11-
using Microsoft.CodeAnalysis.CSharp.Extensions;
129
using Microsoft.CodeAnalysis.CSharp.Syntax;
1310
using Microsoft.CodeAnalysis.Editor.Host;
1411
using Microsoft.CodeAnalysis.Editor.Implementation.DocumentationComments;
15-
using Microsoft.CodeAnalysis.Shared.Extensions;
1612
using Microsoft.VisualStudio.Commanding;
1713
using Microsoft.VisualStudio.Language.Intellisense.AsyncCompletion;
1814
using Microsoft.VisualStudio.Text.Operations;
@@ -39,249 +35,5 @@ public DocumentationCommentCommandHandler(
3935
}
4036

4137
protected override string ExteriorTriviaText => "///";
42-
43-
protected override MemberDeclarationSyntax GetContainingMember(
44-
SyntaxTree syntaxTree, int position, CancellationToken cancellationToken)
45-
{
46-
return syntaxTree.GetRoot(cancellationToken).FindToken(position).GetAncestor<MemberDeclarationSyntax>();
47-
}
48-
49-
protected override bool SupportsDocumentationComments(MemberDeclarationSyntax member)
50-
{
51-
if (member == null)
52-
{
53-
return false;
54-
}
55-
56-
switch (member.Kind())
57-
{
58-
case SyntaxKind.ClassDeclaration:
59-
case SyntaxKind.InterfaceDeclaration:
60-
case SyntaxKind.StructDeclaration:
61-
case SyntaxKind.DelegateDeclaration:
62-
case SyntaxKind.EnumDeclaration:
63-
case SyntaxKind.EnumMemberDeclaration:
64-
case SyntaxKind.FieldDeclaration:
65-
case SyntaxKind.MethodDeclaration:
66-
case SyntaxKind.ConstructorDeclaration:
67-
case SyntaxKind.DestructorDeclaration:
68-
case SyntaxKind.PropertyDeclaration:
69-
case SyntaxKind.IndexerDeclaration:
70-
case SyntaxKind.EventDeclaration:
71-
case SyntaxKind.EventFieldDeclaration:
72-
case SyntaxKind.OperatorDeclaration:
73-
case SyntaxKind.ConversionOperatorDeclaration:
74-
return true;
75-
76-
default:
77-
return false;
78-
}
79-
}
80-
81-
protected override bool HasDocumentationComment(MemberDeclarationSyntax member)
82-
=> member.GetFirstToken().LeadingTrivia.Any(SyntaxKind.SingleLineDocumentationCommentTrivia, SyntaxKind.MultiLineDocumentationCommentTrivia);
83-
84-
protected override int GetPrecedingDocumentationCommentCount(MemberDeclarationSyntax member)
85-
{
86-
var firstToken = member.GetFirstToken();
87-
88-
var count = firstToken.LeadingTrivia.Count(t => t.IsDocComment());
89-
90-
var previousToken = firstToken.GetPreviousToken();
91-
if (previousToken.Kind() != SyntaxKind.None)
92-
{
93-
count += previousToken.TrailingTrivia.Count(t => t.IsDocComment());
94-
}
95-
96-
return count;
97-
}
98-
99-
protected override bool IsMemberDeclaration(MemberDeclarationSyntax member)
100-
=> true;
101-
102-
protected override List<string> GetDocumentationCommentStubLines(MemberDeclarationSyntax member)
103-
{
104-
var list = new List<string>
105-
{
106-
"/// <summary>",
107-
"/// ",
108-
"/// </summary>"
109-
};
110-
111-
var typeParameterList = member.GetTypeParameterList();
112-
if (typeParameterList != null)
113-
{
114-
foreach (var typeParam in typeParameterList.Parameters)
115-
{
116-
list.Add("/// <typeparam name=\"" + typeParam.Identifier.ValueText + "\"></typeparam>");
117-
}
118-
}
119-
120-
var parameterList = member.GetParameterList();
121-
if (parameterList != null)
122-
{
123-
foreach (var param in parameterList.Parameters)
124-
{
125-
list.Add("/// <param name=\"" + param.Identifier.ValueText + "\"></param>");
126-
}
127-
}
128-
129-
if (member.IsKind(SyntaxKind.MethodDeclaration) ||
130-
member.IsKind(SyntaxKind.IndexerDeclaration) ||
131-
member.IsKind(SyntaxKind.DelegateDeclaration) ||
132-
member.IsKind(SyntaxKind.OperatorDeclaration))
133-
{
134-
var returnType = member.GetMemberType();
135-
if (returnType != null &&
136-
!(returnType.IsKind(SyntaxKind.PredefinedType, out PredefinedTypeSyntax predefinedType) && predefinedType.Keyword.IsKindOrHasMatchingText(SyntaxKind.VoidKeyword)))
137-
{
138-
list.Add("/// <returns></returns>");
139-
}
140-
}
141-
142-
return list;
143-
}
144-
145-
protected override SyntaxToken GetTokenToRight(
146-
SyntaxTree syntaxTree, int position, CancellationToken cancellationToken)
147-
{
148-
if (position >= syntaxTree.GetText(cancellationToken).Length)
149-
{
150-
return default;
151-
}
152-
153-
return syntaxTree.GetRoot(cancellationToken).FindTokenOnRightOfPosition(
154-
position, includeDirectives: true, includeDocumentationComments: true);
155-
}
156-
157-
protected override SyntaxToken GetTokenToLeft(
158-
SyntaxTree syntaxTree, int position, CancellationToken cancellationToken)
159-
{
160-
if (position < 1)
161-
{
162-
return default;
163-
}
164-
165-
return syntaxTree.GetRoot(cancellationToken).FindTokenOnLeftOfPosition(
166-
position - 1, includeDirectives: true, includeDocumentationComments: true, includeSkipped: true);
167-
}
168-
169-
protected override bool IsDocCommentNewLine(SyntaxToken token)
170-
=> token.RawKind == (int)SyntaxKind.XmlTextLiteralNewLineToken;
171-
172-
protected override bool IsEndOfLineTrivia(SyntaxTrivia trivia)
173-
=> trivia.RawKind == (int)SyntaxKind.EndOfLineTrivia;
174-
175-
protected override bool IsSingleExteriorTrivia(DocumentationCommentTriviaSyntax documentationComment, bool allowWhitespace = false)
176-
{
177-
if (documentationComment == null)
178-
{
179-
return false;
180-
}
181-
182-
if (IsMultilineDocComment(documentationComment))
183-
{
184-
return false;
185-
}
186-
187-
if (documentationComment.Content.Count != 1)
188-
{
189-
return false;
190-
}
191-
192-
if (!(documentationComment.Content[0] is XmlTextSyntax xmlText))
193-
{
194-
return false;
195-
}
196-
197-
var textTokens = xmlText.TextTokens;
198-
if (!textTokens.Any())
199-
{
200-
return false;
201-
}
202-
203-
if (!allowWhitespace && textTokens.Count != 1)
204-
{
205-
return false;
206-
}
207-
208-
if (textTokens.Any(t => !string.IsNullOrWhiteSpace(t.ToString())))
209-
{
210-
return false;
211-
}
212-
213-
var lastTextToken = textTokens.Last();
214-
var firstTextToken = textTokens.First();
215-
216-
return lastTextToken.Kind() == SyntaxKind.XmlTextLiteralNewLineToken
217-
&& firstTextToken.LeadingTrivia.Count == 1
218-
&& firstTextToken.LeadingTrivia.ElementAt(0).Kind() == SyntaxKind.DocumentationCommentExteriorTrivia
219-
&& firstTextToken.LeadingTrivia.ElementAt(0).ToString() == ExteriorTriviaText
220-
&& lastTextToken.TrailingTrivia.Count == 0;
221-
}
222-
223-
private static IList<SyntaxToken> GetTextTokensFollowingExteriorTrivia(XmlTextSyntax xmlText)
224-
{
225-
var result = new List<SyntaxToken>();
226-
227-
var tokenList = xmlText.TextTokens;
228-
foreach (var token in tokenList.Reverse())
229-
{
230-
result.Add(token);
231-
232-
if (token.LeadingTrivia.Any(SyntaxKind.DocumentationCommentExteriorTrivia))
233-
{
234-
break;
235-
}
236-
}
237-
238-
result.Reverse();
239-
240-
return result;
241-
}
242-
243-
protected override bool EndsWithSingleExteriorTrivia(DocumentationCommentTriviaSyntax documentationComment)
244-
{
245-
if (documentationComment == null)
246-
{
247-
return false;
248-
}
249-
250-
if (IsMultilineDocComment(documentationComment))
251-
{
252-
return false;
253-
}
254-
255-
if (!(documentationComment.Content.LastOrDefault() is XmlTextSyntax xmlText))
256-
{
257-
return false;
258-
}
259-
260-
var textTokens = GetTextTokensFollowingExteriorTrivia(xmlText);
261-
262-
if (textTokens.Any(t => !string.IsNullOrWhiteSpace(t.ToString())))
263-
{
264-
return false;
265-
}
266-
267-
var lastTextToken = textTokens.LastOrDefault();
268-
var firstTextToken = textTokens.FirstOrDefault();
269-
270-
return lastTextToken.Kind() == SyntaxKind.XmlTextLiteralNewLineToken
271-
&& firstTextToken.LeadingTrivia.Count == 1
272-
&& firstTextToken.LeadingTrivia.ElementAt(0).Kind() == SyntaxKind.DocumentationCommentExteriorTrivia
273-
&& firstTextToken.LeadingTrivia.ElementAt(0).ToString() == ExteriorTriviaText
274-
&& lastTextToken.TrailingTrivia.Count == 0;
275-
}
276-
277-
protected override bool IsMultilineDocComment(DocumentationCommentTriviaSyntax documentationComment)
278-
=> documentationComment.IsMultilineDocComment();
279-
280-
protected override bool AddIndent
281-
{
282-
get { return true; }
283-
}
284-
285-
internal override bool HasSkippedTrailingTrivia(SyntaxToken token) => token.TrailingTrivia.Any(t => t.Kind() == SyntaxKind.SkippedTokensTrivia);
28638
}
28739
}

0 commit comments

Comments
 (0)