Skip to content

Commit

Permalink
wip
Browse files Browse the repository at this point in the history
  • Loading branch information
134130 committed Jan 4, 2025
1 parent 1ba18ef commit 6a88e1b
Show file tree
Hide file tree
Showing 16 changed files with 2,153 additions and 1 deletion.
1 change: 1 addition & 0 deletions modules/core/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ dependencies {

bundledPlugin("com.intellij.java")
bundledPlugin("org.jetbrains.plugins.terminal")
bundledPlugin("org.toml.lang")

jetbrainsRuntime()
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package com.github.l34130.mise.core.lang

import com.github.l34130.mise.core.icon.MiseIcons
import com.intellij.openapi.fileTypes.LanguageFileType
import javax.swing.Icon

object MiseTomlFileType : LanguageFileType(MiseTomlLanguage) {
override fun getName(): String = "mise"

override fun getDescription(): String = "Mise Configuration file"

override fun getDefaultExtension(): String = "toml"

override fun getIcon(): Icon = MiseIcons.DEFAULT
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package com.github.l34130.mise.core.lang

import com.intellij.lang.Language
import org.toml.lang.TomlLanguage

object MiseTomlLanguage : Language(TomlLanguage, "MiseToml")
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package com.github.l34130.mise.core.lang.completion

import com.github.l34130.mise.core.lang.psi.MiseTomlPsiPatterns
import com.intellij.codeInsight.completion.CompletionContributor
import com.intellij.codeInsight.completion.CompletionType

class MiseTomlCompletionContributor : CompletionContributor() {
init {
extend(CompletionType.BASIC, MiseTomlPsiPatterns.inTaskDependsArray, MiseTomlTaskCompletionProvider())
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
package com.github.l34130.mise.core.lang.completion

import com.github.l34130.mise.core.lang.psi.MiseTomlFile
import com.github.l34130.mise.core.lang.psi.allTasks
import com.github.l34130.mise.core.lang.psi.stringValue
import com.github.l34130.mise.core.lang.psi.taskName
import com.intellij.codeInsight.completion.CompletionParameters
import com.intellij.codeInsight.completion.CompletionProvider
import com.intellij.codeInsight.completion.CompletionResultSet
import com.intellij.codeInsight.lookup.LookupElementBuilder
import com.intellij.psi.util.parentOfType
import com.intellij.util.ProcessingContext
import org.toml.lang.psi.TomlArray
import org.toml.lang.psi.TomlTable

/**
* ```
* [tasks.foo]
* ...
*
* [tasks.<task-name>]
* depends = [ "f<caret>" ]
* #^ Provides completion for "foo"
*/
class MiseTomlTaskCompletionProvider : CompletionProvider<CompletionParameters>() {
override fun addCompletions(
parameters: CompletionParameters,
context: ProcessingContext,
result: CompletionResultSet,
) {
val element = parameters.position
val miseTomlFile = element.containingFile as? MiseTomlFile ?: return

val dependsArray = (element.parent.parent as? TomlArray) ?: return

val parentTable = element.parentOfType<TomlTable>() ?: return
val parentTaskName = parentTable.taskName

for (task in miseTomlFile.allTasks()) {
val taskName = task.name ?: continue
if (dependsArray.elements.any { it.stringValue == taskName }) continue
if (taskName == parentTaskName) continue

result.addElement(
LookupElementBuilder
.createWithSmartPointer(taskName, task)
.withInsertHandler(StringLiteralInsertionHandler()),
)
}
}

// TODO: Need to support literal string completion
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package com.github.l34130.mise.core.lang.completion

import com.intellij.codeInsight.completion.InsertHandler
import com.intellij.codeInsight.completion.InsertionContext
import com.intellij.codeInsight.lookup.LookupElement
import com.intellij.psi.PsiElement
import com.intellij.psi.tree.TokenSet
import com.intellij.psi.util.PsiTreeUtil
import org.toml.lang.psi.TomlElementTypes
import org.toml.lang.psi.TomlLiteral
import org.toml.lang.psi.ext.elementType

class StringLiteralInsertionHandler : InsertHandler<LookupElement> {
override fun handleInsert(
context: InsertionContext,
item: LookupElement,
) {
val leaf = context.getElementOfType<PsiElement>() ?: return
val elementType = (leaf.parent as? TomlLiteral)?.elementType

val hasQuotes = elementType in tokenSet
if (!hasQuotes) {
context.document.insertString(context.startOffset, "\"")
context.document.insertString(context.selectionEndOffset, "\"")
}
}

private val tokenSet =
TokenSet.create(
TomlElementTypes.BASIC_STRING,
TomlElementTypes.LITERAL_STRING,
TomlElementTypes.LITERAL,
)
}

inline fun <reified T : PsiElement> InsertionContext.getElementOfType(strict: Boolean = false): T? =
PsiTreeUtil.findElementOfClassAtOffset(file, tailOffset - 1, T::class.java, strict)
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package com.github.l34130.mise.core.lang.json

import com.github.l34130.mise.core.lang.MiseTomlFileType
import com.intellij.openapi.project.DumbAware
import com.intellij.openapi.project.Project
import com.intellij.openapi.vfs.VirtualFile
import com.jetbrains.jsonSchema.extension.JsonSchemaFileProvider
import com.jetbrains.jsonSchema.extension.JsonSchemaProviderFactory
import com.jetbrains.jsonSchema.extension.SchemaType

class MiseTomlJsonSchemaFileProviderFactory :
JsonSchemaProviderFactory,
DumbAware {
override fun getProviders(project: Project): List<JsonSchemaFileProvider> = listOf(MiseTomlJsonSchemaFileProvider())

class MiseTomlJsonSchemaFileProvider : JsonSchemaFileProvider {
override fun isAvailable(file: VirtualFile): Boolean = file.fileType is MiseTomlFileType

override fun getName(): String = "mise"

override fun getSchemaType(): SchemaType = SchemaType.embeddedSchema

override fun isUserVisible(): Boolean = false

override fun getSchemaFile(): VirtualFile? = JsonSchemaProviderFactory.getResourceFile(javaClass, "/schemas/mise.json")
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package com.github.l34130.mise.core.lang.json

import com.github.l34130.mise.core.lang.MiseTomlLanguage
import com.intellij.psi.PsiElement
import com.jetbrains.jsonSchema.extension.JsonLikePsiWalker
import com.jetbrains.jsonSchema.extension.JsonLikePsiWalkerFactory
import com.jetbrains.jsonSchema.impl.JsonSchemaObject
import org.toml.ide.json.TomlJsonPsiWalker

class MiseTomlPsiWalkerFactory : JsonLikePsiWalkerFactory {
override fun handles(element: PsiElement): Boolean = element.containingFile.language is MiseTomlLanguage

override fun create(schemaObject: JsonSchemaObject): JsonLikePsiWalker = TomlJsonPsiWalker
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package com.github.l34130.mise.core.lang.psi

import com.github.l34130.mise.core.lang.MiseTomlFileType
import com.github.l34130.mise.core.lang.MiseTomlLanguage
import com.intellij.extapi.psi.PsiFileBase
import com.intellij.psi.FileViewProvider

class MiseTomlFile(
viewProvider: FileViewProvider,
) : PsiFileBase(viewProvider, MiseTomlLanguage) {
override fun getFileType() = MiseTomlFileType

override fun toString() = "Mise Toml File"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package com.github.l34130.mise.core.lang.psi

import com.github.l34130.mise.core.lang.MiseTomlLanguage
import com.intellij.lang.ASTNode
import com.intellij.lang.ParserDefinition
import com.intellij.lang.PsiParser
import com.intellij.lexer.Lexer
import com.intellij.openapi.project.Project
import com.intellij.psi.FileViewProvider
import com.intellij.psi.PsiElement
import com.intellij.psi.PsiFile
import com.intellij.psi.tree.IFileElementType
import com.intellij.psi.tree.TokenSet
import org.toml.lang.lexer.TomlLexer
import org.toml.lang.parse.TomlParser
import org.toml.lang.psi.TOML_COMMENTS

class MiseTomlParserDefinition : ParserDefinition {
override fun createLexer(project: Project): Lexer = TomlLexer()

override fun createParser(project: Project): PsiParser = TomlParser()

override fun getFileNodeType(): IFileElementType = FILE

override fun getCommentTokens(): TokenSet = TOML_COMMENTS

override fun getStringLiteralElements(): TokenSet = TokenSet.EMPTY

override fun createElement(node: ASTNode): PsiElement = throw UnsupportedOperationException()

override fun createFile(viewProvider: FileViewProvider): PsiFile = MiseTomlFile(viewProvider)

companion object {
val FILE = IFileElementType(MiseTomlLanguage)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
package com.github.l34130.mise.core.lang.psi

import com.github.l34130.mise.core.lang.MiseTomlFileType
import com.intellij.patterns.ObjectPattern
import com.intellij.patterns.PatternCondition
import com.intellij.patterns.PlatformPatterns
import com.intellij.patterns.PsiElementPattern
import com.intellij.patterns.StandardPatterns
import com.intellij.patterns.VirtualFilePattern
import com.intellij.psi.PsiElement
import com.intellij.util.ProcessingContext
import org.toml.lang.psi.TomlArray
import org.toml.lang.psi.TomlInlineTable
import org.toml.lang.psi.TomlKeyValue
import org.toml.lang.psi.TomlLiteral
import org.toml.lang.psi.TomlTable
import org.toml.lang.psi.TomlTableHeader
import org.toml.lang.psi.ext.TomlLiteralKind
import org.toml.lang.psi.ext.kind
import org.toml.lang.psi.ext.name

object MiseTomlPsiPatterns {
private inline fun <reified I : PsiElement> miseTomlPsiElement(): PsiElementPattern.Capture<I> =
psiElement<I>().inVirtualFile(
VirtualFilePattern().ofType(MiseTomlFileType),
)

fun miseTomlStringLiteral() = miseTomlPsiElement<TomlLiteral>().with("stringLiteral") { e, _ -> e.kind is TomlLiteralKind.String }

val onTaskTableHeader =
miseTomlPsiElement<TomlTableHeader>()
.with("taskCondition") { header, _ -> header.isTaskListHeader }

val onTaskTable =
miseTomlPsiElement<TomlTable>()
.withChild(onTaskTableHeader)

val onSpecificTaskTable =
miseTomlPsiElement<TomlTable>()
.withChild(
psiElement<TomlTableHeader>()
.with("specificTaskCondition") { header, _ ->
header.isSpecificTaskTableHeader
},
)

/**
* ```toml
* [tasks.lint]
* depends = ["build"]
* ^
* ```
*
* or
*
* ```toml
* [tasks]
* lint = { depends = ["build"] }
* ^
* ```
*/
val onSpecificTaskKeyValue =
miseTomlPsiElement<TomlKeyValue>()
.withParent(onSpecificTaskTable.andOr(psiElement<TomlInlineTable>().withSuperParent(2, onTaskTable)))

/**
* ```
* [tasks]
* foo = { $name = [] }
* #^
* ```
*
* ```
* [tasks.foo]
* $name = []
* #^
* ```
*/
fun taskProperty(name: String) =
psiElement<TomlKeyValue>()
.with("name") { e, _ -> e.key.name == name }
.withParent(
onSpecificTaskTable,
// onSpecificTaskTable.andOr(
// psiElement<TomlInlineTable>().withSuperParent(2, onTaskTable),
// ),
)

/**
* ```
* [tasks]
* foo = { version = "*", depends = [] }
* #^
* ```
*
* ```
* [tasks.foo]
* depends = []
* #^
* ```
*/
val onTaskDependsArray =
StandardPatterns.or(
psiElement<TomlArray>()
.withParent(taskProperty("depends")),
psiElement<TomlArray>()
.withParent(taskProperty("depends_post")),
)
val inTaskDependsArray = miseTomlPsiElement<PsiElement>().inside(onTaskDependsArray)

/**
* ```
* [tasks]
* foo = { depends = [ "bar" ] }
* #^
* ```
*
* ```
* [tasks.foo]
* depends = [ "bar" ]
* #^
* ```
*/
val onTaskDependsLiteral = psiElement<PsiElement>().inside(miseTomlStringLiteral().withParent(onTaskDependsArray))

inline fun <reified I : PsiElement> psiElement() = PlatformPatterns.psiElement(I::class.java)

fun <T : Any, Self : ObjectPattern<T, Self>> ObjectPattern<T, Self>.with(
name: String,
cond: (T, ProcessingContext?) -> Boolean,
): Self =
with(
object : PatternCondition<T>(name) {
override fun accepts(
t: T,
context: ProcessingContext?,
): Boolean = cond(t, context)
},
)
}
Loading

0 comments on commit 6a88e1b

Please sign in to comment.