Skip to content

fix for 3725 #3726

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 5 commits into
base: main
Choose a base branch
from
Open
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
Original file line number Diff line number Diff line change
@@ -1,37 +1,40 @@
package scala.build.preprocessing

import scala.util.matching.Regex

object SheBang {
private val sheBangRegex: Regex = s"""(^(#!.*(\\X)?)+(\\X*!#.*)?)""".r

def isShebangScript(content: String): Boolean = sheBangRegex.unanchored.matches(content)
def isShebangScript(content: String): Boolean = content.startsWith("#!")

/** Returns the shebang section and the content without the shebang section */
def partitionOnShebangSection(content: String): (String, String) =
if (content.startsWith("#!")) {
val regexMatch = sheBangRegex.findFirstMatchIn(content)
regexMatch match {
case Some(firstMatch) =>
(firstMatch.toString(), content.replaceFirst(firstMatch.toString(), ""))
case None => ("", content)
val (header, body) = content.indexOf("\n!#") match {
case -1 =>
val line1 = content.take(content.indexOf("\n") + 1)
val remaining = content.drop(line1.length)
val header = if remaining.startsWith("#!") then
val l2eol = remaining.indexOf("\n")
val line2 = remaining.take(l2eol + 1)
line1 + line2
else
line1
val body = content.drop(header.length)
(header, body)
case index =>
var i = index + 1 // skip over leading newline
while (i < content.length && content(i) != '\n') i += 1
// skip over newline
while (i < content.length && (content(i) == '\r' || content(i) == '\n')) i += 1
content.splitAt(i) // split at start of subsequent line
}
(header, body)
}
else
("", content)

def ignoreSheBangLines(content: String): (String, Boolean) =
if (content.startsWith("#!")) {
val regexMatch = sheBangRegex.findFirstMatchIn(content)
regexMatch match {
case Some(firstMatch) =>
content.replace(
firstMatch.toString(),
System.lineSeparator() * firstMatch.toString().split(System.lineSeparator()).length
) -> true
case None => (content, false)
}
}
if (content.startsWith("#!"))
val (header, body) = partitionOnShebangSection(content)
val blankHeader = "\n" * (header.split("\\R", -1).length - 1)
(blankHeader + body, true)
else
(content, false)

Expand Down
39 changes: 34 additions & 5 deletions modules/build/src/test/scala/scala/build/tests/SourcesTests.scala
Original file line number Diff line number Diff line change
Expand Up @@ -308,24 +308,31 @@ class SourcesTests extends TestUtil.ScalaCliBuildSuite {
|#! nix-shell -i scala-cli
|
|println("Hello World")""".stripMargin,
os.rel / "something4.scala" ->
os.rel / "something4.sc" ->
"""#!/usr/bin/scala-cli
|#! nix-shell -i scala-cli
|
|!#
|
|println("Hello World")""".stripMargin,
os.rel / "something5.scala" ->
os.rel / "something5.sc" ->
"""#!/usr/bin/scala-cli
|
|println("Hello World #!")""".stripMargin,
os.rel / "multiline.scala" ->
os.rel / "multiline.sc" ->
"""#!/usr/bin/scala-cli
|# comment
|VAL=1
|!#
|
|println("Hello World #!")""".stripMargin
|println("Hello World #!")""".stripMargin,
os.rel / "hasBangHashInComment.sc" ->
"""#!/usr/bin/scala-cli
|
|
|
|
|println("Hello World !#")""".stripMargin
)
val expectedParsedCodes = Seq(
"""
Expand All @@ -349,7 +356,13 @@ class SourcesTests extends TestUtil.ScalaCliBuildSuite {
|
|
|
|println("Hello World #!")""".stripMargin
|println("Hello World #!")""".stripMargin,
"""
|
|
|
|
|println("Hello World !#")""".stripMargin
)

testInputs.withInputs { (root, inputs) =>
Expand All @@ -375,10 +388,26 @@ class SourcesTests extends TestUtil.ScalaCliBuildSuite {
sources.inMemory.map(_.content).map(s => new String(s, StandardCharsets.UTF_8))

parsedCodes.zip(expectedParsedCodes).foreach { case (parsedCode, expectedCode) =>
showDiff(parsedCode, expectedCode)
expect(parsedCode.contains(expectedCode))
}
}
}
def showDiff(parsed: String, expected: String): Unit = {
if (!parsed.contains(expected)) {
def c2s(c: Char): String = c match {
case '\r' => "\\r"
case '\n' => "\\n"
case s => s"$s"
}
for (((p, e), i) <- (parsed zip expected).zipWithIndex) {
val ps = c2s(p)
val es = c2s(e)
if (ps != es)
System.err.printf("%2d: [%s]!=[%s]\n", i, ps, es)
}
}
}

test("dependencies in .sc - using") {
val testInputs = TestInputs(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,30 @@ class MarkdownCodeBlockTests extends TestUtil.ScalaCliBuildSuite {
)
val Right(Seq(actualResult: MarkdownCodeBlock)) =
MarkdownCodeBlock.findCodeBlocks(os.sub / "Example.md", markdown)
showDiffs(actualResult, expectedResult)
expect(actualResult == expectedResult)
}

test("shebang closing line allowed in scala code") {
val code = """println("Hello !#")""".stripMargin
val markdown =
s"""# Some snippet
|
|```scala
|#!/usr/bin/env -S scala-cli shebang
|$code
|```
|""".stripMargin
val expectedResult =
MarkdownCodeBlock(
info = PlainScalaInfo,
body = "\n" + code,
startLine = 3,
endLine = 4
)
val Right(Seq(actualResult: MarkdownCodeBlock)) =
MarkdownCodeBlock.findCodeBlocks(os.sub / "Example.md", markdown)
showDiffs(actualResult, expectedResult)
expect(actualResult == expectedResult)
}

Expand Down Expand Up @@ -114,8 +138,24 @@ class MarkdownCodeBlockTests extends TestUtil.ScalaCliBuildSuite {
)
val Right(Seq(actualResult: MarkdownCodeBlock)) =
MarkdownCodeBlock.findCodeBlocks(os.sub / "Example.md", markdown)
showDiffs(actualResult, expectedResult)
expect(actualResult == expectedResult)
}
def showDiffs(actual: MarkdownCodeBlock, expect: MarkdownCodeBlock): Unit = {
if actual.toString != expect.toString then
for (((a, b), i) <- (actual.body zip expect.body).zipWithIndex)
if (a != b) {
def c2s(c: Char): String = c match {
case '\r' => "\\r"
case '\n' => "\\n"
case _ => s"$c"
}
val aa = c2s(a)
val bb = c2s(b)
System.err.printf("== index %d: [%s]!=[%s]\n", i, aa, bb)
}
System.err.printf("actual[%s]\nexpect[%s]\n", actual, expect)
}

test("a test Scala snippet is extracted correctly from markdown") {
val code =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,24 @@ class MarkdownCodeWrapperTests extends TestUtil.ScalaCliBuildSuite {
|}}""".stripMargin
)
val result = MarkdownCodeWrapper(os.sub / "Example.md", markdown)
showDiffs(result._1.get.code, expectedScala.code)
expect(result == (Some(expectedScala), None, None))
}
def showDiffs(actual: String, expect: String): Unit = {
if actual.toString != expect.toString then
for (((a, b), i) <- (actual zip expect).zipWithIndex)
if (a != b) {
def c2s(c: Char): String = c match {
case '\r' => "\\r"
case '\n' => "\\n"
case _ => s"$c"
}
val aa = c2s(a)
val bb = c2s(b)
System.err.printf("== index %d: [%s]!=[%s]\n", i, aa, bb)
}
System.err.printf("actual[%s]\nexpect[%s]\n", actual, expect)
}

test("multiple plain Scala code blocks are wrapped correctly") {
val snippet1 = """println("Hello")"""
Expand Down Expand Up @@ -65,6 +81,7 @@ class MarkdownCodeWrapperTests extends TestUtil.ScalaCliBuildSuite {
|}}""".stripMargin
)
val result = MarkdownCodeWrapper(os.sub / "Example.md", markdown)
showDiffs(result._1.get.code, expectedScala.code)
expect(result == (Some(expectedScala), None, None))
}

Expand Down Expand Up @@ -95,6 +112,7 @@ class MarkdownCodeWrapperTests extends TestUtil.ScalaCliBuildSuite {
|}}""".stripMargin
)
val result = MarkdownCodeWrapper(os.sub / "Example.md", markdown)
showDiffs(result._1.get.code, expectedScala.code)
expect(result == (Some(expectedScala), None, None))
}

Expand All @@ -114,6 +132,7 @@ class MarkdownCodeWrapperTests extends TestUtil.ScalaCliBuildSuite {
|""".stripMargin
)
val result = MarkdownCodeWrapper(os.sub / "Example.md", markdown)
showDiffs(result._1.get.code, expectedScala.code)
expect(result == (None, Some(expectedScala), None))
}

Expand All @@ -138,6 +157,7 @@ class MarkdownCodeWrapperTests extends TestUtil.ScalaCliBuildSuite {
|""".stripMargin
)
val result = MarkdownCodeWrapper(os.sub / "Example.md", markdown)
showDiffs(result._1.get.code, expectedScala.code)
expect(result == (None, Some(expectedScala), None))
}

Expand All @@ -158,6 +178,7 @@ class MarkdownCodeWrapperTests extends TestUtil.ScalaCliBuildSuite {
|""".stripMargin
)
val result = MarkdownCodeWrapper(os.sub / "Example.md", markdown)
showDiffs(result._1.get.code, expectedScala.code)
expect(result == (None, None, Some(expectedScala)))
}

Expand Down Expand Up @@ -185,6 +206,7 @@ class MarkdownCodeWrapperTests extends TestUtil.ScalaCliBuildSuite {
|""".stripMargin
)
val result = MarkdownCodeWrapper(os.sub / "Example.md", markdown)
showDiffs(result._1.get.code, expectedScala.code)
expect(result == (None, None, Some(expectedScala)))
}
}
Loading