Add PsiElement to another

Answered

Hi,

I have a PHP array like this:

<?php
namespace Intranet;

return [
'factories' => [
// Api
Model\Infrastructure\Api\Adapter::class => Model\Infrastructure\Api\AdapterFactory::class,
// Command
Model\Application\Command\Application\CreateApplication::class => Model\Application\Command\Application\CreateApplicationFactory::class,


],
'foo' => [
'bar' => 'baz'
],
Model\Application\Query\Search\FindApplicationForUser::class => 'test'
];

My goal is to add a key/value inside the factories index (I want to add a ClassConstantReference \namespace\something::class not a StringLiteralExpression like 'foo').

Currently I have this code:

fun findFactoriesValuesArray(file: PsiFile): PsiElement? {
val phpReturn = PsiTreeUtil.findChildOfType(file, PhpReturn::class.java)
if (phpReturn != null) {
val arrayCreation: PhpPsiElement? = phpReturn.firstPsiChild
if (arrayCreation is ArrayCreationExpression) {

for (hashElement in arrayCreation.hashElements) {
val arrayKey = hashElement.key
if (arrayKey is StringLiteralExpression && arrayKey.text == "'factories'") {
val arrayValue = hashElement.value
val lastArrayHashElement: PsiElement = arrayValue!!.children[arrayValue.children.size - 1]
if (lastArrayHashElement is ArrayHashElement) {
println(lastArrayHashElement)
return lastArrayHashElement
}
}
}
}
}
return null
}

 

val projectPath = VirtualFileManager.constructUrl(LocalFileSystem.PROTOCOL, event.project!!.basePath.toString())

val file: VirtualFile? = VirtualFileManager.getInstance().findFileByUrl("$projectPath/module/Intranet/config/services.config.php")
val document: Document? = FileDocumentManager.getInstance().getDocument(file!!);
val psiFile: PsiFile = PhpPsiElementFactory.createPsiFileFromText(event.project!!, document!!.text)

val arrayCreation = findFactoriesValuesArray(psiFile)

if (arrayCreation != null) {
val createString: PsiElement = PhpPsiElementFactory.createPhpPsiFromText(event.project!!, ArrayHashElement::class.java, "test::class => testtest::class")
arrayCreation.add(createString)
println(psiFile.text)
}

I have an error:

java.lang.AssertionError: cannot create element from text:
<?php
test::class => testtest::class
at com.jetbrains.php.lang.psi.PhpPsiElementFactory.createPhpPsiFromText(PhpPsiElementFactory.java:294)

Why is try to add the open PHP tag and not only the string passed to the method?

Thanks.

5 comments
Comment actions Permalink

I've updated my method findFactoriesValuesArray which now return the last ArrayHashElement:

 

private fun findFactoriesValuesArray(file: PsiFile): PsiElement? {
val phpReturn = PsiTreeUtil.findChildOfType(file, PhpReturn::class.java)
if (phpReturn != null) {
val arrayCreation: PhpPsiElement? = phpReturn.firstPsiChild
if (arrayCreation is ArrayCreationExpression) {
for (hashElement in PsiTreeUtil.getChildrenOfTypeAsList(arrayCreation, ArrayHashElement::class.java)) {
val arrayKey = hashElement.key

if (arrayKey is StringLiteralExpression && arrayKey.text == "'factories'") {
val arrayValue = hashElement.value
val lastArrayHashElement: PsiElement = arrayValue!!.children[arrayValue.children.size - 1]
if (lastArrayHashElement is ArrayHashElement) {
return lastArrayHashElement
}
}
}
}
}
return null
}

The output is:

<?php
<?php


namespace Intranet;



return [
Model\Application\Query\Search\FindApplicationForUser::class => 'test',
'factories' => [
// Api
Model\Infrastructure\Api\Adapter::class => Model\Infrastructure\Api\AdapterFactory::class,
// Command
Model\Application\Command\Application\CreateApplication::class => Model\Application\Command\Application\CreateApplicationFactory::class'test => test',


],
'foo' => [
'bar' => 'baz'
],
];

