Lots of work on web config

Change to make fish immediately show color changes
This commit is contained in:
ridiculousfish 2012-03-25 03:00:38 -07:00
parent c0655b6b08
commit 0c9a1a56c2
9 changed files with 576 additions and 129 deletions

12
env.cpp
View file

@ -341,7 +341,9 @@ static void react_to_variable_change(const wcstring &key) {
handle_locale(); handle_locale();
} else if (key == L"fish_term256") { } else if (key == L"fish_term256") {
update_fish_term256(); update_fish_term256();
reader_repaint_needed(); reader_react_to_color_change();
} else if (string_prefixes_string(L"fish_color_", key)) {
reader_react_to_color_change();
} }
} }
@ -355,11 +357,6 @@ static void universal_callback( int type,
{ {
const wchar_t *str=0; const wchar_t *str=0;
if( var_is_locale( name ) )
{
handle_locale();
}
switch( type ) switch( type )
{ {
case SET: case SET:
@ -388,6 +385,9 @@ static void universal_callback( int type,
event_fire( &ev ); event_fire( &ev );
ev.arguments.reset(NULL); ev.arguments.reset(NULL);
} }
if (name)
react_to_variable_change(name);
} }
/** /**

View file

@ -44,16 +44,23 @@ static wint_t lookahead_arr[1024];
*/ */
static int lookahead_count = 0; static int lookahead_count = 0;
/** /** Callback function for handling interrupts on reading */
Callback function for handling interrupts on reading
*/
static int (*interrupt_handler)(); static int (*interrupt_handler)();
/** Callback function to be invoked before reading each byte */
static void (*poll_handler)();
void input_common_init( int (*ih)() ) void input_common_init( int (*ih)() )
{ {
interrupt_handler = ih; interrupt_handler = ih;
} }
void input_common_set_poll_callback(void (*handler)(void))
{
poll_handler = handler;
}
void input_common_destroy() void input_common_destroy()
{ {
@ -66,10 +73,14 @@ void input_common_destroy()
static wint_t readb() static wint_t readb()
{ {
unsigned char arr[1]; unsigned char arr[1];
int do_loop = 0; bool do_loop = false;
do do
{ {
/* Invoke any poll handler */
if (poll_handler)
poll_handler();
fd_set fdset; fd_set fdset;
int fd_max=0; int fd_max=0;
int ioport = iothread_port(); int ioport = iothread_port();
@ -88,7 +99,7 @@ static wint_t readb()
} }
do_loop = 0; do_loop = false;
res = select( fd_max + 1, &fdset, 0, 0, 0 ); res = select( fd_max + 1, &fdset, 0, 0, 0 );
if( res==-1 ) if( res==-1 )
@ -113,7 +124,7 @@ static wint_t readb()
} }
do_loop = 1; do_loop = true;
break; break;
} }
default: default:
@ -133,7 +144,7 @@ static wint_t readb()
{ {
debug( 3, L"Wake up on universal variable event" ); debug( 3, L"Wake up on universal variable event" );
env_universal_read_all(); env_universal_read_all();
do_loop = 1; do_loop = true;
if( lookahead_count ) if( lookahead_count )
{ {
@ -148,7 +159,7 @@ static wint_t readb()
{ {
iothread_service_completion(); iothread_service_completion();
} }
do_loop = 1; do_loop = true;
} }
if( FD_ISSET( 0, &fdset ) ) if( FD_ISSET( 0, &fdset ) )
@ -160,7 +171,7 @@ static wint_t readb()
*/ */
return R_EOF; return R_EOF;
} }
do_loop = 0; do_loop = false;
} }
} }
} }

View file

@ -30,6 +30,9 @@ enum
*/ */
void input_common_init( int (*ih)() ); void input_common_init( int (*ih)() );
/* Sets a callback to be invoked every time a byte is read */
void input_common_set_poll_callback(void (*handler)(void));
/** /**
Free memory used by the library Free memory used by the library
*/ */

View file

