Skip to content

Commit

Permalink
fix backtracking with ' and remove minIndent parsing in decode
Browse files Browse the repository at this point in the history
  • Loading branch information
cottand committed Jul 7, 2024
1 parent 43664e2 commit d675347
Show file tree
Hide file tree
Showing 2 changed files with 21 additions and 22 deletions.
26 changes: 12 additions & 14 deletions src/main/java/org/nixos/idea/psi/NixStringLiteralEscaper.kt
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,14 @@ package org.nixos.idea.psi
import com.intellij.openapi.util.TextRange
import com.intellij.psi.LiteralTextEscaper
import com.intellij.psi.PsiLanguageInjectionHost
import org.intellij.lang.annotations.Language
import org.nixos.idea.psi.impl.AbstractNixString

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

override fun isOneLine(): Boolean = false

private var outSourceOffsets: IntArray? = null
private var minIndent: Int? = 0

override fun getRelevantTextRange(): TextRange {
if (myHost.textLength <= 4) return TextRange.EMPTY_RANGE
Expand All @@ -25,10 +25,9 @@ class NixStringLiteralEscaper(host: AbstractNixString) : LiteralTextEscaper<PsiL

val subText: String = rangeInsideHost.substring(myHost.text)
val array = IntArray(subText.length + 1)
val foundIndent = unescapeAndDecode(subText, outChars, array)
val success = unescapeAndDecode(subText, outChars, array)
outSourceOffsets = array
minIndent = foundIndent ?: return false
return true
return success
}

override fun getOffsetInHost(offsetInDecoded: Int, rangeInsideHost: TextRange): Int {
Expand All @@ -44,7 +43,7 @@ class NixStringLiteralEscaper(host: AbstractNixString) : LiteralTextEscaper<PsiL
*
* @returns the minIndent of the string if successful, or null if unsuccessful.
*/
fun unescapeAndDecode(chars: String, outChars: StringBuilder, sourceOffsets: IntArray?): Int? {
fun unescapeAndDecode(chars: String, outChars: StringBuilder, sourceOffsets: IntArray?): Boolean {
assert(sourceOffsets == null || sourceOffsets.size == chars.length + 1)

var index = 0
Expand Down Expand Up @@ -86,19 +85,18 @@ class NixStringLiteralEscaper(host: AbstractNixString) : LiteralTextEscaper<PsiL
}

if (c == '\'') {
if (index == chars.length) return null
if (index == chars.length) return false
c = chars[index++]

if (c != '\'') {
// if what follows isn't another ' then we are not escaping anything,
// so we can continue
// so we can backtrace and continue
outChars.append("\'")
updateOffsets(index - 1)
outChars.append(c)
index--
continue
}

if (index == chars.length) return null
if (index == chars.length) return false
c = chars[index++]

when (c) {
Expand All @@ -111,7 +109,7 @@ class NixStringLiteralEscaper(host: AbstractNixString) : LiteralTextEscaper<PsiL
// $ can be escaped by prefixing it with '' (that is, two single quotes), i.e., ''$.
'$' -> outChars.append(c)
'\\' -> {
if (index == chars.length) return null
if (index == chars.length) return false
c = chars[index++]
when (c) {
// Linefeed, carriage-return and tab characters can
Expand All @@ -123,11 +121,11 @@ class NixStringLiteralEscaper(host: AbstractNixString) : LiteralTextEscaper<PsiL
't' -> outChars.append('\t')
'r' -> outChars.append('\r')
'v' -> outChars.append(0x0b.toChar())
else -> return null
else -> return false
}
}

else -> return null
else -> return false
}
if (sourceOffsets != null) {
sourceOffsets[outChars.length - outOffset] = index
Expand All @@ -137,7 +135,7 @@ class NixStringLiteralEscaper(host: AbstractNixString) : LiteralTextEscaper<PsiL

outChars.append(c)
}
return minIndent
return true
}
}

Expand Down
17 changes: 9 additions & 8 deletions src/main/java/org/nixos/idea/psi/impl/AbstractNixString.kt
Original file line number Diff line number Diff line change
Expand Up @@ -27,17 +27,18 @@ abstract class AbstractNixString(private val astNode: ASTNode) : PsiLanguageInje

val leadingSpace = buildString { repeat(minIndentInOriginal) { append(' ') } }

val withoutQuotesWithIndent = s
// remove quotes
.substring(2..(s.lastIndex - 2))
// restore indent
val lines = s.substring(2..(s.lastIndex - 2)) // remove quotes
.lines()

// restore indent
val withIndent = lines
.withIndex()
.joinToString(separator = System.lineSeparator()) { (index, line) ->
if (index != 0) leadingSpace + line else line
}
.map { (index, line) -> if (index != 0) leadingSpace + line else line }

// if the first line was removed in the fragment, add it back to preserve a multiline string
val withLeadingBlankLine = if (lines.first().isNotEmpty()) listOf("") + withIndent else withIndent

originalNode?.replaceWithText(withoutQuotesWithIndent)
originalNode?.replaceWithText(withLeadingBlankLine.joinToString(separator = System.lineSeparator()))
return this
}

Expand Down

0 comments on commit d675347

Please sign in to comment.