Skip to content

Commit

Permalink
Add support for multiline clippings
Browse files Browse the repository at this point in the history
  • Loading branch information
obruchez committed Feb 9, 2019
1 parent 7744f63 commit de7a3d7
Show file tree
Hide file tree
Showing 4 changed files with 49 additions and 26 deletions.
4 changes: 2 additions & 2 deletions build.sbt
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
name := "Kindle clippings to Markdown"

version := "1.1"
version := "1.2"

scalaVersion := "2.12.4"
scalaVersion := "2.12.8"

scalafmtOnCompile in ThisBuild := true
2 changes: 1 addition & 1 deletion project/build.properties
Original file line number Diff line number Diff line change
@@ -1 +1 @@
sbt.version=1.0.1
sbt.version=1.2.8
2 changes: 1 addition & 1 deletion project/plugins.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,5 @@ logLevel := Level.Warn

resolvers += Resolver.sonatypeRepo("releases")

addSbtPlugin("com.lucidchart" % "sbt-scalafmt" % "1.14")
addSbtPlugin("com.lucidchart" % "sbt-scalafmt" % "1.16")
addSbtPlugin("org.scalastyle" %% "scalastyle-sbt-plugin" % "1.0.0")
Original file line number Diff line number Diff line change
Expand Up @@ -42,34 +42,57 @@ object KindleClippings {
}

def apply(lines: List[String]): KindleClippings = {
val LinesPerBook = 5
val MinLinesPerBook = 5

val clippingsByBook = mutable.HashMap[Book, Vector[Clipping]]()
@annotation.tailrec
def clippingsByBook(remainingLines: List[String],
acc: List[(Book, Clipping)] = List()): Seq[(Book, Clipping)] =
if (remainingLines.size < MinLinesPerBook) {
acc.reverse
} else {
val title :: pageOrLocation :: empty :: clippingContentsAndRemainingLines = remainingLines

for {
title :: pageOrlocation :: empty :: clippingContents :: separator :: Nil <- lines.grouped(
LinesPerBook)
trimmedTitle = title.trim.replaceAll("\uFEFF", "")
trimmedClippingContents = clippingContents.trim
if trimmedClippingContents.nonEmpty
book = Book(trimmedTitle)
clippingsForBook = clippingsByBook.getOrElse(book, Vector[Clipping]())
pageOption = pageOrlocation match {
case Page(page) => Try(page.toInt).toOption
case _ => None
}
locationOption = pageOrlocation match {
case Location(location) => Some(location)
case _ => None
val newRemainingLines =
clippingContentsAndRemainingLines.dropWhile(line => !separator(line)).tail

val clippingContents = clippingContentsAndRemainingLines
.take(clippingContentsAndRemainingLines.size - newRemainingLines.size - 1)
.map(_.trim)
.dropWhile(_.isEmpty)
.reverse
.dropWhile(_.isEmpty)
.reverse

val newAcc =
if (clippingContents.isEmpty) {
acc
} else {
val clippingContentsAsString = clippingContents.mkString("\n")

val trimmedTitle = title.trim.replaceAll("\uFEFF", "")
val book = Book(trimmedTitle)
val pageOption = pageOrLocation match {
case Page(page) => Try(page.toInt).toOption
case _ => None
}
val locationOption = pageOrLocation match {
case Location(location) => Some(location)
case _ => None
}
val clipping = Clipping(clippingContentsAsString, pageOption, locationOption)

(book -> clipping) :: acc
}

clippingsByBook(newRemainingLines, acc = newAcc)
}
clipping = Clipping(trimmedClippingContents, pageOption, locationOption)
} {
clippingsByBook.update(book, clippingsForBook :+ clipping)
}

KindleClippings(Map(clippingsByBook.toSeq.map(kv => kv._1 -> kv._2.distinct): _*))
KindleClippings(
clippingsByBook(lines).groupBy(_._1).map(kv => kv._1 -> kv._2.map(_._2).distinct))
}

private def separator(string: String): Boolean = string.trim.toSet == Set('=')

private val Page = """.*[Pp]age (\d+) .*""".r

private val Location = """.*Loc(?:\.|ation) ([^ ]+) .*""".r
Expand Down

0 comments on commit de7a3d7

Please sign in to comment.