1
+ // Copyright 2000-2021 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
1
2
package cc.unitmesh.language.parser
2
3
3
4
import cc.unitmesh.language.psi.DevInTypes
4
5
import com.intellij.extapi.psi.ASTWrapperPsiElement
5
6
import com.intellij.lang.ASTNode
7
+ import com.intellij.openapi.util.TextRange
6
8
import com.intellij.psi.ElementManipulators
7
9
import com.intellij.psi.LiteralTextEscaper
8
10
import com.intellij.psi.PsiElement
9
11
import com.intellij.psi.PsiLanguageInjectionHost
10
12
import com.intellij.psi.impl.source.tree.injected.InjectionBackgroundSuppressor
11
- import com.intellij.psi.util.PsiTreeUtil
12
- import com.intellij.psi.util.elementType
13
+ import com.intellij.psi.tree.IElementType
14
+ import com.intellij.psi.util.*
13
15
14
16
class CodeBlockElement (node : ASTNode ) : ASTWrapperPsiElement(node), PsiLanguageInjectionHost,
15
17
InjectionBackgroundSuppressor {
16
18
override fun isValidHost (): Boolean {
19
+ // use MarkdownCodeFenceUtils.isAbleToAcceptInjections
17
20
return true
18
21
}
19
22
@@ -22,20 +25,100 @@ class CodeBlockElement(node: ASTNode) : ASTWrapperPsiElement(node), PsiLanguageI
22
25
}
23
26
24
27
override fun createLiteralTextEscaper (): LiteralTextEscaper <out PsiLanguageInjectionHost > {
25
- return LiteralTextEscaper .createSimple (this )
28
+ return CodeBlockLiteralTextEscaper (this )
26
29
}
27
30
28
31
fun getLanguageId (): PsiElement ? {
29
32
return findChildByType(DevInTypes .LANGUAGE_ID )
30
33
}
31
34
32
35
fun getContents (): List <PsiElement > {
33
- val codeContents = children.filter { it.elementType == DevInTypes .CODE_CONTENTS }.firstOrNull() ? : return emptyList()
36
+ return obtainFenceContent(this , withWhitespaces = false ) ? : return emptyList()
37
+ }
38
+
39
+ companion object {
40
+ fun obtainFenceContent (element : CodeBlockElement , withWhitespaces : Boolean ): List <PsiElement >? {
41
+ return when {
42
+ withWhitespaces -> CachedValuesManager .getCachedValue(element) {
43
+ CachedValueProvider .Result .create(getContent(element, true ), element)
44
+ }
45
+ else -> CachedValuesManager .getCachedValue(element) {
46
+ CachedValueProvider .Result .create(getContent(element, false ), element)
47
+ }
48
+ }
49
+ }
50
+
51
+ private fun getContent (element : CodeBlockElement , withWhitespaces : Boolean ): List <PsiElement >? {
52
+ val codeContents = element.children.firstOrNull { it.elementType == DevInTypes .CODE_CONTENTS } ? : return null
53
+
54
+ val psiElements = PsiTreeUtil .collectElements(codeContents) {
55
+ it.elementType == DevInTypes .CODE_CONTENT
56
+ }.toMutableList()
57
+
58
+ return psiElements.toList()
59
+ }
60
+
61
+ fun obtainRelevantTextRange (element : CodeBlockElement ): TextRange {
62
+ val elements = obtainFenceContent(element, withWhitespaces = true ) ? : return getEmptyRange(element)
63
+ val first = elements.first()
64
+ val last = elements.last()
65
+ return TextRange .create(first.startOffsetInParent, last.startOffsetInParent + last.textLength)
66
+ }
67
+
68
+ private fun getEmptyRange (host : CodeBlockElement ): TextRange {
69
+ val start = host.children.find { it.hasType(DevInTypes .LANGUAGE_ID ) }
70
+ ? : host.children.find { it.hasType(DevInTypes .CODE_BLOCK_START ) }
71
+
72
+ return TextRange .from(start!! .startOffsetInParent + start.textLength + 1 , 0 )
73
+ }
74
+ }
75
+ }
76
+
77
+ fun PsiElement.hasType (type : IElementType ): Boolean {
78
+ return PsiUtilCore .getElementType(this ) == type
79
+ }
80
+
81
+ class CodeBlockLiteralTextEscaper (host : CodeBlockElement ) : LiteralTextEscaper<CodeBlockElement>(host) {
82
+ override fun isOneLine (): Boolean = false ;
83
+
84
+ override fun decode (rangeInsideHost : TextRange , outChars : StringBuilder ): Boolean {
85
+ val elements = CodeBlockElement .obtainFenceContent(myHost, withWhitespaces = false ) ? : return true
86
+ for (element in elements) {
87
+ val intersected = rangeInsideHost.intersection(element.textRangeInParent) ? : continue
88
+ outChars.append(intersected.substring(myHost.text))
89
+ }
90
+ return true
91
+ }
34
92
35
- val psiElements = PsiTreeUtil .collectElements(codeContents) {
36
- it.elementType == DevInTypes .CODE_CONTENT
37
- }.toMutableList()
93
+ override fun getOffsetInHost (offsetInDecoded : Int , rangeInsideHost : TextRange ): Int {
94
+ val elements = CodeBlockElement .obtainFenceContent(myHost, withWhitespaces = false ) ? : return - 1
95
+ var cur = 0
96
+ for (element in elements) {
97
+ val intersected = rangeInsideHost.intersection(element.textRangeInParent)
98
+ if (intersected == null || intersected.isEmpty) continue
99
+ if (cur + intersected.length == offsetInDecoded) {
100
+ return intersected.startOffset + intersected.length
101
+ }
102
+ else if (cur == offsetInDecoded) {
103
+ return intersected.startOffset
104
+ }
105
+ else if (cur < offsetInDecoded && cur + intersected.length > offsetInDecoded) {
106
+ return intersected.startOffset + (offsetInDecoded - cur)
107
+ }
108
+ cur + = intersected.length
109
+ }
110
+
111
+ val last = elements[elements.size - 1 ]
112
+ val intersected = rangeInsideHost.intersection(last.textRangeInParent)
113
+ if (intersected == null || intersected.isEmpty) return - 1
114
+ val result = intersected.startOffset + (offsetInDecoded - (cur - intersected.length))
115
+ return if (rangeInsideHost.startOffset <= result && result <= rangeInsideHost.endOffset) {
116
+ result
117
+ }
118
+ else - 1
119
+ }
38
120
39
- return psiElements.toList()
121
+ override fun getRelevantTextRange (): TextRange {
122
+ return CodeBlockElement .obtainRelevantTextRange(myHost)
40
123
}
41
- }
124
+ }
0 commit comments