Skip to content

Commit 19a1b0d

Browse files
authored
Merge pull request #74 from sparsetech/feat/node-text-content
Node: Add textContent function
2 parents d0c6717 + 47d2f36 commit 19a1b0d

File tree

2 files changed

+47
-6
lines changed

2 files changed

+47
-6
lines changed

src/main/scala/pine/Node.scala

Lines changed: 21 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -19,14 +19,18 @@ sealed trait Node {
1919
def map(f: Node => Node): T
2020
def flatMap(f: Node => List[Node]): T
2121
def mapFirst(f: PartialFunction[Node, Node]): T
22+
23+
/** Recursively traverses tree and returns content of first text node */
24+
def textContent: Option[String]
2225
}
2326

2427
case class Text(text: String) extends Node {
2528
override type T = Text
2629

27-
def map(f: Node => Node): T = this
28-
def flatMap(f: Node => List[Node]): T = this
29-
def mapFirst(f: PartialFunction[Node, Node]): T = this
30+
override def map(f: Node => Node): T = this
31+
override def flatMap(f: Node => List[Node]): T = this
32+
override def mapFirst(f: PartialFunction[Node, Node]): T = this
33+
override def textContent: Option[String] = Some(text)
3034
}
3135

3236
case class Tag[TagName <: Singleton](tagName : String with TagName,
@@ -129,7 +133,7 @@ case class Tag[TagName <: Singleton](tagName : String with TagName,
129133
}
130134

131135
/** Recursively map children, excluding root node */
132-
def map(f: Node => Node): Tag[TagName] = set(children.map(f(_).map(f)))
136+
override def map(f: Node => Node): Tag[TagName] = set(children.map(f(_).map(f)))
133137

134138
/** Recursively map tag children, including root node */
135139
def mapRoot(f: Tag[_] => Tag[_]): Tag[TagName] = {
@@ -142,10 +146,10 @@ case class Tag[TagName <: Singleton](tagName : String with TagName,
142146
iter(this).asInstanceOf[T]
143147
}
144148

145-
def flatMap(f: Node => List[Node]): Tag[TagName] =
149+
override def flatMap(f: Node => List[Node]): Tag[TagName] =
146150
copy(children = children.flatMap(n => f(n.flatMap(f))))
147151

148-
def mapFirst(f: PartialFunction[Node, Node]): Tag[TagName] = {
152+
override def mapFirst(f: PartialFunction[Node, Node]): Tag[TagName] = {
149153
var done = false
150154

151155
def m(n: Node): Node =
@@ -168,6 +172,17 @@ case class Tag[TagName <: Singleton](tagName : String with TagName,
168172
def partialMap(f: PartialFunction[Node, Node]): Tag[TagName] =
169173
map(node => f.lift(node).getOrElse(node))
170174

175+
override def textContent: Option[String] = {
176+
for (c <- children) {
177+
c.textContent match {
178+
case Some(c) => return Some(c)
179+
case _ =>
180+
}
181+
}
182+
183+
None
184+
}
185+
171186
/**
172187
* Recursively adds `suffix` to every given attribute.
173188
*

src/test/scala/pine/NodeSpec.scala

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -411,4 +411,30 @@ class NodeSpec extends FunSuite {
411411
val ol = tag.Ol.start(42)
412412
assert(ol.toHtml == "<ol start=\"42\"></ol>")
413413
}
414+
415+
test("textContent") {
416+
// top-level text node
417+
val n = Text("hello world")
418+
assert(n.textContent == Some("hello world"))
419+
420+
// an empty string is treated like a regular text node
421+
val n2 = Text("")
422+
assert(n2.textContent == Some(""))
423+
424+
// one nesting level
425+
val n3 = tag.Div.set(Text("hello"))
426+
assert(n3.textContent == Some("hello"))
427+
428+
// two nesting levels
429+
val n4 = tag.Div.set(tag.Div.set(Text("hello")))
430+
assert(n4.textContent == Some("hello"))
431+
432+
// return only first text node
433+
val n5 = tag.Div.set(List(tag.Div.set(Text("hello")), Text(" world")))
434+
assert(n5.textContent == Some("hello"))
435+
436+
// no text node found
437+
val n6 = tag.Div
438+
assert(n6.textContent == None)
439+
}
414440
}

0 commit comments

Comments
 (0)