-
Notifications
You must be signed in to change notification settings - Fork 4.1k
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
Is it possible to add intellisense to dynamic object with something like JSDoc? #15974
Comments
Supporting low-effort libraries that return dynamic objects, what else could go wrong? |
Note that C# IntelliSense in Visual Studio is part of Roslyn. So, without support in Roslyn, there's no hope for |
What I would do is probably something like this public class MyDynamicBase : DynamicObject
{
private Dictionary<string, object> _properties = new Dictionary<string, object>();
public override bool TryGetMember(GetMemberBinder binder, out object result)
{
return _properties.TryGetValue(binder.Name, out result);
}
public override bool TrySetMember(SetMemberBinder binder, object value)
{
if (GetType().GetProperty(binder.Name) != null)
{
//you want this to stay sane
throw new InvalidOperationException("Setting member of incompatible type");
}
_properties[binder.Name] = value;
return true;
}
public dynamic More => this;
}
public class MyDynamicObject : MyDynamicBase
{
public string MyFixedProperty { get; set; }
} All statically typed stuff goes to |
FWIW, there is an extensibility point for the completion list that you can use to add members to the list: CompletionProvider |
I would like to propose one idea, using interface and generic attribute Not sure it possible but would be something like this interface IUser
{
string UserID { get; set; }
string Name { get; set; }
}
[DynamicProperty<IUser>]
dynamic obj = LoadUserFromDB();
obj.User :|[intellisense show UserID]|: |
@DustinCampbell If I write my own custom |
@Thaina: You don't so much register it with Roslyn as you'd register it with a particular host (such as VS, OmniSharp, etc.), which can be different depending on the host. In VS, integrating a new CompletionProvider would require adding an |
@DustinCampbell I use vscode and omnisharp. Could you please envisioned me how should I get start with? |
Today, there isn't a mechanism for adding anything to OmniSharp's MEF composition without recompiling. You'd need to specifically add your assembly into the set of host assemblies used by OmniSharp. That's set is constructed here. You can provide an That said, if the feature turns out well, I would hope you'd eventually submit it as a PR to Roslyn. That way, everyone can enjoy it! 😄 |
Thanks for your guide @DustinCampbell I really wish roslyn itself should be able to add plugin or modulate some parts such as completion and formatting |
How and if extensibility works is really a decision that should be made by the host. Roslyn takes the position that it shouldn't impose an extensibility pattern on an application that hosts Roslyn. |
I'm not sure but I was skim through roslyn and omnisharp source code once and found that completion list was generated from roslyn, am I wrong? At first I was thinking I could write some plugin on vscode if the list of completion item give me the declaration type of each item and I could get attribute of the field. But it seem omnisharp was proxy all those to roslyn and no additional data was sent to omnisharp, so omnisharp member said that I need to have roslyn add more feature |
Well, I'm both an OmniSharp member and have worked on Roslyn since the beginning. 😄 There are some changes that would need to be done to be able to extend OmniSharp for this purpose. Roslyn provides an API to retrieve that data to display from a completion list. By default, that API will aggregate together the results of a set of completion providers. OmniSharp uses this API today, but only for providing keywords to the completion list. Today, OmniSharp provides symbols to the completion list differently, though that will change in the future (OmniSharp/omnisharp-roslyn#78). All of that said, this is all open source, so you can feel free to have a go at it. |
Not necessarily. It could be done in many ways, each with different levels of reach:
That said, whether or not this is doable is unclear. Providing "dynamic" completion statically is super-hard. |
@DustinCampbell My idea is not to analyze all of possible dynamic member statically. Just take the idea from typescript salsa for js that we could mark dynamic object with the known member we want it to be In vscode we can write js like this /** @type {{key:string,value:number}[]} */
var keyValuePairs = [];
keyValuePairs[0].va // show intellisense for value as number So if we could mark dynamic with something [Dynamic(typeof(IEnumerable<KeyValuePair<string,float>>))]
dynamic keyValuePairs = new Dictionary<string,float>();
keyValuePair.FirstOrDefault().Va // show intellisense for value as float So I think it doable in the service that
Which I think roslyn would be a place to do. But would it possible on just @omnisharp-roslyn or just @omnisharp-vscode ? |
Your code example above is a bit confusing. Is It would be more work to do this in omnisharp-vscode. Because omnisharp-vscode is written in TypeScript and runs on nodejs, you wouldn't be able to write this in C#, or use Roslyn or other managed libraries without other shenanigans. Adding it to omnisharp-roslyn would be the way to go. |
I hope somebody will build a cool tuple-based serialization library for simple data-only cases like this. interface IUser
{
string UserID { get; set; }
string Name { get; set; }
}
dynamic obj = LoadUserFromDB(); I'd rather write like this: (string usedId, string name) = LoadUserFromDB(); |
@DustinCampbell The problem that I wish it must be dynamic because loading object from DB or anywhere dynamic is. It not always contains just the parameter you think it exist Again please understand this example In the db there are json like this {
UserID: "TT",
Name: "ABC",
CustomData:"123",
} Whenever you do ANYTHING that parse this json into static type in C#. You will lose the data you don't know about. And when you pass that object to another system. You destroy the data we load from DB I see you all never get my point why I request dynamic intellisense. Please understand the problem first |
To put it simply In most NoSQL database we use today let we save document in json format It require us to work with json object by loading whole document out from DB. Modified some field, and save it back by posting whole document back to database But whenever you load any json object into static type. Any field that named difference from what you have will gone missing The best way to handle this situation is using dictionary and wrap it as dynamic object. That what we use in almost all of json plugin. NewtonSoft is one popular example But whenever that object is large. Most of the time it has some field that it always have. If it is data of user it always have @eyalsk As I said. Please go use typescript or js salsa with nosql db and you will understand. you still trying to mess with this issue without understanding and that is what frustrate me. I cannot appreciate your intervention because you never ever try to understand the problem dynamic object can change. But we always have something in mind that it would not or should not change even if it is dynamic object We want it to be dynamic because it has something that can change. But something is not everything. We may want it to have 100 fields that could change but only 10 field that will be present or null. And that's all we need to deal with dynamic object in the world |
@Thaina: I do understand the scenario you're talking about, which is why I've asked for more detail. I've spent a lot of time thinking about IntelliSense for dynamic in the past. To implement dynamic IntelliSense reliably (for some definition of "reliable" 😄), you must either use the runtime type of an object (which isn't possible to determine statically) or add features to the type system (a la TypeScript) to allow hints to be provided about what a type might be. The proposal you're making is similar to the latter approach: adding hints via attributes. The problem is that the examples you've shown are a little too simplistic and confusing. Consider your own example: [Dynamic(typeof(IEnumerable<KeyValuePair<string,float>>))]
dynamic keyValuePairs = new Dictionary<string,float>();
keyValuePair.FirstOrDefault().Va // show intellisense for value as float First, the use of an attribute limits the feature so that it this will only work if Second, the example falls apart as soon as it is passed to another method. [Dynamic(typeof(IEnumerable<KeyValuePair<string,float>>))]
dynamic keyValuePairs = new Dictionary<string,float>();
void M2()
{
M1(keyValuePairs);
}
void M1(dynamic d)
{
d.FirstOrDefault().Va // show intellisense for value as float
} You could achieve this, but it would take a significant amount of work to perform the flow analysis necessary to determine the type hint for |
@DustinCampbell What I request from the start is exactly latter case, as I mention JSDoc of typescript I don't think we could be ever to reliably generate intellisense on the fly. Instead, as programmer, I just want to control what it will be present to me by myself The ability to control what to hint is all I need. In fact I think that's all we should need. To let compiler guess your dynamic is too lazy But you right, sorry I just forget that attribute cannot put on local variable. So it must be comment or some other mechanism. But the point is just to add custom hint into the code for dynamic object. I just use attribute because it feel familiar Also its fine that it cannot work out of its scope. For any new things came in scope you should and must apply attribute mark it. using typescript salsa is also like that [Dynamic(typeof(IEnumerable<KeyValuePair<string,float>>))]
dynamic keyValuePairs = new Dictionary<string,float>();
void M2()
{
M1(keyValuePairs);
}
void M1([Dynamic(typeof(IEnumerable<KeyValuePair<string,float>>))]dynamic d)
{
d.FirstOrDefault().Va // show intellisense for value as float
} or to make this possible with local var /// <var type="IEnumerable<KeyValuePair<string,float>>" />
dynamic keyValuePairs = new Dictionary<string,float>();
void M2()
{
M1(keyValuePairs);
}
/// <param name="d" type="IEnumerable<KeyValuePair<string,float>>" />
void M1(dynamic d)
{
/// <var type="IEnumerable<KeyValuePair<string,float>>" />
dynamic dict = new Dict();
d.FirstOrDefault().Va // show intellisense for value as float
} |
To my eye, the annotations are already getting a bit out of control. I can see this appealing to a certain sort of developer, but it's probably not something that would necessarily appeal to everybody. As I mentioned, you can definitely do this if you want to invest the time and are OK with the limitations. We've considered allowing custom CompletionProviders to be included in a NuGet package in the same way that a Roslyn analyzer can be today. To me, that would be the ideal way to deliver a feature like this. That way, a user could simply reference the NuGet package for a particular project where they want to use this enhanced completion behavior. @Pilchie, what do you think? |
I'm not concerned who started the dispute, but I would like it to end. https://opensource.microsoft.com/codeofconduct/ |
@Thaina It's okay, I will unsubscribe from all your discussions and won't join any of them in the future. Have a nice day. |
@eyalsk Wow. After my threads became uncleanable mess you still think I can have a nice day with? Good day sir |
@svick Thank you recommending that for I can subscribed to it But it seem those thread has no movement so long. And I think it overkill to add language feature just for intellisense. All we need for dynamic is intellisense so it should not be syntax to compiled If we need to extend compiler to support new syntax would it be harder to implement? |
So, to chime in super late @DustinCampbell, regarding your comment on allowing completion providers via nugets, is that something we support already? /cc @Pilchie Thanks! |
|
@kzu No, that ability does not exist today. The only way to do this currently is the mechanisms Dustin described earlier (VS: export as I don't see an active issue for this... If this is something you think you'd make use of, please file an issue for it in this repo. Thanks! |
Done, thanks! #30270 |
So, what is the status of this now? |
@VBAndCs please stop pinging issues asking for status. If there has been no movement, then there as been no movement. If you would like to contribute toward improvements here reach out and let us know. We can let you know if we'd take PR contributions. |
My understanding of this thread is that we would like to do something to make intellisense for |
Note: i'm going to close this out. There is an existing mechanism to support this scenario. Namly, the public CompletionProvider api. With that people can build completion that htey want for advanced scenarios like this. Specifically, the thread has stated numerous times that supporting something like a Such an approach could absolutely be built externally using our public api using whatever mechanism the author deems fit. i.e. they could use attributes on data for this, or they could add special sigils in doc comments or regular comments to indicate this information. I do not see anything additional that roslyn has to add here. |
Marking as fixed as we now have a public, 100% supported mechanism for 3rd parties to enhance completion lists with this sort of information. |
I want to feel like working with javascript object when using dynamic type in C#. And jsdoc approach like salsa from typescript is very neat in my opinion
So I wish C# could be able to do something the same
Is it possible for roslyn to support this feature?
The text was updated successfully, but these errors were encountered: