Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commitb70165b

Browse files
authored
CLI: Add support for dark / light mode that is automatically detected (#19985)
This PR adds support for dark / light mode to the CLI. The mode isautomatically detected on start-up by looking at the terminal backgroundcolor. The old highlight scheme is still available as `.highlight_modemixed` - and is selected if the background color cannot beauto-detected. The highlight mode can also be manually set using the`.highlight_mode` setting, e.g.:```.highlight_mode [light|dark|mixed|auto]```### Dark Background###### Dark Mode<img width="1638" height="1104" alt="Screenshot 2025-11-29 at 08 30 00"src="https://github.com/user-attachments/assets/4b7874d3-202e-4caf-89fd-9474800abd76"/>###### Mixed Mode<img width="1658" height="1104" alt="Screenshot 2025-11-29 at 08 31 47"src="https://github.com/user-attachments/assets/1068ef44-d07f-4114-b16b-fe7b7c0dca08"/>### Light Background###### Light Mode<img width="1640" height="1043" alt="Screenshot 2025-11-29 at 08 31 00"src="https://github.com/user-attachments/assets/a72f5e55-344b-4a05-a140-ae026d2bcbea"/>###### Mixed Mode<img width="1645" height="1047" alt="Screenshot 2025-11-29 at 08 31 17"src="https://github.com/user-attachments/assets/c83659e2-20bc-4bf2-acc3-800c3d1295ec"/>
2 parents209f929 +adc8292 commitb70165b

File tree

11 files changed

+531
-283
lines changed

11 files changed

+531
-283
lines changed

‎tools/shell/include/shell_highlight.hpp‎

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ enum class HighlightElementType : uint32_t {
5050
TABLE_LAYOUT,
5151
VIEW_LAYOUT,
5252
PRIMARY_KEY_COLUMN,
53+
PROMPT,
5354
NONE
5455
};
5556

@@ -76,6 +77,8 @@ struct ShellHighlight {
7677
voidPrintError(string error_msg);
7778

7879
boolSetColor(constchar *element_type,constchar *color,constchar *intensity);
80+
voidSetColor(HighlightElementType element_type, PrintColor print_color, PrintIntensity intensity,
81+
bool user_configured =true);
7982
//! Whether or not a color is part of the extended color set (not available on all platforms)
8083
staticboolIsExtendedColor(PrintColor color);
8184
static optional_ptr<const HighlightColorInfo>GetColorInfo(PrintColor color);
@@ -89,6 +92,9 @@ struct ShellHighlight {
8992
//! Enable or disable highlighting
9093
staticvoidSetHighlighting(bool enabled);
9194

95+
voidToggleMode(HighlightMode mode);
96+
97+
static HighlightElementTypeTryGetHighlightElement(constchar *element_type, string &error_msg);
9298
staticconst HighlightElement &GetHighlightElement(HighlightElementType type);
9399

94100
public:

‎tools/shell/include/shell_prompt.hpp‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,13 +13,14 @@
1313

1414
namespaceduckdb_shell {
1515

16-
enumclassPromptComponentType { LITERAL, SQL, SET_COLOR, SET_INTENSITY, RESET_COLOR, SETTING };
16+
enumclassPromptComponentType { LITERAL, SQL, SET_COLOR, SET_INTENSITY,SET_HIGHLIGHT_ELEMENT,RESET_COLOR, SETTING };
1717

1818
structPromptComponent {
1919
PromptComponentType type;
2020
string literal;
2121
PrintColor color;
2222
PrintIntensity intensity;
23+
HighlightElementType highlight_element;
2324
};
2425

2526
// Supports dynamic prompts

‎tools/shell/include/shell_state.hpp‎

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ struct Prompt;
4646
structShellProgressBar;
4747
structPagerState;
4848
structShellTableInfo;
49+
enumclassHighlightElementType :uint32_t;
4950

5051
usingidx_t =uint64_t;
5152

@@ -98,6 +99,7 @@ enum class ReadLineVersion { LINENOISE, FALLBACK };
9899
enumclassPagerMode { PAGER_AUTOMATIC, PAGER_ON, PAGER_OFF };
99100

100101
enumclassMetadataResult :uint8_t { SUCCESS =0, FAIL =1, EXIT =2, PRINT_USAGE =3 };
102+
enumclassHighlightMode :uint32_t { AUTOMATIC, MIXED_MODE, DARK_MODE, LIGHT_MODE };
101103

102104
enumclassExecuteSQLSingleValueResult {
103105
SUCCESS,
@@ -244,6 +246,8 @@ struct ShellState {
244246
char continuePromptSelected[MAX_PROMPT_SIZE];/* Selected continuation prompt. default: " ...> "*/
245247
//! Progress bar used to render the components that are displayed when query status / progress is rendered
246248
unique_ptr<ShellProgressBar> progress_bar;
249+
//! User-configured highlight elements
250+
duckdb::unordered_set<HighlightElementType> user_configured_elements;
247251

248252
#ifdef HAVE_LINENOISE
249253
ReadLineVersion rl_version = ReadLineVersion::LINENOISE;
@@ -261,6 +265,8 @@ struct ShellState {
261265
idx_t pager_min_columns =5;
262266
//! Whether or not the pager is currently active
263267
bool pager_is_active =false;
268+
//! Shell highlighting mode
269+
HighlightMode highlight_mode = HighlightMode::AUTOMATIC;
264270

265271
#if defined(_WIN32) || defined(WIN32)
266272
//! When enabled, sets the console page to UTF8 and renders using that code page

‎tools/shell/linenoise/include/linenoise.h‎

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,13 @@ typedef struct _linenoiseCompletions {
4747
extern"C" {
4848
#endif
4949

50+
enumlinenoiseTerminalColorMode {
51+
LINENOISE_UNKNOWN_MODE=0,
52+
LINENOISE_DARK_MODE=1,
53+
LINENOISE_LIGHT_MODE=2,
54+
LINENOISE_MIXED_MODE=3
55+
};
56+
5057
typedefvoid(linenoiseCompletionCallback)(constchar*,linenoiseCompletions*);
5158
typedefchar*(linenoiseHintsCallback)(constchar*,int*color,int*bold);
5259
typedefvoid(linenoiseFreeHintsCallback)(void*);
@@ -69,6 +76,7 @@ void linenoiseSetCompletionRendering(int enabled);
6976
size_tlinenoiseComputeRenderWidth(constchar*buf,size_tlen);
7077
intlinenoiseGetRenderPosition(constchar*buf,size_tlen,intmax_width,int*n);
7178
voidlinenoiseSetPrompt(constchar*continuation,constchar*continuationSelected);
79+
intlinenoiseGetTerminalColorMode();
7280

7381
#ifdef__cplusplus
7482
}

‎tools/shell/linenoise/include/terminal.hpp‎

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,12 @@ struct TerminalSize {
9898
int ws_row =0;
9999
};
100100

101+
structTerminalColor {
102+
uint8_t r =0;
103+
uint8_t g =0;
104+
uint8_t b =0;
105+
};
106+
101107
structKeyPress {
102108
KeyPress() {
103109
}
@@ -131,6 +137,7 @@ class Terminal {
131137
staticboolIsAtty();
132138
staticintHasMoreData(int fd);
133139
static TerminalSizeGetTerminalSize();
140+
staticboolTryGetBackgroundColor(TerminalColor &color);
134141

135142
staticchar *EditNoTTY();
136143
staticintEditRaw(char *buf,size_t buflen,constchar *prompt);

‎tools/shell/linenoise/linenoise-c.cpp‎

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -154,3 +154,21 @@ int linenoiseGetRenderPosition(const char *buf, size_t len, int max_width, int *
154154
voidlinenoiseClearScreen(void) {
155155
Terminal::ClearScreen();
156156
}
157+
158+
intlinenoiseGetTerminalColorMode() {
159+
duckdb::TerminalColor background_color;
160+
if (!duckdb::Terminal::TryGetBackgroundColor(background_color)) {
161+
return LINENOISE_UNKNOWN_MODE;
162+
}
163+
// calculate the brightness
164+
double brightness =0.2126 * background_color.r +0.7152 * background_color.g +0.0722 * background_color.b;
165+
// determine light or dark mode based on the brightness
166+
// if the value is too much in the middle we leave it as mixed
167+
if (brightness <=96) {
168+
return LINENOISE_DARK_MODE;
169+
}
170+
if (brightness >=160) {
171+
return LINENOISE_LIGHT_MODE;
172+
}
173+
return LINENOISE_MIXED_MODE;
174+
}

‎tools/shell/linenoise/terminal.cpp‎

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -352,6 +352,98 @@ TerminalSize Terminal::TryMeasureTerminalSize() {
352352
return result;
353353
}
354354

355+
boolParseTerminalColor(TerminalColor &color,constchar *buf,idx_t buflen) {
356+
/* Parse it.*/
357+
// expected format is: rgb:1e1e/1e1e/1e1e
358+
idx_t offset =0;
359+
// find "rgb:"
360+
for (; offset +4 < buflen; offset++) {
361+
if (memcmp(buf + offset, (constvoid *)"rgb:",4) ==0) {
362+
break;
363+
}
364+
}
365+
// now parse the actual r/g/b values
366+
offset +=4;
367+
if (offset >= buflen) {
368+
returnfalse;
369+
}
370+
uint8_t values[3];
371+
memset(values,0,sizeof(values));
372+
373+
for (idx_t k =0; k <3; k++) {
374+
if (k >0) {
375+
// expected a "/"
376+
if (offset >= buflen || buf[offset] !='/') {
377+
returnfalse;
378+
}
379+
offset++;
380+
}
381+
// parse the hexadecimal value
382+
// note that these values are from 0...65535, not from 0...255
383+
uint32_t value =0;
384+
idx_t end_pos = offset +4;
385+
for (; offset < end_pos; offset++) {
386+
if (offset >= buflen) {
387+
returnfalse;
388+
}
389+
auto c = buf[offset];
390+
if (c =='/') {
391+
// found a slash early - done
392+
break;
393+
}
394+
uint32_t current_value;
395+
if (c >='A' && c <='F') {
396+
current_value =10 + (c -'A');
397+
}elseif (c >='a' && c <='f') {
398+
current_value =10 + (c -'a');
399+
}elseif (c >='0' && c <='9') {
400+
current_value = c -'0';
401+
}else {
402+
// unsupported hex value
403+
returnfalse;
404+
}
405+
value = value *16 + current_value;
406+
}
407+
// normalize from
408+
values[k] =static_cast<uint8_t>(value >>8);
409+
}
410+
// found the r/g/b
411+
color.r = values[0];
412+
color.g = values[1];
413+
color.b = values[2];
414+
returntrue;
415+
}
416+
417+
boolTerminal::TryGetBackgroundColor(TerminalColor &color) {
418+
int ifd = STDIN_FILENO;
419+
int ofd = STDOUT_FILENO;
420+
421+
if (Terminal::EnableRawMode() == -1) {
422+
return -1;
423+
}
424+
425+
bool success =false;
426+
if (write(ofd,"\x1b]11;?\007",7) ==7) {
427+
// Read the response: until \a or until we fill up our buffer
428+
char buf[64];
429+
idx_t i =0;
430+
while (i <sizeof(buf) -1) {
431+
if (read(ifd, buf + i,1) !=1) {
432+
break;
433+
}
434+
if (buf[i] =='\a') {
435+
break;
436+
}
437+
i++;
438+
}
439+
buf[i] ='\0';
440+
441+
success =ParseTerminalColor(color, buf, i);
442+
}
443+
Terminal::DisableRawMode();
444+
return success;
445+
}
446+
355447
/* Try to get the number of columns in the current terminal, or assume 80
356448
* if it fails.*/
357449
TerminalSizeTerminal::GetTerminalSize() {

‎tools/shell/shell.cpp‎

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3343,8 +3343,7 @@ void ShellState::Initialize() {
33433343
showHeader =true;
33443344
main_prompt = make_uniq<Prompt>();
33453345
string default_prompt;
3346-
default_prompt =
3347-
"{max_length:40}{color:darkorange}{color:bold}{setting:current_database_and_schema}{color:reset} D";
3346+
default_prompt ="{max_length:40}{highlight_element:prompt}{setting:current_database_and_schema}{color:reset} D";
33483347
main_prompt->ParsePrompt(default_prompt);
33493348
vector<string> default_components;
33503349
default_components.push_back("{setting:progress_bar_percentage} {setting:progress_bar}{setting:eta}");
@@ -3538,8 +3537,19 @@ int wmain(int argc, wchar_t **wargv) {
35383537
if (data.stdin_is_interactive) {
35393538
string zHome;
35403539
constchar *zHistory;
3541-
35423540
ShellHighlighthighlight(data);
3541+
#ifdef HAVE_LINENOISE
3542+
if (data.highlight_mode == HighlightMode::AUTOMATIC && data.stdout_is_console && data.stderr_is_console) {
3543+
// detect terminal colors
3544+
auto terminal_color =linenoiseGetTerminalColorMode();
3545+
if (terminal_color == LINENOISE_DARK_MODE) {
3546+
highlight.ToggleMode(HighlightMode::DARK_MODE);
3547+
}elseif (terminal_color == LINENOISE_LIGHT_MODE) {
3548+
highlight.ToggleMode(HighlightMode::LIGHT_MODE);
3549+
}
3550+
}
3551+
#endif
3552+
35433553
auto startup_version =StringUtil::Format("DuckDB %s (%s",duckdb::DuckDB::LibraryVersion(),
35443554
duckdb::DuckDB::ReleaseCodename());
35453555
if (StringUtil::Contains(duckdb::DuckDB::ReleaseCodename(),"Development")) {

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp