Skip to content

Commit afd276a

Browse files
committed
Separate namespace for ANSI code sequences to avoid magic strings
1 parent 0edd017 commit afd276a

File tree

5 files changed

+69
-13
lines changed

5 files changed

+69
-13
lines changed

CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,8 @@ set(GIT2CPP_SRC
5858
${GIT2CPP_SOURCE_DIR}/subcommand/reset_subcommand.hpp
5959
${GIT2CPP_SOURCE_DIR}/subcommand/status_subcommand.cpp
6060
${GIT2CPP_SOURCE_DIR}/subcommand/status_subcommand.hpp
61+
${GIT2CPP_SOURCE_DIR}/utils/ansi_code.cpp
62+
${GIT2CPP_SOURCE_DIR}/utils/ansi_code.hpp
6163
${GIT2CPP_SOURCE_DIR}/utils/common.cpp
6264
${GIT2CPP_SOURCE_DIR}/utils/common.hpp
6365
${GIT2CPP_SOURCE_DIR}/utils/git_exception.cpp

src/utils/ansi_code.cpp

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
#include "ansi_code.hpp"
2+
3+
namespace ansi_code
4+
{
5+
std::string cursor_to_row(size_t row)
6+
{
7+
return "\e[" + std::to_string(row) + "H";
8+
}
9+
10+
bool is_down_arrow(std::string str)
11+
{
12+
return str == "\e[B" || str == "\e[1B]";
13+
}
14+
15+
bool is_escape_char(char ch)
16+
{
17+
return ch == '\e';
18+
}
19+
20+
bool is_up_arrow(std::string str)
21+
{
22+
return str == "\e[A" || str == "\e[1A]";
23+
}
24+
}

src/utils/ansi_code.hpp

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
#pragma once
2+
3+
#include <string>
4+
5+
/**
6+
* ANSI escape codes.
7+
* Use `termcolor` for colours.
8+
*/
9+
namespace ansi_code
10+
{
11+
// Constants.
12+
const std::string bel = "\a"; // ASCII 7, used for audio/visual feedback.
13+
const std::string cursor_to_top = "\e[H";
14+
const std::string erase_screen = "\e[2J";
15+
16+
const std::string enable_alternative_buffer = "\e[?1049h";
17+
const std::string disable_alternative_buffer = "\e[?1049l";
18+
19+
const std::string hide_cursor = "\e[?25l";
20+
const std::string show_cursor = "\e[?25h";
21+
22+
// Functions.
23+
std::string cursor_to_row(size_t row);
24+
25+
bool is_escape_char(char ch);
26+
27+
bool is_down_arrow(std::string str);
28+
bool is_up_arrow(std::string str);
29+
}

src/utils/output.hpp

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
#pragma once
22

33
#include <iostream>
4+
#include "ansi_code.hpp"
45
#include "common.hpp"
56

67
// Scope object to hide the cursor. This avoids
@@ -10,11 +11,11 @@ struct cursor_hider : noncopyable_nonmovable
1011
{
1112
cursor_hider()
1213
{
13-
std::cout << "\e[?25l";
14+
std::cout << ansi_code::hide_cursor;
1415
}
1516

1617
~cursor_hider()
1718
{
18-
std::cout << "\e[?25h";
19+
std::cout << ansi_code::show_cursor;
1920
}
2021
};

src/utils/terminal_pager.cpp

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010

1111
#include <termcolor/termcolor.hpp>
1212

13+
#include "ansi_code.hpp"
1314
#include "terminal_pager.hpp"
1415

1516
terminal_pager::terminal_pager()
@@ -31,7 +32,7 @@ std::string terminal_pager::get_input() const
3132
std::cin.get(ch);
3233
str += ch;
3334

34-
if (ch == '\e') // Start of ANSI escape sequence.
35+
if (ansi_code::is_escape_char(ch)) // Start of ANSI escape sequence.
3536
{
3637
do
3738
{
@@ -83,19 +84,19 @@ bool terminal_pager::process_input(std::string input)
8384
return false;
8485
case '\e': // ANSI escape sequence.
8586
// Cannot switch on a std::string.
86-
if (input == "\e[A" || input == "\e[1A]") // Up arrow.
87+
if (ansi_code::is_up_arrow(input))
8788
{
8889
scroll(true, false); // Up a line.
8990
return false;
9091
}
91-
else if (input == "\e[B" || input == "\e[1B]") // Down arrow.
92+
else if (ansi_code::is_down_arrow(input))
9293
{
9394
scroll(false, false); // Down a line.
9495
return false;
9596
}
9697
}
9798

98-
std::cout << '\a'; // Emit BEL for visual feedback.
99+
std::cout << ansi_code::bel;
99100
return false;
100101
}
101102

@@ -108,8 +109,8 @@ void terminal_pager::render_terminal() const
108109
{
109110
auto end_row_index = m_start_row_index + m_rows - 1;
110111

111-
std::cout << "\e[2J"; // Erase screen.
112-
std::cout << "\e[H"; // Cursor to top.
112+
std::cout << ansi_code::erase_screen;
113+
std::cout << ansi_code::cursor_to_top;
113114

114115
for (size_t i = m_start_row_index; i < end_row_index; i++)
115116
{
@@ -120,7 +121,7 @@ void terminal_pager::render_terminal() const
120121
std::cout << m_lines[i] << std::endl;
121122
}
122123

123-
std::cout << "\e[" << m_rows << "H"; // Move cursor to bottom row of terminal.
124+
std::cout << ansi_code::cursor_to_row(m_rows); // Move cursor to bottom row of terminal.
124125
std::cout << ":";
125126
}
126127

@@ -154,8 +155,7 @@ void terminal_pager::scroll(bool up, bool page)
154155

155156
if (m_start_row_index == old_start_row_index)
156157
{
157-
// No change, emit BEL for visual feedback.
158-
std::cout << '\a';
158+
std::cout << ansi_code::bel;
159159
}
160160
else
161161
{
@@ -188,7 +188,7 @@ void terminal_pager::show()
188188
new_termios.c_lflag &= (~ICANON & ~ECHO);
189189
tcsetattr(fileno(stdin), TCSANOW, &new_termios);
190190

191-
std::cout << "\e[?1049h"; // Enable alternative buffer.
191+
std::cout << ansi_code::enable_alternative_buffer;
192192

193193
m_start_row_index = 0;
194194
render_terminal();
@@ -199,7 +199,7 @@ void terminal_pager::show()
199199
stop = process_input(get_input());
200200
} while (!stop);
201201

202-
std::cout << "\e[?1049l"; // Disable alternative buffer.
202+
std::cout << ansi_code::disable_alternative_buffer;
203203

204204
// Restore original termios settings.
205205
tcsetattr(fileno(stdin), TCSANOW, &old_termios);

0 commit comments

Comments
 (0)