Skip to content

Commit 69ffadc

Browse files
committed
feat: generate endpoints api
1 parent 52a67ff commit 69ffadc

File tree

14 files changed

+281
-14
lines changed

14 files changed

+281
-14
lines changed

CONTRIBUTING.md

Lines changed: 24 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,9 @@ The project is structured around a **Base Package**: `com.github.xepozz.temporal
1212
### 1. `common` Package
1313
Contains language-agnostic logic, base classes, and Extension Points (EPs).
1414
- **Location**: `src/main/kotlin/com/github/xepozz/temporal/common`
15+
- **Sub-packages**:
16+
- `extensionPoints`: Contains all Extension Point interfaces.
17+
- `model`: Contains common data classes representing Temporal entities (Workflows, Activities).
1518
- **Purpose**: Define how features should work across all languages.
1619
- **Components**:
1720
- **Extension Points (EP)**: Interfaces or abstract classes that define the contract for language-specific support.
@@ -28,44 +31,51 @@ Contains language-specific implementations (adapters).
2831

2932
When implementing a feature that should work across multiple languages (e.g., Activity Name completion), follow this pattern:
3033

31-
1. **Define the EP Interface** in `common`:
34+
1. **Define the EP Interface** in `common.extensionPoints`:
3235
```kotlin
33-
// common/completion/ActivityCompletionEP.kt
34-
interface ActivityCompletionEP {
35-
fun getActivityNames(project: Project): List<String>
36+
// common/extensionPoints/Activity.kt
37+
interface Activity {
38+
fun getActivities(project: Project): List<ActivityModel>
39+
40+
companion object {
41+
val ACTIVITY_EP = ExtensionPointName.create<Activity>("com.github.xepozz.temporal.activity")
42+
43+
fun getActivities(project: Project): List<ActivityModel> {
44+
return ACTIVITY_EP.extensionList.flatMap { it.getActivities(project) }
45+
}
46+
}
3647
}
3748
```
3849
2. **Register the EP** in `plugin.xml`:
3950
```xml
4051
<extensionPoints>
41-
<extensionPoint name="activityCompletion" interface="com.github.xepozz.temporal.common.completion.ActivityCompletionEP"/>
52+
<extensionPoint name="activity" interface="com.github.xepozz.temporal.common.extensionPoints.Activity"/>
4253
</extensionPoints>
4354
```
4455
3. **Implement the Shared Provider** in `common`:
4556
```kotlin
4657
// common/completion/ActivityCompletionProvider.kt
4758
class ActivityCompletionProvider : CompletionProvider<CompletionParameters>() {
4859
override fun addCompletions(parameters: CompletionParameters, context: ProcessingContext, result: CompletionResultSet) {
49-
val ep = ExtensionPointName.create<ActivityCompletionEP>("com.github.xepozz.temporal.activityCompletion")
50-
ep.extensionList.forEach { adapter ->
51-
adapter.getActivityNames(parameters.editor.project!!).forEach { result.addElement(LookupElementBuilder.create(it)) }
60+
ActivityCompletion.getActivityNames(parameters.editor.project!!).forEach {
61+
result.addElement(LookupElementBuilder.create(it))
5262
}
5363
}
5464
}
5565
```
5666
4. **Implement the Language Adapter**:
5767
```kotlin
58-
// languages/php/completion/PhpActivityCompletionProvider.kt
59-
class PhpActivityCompletionProvider : ActivityCompletionEP {
60-
override fun getActivityNames(project: Project): List<String> {
61-
// PHP-specific logic to find Activity names
68+
// languages/php/endpoints/PhpActivity.kt
69+
class PhpActivity : Activity {
70+
override fun getActivities(project: Project): List<ActivityModel> {
71+
// PHP-specific logic to find Activities
6272
}
6373
}
6474
```
6575
5. **Register the Adapter** in the language-specific XML (e.g., `language-php.xml`):
6676
```xml
6777
<extensions defaultExtensionNs="com.github.xepozz.temporal">
68-
<activityCompletion implementation="com.github.xepozz.temporal.languages.php.completion.PhpActivityCompletionProvider"/>
78+
<activity implementation="com.github.xepozz.temporal.languages.php.endpoints.PhpActivity"/>
6979
</extensions>
7080
```
7181

@@ -75,7 +85,7 @@ When implementing a feature that should work across multiple languages (e.g., Ac
7585
- **Performance**: Use `CachedValue` and `DumbService.isDumb()` checks where appropriate.
7686
- **Consistency**: Follow the existing package structure. For example, if a feature is implemented for PHP in `languages.php.navigation`, any future language implementations should follow the same sub-package structure (e.g., `languages.go.navigation`).
7787
- **Naming**:
78-
- Extension Points should end with `EP`.
88+
- Extension Points should **not** end with `EP`. They should represent the entity or feature (e.g., `ActivityCompletion`, `Workflow`).
7989
- Language-specific implementations should be prefixed with the language name (e.g., `Php...`).
8090
- **Namespaces**:
8191
- All Extension Point names and IDs **must** start with `com.github.xepozz.temporal`.
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
package com.github.xepozz.temporal
2+
3+
import com.intellij.openapi.util.IconLoader
4+
5+
object TemporalIcons {
6+
@JvmStatic
7+
val TEMPORAL = IconLoader.getIcon("/icons/temporal/icon.svg", javaClass)
8+
}
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
package com.github.xepozz.temporal.common.endpoints
2+
3+
import com.github.xepozz.temporal.TemporalIcons
4+
import com.github.xepozz.temporal.common.extensionPoints.Activity
5+
import com.intellij.microservices.endpoints.EndpointType
6+
import com.intellij.microservices.endpoints.EndpointsFilter
7+
import com.intellij.microservices.endpoints.EndpointsProvider
8+
import com.intellij.microservices.endpoints.FrameworkPresentation
9+
import com.intellij.navigation.ItemPresentation
10+
import com.intellij.openapi.project.Project
11+
import com.intellij.openapi.util.ModificationTracker
12+
import java.util.function.Supplier
13+
import javax.swing.Icon
14+
import com.github.xepozz.temporal.common.model.Activity as ActivityModel
15+
16+
class TemporalActivityEndpointsProvider : EndpointsProvider<ActivityEndpointGroup, ActivityEndpoint> {
17+
override val endpointType: EndpointType = EndpointType("Temporal Activity", null, Supplier { "Temporal Activity" })
18+
override val presentation: FrameworkPresentation = FrameworkPresentation("Temporal Activity", "Temporal Activity", TemporalIcons.TEMPORAL)
19+
20+
override fun getEndpointGroups(project: Project, filter: EndpointsFilter): Iterable<ActivityEndpointGroup> {
21+
return Activity.getActivities(project).map { ActivityEndpointGroup(it) }
22+
}
23+
24+
override fun getEndpoints(group: ActivityEndpointGroup): Iterable<ActivityEndpoint> {
25+
return listOf(ActivityEndpoint(group.activity))
26+
}
27+
28+
override fun getModificationTracker(project: Project): ModificationTracker = ModificationTracker.NEVER_CHANGED
29+
30+
override fun getStatus(project: Project): EndpointsProvider.Status = EndpointsProvider.Status.AVAILABLE
31+
32+
override fun isValidEndpoint(group: ActivityEndpointGroup, endpoint: ActivityEndpoint): Boolean = true
33+
34+
override fun getEndpointPresentation(group: ActivityEndpointGroup, endpoint: ActivityEndpoint): ItemPresentation {
35+
return object : ItemPresentation {
36+
override fun getPresentableText(): String = endpoint.activity.id
37+
override fun getLocationString(): String? = endpoint.activity.psiAnchor?.element?.containingFile?.name
38+
override fun getIcon(unused: Boolean): Icon = TemporalIcons.TEMPORAL
39+
}
40+
}
41+
}
42+
43+
data class ActivityEndpointGroup(val activity: ActivityModel)
44+
data class ActivityEndpoint(val activity: ActivityModel)
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
package com.github.xepozz.temporal.common.endpoints
2+
3+
import com.github.xepozz.temporal.TemporalIcons
4+
import com.github.xepozz.temporal.common.extensionPoints.Workflow
5+
import com.intellij.microservices.endpoints.EndpointType
6+
import com.intellij.microservices.endpoints.EndpointsFilter
7+
import com.intellij.microservices.endpoints.EndpointsProvider
8+
import com.intellij.microservices.endpoints.FrameworkPresentation
9+
import com.intellij.navigation.ItemPresentation
10+
import com.intellij.openapi.project.Project
11+
import com.intellij.openapi.util.ModificationTracker
12+
import java.util.function.Supplier
13+
import javax.swing.Icon
14+
import com.github.xepozz.temporal.common.model.Workflow as WorkflowModel
15+
16+
class TemporalWorkflowEndpointsProvider : EndpointsProvider<WorkflowEndpointGroup, WorkflowEndpoint> {
17+
override val endpointType: EndpointType = EndpointType("Temporal Workflow", null, Supplier { "Temporal Workflow" })
18+
override val presentation: FrameworkPresentation = FrameworkPresentation("Temporal Workflow", "Temporal Workflow", TemporalIcons.TEMPORAL)
19+
20+
override fun getEndpointGroups(project: Project, filter: EndpointsFilter): Iterable<WorkflowEndpointGroup> {
21+
return Workflow.getWorkflows(project).map { WorkflowEndpointGroup(it) }
22+
}
23+
24+
override fun getEndpoints(group: WorkflowEndpointGroup): Iterable<WorkflowEndpoint> {
25+
return listOf(WorkflowEndpoint(group.workflow))
26+
}
27+
28+
override fun getModificationTracker(project: Project): ModificationTracker = ModificationTracker.NEVER_CHANGED
29+
30+
override fun getStatus(project: Project): EndpointsProvider.Status = EndpointsProvider.Status.AVAILABLE
31+
32+
override fun isValidEndpoint(group: WorkflowEndpointGroup, endpoint: WorkflowEndpoint): Boolean = true
33+
34+
override fun getEndpointPresentation(group: WorkflowEndpointGroup, endpoint: WorkflowEndpoint): ItemPresentation {
35+
return object : ItemPresentation {
36+
override fun getPresentableText(): String = endpoint.workflow.id
37+
override fun getLocationString(): String? = endpoint.workflow.psiAnchor.element?.containingFile?.name
38+
override fun getIcon(unused: Boolean): Icon = TemporalIcons.TEMPORAL
39+
}
40+
}
41+
}
42+
43+
data class WorkflowEndpointGroup(val workflow: WorkflowModel)
44+
data class WorkflowEndpoint(val workflow: WorkflowModel)
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
package com.github.xepozz.temporal.common.extensionPoints
2+
3+
import com.intellij.openapi.extensions.ExtensionPointName
4+
import com.intellij.openapi.project.Project
5+
import com.github.xepozz.temporal.common.model.Activity as ActivityModel
6+
7+
interface Activity {
8+
fun getActivities(project: Project): List<ActivityModel>
9+
10+
companion object {
11+
val ACTIVITY_EP = ExtensionPointName.create<Activity>("com.github.xepozz.temporal.activity")
12+
13+
fun getActivities(project: Project): List<ActivityModel> {
14+
return ACTIVITY_EP.extensionList.flatMap { it.getActivities(project) }
15+
}
16+
}
17+
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
package com.github.xepozz.temporal.common.extensionPoints
2+
3+
import com.intellij.openapi.extensions.ExtensionPointName
4+
import com.intellij.openapi.project.Project
5+
import com.github.xepozz.temporal.common.model.Workflow as WorkflowModel
6+
7+
interface Workflow {
8+
fun getWorkflows(project: Project): List<WorkflowModel>
9+
10+
companion object {
11+
val WORKFLOW_EP = ExtensionPointName.create<Workflow>("com.github.xepozz.temporal.workflow")
12+
13+
fun getWorkflows(project: Project): List<WorkflowModel> {
14+
return WORKFLOW_EP.extensionList.flatMap { it.getWorkflows(project) }
15+
}
16+
}
17+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
package com.github.xepozz.temporal.common.model
2+
3+
import com.intellij.psi.PsiElement
4+
import com.intellij.psi.SmartPsiElementPointer
5+
6+
data class Activity(
7+
val id: String,
8+
val language: String,
9+
val psiAnchor: SmartPsiElementPointer<out PsiElement>?,
10+
val parameters: Collection<String>
11+
)
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
package com.github.xepozz.temporal.common.model
2+
3+
import com.intellij.psi.PsiElement
4+
import com.intellij.psi.SmartPsiElementPointer
5+
6+
data class Workflow(
7+
val id: String,
8+
val language: String,
9+
val psiAnchor: SmartPsiElementPointer<out PsiElement>,
10+
val parameters: Collection<String>
11+
)
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
package com.github.xepozz.temporal.languages.php.endpoints
2+
3+
import com.github.xepozz.temporal.common.extensionPoints.Activity
4+
import com.github.xepozz.temporal.languages.php.TemporalClasses
5+
import com.github.xepozz.temporal.languages.php.hasAttribute
6+
import com.intellij.openapi.project.Project
7+
import com.intellij.psi.SmartPointerManager
8+
import com.jetbrains.php.PhpIndex
9+
import com.github.xepozz.temporal.common.model.Activity as ActivityModel
10+
11+
class PhpActivity : Activity {
12+
override fun getActivities(project: Project): List<ActivityModel> {
13+
val phpIndex = PhpIndex.getInstance(project)
14+
val smartPointerManager = SmartPointerManager.getInstance(project)
15+
16+
return phpIndex.getAllClassNames(null).flatMap { name ->
17+
phpIndex.getClassesByName(name)
18+
.filter { it.hasAttribute(TemporalClasses.ACTIVITY) }
19+
.map {
20+
ActivityModel(
21+
id = it.name,
22+
language = "PHP",
23+
psiAnchor = smartPointerManager.createSmartPsiElementPointer(it),
24+
parameters = emptyList()
25+
)
26+
}
27+
}
28+
}
29+
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
package com.github.xepozz.temporal.languages.php.endpoints
2+
3+
import com.github.xepozz.temporal.common.extensionPoints.Workflow
4+
import com.github.xepozz.temporal.languages.php.TemporalClasses
5+
import com.github.xepozz.temporal.languages.php.hasAttribute
6+
import com.intellij.openapi.project.Project
7+
import com.intellij.psi.SmartPointerManager
8+
import com.jetbrains.php.PhpIndex
9+
import com.github.xepozz.temporal.common.model.Workflow as WorkflowModel
10+
11+
class PhpWorkflow : Workflow {
12+
override fun getWorkflows(project: Project): List<WorkflowModel> {
13+
val phpIndex = PhpIndex.getInstance(project)
14+
val smartPointerManager = SmartPointerManager.getInstance(project)
15+
return phpIndex.getAllClassNames(null).flatMap { name ->
16+
phpIndex.getClassesByName(name)
17+
.filter { it.hasAttribute(TemporalClasses.WORKFLOW) }
18+
.map {
19+
WorkflowModel(
20+
id = it.name,
21+
language = "PHP",
22+
psiAnchor = smartPointerManager.createSmartPsiElementPointer(it),
23+
parameters = emptyList()
24+
)
25+
}
26+
}
27+
}
28+
}

0 commit comments

Comments
 (0)