Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 25 additions & 10 deletions src/main/java/org/c3lang/intellij/annotation/C3Annotator.kt
Original file line number Diff line number Diff line change
Expand Up @@ -3,23 +3,34 @@ package org.c3lang.intellij.annotation
import com.intellij.lang.annotation.AnnotationHolder
import com.intellij.lang.annotation.Annotator
import com.intellij.lang.annotation.HighlightSeverity
import com.intellij.psi.PsiComment
import com.intellij.psi.PsiElement
import com.intellij.psi.util.elementType
import org.c3lang.intellij.C3ParserDefinition
import org.c3lang.intellij.psi.C3CallExpr

class C3Annotator : Annotator {
class C3Annotator : Annotator
{

override fun annotate(element: PsiElement, holder: AnnotationHolder) {
when (element) {
override fun annotate(element: PsiElement, holder: AnnotationHolder)
{
when (element)
{
is C3CallExpr -> annotateMissingCallables(element, holder)
is PsiComment ->
{
if (element.elementType == C3ParserDefinition.DOC_COMMENT) annotateDocComment(element, holder)
}
}

org.c3lang.intellij.C3Annotator.INSTANCE.annotate(element, holder)
}

private fun annotateMissingCallables(
callExpr: C3CallExpr,
holder: AnnotationHolder
) {
callExpr: C3CallExpr,
holder: AnnotationHolder
)
{
// val pathIdentExpr = callExpr.expr as? C3PathIdentExpr ?: return
// val nameIdentElement = pathIdentExpr.pathIdent.nameIdentElement ?: return
// val callName = nameIdentElement.text
Expand All @@ -44,19 +55,23 @@ class C3Annotator : Annotator {
// }
}

private fun AnnotationHolder.error(message: String, element: PsiElement) {
private fun AnnotationHolder.error(message: String, element: PsiElement)
{
return newAnnotation(HighlightSeverity.ERROR, message).range(element.textRange).create()
}

private fun AnnotationHolder.warning(message: String, element: PsiElement) {
private fun AnnotationHolder.warning(message: String, element: PsiElement)
{
return newAnnotation(HighlightSeverity.WARNING, message).range(element.textRange).create()
}

private fun AnnotationHolder.weakWarning(message: String, element: PsiElement) {
private fun AnnotationHolder.weakWarning(message: String, element: PsiElement)
{
return newAnnotation(HighlightSeverity.WEAK_WARNING, message).range(element.textRange).create()
}

private fun AnnotationHolder.info(message: String, element: PsiElement) {
private fun AnnotationHolder.info(message: String, element: PsiElement)
{
return newAnnotation(HighlightSeverity.INFORMATION, message).range(element.textRange).create()
}

Expand Down
107 changes: 107 additions & 0 deletions src/main/java/org/c3lang/intellij/annotation/DocComment.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
package org.c3lang.intellij.annotation

import ai.grazie.text.range
import com.intellij.lang.annotation.AnnotationHolder
import com.intellij.lang.annotation.HighlightSeverity
import com.intellij.openapi.editor.DefaultLanguageHighlighterColors
import com.intellij.openapi.util.TextRange
import com.intellij.psi.PsiComment
import com.intellij.psi.PsiWhiteSpace
import org.c3lang.intellij.psi.C3FuncDefinition

internal fun annotateDocComment(element: PsiComment, holder: AnnotationHolder)
{
annotateDocTags(element, holder)
annotateParamTags(element, holder)
}

private fun annotateDocTags(element: PsiComment, holder: AnnotationHolder)
{
val regex = Regex("@(param|return(!)?|deprecated|require|ensure|pure)")
val commentText = element.text
val commentStart = element.textRange.startOffset

regex.findAll(commentText).forEach { match ->
val range = TextRange(commentStart + match.range.first, commentStart + match.range.last + 1)

holder.newSilentAnnotation(HighlightSeverity.INFORMATION)
.range(range)
.textAttributes(DOC_COMMENT_TAG)
.create()
}
}

private fun getUnderlyingFunction(comment: PsiComment): C3FuncDefinition?
{
var next = comment.nextSibling

while (next is PsiWhiteSpace || next is PsiComment)
{
next = next.nextSibling
}

if (next.firstChild is C3FuncDefinition) return next.firstChild as C3FuncDefinition
return null
}

private fun annotateParamTags(element: PsiComment, holder: AnnotationHolder)
{
val function = getUnderlyingFunction(element)
val args = arrayListOf<String>()

if (function != null)
{
function.funcDef.fnParameterList.parameterList?.paramDeclList?.forEach {
args.add(it.parameter.name!!)
}
}

val regex = Regex("@param\\s+((\\[(in|&in|out|&out|inout|&inout)])\\s+)?(\\w+)(\\s+:\\s+(\"((?:[^\"\\\\]|\\\\.)*)\"|`((?:[^`\\\\]|\\\\.)*)`))?")
val commentText = element.text
val commentStart = element.textRange.startOffset

val nonMatchingLines = commentText.lines().filter { it.trim().startsWith("@param") && !regex.matches(it.trim()) }

nonMatchingLines.forEach {
holder.newAnnotation(HighlightSeverity.ERROR, "Invalid syntax")
.range(TextRange(commentStart + it.range.start, commentStart + it.range.endInclusive + 1))
.create()
}

regex.findAll(commentText).forEach { match ->
val contract = match.groups[1]
val name = match.groups[4]!!
val description = match.groups[6]

if (!args.contains(name.value))
{
val range = TextRange(commentStart + match.range.first, commentStart + match.range.last + 1)

holder.newAnnotation(HighlightSeverity.ERROR, "Argument missing in function")
.range(range)
.create()

return@forEach
}

if (contract != null)
{
val contractRange = TextRange(commentStart + contract.range.first, commentStart + contract.range.last + 1)

holder.newSilentAnnotation(HighlightSeverity.INFORMATION)
.range(contractRange)
.textAttributes(DefaultLanguageHighlighterColors.CONSTANT)
.create()
}

if (description != null)
{
val descriptionRange = TextRange(commentStart + description.range.first, commentStart + description.range.last + 1)

holder.newSilentAnnotation(HighlightSeverity.INFORMATION)
.range(descriptionRange)
.textAttributes(DefaultLanguageHighlighterColors.STRING)
.create()
}
}
}
8 changes: 8 additions & 0 deletions src/main/java/org/c3lang/intellij/annotation/Highlights.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package org.c3lang.intellij.annotation

import com.intellij.openapi.editor.colors.TextAttributesKey
import java.awt.Font

val DOC_COMMENT_TAG = TextAttributesKey.createTextAttributesKey("C3_DOC_COMMENT_TAG").apply {
defaultAttributes.fontType = Font.BOLD or Font.ITALIC // kotlin bitwise-or
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,11 @@ import com.intellij.codeInsight.completion.CompletionContributor
import com.intellij.codeInsight.completion.CompletionInitializationContext
import com.intellij.codeInsight.completion.CompletionType
import com.intellij.patterns.PlatformPatterns.psiElement
import org.c3lang.intellij.psi.C3ModuleDefinition

class C3CompletionContributor : CompletionContributor() {
init {
class C3CompletionContributor : CompletionContributor()
{
init
{
val pattern = psiElement()/*.inside(C3ModuleDefinition::class.java)*/

extend(CompletionType.BASIC, pattern, FunctionCompletionContributor)
Expand All @@ -17,9 +18,11 @@ class C3CompletionContributor : CompletionContributor() {
extend(CompletionType.BASIC, pattern, FaultCompletionContributor)
extend(CompletionType.BASIC, pattern, TailExprCompletionContributor)
extend(CompletionType.BASIC, pattern, InitializerListCompletionContributor)
extend(CompletionType.BASIC, pattern, DocCommentCompletionContributor)
}

override fun beforeCompletion(context: CompletionInitializationContext) {
override fun beforeCompletion(context: CompletionInitializationContext)
{
// path
context.dummyIdentifier = DUMMY_IDENTIFIER
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package org.c3lang.intellij.completion

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.patterns.PlatformPatterns.or
import com.intellij.patterns.PlatformPatterns.psiElement
import com.intellij.util.ProcessingContext
import org.c3lang.intellij.C3ParserDefinition

object DocCommentCompletionContributor : CompletionProvider<CompletionParameters>()
{
private val pattern = or(
psiElement().inside(psiElement().withElementType(C3ParserDefinition.DOC_COMMENT)),
)

override fun addCompletions(parameters: CompletionParameters, context: ProcessingContext, result: CompletionResultSet)
{
if (!pattern.accepts(parameters.position) && !pattern.accepts(parameters.originalPosition))
{
return
}

listOf(
"@param",
"@return",
"@return!",
"@deprecated",
"@require",
"@ensure",
"@pure",
"[in]",
"[&in]",
"[out]",
"[&out]",
"[inout]",
"[&inout]"
).forEach {
result.addElement(LookupElementBuilder.create(it))
}
}
}
24 changes: 24 additions & 0 deletions src/main/java/org/c3lang/intellij/docs/C3DocumentationProvider.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package org.c3lang.intellij.docs

import com.intellij.lang.documentation.AbstractDocumentationProvider
import com.intellij.psi.PsiElement
import org.c3lang.intellij.psi.C3ConstDeclarationStmt
import org.c3lang.intellij.psi.C3FuncDef
import org.c3lang.intellij.psi.C3LocalDeclAfterType

class C3DocumentationProvider : AbstractDocumentationProvider()
{
override fun generateDoc(element: PsiElement?, originalElement: PsiElement?): String?
{
if (element is C3FuncDef) return generateFuncDefDoc(element)
if (element is C3LocalDeclAfterType) return generateVarDeclDoc(element)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is not picking up the globals, could that be made to work?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

as of now i don't see a way to detect top level elements as it win't even recognize the module declaration or imports. the other changes are finished i just can't get around to getting this to work

if (element is C3ConstDeclarationStmt) return generateConstDeclDoc(element)

return null
}

override fun generateHoverDoc(element: PsiElement, originalElement: PsiElement?): String?
{
return generateDoc(element, originalElement)
}
}
27 changes: 27 additions & 0 deletions src/main/java/org/c3lang/intellij/docs/ConstDeclDocs.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package org.c3lang.intellij.docs

import com.intellij.lang.documentation.DocumentationMarkup
import com.intellij.openapi.project.Project
import com.intellij.psi.presentation.java.SymbolPresentationUtil
import org.c3lang.intellij.psi.C3ConstDeclarationStmt

internal fun generateConstDeclDoc(element: C3ConstDeclarationStmt): String
{
val name = element.name ?: "Error getting name"
val type = element.type?.text ?: "Error getting type"
val value = element.expr?.text ?: "Error getting value"
val file = SymbolPresentationUtil.getFilePathPresentation(element.containingFile)

return renderFullDoc(file, name, value, type, element.project)
}

private fun renderFullDoc(file: String, name: String, value: String, type: String, project: Project): String
{
val builder = StringBuilder()
appendDefinition("const $type $name = $value", project, builder)
builder.append(DocumentationMarkup.SECTIONS_START)
appendFileSection(file, builder)
builder.append(DocumentationMarkup.SECTIONS_END)

return builder.toString()
}
Loading
Loading