Dart plugin - How to get the fields of an object?

I am trying to add a feature to my plugin for the Dart language and I hope this is the right place to get help for this.

 

Given a variable, I need to list it's fields inside my `AnAction` class, so that I can generate code. (Actually, given a PsiElement, I need to find it's type and list the variables if it's an object.)

void test() {
final value = OtherClass();

value// <-- obtain a list of field of `OtherClass` if caret is after `value`
}

What I've tried:

I have tried getting a reference using the closest `DartReference` parent which sometimes returns null, because the single variable name by itself makes the code invalid. (Even if I could get a reference to the declaration, it would be still difficult to get the class type eventually).

So then I found the `DartServerCompletionContributor` (because I realized the Dart autocomplete knows the hints I want to get), got a list of contributors using `CompletionContributor.forLanguage(DartLanguage.INSTANCE)`, but the `fillCompletionVariants` method is not meant to be called by users.

And now I'm lost. I need this autogeneration feature in my plugin, because it would make the development more productive, but the docs do not really help me out what helper functions I could use (some of them don't exist in Dart). I've been digging the open-source idea plugin code for Dart and Flutter, but it's not obvious from those codes how the given feature is implemented.


Any help would be appreciated. 

5 comments
Comment actions Permalink

I keep finding new ways that do not work for finding the members (but first finding the class) of the given selected PsiElement. I can only resolve to the declaration.

 

PsiTreeUtil.getParentOfType(selectedElement, DartReference::class.java)
// DartReferenceExpressionImpl

PsiTreeUtil.getParentOfType(selectedElement, PsiReferenceExpression::class.java)
// null

PsiTreeUtil.getParentOfType(selectedElement, DartReference::class.java).resolve()
selectedReference.resolve()
// DartComponentNameImpl@33694

DartResolveUtil.findType(selectedElement)
// null

PsiTreeUtil.getParentOfType(selectedElement, DartReference::class.java).resolveDartClass()
DartResolveUtil.findCoreClass(selectedElement.parent.parent.parent.parent.parent.parent.parent, "OtherClass")
// DartClassResolveResult
// myDartClass = null

(PsiTreeUtil.getParentOfType(selectedElement, DartReference::class.java).resolveReference().first() as PsiElementResolveResult).element
// -> returns the element on the declaration


//---

// On the declaration:
((selectedElement.parent as DartId).parent.parent as DartVarAccessDeclaration).type
// null


 

I would need to know how the 'Add type annotation' action is implemented inside the editor and use that, but I can't find the implementation.

0
Comment actions Permalink

I guess this was answered in gitter?

0
Comment actions Permalink

Yes, thank you,

DartAnalysisServerService

seems to be the way. I could get the classname of the referenced variable throught the server, but I still have a long way to go to list the variables.

0
Comment actions Permalink

Hi Ferenczi,

Curious, what feature are you trying to implement?

I'm afraid I have bad news for you. Let me describe the Dart plugin architecture in short. 

In the old days, all the code insight was implemented right in the Dart plugin. Later the Analysis Server appeared, a tool from the Dart SDK. And now all smart plugin features (like code highlighting, completion, reference resolution, etc) are powered by the Analysis Server. IDE only shows / does what server asks it to show / do.

Dart plugin still has its own parser, and it's still in a good shape, so traversing the PSI tree is ok. But most of the methods in DartResolveUtil are outdated, work incorrectly and are effectively not used anymore. Most of the methods (like findType) will be removed eventually.

Again, all smart features are performed on the server end. Dart plugin either calls server directly and waits for the answer (DartAnalysisServerService) or uses the data already received from server (DartServerData). For example, PsiReference.resolve() effectively uses navigation data received from the server. Sounds like you are going to implement some tricky feature based on deep code knowledge. The canonical way is that such feature is implemented fully on the server end and an IDE simply receives SourceChange from the server and it applies. It's theoretically possible to write a plugin for Analysis Server but I'm afraid it's a very complicated task.

So all you can do is to use existing server functionality (methods of DartAnalysisServerService) and existing data (DartServerData). It's usually ok to traverse the PSI tree and to use PsiReference.resolve(). If it's not enough then I'm afraid there's no way to implement your feature in a reliable way.

Speaking of getting variable type, I remember there's DartAnalysisServerService.analysis_getHover() that includes such info. It is designed for doc popup and maybe not exactly what you want. Well, you may resolve the reference (if you are lucky to have it) and look for variable type around its declaration - but there's no reliable way to succeed.

Speaking of getting a list of members for some class - again, no reliable way because there's no such server API. 'Add type annotation' and other code insight actions are implemented fully on the server end, IDE simply applies a SourceChange.

0
Comment actions Permalink

Hi Alexander,

Thank you for the detailed description. It's a pity I had to find out about the mentioned features by trial and error. Your comment clears things up in general, so I'm glad you took the time to write it out.

As for the feature itself, I've found the need to have an automatic "spread operator" (the three dots in JavaScript / TypeScript) in Dart, so I thought it would be the best to have a generator to do the heavy boilerplate for me.

Unfortunately, 'DartAnalysisService.java' interface has a few unimplemented methods (like 'completion_listTokenDetails'), some that are not called from the 'DartAnalysisServerService.java' (like 'search_findMemberDeclarations') and some that I could not make to return any meaningful result (like 'execution_getSuggestions', which could have been really useful).

In the end I implemented the feature I wanted (I am not proud of the way I solved it, only happy to have done it).

You can find the gif under this link (and the corresponding repository with the source code attached):

https://raw.githubusercontent.com/andrasferenczi/dart-data-plugin/spread_action/img/dart-spread-usage.gif

Given the hacks around the implementation (I rely on the completions and change the Document as if I was pressing CTRL + SPACE) I hesitate a bit to publish it, will see what I do.

Regards,
Andras

0

Please sign in to leave a comment.