Skip to content

Commit

Permalink
Merge pull request #12 from Alaanor/rust-integration
Browse files Browse the repository at this point in the history
Rust integration
  • Loading branch information
Alaanor authored Jun 5, 2022
2 parents 406d124 + 6a576e3 commit e5d6a12
Show file tree
Hide file tree
Showing 33 changed files with 512 additions and 46 deletions.
30 changes: 17 additions & 13 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,21 +3,25 @@
# candid-intellij-plugin Changelog

## [Unreleased]
### Added
- 🔧 Line marker on rust files when a matching candid method is found
- 🔧 Resolve candid method to their corresponding rust method
- 🧐 Unused candid method inspection

## [0.2.0]
### Added
- 🪄 Suggest missing import whenever possible
- ️🧐 Marking self import as invalid
- ️🧐 Marking duplicated type name as invalid
- ️🧐 Marking empty and invalid import as invalid
- ✨ Comment code through shortcut
- 📝 Documentation for type reference
- 🔎 Go to symbol for candid type
- 📝 Structure view

### Fixed
- Added missing top level keyword import
- Missing keyword completion for query and oneway
### Added
- 🪄 Suggest missing import whenever possible
- ️🧐 Marking self import as invalid
- ️🧐 Marking duplicated type name as invalid
- ️🧐 Marking empty and invalid import as invalid
- ✨ Comment code through shortcut
- 📝 Documentation for type reference
- 🔎 Go to symbol for candid type
- 📝 Structure view

### Fixed
- Added missing top level keyword import
- Missing keyword completion for query and oneway
- Stop suggesting top level keyword inside a service

## [0.1.1]
Expand Down
20 changes: 18 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,27 @@ Features supported so far:
- ⌨️ Auto Completion
- 🔍 Find Usage
- 💄 Code Format
- 🦀 Rust integration
<!-- Plugin description end -->

## Early stage
## 🦀 Rust integration

This plugin is in the early stage and while this already has some basic feature it might as well miss some. It is in my plan to work on it for the next 5 weeks and submit as an entry for the [Supernova hackathon](https://dfinity.org/supernova/). If you have any feedback or issue feel free to open an issue or directly get in touch with me on discord Alaanor#9999.
For the sake of correctness, the plugin will only enable rust integration for a given candid file if the followings are found in `dfx.json`:

```json
{
"canisters": {
"foobar-canister": {
"type": "rust",
"candid": "correct/path/to/candid-file.did",
"package": "rust-package-name"
}
}
}
```

All three `type`, `candid` and `package` fields are required to enable rust integration. `dfx.json` is expected to be found at the root of the project.
The type `custom` will not be supported because of the lack of explicit information that the plugin require to correctly resolve items.

## Installation

Expand Down
4 changes: 2 additions & 2 deletions gradle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
pluginGroup = com.github.alaanor.candid
pluginName = candid-intellij-plugin
# SemVer format -> https://semver.org
pluginVersion = 0.2.0
pluginVersion = 0.3.0

# See https://plugins.jetbrains.com/docs/intellij/build-number-ranges.html
# for insight into build numbers and IntelliJ Platform versions.
Expand All @@ -17,7 +17,7 @@ platformVersion = 2021.1.3

# Plugin Dependencies -> https://plugins.jetbrains.com/docs/intellij/plugin-dependencies.html
# Example: platformPlugins = com.intellij.java, com.jetbrains.php:203.4449.22
platformPlugins =
platformPlugins = org.rust.lang:0.4.156.4145-211, org.toml.lang:0.2.155.4114-211

# Java language level used to compile sources and to generate the files for - Java 11 is required since 2020.3
javaVersion = 11
Expand Down
7 changes: 5 additions & 2 deletions grammar/Candid.bnf
Original file line number Diff line number Diff line change
Expand Up @@ -54,16 +54,19 @@ actor ::= service actor_name? ':' (tup_type '->')? (actor_type | identifier_refe

actor_name ::= id

private actor_type ::= '{' <<list meth_type ';'>>? '}' { pin=1 }
private actor_type ::= '{' <<list method_type ';'>>? '}' { pin=1 }

import_statement ::= import string_literal {
mixin="com.github.alaanor.candid.psi.mixin.CandidImportStatementMixin"
name="import"
pin=1
}

meth_type ::= method_name ':' (func_type | identifier_reference) {
method_type ::= method_name ':' (func_type | identifier_reference) {
mixin="com.github.alaanor.candid.psi.mixin.CandidMethodMixin"
stubClass="com.github.alaanor.candid.psi.stub.impl.CandidMethodStub"
elementTypeFactory="com.github.alaanor.candid.psi.stub.type.CandidStubTypes.get"
name = "method"
}

method_name ::= name
Expand Down
Original file line number Diff line number Diff line change
@@ -1,19 +1,27 @@
package com.github.alaanor.candid

import com.github.alaanor.candid.psi.CandidIdentifierDeclaration
import com.github.alaanor.candid.psi.primitive.CandidNamedElement
import com.github.alaanor.candid.psi.CandidIdentifierReference
import com.github.alaanor.candid.psi.CandidMethodType
import com.intellij.lang.findUsages.FindUsagesProvider
import com.intellij.psi.PsiElement

class CandidFindUsageProvider : FindUsagesProvider {
override fun canFindUsagesFor(psiElement: PsiElement): Boolean = psiElement is CandidNamedElement
override fun canFindUsagesFor(psiElement: PsiElement): Boolean {
return psiElement is CandidIdentifierDeclaration
|| psiElement is CandidIdentifierReference
|| psiElement is CandidMethodType
}

override fun getHelpId(psiElement: PsiElement): String? = null
override fun getType(element: PsiElement): String = "Candid type"
override fun getType(element: PsiElement): String = getNodeText(element, false)
override fun getDescriptiveName(element: PsiElement): String = getNodeText(element, true)

override fun getNodeText(element: PsiElement, useFullName: Boolean): String {
return when (element) {
is CandidIdentifierDeclaration -> "Type declaration"
is CandidIdentifierReference -> "Type reference"
is CandidMethodType -> "Method"
else -> "Unknown node"
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package com.github.alaanor.candid.completion

import com.github.alaanor.candid.psi.CandidActor
import com.github.alaanor.candid.psi.CandidMethType
import com.github.alaanor.candid.psi.CandidMethodType
import com.intellij.codeInsight.completion.InsertHandler
import com.intellij.codeInsight.lookup.LookupElement
import com.intellij.patterns.ElementPattern
Expand All @@ -18,7 +18,7 @@ class MethodAnnotationCompletion : CandidBasicCompletion() {
psiElement(CandidActor::class.java)
.with(object : PatternCondition<CandidActor>("bruh") {
override fun accepts(t: CandidActor, context: ProcessingContext?): Boolean {
return t.lastChild is CandidMethType
return t.lastChild is CandidMethodType
}
})
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ class CandidBlock(node: ASTNode, wrap: Wrap?, alignment: Alignment?, private val
return when (node.elementType) {
CandidTypes.FIELD_TYPE_RECORD,
CandidTypes.FIELD_TYPE_VARIANT,
CandidTypes.METH_TYPE,
CandidTypes.METHOD_TYPE,
CandidTypes.BLOCK_COMMENT,
CandidTypes.LINE_COMMENT -> {
Indent.getNormalIndent()
Expand All @@ -53,7 +53,7 @@ class CandidBlock(node: ASTNode, wrap: Wrap?, alignment: Alignment?, private val
return when (node.elementType) {
CandidTypes.RECORD_STATEMENT,
CandidTypes.VARIANT_STATEMENT,
CandidTypes.METH_TYPE -> {
CandidTypes.METHOD_TYPE -> {
ChildAttributes(Indent.getNormalIndent(), null)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import com.intellij.psi.codeStyle.CustomCodeStyleSettings
class CandidCodeStyleSettingsProvider : CodeStyleSettingsProvider() {
override fun getConfigurableDisplayName(): String = "Candid"

override fun createCustomSettings(settings: CodeStyleSettings): CustomCodeStyleSettings? {
override fun createCustomSettings(settings: CodeStyleSettings): CustomCodeStyleSettings {
return CandidCodeStyleSettings(settings)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ class CandidFormattingModelBuilder : FormattingModelBuilder {
val fields_tokens = TokenSet.create(
CandidTypes.FIELD_TYPE_RECORD,
CandidTypes.FIELD_TYPE_VARIANT,
CandidTypes.METH_TYPE
CandidTypes.METHOD_TYPE
)

val comment_tokens = TokenSet.create(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,6 @@ class CandidSelfImportInspection : LocalInspectionTool() {
}
}

return problemsHolder.resultsArray;
return problemsHolder.resultsArray
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package com.github.alaanor.candid.inspection

import com.github.alaanor.candid.project.DfxJson
import com.github.alaanor.candid.psi.CandidMethodType
import com.intellij.codeInspection.*
import com.intellij.psi.PsiFile
import com.intellij.psi.util.PsiTreeUtil

class CandidUnusedMethodInspection : LocalInspectionTool() {
override fun checkFile(file: PsiFile, manager: InspectionManager, isOnTheFly: Boolean): Array<ProblemDescriptor>? {
if (!DfxJson.isRustCanister(file.project, file.virtualFile))
return null

val problemsHolder = ProblemsHolder(manager, file, isOnTheFly)
val methods = PsiTreeUtil.findChildrenOfType(file, CandidMethodType::class.java)

methods.forEach { method ->
if (method.reference?.resolve() != null)
return@forEach

problemsHolder.registerProblem(
method.originalElement,
"No matching rust method found for this candid method",
ProblemHighlightType.LIKE_UNUSED_SYMBOL
)
}

return problemsHolder.resultsArray
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package com.github.alaanor.candid.listener

import com.github.alaanor.candid.project.DfxJson
import com.intellij.openapi.vfs.AsyncFileListener
import com.intellij.openapi.vfs.newvfs.events.VFileEvent

class DfxFileListener : AsyncFileListener {
override fun prepareChange(events: MutableList<out VFileEvent>): AsyncFileListener.ChangeApplier? {
var hasInterestingEvent = false

for (event in events)
if (event.path.endsWith("dfx.json"))
hasInterestingEvent = true

if (!hasInterestingEvent) return null

return object : AsyncFileListener.ChangeApplier {
override fun afterVfsChange() {
DfxJson.updateCache()
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package com.github.alaanor.candid.marker

import com.github.alaanor.candid.icon.CandidIcons
import com.github.alaanor.candid.project.DfxJson
import com.github.alaanor.candid.psi.CandidMethodType
import com.github.alaanor.candid.psi.stub.index.CandidStubMethodIndex
import com.github.alaanor.candid.util.CandidRustIcCdkUtil
import com.intellij.codeInsight.daemon.RelatedItemLineMarkerInfo
import com.intellij.codeInsight.daemon.RelatedItemLineMarkerProvider
import com.intellij.codeInsight.navigation.NavigationGutterIconBuilder
import com.intellij.psi.PsiElement
import com.intellij.psi.search.GlobalSearchScope
import com.intellij.psi.stubs.StubIndex
import org.rust.lang.core.psi.RsFunction

class CandidMethodRsLineMarkerProvider : RelatedItemLineMarkerProvider() {

override fun collectNavigationMarkers(
element: PsiElement,
result: MutableCollection<in RelatedItemLineMarkerInfo<*>>
) {
val rsFunction = element as? RsFunction ?: return
val name = CandidRustIcCdkUtil.getName(rsFunction) ?: return
val candidFile = DfxJson.getCandidFileFromRustFile(element.containingFile) ?: return

val candidMethod = StubIndex.getElements(
CandidStubMethodIndex.Key,
name,
element.project,
GlobalSearchScope.fileScope(element.project, candidFile),
CandidMethodType::class.java
).firstOrNull() ?: return

// we found a matching rust method and candid method

val builder = NavigationGutterIconBuilder.create(CandidIcons.FileType)
.setTarget(candidMethod)
.setTooltipText("Navigate to the corresponding candid method")

result.add(builder.createLineMarkerInfo(element.identifier))
}
}
Loading

0 comments on commit e5d6a12

Please sign in to comment.