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

Added support for .editorconfig #1526

Merged
merged 18 commits into from
Jun 23, 2019

Conversation

filipw
Copy link
Member

@filipw filipw commented Jun 15, 2019

This PR adds support for .editorconfig.
It spans across multiple features:

  • ability to control formatting options via .editorconfig (i.e. indentation, spacing, new lines etc)
  • ability to control code style options via .editorconfig (i.e. prefer var over concrete types etc)
  • ability to control naming conventions via .editorconfig (i.e. camel casing or pascal casing of members, prefixes etc)
  • ability to control analyzers via .editorconfig

.editorconfig rules are also respected by the invoked analyzers, the invoked refactoring and code fixes. In other words, they manifest themselves through analyzers and in code generation.

At the moment, changes to .editorconfig related to formatting are recomputed in real time, for other features they require omnisharp restart.
The implementation is reflection based since the APIs are not public in Roslyn, however this is a very important feature, that we have waited years for, and is one of the most upvoted feature requests, so as much as we try to avoid it, I think reflection is acceptable here.

The approach is adapted from how https://github.com/dotnet/format works (although it only deals with formatting, not with code style/naming conventions/analyzers).

Fixes #950
Fixes #1242
Fixes #31
Fixes dotnet/vscode-csharp#1726

@kpko
Copy link

kpko commented Jun 15, 2019

This is awesome, thank you so much! I think this will enable a lot of people to switch from Visual Studio to VSCode or even VIM via Omnisharp.

Is there an existing issue to open up the APIs on Roslyns side?

@Hoffs
Copy link

Hoffs commented Jun 16, 2019

Are there additional changes required in omnisharp-vscode extension? I've looked through and I saw that you've added additional option "EnableEditorConfigSupport" which I suppose needs to be passed from mentioned extension as well, so I guess it does indeed need an update. Is this planned by you or someone else?

@bjorkstromm
Copy link
Member

@Hoffs quickly checked, and it should be enough to enable it via omnisharp.json in the FormattingOptions section. See https://github.com/OmniSharp/omnisharp-roslyn/wiki/Configuration-Options#formatting-options for more info.

@Hoffs
Copy link

Hoffs commented Jun 16, 2019

I see, well I tested another way by adding an option to the extension (similar to enableRoslynAnalyzers), but what I've found out is that code actions don't work correctly. It seems OmniSharp server is returning what I believe incorrect identifier:

{
        "Identifier": "NamingStyleCodeFixProvider",
        "Name": "Fix Name Violation: _mapper"
}

Where as in tests comitted here the request to fix it is sent in same fashion as the message:

var runRequest = new RunCodeActionRequest
                {
                    Line = 2,
                    Column = 31,
                    FileName = testFile.FileName,
                    Identifier = "Create and initialize field 'xxx_something'",
                    WantsTextChanges = false,
                    WantsAllCodeActionOperations = true,
                    Buffer = testFile.Content.Code
                };

where other test suggest that identifier is identical to text

Assert.Contains(getResponse.CodeActions, f => f.Name == "Create and initialize field 'xxx_something'");

Similarly other actions return identifier in same style.

{
        "Identifier": "Encapsulate field: 'mapper' (and use property)",
        "Name": "Encapsulate field: 'mapper' (and use property)"
}

So my guess is that the NamingStyleCodeFixProvider code action identifiers are returned incorrectly for them to work properly.

@filipw
Copy link
Member Author

filipw commented Jun 16, 2019

I see, well I tested another way by adding an option to the extension (similar to enableRoslynAnalyzers)

yes the intent is the feature is opt-in in the beginning (until we gather more feedback and polish it off)

So my guess is that the NamingStyleCodeFixProvider code action identifiers are returned incorrectly for them to work properly.

That's a good catch, however this is not really caused by this PR. The code fixes that use EquivalenceKey have actually never worked in OmniSharp 😀
That said I can have a look at fixing it in this PR too, but we may also handle this separately.

@filipw
Copy link
Member Author

filipw commented Jun 16, 2019

That's a good catch, however this is not really caused by this PR. The code fixes that use EquivalenceKey have actually never worked in OmniSharp 😀
That said I can have a look at fixing it in this PR too, but we may also handle this separately.

Actually I had a quick look and this is caused by a sigh null reference in the Roslyn code, will need to dig a bit deeper 😅

@Hoffs
Copy link

Hoffs commented Jun 16, 2019

Yeah, code action does fail due to NullReference, just was guessing that identifier bit was the cause

System.NullReferenceException: Object reference not set to an instance of an object.
  at Microsoft.CodeAnalysis.CodeFixes.NamingStyles.NamingStyleCodeFixProvider.FixNameCodeAction.<ComputeOperationsAsync>d__8.MoveNext()

@filipw
Copy link
Member Author

filipw commented Jun 16, 2019

@Hoffs can you check again after the latest commits? it should be fixed now

@Hoffs
Copy link

Hoffs commented Jun 16, 2019

Just did, it worked this time

{
[MetadataAttribute]
[AttributeUsage(AttributeTargets.Class)]
public class ExportWorkspaceServiceFactoryWithAssemblyQualifiedNameAttribute : ExportAttribute
Copy link
Member Author

Choose a reason for hiding this comment

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

cc @savpek needed to pull this in 😀
used the same name so that there are no conflicts

OptionSet Process(OptionSet currentOptionSet, OmniSharpOptions omniSharpOptions);
OptionSet Process(OptionSet currentOptionSet, OmniSharpOptions omniSharpOptions, IOmniSharpEnvironment omnisharpEnvironment);

int Order { get; }
Copy link
Member

Choose a reason for hiding this comment

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

Public api change... but I guess we don't publish packages yet... so we're good.

Copy link
Member Author

Choose a reason for hiding this comment

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

indeed. this will be one of the key extensibility points of OmniSharp so we have to get it right, hopefully now it's good, it's changed 3 times already to facilitate various features 😀we do have 3 feature implementations on top of it already too

{
[Shared]
[ExportWorkspaceServiceFactoryWithAssemblyQualifiedName("Microsoft.CodeAnalysis.Features", "Microsoft.CodeAnalysis.CodeActions.WorkspaceServices.ISymbolRenamedCodeActionOperationFactoryWorkspaceService")]
public class OmniSharpSymbolRenamedCodeActionOperationFactoryWorkspaceServiceFactory : IWorkspaceServiceFactory
Copy link
Member Author

Choose a reason for hiding this comment

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

Just to explain what is going on here - we need to do this because NamingStyleCodeFixProvider internally tries to resolve ISymbolRenamedCodeActionOperationFactoryWorkspaceService. That doesn't ship together with Roslyn but instead is only implemented inside Microsoft.VisualStudio.LanguageServices, which we can't really reference (it has the whole tree of VS specific GUI dependencies).

On the other hand, ISymbolRenamedCodeActionOperationFactoryWorkspaceService is internal, so implementing is by hand requires a bit twisting

@jasonmalinowski
Copy link
Contributor

The approach is adapted from how https://github.com/dotnet/format works (although it only deals with formatting, not with code style/naming conventions/analyzers).

Do note that we're currently in the progress of changing how dotnet-format works. We've been working to update Roslyn to support .editorconfig parsing natively in the compiler and thus won't be reliant on the VS parsing library. I'm not sure what that means for PR, as much as be aware we consider dotnet-format to be an example of how not to do it. 😄

@filipw
Copy link
Member Author

filipw commented Jun 17, 2019

@jasonmalinowski unfortunately OmniSharp has to flex quite a lot to make things work in different places (this is just another case) - we've been chasing various internal APIs of Roslyn for a long time, so I think we'll just keep adapting as things change, as always 😀

Copy link
Member

@bjorkstromm bjorkstromm left a comment

Choose a reason for hiding this comment

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

Code and tests look good! Will take it for a spin tomorrow, great work! 💪

@jasonmalinowski
Copy link
Contributor

@filipw The good news is this time we actually thought about you all and are happy to make changes accordingly. 😄

@bjorkstromm
Copy link
Member

Had a chance to play with this. Works really great and it's super powerful when analyzers support is enabled! Thank you!

@rchande
Copy link

rchande commented Jun 18, 2019

@filipw Note that @jasonmalinowski and I have some time scheduled tomorrow to go over the new APIs and talk about how OmniSharp should use them. Might be nice if you could hold off merging until then... :)

@filipw
Copy link
Member Author

filipw commented Jun 18, 2019

we can of course wait for the APIs but personally I'd like to be able to already use this from the "omnisharp": "latest" just so that we can dogfood this and gather some feedback (note that it's all behind an opt-in flag), at the end of the day we can always adapt and change when needed. Not to mention there are plenty other "proper" APIs we are not consuming yet (completion API, quick info etc) too

@filipw
Copy link
Member Author

filipw commented Jun 22, 2019

rebased on top of latest master

@david-driscoll david-driscoll merged commit 100e73f into OmniSharp:master Jun 23, 2019
@Hoffs
Copy link

Hoffs commented Jun 23, 2019

When is this expected to release or rather when is the next planned release of ombisharp-roslyn?

@ExceptionGit
Copy link

Thanks, but how it's work?

I installed the latest build omnisharp-vscode v1.21.0-beta2 and enabled these flags:
"omnisharp.enableRoslynAnalyzers": true
"omnisharp.enableEditorConfigSupport": true dotnet/vscode-csharp#3136
"omnisharp.path": "latest"

I can't get warning:

-root
--.editorconfig
--proj1
---proj1.csproj
---Program.cs


Peogram.cs
...
FileInfo fi = new FileInfo(path);
Console.WriteLine(fi.FullName);
...


.editorconfig
...
root = true
[*.cs]
# https://github.com/MicrosoftDocs/visualstudio-docs/blob/master/docs/ide/editorconfig-language-conventions.md#csharp_style_var_when_type_is_apparent
csharp_style_var_when_type_is_apparent = true:warning
...

@filipw
Copy link
Member Author

filipw commented Jul 12, 2019

can you please provide your omnisharp log and a repro project?

This is what I see with your configuration using omnisharp-vscode v1.21.0-beta2

Screenshot 2019-07-12 09 07 05

@ExceptionGit
Copy link

My root folder ${workspaceFolder} name [Foo] bar contains the characters []. If I rename to foobar, everything will work.

  1. create folder [Foo] bar & open
  2. dotnet new console -n Proj1
  3. put .editorconfig
  4. open via VSCode [Foo] bar folder
  5. no warning

@filipw
Copy link
Member Author

filipw commented Jul 12, 2019

@ExceptionGit thanks - I have created a new issue based on your comment #1551
you are correct, when the folder contains [] characters, the editorconfig settings are not discovered.

We actually use an external component - Microsoft.VisualStudio.CodingConventions to do this discovery, and it's really a bug there.
In fact, I just checked, it doesn't work in Visual Studio too. Nevertheless we will use #1551 for tracking this.

@nickspoons
Copy link
Member

This is really great, thanks @filipw!

After some experimentation however, it seems like .editorconfig settings aren't being passed through to analyzers, is that right? Using roslynator, omnisharp.json settings appear to be passed through to the analyzer and affect diagnostics and code actions, but not .editorconfig settings.

Example:

// Program.cs
public class Program
{
   public string Something { get; set; }
}
# .editorconfig
root = true
[*.cs]
indent_size = 3
// omnisharp.json
{
   "FormattingOptions": {
      "EnableEditorConfigSupport": true,
      "IndentationSize": 2
   },
   "RoslynExtensionsOptions": {
      "EnableAnalyzersSupport": true,
      "LocationPaths": [
         "C:/Users/Nick/.omnisharp/roslynator"
      ]
   }
}

If I now Format Document, OmniSharp-roslyn correctly sets the indent to 3 (as specified by .editorconfig). However, if I move the cursor to the beginning of line 2 there is a "Fix formatting" code action available from roslynator, and running this sets the indent back to 2 (as specified by omnisharp.json).

Removing the "IndentationSize" line entirely from omnisharp.json results in roslynator using the default of 4.

Similar things occur with newlines, where roslynator applies omnisharp.json preferences and O#-roslyn applies .editorconfig preferences.

xtqqczze added a commit to xtqqczze/PowerShell-PowerShell that referenced this pull request Jan 19, 2020
Enable support for EditorConfig in OmniSharp. Since omnisharp-roslyn version 1.33.0, OmniSharp has had this support.

* https://github.com/OmniSharp/omnisharp-roslyn/blob/master/CHANGELOG.md#1330---2019-07-01
* PowerShell#7357
* OmniSharp/omnisharp-roslyn#1526
@ramosisw
Copy link

can someone help me ?, I have added the file .editorconfig with following lines

root = true

[*.cs]
dotnet_naming_style.instance_field_style.required_prefix = _

also enabled the option

{
    "omnisharp.enableEditorConfigSupport": true
}

in settings.json of vs code, but omnisharp does not consider it.

I wait for the following result:

Assert.Contains(getResponse.CodeActions, f => f.Name == "Create and assign field 'xxx_something'");

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
10 participants