Skip to content

Commit 9bc2749

Browse files
committed
first iteration of offset tracking
1 parent 88c8dce commit 9bc2749

3 files changed

Lines changed: 119 additions & 18 deletions

File tree

src/main/java/org/nixos/idea/psi/NixStringLiteralEscaper.kt

Lines changed: 115 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -4,30 +4,131 @@ import com.intellij.openapi.util.TextRange
44
import com.intellij.psi.LiteralTextEscaper
55
import com.intellij.psi.PsiLanguageInjectionHost
66
import org.nixos.idea.psi.impl.AbstractNixString
7-
import org.nixos.idea.util.NixIndStringUtil
8-
import org.nixos.idea.util.NixStringUtil
97

108
class NixStringLiteralEscaper(host: AbstractNixString) : LiteralTextEscaper<PsiLanguageInjectionHost>(host) {
119

1210
override fun isOneLine(): Boolean = false
1311

12+
private var outSourceOffsets: IntArray? = null
13+
14+
override fun getRelevantTextRange(): TextRange {
15+
if (myHost.textLength <= 4) return TextRange.EMPTY_RANGE
16+
return TextRange.create(2, myHost.textLength - 2)
17+
}
18+
1419
override fun decode(rangeInsideHost: TextRange, outChars: StringBuilder): Boolean {
20+
// only indented strings supported for now
21+
if (myHost !is NixIndString) return false
22+
1523
val subText: String = rangeInsideHost.substring(myHost.text)
16-
if (myHost is NixIndString) {
17-
outChars.append(NixIndStringUtil.escape(subText))
18-
} else {
19-
NixStringUtil.escape(outChars, subText)
20-
}
21-
return true
24+
25+
26+
val array = IntArray(subText.length + 1)
27+
val success = unescapeAndDecode(subText, outChars, array, interpolations = false)
28+
outSourceOffsets = array
29+
return success
2230
}
2331

2432
override fun getOffsetInHost(offsetInDecoded: Int, rangeInsideHost: TextRange): Int {
25-
// TODO: Implement proper String back-feed support.
26-
// this involves keeping track of text offsets between decoded
27-
// and encoded Nix text. See how Terraform does it here:
28-
// https://github.com/JetBrains/intellij-plugins/blob/master/terraform/src/org/intellij/terraform/hcl/psi/impl/HCLStringLiteralTextEscaper.kt
29-
val offsetInHost = offsetInDecoded + rangeInsideHost.startOffset
30-
return (offsetInHost).coerceIn(rangeInsideHost.startOffset..rangeInsideHost.endOffset)
33+
val offsets = outSourceOffsets ?: throw IllegalStateException("#decode was not called")
34+
val result = if (offsetInDecoded < offsets.size) offsets[offsetInDecoded] else -1
35+
println("gotOffsetInHost decoded=${offsetInDecoded} rangeInsideHost=${rangeInsideHost} result=$result")
36+
return result.coerceIn(2..rangeInsideHost.length) + rangeInsideHost.startOffset
37+
}
38+
39+
companion object {
40+
fun unescapeAndDecode(
41+
chars: String,
42+
outChars: StringBuilder,
43+
sourceOffsets: IntArray?,
44+
interpolations: Boolean
45+
): Boolean {
46+
assert(sourceOffsets == null || sourceOffsets.size == chars.length + 1)
47+
48+
var index = 0
49+
val outOffset = outChars.length
50+
var braces = 0
51+
52+
53+
while (index < chars.length) {
54+
fun updateOffsets(index: Int) {
55+
if (sourceOffsets != null) {
56+
sourceOffsets[outChars.length - outOffset] = index - 1
57+
sourceOffsets[outChars.length - outOffset + 1] = index
58+
}
59+
}
60+
var c = chars[index++]
61+
62+
updateOffsets(index)
63+
64+
65+
if (braces > 0) {
66+
if (c == '{') braces++
67+
else if (c == '}') braces--
68+
outChars.append(c)
69+
continue
70+
}
71+
72+
if (c == '\'') {
73+
if (index == chars.length) return false
74+
c = chars[index++]
75+
76+
if (c != '\'') {
77+
// if what follows isn't another ' then we are not escaping anything,
78+
// so we can continue
79+
outChars.append("\'")
80+
updateOffsets(index - 1)
81+
outChars.append(c)
82+
continue
83+
}
84+
85+
if (index == chars.length) return false
86+
c = chars[index++]
87+
88+
when(c) {
89+
// '' can be escaped by prefixing it with ', i.e., '''.
90+
'\'' -> {
91+
outChars.append("\'")
92+
updateOffsets(index - 1)
93+
outChars.append(c)
94+
}
95+
// $ can be escaped by prefixing it with '' (that is, two single quotes), i.e., ''$.
96+
'$' -> outChars.append(c)
97+
// Linefeed, carriage-return and tab characters can
98+
// be written as ''\n, ''\r, ''\t, and ''\ escapes any other character.
99+
'a' -> outChars.append(0x07.toChar())
100+
'b' -> outChars.append('\b')
101+
'f' -> outChars.append(0x0c.toChar())
102+
'n' -> outChars.append('\n')
103+
't' -> outChars.append('\t')
104+
'r' -> outChars.append('\r')
105+
'v' -> outChars.append(0x0b.toChar())
106+
else -> return false
107+
}
108+
}
109+
110+
// // $ removes any special meaning from the following $.
111+
// if (c == '$') {
112+
// if (index == chars.length) return false
113+
// c = chars[index++]
114+
// if (c != '$') {
115+
// // if what follows isn't another ' then we are not escaping anything,
116+
// // so we can continue
117+
// outChars.append('$')
118+
// updateOffsets(index - 1)
119+
// outChars.append(c)
120+
// continue
121+
// }
122+
// // what here??
123+
// }
124+
125+
126+
if (sourceOffsets != null) {
127+
sourceOffsets[outChars.length - outOffset] = index
128+
}
129+
}
130+
return true
131+
}
31132
}
32133

33134
}

src/main/java/org/nixos/idea/util/NixIndStringUtil.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ package org.nixos.idea.util
22

33
object NixIndStringUtil {
44
/**
5-
* Escapes the given string for use in a double-quoted string expression in the Nix Expression Language.
5+
* Unescapes the given string for use in a double-quoted string expression in the Nix Expression Language.
66
*
77
* See [Nix docs](https://nix.dev/manual/nix/2.22/language/values.html#type-string) for the logic, which
88
* is non-trivial.
@@ -21,7 +21,7 @@ object NixIndStringUtil {
2121
* ```
2222
*/
2323
@JvmStatic
24-
fun escape(chars: CharSequence): String = buildString {
24+
fun unescape(chars: CharSequence): String = buildString {
2525
for ((index, c) in chars.withIndex()) {
2626
fun prevChar() = chars.getOrNull(index - 1)
2727
fun prev2Chars(): String? {

src/test/java/org/nixos/idea/util/NixIndStringUtilTest.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,8 @@ final class NixIndStringUtilTest {
2626
# which needs a surrogate pair to be represented in UTF-16
2727
\uD83C\uDF09 , \uD83C\uDF09
2828
""")
29-
void escape(String unescaped, String expectedResult) {
30-
var str = NixIndStringUtil.escape(unescaped);
29+
void unescape(String unescaped, String expectedResult) {
30+
var str = NixIndStringUtil.unescape(unescaped);
3131
assertEquals(expectedResult, str);
3232
}
3333
}

0 commit comments

Comments
 (0)