Skip to content

Commit

Permalink
Add fix support for sources with shebang
Browse files Browse the repository at this point in the history
  • Loading branch information
MaciejG604 committed Aug 1, 2023
1 parent 03a1706 commit 024fed6
Show file tree
Hide file tree
Showing 3 changed files with 95 additions and 8 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,19 @@ object SheBang {

def isShebangScript(content: String): Boolean = sheBangRegex.unanchored.matches(content)

/** 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.replace(firstMatch.toString(), ""))
case None => ("", content)
}
}
else
("", content)

def ignoreSheBangLines(content: String): (String, Boolean) =
if (content.startsWith("#!")) {
val regexMatch = sheBangRegex.findFirstMatchIn(content)
Expand Down
31 changes: 23 additions & 8 deletions modules/cli/src/main/scala/scala/cli/commands/fix/Fix.scala
Original file line number Diff line number Diff line change
Expand Up @@ -148,30 +148,37 @@ object Fix extends ScalaCommand[FixOptions] {
val logger = loggingUtilities.logger

val fromPaths = sources.paths.map { (path, _) =>
val content = os.read(path).toCharArray
val (_, content) = SheBang.partitionOnShebangSection(os.read(path))
logger.debug(s"Extracting directives from ${loggingUtilities.relativePath(path)}")
ExtractedDirectives.from(content, Right(path), logger, _ => None).orExit(logger)
ExtractedDirectives.from(content.toCharArray, Right(path), logger, _ => None).orExit(logger)
}

val fromInMemory = sources.inMemory.map { inMem =>
val originOrPath = inMem.originalPath.map((_, path) => path)
val content = originOrPath match {
case Right(path) =>
logger.debug(s"Extracting directives from ${loggingUtilities.relativePath(path)}")
os.read(path).toCharArray
os.read(path)
case Left(origin) =>
logger.debug(s"Extracting directives from $origin")
inMem.wrapperParamsOpt match {
// In case of script snippets, we need to drop the top wrapper lines
case Some(wrapperParams) => String(inMem.content)
.linesWithSeparators
.drop(wrapperParams.topWrapperLineCount)
.mkString.toCharArray
case None => inMem.content.map(_.toChar)
.mkString
case None => String(inMem.content)
}
}

ExtractedDirectives.from(content, originOrPath, logger, _ => None).orExit(logger)
val (_, contentWithNoShebang) = SheBang.partitionOnShebangSection(content)

ExtractedDirectives.from(
contentWithNoShebang.toCharArray,
originOrPath,
logger,
_ => None
).orExit(logger)
}

fromPaths ++ fromInMemory
Expand Down Expand Up @@ -244,8 +251,16 @@ object Fix extends ScalaCommand[FixOptions] {
): Unit = {
position match {
case Some(Position.File(Right(path), _, _, offset)) =>
val keepLines = toKeep.mkString("", newLine, newLine * 2)
val newContents = keepLines + os.read(path).drop(offset).stripLeading()
val (shebangSection, strippedContent) = SheBang.partitionOnShebangSection(os.read(path))

def ignoreOrAddNewLine(str: String) = if str.isBlank then "" else str + newLine

val keepLines = ignoreOrAddNewLine(shebangSection) + ignoreOrAddNewLine(toKeep.mkString(
"",
newLine,
newLine
))
val newContents = keepLines + strippedContent.drop(offset).stripLeading()
val relativePath = loggingUtilities.relativePath(path)

loggingUtilities.logger.message(s"Removing directives from $relativePath")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,65 @@ class FixTests extends ScalaCliSuite {
}
}

test("fix script with shebang") {
val mainFileName = "main.sc"
val inputs = TestInputs(
os.rel / mainFileName ->
s"""#!/usr/bin/env -S scala-cli shebang
|
|//> using objectWrapper
|//> using dep com.lihaoyi::os-lib:0.9.1 com.lihaoyi::upickle:3.1.2
|
|println(os.pwd)
|""".stripMargin,
os.rel / projectFileName ->
s"""//> using lib "com.lihaoyi::pprint:0.6.6"
|""".stripMargin
)

inputs.fromRoot { root =>

val fixOutput = os.proc(TestUtil.cli, "--power", "fix", ".", "-v", "-v", extraOptions)
.call(cwd = root, mergeErrIntoOut = true).out.trim()

assertNoDiff(
fixOutput,
"""Extracting directives from project.scala
|Extracting directives from main.sc
|Removing directives from project.scala
|Removing directives from main.sc""".stripMargin
)

val projectFileContents = os.read(root / projectFileName)
val mainFileContents = os.read(root / mainFileName)

assertNoDiff(
projectFileContents,
"""// Main
|//> using objectWrapper
|
|//> using dependency "com.lihaoyi::os-lib:0.9.1"
|//> using dependency "com.lihaoyi::pprint:0.6.6"
|//> using dependency "com.lihaoyi::upickle:3.1.2"
|
|""".stripMargin
)

assertNoDiff(
mainFileContents,
"""#!/usr/bin/env -S scala-cli shebang
|
|println(os.pwd)
|""".stripMargin
)

val runProc = os.proc(TestUtil.cli, "--power", "compile", ".", extraOptions)
.call(cwd = root, stderr = os.Pipe)

expect(!runProc.err.trim.contains("Using directives detected in multiple files"))
}
}

test("fix with test scope") {
val mainSubPath = os.rel / "src" / "Main.scala"
val testSubPath = os.rel / "test" / "MyTests.scala"
Expand Down

0 comments on commit 024fed6

Please sign in to comment.