Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Conversation

@RikkiGibson
Copy link
Member

@RikkiGibson RikkiGibson commented Sep 22, 2025

File-based programs editor features

Related discussion: #79275 (comment) and below in that PR

This PR adds a project Microsoft.CodeAnalysis.FileBasedPrograms.Features. This DLL ships with the language server only, not with Visual Studio. The new editor features are being added to the existing MS.CA.CSharp.Features.dll. I created a small completion provider for #:property to prove out the viability of the approach.

This is planned to include stuff like:

There has been some concern about where the types that provide this functionality should physically live. In the discussion linked above I previously outlined some options. I'll try to reiterate here as that discussion was a while ago.

Where to put FileBasedPrograms.Features

I think the Roslyn repo is the best place for this project to live at this point. Shipping from Roslyn means all we need to do is add the ProjectReference in the LanguageServer, and the components we want will simply land in the language server. This minimizes the need for cross-repo dependency flow.

The other candidate location we had in mind was the dotnet/sdk repo. Shipping from there would make it difficult to ship updates to these features. When we fix bugs for these features, we would have to start considering whether to take fixes for them thru SDK servicing and so on. This represents a difficulty/risk which seems avoidable. See also #79275 (comment).

One reason dotnet/sdk was interesting as a candidate was that we could share the implementation code which reports directive diagnostics for dotnet run app.cs. Due to the difficulty represented by servicing the editor features in the SDK, I think the best way forward for sharing, would be to publish a source-only package from the SDK, which is consumed in the Roslyn repo, and includes API we can call from an analyzer defined in the FileBasedPrograms.Features to diagnose problems with the directives. This is something previously discussed with @jjonescz.

The aforementioned source package may also provide the ability to obtain the virtual project itself and remove the need to spawn a subprocess for dotnet run-api from FileBasedProgramsProjectSystem in the language server. In other words, this is a dependency which we have reason for wanting to create, even without considering the editor features scenario. This is also an area where we previously expressed being comfortable with IDE aligning with the "latest SDK behavior" for this feature area, rather than attempting to support as many combinations as possible of supported SDKs and C# extension versions. Tagging @jasonmalinowski @jaredpar regarding that particular line of thinking.

Use of internal APIs

It was previously suggested that the file-based programs editor features should use the Roslyn public API surface only: #79275 (comment)

This approach poses a few problems, outlined below. This PR opts to add IVTs to some similar areas where CSharp.Features project already holds IVT.

Testing is difficult

The solution in this PR just tests the new features in the same CSharp.Features.UnitTests project as the built-in ones. I did this because identifying and copying the subset of internal API needed for the tests did not feel like a productive activity when we are already shipping out of the Roslyn repo. We can also consider breaking out a separate test project, which has IVT to the helpers that it needs.

Useful implementation APIs are internal

Here are some examples of APIs which would either require copy-pasting or searching for an alternative solution.

  • internal class LSPCompletionProvider provides TriggerCharacters which are aggregated and forwarded to the LSP client.
    • Per discussion with @dibarbet we think we could get away with not subtyping this, because there are already built-in providers which trigger completion on :. We may want to consider exposing some public API for completion providers to contribute TriggerCharacters, but, the main reason for that would perhaps be the principle of it.
  • SyntaxToken FindTokenOnLeftOfPosition(SyntaxNode, int)
  • CompletionUtilities.IsStartingNewWord
  • Possibly some or all of these APIs are available via source packages a la what are used in Have RoslynAnalyzers reference shared utilities/extensions from Roslyn layer itself. #79030.

As implementation work continues more of these cases are likely to be found. In general these can be solved, but, it's not clear to me if doing that work "up front" offers a compelling benefit. Essentially, in the future, if there is hypothetically a need to migrate the features to a different repo, then we would have to do the work to move from IVTs to source packages or similar solution.


I am hoping to get your feedback on whether this general direction is reasonable, and to address any concerns, so that we can move forward with this work. Thanks!

I would also like to emphasize that finding the ideal home for things, has been particularly difficult with this feature. We have "project system" code in the language server, which is used for FBPs, which theoretically would be better placed in the actual project system repo. We have code for parsing/diagnosing directives and creating the virtual project, which we might prefer to live in msbuild rather than dotnet/sdk. For all of these, and the editor features components, we have some ability to migrate where these components live over time, particularly as we move from building out the core end-to-end experience, to making things work more broadly toward the corners of the .NET platform.

@RikkiGibson RikkiGibson requested a review from a team as a code owner September 22, 2025 22:57
@dibarbet
Copy link
Member

