Skip to content
This repository was archived by the owner on Aug 18, 2020. It is now read-only.

Commit 4771db6

Browse files
authored
Merge pull request #16 from daniel0611/bootstrap-progress-bar
Add progress bars for checking and downloading libs in Bootstrap Launcher
2 parents c028b72 + efb86ae commit 4771db6

File tree

3 files changed

+114
-4
lines changed

3 files changed

+114
-4
lines changed

Diff for: bootstrap/build.sbt

+1
Original file line numberDiff line numberDiff line change
@@ -3,4 +3,5 @@ version := "0.1"
33
assemblyJarName in assembly := "ChatOverflow.jar"
44

55
libraryDependencies += "org.scala-lang.modules" %% "scala-xml" % "1.1.1"
6+
libraryDependencies += "org.jline" % "jline-terminal-jansi" % "3.11.0" // used for terminal width
67
fork := true

Diff for: bootstrap/src/main/scala/Bootstrap.scala

+18-4
Original file line numberDiff line numberDiff line change
@@ -125,25 +125,39 @@ object Bootstrap {
125125
* @return false, if there is a serious problem
126126
*/
127127
private def downloadMissingLibraries(dependencies: List[(String, String)]): Boolean = {
128+
val pb = new ProgressBar(dependencies.length)
129+
128130
// using par here to make multiple http requests in parallel, otherwise its awfully slow on internet connections with high RTTs
129-
val missing = dependencies.par.filterNot(dep => isLibraryDownloaded(dep._2)).toList
131+
val missing = dependencies.par.filterNot(dep => {
132+
val (name, url) = dep
133+
pb.countUp()
134+
pb.updateDescription(s"$name@$url")
135+
136+
isLibraryDownloaded(url)
137+
}).toList
138+
139+
pb.finish()
130140

131141
if (missing.isEmpty) {
132142
println("All required libraries are already downloaded.")
133143
} else {
134144
println(s"Downloading ${missing.length} missing libraries...")
135145

136-
for (i <- missing.indices) {
137-
val (name, url) = missing(i)
146+
val pb = new ProgressBar(missing.length)
147+
148+
for ((name, url) <- missing) {
149+
pb.countUp()
150+
pb.updateDescription(s"$name@$url")
138151

139-
println(s"[${i + 1}/${missing.length}] $name ($url)")
140152
if (!downloadLibrary(name, url)) {
141153
// Second try, just in case
142154
if (!downloadLibrary(name, url)) {
143155
return false // error has been displayed, stop bootstrapper from starting with missing lib
144156
}
145157
}
146158
}
159+
160+
pb.finish()
147161
}
148162
true // everything went fine
149163
}

Diff for: bootstrap/src/main/scala/ProgressBar.scala

+95
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
import org.jline.terminal.TerminalBuilder
2+
3+
/**
4+
* Progress bar used reporting the status while checking and downloading libs.
5+
*
6+
* @param max count of events e.g. length of the list which progress is monitored.
7+
*/
8+
class ProgressBar(max: Int) {
9+
// Width of the terminal, used for size calculations
10+
private val width = {
11+
val width = TerminalBuilder.builder().dumb(true).build().getWidth
12+
13+
// Size couldn't be figured out, use a default
14+
if (width <= 10)
15+
80
16+
else
17+
width
18+
}
19+
20+
private var count = 0
21+
private var description = ""
22+
23+
// We need to create a empty line so that latest line before creation won't be overwritten by the draw method.
24+
println()
25+
draw() // Initial draw
26+
27+
/**
28+
* Increases count by 1 and re-draws the progress bar with the updated count.
29+
*/
30+
def countUp(): Unit = {
31+
// Thread-safeness when working with parallel collections
32+
count.synchronized {
33+
count += 1
34+
draw()
35+
}
36+
}
37+
38+
/**
39+
* Updates the description and re-draws the description line.
40+
* @param desc the new description
41+
*/
42+
def updateDescription(desc: String): Unit = {
43+
// Thread-safeness when working with parallel collections
44+
description.synchronized {
45+
description = desc
46+
drawDescription()
47+
}
48+
}
49+
50+
/**
51+
* Deletes the description to result in a blank line. The progress bar will still be visible.
52+
* After this you can normally print at the beginning of a new line and don't start at the end of the description.
53+
*/
54+
def finish(): Unit = {
55+
description = ""
56+
drawDescription()
57+
}
58+
59+
/**
60+
* Draws the progress bar in the line above the current one.
61+
*/
62+
private def draw(): Unit = {
63+
val barWidth = width - 16 // Width of the bar without percentage and counts
64+
val percentage = count * 100 / max
65+
val equalsSigns = "=" * (barWidth * percentage / 100)
66+
val whiteSpaces = " " * (barWidth - equalsSigns.length)
67+
68+
val content = "%3d%% (%2d|%2d) [%s]".format(percentage, count, max, equalsSigns + ">" + whiteSpaces)
69+
70+
print(s"\033[1A\r$content\n")
71+
// | | | |
72+
// Go up 1 line | | |
73+
// Go to beginning of line
74+
// | |
75+
// Actual progress bar
76+
// |
77+
// Go back down for description
78+
}
79+
80+
/**
81+
* Draws the description which is located in the current line.
82+
*/
83+
private def drawDescription(): Unit = {
84+
// Cap the description at the width of the terminal, otherwise a new line is created and everything would shift.
85+
// If the user needs to see a really long url he can just widen his terminal.
86+
val content = description.take(width)
87+
88+
print(s"\r\033[0K$content")
89+
// | |
90+
// Go to beginning
91+
// |
92+
// Clear from cursor to end of the line
93+
}
94+
95+
}

0 commit comments

Comments
 (0)