-
Notifications
You must be signed in to change notification settings - Fork 196
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Port semantic tokens range endpoint to cohost server #9761
Port semantic tokens range endpoint to cohost server #9761
Conversation
In some respects this is a temporary thing until we get proper options support in cohosting, but in other respects having the service just take the one option value it actually needs is much nicer
59e2394
to
bf504f3
Compare
private readonly RazorLSPOptionsMonitor _razorLSPOptionsMonitor; | ||
private readonly IClientConnection _clientConnection; | ||
private readonly ILogger _logger; | ||
private readonly IRazorDocumentMappingService _documentMappingService = documentMappingService ?? throw new ArgumentNullException(nameof(documentMappingService)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: what do you think of suggestions here
https://stackoverflow.com/questions/77556444/null-checking-with-primary-constructor-in-c-sharp-12
Couple of interesting thoughts there. I personally like the idea of re-using primary constructor parameter name for class field name to shadow it. That way you won't have both "documentMappingService" and "_documentMappingService" in your completion list. Also that helper methods seems nice, though it becomes another "magic" method someone not familiar with codebase won't immediately understand when looking at the code. So not sure about the helper, but re-using parameter names seems nice.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Probably something we should discuss, and maybe vote on, in a meeting early next year. Personally I'm a fan of having separate fields because a) they're readonly, and b) its obvious in a PR diff whether something is a field because of the underscore. If a primary constructor parameter is captured in a field, then the compiler will warn if the parameter is used in a method body, so its not really possible to mix the two styles.
As for the extension method, I don't have any issue with it. We already have one, .AssumeNotNull()
which we could use, though it throws a different exception type. Don't know if that matters. My real personal preference there is to get rid of the null checks altogether and just rely on the MEF composition exceptions.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't have strong feelings here and I also like having underscores for readability, but then should we always have private (readonly when appropriate) field starting with underscore for primary constructor parameters used in class methods? Definitely something we should agree on to be consistent. Seems like half of the clean-up from primary constructors came from getting rid of fields, then of course naming becomes a bit confusing, especially in long files when you are looking at a name and you don't know if it's a local defined somewhere above in the method or primary constructor parameter. If we do have fields for each parameter, do we even need to switch to primary constructors?
I'm good with relying on MEF composition exceptions in MEF parts, the code wouldn't even get to throwing ANE exception in that case, so the check is pointless.
Might be nice to have ANE thrown instead of IOE, so maybe adding ArgumentNotNull would be nice in places where we keep null checking for arguments. Not asking to add in this PR :). I was looking at this mostly in the context of switching to primary constructors and having each field initializer doing the whole "name ?? throw new ANE(nameof(name))" everywhere to see if there is something more succinct we could do.
private readonly ILogger _logger; | ||
private readonly IRazorDocumentMappingService _documentMappingService = documentMappingService ?? throw new ArgumentNullException(nameof(documentMappingService)); | ||
private readonly LanguageServerFeatureOptions _languageServerFeatureOptions = languageServerFeatureOptions ?? throw new ArgumentNullException(nameof(languageServerFeatureOptions)); | ||
private readonly ILogger _logger = loggerFactory.CreateLogger<RazorSemanticTokensInfoService>(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If logger factory is null, this line would throw anyway, so not sure its worth it?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't care too much, maybe a little nicer to have the name of the argument captured? Feel free to punt.
|
||
var semanticTokens = await GetSemanticTokensAsync(clientConnection, textDocumentIdentifier, range, documentContext, correlationId, colorBackground, cancellationToken).ConfigureAwait(false); | ||
|
||
var amount = semanticTokens is null ? "no" : (semanticTokens.Data.Length / 5).ToString(Thread.CurrentThread.CurrentCulture); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fair call, I just copied this as is. Will update
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yep, I saw it first in the original code location and was like "what's the magic number 5 and where does it come from"? :) The constant in the target class made sense.
{ | ||
_telemetryReporter = telemetryReporter; | ||
} | ||
private readonly IRazorSemanticTokensInfoService _semanticTokensInfoService = semanticTokensInfoService; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
See reply above, but TL;DR I like the underscore :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I like the underscores too :) Also see my response above. I don't have strong feeling either way, other than - do the primary constructors even make sense then?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
services.AddHandlerWithCapabilities<SemanticTokensRangeEndpoint>(); | ||
// Ensure that we don't add the default service if something else has added one. | ||
services.TryAddSingleton<IRazorSemanticTokensInfoService, RazorSemanticTokensInfoService>(); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Given that there is no else case for this if (!featureOptions.UseRazorCohostServer)
condition,
I wonder where is the cohost setup happening and why we don't add IRazorSemanticTokensInfoService
for when UseRazorCohostServer
is set to true
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Cohosting uses MEF for DI, so the setup is all done via attributes and not by manually adding things to service collections
Brings semantic tokens to cohosting, so there is an easy visual indicator if things are working.
Right now colours are disco on first open of a file, but are fixed with an edit. This is actually a good thing, as it will help me diagnose and improve the document system in cohosting.