Intellij idea plugin: Psi Classes - intellij-idea

I am developing a plugin in Intellij idea. I am stuck at inserting a piece of code after a statement.
For eg:
mHelper.launchPurchaseflow(str,str,str);
I need to find the element launchPurchaseflow and add a piece of code after that statement.I used PsiElement and string matcher to get to the corresponding string.
Now my PsiElement = mhelper.launchPurchaseFlow.
If I use
psiClass.addAfter(newElement, PsiElement.getContext());
It is only trying to add after the closing paranthesis.So its pop out my new element as Incorrect Statement.
For eg:
Its adding at position
mHelper.launchPurchaseflow(str,str,str)//newElement;
But I need to add at
mHelper.launchPurchaseflow(str,str,str); // new Element.

It's hard to give you a correct answer since you're not showing us how you are inserting the new text, but let's say you have a variable PsiElement myElement that contains the expression mHelper.launchPurchaseflow(str,str,str) (without the semicolon).
If you call getParent() on that element, you will likely have access to the statement representing the method call:
// Should represent "mHelper.launchPurchaseflow(str,str,str);" with the semicolon
PsiElement parent = myElement.getParent();
If you add your code after this node, it should produce a valid statement.
Alternatively, if getParent() does not return a PsiStatement and you want to be certain to have the full statement, you can do this:
PsiElement statement = PsiTreeUtil.getParentOfType(myElement, PsiStatement.class);

Related

Kotlin // Solve the example in a line

How to solve an example in a string?
Let's say we have val example : String = "3+5"
So how do I solve this example? Or if val example : String = "3*5/3"
Two ways to achieve it:
Keval - 3rd party dependency
You can either use Keval.eval("(3+4)(2/8 * 5) % PI") or as an extension function on String, "(3+4)(2/8 * 5) % PI".keval(). This will return the calculated value as Double. For your example, "3*5/3".keval().
To use it, add implementation("com.notkamui.libs:keval:0.8.0") in dependencies in app level build.gradle file and sync gradle. Then, use it in any file as mentioned in the above para, put the cursor on the line and press Alt + Enter (or hover for suggestions) to import the necessary imports.
Look into its Readme.md on the provided link for more usage and implementation details.
Custom String parsing using BODMAS rule
You can split the string using the BODMAS rule, parse the split array as int/double, if it throws exception, it means the substring is an expression too, again split it using BODMAS, parse and perform the calculation.

ASSIGN fails with variable from debugger path

I am trying to assign the value of this stucture path to a fieldsymbol, but this path does not work because it has a table in it's path.
But with in the debugger this value of this path is shown correctly.
Is there a way to dynamically assign a component of a table line to a fieldsymbol, by passing one path?
If not then I will just read the table line and then use the path to get the wanted value.
ls_struct (Struct)
- SUPPLYCHAINTRADETRANSACTION (Struct)
- INCL_SUPP_CHAIN_ITEM (Table)
- ASSOCIATEDDOCUMENTLINEDOCUMENT (Element)
i_component_path = |IG_DDIC-SUPPLYCHAINTRADETRANSACTION-INCL_SUPP_CHAIN_ITEM[1]-ASSOCIATEDDOCUMENTLINEDOCUMENT|.
ASSIGN (i_component_path) TO FIELD-SYMBOL(<lg_value>).
IF <lg_value> IS NOT ASSIGNED.
return.
ENDIF.
<lg_value> won't be assigned
Solution by Sandra Rossi
The debugger has its own syntax and own logic, it doesn't apply the ASSIGN algorithm at all. With ABAP source code, you have to use ASSIGN twice, the first one to reach the internal table, then you select the first line, and the second one to reach the component of the line.
The debugger works completely differently, the debugger code works only in debug mode, you can't call the code from the debugger (i.e. if you call it, the kernel code used by the debugger will fail). No, there's no "abappath". There are the XSL transformation objects (xpath), but it's slow for what you ask.
Thank you very much
This seems to be a rather unexpected limitation of the ASSIGN statement. Probably worth a ticket to SAP's ABAP language group to clarify whether it's even a bug.
While this works:
ASSIGN data-some_table[ 1 ]-some_field TO FIELD-SYMBOL(<lv_source>).
the same expressed as a string doesn't:
ASSIGN (`data-some_table[ 1 ]-some_field`) TO FIELD-SYMBOL(<lv_source>).
Alternative 1 for (name) of the ABAP keyword documentation for the ASSIGN statement says that "[t]he name in name is structured in the same way as if specified directly".
However, this declaration is immediately followed by "the content of name must be the name of a data object which may contain offsets and lengths, structure component selectors, and component selectors for assigning structured data objects and attributes in classes or objects", a list that does not include the table expressions we would need here.

