I'm creating a Code Fix which turns the access modifier of detected methods public. The implementation is straightforward: remove all existing access modifiers and add public at the front. Afterwards I replace the node and return the solution.
This however results in a modifier list that looks like this: publicvirtual void Method(). On top of the modifiers being pasted against eachother, that line of code is wrongly indented. It looks like this:
[TestClass]
public class MyClass
{
[TestMethod]
publicvirtual void Method()
{
}
}
So as a solution I format the code instead. Using
var formattedMethod = Formatter.Format(newMethod,
newMethod.Modifiers.Span,
document.Project.Solution.Workspace,
document.Project.Solution.Workspace.Options);
I can format the modifiers but they are still wrongly indented:
[TestClass]
public class MyClass
{
[TestMethod]
public virtual void Method()
{
}
}
I assume this is because of trivia but prepending the formatted method with the original method's leading trivia does not make a difference. I want to avoid formatting the entire document because, well, this isn't an action to format the entire document.
The entire relevant implementation of this Code Fix:
private Task<Solution> MakePublicAsync(Document document, SyntaxNode root, MethodDeclarationSyntax method)
{
var removableModifiers = new[]
{
SyntaxFactory.Token(SyntaxKind.InternalKeyword),
SyntaxFactory.Token(SyntaxKind.ProtectedKeyword),
SyntaxFactory.Token(SyntaxKind.PrivateKeyword)
};
var modifierList = new SyntaxTokenList()
.Add(SyntaxFactory.Token(SyntaxKind.PublicKeyword))
.AddRange(method.Modifiers.Where(x => !removableModifiers.Select(y => y.RawKind).Contains(x.RawKind)));
var newMethod = method.WithModifiers(modifierList);
var formattedMethod = Formatter.Format(newMethod, newMethod.Modifiers.Span, document.Project.Solution.Workspace, document.Project.Solution.Workspace.Options);
var newRoot = root.ReplaceNode(method, formattedMethod.WithLeadingTrivia(method.GetLeadingTrivia()));
var newDocument = document.WithSyntaxRoot(newRoot);
return Task.FromResult(newDocument.Project.Solution);
}
Instead of calling Formatter.Format manually, just put the Formatter.Annotation on your fixed nodes, and the CodeFix engine will call it automatically for you.
The issue is that you need to call Format on the root of the tree, but specify the span of the tree you want formatted, otherwise the formatter will run on just the tree you pass in, with no context from its parent.
The problem was that I had my tests indented in the string representation itself, like this:
var original = #"
using System;
using System.Text;
namespace ConsoleApplication1
{
class MyClass
{
void Method(Nullable<int> myVar = 5)
{
}
}
}";
As you can see there's still a tab between the left margin and the actual code. Apparently the Roslyn formatter can't handle this scenario (which is, admittedly, not a common situation).
In a situation unlike this though, you're probably interested in the formatter which is why I'll accept Kevin's answer.
Related
I'm trying to serialize/deserialize an IEnumerator generated from a function using yield.
I would like to serialize the IEnumerator at any iteration, I don't want to force it to generate all of it values.
I know that the yield keyword generate a class behind the scene, and that why I'm using it, to avoid manually writing iterator, and also to make the code cleaner.
My goal is to make a small game engine similar to Nick Gravelyn - The magic of yield, where each GameElement generate an iterator about his behavior, allowing the programmer to easily control timings (because the iterator allow to interrupt and continue a script). I want to try to add a multiplayer layer on top of that, that why I need to serialize/deserialize an IEnumerator.
Forcing the IEnumerator to generate all of his values also force the game to update, this should be avoided.
My first tentative was somethings like :
using System;
using System.Collections.Generic;
using System.Text.Json;
namespace SerializeTest
{
class Program
{
public static IEnumerator<int> CountTo(int end)
{
for(int i = 1; i <= end; i++)
{
Console.WriteLine("i = " + i);
yield return i;
}
}
static void Main(string[] args)
{
IEnumerator<int> e = CountTo(5);
string json = JsonSerializer.Serialize(e);
Console.WriteLine(json);
var f = JsonSerializer.Deserialize<IEnumerator<int>> (json); // crash because interface
while (f.MoveNext());
}
}
}
But deserializing an interface (IEnumerator<int>) is illegal, we need to know the Type of the object behind IEnumerator<int>.
Since the type is generated by the compiler, I try to use reflection over the Deserialize method in order to call it with the correct type generated by the function CountTo() (SerializeTest.Program+<CountTo>d__0 if you are curious)
So my second tentative was somethings like :
using System;
using System.Collections.Generic;
using System.Reflection;
using System.Text.Json;
namespace SerializeTest
{
class Program
{
public static IEnumerator<int> CountTo(int end)
{
for(int i = 1; i <= end; i++)
{
Console.WriteLine("i = " + i);
yield return i;
}
}
public static T Deserialize<T>(string json) => JsonSerializer.Deserialize<T>(json); // still crash
static void Main(string[] args)
{
IEnumerator<int> e = CountTo(5);
string json = JsonSerializer.Serialize(e);
Console.WriteLine(json);
Type eType = e.GetType();
MethodInfo method = typeof(Program).GetMethod("Deserialize");
MethodInfo genericMethod = method.MakeGenericMethod(eType);
object fObj = genericMethod.Invoke(null, new object[] { json });
var f = (IEnumerator<int>)fObj;
while (f.MoveNext()) ;
}
}
}
but it still crash when calling JsonSerializer.Deserialize() even if the type is now correct and not an interface.
(System.InvalidOperationException : 'Each parameter in constructor 'Void .ctor(Int32)' on type 'SerializeTest.Program+<CountTo>d__0' must bind to an object property or field on deserialization. Each parameter name must match with a property or field on the object. The match can be case-insensitive.'
)
The format don't have any importance (json, xml...) as long as it is possible to send it to another computer and deserialize it back.
I naively hope that there is a way to Serialize/Deserialize an IEnumerator because a lot of cool stuff can be done with it, although I have some serious doubts about such a things to be possible.
Thank for reading so far.
I naively hope that there is a way to Serialize/Deserialize an IEnumerator because a lot of cool stuff can be done with it, although I have some serious doubts about such a things to be possible.
Sending code (and enumerator state machine generated by the compiler which you are trying to send is basically a code) is far more complex then just serializing it to json/xml/etc. For example Apache Ignite supports sending code to another nodes including assembly peer loading. One of the starting points for investigation can be here.
As for your attempt, as you should have already seen that serialized version of the state machine contains only one one property - Current: {"Current":0} while the generated class contains some other state data looking something like this:
private sealed class <<<Main>$>g__CountTo|0_0>d : IEnumerator<int>, IEnumerator, IDisposable
{
private int <>1__state;
private int <>2__current;
public int end;
private int <i>5__1;
int IEnumerator<int>.Current
{
[DebuggerHidden]
get
{
return <>2__current;
}
}
// ... rest of the generated code
}
While you can look into writing your own custom converter that will use reflection to actually serialize/deserialize the internal state data (that is possibly not that hard), it can be quite brittle (class names can change, the generated code can change, the code generator code can change, and we have not even started with multiversion client support), in the end those fields a private for a reason, so I suggest looking into some set of messages the multiplayer clients will send to each other and you can turn them into iterators, classes, method calls, etc.
I'm new to NSubstitute and trying to fake an existing class named OrgDataWS. This class has a method named GetDataSet:
public XmlElement GetDataSet(int token)
{
string perfLogMessage = string.Format("Org.GetDataSet: {0}", Guid.NewGuid().ToString());
MultiMessagePerformanceCounter performanceCounter = MultiMessagePerformanceCounter.StartNew(perfLogMessage);
XmlElement result = orgDataManager.GetDataSet(token);
performanceCounter.Stop();
return result;
}
The following is my test methods:
[TestMethod]
public void GetDataSetTest()
{
var dataWSStub = Substitute.For<OrgDataWS>();
var orgManagerStub = Substitute.For<OrgDataManager>();
var document = new XmlDocument();
var xmlElement = document.CreateElement("a");
orgManagerStub.GetDataSet(Arg.Any<int>()).Returns<XmlElement>(xmlElement);
dataWSStub.OrgDataManager = orgManagerStub;
var result = dataWSStub.GetDataSet(99);
}
However, when I run my test methods, this line
orgManagerStub.GetDataSet(Arg.Any<int>()).Returns<XmlElement>(xmlElement);
threw an exception. This exception is from the implementation of OrgDataManager class, from my understanding, this is not supposed to happen. The purpose of using that clause is that I hope if the orgManagerStub's DataDataSet method is invoked with any Int parameter, just return my xmlElement instance. I didn't hope my code to run the detailed implementation of OrgDataManager.
What's wrong with my test code? How to fix it?
As per the documentation:
Warning: Substituting for classes can have some nasty side-effects. For starters, NSubstitute can only work with virtual members of the class, so any non-virtual code in the class will actually execute! If you try to substitute for your class that formats your hard drive in the constructor or in a non-virtual property setter then you’re asking for trouble. If possible, stick to substituting interfaces.
(my emphasis)
The declaration you showed is not virtual, so the solution is to either create an interface for it, and substitute for that, or at least make that method virtual (and possibly other methods as well).
I'm trying to add custom coloring for only certain keywords in my Visual Studio editor for C# code. I want to be able to color any type that implements IDisposable as a different color. Ideally I'd like to create a simple list of classes/interfaces that derive from IDisposable in some sort of configuration that I can edit. (Although if you said there was a method/plugin that would automatically find all disposable types and color them independently that would be the Holy Grail).
I've done a ton of research and it looks like an "editor classifier" extension might do the trick. However I created one that merely tries to color the word "Stream" and although it does hit my code that attempts to highlight that word, it does not end up highlighted in the editor.
I have added my VS extension to Github here
This really seems like this should be fairly straightforward but I have gone down many alleys on this one only to find dead-ends. Is there a simpler way to do this, or is my extension broken?
Update
Very strange. I just ran my extension again and although it does not highlight the text in the editor it highlights all instances of "Stream" in the popup text when you hover over a type/variable! Is there any way to get it to apply to the editor?
Depending on wether you are using Jetbrains Resharper or not you may write a plugin for that. That way you are able not only to add visual notification of IDisposable on a variable but also provide quickfixes if, and only if, it is not beeing called, which is what i am assuming you want to catch. Mind you that i can imagine that there's already a R# plugin for that. I know i've considered this too, but i was too lazy to write a plugin for that.
Don't get me wrong btw - If you're not using r# yet you should consider trying it out.
Among others you'd be working with this: API-QuickFix
There are also ways to define custom keywords, as resharper does, given by a custom markup and apply quickfixes to that.
PS: No i don't work at jetbrains. it's just that good :)
UPDATE:
potential VS Extension fix?
check this one out: MSDN Link Highlighting Text
I tried opening your github project but couldn't so i thought i'll just check msdn instead. it seems you are deriving from the wrong class to fulfill your needs?
MSDN keyword "Editors - Extending the Editor - Walkthrough: Highlighting Text"
I know SO wants code on the site, but msdn links going down is rather unlikely and with the given information the content can be found easily enough :)
I'm a bit late to the party, but hey, why not throw my 2 cents in.
As you've explained in your question, your project has two basic parts:
Finding the classes that implement IDisposable
Highlighting them
The first is by far the hardest, though not impossible. A word-list based approach is probably the simplest, though it should be possible with Roslyn to figure out on the fly which classes inherit IDisposible.
You could also always resort to loading the project's compiled .exe/.dll in the background after a build and figuring out what the types are there, but you'd still have to write some magic glue code to figure out what short class names in the code referred to what actual full-name classes in the assembly.
The second part, highlighting, is quite easy once you know how to do it (it helps that I've spent the last several months working full-time on extending VS). Of course, with Visual Studio, nothing is as simple as it looks (despite the efforts of Microsoft to try to make it user-friendly). So, I've built a sample extension that highlights just classes named "Stream" within C# files to get you started.
The relevant code is below, and the full project source is on GitHub). It starts with a classification-tagger provider:
[Export(typeof(ITaggerProvider))]
[ContentType("CSharp")]
[TagType(typeof(ClassificationTag))]
[Name("HighlightDisposableTagger")]
public class HighlightDisposableTaggerProvider : ITaggerProvider
{
[Import]
private IClassificationTypeRegistryService _classificationRegistry = null;
[Import]
private IClassifierAggregatorService _classifierAggregator = null;
private bool _reentrant;
public ITagger<T> CreateTagger<T>(ITextBuffer buffer) where T : ITag
{
if (_reentrant)
return null;
try {
_reentrant = true;
var classifier = _classifierAggregator.GetClassifier(buffer);
return new HighlightDisposableTagger(buffer, _classificationRegistry, classifier) as ITagger<T>;
}
finally {
_reentrant = false;
}
}
}
Then the tagger itself:
public class HighlightDisposableTagger : ITagger<ClassificationTag>
{
private const string DisposableFormatName = "HighlightDisposableFormat";
[Export]
[Name(DisposableFormatName)]
public static ClassificationTypeDefinition DisposableFormatType = null;
[Export(typeof(EditorFormatDefinition))]
[Name(DisposableFormatName)]
[ClassificationType(ClassificationTypeNames = DisposableFormatName)]
[UserVisible(true)]
public class DisposableFormatDefinition : ClassificationFormatDefinition
{
public DisposableFormatDefinition()
{
DisplayName = "Disposable Format";
ForegroundColor = Color.FromRgb(0xFF, 0x00, 0x00);
}
}
public event EventHandler<SnapshotSpanEventArgs> TagsChanged = delegate { };
private ITextBuffer _subjectBuffer;
private ClassificationTag _tag;
private IClassifier _classifier;
private bool _reentrant;
public HighlightDisposableTagger(ITextBuffer subjectBuffer, IClassificationTypeRegistryService typeService, IClassifier classifier)
{
_subjectBuffer = subjectBuffer;
var classificationType = typeService.GetClassificationType(DisposableFormatName);
_tag = new ClassificationTag(classificationType);
_classifier = classifier;
}
public IEnumerable<ITagSpan<ClassificationTag>> GetTags(NormalizedSnapshotSpanCollection spans)
{
if (_reentrant) {
return Enumerable.Empty<ITagSpan<ClassificationTag>>();
}
var tags = new List<ITagSpan<ClassificationTag>>();
try {
_reentrant = true;
foreach (var span in spans) {
if (span.IsEmpty)
continue;
foreach (var token in _classifier.GetClassificationSpans(span)) {
if (token.ClassificationType.IsOfType(/*PredefinedClassificationTypeNames.Identifier*/ "User Types")) {
// TODO: Somehow figure out if this refers to a class which implements IDisposable
if (token.Span.GetText() == "Stream") {
tags.Add(new TagSpan<ClassificationTag>(token.Span, _tag));
}
}
}
}
return tags;
}
finally {
_reentrant = false;
}
}
}
I've only tested this on VS2010, but it should work for VS2013 too (the only thing that might be different is the class classification name, but that's easy to discover with a well-placed breakpoint). I've never written an extension for VS2012, so I can't comment on that, but I know it's quite close to VS2013 in most respects.
So, one possible solution(I believe this one works):
1) Create your own content type which inherits from csharp.
2) Create new TextViewCreationListener which will swap out all "csharp" content types with your own one, thus potentially "disarming" all the other classifiers.
3) Register your classifier to handle your own content type.
Here is some of the code:
[Export(typeof(IVsTextViewCreationListener))]
[ContentType("csharp")]
[TextViewRole(PredefinedTextViewRoles.Editable)]
class TextViewCreationListener : IVsTextViewCreationListener {
internal readonly IVsEditorAdaptersFactoryService _adaptersFactory;
[Import] internal IContentTypeRegistryService ContentTypeRegistryService = null;
[ImportingConstructor]
public TextViewCreationListener(IVsEditorAdaptersFactoryService adaptersFactory) {
_adaptersFactory = adaptersFactory;
}
#region IVsTextViewCreationListener Members
public void VsTextViewCreated(VisualStudio.TextManager.Interop.IVsTextView textViewAdapter) {
var textView = _adaptersFactory.GetWpfTextView(textViewAdapter);
var myContent = ContentTypeRegistryService.GetContentType(MyContentType);
if(myContent == null)
{
ContentTypeRegistryService.AddContentType(MyContentType, new[] {"csharp"});
myContent = ContentTypeRegistryService.GetContentType(MyContentType);
}
// some kind of check if the content type is not already MyContentType.
textView.TextBuffer.ChangeContentType(myContent, null);
}
#endregion
}
And now, just modify your IClassifierProvider to register with your own content type, as such: [ContentType(MyContentType)]
Iin your own IClassifier, you can basically do your own calculation and once you think you can't handle the stuff, you could pass the control to other classifiers.
If you use MEF and import IClassifierAggregatorService, you can get a "MASTER-classifier" which will run all the logic for you. I haven't implemented it yet, but I've suggestes something similiar in the past, and it seemed to work. Alternatively you could maybe use [ImportMany] with List<IClassifier> and filter out the csharp ones?!
I've been looking at some code in a debugger associated with Razor View engine and I noticed that some of the types appear in Debugger with a trailing dot character at the end of the type name e.g.:
{Nancy.ViewEngines.Razor.RazorViewEngine.}
Does anyone know what this indicates? It's not valid syntax to use it when specifying a cast on an object so I'm intrigued as to what it indicates within the debugger.
EDIT: As requested by #Damien_The_Unbeliever, screenshot of the variable in debugger:
And the code that I'm looking at:
public TCompiledView GetOrAdd<TCompiledView>(
ViewLocationResult viewLocationResult, Func<ViewLocationResult, TCompiledView> valueFactory)
{
TCompiledView compiledView = default(TCompiledView);
compiledView = (TCompiledView)this.cache.GetOrAdd(viewLocationResult, x => valueFactory(x));
To give a little more background, we're trying to add logging to our Nancy View Cache to investigate an intermittent issue with Razor Views throwing compilation errors, but that isn't really relevant to the question.
I've seen this happen when the variable/value is actually of a compiler generated type (e.g. for holding the "local variables" captured by a lambda, async, iterator, etc). The debugger (in various places) seems unable to display the actual class name.
E.g. this example program:
class Program
{
static void Main(string[] args)
{
var p = new Program();
p.DoStuff();
}
void DoStuff()
{
int i = 19;
Expression<Func<int>> j = () => i + 10;
var k = (((j.Body as BinaryExpression).Left as MemberExpression).Expression as ConstantExpression).Value;
Console.ReadLine();
}
}
With a breakpoint on Console.ReadLine(), you'll find the k class's type looks like Program. rather than Program+<>_DisplayClass0
Addition by Jeppe: This example is a slight simplification of the above, avoiding the expression tree. Looks at a delegate instance's Target which will be an instance of a generated class. For comparison also looks at an iterator block type:
using System;
using System.Collections.Generic;
static class Program
{
static void Main()
{
int i = 19; // to be captured by lambda, will become field on a generated class
Func<int> f = () => i;
var target = f.Target; // when debugging type looks like "Program."
Console.WriteLine(target.GetType().ToString()); // writes "Program+<>c__DisplayClass1"
var seq = GetSeq(); // when debugging type looks like "Program.GetSeq"
Console.WriteLine(seq.GetType().ToString()); // writes "Program+<GetSeq>d__3"
}
static IEnumerable<int> GetSeq() // returns "state machine" (iterator block)
{
yield return 42;
}
}
I am trying to serialse a fingerprint FMD to XML using the code below, but get an error:
Error: DPUruNet.DataResult`1[DPUruNet.Fmd] cannot be serialized
because it does not have a parameterless constructor.
public void storePrint(DataResult<Fmd> resultConversion)
{
//store fingerprint as byte and insert to server------------
using (StreamWriter myWriter = new StreamWriter("test.txt", false))
{
System.Xml.Serialization.XmlSerializer x = new System.Xml.Serialization.XmlSerializer(resultConversion.GetType());
x.Serialize(myWriter, resultConversion);
}
MessageBox.Show("Fingerprint Stored!");
//------------------------------------------------------------
}
private void OnCaptured(CaptureResult captureResult)
{
try
{
// Check capture quality and throw an error if bad.
if (!_sender.CheckCaptureResult(captureResult)) return;
count++;
DataResult<Fmd> resultConversion = FeatureExtraction.CreateFmdFromFid(captureResult.Data, Constants.Formats.Fmd.ANSI);
SendMessage(Action.SendMessage, "A finger was captured. \r\nCount: " + (count));
if (resultConversion.ResultCode != Constants.ResultCode.DP_SUCCESS)
{
_sender.Reset = true;
throw new Exception(resultConversion.ResultCode.ToString());
}
preenrollmentFmds.Add(resultConversion.Data);
//--------------------CALL METHOD
storePrint(resultConversion);
//
The class DataResult is being referenced, so I can not alter it
UPDATE
If you don't have access to the DataResult<T> class, then you might have to take a slightly different approach and wrap this class with a different, serializable one. You can find a full example here:
How can I XML Serialize a Sealed Class with No Parameterless Constructor?
Previous Answer
The error is clear; you just need to add a parameterless constructor to the DataResult<T> class:
public class DataResult<T>
{
// Add a default constructor (public visibility, no parameters)
public DataResult()
{
// You can still define a method body if you wish,
// no restrictions there. Just don't do anything that
// could jeopardize the (de)serialization.
}
}
As for the implications of adding a default constructor, without knowing what
FeatureExtraction.CreateFmdFromFid(...)
is doing to create the DataResult<Fmd>, it would be impossible to know whether it would cause any issues.
Thanks to Cory, that is a useful answer, however in this example there is another way of serializing using
tempFingerPrint = Fmd.SerializeXml(resultConversion.Data);
this is specific to the Digital Persona SDK