diff --git a/README.md b/README.md index 5879d7e..1fb0605 100644 --- a/README.md +++ b/README.md @@ -117,6 +117,27 @@ To override the configuration for Scala/Java files or add a configuration for so headerMappings := headerMappings.value + (HeaderFileType.scala -> HeaderCommentStyle.cppStyleLineComment) ``` +#### Custom comment creators + +You can customize how content gets created by providing your own +`CommentCreator`. For example, this would be a (crude) way to preserve the +copyright year in existing headers but still update the rest: + + CommentStyle.cStyleBlockComment.copy(commentCreator = new CommentCreator() { + val Pattern = "(?s).*?(\\d{4}(-\\d{4})?).*".r + def findYear(header: String): Option[String] = header match { + case Pattern(years, _) => Some(years) + case _ => None + } + override def apply(text: String, existingText: Option[String]): String = { + val newText = CommentStyle.cStyleBlockComment.commentCreator.apply(text, existingText) + existingText + .flatMap(findYear) + .map(year => newText.replace("2017", year)) + .getOrElse(newText) + } + }) + ### Excluding files To exclude some files, use the [sbt's file filters](http://www.scala-sbt.org/0.13/docs/Howto-Customizing-Paths.html#Include%2Fexclude+files+in+the+source+directory): diff --git a/src/main/scala/de/heikoseeberger/sbtheader/CommentStyle.scala b/src/main/scala/de/heikoseeberger/sbtheader/CommentStyle.scala index a591593..db58906 100644 --- a/src/main/scala/de/heikoseeberger/sbtheader/CommentStyle.scala +++ b/src/main/scala/de/heikoseeberger/sbtheader/CommentStyle.scala @@ -27,15 +27,23 @@ import scala.util.matching.Regex */ final case class CommentStyle(commentCreator: CommentCreator, pattern: Regex) { + def apply(licenseText: String, existingHeader: Option[String]): String = + commentCreator(licenseText, existingHeader) + newLine + newLine + + def apply(license: License, existingHeader: Option[String]): String = + apply(license.text, existingHeader) + def apply(licenseText: String): String = - commentCreator(licenseText) + newLine + newLine + apply(licenseText, None) def apply(license: License): String = apply(license.text) } -sealed trait CommentCreator { - def apply(text: String): String +trait CommentCreator { + def apply(text: String, existingText: Option[String] = None): String + + def apply(text: String): String = apply(text, None) } object CommentStyle { @@ -60,7 +68,7 @@ object CommentStyle { object TwirlStyleFramedBlockCommentCreator extends CommentCreator { - def apply(text: String): String = { + def apply(text: String, existingText: Option[String]): String = { val maxLineLength = text.lines.map(_.length).max def fillLine(line: String) = @@ -80,7 +88,7 @@ object TwirlStyleFramedBlockCommentCreator extends CommentCreator { final class LineCommentCreator(linePrefix: String) extends CommentCreator { - override def apply(text: String): String = { + override def apply(text: String, existingText: Option[String]): String = { def prependWithLinePrefix(s: String) = s match { case "" => if (!linePrefix.trim.isEmpty) linePrefix else "" @@ -96,10 +104,10 @@ final class CommentBlockCreator(blockPrefix: String, linePrefix: String, blockSu private val lineCommentCreator = new LineCommentCreator(linePrefix) - def apply(text: String): String = + def apply(text: String, existingText: Option[String]): String = blockPrefix + newLine + lineCommentCreator(text) + newLine + blockSuffix } object IdentityCommentCreator extends CommentCreator { - override def apply(text: String) = text + override def apply(text: String, existingText: Option[String]) = text } diff --git a/src/main/scala/de/heikoseeberger/sbtheader/HeaderCreator.scala b/src/main/scala/de/heikoseeberger/sbtheader/HeaderCreator.scala index fe0d4ff..481a498 100644 --- a/src/main/scala/de/heikoseeberger/sbtheader/HeaderCreator.scala +++ b/src/main/scala/de/heikoseeberger/sbtheader/HeaderCreator.scala @@ -38,7 +38,6 @@ final class HeaderCreator private (fileType: FileType, private val crlf = """(?s)(?:.*)(\r\n)(?:.*)""".r private val cr = """(?s)(?:.*)(\r)(?:.*)""".r - private val headerText = commentStyle(license) private val headerPattern = commentStyle.pattern private val (firstLine, text) = { @@ -67,21 +66,26 @@ final class HeaderCreator private (fileType: FileType, case _ => "\n" } - private val headerNewLine = - headerText match { - case crlf(_) => "\r\n" - case cr(_) => "\r" - case _ => "\n" - } - - private val newHeaderText = headerText.replace(headerNewLine, fileNewLine) + private def newHeaderText(existingHeader: Option[String]) = { + val headerText = commentStyle(license, existingHeader) + val headerNewLine = + headerText match { + case crlf(_) => "\r\n" + case cr(_) => "\r" + case _ => "\n" + } + headerText.replace(headerNewLine, fileNewLine) + } private val modifiedText = text match { - case headerPattern(`headerText`, _) => None - case headerPattern(_, body) => Some(firstLine + newHeaderText + body) - case body if body.isEmpty => None - case body => Some(firstLine + newHeaderText + body.replaceAll("""^\s+""", "")) // Trim left + case headerPattern(existingText, body) => + val newText = newHeaderText(Some(existingText)) + if (newText == existingText) None + else Some(firstLine + newText + body.replaceAll("""^\s+""", "")) // Trim left + case body if body.isEmpty => None + case body => + Some(firstLine + newHeaderText(None) + body.replaceAll("""^\s+""", "")) // Trim left } log.debug(s"Modified text of file is:$newLine$modifiedText") diff --git a/src/test/scala/de/heikoseeberger/sbtheader/HeaderCreatorSpec.scala b/src/test/scala/de/heikoseeberger/sbtheader/HeaderCreatorSpec.scala index e9596bd..7afc651 100644 --- a/src/test/scala/de/heikoseeberger/sbtheader/HeaderCreatorSpec.scala +++ b/src/test/scala/de/heikoseeberger/sbtheader/HeaderCreatorSpec.scala @@ -179,12 +179,51 @@ final class HeaderCreatorSpec extends WordSpec with Matchers { ) } } + + "given a file with an existing copyright year" should { + val yearPreservingStyle = + CommentStyle.cStyleBlockComment.copy(commentCreator = new CommentCreator() { + val Pattern = "(?s).*?(\\d{4}(-\\d{4})?).*".r + def findYear(header: String): Option[String] = header match { + case Pattern(years, _) => Some(years) + case _ => None + } + override def apply(text: String, existingText: Option[String]): String = { + val newText = CommentStyle.cStyleBlockComment.commentCreator.apply(text, existingText) + existingText + .flatMap(findYear) + .map(year => newText.replace("2017", year)) + .getOrElse(newText) + } + }) + val licenseText = "Copyright 2017 MyCorp, Inc " + + "allow updating the header retaining the copyright year" in { + val fileContent = """/* + | * Copyright 2016 MyCorp http://mycorp.com + | */ + |This is the file content + |""".stripMargin + + createHeader(fileContent, licenseText, commentCreator = yearPreservingStyle) shouldBe Some( + """/* + | * Copyright 2016 MyCorp, Inc + | */ + | + |This is the file content + |""".stripMargin + ) + } + } } - private def createHeader(fileContent: String, header: String, fileType: FileType = FileType.sh) = + private def createHeader(fileContent: String, + header: String, + fileType: FileType = FileType.sh, + commentCreator: CommentStyle = hashLineComment) = HeaderCreator( fileType, - hashLineComment, + commentCreator, Custom(header), new StubLogger, new ByteArrayInputStream(fileContent.getBytes)