This PR adds a project Microsoft.CodeAnalysis.FileBasedPrograms.Features. This DLL ships with the language server only, not with Visual Studio. I created a small completion provider for #:property to prove out the viability of the approach.

Understand that we want to only enable these where file based programs are enabled, and shipping an LSP only dll makes that easy. However that will cause problems when we eventually want to turn these on in VS - I don't think I can make a good argument to the perf team that an extra dll load for this is necessary.

Is there any way we can put these in the LanguageServer.Protocol or Features projects, but dynamically enable them when file based programs are enabled? The completion provider especially seems like it should just be like every other completion provider we ship. It is IDE-only and has no need to be in the SDK.

For the analyzer, could we build off prior work (e.g. code style analyzers) to share with the SDK?

@RikkiGibson
Copy link
Member Author

This solution has come about in part to try and address pushback against including these in the main CSharp.Features.dll.

In the hypothetical scenario we needed to deliver editor support in VS, it feels like we should be able to move these features to an existing DLL. It seems like it would not be harmful to, for example, delete this project in the future and move its contents to another existing project.

What specifically did you have in mind for dynamically enabling them? Is it important that the feature(s) don't load/get created at all, or is it enough if they don't contribute behaviors in the editor? The latter is something we can do, and I think we want to do anyway, in the case of ordinary projects in VS Code (avoiding suggesting user writes #:property and so on.)

@dibarbet
Copy link
Member

This solution has come about in part to try and address pushback against including these in the main CSharp.Features.dll

Definitely some parts seem more reasonable than others to be in the main csharp features (e.g. completion provider seems totally reasonable to be in roslyn). I am happy to be overruled though if that discussion has already happened.

In the hypothetical scenario we needed to deliver editor support in VS, it feels like we should be able to move these features to an existing DLL. It seems like it would not be harmful to, for example, delete this project in the future and move its contents to another existing project.

Yes, that is possible - but it'll save us work later if we only have to do it once. But again, open to being overruled here.

What specifically did you have in mind for dynamically enabling them? Is it important that the feature(s) don't load/get created at all, or is it enough if they don't contribute behaviors in the editor?

It depends - while not loading / not getting created is ideal, I don't think it is much of a problem if they are as long as they are not causing many other things to get created (for example, MEF importing a type from an assembly that isn't otherwise loaded would be bad).

@CyrusNajmabadi
Copy link
Member

I will try to review this today

Copy link
Member

@jasonmalinowski jasonmalinowski left a comment

Choose a reason for hiding this comment

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

Leaving some general comments, but as long as we're putting this in the Roslyn repo I'm not sure why we wouldn't just put this in our existing features DLLs. Since there's a check for the compiler feature being enabled (which just won't be in VS), we probably get all the same layering and no extra complexity.

@CyrusNajmabadi
Copy link
Member

I dobn't have strong feeligns here. but i trust jason a lot :)

@RikkiGibson RikkiGibson requested a review from dibarbet October 3, 2025 19:45
@RikkiGibson RikkiGibson changed the title Initial spike for file-based programs editor features DLL Initial spike for file-based programs editor features Oct 3, 2025
…ompletionProviderOrderTests.cs

Co-authored-by: Joey Robichaud <[email protected]>
Copy link
Member

@jasonmalinowski jasonmalinowski left a comment

Choose a reason for hiding this comment

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

Looks fine except for the filtering logic happening in the completion provider; I don't think that's necessary since that'll be done for you by the core filtering logic. I know there's also some fancier logic (but might be for VB?) where sometimes we don't filter and that'd mess this up.

Comment on lines +590 to +606
<data name="Build_property_value" xml:space="preserve">
<value>Value</value>
<comment>'Value' is a placeholder for a build property value in a directive like '#:property Name=Value'.</comment>
</data>
<data name="Build_property_name" xml:space="preserve">
<value>Name</value>
<comment>'Name' is a placeholder for a property name in a directive like '#:property Name=Value'.</comment>
</data>
<data name="Package_name" xml:space="preserve">
<value>Name</value>
<comment>'Name' is a placeholder for a package or sdk name in a directive like '#:package Name@Version'.</comment>
</data>
<data name="Package_version" xml:space="preserve">
<value>Version</value>
<comment>'Version' is a placeholder for a package or sdk version in a directive like '#:package Name@Version'.</comment>
</data>
<data name="Project_directive_file_path" xml:space="preserve">
Copy link
Member

Choose a reason for hiding this comment

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

For consistency should we update these other names similar to Jason's suggestion?

Property_directive_value
Property_directive_name
Package_directive_name
Package_directive_version

Copy link
Member Author

Choose a reason for hiding this comment

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

Perhaps in a follow up?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants