I'm experimenting with using doxygen on a C# application, however I'm having issues with the REFERENCES_RELATION option. For example, I have the following method:
class ControlManager
{
...
public void SendRequest(IRequest req)
{
Log.WriteLine("Received request {0}", req.Name);
requestChain.Handle(req);
}
private MessageChain<IRequest> requestChain = ...;
}
Which generates the following references line in the html docs:
"References IRequest.Name, ControlManager.requestChain, and Log.WriteLine()."
What I really want it to show is:
"References IRequest.Name, MessageChain.Handle() and Log.Writeline()."
It seems that as Doxygen doesn't see it as a method reference, which means that the MessageChain.Handle docs also doesn't show it as referenced by anything (using the REFERENCED_BY_RELATION option). Is there any way of doing this, or have I missed some key reason why the class field is treated differently from the method parameter?
I'm not sure if it matters but ControlManager and MessageChain are in different namespaces and different projects.
ControlManager and MessageChain are in different namespaces and different projects.
Do MessageChain and ControlManager classes are parsed using the same doxygen project file ?
If not, as doxygen can't generate a documentation for one of your class, the reference won't be added.
Related
I am currently using the RazorEngine library to create html Email templates. Code:
var result = Engine.Razor.RunCompile(File.ReadAllText(templateFilePath), key, typeof(T), data);
Within the template itself I want to get access to some of the helper methods provided by MVC, namely #Html.Format(string string);
However Whenever I add said line of code in the template is:
Errors while compiling a Template.
Please try the following to solve the situation: * If the problem is
about missing/invalid references or multiple defines either try to
load
the missing references manually (in the compiling appdomain!) or
Specify your references manually by providing your own IReferenceResolver implementation.
See https://antaris.github.io/RazorEngine/ReferenceResolver.html for details.
Currently all references have to be available as files! * If you get 'class' does not contain a definition for 'member':
try another modelType (for example 'null' to make the model dynamic).
NOTE: You CANNOT use typeof(dynamic) to make the model dynamic!
Or try to use static instead of anonymous/dynamic types. More details about the error:
- error: (180, 97) The name 'Html' does not exist in the current context
Which does explain that I need to reference the right namespace. Question is, what do I reference and where?
Are you looking for HtmlHelper in System.Web.Mvc ?
Ref: https://msdn.microsoft.com/en-us/library/system.web.mvc.htmlhelper_methods(v=vs.118).aspx
Example, the EntityFramework Microsoft.EntityFrameworkCore.Relational project has the following text in the resource files:
...
<data name="FromSqlMissingColumn" xml:space="preserve">
<value>The required column '{column}' was not present in the results of a 'FromSql' operation.</value>
</data>
...
which generates the following C# code:
...
/// <summary>
/// The required column '{column}' was not present in the results of a 'FromSql' operation.
/// </summary>
public static string FromSqlMissingColumn([CanBeNull] object column)
{
return string.Format(CultureInfo.CurrentCulture, GetString("FromSqlMissingColumn", "column"), column);
}
...
private static string GetString(string name, params string[] formatterNames)
{
var value = _resourceManager.GetString(name);
Debug.Assert(value != null);
if (formatterNames != null)
{
for (var i = 0; i < formatterNames.Length; i++)
{
value = value.Replace("{" + formatterNames[i] + "}", "{" + i + "}");
}
}
return value;
}
...
But when I edit the file in VS and save it, I get only simple properties generated, like:
...
/// <summary>
/// The required column '{column}' was not present in the results of a 'FromSql' operation.
/// </summary>
public static string FromSqlMissingColumn
{
get { return ResourceManager.GetString("FromSqlMissingColumn"); }
}
...
The files in question can be found here:
RelationalStrings.resx
RelationalStrings.Designer.cs
So the question again - How did they do it, and how could I get the same result?
How did they do it?
First it should be obvious that they don't use the standard ResXFileCodeGenerator, but some custom code generation tool.
Currently there are 2 standard ways of generating code - the old school way using a Custom Tool similar to ResXFileCodeGenerator, or the modern way using a T4 Template. So let see.
The correspondig entry inside the Microsoft.EntityFrameworkCore.Relational.csproj file looks like this:
<ItemGroup>
<EmbeddedResource Include="Properties\RelationalStrings.resx">
<LogicalName>Microsoft.EntityFrameworkCore.Relational.Properties.RelationalStrings.resources</LogicalName>
</EmbeddedResource>
</ItemGroup>
As we can see, definitely they do not use Custom Tool.
So it should be a T4 template. And indeed right after the above item we can see:
<ItemGroup>
<Content Include="..\..\tools\Resources.tt">
<Link>Properties\Resources.tt</Link>
<Generator>TextTemplatingFileGenerator</Generator>
<LastGenOutput>Resources.cs</LastGenOutput>
<CustomToolNamespace>Microsoft.EntityFrameworkCore.Internal</CustomToolNamespace>
</Content>
<Content Include="Properties\Microsoft.EntityFrameworkCore.Relational.rd.xml" />
</ItemGroup>
So there you go!
Now, I don't know what's the purpose of the included xml file without diving into the implementation (it might be something that is used by the generator, like options or something), but the actual code generation is contained in the following Resources.tt file.
how could I get the same result?
I guess you are asking for your own projects. Well, you can do something similar. Select your resx file, go to Properties and clear the Custom Tool. Then add T4 template to your project and write the code generation (I'm not sure if the license allows you to use their code, so if you want to do so, make sure you first check if it is allowed). But the principle would be the same.
I think, EF team uses own custom Custom Tool for that purposes. But visual studio uses PublicResXFileCodeGenerator as a default custom tool for .resx files and this tool have no such functionality as PublicResXFileCodeGenerator and it's base class ResXFileCodeGenerator (both can be found in Microsoft.VisualStudio.Design assembly) is just a wrappers for visual studio around StronglyTypedResourceBuilder.
They are implement IVsSingleFileGenerator (located in Microsoft.VisualStudio.Shell.Interop assembly). So this is the place you can start implementing your own Custom Tool. Start new Class Library, add Microsoft.VisualStudio.Shell.14.0 and Microsoft.VisualStudio.Shell.Interop references. Create new class and implement this interface. Interface IVsSingleFileGenerator is pretty simple. It contains only two methods:
DefaultExtension which returns extension for generated file (with a leading period) as out string pbstrDefaultExtension paratemer and VSConstant.S_OK as return value (of course if everything is OK).
Generate which is accept:
wszInputFilePath - input file path, may be null, do not use it.
bstrInputFileContents - input file content, should be used.
wszDefaultNamespace - default namespace (cannot tell right now why ResXFileCodeGenerator interops with Visual Studio to get namespace instead of using this parameter).
rgbOutputFileContents - array of bytes of generated file. You must include UNICODE or UTF-8 signature bytes in the returned byte array, as this is a raw stream. The memory for rgbOutputFileContents must be allocated using the .NET Framework call, Marshal.AllocCoTaskMem, or the equivalent Win32 system call, CoTaskMemAlloc. The project system is responsible for freeing this memory.
pcbOutput - count of bytes in the rgbOutputFileContent array.
pGenerateProgress - A reference to the IVsGeneratorProgress interface through which the generator can report its progress to the project system.
And returns VSConstant.S_OK if everything is fine, or corresponding error code.
Also there is small guide about implementation. But this guide does not tells too much. Most useful thing is how to register own generator.
You'd better dive into ResXFileCodeGenerator code (or simply decompile) for implementation example or to get some hints such as how to interop with visual studio. But I see no reason to interop with VS as everything you need you already provided. .resx file content could be read by ResXResourceReader.FromFileContents.
Rest of all would be simple as you have resource names and values and only need to return array of bytes of generated file. I think, parsing resource values to have a compilation-time error of invalid format (e.g.: {{param}}}) would be the biggest difficulty.
When values parsed and parameters for your upcoming method found you can generate code (again as an example you can refer to ResXFileCodeGenerator and StronglyTypedResourceBuilder, or do it by your self the way you want, via CodeDom or compose source code text manually). It is also should not be difficult as you already has an example of the methods you need to generate in the question you posted.
Compile your own generator, register it, set it in Custom Tool property of your .resx files and you will get resources classes with methods instead of properties.
Also you could share it on github with others. :)
Here is instruction from custom tool registration (as msdn link could die soon or later):
To make a custom tool available in Visual Studio, you must register it so Visual Studio can instantiate it and associates it with a particular project type.
Register the custom tool DLL either in the Visual Studio local registry or in the system registry, under HKEY_CLASSES_ROOT.
For example, here's the registration information for the managed MSDataSetGenerator custom tool, which comes with Visual Studio:
[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\VisualStudio\14.0\CLSID\{E76D53CC-3D4F-40A2-BD4D-4F3419755476}]
#="COM+ class: Microsoft.VSDesigner.CodeGenerator.TypedDataSourceGenerator.DataSourceGeneratorWrapper"
"InprocServer32"="C:\\WINDOWS\\system32\\mscoree.dll"
"ThreadingModel"="Both"
"Class"="Microsoft.VSDesigner.CodeGenerator.TypedDataSourceGenerator.DataSourceGeneratorWrapper"
"Assembly"="Microsoft.VSDesigner, Version=14.0.0.0, Culture=Neutral, PublicKeyToken=b03f5f7f11d50a3a"
Create a registry key in the desired Visual Studio hive under Generators\GUID where GUID is the GUID defined by the specific language's project system or service. The name of the key becomes the programmatic name of your custom tool. The custom tool key has the following values:
(Default) - Optional. Provides a user-friendly description of the custom tool. This parameter is optional, but recommended.
CLSID - Required. Specifies the identifier of the class library of the COM component that implements IVsSingleFileGenerator.
GeneratesDesignTimeSource - Required. Indicates whether types from files produced by this custom tool are made available to visual designers. The value of this parameter needs to be (zero) 0 for types not available to visual designers or (one) 1 for types available to visual designers.
Note, you must register the custom tool separately for each language for which you want the custom tool to be available.
For example, the MSDataSetGenerator registers itself once for each language:
[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\VisualStudio\14.0\Generators\{164b10b9-b200-11d0-8c61-00a0c91e29d5}\MSDataSetGenerator]
#="Microsoft VB Code Generator for XSD"
"CLSID"="{E76D53CC-3D4F-40a2-BD4D-4F3419755476}"
"GeneratesDesignTimeSource"=dword:00000001
[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\VisualStudio\14.0\Generators\{fae04ec1-301f-11d3-bf4b-00c04f79efbc}\MSDataSetGenerator]
#="Microsoft C# Code Generator for XSD"
"CLSID"="{E76D53CC-3D4F-40a2-BD4D-4F3419755476}"
"GeneratesDesignTimeSource"=dword:00000001
[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\VisualStudio\14.0\Generators\{e6fdf8b0-f3d1-11d4-8576-0002a516ece8}\MSDataSetGenerator]
#="Microsoft J# Code Generator for XSD"
"CLSID"="{E76D53CC-3D4F-40a2-BD4D-4F3419755476}"
"GeneratesDesignTimeSource"=dword:00000001
I'm working on a semantic highlighting plugin for VS. Here you can see a web Example.
The goal:
Acquiring all variables and creating different Classifications for every one of them.
The problem:
Getting the variables from the code without writing a C# lexer.
My current approach uses an ITagger. I use an ITagAggregator to get the tags of all the spans that get passed to the ITagger. Then I filter those and get only spans with the "identifier" classification which includes varibles, methods names, class names, usings and properties.
public class Classifier : ITagger<ClassificationTag> {
public IEnumerable<ITagSpan<ClassificationTag>> GetTags(NormalizedSnapshotSpanCollection spans) {
ITextSnapshot snapshot = spans[0].Snapshot;
var tags = _aggregator.GetTags(spans).Where((span) => span.Tag.ClassificationType.Classification.Equals("identifier")).ToArray();
foreach(var classifiedSpan in tags) {
foreach(SnapshotSpan span in classifiedSpan.Span.GetSpans(snapshot)) {
//generate classification based on variable name
yield return new TagSpan<ClassificationTag>(span, new ClassificationTag(_classification));
}
}
}
}
It would be a lot easier to use the builtin C# Lexer to get a list of all variables bundled to a bunch of meta data. Is this data available for plugin development? Is there an alternative way I could acquire it, if not?
The problem: Getting the variables from the code without writing a C# lexer.
Roslyn can do this: https://roslyn.codeplex.com/
There's even a Syntax Visualizer sample that might interest you. I also found an example using Roslyn to create a Syntax Highlighter.
Visual Studio exposes that information as a code model.
Here is an example how you can access class, and then find attribute on the class, and parse attribute arguments:
Accessing attribute info from DTE
Here is more information about code models:
http://msdn.microsoft.com/en-us/library/ms228763.aspx
Here's also automation object model chart what I've been using quite few times: http://msdn.microsoft.com/en-us/library/za2b25t3.aspx
Also, as said, Roslyn is indeed also a possible option. Here is an example for VS2015 using roslyn: https://github.com/tomasr/roslyn-colorizer/blob/master/RoslynColorizer/RoslynColorizer.cs
For building language tools if may be better to use a parser generator for C#. The GOLD parsing system is one such toolkit which can handle LALR grammars. It has a .NET component based engine that you can use in your project and it can be used to integrate with any IDE. You can also find the grammars for various programming languages including C#.
I like how String.Format uses arguments to inject variables in to the string it is formatting. This is called Composite Formating and is discussed by MSDN here.
I want this functionality with my logging facade:
string foo = "fancy";
string bar = "message";
log.Debug("My {0} log {1}.", foo, bar)
My ILoggerFacade has the following method signature:
void Debug<T>(T message, params Object[] args);
And, I know I can implement this quite simply:
ILog m_Log = \\some logging implementation
public void Debug<T>(T message, params Object[] args)
{
m_Log.Debug(String.Format(message, args));
}
However, in Visual Studio I don't get the fancy highlighting of the {0}, {1}, ... arguments:
I guess it is ReSharper who is resposible for them, and it seems like it is just ignoring the formatting arguments and giving no "intellisense" help. This isn't good since the other developers who will be using the facade will be expecting this.
How do I get argument highlighting and "intellisense" for custom formatted methods similar to how these work:
Console.WriteLine(...)
String.Format(...)
etc...
Any help would be appreciated.
Check out ReSharpers External Annotations. Specifically, you want to use StringFormatMethodAttribute for this.
To use the External Annotations there are actually 3 methods. Two that it spells out, and one that you have to read between the lines to see.
Reference "JetBrains.Annotations.dll". I would recommend against this one. I don't like the idea of copying the DLL, or having to reference the ReSharper install directory. This could cause issues if you upgrade or re-install.
Copying and pasting attribute declarations into your solution. I'd recommend this as it gives you more control. Additionally, you can get rid of ReSharper (why would anyone do this? Stranger things have happened, I guess.), and still provide this feature to anyone that consumes your library. There are step by step instructions on how to do this in the first link.
Create an XML file, similar to what it uses for for the .NET Assemblies. I did this for the Silverlight Unit Test Framework. ReSharper does not recognize these tests by default.
To do this
Create a file name <assembly>.xml and put it in "ReSharper\vXX\Bin\ExternalAnnotations".
Add a root element "<assembly name="<assembly>">
Now add <member> elements for each member that you want to give an attribute.
I do not recommend doing this for your own code. However, if you have an assembly that you want to have this functionality, but cannot edit, this is the way to do it. This will only apply on your machine and each developer that uses the assembly will need to copy the xml file.
Basically what I'm hoping for is something that would work like how the Obsolete attribute works with Intellisense and strikes the method text when typing out the name. What I'm looking for is an attribute that blocks the method from being seen with the assembly it's defined. Kind of like an reverse internal. Using 3.5 by the by.
Yeah sounds odd but if you need the reason why, here it is:
My current solution for lazy loading in entity framework involves having the generated many to one or one to one properties be internal and have a facade? property that is public and basically loads the internal property's value:
public ChatRoom ParentRoom
{
get
{
if(!ParentRoomInnerReference.IsLoaded)
{
ParentRoomInnerReference.Load();
}
return ParentRoomInner;
}
set
{
ParentRoomInner = value;
}
}
Problem with this is if someone tries to use the ParentRoom property in a query:
context.ChatItem.Where(item => item.ParentRoom.Id = someId)
This will blow up since it doesn't know what to do with the facade property when evaluating the expression. This isn't a huge problem since the ParentRoomInner property can be used and queries are only in the entity assembly. (IE no selects and such in the UI assembly) The only situation comes in the entity assembly since it can see both properties and it's possible that someone might forget and use the above query and blow up at runtime.
So it would be nice if there were an attribute or some way to stop the entity assembly from seeing (ie blocked by intellisense) the outward facing properties.
Basically inside the assembly see ParentRoomInner. Outside the assembly see ParentRoom. Going to guess this isn't possible but worth a try.
I do see that there is an attribute
for stopping methods from being
viewable
(System.ComponentModel.EditorBrowsable)
but it's choices are rather slim and
don't really help.
You can use the EditorBrowsableAttribute for this:
[System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)]
public void MyMethod() {}
One thing to know, though: In c#, you will still get intellisense on the method if it is in the same assembly as the one you are working in. Someone referencing your assembly (or your project, for a project reference) will not see it though. You can also pass EditorBrowsableState.Advanced, and then you will only get intellisense if c# if you clear the HideAdvancedMembers option in Tools Options.
I haven't heard of a good way to do this in plain .NET. But, here are some ideas. Maybe one of them will work, or set you off in a direction that will be helpful.
Use FxCop, probably writing your own rule to make sure ParentRoom isn't called from the asslembly that defined it.
Look into the various post-processing projects for .NET (link design-by-contract).
Write some code inside your ParentRoom getter which will check the stack (using "new Stack()" or "new StackFrame(1)" to figure out whether the caller was from the same assembly. If so, either throw an exception or simply return ParentRoomInner.