So my string is added to the end of line of the last ArrayHashElement but I need to add a comma and a new line.

 

PS : I don't know why the ouput contain two PHP open tag.

0
Comment actions Permalink

Please do not create multiple threads for the same problem. https://intellij-support.jetbrains.com/hc/en-us/community/posts/360004324519-Add-some-code-to-a-file?page=1#community_comment_360000662460

 

Please try creating "," as separate element and append it before appending new array element

0
Comment actions Permalink

Thank you for your reply.

I add the comma like this: (It's work, after that I will need to check if the comma exists and add it if not the case)

val arrayCreation = findFactoriesValuesArray(psiFile)

if (arrayCreation != null) {

val comma: PsiElement = PhpPsiElementFactory.createComma(event.project!!)
val arrayHashElement: PsiElement = PhpPsiElementFactory.createPhpPsiFromText(event.project!!, PhpElementTypes.ARRAY_HASH_ELEMENT, "'test' => 'test'")
arrayCreation.add(comma)
comma.add(arrayHashElement)
println(psiFile.text)
}

The same exception is thrown:

java.lang.AssertionError: cannot create element from text:
<?php
'test' => 'test'
at com.jetbrains.php.lang.psi.PhpPsiElementFactory.createPhpPsiFromText(PhpPsiElementFactory.java:272)

The PsiTreeView plugin tell me an ArrayHashElement has a key => value as text

I really don't know why an exception is thrown.

0
Comment actions Permalink

I really need help. 4 evening and still not the Psi I want.

var hashArrayElement: ArrayHashElement? = findFactoriesValuesArray(psiFile) as ArrayHashElement

if (hashArrayElement != null) {

// add comma
// add hash array element
hashArrayElement.parent.addAfter(PhpPsiElementFactory.createFromText(event.project!!, PhpElementTypes.HASH_ARRAY_ELEMENT, "'test' => 'test"), hashArrayElement)
}

java.lang.IllegalArgumentException: Argument for @NotNull parameter 'element' of com/intellij/extapi/psi/ASTDelegatePsiElement.addAfter must not be null
at com.intellij.extapi.psi.ASTDelegatePsiElement.$$$reportNull$$$0(ASTDelegatePsiElement.java)
at com.intellij.extapi.psi.ASTDelegatePsiElement.addAfter(ASTDelegatePsiElement.java)

Why the element parameter is null?

PhpPsiElementFactory.createFromText(event.project!!, PhpElementTypes.HASH_ARRAY_ELEMENT, "'test' => 'test")

What is the correct way to create a HashArrayElement from text?

I don't understand at all how to add a HashArrayElement to my psi element.

0
Comment actions Permalink

I found something but I don't think it's the best way to do.

 

if (hashArrayElement != null) {

WriteCommandAction.runWriteCommandAction(event.project!!) {
val createKey: PsiElement? = PhpPsiElementFactory.createFromText(event.project!!, ClassConstantReference::class.java, "Model\\Application\\Command\\test::class")
val createArrow: PsiElement? = PhpPsiElementFactory.createFromText(event.project!!, LeafPsiElement::class.java, "=>")
val createValue: PsiElement? = PhpPsiElementFactory.createFromText(event.project!!, ClassConstantReference::class.java, "Model\\Application\\Command\\toto::class")

val comma = hashArrayElement.parent.addAfter(PhpPsiElementFactory.createComma(event.project!!), hashArrayElement)
val arrayKey = hashArrayElement.parent.addAfter(createKey!!, comma)
val arrow = hashArrayElement.parent.addAfter(createArrow!!, arrayKey)
hashArrayElement.parent.addAfter(createValue!!, arrow)
CodeStyleManager.getInstance(event.project!!).reformat(psiFile1)
}
}

It's working, but why the reformat method don't add a new line after the comma?

It's possible to refactor this code?

0

Please sign in to leave a comment.