How to change a Kotlin code reference from a function to a field in an intellij plugin?

I'm writing an intellij plugin where I'm refactoring a class, changing its getters (e.g., fun name(): String) to fields (e.g., val name: String).
However, I don't know how best to update the corresponding PsiReference instances. A Kotlin caller needs to change from myObj.name() to myObj.name without the parenthesis.
Currently, I'm doing the following:
ReferencesSearch.search(function).findAll().forEach {
val nextSibling = it.element.nextSibling
if ((nextSibling as? KtValueArgumentList)?.arguments?.isEmpty() == true) {
nextSibling.delete()
}
}
The above works somewhat. That is, the conversion happens correctly. However, the IDE still thinks it is calling a function. It underlines an error in the converted myObj.name with the following message:
Expression 'name' of type String cannot be invoked as a function. The function 'invoke()' is not found
Manually rewriting name in the editor forces intellij to refresh the reference and error disappears.
What should I do instead to prevent this from happening?
You are getting that error message because you are not modifying the reference to the old method. Intellij still thinks your call myObj.name is trying to access some method called name() that doesn't exist anymore.
Also, the search result is going to point to the leaf node in the AST that uses your method. In this case name() has a parent PSI object that holds a reference to name and to (). That's why calling element.nextSibling gives you the () which you can then call delete() on. But this doesn't change the parent's reference.
I'm not sure what is the best way to do what you want but you could try to replace the parent's reference directly. Try:
element.parent.replace(<reference to your the data class field>)

Modify JsonNode of unknown JSON dynamically in Java

I am trying to modify a JSON (of an unknown structure) where the JsonPath and its equivalent XML Xpath is known to me.
I have tired using com.jayway.jsonpath.JsonPath library for the same.
The problem with JsonPath is, it returns me the value but I am not able to modify the Target Node.
Follows is my code snippet for the same
JsonPath.read(jsonFile, jsonPath);
JsonPath.parse(jsonPath);
System.out.println("Author: "+JsonPath.read(jsonFile, jsonPath));
I tried using Jackson as mentioned in previously asked quetion, But it needs to be traversed node by node as follows
((ObjectNode) parent).put(fieldName, newValue);
which I cannot do due to unknown structure.
I have tried the answer given to the question recursively parse JSON object but it says how to parse not modify
I need to do the follows
JsonNode root = mapper.readTree("Json in form of String");
((JsonNode)(root.get("JsonPath")).set("New Value");
Is there any way in which this can be achieved?
JsonNode objects are immutable so you can't modify them. What you can do is replace a JsonNode with another one. A cast to ObjectNode is also required to expose the required methods. First find the parent of the node you want to replace :
JsonNode node = root.findParent("JsonPath");
Then use either of these 2 methods to replace it with a new one:
((ObjectNode) node).remove("JsonPath"); // remove current node
((ObjectNode) node).put("JsonPath", "New Value"); // add new one with new value
or
((ObjectNode) node).replace("JsonPath", new TextNode("New Value"));

How to use the data store to set a toolbar title in Sencha Touch

I am trying to set the toolbar item dynamically. So far I have a back button that resets the toolbar title to 'start' if the user chooses to go back.
But the following code won't work:
menuList.on('itemtap', function(dataView, index, item, e){
viewport.dockedItems.items[0].setTitle('{title}');
});
It tries to use a variable called 'title' out of my data store array. This works great for providing text to my Ext.List items. But the above code sets the toolbar title to the string '{title}' without even thinking of it being a variable.
Can you help me out?
List's use templates so items within curley braces get evaluated... you'll need to pass a reference to a variable without quotes. You haven't provided enough code for me to tell you where that information would be. If you already have a variable in scope called title that you put the data into then you can just reamove the '{ and }' ... otherwise you'll need to get the data you need from your store through some means, like Ext.StoreMgr or [appname].stores
Two things. 1) You will really want to get used to digging into the ST source code. In this case, if you look at the code for "setTitle", you will see that its argument is interpreted as straight HTML, not a template. So you can't use curly bracket syntax here. 2) Note that the type of the "item" argument to the event handler is an Element (i.e. ST's representation of the DOM object, not the selected datastore object. So that's not going to help you. However, the "index" arg gives you an easy way to get the appropriate object from the store. i.e.
[appname].stores.pages.getAt(index).title
I really don't know why, but it works if you put up to variables: One for the record and one for the value inside that record. There is a detailed explanation in the sencha.com-forum