Skip to content
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

Merged
merged 12 commits into from
Dec 27, 2023

Conversation

davidwengier
Copy link
Contributor

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.

@davidwengier davidwengier requested a review from a team as a code owner December 24, 2023 10:41
@davidwengier davidwengier force-pushed the dev/dawengie/SemanticTokensCohost branch from 59e2394 to bf504f3 Compare December 25, 2023 10:17
private readonly RazorLSPOptionsMonitor _razorLSPOptionsMonitor;
private readonly IClientConnection _clientConnection;
private readonly ILogger _logger;
private readonly IRazorDocumentMappingService _documentMappingService = documentMappingService ?? throw new ArgumentNullException(nameof(documentMappingService));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

_documentMappingService

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.

Copy link
Contributor Author

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.

Copy link
Contributor

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>();
Copy link
Contributor

@alexgav alexgav Dec 25, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

loggerFactory.CreateLogger()

nit: maybe

_logger = (loggerFactory ?? throw new ArgumentNullException(nameof(loggerFactory)).CreateLogger();

Copy link
Contributor Author

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?

Copy link
Contributor

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);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

5

Should this be the "TokenSize" const already defined in this class? Same with two usages of magic number "5" below :)

Copy link
Contributor Author

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

Copy link
Contributor

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;
Copy link
Contributor

@alexgav alexgav Dec 26, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

_semanticTokensInfoService

Why the field instead of using primary constructor parameter in the methods?

Copy link
Contributor Author

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 :)

Copy link
Contributor

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?

Copy link
Contributor

@alexgav alexgav left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

:shipit:

@davidwengier davidwengier merged commit be23375 into features/cohost Dec 27, 2023
12 checks passed
@davidwengier davidwengier deleted the dev/dawengie/SemanticTokensCohost branch December 27, 2023 08:27
services.AddHandlerWithCapabilities<SemanticTokensRangeEndpoint>();
// Ensure that we don't add the default service if something else has added one.
services.TryAddSingleton<IRazorSemanticTokensInfoService, RazorSemanticTokensInfoService>();
}
Copy link
Member

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.

Copy link
Contributor Author

@davidwengier davidwengier Jan 2, 2024

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

davidwengier added a commit that referenced this pull request Jan 5, 2024
Part of #9519

Now that the initial cohost PR has been merged, we don't need to use the
feature branch any more. That means this PR, which is big, but all of
the code in it has been reviewed, in
#9761,
#9766 and
#9767.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants