Skip to content

Commit

Permalink
Feat: eck content entity support.
Browse files Browse the repository at this point in the history
  • Loading branch information
nvelychenko committed Jan 25, 2024
1 parent d8808dc commit 7efd0e2
Show file tree
Hide file tree
Showing 4 changed files with 81 additions and 41 deletions.
3 changes: 1 addition & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,14 @@

# drupal-extend Changelog

## [Unreleased]

## [0.0.5]

### Added

- Index caching
- Static content entity provider e.g. Node::load()
- Fields autocomplete only knowing entity interface e.g. NodeInterface
- ECK content entity type support.

### Fixed

Expand Down
2 changes: 1 addition & 1 deletion gradle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ pluginGroup = com.github.nvelychenko.drupalextend
pluginName = drupal-extend
pluginRepositoryUrl = https://github.com/nvelychenko/drupal-extend
# SemVer format -> https://semver.org
pluginVersion = 0.0.4
pluginVersion = 0.0.5

# Supported build number ranges and IntelliJ Platform versions -> https://plugins.jetbrains.com/docs/intellij/build-number-ranges.html
pluginSinceBuild = 233.13135.108
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,10 +37,10 @@ class DrupalContentEntityContributor : CompletionContributor() {
val parent: PsiElement
val prevSibling = completionParameters.position.prevSibling
parent = if (prevSibling != null && prevSibling.node.elementType === PhpTokenTypes.ARROW) {
// $foo->
// $xex->
prevSibling.parent
} else {
// $foo->ad
// $xex->ad
psiElement.parent
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@ package com.github.nvelychenko.drupalextend.index

import com.github.nvelychenko.drupalextend.index.types.DrupalContentEntity
import com.github.nvelychenko.drupalextend.util.isValidForIndex
import com.intellij.openapi.vfs.VirtualFile
import com.intellij.openapi.project.guessProjectDir
import com.intellij.openapi.vfs.VfsUtil
import com.intellij.psi.util.PsiTreeUtil
import com.intellij.util.indexing.*
import com.intellij.util.io.DataExternalizer
Expand All @@ -12,6 +13,10 @@ import com.jetbrains.php.lang.PhpFileType
import com.jetbrains.php.lang.documentation.phpdoc.psi.PhpDocComment
import com.jetbrains.php.lang.psi.PhpFile
import com.jetbrains.php.lang.psi.elements.PhpClass
import org.jetbrains.yaml.YAMLFileType
import org.jetbrains.yaml.psi.YAMLDocument
import org.jetbrains.yaml.psi.YAMLFile
import org.jetbrains.yaml.psi.YAMLKeyValue
import java.io.DataInput
import java.io.DataOutput

Expand Down Expand Up @@ -54,56 +59,92 @@ class ContentEntityIndex : FileBasedIndexExtension<String, DrupalContentEntity>(
override fun getIndexer(): DataIndexer<String, DrupalContentEntity, FileContent> {
return DataIndexer { inputData ->
val map = hashMapOf<String, DrupalContentEntity>()
val phpFile = inputData.psiFile as PhpFile
val psiFile = inputData.psiFile

if (!isValidForIndex(inputData)) {
return@DataIndexer map
}

val phpClass = PsiTreeUtil.findChildOfType(phpFile, PhpClass::class.java) ?: return@DataIndexer map
if (phpClass.docComment !is PhpDocComment) return@DataIndexer map
when (psiFile) {
is YAMLFile -> processYml(map, psiFile)
is PhpFile -> processPhp(map, psiFile)
}

val contentEntityTypes = (phpClass.docComment as PhpDocComment).getTagElementsByName("@ContentEntityType")
if (contentEntityTypes.isEmpty()) {
return@DataIndexer map
map
}
}

private fun processYml(map: HashMap<String, DrupalContentEntity>, psiFile: YAMLFile) {
val file = psiFile.virtualFile
val baseDir = psiFile.project.guessProjectDir()

if (baseDir != null && file != null) {
val relativePath = VfsUtil.getRelativePath(file, baseDir, '/') ?: return

if (!relativePath.contains("config/sync")) {
return
}
}

val contentEntityTypeDocText = contentEntityTypes[0].text

val id = getPhpDocParameter(contentEntityTypeDocText, "id") ?: return@DataIndexer map

val hardcodedKeys = arrayOf(
"id",
"revision",
"bundle",
"label",
"langcode",
"uuid",
"status",
"published",
"uid",
"owner",
"revision_log_message",
"revision_created",
"revision_user",
)

val resolvedKeys = hashMapOf<String, String>()

// @todo Implement better parsing for phpdoc.
for (key in hardcodedKeys) {
resolvedKeys[key] = (getPhpDocParameter(contentEntityTypeDocText, key) ?: continue)
if (!psiFile.name.startsWith("eck.eck_entity_type.")) {
return
}

PsiTreeUtil.getChildOfType(psiFile, YAMLDocument::class.java)?.topLevelValue?.children?.forEach { node ->
if (node is YAMLKeyValue && node.keyText == "id") {
map[node.valueText] = DrupalContentEntity(
node.valueText,
"\\Drupal\\eck\\Entity\\EckEntity",
hashMapOf(),
"\\Drupal\\Core\\Entity\\Sql\\SqlContentEntityStorage"
)
}
}
}

val sqlStorageHandler = getPhpDocParameter(contentEntityTypeDocText, "storage") ?: "\\Drupal\\Core\\Entity\\Sql\\SqlContentEntityStorage"
private fun processPhp(map: HashMap<String, DrupalContentEntity>, phpFile: PhpFile) {
val phpClass = PsiTreeUtil.findChildOfType(phpFile, PhpClass::class.java) ?: return
if (phpClass.docComment !is PhpDocComment) return

map[id] = DrupalContentEntity(id, phpClass.fqn, resolvedKeys, sqlStorageHandler)
val contentEntityTypes = (phpClass.docComment as PhpDocComment).getTagElementsByName("@ContentEntityType")
if (contentEntityTypes.isEmpty()) {
return
}

map
val contentEntityTypeDocText = contentEntityTypes[0].text

val id = getPhpDocParameter(contentEntityTypeDocText, "id") ?: return

val hardcodedKeys = arrayOf(
"id",
"revision",
"bundle",
"label",
"langcode",
"uuid",
"status",
"published",
"uid",
"owner",
"revision_log_message",
"revision_created",
"revision_user",
)

val resolvedKeys = hashMapOf<String, String>()

// @todo Implement better parsing for phpdoc.
for (key in hardcodedKeys) {
resolvedKeys[key] = (getPhpDocParameter(contentEntityTypeDocText, key) ?: continue)
}

val sqlStorageHandler = getPhpDocParameter(contentEntityTypeDocText, "storage") ?: "\\Drupal\\Core\\Entity\\Sql\\SqlContentEntityStorage"

map[id] = DrupalContentEntity(id, phpClass.fqn, resolvedKeys, sqlStorageHandler)
}

private fun getPhpDocParameter(phpDocText: String, id: String): String? {
@Suppress("RegExpUnnecessaryNonCapturingGroup")
val entityTypeMatch = Regex("${id}(?:\"?)\\s*=\\s*\"([^\"]+)\"").find(phpDocText)

return entityTypeMatch?.groups?.get(1)?.value
Expand All @@ -114,7 +155,7 @@ class ContentEntityIndex : FileBasedIndexExtension<String, DrupalContentEntity>(
override fun getValueExternalizer(): DataExternalizer<DrupalContentEntity> = myDataExternalizer

override fun getInputFilter(): FileBasedIndex.InputFilter {
return FileBasedIndex.InputFilter { file: VirtualFile -> file.fileType == PhpFileType.INSTANCE }
return FileBasedIndex.InputFilter { file -> file.fileType == YAMLFileType.YML || file.fileType == PhpFileType.INSTANCE }
}

override fun dependsOnFileContent(): Boolean = true
Expand Down

0 comments on commit 7efd0e2

Please sign in to comment.