# limitations under the License.
# Defines some ANSI Terminal Control Escape Sequences.
+#
+# The color methods (e.g. `Text::green`) format the text to appear colored
+# in a ANSI/VT100 terminal. By default, this coloring is skipped if stdout
+# is not a TTY, but it can be forced by setting `force_console_colors = true`.
module console
# A ANSI/VT100 escape sequence.
abstract class TermEscape
# The US-ASCII ESC character.
- protected fun esc: Char do return 27.ascii
+ protected fun esc: Char do return 27.code_point
# The Control Sequence Introducer (CSI).
protected fun csi: String do return "{esc}["
class TermMoveUp
super TermDirectionalMove
- init do end
-
# Move by the specified number of cells.
init by(magnitude: Int) do self.magnitude = magnitude
class TermMoveDown
super TermDirectionalMove
- init do end
-
# Move by the specified number of cells.
init by(magnitude: Int) do self.magnitude = magnitude
redef fun code do return "B"
end
-# ANSI/VT100 code to move the cursor foward by `magnitude` columns (CUF).
+# ANSI/VT100 code to move the cursor forward by `magnitude` columns (CUF).
class TermMoveFoward
super TermDirectionalMove
- init do end
-
# Move by the specified number of cells.
init by(magnitude: Int) do self.magnitude = magnitude
class TermMoveBackward
super TermDirectionalMove
- init do end
-
# Move by the specified number of cells.
init by(magnitude: Int) do self.magnitude = magnitude
# 1 is the left.
var column: Int = 1
- init do end
-
# Move at the specified position.
#
# (1, 1) is the top-left corner of the display.
attributes.add_all(format.attributes)
end
- redef fun to_s: String do return "{csi}{attributes.join(";")}m"
+ redef fun to_s do return "{csi}{attributes.join(";")}m"
# Apply the specified SGR and return `self`.
private fun apply(sgr: String): TermCharFormat do
# Apply normal weight and return `self`.
fun normal_weight: TermCharFormat do return apply("22")
- # Add the attribute that disable inderlining and return `self`.
+ # Add the attribute that disable underlining and return `self`.
fun not_underlined: TermCharFormat do return apply("24")
# Add the attribute that disable blinking and return `self`.
# Apply a blue foreground and return `self`.
fun blue_fg: TermCharFormat do return apply("34")
- # Apply a mangenta foreground and return `self`.
+ # Apply a magenta foreground and return `self`.
fun magenta_fg: TermCharFormat do return apply("35")
# Apply a cyan foreground and return `self`.
# Apply the default foreground and return `self`.
fun default_fg: TermCharFormat do return apply("39")
- # Apply a black backgroud and return `self`.
+ # Apply a black background and return `self`.
fun black_bg: TermCharFormat do return apply("40")
- # Apply a red backgroud and return `self`.
+ # Apply a red background and return `self`.
fun red_bg: TermCharFormat do return apply("41")
- # Apply a green backgroud and return `self`.
+ # Apply a green background and return `self`.
fun green_bg: TermCharFormat do return apply("42")
- # Apply a yellow backgroud and return `self`.
+ # Apply a yellow background and return `self`.
fun yellow_bg: TermCharFormat do return apply("43")
- # Apply a blue backgroud and return `self`.
+ # Apply a blue background and return `self`.
fun blue_bg: TermCharFormat do return apply("44")
- # Apply a mangenta backgroud and return `self`.
+ # Apply a magenta background and return `self`.
fun magenta_bg: TermCharFormat do return apply("45")
- # Apply a cyan backgroud and return `self`.
+ # Apply a cyan background and return `self`.
fun cyan_bg: TermCharFormat do return apply("46")
- # Apply a white backgroud and return `self`.
+ # Apply a white background and return `self`.
fun white_bg: TermCharFormat do return apply("47")
- # Apply the default backgroud and return `self`.
+ # Apply the default background and return `self`.
fun default_bg: TermCharFormat do return apply("49")
end
-# Redefine the `String` class to add functions to color the string.
-redef class String
+# Services to color terminal output
+redef class Text
private fun apply_format(f: TermCharFormat): String do
- return "{f}{self}{normal}"
+ if stdout_isatty or force_console_colors then
+ return "{f}{self}{normal}"
+ else return to_s
end
private fun normal: TermCharFormat do return new TermCharFormat
# Make the text appear in dark gray (or black) in a ANSI/VT100 terminal.
#
- # WARNING: SEE: `TermCharFormat`
+ # SEE: `TermCharFormat`
fun gray: String do return apply_format(normal.black_fg)
# Make the text appear in red in a ANSI/VT100 terminal.
#
- # WARNING: SEE: `TermCharFormat`
+ # SEE: `TermCharFormat`
fun red: String do return apply_format(normal.red_fg)
# Make the text appear in green in a ANSI/VT100 terminal.
#
- # WARNING: SEE: `TermCharFormat`
+ # SEE: `TermCharFormat`
fun green: String do return apply_format(normal.green_fg)
# Make the text appear in yellow in a ANSI/VT100 terminal.
#
- # WARNING: SEE: `TermCharFormat`
+ # SEE: `TermCharFormat`
fun yellow: String do return apply_format(normal.yellow_fg)
# Make the text appear in blue in a ANSI/VT100 terminal.
#
- # WARNING: SEE: `TermCharFormat`
+ # SEE: `TermCharFormat`
fun blue: String do return apply_format(normal.blue_fg)
- # Make the text appear in mangenta in a ANSI/VT100 terminal.
+ # Make the text appear in magenta in a ANSI/VT100 terminal.
#
- # WARNING: SEE: `TermCharFormat`
+ # SEE: `TermCharFormat`
fun purple: String do return apply_format(normal.magenta_fg)
# Make the text appear in cyan in a ANSI/VT100 terminal.
#
- # WARNING: SEE: `TermCharFormat`
+ # SEE: `TermCharFormat`
fun cyan: String do return apply_format(normal.cyan_fg)
# Make the text appear in light gray (or white) in a ANSI/VT100 terminal.
#
- # WARNING: SEE: `TermCharFormat`
+ # SEE: `TermCharFormat`
fun light_gray: String do return apply_format(normal.white_fg)
# Make the text appear in bold in a ANSI/VT100 terminal.
#
- # WARNING: SEE: `TermCharFormat`
+ # SEE: `TermCharFormat`
fun bold: String do return apply_format(normal.bold)
# Make the text underlined in a ANSI/VT100 terminal.
#
- # WARNING: SEE: `TermCharFormat`
+ # SEE: `TermCharFormat`
fun underline: String do return apply_format(normal.underline)
end
+
+# A dynamic progress bar displayable in console.
+#
+# Example:
+# ~~~nitish
+# var max = 10
+# var current = 0
+# var pb = new TermProgress(max, current)
+#
+# pb.display
+# for i in [current + 1 .. max] do
+# nanosleep(1, 0)
+# pb.update(i)
+# end
+#
+# print "\ndone"
+# ~~~
+#
+# Progress bar can accept metadata to display a small amount of data.
+#
+# Example with metadata:
+# ~~~nitish
+# var pb = new TermProgress(10, 0)
+# for i in [0..10] do
+# pb.update(i, "Step {i}")
+# end
+# ~~~
+class TermProgress
+
+ # Max value of the progress bar (business value).
+ var max_value: Int
+
+ # Current value of the progress bar (business value).
+ var current_value: Int
+
+ # Number of columns used to display the progress bar.
+ var max_columns = 70 is writable
+
+ # Get the current percent value.
+ fun current_percentage: Int do
+ return current_value * 100 / max_value
+ end
+
+ # Display the progress bar.
+ #
+ # `metadata` can be used to pass a small amount of data to display after
+ # the progress bar.
+ fun display(metadata: nullable String) do
+ var percent = current_percentage
+ var p = current_value * max_columns / max_value
+ printn "\r{percent}% ["
+ for i in [1..max_columns] do
+ if i < p then
+ printn "="
+ else if i == p then
+ printn ">"
+ else
+ printn " "
+ end
+ end
+ printn "]"
+ if metadata != null then printn " ({metadata})"
+ end
+
+ # Update and display the progress bar.
+ #
+ # See `display`.
+ fun update(new_current: Int, metadata: nullable String) do
+ current_value = new_current
+ display(metadata)
+ end
+end
+
+redef class Sys
+ private var stdout_isatty: Bool = 1.isatty is lazy
+
+ # Force coloring terminal output, even if stdout is not a TTY?
+ #
+ # Defaults to `false`.
+ var force_console_colors = false is writable
+end