@ -302,7 +302,10 @@ class reader_data_t
Keep track of whether any internal code has done something Keep track of whether any internal code has done something
which is known to require a repaint. which is known to require a repaint.
*/ */
int repaint_needed; bool repaint_needed;
/** Whether the a screen reset is needed after a repaint. */
bool screen_reset_needed;
}; };
/** /**
@ -446,7 +449,7 @@ static void reader_repaint()
&indents[0], &indents[0],
data->buff_pos ); data->buff_pos );
#endif #endif
data->repaint_needed = 0; data->repaint_needed = false;
} }
/** /**
@ -678,6 +681,9 @@ void reader_init()
shell_modes.c_lflag &= ~ECHO; /* turn off echo mode */ shell_modes.c_lflag &= ~ECHO; /* turn off echo mode */
shell_modes.c_cc[VMIN]=1; shell_modes.c_cc[VMIN]=1;
shell_modes.c_cc[VTIME]=0; shell_modes.c_cc[VTIME]=0;
/* Repaint if necessary before each byte is read. This lets us react immediately to universal variable color changes. */
input_common_set_poll_callback(reader_repaint_if_needed);
} }
@ -699,12 +705,29 @@ void reader_exit( int do_exit, int forced )
void reader_repaint_needed() void reader_repaint_needed()
{ {
if( data ) if (data) {
{ data->repaint_needed = true;
data->repaint_needed = 1;
} }
} }
void reader_repaint_if_needed() {
if (data && data->screen_reset_needed) {
s_reset( &data->screen, false);
data->screen_reset_needed = false;
}
if (data && data->repaint_needed) {
reader_repaint();
/* reader_repaint clears repaint_needed */
}
}
void reader_react_to_color_change() {
if (data) {
data->repaint_needed = true;
data->screen_reset_needed = true;
}
}
/** /**
@ -1651,7 +1674,7 @@ static int handle_completions( std::vector<completion_t> &comp )
} }
free( prefix ); free( prefix );
s_reset( &data->screen, 1 ); s_reset( &data->screen, true);
reader_repaint(); reader_repaint();
} }
@ -2325,7 +2348,7 @@ void reader_pop()
{ {
end_loop = 0; end_loop = 0;
//history_set_mode( data->app_name.c_str() ); //history_set_mode( data->app_name.c_str() );
s_reset( &data->screen, 1 ); s_reset( &data->screen, true);
} }
} }
@ -2678,7 +2701,7 @@ const wchar_t *reader_readline()
exec_prompt(); exec_prompt();
reader_super_highlight_me_plenty( data->buff_pos ); reader_super_highlight_me_plenty( data->buff_pos );
s_reset( &data->screen, 1 ); s_reset( &data->screen, true);
reader_repaint(); reader_repaint();
/* /*
@ -2804,9 +2827,7 @@ const wchar_t *reader_readline()
case R_NULL: case R_NULL:
{ {
if( data->repaint_needed ) reader_repaint_if_needed();
reader_repaint();
break; break;
} }
@ -2814,7 +2835,7 @@ const wchar_t *reader_readline()
{ {
exec_prompt(); exec_prompt();
write_loop( 1, "\r", 1 ); write_loop( 1, "\r", 1 );
s_reset( &data->screen, 0 ); s_reset( &data->screen, false);
reader_repaint(); reader_repaint();
break; break;
} }
@ -3084,7 +3105,7 @@ const wchar_t *reader_readline()
*/ */
default: default:
{ {
s_reset( &data->screen, 1 ); s_reset( &data->screen, true);
reader_repaint(); reader_repaint();
break; break;
} }

View file

@ -75,6 +75,12 @@ void reader_write_title();
*/ */
void reader_repaint_needed(); void reader_repaint_needed();
/** Call this function to tell the reader that some color has changed. */
void reader_react_to_color_change();
/* Repaint immediately if needed. */
void reader_repaint_if_needed();
/** /**
Run the specified command with the correct terminal modes, and Run the specified command with the correct terminal modes, and
while taking care to perform job notification, set the title, etc. while taking care to perform job notification, set the title, etc.

View file

@ -344,7 +344,7 @@ static void s_check_status( screen_t *s)
int prev_line = s->actual.cursor[1]; int prev_line = s->actual.cursor[1];
write_loop( 1, "\r", 1 ); write_loop( 1, "\r", 1 );
s_reset( s, 0 ); s_reset( s, false );
s->actual.cursor[1] = prev_line; s->actual.cursor[1] = prev_line;
} }
} }
@ -606,7 +606,7 @@ static void s_update( screen_t *scr, const wchar_t *prompt )
need_clear = 1; need_clear = 1;
s_move( scr, &output, 0, 0 ); s_move( scr, &output, 0, 0 );
scr->actual_width = screen_width; scr->actual_width = screen_width;
s_reset( scr, 0 ); s_reset( scr, false );
} }
if( wcscmp( prompt, scr->actual_prompt.c_str() ) ) if( wcscmp( prompt, scr->actual_prompt.c_str() ) )
@ -855,7 +855,7 @@ void s_write( screen_t *s,
s_save_status( s ); s_save_status( s );
} }
void s_reset( screen_t *s, int reset_cursor ) void s_reset( screen_t *s, bool reset_cursor )
{ {
CHECK( s, ); CHECK( s, );

View file

@ -85,10 +85,11 @@ class screen_data_t
}; };
/** /**
The struct representing the current and desired screen contents. The class representing the current and desired screen contents.
*/ */
typedef struct class screen_t
{ {
public:
/** /**
The internal representation of the desired screen contents. The internal representation of the desired screen contents.
*/ */
@ -123,8 +124,7 @@ typedef struct
other than from fish's main loop, in which case we need to redraw. other than from fish's main loop, in which case we need to redraw.
*/ */
struct stat prev_buff_1, prev_buff_2, post_buff_1, post_buff_2; struct stat prev_buff_1, prev_buff_2, post_buff_1, post_buff_2;
} };
screen_t;
/** /**
This is the main function for the screen putput library. It is used This is the main function for the screen putput library. It is used
@ -155,6 +155,6 @@ void s_write( screen_t *s,
resizing, there will be one line of garbage for every repaint, resizing, there will be one line of garbage for every repaint,
which will quicly fill the screen. which will quicly fill the screen.
*/ */
void s_reset( screen_t *s, int reset_cursor ); void s_reset( screen_t *s, bool reset_cursor );
#endif #endif

View file

@ -56,34 +56,56 @@ body {
height: 30px; height: 30px;
} }
#master_detail_box { #master_detail_table {
overflow: hidden; display: table;
margin-top: 10px;
} }
#master { #master {
float: left; display: table-cell;
text-align: right; text-align: right;
min-width: 200px; min-width: 200px;
font-size: 16pt; font-size: 16pt;
padding-left: 12px; padding-left: 12px;
padding-bottom: 0px;
margin-top: -7px; margin-top: -7px;
} }
#detail {
display: table-cell;
border: 1px solid #555;
background-color: #181818;
padding-top: 30px;
padding-bottom: 20px;
padding-left: 30px;
padding-right: 30px;
}
#detail_function {
white-space: pre;
overflow: auto;
width: 100%;
}
.master_element { .master_element {
padding-top: 7px; padding-top: 6px;
padding-bottom: 12px; padding-bottom: 11px;
padding-left: 5px; padding-left: 5px;
padding-right: 32px; padding-right: 32px;
font-size: 12pt; font-size: 12pt;
/* Make our border overlap the detail, even if we're unselected (so it doesn't jump when selected) */
position: relative;
left: 1px;
} }
.selected_master_elem { .selected_master_elem {
border: 1px solid #555; border: 1px solid #555;
border-right: none; border-right: none;
/* Make our border overlap the box */ background-color: #181818;
position: relative;
left: 1px; /* Pad one less than .master_element, to accomodate our border. */
background-color: black; padding-top: 5px;
padding-bottom: 10px;
} }
.master_element_text { .master_element_text {
@ -93,8 +115,33 @@ body {
} }
#colorpicker_term256 { #colorpicker_term256 {
padding: 30px; border: solid #444 1px;
border: 1px solid #555; }
.colorpicker_modifiers {
margin-top: 10px;
display:inline-block;
margin-left: auto;
margin-right: auto;
color: #AAA;
font-size: smaller;
}
.colorpicker_modifier_cell {
cursor: pointer;
display:inline-block;
text-align: center;
border: solid #333 2px;
padding: 5px;
margin-top: 5px;
margin-left: auto;
margin-right: auto;
}
.modifier_cell_selected {
color: #CCC;
border-color: #AAA;
background-color: #444;
} }
#data_table { #data_table {
@ -120,16 +167,46 @@ body {
white-space: nowrap; white-space: nowrap;
} }
.colorpicker_ground {
position: relative;
bottom: 50px;
margin: 0px;
height: 50px;
margin-bottom: -50px;
}
.colorpicker_ground_tab {
cursor: pointer;
color: #AAA;
border: solid 2px #555;
padding-top: 5px;
padding-bottom: 5px;
padding-left: 7px;
padding-right: 7px;
display: inline-block;
background-color: black;
margin-right: -2px;
min-width: 110px;
text-align: center;
}
.colorpicker_ground_selected {
background-color: #777;
color: white;
}
.colorpicker_term256_row { .colorpicker_term256_row {
} }
.colorpicker_term256_cell { .colorpicker_term256_cell {
width: 24; width: 27;
height: 24; height: 27;
} }
.colorpicker_cell_selected { .colorpicker_cell_selected {
border: solid white 3px; border: dashed white 3px;
width: 21;
height: 21;
} }
.error_msg { .error_msg {
@ -145,6 +222,10 @@ body {
<script type="text/javascript" src="jquery.js"></script> <script type="text/javascript" src="jquery.js"></script>
<script type="text/javascript"> <script type="text/javascript">
function show_error(msg) {
$('#global_error').text(msg)
}
function request_failed(jqXHR, textStatus, errorThrown) { function request_failed(jqXHR, textStatus, errorThrown) {
msg = '' msg = ''
if (textStatus == "timeout") { if (textStatus == "timeout") {
@ -164,7 +245,7 @@ function request_failed(jqXHR, textStatus, errorThrown) {
if (errorThrown.length > 0) { if (errorThrown.length > 0) {
msg = msg + ' The HTTP reply returned ' + errorThrown msg = msg + ' The HTTP reply returned ' + errorThrown
} }
$('#global_error').text(msg) show_error(msg)
} }
/* Runs a GET request, parses the JSON, and invokes the handler for each element in it. The JSON result is assumed to be an array. */ /* Runs a GET request, parses the JSON, and invokes the handler for each element in it. The JSON result is assumed to be an array. */
@ -182,6 +263,103 @@ function run_get_request(url, handler) {
}) })
} }
/* As above but with POST request. */
function run_post_request(url, data_map, handler) {
$.ajax({
type: "POST",
url: url,
data: data_map,
success: function(data){
$('#global_error').text('')
$.each($.parseJSON(data), function(idx, contents) {
handler(contents)
})
},
error: request_failed
})
}
function rgb_to_hsl(r, g, b){
r /= 255, g /= 255, b /= 255;
var max = Math.max(r, g, b), min = Math.min(r, g, b);
var h, s, l = (max + min) / 2;
if(max == min){
h = s = 0; // achromatic
}else{
var d = max - min;
s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
switch(max){
case r: h = (g - b) / d + (g < b ? 6 : 0); break;
case g: h = (b - r) / d + 2; break;
case b: h = (r - g) / d + 4; break;
}
h /= 6;
}
return [h, s, l];
}
function hsl_to_rgb(h, s, l){
var r, g, b;
if(s == 0){
r = g = b = l; // achromatic
}else{
function hue2rgb(p, q, t){
if(t < 0) t += 1;
if(t > 1) t -= 1;
if(t < 1/6) return p + (q - p) * 6 * t;
if(t < 1/2) return q;
if(t < 2/3) return p + (q - p) * (2/3 - t) * 6;
return p;
}
var q = l < 0.5 ? l * (1 + s) : l + s - l * s;
var p = 2 * l - q;
r = hue2rgb(p, q, h + 1.0/3);
g = hue2rgb(p, q, h);
b = hue2rgb(p, q, h - 1.0/3);
}
return [r * 255, g * 255, b * 255]
}
/* Given an RGB color as a hex string, like FF0033, convert to HSL, apply the function to adjust its lightness, then return the new color as an RGB string */
function adjust_lightness(color_str, func) {
/* Hack to handle for example F00 */
if (color_str.length == 3) {
color_str = color_str[0] + color_str[0] + color_str[1] + color_str[1] + color_str[2] + color_str[2]
}
rgb = parseInt(color_str, 16)
r = (rgb >> 16) & 0xFF
g = (rgb >> 8) & 0xFF
b = (rgb >> 0) & 0xFF
hsl = rgb_to_hsl(r, g, b)
new_lightness = func(hsl[2])
function to_int_str(val) {
str = Math.round(val).toString(16)
while (str.length < 2)
str = '0' + str
return str
}
new_rgb = hsl_to_rgb(hsl[0], hsl[1], new_lightness)
return to_int_str(new_rgb[0]) + to_int_str(new_rgb[1]) + to_int_str(new_rgb[2])
}
/* Given a color, compute the master text color for it, by giving it a minimum brightness */
function master_color_for_color(color_str) {
return adjust_lightness(color_str, function(lightness){
if (lightness < .33) {
lightness = .33
}
return lightness
})
}
function switch_tab(new_tab) { function switch_tab(new_tab) {
/* Switch selected tab */ /* Switch selected tab */
$(".selected_tab").removeClass("selected_tab") $(".selected_tab").removeClass("selected_tab")
@ -190,25 +368,40 @@ function switch_tab(new_tab) {
/* Empty master element */ /* Empty master element */
$('#master').empty() $('#master').empty()
/* Unselect some things */
$(".colorpicker_cell_selected").removeClass('colorpicker_cell_selected')
/* Hide some things */ /* Hide some things */
$('#colorpicker_term256').hide() $('#master_detail_table').hide()
$('#detail_colorpicker').hide()
$('#detail_function').hide()
$('#data_table').hide() $('#data_table').hide()
$('#data_table').empty() $('#data_table').empty()
/* Load something new */ /* Load something new */
if (new_tab == 'tab_colors') { if (new_tab == 'tab_colors') {
run_get_request('/colors/', function(contents){ /* Keep track of whether this is the first element */
var key = contents[0] var first = true
var value = contents[1] run_get_request('/colors/', function(key_and_values){
var color = (value.length > 0 ? '#' + value : '') var key = key_and_values[0]
create_master_element(key, color) var style = new Style(key_and_values[1])
style_map[key] = style
elem = create_master_element(key, style.color, select_color_master_element)
if (first) {
/* It's the first element, so select it, so something gets selected */
select_color_master_element(elem)
first = false
}
}) })
$('#colorpicker_term256').show() $('#detail_colorpicker').show()
$('#master_detail_table').show()
} else if (new_tab == 'tab_functions') { } else if (new_tab == 'tab_functions') {
run_get_request('/functions/', function(contents){ run_get_request('/functions/', function(contents){
create_master_element(contents, '') create_master_element(contents, 'AAAAAA', select_function_master_element)
}) })
$('#data_table').show() $('#detail_function').show()
$('#master_detail_table').show()
} else if (new_tab == 'tab_variables') { } else if (new_tab == 'tab_variables') {
run_get_request('/variables/', function(contents){ run_get_request('/variables/', function(contents){
var name = contents[0] var name = contents[0]
@ -228,47 +421,172 @@ function switch_tab(new_tab) {
return false return false
} }
function select_master_element(elem) { function current_master_element_name() {
$(".selected_master_elem").removeClass("selected_master_elem") /* Get the name of the current color variable, like 'autosuggestion' */
$(elem).addClass("selected_master_elem") var elems = $('.selected_master_elem')
if (elems.length == 0) {
return ''
}
elem = elems[0]
if (elem.id.indexOf('master_') != 0) {
show_error('Unknown color variable')
return ''
}
return elem.id.substring(7)
} }
/* The first index here corresponds to value 16 */ function is_foreground() {
term256_colors = [ /* Returns true if the selected tab is foreground, false if it's background */
"ffd7d7", "d7afaf", "af8787", "875f5f", "ffafaf", "d78787", "af5f5f", "ff8787", //8 who = $('.colorpicker_ground_selected')
"d75f5f", "ff5f5f", "5f0000", "870000", "af0000", "d70000", "ff0000", "ff875f", //16 if (who.length == 0) {
"ffaf87", "d7875f", "ff5f00", "d75f00", "ffd7af", "d7af87", "af875f", "ffaf5f", //24 show_error('Not sure if we are in foreground or background')
"ff8700", "af5f00", "d78700", "ffd787", "d7af5f", "ffaf00", "875f00", "ffd75f", //32 return false
"af8700", "d7af00", "ffd700", "ffffd7", "d7d7af", "afaf87", "87875f", "ffffaf", //40 }
"d7d787", "afaf5f", "ffff87", "d7d75f", "ffff5f", "5f5f00", "878700", "afaf00", //48 return who[0].id == 'foreground'
"d7d700", "ffff00", "d7ff00", "afd700", "87af00", "d7ff5f", "5f8700", "afff00", //56 }
"d7ff87", "afd75f", "87d700", "5faf00", "87ff00", "d7ffaf", "afd787", "87af5f", //64
"afff5f", "5fd700", "5fff00", "afff87", "87d75f", "87ff5f", "d7ffd7", "afd7af", //72
"87af87", "5f875f", "afffaf", "87d787", "5faf5f", "87ff87", "5fd75f", "5fff5f", //80
"005f00", "008700", "00af00", "00d700", "00ff00", "5fff87", "87ffaf", "5fd787", //88
"00ff5f", "00d75f", "afffd7", "87d7af", "5faf87", "5fffaf", "00ff87", "00af5f", //96
"00d787", "87ffd7", "5fd7af", "00ffaf", "00875f", "5fffd7", "00af87", "00d7af", //104
"00ffd7", "d7ffff", "afd7d7", "87afaf", "5f8787", "afffff", "87d7d7", "5fafaf", //112
"87ffff", "5fd7d7", "5fffff", "005f5f", "008787", "00afaf", "00d7d7", "00ffff", //120
"00d7ff", "00afd7", "0087af", "5fd7ff", "005f87", "00afff", "87d7ff", "5fafd7", //128
"0087d7", "005faf", "0087ff", "afd7ff", "87afd7", "5f87af", "5fafff", "005fd7", //136
"005fff", "87afff", "5f87d7", "5f87ff", "d7d7ff", "afafd7", "8787af", "5f5f87", //144
"afafff", "8787d7", "5f5faf", "8787ff", "5f5fd7", "5f5fff", "00005f", "000087", //152
"0000af", "0000d7", "0000ff", "875fff", "af87ff", "875fd7", "5f00ff", "5f00d7", //160
"d7afff", "af87d7", "875faf", "af5fff", "8700ff", "5f00af", "8700d7", "d787ff", //168
"af5fd7", "af00ff", "5f0087", "d75fff", "8700af", "af00d7", "d700ff", "ffd7ff", //176
"d7afd7", "af87af", "875f87", "ffafff", "d787d7", "af5faf", "ff87ff", "d75fd7", //184
"ff5fff", "5f005f", "870087", "af00af", "d700d7", "ff00ff", "ff00d7", "d700af", //192
"af0087", "ff5fd7", "87005f", "ff00af", "ff87d7", "d75faf", "d70087", "af005f", //200
"ff0087", "ffafd7", "d787af", "af5f87", "ff5faf", "d7005f", "ff005f", "ff87af", //208
"d75f87", "ff5f87", "000000", "080808", "121212", "1c1c1c", "262626", "303030", //216
"3a3a3a", "444444", "4e4e4e", "585858", "5f5f5f", "626262", "6c6c6c", "767676", //224
"808080", "878787", "8a8a8a", "949494", "9e9e9e", "a8a8a8", "afafaf", "b2b2b2", //232
"bcbcbc", "c6c6c6", "d0d0d0", "d7d7d7", "dadada", "e4e4e4", "eeeeee", "ffffff" //240
]
term256_colors = [ //222 function current_style() {
/* Returns the style object corresponding to the current color variable */
return style_map[current_master_element_name()]
}
function reflect_style() {
/* Unselect everything */
$('.colorpicker_cell_selected').removeClass('colorpicker_cell_selected')
$('.modifier_cell_selected').removeClass('modifier_cell_selected')
/* Now update the color picker with the current style (if we have one) */
style = current_style()
if (style) {
var adjust = .5
function compute_constrast(lightness){
var new_lightness = lightness + adjust
if (new_lightness > 1.0 || new_lightness < 0.0) {
new_lightness -= 2 * adjust
}
return new_lightness
}
color = is_foreground() ? style.color : style.background_color
var color_cell = $('#color_' + color)
color_cell.addClass('colorpicker_cell_selected')
color_cell.css('border-color', adjust_lightness(is_foreground() ? style.color : style.background_color, compute_constrast))
if (style.underline) {
$('#modifier_underline').addClass('modifier_cell_selected')
}
/* In the master list, ensure the color is visible against the dark background. If we're deselecting, use COLOR_NORMAL */
master_color = style.color ? master_color_for_color(style.color) : COLOR_NORMAL
$('.selected_master_elem').children('.master_element_text').css({'color': master_color, 'border-bottom-color': master_color})
}
}
function select_master_element(elem) {
$('.selected_master_elem').removeClass('selected_master_elem')
$(elem).addClass('selected_master_elem')
}
function select_color_master_element(elem) {
select_master_element(elem)
/* This changed the current style; reflect that */
reflect_style()
}
function select_function_master_element(elem) {
select_master_element(elem)
run_post_request('/get_function/', {
what: current_master_element_name()
}, function(contents){
$('#detail_function').text(contents)
});
}
function post_style_to_server() {
style = current_style()
if (! style)
return
run_post_request('/set_color/', {
what: current_master_element_name(),
color: style.color,
background_color: style.background_color,
bold: style.bold,
underline: style.underline
}, function(contents){
})
}
function picked_color_cell(cell) {
/* Get the color to set */
if (cell.id.indexOf('color_') != 0) {
show_error('Unknown cell')
return
}
color = cell.id.substring(6)
/* Determine whether we are going to select or unselect this cell */
var deselect = $(cell).hasClass('colorpicker_cell_selected')
/* Get the current style */
style = current_style()
if (! style)
return
/* Change the color */
if (is_foreground()) {
style.color = deselect ? '' : color
} else {
style.background_color = deselect ? '' : color
}
/* Show our changes */
reflect_style()
/* Tell the server */
post_style_to_server()
}
function picked_modifier(cell) {
style = current_style()
if (! style)
return
if (cell.id == 'modifier_underline') {
style.underline = ! style.underline
} else if (cell.id == 'modifier_bold') {
style.bold = ! style.bold
} else {
show_error('Unknown cell')
}
reflect_style()
post_style_to_server()
}
function picked_ground(tab) {
/* The function that gets called when a tab is selected */
$('.colorpicker_ground_selected').removeClass('colorpicker_ground_selected')
$(tab).addClass('colorpicker_ground_selected')
reflect_style()
}
/* Class representing a color style */
function Style(stuff) {
this.color = stuff[0]
this.background_color = stuff[1]
this.bold = stuff[2]
this.underline = stuff[3]
}
var style_map = new Array();
/* The first index here corresponds to value 16 */
term256_colors = [ //247
"ffd7d7", "ffd7d7",
"d7afaf", "d7afaf",
"af8787", "af8787",
@ -514,15 +832,21 @@ term256_colors = [ //222
var items_per_row = 15 var items_per_row = 15
var show_labels = 0 var show_labels = 0
var COLOR_NORMAL = 'DDDDDD'
/* Adds a new element to master */ /* Adds a new element to master */
function create_master_element(contents, color) { function create_master_element(contents, color, click_handler) {
if (color.length == 0) color = 'inherit' if (color.length == 0) color = 'inherit'
style_str = 'color: ' + color + '; border-bottom: 1px solid ' + color + ' ;'
$('<div/>', { /* In the master list, ensure the color is visible against the dark background */
master_color = master_color_for_color(color)
style_str = 'color: #' + master_color + '; border-bottom: 1px solid #' + master_color + ' ;'
elem = $('<div/>', {
class: 'master_element', class: 'master_element',
id: 'master_' + contents,
click: function(){ click: function(){
select_master_element(this) click_handler(this)
//$(this).toggleClass('master_element');
} }
}).append( }).append(
$("<span/>", { $("<span/>", {
@ -530,7 +854,10 @@ function create_master_element(contents, color) {
style: style_str, style: style_str,
text: contents, text: contents,
}) })
).appendTo('#master') )
elem.appendTo('#master')
return elem
} }
/* Toggle the no_overflow class */ /* Toggle the no_overflow class */
@ -555,7 +882,7 @@ function create_data_table_element(contents_list) {
cell = $('<td>', { cell = $('<td>', {
class: 'data_table_cell no_overflow', class: 'data_table_cell no_overflow',
style: 'text-align: left', style: 'text-align: left',
onClick: "toggle_overflow(this)" onClick: 'toggle_overflow(this)'
}); });
} }
text_list = contents_list[idx].split("\n") text_list = contents_list[idx].split("\n")
@ -578,19 +905,19 @@ function populate_colorpicker_term256() {
class: 'colorpicker_term256_row' class: 'colorpicker_term256_row'
}) })
var subidx for (var subidx = 0; subidx < items_per_row && idx + subidx < term256_colors.length; subidx++) {
for (subidx = 0; subidx < items_per_row && idx + subidx < term256_colors.length; subidx++) {
cell_style = 'background-color: #' + term256_colors[idx + subidx] cell_style = 'background-color: #' + term256_colors[idx + subidx]
row.append($('<td>', { row.append($('<td>', {
class: 'colorpicker_term256_cell', class: 'colorpicker_term256_cell',
style: cell_style, style: cell_style,
text: show_labels ? String(subidx + idx + 223) : '' id: 'color_' + term256_colors[idx + subidx],
text: show_labels ? String(subidx + idx + 223) : '',
onClick: 'picked_color_cell(this)'
})) }))
} }
$('#colorpicker_term256').append(row) $('#colorpicker_term256').append(row)
} }
} }
$(document).ready(function() { $(document).ready(function() {
@ -611,11 +938,22 @@ $(document).ready(function() {
<div class="tab" id="tab_variables" onClick="switch_tab('tab_variables')">variables</div> <div class="tab" id="tab_variables" onClick="switch_tab('tab_variables')">variables</div>
<div class="tab" id="tab_history" onClick="switch_tab('tab_history')">history</div> <div class="tab" id="tab_history" onClick="switch_tab('tab_history')">history</div>
</div> </div>
<div id="master_detail_box"> <div id="master_detail_table">
<div id="master"> <div id="master">
</div> </div>
<table id="colorpicker_term256"> <div id="detail">
</table> <div id="detail_colorpicker">
<div class="colorpicker_ground">
<div class="colorpicker_ground_tab colorpicker_ground_selected" id="foreground" onClick="picked_ground(this)">Foreground</div><div class="colorpicker_ground_tab" id="background" onClick="picked_ground(this)">Background</div>
</div>
<table id="colorpicker_term256">
</table>
<div class="colorpicker_modifiers">
<div class="colorpicker_modifier_cell" id="modifier_underline" style="text-decoration: underline" onClick='picked_modifier(this)'>Underline</div>
</div>
</div>
<div id="detail_function"></div>
</div>
</div> </div>
<table id="data_table"> <table id="data_table">
<table> <table>
@ -624,6 +962,4 @@ $(document).ready(function() {
</div> </div>
</div> </div>
<a id="thelink">Click me</a>
</body></html> </body></html>

View file

@ -4,7 +4,7 @@ import SimpleHTTPServer
import SocketServer import SocketServer
import webbrowser import webbrowser
import subprocess import subprocess
import re, json, socket, sys import re, json, socket, sys, cgi
def run_fish_cmd(text): def run_fish_cmd(text):
from subprocess import PIPE from subprocess import PIPE
@ -25,12 +25,24 @@ named_colors = {
'white' : 'FFFFFF' 'white' : 'FFFFFF'
} }
def parse_one_color(comp):
""" A basic function to parse a single color value like 'FFA000' """
if comp in named_colors:
# Named color
return named_colors[comp]
elif re.match(r"[0-9a-fA-F]{3}", comp) is not None or re.match(r"[0-9a-fA-F]{6}", comp) is not None:
# Hex color
return comp
else:
# Unknown
return ''
def parse_color(color_str): def parse_color(color_str):
""" A basic function to parse a color string, for example, 'red' '--bold' """ """ A basic function to parse a color string, for example, 'red' '--bold' """
comps = color_str.split(' ') comps = color_str.split(' ')
print "comps: ", comps
color = 'normal' color = 'normal'
background_color = ''
bold, underline = False, False bold, underline = False, False
for comp in comps: for comp in comps:
# Remove quotes # Remove quotes
@ -39,16 +51,22 @@ def parse_color(color_str):
bold = True bold = True
elif comp == '--underline': elif comp == '--underline':
underline = True underline = True
elif comp in named_colors: elif comp.startswith('--background='):
# Named color # Background color
color = named_colors[comp] background_color = parse_one_color(comp[len('--background='):])
elif re.match(r"[0-9a-fA-F]{3}", comp) is not None or re.match(r"[0-9a-fA-F]{6}", comp) is not None:
# Hex color
color = comp
else: else:
# Unknown component # Regular color
pass maybe_color = parse_one_color(comp)
return color if maybe_color: color = maybe_color
return [color, background_color, bold, underline]
def parse_bool(val):
val = val.lower()
if val.startswith('f') or val.startswith('0'): return False
if val.startswith('t') or val.startswith('1'): return True
return bool(val)
class FishVar: class FishVar:
""" A class that represents a variable """ """ A class that represents a variable """
@ -72,7 +90,7 @@ class FishConfigHTTPRequestHandler(SimpleHTTPServer.SimpleHTTPRequestHandler):
result = [] result = []
out, err = run_fish_cmd('set -L') out, err = run_fish_cmd('set -L')
for line in out.split('\n'): for line in out.split('\n'):
for match in re.finditer(r"^fish_color_(\S+) (.+)", line): for match in re.finditer(r"^fish_color_(\S+) ?(.*)", line):
color_name, color_value = match.group(1, 2) color_name, color_value = match.group(1, 2)
result.append([color_name.strip(), parse_color(color_value)]) result.append([color_name.strip(), parse_color(color_value)])
print result print result
@ -81,7 +99,7 @@ class FishConfigHTTPRequestHandler(SimpleHTTPServer.SimpleHTTPRequestHandler):
def do_get_functions(self): def do_get_functions(self):
out, err = run_fish_cmd('functions') out, err = run_fish_cmd('functions')
out = out.strip() out = out.strip()
print out
# Not sure why fish sometimes returns this with newlines # Not sure why fish sometimes returns this with newlines
if "\n" in out: if "\n" in out:
return out.split('\n') return out.split('\n')
@ -124,7 +142,23 @@ class FishConfigHTTPRequestHandler(SimpleHTTPServer.SimpleHTTPRequestHandler):
"Return the color with the given name, or the empty string if there is none" "Return the color with the given name, or the empty string if there is none"
out, err = run_fish_cmd("echo -n $" + name) out, err = run_fish_cmd("echo -n $" + name)
return out return out
def do_set_color_for_variable(self, name, color, background_color, bold, underline):
if not color: color = 'normal'
"Sets a color for a fish color name, like 'autosuggestion'"
command = 'set -U fish_color_' + name
if color: command += ' ' + color
if background_color: command += ' --background=' + background_color
if bold: command += ' --bold'
if underline: command += ' --underline'
out, err = run_fish_cmd(command)
return out
def do_get_function(self, func_name):
out, err = run_fish_cmd('functions ' + func_name)
return out
def do_GET(self): def do_GET(self):
p = self.path p = self.path
if p == '/colors/': if p == '/colors/':
@ -149,7 +183,43 @@ class FishConfigHTTPRequestHandler(SimpleHTTPServer.SimpleHTTPRequestHandler):
# Output JSON # Output JSON
json.dump(output, self.wfile) json.dump(output, self.wfile)
def do_POST(self):
p = self.path
ctype, pdict = cgi.parse_header(self.headers.getheader('content-type'))
if ctype == 'multipart/form-data':
postvars = cgi.parse_multipart(self.rfile, pdict)
elif ctype == 'application/x-www-form-urlencoded':
length = int(self.headers.getheader('content-length'))
postvars = cgi.parse_qs(self.rfile.read(length), keep_blank_values=1)
else:
postvars = {}
if p == '/set_color/':
what = postvars.get('what')
color = postvars.get('color')
background_color = postvars.get('background_color')
bold = postvars.get('bold')
underline = postvars.get('underline')
print "underline: ", underline
if what:
# Not sure why we get lists here?
output = self.do_set_color_for_variable(what[0], color[0], background_color[0], parse_bool(bold[0]), parse_bool(underline[0]))
else:
output = 'Bad request'
elif p == '/get_function/':
what = postvars.get('what')
output = [self.do_get_function(what[0])]
else:
return SimpleHTTPServer.SimpleHTTPRequestHandler.do_POST(self)
# Return valid output
self.send_response(200)
self.send_header('Content-type','text/html')
self.wfile.write('\n')
# Output JSON
json.dump(output, self.wfile)
PORT = 8000 PORT = 8000
while PORT <= 9000: while PORT <= 9000: