Where to Start for Plugin; Do I Need a Plugin?

I have a specific set of problems that R# constantly reminds me of currently, and I'd like to teach it how to ignore those inspections in a very specific case.

I'm really getting into using the FubuMVC framework. Razor support was added earlier this year, and that is great. However, R# complains about 2 things in my FubuMVC Razor views.

  1. R# tells me that my @model declaration has a redundant qualifier because I'm importing the same namespace the model is in above it. For example, the following snippet should explain the situation:
    @using TestSite.Features.Customers
    @model TestSite.Features.Customers.ListCustomersViewModel
  2. R# also informs me that it cannot resolve any of my Razor view's sections (e.g., @section Title).


Both of the above inspections are correct, strictly speaking. FubuMVC requires that the @model be fully qualified; regardless of any @using for the same namespace. R# also does not know how to resolve the master Razor template for my views, because FubuMVC uses a convention that looks for the master template at ~/Shared/Application.cshtml. So, R# does not know about this convention in order to understand that there actually is a section defined. I don't expect R# to work differently, out of the box.

However, I fully expect that I can write some exception to these inspections (i.e., ignore them both) when my project contains a reference to FubuMVC. I'm guessing I would need to write a plugin to handle such scenarios; is that correct?

Can anyone point me in the right direction for getting started with such a task? The documentation for the plugin SDK is lengthy, and a nudge in the right direction would be much appreciated. What interface(s) should I implement to get the functionality I'm after? How can I override normal inspection behavior? How can do so if the project references a particular assembly?

I plan on sharing any outcome plugin with the FubuMVC community, and the entire code with the R# community.


Thanks in advance,

-Matt

5 comments
Comment actions Permalink

Hi Matt. I don't think adding support for FubuMVC is going to be terribly straightforward, but on the face of it, it looks possible.

Given an MS MVC app, ReSharper knows about sections defined in _Layout.cshtml even when that file isn't referenced in the view you're currently looking at. This is because it's referenced in the _ViewStart.cshtml, which is included by convention.ReSharper knows about _ViewStart.cshtml because it's listed in the code. If you open the ReSharper dlls in dotPeek and take a look at RazorCSharpMvcTreeBuilder, you can see that it adds an IncludeFileName of RazorCodeGenerator.MVC_INCLUDE_FILE_NAME, which is "_ViewStart.cshtml".

I haven't verified this, but I think you could create your own tree builder for FubuMvc that gave an IncludeFileName of "_Application.cshtml". Once that is all wired up, ReSharper should include _Application.cshtml when parsing a view.

Wiring it up isn't for the faint hearted. You'd need to create a class similar to RazorCSharpMvcTreeBuilderFactory, and override CreateTreeBuilder and return your new tree builder class. Note that this class has an attribute of [Language(typeof(RazorCSharpMvcLanguage))]. I think you'll need to create a new language (e.g. RazorCSharpFubuMvcLanguage) and associate .cshtml files and this tree builder factory to that language. So, firstly, create a class just like RazorCSharpMvcLanguage, and add the [Language(typeof(RazorCSharpFubuMvcLanguage))] attribute to the tree builder factory.

To associate the language with .cshtml files, we'll need to create a ProjectFileLanguageService. This will look remarkably like RazorCSharpMvcProjectFileLanguageService - I'd swap the checks for MVC3 to be checks for FubuMvc, and make sure to return the FubuMvcLanguage for PsiLanguageType.Then I think we need to associate the language with a LanguageService (this does the actual parsing). And this will look like RazorCSharpMvcLanguageService (I think the MVC lexer adds the model keyword - it might be worth using this.)

I think that should do it.

As for the other point of overriding the warning for a fully qualified model reference, I'm still looking into that, and will let you know what I find.

Thanks
Matt

Message was edited by: Matt Ellis - fixed formatting

0
Comment actions Permalink

Also, is there any chance of a sample solution that reproduces the redundant code warning in the @model?

Thanks
Matt

0
Comment actions Permalink

Looking at the redundant name qualifier warning for the @model directive, we're going to have to get a little creative.

ReSharper doesn't have a specific API for suppressing warnings. But it does have support for ignoring #regions with names matching specific text (as defined in Options -> Generated Code) and allows you to disable specific warnings with comments. It implements this using the IFileStructureExplorer interface. This is what scans for the known regions, and looks for the comments, based on different file types.

It's not really intended to be used like this, but you can implement this interface in a plugin, and use it to scan the file, find the @model directive and disable the "RedundantNameQualifier" warning for the text range of the directive element. In effect, you'd be telling ReSharper not to include the "RedundantNameQualifier" warning within this text range, in exactly the same way as if it were wrapped with comments.

To implement it, create a class that implements IFileStructureExplorer and mark it with FileStructureExplorerAttribute. When Run is called, check to see if the source file is a .cshtml file (check IPsiSourceFile.LanguageType == RazorCSharpProjectFileType.Instance). Then check to see if the IFile parameter is an instance of IRazorFile. If so, you want to process this file. IFile is an instance of ITreeNode, so you should be able to call something like GetContainingNode<IRazorModelDirective>() to try and find the node that represents the @model directive. Once you've got that node, create a TreeTextRange from the node.GetTreeStartOffset and node.GetTextLength, then add it to an instance of OneToListMap<string, TreeTextRange> for IFileStructure.WarningDisableRange.

Again, I haven't tried this, but this looks like it will do what you're after. Let me know if you have any problems.

Thanks
Matt

0
Comment actions Permalink

Wow! I had a feeling it would be a bit to accomplish. I sincerely appreciate your input on this. I'll consider if the effor is worth the gain.

Thanks again!


-Matt

0
Comment actions Permalink

Matt,

Also very helpful information. Thank you again.

Perhaps, this warrants a suggestion for suppression support in R#.next. I could even imagine some workflow that runs after other evaluations on code. Support for some post-processor that could then iterate over the generated warnings and remove any that are unwanted. I think I'll see if a suggestion already exists for this feature.


Regards,

-Matt

0

Please sign in to leave a comment.