Merge branch 'master' into ast

This commit is contained in:
ridiculousfish 2014-01-03 18:12:29 -08:00
commit 426f58aa61
15 changed files with 16644 additions and 1453 deletions

View File

@ -0,0 +1,461 @@
body {
background-color: #292939;
font-family: Courier, "Courier New", monospace;
color: white;
}
#ancestor {
width: 800px;
margin-left: auto;
margin-right: auto;
margin-top: 25px;
}
#parent {
width: 100%;
min-height: 480px;
margin-top: 12px;
}
#tab_parent {
display: table;
width: 100%;
height: 50px;;
}
.tab {
display: table-cell;
border: 1px solid #111;
border-right: none;
padding-bottom: 15px;
padding-top: 15px;
font-size: 17pt;
text-align: center;
width: 15%;
background-color: #292929;
cursor: pointer;
}
#tab_parent :first-child {
border-top-left-radius: 8px;
}
#tab_parent :last-child {
border-right: 1px solid #111;
border-top-right-radius: 8px;
}
.tab_first {
border-top-left-radius: 5px;
border-bottom-left-radius: 5px;
}
.tab_last {
border-top-right-radius: 5px;
border-bottom-right-radius: 5px;
}
.selected_tab {
background-color: black;
border-color: black;
}
#tab_contents {
padding-top: 35px;
width: 100%;
background-color: black;
border-bottom-left-radius: 8px;
border-bottom-right-radius: 8px;
margin-bottom: 20px;
}
.footer {
clear: both;
height: 30px;
}
.master_detail_table {
display: table;
margin-top: 10px;
margin-left: 12px;
margin-right: 12px;
}
.master {
display: table-cell;
text-align: right;
min-width: 200px;
font-size: 16pt;
padding-bottom: 20px;
padding-top: 35px;
vertical-align: top;
}
.detail {
display: table-cell;
border: 1px solid #555;
background-color: #181818;
padding-top: 30px;
padding-bottom: 20px;
padding-left: 30px;
padding-right: 30px;
border-radius: 5;
width: 100%;
}
.detail_function {
white-space: pre-wrap;
width: 100%;
font-size: 11pt;
color: #BBB;
}
.master_element {
padding-top: 6px;
padding-bottom: 11px;
padding-left: 5px;
padding-right: 22px;
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;
border-bottom-style: solid;
border-bottom-width: 0px;
}
.selected_master_elem {
border: 1px solid #555;
border-right: none;
background-color: #181818;
border-top-left-radius: 5;
border-bottom-left-radius: 5;
/* Pad one less than .master_element, to accomodate our border. */
padding-top: 5px;
padding-bottom: 10px;
padding-left: 4px;
}
.master_element_text {
text-decoration: none;
padding-bottom: 1px;
border-bottom-style: inherit;
border-bottom-color: inherit;
border-bottom-width: 1px;
}
.master_element_description {
text-decoration: none;
padding-top: 15px;
font-size: 10pt;
border-bottom-style: inherit;
border-bottom-color: inherit;
border-bottom-width: 1px;
display: none;
}
.selected_master_elem > .master_element_description {
display: inline;
}
/* We have a newline between the label and description; hide it initially, but show it when it's selected */
.master_element > br { display: none; }
.selected_master_elem > br { display: inherit; }
/* Set this class to suppress the border bottom on master_element_texts with visible descriptions */
.master_element_no_border { border-bottom-width: 0 }
.colorpicker_term256 {
border: solid #444 1px;
border-collapse: collapse;
}
.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: groove #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 {
table-layout:fixed;
color: #CCC;
width: 100%;
padding-left: 10px;
padding-right: 10px;
}
.data_table_row {
}
.data_table_cell {
padding-top: 5px;
padding-bottom: 5px;
vertical-align: top;
overflow:hidden;
border-bottom: #444 dotted 1px;
word-wrap: break-word;
}
.history_text {
padding-top: 5px;
padding-bottom: 5px;
vertical-align: top;
overflow:hidden;
border-bottom: #444 dotted 1px;
word-wrap: break-word;
}
.history_delete {
width: 20px;
border-bottom: #444 dotted 1px;
}
/* The CSS we apply when a table row is filtered */
.data_table_row_filtered {
display: none;
}
.no_overflow {
text-overflow: ellipsis;
white-space: nowrap;
}
.colorpicker_target {
margin: 0 0 -50px 0;
position: relative;
bottom: 47px;
float: left; /* for some reason this makes the cells that it overlaps (before adjusting its bottom) clickable in Safari */
}
.colorpicker_target_tab {
cursor: pointer;
color: #555;
border: solid 1px #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_target_selected {
background-color: #181818; /* same as #detail */
color: white;
}
.colorpicker_term256_row {
}
.colorpicker_term256_cell {
width: 18px;
height: 18px;
border: solid black 1px;
}
.colorpicker_term256_selection_indicator {
width: 19px;
height: 16px;
margin: -2px;
border: solid white 3px;
position: relative;
z-index: 2;
}
.colorpicker_cell_selected {
border: dashed white 3px;
width: 12px;
height: 12px;
}
.colorpicker_text_sample, .colorpicker_text_sample_tight {
font-size: 12pt;
padding: 25px;
margin: 5px 20px 25px 20px; /* top right bottom left */
cursor: pointer;
line-height: 1.8em;
border: solid #777 1px;
position: relative; /* so that our absolutely positioned elements work */
}
.colorpicker_text_sample_tight {
font-size: 10pt;
line-height: 1.2em;
margin: 0px 6px;
max-width: 220px;
padding: 5px;
white-space:nowrap;
overflow: hidden;
text-overflow: clip;
}
.color_picker_background_cells {
position: absolute;
right: 0px;
top: 0px;
}
.color_picker_background_cells div {
width: 14px;
height: 14px;
border-style: solid;
border-color: #777;
border-width: 0 0 1px 1px; /* top right bottom left */
float: left;
}
.color_scheme_choice_label {
margin-left: 10px;
margin-bottom: 3px;
cursor: pointer;
font-size: 12pt;
white-space: normal;
}
.color_scheme_choices_list {
overflow-y: hidden; /* makes our height account for floats */
padding: 0 10px 15px 10px; /* top right bottom left */
}
.color_scheme_choice_container {
float: left;
padding: 5px;
}
.fake_cursor {
background-color: #999;
}
.error_msg {
color: red;
font-size: 12pt;
margin-left: 24pt;
margin-top: 5pt;
margin-bottom: 5pt;
}
img.delete_icon {
width: 20px;
height: 20px;
cursor: pointer;
text-decoration: none;
border: none;
}
#table_filter_container {
/* top right bottom left*/
padding: 0 10 10 30;
text-align: right;
position: relative;
bottom: 10px;
}
.filter_text_box {
width: 250px;
padding: 5 10 5 10;
background-color: #888;
border: #222 solid 3px;
border-radius: 15px;
font-size: 12pt;
color: white;
font-weight: bold;
}
.text_box_transient {
color: #C8C8C8;
}
.prompt_demo {
font-size: 12pt;
padding: 10px;
margin: 5px 20px 25px; /* top right bottom left */
cursor: pointer;
line-height: 1.8em;
border: solid #777 1px;
position: relative; /* so that our absolutely positioned elements work */
}
.save_button, .prompt_save_button {
border-radius: 5px;
border: solid rgba(71,71,71,0.5) 1px;
padding: 5px 8px;
font-size: 10pt;
display: inline-block;
margin-top: 12px;
background-color: rgba(128,128,128,0.2);
color: #FFF;
cursor: pointer;
}
.prompt_save_button {
background-color: #333;
border: solid #525252 1px;
color: #ffffff;
margin: 2px 20px 25px; /* top right bottom left */
font-size: 12pt;
}
.prompt_demo_choice_label {
margin: 25px 20px 5px;
font-size: 12pt;
white-space: normal;
}
.prompt_demo_text {
white-space: pre;
line-height: 170%;
padding: 4px 12px;
font-size: 14pt;
top: 0px;
bottom: 0px;
vertical-align: middle;
display: table-cell;
height: 72px; /* this is really the min height */
}
.prompt_function {
display: block;
border: 1px solid #555;
background-color: #181818;
margin: 5px 20px 5px;
border-radius: 5;
}
.prompt_function_text {
white-space: pre-wrap;
padding: 15px 3px;
width: 100%;
height: 25%;
overflow: auto;
}
.external_link_img {
width: 16px;
height: 16px;
vertical-align: text-top;
margin-left: 10px;
};

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

14847
share/tools/web_config/js/angular.js vendored Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,69 @@
fishconfig = angular.module("fishconfig", ["filters", "controllers"]);
fishconfig.config(
["$routeProvider", function($routeProvider) {
$routeProvider
.when("/colors", {
controller: "colorsController",
templateUrl: "partials/colors.html"
})
.when("/prompt", {
controller: "promptController",
templateUrl: "partials/prompt.html"
})
.when("/functions", {
controller: "functionsController",
templateUrl: "partials/functions.html"
})
.when("/variables", {
controller: "variablesController",
templateUrl: "partials/variables.html"
})
.when("/history", {
controller: "historyController",
templateUrl: "partials/history.html"
})
.when("/bindings", {
controller: "bindingsController",
templateUrl: "partials/bindings.html"
})
.otherwise({
redirectTo: "/colors"
})
}]);
/* Inspired from http://blog.tomaka17.com/2012/12/random-tricks-when-using-angularjs/ */
fishconfig.config(function($httpProvider, $compileProvider) {
var global_error_element = null;
var showMessage = function(content) {
global_error_element.text(content);
};
$httpProvider.responseInterceptors.push(function($q) {
return function(promise) {
return promise.then(function(successResponse) {
showMessage('');
return successResponse;
}, function(errorResponse) {
switch (errorResponse.status) {
case 0:
showMessage("The request received an error. Perhaps the server has shut down.");
break;
case 500:
showMessage('Server internal error: ' + errorResponse.data);
break;
default:
showMessage('Error ' + errorResponse.status + ': ' + errorResponse.data);
}
return $q.reject(errorResponse);
});
};
});
$compileProvider.directive('errorMessage', function() {
return {
link: function(scope, element, attrs) { global_error_element = element; }
};
});
});

View File

@ -0,0 +1,624 @@
/* TODO: Write an angularjs service to wrap these methods */
term_256_colors = [ //247
"ffd7d7",
"d7afaf",
"af8787",
"875f5f",
"5f0000",
"870000",
"af0000",
"d70000",
"ff0000",
"ff5f5f",
"d75f5f",
"d78787",
"ff8787",
"ffafaf",
"ffaf87",
"ffaf5f",
"ffaf00",
"ff875f",
"ff8700",
"ff5f00",
"d75f00",
"af5f5f",
"af5f00",
"d78700",
"d7875f",
"af875f",
"af8700",
"875f00",
"d7af87",
"ffd7af",
"ffd787",
"ffd75f",
"d7af00",
"d7af5f",
"ffd700",
"ffff5f",
"ffff00",
"ffff87",
"ffffaf",
"ffffd7",
"d7ff00",
"afd75f",
"d7d700",
"d7d787",
"d7d7af",
"afaf87",
"87875f",
"5f5f00",
"878700",
"afaf00",
"afaf5f",
"d7d75f",
"d7ff5f",
"d7ff87",
"87ff00",
"afff00",
"afff5f",
"afd700",
"87d700",
"87af00",
"5f8700",
"87af5f",
"5faf00",
"afd787",
"d7ffd7",
"d7ffaf",
"afffaf",
"afff87",
"5fff00",
"5fd700",
"87d75f",
"5fd75f",
"87ff5f",
"5fff5f",
"87ff87",
"afd7af",
"87d787",
"87d7af",
"87af87",
"5f875f",
"5faf5f",
"005f00",
"008700",
"00af00",
"00d700",
"00ff00",
"00ff5f",
"5fff87",
"00ff87",
"87ffaf",
"afffd7",
"5fd787",
"00d75f",
"5faf87",
"00af5f",
"5fffaf",
"00ffaf",
"5fd7af",
"00d787",
"00875f",
"00af87",
"00d7af",
"5fffd7",
"87ffd7",
"00ffd7",
"d7ffff",
"afd7d7",
"87afaf",
"5f8787",
"5fafaf",
"87d7d7",
"5fd7d7",
"5fffff",
"00ffff",
"87ffff",
"afffff",
"00d7d7",
"00d7ff",
"5fd7ff",
"5fafd7",
"00afd7",
"00afff",
"0087af",
"00afaf",
"008787",
"005f5f",
"005f87",
"0087d7",
"0087ff",
"5fafff",
"87afff",
"5f87d7",
"5f87ff",
"005fd7",
"005fff",
"005faf",
"5f87af",
"87afd7",
"afd7ff",
"87d7ff",
"d7d7ff",
"afafd7",
"8787af",
"afafff",
"8787d7",
"8787ff",
"5f5fff",
"5f5fd7",
"5f5faf",
"5f5f87",
"00005f",
"000087",
"0000af",
"0000d7",
"0000ff",
"5f00ff",
"5f00d7",
"5f00af",
"5f0087",
"8700af",
"8700d7",
"8700ff",
"af00ff",
"af00d7",
"d700ff",
"d75fff",
"d787ff",
"ffafd7",
"ffafff",
"ffd7ff",
"d7afff",
"d7afd7",
"af87af",
"af87d7",
"af87ff",
"875fd7",
"875faf",
"875fff",
"af5fff",
"af5fd7",
"af5faf",
"d75fd7",
"d787d7",
"ff87ff",
"ff5fff",
"ff5fd7",
"ff00ff",
"ff00af",
"ff00d7",
"d700af",
"d700d7",
"af00af",
"870087",
"5f005f",
"87005f",
"af005f",
"af0087",
"d70087",
"d7005f",
"ff0087",
"ff005f",
"ff5f87",
"d75f87",
"d75faf",
"ff5faf",
"ff87af",
"ff87d7",
"d787af",
"af5f87",
"875f87",
"000000",
"080808",
"121212",
"1c1c1c",
"262626",
"303030",
"3a3a3a",
"444444",
"4e4e4e",
"585858",
"5f5f5f",
"626262",
"6c6c6c",
"767676",
"808080",
"878787",
"8a8a8a",
"949494",
"9e9e9e",
"a8a8a8",
"afafaf",
"b2b2b2",
"bcbcbc",
"c6c6c6",
"d0d0d0",
"d7d7d7",
"dadada",
"e4e4e4",
"eeeeee",
"ffffff",
]
/* Returns array of values from a dictionary (or any object) */
function dict_values(dict) {
var result = [];
for (var i in dict) result.push(dict[i]);
return result;
}
/* Return the array of colors as an array of N arrays of length items_per_row */
function get_colors_as_nested_array(colors, items_per_row) {
var result = new Array();
for (var idx = 0; idx < colors.length; idx += items_per_row) {
var row = new Array();
for (var subidx = 0; subidx < items_per_row && idx + subidx < colors.length; subidx++) {
row.push(colors[idx + subidx]);
}
result.push(row);
}
return result;
}
/* 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]
}
/* More hacks */
if (color_str == 'black') color_str = '000000';
if (color_str == 'white') color_str = 'FFFFFF';
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 a "border color" for it that can show it selected */
function border_color_for_color(color_str) {
return adjust_lightness(color_str, function(lightness){
var adjust = .5
var new_lightness = lightness + adjust
if (new_lightness > 1.0 || new_lightness < 0.0) {
new_lightness -= 2 * adjust
}
return new_lightness
})
}
/* Use this function to make a color that contrasts well with the given color */
function text_color_for_color(color_str) {
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
}
return adjust_lightness(color_str, compute_constrast);
}
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 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
})
}
/* Given a color name, like 'normal' or 'red' or 'FF00F0', return an RGB color string (or empty string) */
function interpret_color(str) {
str = str.toLowerCase()
if (str == 'black') return '000000'
if (str == 'red') return 'FF0000'
if (str == 'green') return '00FF00'
if (str == 'brown') return '725000'
if (str == 'yellow') return 'FFFF00'
if (str == 'blue') return '0000FF'
if (str == 'magenta') return 'FF00FF'
if (str == 'purple') return 'FF00FF'
if (str == 'cyan') return '00FFFF'
if (str == 'white') return 'FFFFFF'
if (str == 'normal') return ''
return str
}
var color_scheme_fish_default = {
"name": "fish default",
"colors": [],
'preferred_background': 'black',
'autosuggestion': '555',
'command': '005fd7',
'param': '00afff',
'redirection': '00afff',
'comment': '990000',
'error': 'ff0000',
'escape': '00a6b2',
'operator': '00a6b2',
'quote': '999900',
'end': '009900'
};
var TomorrowTheme = {
tomorrow_night: {'Background': '1d1f21', 'Current Line': '282a2e', 'Selection': '373b41', 'Foreground': 'c5c8c6', 'Comment': '969896', 'Red': 'cc6666', 'Orange': 'de935f', 'Yellow': 'f0c674', 'Green': 'b5bd68', 'Aqua': '8abeb7', 'Blue': '81a2be', 'Purple': 'b294bb'
},
tomorrow: {'Background': 'ffffff', 'Current Line': 'efefef', 'Selection': 'd6d6d6', 'Foreground': '4d4d4c', 'Comment': '8e908c', 'Red': 'c82829', 'Orange': 'f5871f', 'Yellow': 'eab700', 'Green': '718c00', 'Aqua': '3e999f', 'Blue': '4271ae', 'Purple': '8959a8'
},
tomorrow_night_bright: {'Background': '000000', 'Current Line': '2a2a2a', 'Selection': '424242', 'Foreground': 'eaeaea', 'Comment': '969896', 'Red': 'd54e53', 'Orange': 'e78c45', 'Yellow': 'e7c547', 'Green': 'b9ca4a', 'Aqua': '70c0b1', 'Blue': '7aa6da', 'Purple': 'c397d8'},
apply: function(theme, receiver){
receiver['autosuggestion'] = theme['Comment']
receiver['command'] = theme['Purple']
receiver['comment'] = theme['Yellow'] /* we want to use comment for autosuggestions */
receiver['end'] = theme['Purple']
receiver['error'] = theme['Red']
receiver['param'] = theme['Blue']
receiver['quote'] = theme['Green']
receiver['redirection'] = theme['Aqua']
receiver['colors'] = []
for (var key in theme) receiver['colors'].push(theme[key])
}
}
var solarized = {
base03: '002b36', base02: '073642', base01: '586e75', base00: '657b83', base0: '839496', base1: '93a1a1', base2: 'eee8d5', base3: 'fdf6e3', yellow: 'b58900', orange: 'cb4b16', red: 'dc322f', magenta: 'd33682', violet: '6c71c4', blue: '268bd2', cyan: '2aa198', green: '859900'
};
/* Sample color schemes */
var color_scheme_solarized_light = {
name: "Solarized Light",
colors: dict_values(solarized),
preferred_background: '#' + solarized.base3,
autosuggestion: solarized.base1,
command: solarized.base01,
comment: solarized.base1,
end: solarized.blue,
error: solarized.red,
param: solarized.base00,
quote: solarized.base0,
redirection: solarized.violet,
url: 'http://ethanschoonover.com/solarized'
};
var color_scheme_solarized_dark = {
name: "Solarized Dark",
colors: dict_values(solarized),
preferred_background: '#' + solarized.base03,
autosuggestion: solarized.base01,
command: solarized.base1,
comment: solarized.base01,
end: solarized.blue,
error: solarized.red,
param: solarized.base0,
quote: solarized.base00,
redirection: solarized.violet,
url: 'http://ethanschoonover.com/solarized'
};
var color_scheme_tomorrow = {
name: 'Tomorrow',
preferred_background: 'white',
url: 'https://github.com/chriskempson/tomorrow-theme'
}
TomorrowTheme.apply(TomorrowTheme.tomorrow, color_scheme_tomorrow)
var color_scheme_tomorrow_night = {
name: 'Tomorrow Night',
preferred_background: '#232323',
url: 'https://github.com/chriskempson/tomorrow-theme'
}
TomorrowTheme.apply(TomorrowTheme.tomorrow_night, color_scheme_tomorrow_night)
var color_scheme_tomorrow_night_bright = {
'name': 'Tomorrow Night Bright',
'preferred_background': 'black',
'url': 'https://github.com/chriskempson/tomorrow-theme',
}
TomorrowTheme.apply(TomorrowTheme.tomorrow_night_bright, color_scheme_tomorrow_night_bright)
function construct_scheme_analogous(label, background, color_list) {
return {
name: label,
preferred_background: background,
colors: color_list,
command: color_list[0],
quote: color_list[6],
param: color_list[5],
autosuggestion: color_list[4],
error: color_list[9],
comment: color_list[12],
end: color_list[10],
redirection: color_list[11]
};
}
function construct_scheme_triad(label, background, color_list) {
return {
name: label,
preferred_background: background,
colors: color_list,
command: color_list[0],
quote: color_list[2],
param: color_list[1],
autosuggestion: color_list[3],
redirection: color_list[4],
error: color_list[8],
comment: color_list[10],
end: color_list[7],
};
}
function construct_scheme_complementary(label, background, color_list) {
return {
name: label,
preferred_background: background,
colors: color_list,
command: color_list[0],
quote: color_list[4],
param: color_list[3],
autosuggestion: color_list[2],
redirection: color_list[6],
error: color_list[5],
comment: color_list[8],
end: color_list[9],
};
}
function construct_color_scheme_mono(label, background, from_end) {
var mono_colors = ['000000', '121212', '1c1c1c', '262626', '303030', '3a3a3a', '444444', '4e4e4e', '585858', '5f5f5f', '626262', '6c6c6c', '767676', '808080', '878787', '8a8a8a', '949494', '9e9e9e', 'a8a8a8', 'afafaf', 'b2b2b2', 'bcbcbc', 'c6c6c6', 'd0d0d0', 'd7d7d7', 'dadada', 'e4e4e4', 'eeeeee', 'ffffff'];
if (from_end) mono_colors.reverse();
return {
name: label,
preferred_background: background,
colors: mono_colors,
autosuggestion: '777777',
command: mono_colors[0],
comment: mono_colors[7],
end: mono_colors[12],
error: mono_colors[20],
param: mono_colors[4],
quote: mono_colors[10],
redirection: mono_colors[15]
};
}
var additional_color_schemes = [
construct_scheme_analogous('Snow Day', 'white', ['164CC9', '325197', '072D83', '4C7AE4', '7596E4', '4319CC', '4C3499', '260885', '724EE5', '9177E5', '02BDBD', '248E8E', '007B7B', '39DEDE', '65DEDE']),
construct_scheme_analogous('Lava', '#232323', ['FF9400', 'BF8330', 'A66000', 'FFAE40', 'FFC473', 'FFC000', 'BF9C30', 'A67D00', 'FFD040', 'FFDD73', 'FF4C00', 'BF5B30', 'A63100', 'FF7940', 'FF9D73']),
construct_scheme_analogous('Seaweed', '#232323', ['00BF32', '248F40', '007C21', '38DF64', '64DF85', '04819E', '206676', '015367', '38B2CE', '60B9CE', '8EEB00', '7CB02C', '5C9900', 'ACF53D', 'C0F56E']),
construct_scheme_triad('Fairground', '#003', ['0772A1', '225E79', '024A68', '3BA3D0', '63AFD0', 'D9005B', 'A3295C', '8D003B', 'EC3B86', 'EC6AA1', 'FFE100', 'BFAE30', 'A69200', 'FFE840', 'FFEE73']),
construct_scheme_complementary('Bay Cruise', 'black', ['009999', '1D7373', '006363', '33CCCC', '5CCCCC', 'FF7400', 'BF7130', 'A64B00', 'FF9640', 'FFB273']),
{
'name': 'Old School',
'preferred_background': 'black',
colors: ['00FF00', '30BE30', '00A400', '44FF44', '7BFF7B', 'FF0000', 'BE3030', 'A40000', 'FF7B7B', '777777', 'CCCCCC'],
autosuggestion: '777777',
command: '00FF00',
comment: '30BE30',
end: 'FF7B7B',
error: 'A40000',
param: '30BE30',
quote: '44FF44',
redirection: '7BFF7B'
},
{
'name': 'Just a Touch',
'preferred_background': 'black',
colors: ['B0B0B0', '969696', '789276', 'F4F4F4', 'A0A0F0', '666A80', 'F0F0F0', 'D7D7D7', 'B7B7B7', 'FFA779', 'FAFAFA'],
autosuggestion: '9C9C9C',
command: 'F4F4F4',
comment: 'B0B0B0',
end: '969696',
error: 'FFA779',
param: 'A0A0F0',
quote: '666A80',
redirection: 'FAFAFA'
},
construct_color_scheme_mono('Mono Lace', 'white', false),
construct_color_scheme_mono('Mono Smoke', 'black', true)
];

View File

@ -0,0 +1,232 @@
controllers = angular.module("controllers", []);
controllers.controller("main", function($scope, $location) {
$scope.currentTab = "colors";
$scope.changeView = function(view) {
$location.path(view);
$scope.currentTab = view;
}
})
controllers.controller("colorsController", function($scope, $http) {
$scope.changeSelectedColorScheme= function(newScheme) {
$scope.selectedColorScheme = newScheme;
if ($scope.selectedColorScheme.preferred_background) {
$scope.terminalBackgroundColor = $scope.selectedColorScheme.preferred_background;
}
$scope.selectedColorSetting = 'command';
$scope.colorArraysArray = $scope.getColorArraysArray();
//TODO: Save button should be shown only when colors are changed
$scope.showSaveButton = true;
}
$scope.changeTerminalBackgroundColor = function(color) {
$scope.terminalBackgroundColor = color;
}
$scope.text_color_for_color = function(color) {
return text_color_for_color(color);
}
$scope.getColorArraysArray = function() {
var result = null;
if ( $scope.selectedColorScheme.colors && $scope.selectedColorScheme.colors.length > 0)
result = get_colors_as_nested_array($scope.selectedColorScheme.colors, 32);
else
result = get_colors_as_nested_array(term_256_colors, 32);
return result;
}
$scope.selectColorSetting = function(name) {
$scope.selectedColorSetting = name;
}
$scope.changeSelectedTextColor = function(color) {
$scope.selectedColorScheme[$scope.selectedColorSetting] = color;
}
$scope.sampleTerminalBackgroundColors = ['white', '#' + solarized.base3, '#300', '#003', '#' + solarized.base03, '#232323', 'black'];
/* Array of FishColorSchemes */
$scope.colorSchemes = [color_scheme_fish_default, color_scheme_solarized_light, color_scheme_solarized_dark, color_scheme_tomorrow, color_scheme_tomorrow_night, color_scheme_tomorrow_night_bright];
for (var i=0; i < additional_color_schemes.length; i++)
$scope.colorSchemes.push(additional_color_schemes[i])
$scope.getCurrentTheme = function() {
$http.get("/colors/").success(function(data, status, headers, config) {
var currentScheme = { "name": "Current", "colors":[], "preferred_background": "" };
for (var i in data) {
currentScheme[data[i].name] = data[i].color;
}
$scope.colorSchemes.splice(0, 0, currentScheme);
$scope.changeSelectedColorScheme(currentScheme);
})};
$scope.setTheme = function() {
var settingNames = ["autosuggestion", "command", "param", "redirection", "comment", "error", "quote", "end"];
for (name in settingNames) {
var postData = "what=" + settingNames[name] + "&color=" + $scope.selectedColorScheme[settingNames[name]] + "&background_color=&bold=&underline=";
$http.post("/set_color/", postData, { headers: {'Content-Type': 'application/x-www-form-urlencoded'} }).success(function(data, status, headers, config) {
})
}
};
$scope.getCurrentTheme();
});
controllers.controller("promptController", function($scope, $http) {
$scope.selectedPrompt = null;
$scope.fetchSamplePrompts= function() {
$http.get("/sample_prompts/").success(function(data, status, headers, config) {
$scope.samplePrompts = data;
$scope.samplePromptsArrayArray = get_colors_as_nested_array($scope.samplePrompts, 1);
if ($scope.selectedPrompt == null) {
$scope.selectPrompt($scope.samplePrompts[0]);
}
})};
$scope.selectPrompt = function(promptt) {
$scope.selectedPrompt= promptt;
}
$scope.setNewPrompt = function(selectedPrompt) {
$http.post("/set_prompt/","what=" + encodeURIComponent(selectedPrompt.function), { headers: {'Content-Type': 'application/x-www-form-urlencoded'} }).success(function(data, status, headers, config){
// Update attributes of current prompt and select it
$scope.samplePrompts[0].demo = selectedPrompt.demo;
$scope.samplePrompts[0].function = selectedPrompt.function;
$scope.samplePrompts[0].font_size = selectedPrompt.font_size;
$scope.selectedPrompt = $scope.samplePrompts[0];
})};
$scope.fetchSamplePrompts();
});
controllers.controller("functionsController", function($scope, $http) {
$scope.selectedFunction = null;
$scope.functionDefinition = "";
$scope.selectFunction = function(fun) {
$scope.selectedFunction = fun;
$scope.fetchFunctionDefinition($scope.selectedFunction);
}
$scope.fetchFunctions= function() {
$http.get("/functions/").success(function(data, status, headers, config) {
$scope.functions = data;
$scope.selectFunction($scope.functions[0]);
})};
$scope.cleanupFishFunction = function (contents) {
/* Replace leading tabs and groups of four spaces at the beginning of a line with two spaces. */
lines = contents.split('\n')
rx = /^[\t ]+/
for (var i=0; i < lines.length; i++) {
line = lines[i]
/* Get leading tabs and spaces */
whitespace_arr = rx.exec(line)
if (whitespace_arr) {
/* Replace four spaces with two spaces, and tabs with two spaces */
var whitespace = whitespace_arr[0]
new_whitespace = whitespace.replace(/( )|(\t)/g, ' ')
lines[i] = new_whitespace + line.slice(whitespace.length)
}
}
return lines.join('\n')
}
$scope.fetchFunctionDefinition = function(name) {
$http.post("/get_function/","what=" + name, { headers: {'Content-Type': 'application/x-www-form-urlencoded'} }).success(function(data, status, headers, config) {
$scope.functionDefinition = $scope.cleanupFishFunction(data[0]);
})};
$scope.fetchFunctions();
});
controllers.controller("variablesController", function($scope, $http) {
$scope.query = null;
$scope.fetchVariables= function() {
$http.get("/variables/").success(function(data, status, headers, config) {
$scope.variables = data;
})};
$scope.fetchVariables();
});
controllers.controller("historyController", function($scope, $http, $timeout) {
$scope.historyItems = [];
$scope.historySize = 0;
// Stores items which are yet to be added in history items
$scope.remainingItems = [];
$scope.selectedItems = [];
// Populate history items in parts
$scope.loadHistory = function() {
if ($scope.remainingItems.length <= 0) {
$scope.loadingText = "";
return;
}
var toLoad = $scope.remainingItems.splice(0, 100);
for (i in toLoad) {
$scope.historyItems.push(toLoad[i]);
}
$scope.loadingText = "Loading " + $scope.historyItems.length + "/" + $scope.historySize;
$timeout($scope.loadHistory, 100);
}
$scope.selectItem = function(item) {
var index = $scope.selectedItems.indexOf(item);
if ( index >= 0) {
$scope.selectedItems.splice(index,1);
}
else {
$scope.selectedItems.push(item);
}
}
// Get history from server
$scope.fetchHistory = function() {
$http.get("/history/").success(function(data, status, headers, config) {
$scope.historySize = data.length;
$scope.remainingItems = data;
/* Call this function 10 times/second */
$timeout($scope.loadHistory, 100);
})};
$scope.deleteHistoryItem = function(item) {
index = $scope.historyItems.indexOf(item);
$http.post("/delete_history_item/","what=" + encodeURIComponent(item), { headers: {'Content-Type': 'application/x-www-form-urlencoded'} }).success(function(data, status, headers, config) {
$scope.historyItems.splice(index, 1);
})};
var queryInputTimeout = null;
$scope.$watch("queryInput", function() {
if (queryInputTimeout){
$timeout.cancel(queryInputTimeout);
}
queryInputTimeout = $timeout(function() {
$scope.query = $scope.queryInput;
}, 1000);
});
$scope.fetchHistory();
});
controllers.controller("bindingsController", function($scope, $http) {
$scope.bindings = [];
$scope.fetchBindings = function() {
$http.get("/bindings/").success(function(data, status, headers, config) {
$scope.bindings = data;
})};
$scope.fetchBindings();
});

View File

@ -0,0 +1,35 @@
filters = angular.module("filters", []);
filters.filter("filterVariable", function() {
return function(variables, query) {
var result = []
if (variables == undefined) return result;
if (query == null) { return variables };
for(i=0; i<variables.length; ++i) {
variable = variables[i];
if (variable.name.indexOf(query) != -1 || variable.value.indexOf(query) != -1) {
result.push(variable);
}
}
return result;
}
});
filters.filter("filterBinding", function() {
return function(bindings, query) {
var result = []
if (bindings == undefined) return result;
if (query == null) { return bindings};
for(i=0; i<bindings.length; ++i) {
binding = bindings[i];
if (binding.command.indexOf(query) != -1 || binding.readable_binding.toLowerCase().indexOf(query.toLowerCase()) != -1) {
result.push(binding);
}
}
return result;
}
});

View File

@ -0,0 +1,13 @@
<div id="table_filter_container" style="display: block;">
<input id="table_filter_text_box" class="filter_text_box text_box_transient" placeholder="Filter" ng-model="query">
</div>
<table class="data_table">
<tbody>
<tr class="data_table_row" ng-repeat="binding in bindings | filterBinding:query">
<td ng-class="{ data_table_cell: true, no_overflow: !binding._is_selected }" style="text-align: right; padding-right: 30px;" ng-click="binding._is_selected = !binding._is_selected">{{ binding.command }}</td>
<!-- Some bindings are listed multiple times for e.g. function backward-char is bound to \e\[D as well as key left. Users may want to know why some bindings are listed twice, so the actual binding is shown in next line on a click -->
<td ng-class="{ data_table_cell: true, no_overflow: !binding._is_selected }" style="text-align: left; padding-right: 30px;" ng-click="binding._is_selected = !binding._is_selected">{{ binding.readable_binding }} <div ng-show="binding._is_selected"> {{ binding.binding }} </div> </td>
</tr>
</tbody>
</table>

View File

@ -0,0 +1,65 @@
<div>
<!-- ko with: color_picker -->
<span style="padding-left: 25px">Click to customize each color: </span><br>
<div class="colorpicker_text_sample" ng-style="{'background-color': terminalBackgroundColor}">
<span style="position: absolute; left: 10px; top: -6px;" data-ng-style="{'color': text_color_for_color(selectedColorScheme.preferred_background || 'white')}">{{ selectedColorScheme.name }}</span><br>
<div class="color_picker_background_cells">
<div ng-style="{'background-color': color}" ng-repeat="color in sampleTerminalBackgroundColors" ng-click="changeTerminalBackgroundColor(color)"></div>
</div>
<!-- This is the sample text -->
<span data-ng-style="{ 'color': selectedColorScheme.command}" ng-click="selectColorSetting('command')">/bright/vixens</span>
<span data-ng-style="{ 'color': selectedColorScheme.param}" ng-click="selectColorSetting('param')">jump</span>
<span data-ng-style="{ 'color': selectedColorScheme.end}" ng-click="selectColorSetting('end')">|</span>
<span data-ng-style="{ 'color': selectedColorScheme.command}" ng-click="selectColorSetting('command')">dozy</span>
<span data-ng-style="{ 'color': selectedColorScheme.quote}" ng-click="selectColorSetting('quote')"> "fowl" </span>
<span data-ng-style="{ 'color': selectedColorScheme.redirection}" ng-click="selectColorSetting('redirection')">&gt; quack</span>
<span data-ng-style="{ 'color': selectedColorScheme.end}" ng-click="selectColorSetting('end')">&</span>
<br>
<span data-ng-style="{ 'color': selectedColorScheme.command}" ng-click="selectColorSetting('command')">echo</span>
<span data-ng-style="{ 'color': selectedColorScheme.error}" ng-click="selectColorSetting('error')">'Errors are the portals to discovery</span>
<br>
<span data-ng-style="{ 'color': selectedColorScheme.comment}" ng-click="selectColorSetting('comment')"># This is a comment</span>
<br>
<span data-ng-style="{ 'color': selectedColorScheme.command}" ng-click="selectColorSetting('command')">Th</span><span data-ng-style="{ 'color': selectedColorScheme.autosuggestion }" ng-click="selectColorSetting('autosuggestion')"><span class="fake_cursor"><span style="visibility: hidden">i</span></span>s is an autosuggestion</span>
<span class="save_button" style="position: absolute; right: 5px; bottom: 5px;" title="Terminal background color is not set automatically on Apply. See your terminal documentation to set its background color." data-ng-style="{'color': text_color_for_color(selectedColorScheme.preferred_background || 'white')}" ng-show="showSaveButton" ng-click="setTheme()">Apply</span>
</div>
<table class="colorpicker_term256" style="margin: 0px 20px;">
<tbody>
<tr class="colorpicker_term256_row" data-ng-repeat="color_array in colorArraysArray">
<td class="colorpicker_term256_cell" data-ng-style="{'background-color': color, 'color': color}" ng-click="changeSelectedTextColor(color)" ng-repeat="color in color_array">
<div class="colorpicker_term256_selection_indicator" ng-show="selectedColorScheme[selectedColorSetting] == color" ng-style="{'border-color': border_color_for_color(color)}"</div>
</td>
</tr>
</tbody>
<!-- /ko -->
</table>
<div class="color_scheme_choices_list">
<div class="color_scheme_choice_container" data-ng-repeat="colorScheme in colorSchemes" ng-click="changeSelectedColorScheme(colorScheme)">
<div class="color_scheme_choice_label">
<!-- This click/clickBubble nonsense is so that we can have a separate URL inside a parent with an onClick handler -->
<span>{{colorScheme.name }}</span><!--a data-bind="if: $data.url, click: function(){return true;}, clickBubble: false, attr: { href: $data.url}"><img class="external_link_img" src="external_link.png"></a-->
</div>
<div class="colorpicker_text_sample_tight" data-ng-style="{'background-color': colorScheme.preferred_background}">
<span data-ng-style="{'color': colorScheme.command}">/bright/vixens</span>
<span data-ng-style="{'color': colorScheme.param}">jump</span>
<span data-ng-style="{'color': colorScheme.end}">|</span>
<span data-ng-style="{'color': colorScheme.command}">dozy</span>
<span data-ng-style="{'color': colorScheme.quote}"> "fowl" </span>
<span data-ng-style="{'color': colorScheme.redirection}">&gt; quack</span>
<span data-ng-style="{'color': colorScheme.end}">&</span>
<br>
<span data-ng-style="{'color': colorScheme.command}">echo</span>
<span data-ng-style="{'color': colorScheme.error}">'Errors are the portals to discovery</span>
<br>
<span data-ng-style="{ 'color': colorScheme.comment}"># This is a comment</span>
<br>
<span data-ng-style="{ 'color': colorScheme.command}">Th</span><span data-ng-style="{ 'color': colorScheme.autosuggestion}"><span class="fake_cursor"><span style="visibility: hidden">i</span></span>s is an autosuggestion</span>
</div>
</div>
</div>
<!-- /ko -->
</div>

View File

@ -0,0 +1,12 @@
<div class="master_detail_table">
<div class="master">
<div ng-repeat="func in functions">
<div id="master_{{func}}" ng-class="{'master_element': true, 'selected_master_elem': func == selectedFunction }" ng-style="'color: #aaaaaa'" ng-click="selectFunction(func)">
<span class="master_element_text" style="font-size: 11pt;">{{ func }}</span>
</div>
</div>
</div>
<div class="detail">
<div class="detail_function">{{ functionDefinition }}</div>
</div>
</div>

View File

@ -0,0 +1,16 @@
<div id="table_filter_container">
<span ng-show="loadingText.length > 0"> {{ loadingText }} </span>
<input id="table_filter_text_box" class="filter_text_box text_box_transient" placeholder="Filter" ng-model="queryInput">
</div>
<table class="data_table">
<tbody>
<tr ng-repeat="item in historyItems | filter:query">
<td ng-class="{'history_text': true, 'no_overflow': selectedItems.indexOf(item) < 0}" ng-click="selectItem(item)">{{ item }}</td>
<td class="history_delete">
<a ng-click="deleteHistoryItem(item)">
<img class="delete_icon" src="delete.png">
</a>
</td>
</tr>
</tbody>
</table>

View File

@ -0,0 +1,10 @@
<div style="padding: 0 10px 15px;">
<div ng-repeat="prompt in samplePrompts">
<div class="prompt_demo_choice_label">{{ prompt.name }}</div>
<div ng-bind-html-unsafe='prompt.demo' class="prompt_demo" ng-click="selectPrompt(prompt)"></div>
<div class="prompt_function" ng-show="selectedPrompt == prompt">
<div class="prompt_function_text">{{ prompt.function }}</div>
</div>
<span class="prompt_save_button" ng-click="setNewPrompt(selectedPrompt)" ng-show="selectedPrompt == prompt && selectedPrompt != samplePrompts[0]">Use</span>
</div>
</div>

View File

@ -0,0 +1,13 @@
<div id="table_filter_container">
<input id="table_filter_text_box" class="filter_text_box text_box_transient" placeholder="Filter" ng-model="query">
</div>
<table class="data_table">
<tbody>
<tr class="data_table_row" ng-repeat="variable in variables | filterVariable:query">
<td class="data_table_cell no_overflow" style="text-align: right; padding-right: 30px;">{{ variable.name }}</td>
<!-- Small hack to select/unselect variables -->
<td ng-class="{'data_table_cell': true, 'no_overflow': !variable._is_selected}" style="text-align: left; padding-right: 30px;" ng-click="variable._is_selected=!variable._is_selected">{{ variable.value }}</td>
</tr>
</tbody>
</table>

View File

@ -101,7 +101,7 @@ def parse_color(color_str):
# Regular color
color = better_color(color, parse_one_color(comp))
return [color, background_color, bold, underline]
return {"color": color, "background": background_color, "bold": bold, "underline": underline}
def parse_bool(val):
val = val.lower()
@ -124,7 +124,7 @@ def get_special_ansi_escapes():
import curses
g_special_escapes_dict = {}
curses.setupterm()
# Helper function to get a value for a tparm
def get_tparm(key):
val = None
@ -132,12 +132,12 @@ def get_special_ansi_escapes():
if key: val = curses.tparm(key)
if val: val = val.decode('utf-8')
return val
# Just a few for now
g_special_escapes_dict['exit_attribute_mode'] = get_tparm('sgr0')
g_special_escapes_dict['bold'] = get_tparm('bold')
g_special_escapes_dict['underline'] = get_tparm('smul')
return g_special_escapes_dict
# Given a known ANSI escape sequence, convert it to HTML and append to the list
@ -146,12 +146,12 @@ def append_html_for_ansi_escape(full_val, result, span_open):
# Strip off the initial \x1b[ and terminating m
val = full_val[2:-1]
# Helper function to close a span if it's open
def close_span():
if span_open:
result.append('</span>')
# term256 foreground color
match = re.match('38;5;(\d+)', val)
if match is not None:
@ -159,7 +159,7 @@ def append_html_for_ansi_escape(full_val, result, span_open):
html_color = html_color_for_ansi_color_index(int(match.group(1)))
result.append('<span style="color: ' + html_color + '">')
return True # span now open
# term8 foreground color
if val in [str(x) for x in range(30, 38)]:
close_span()
@ -172,26 +172,26 @@ def append_html_for_ansi_escape(full_val, result, span_open):
if full_val == special_escapes['exit_attribute_mode']:
close_span()
return False
# We don't handle bold or underline yet
# Do nothing on failure
return span_open
def strip_ansi(val):
# Make a half-assed effort to strip ANSI control sequences
# We assume that all such sequences start with 0x1b and end with m,
# which catches most cases
return re.sub("\x1b[^m]*m", '', val)
def ansi_prompt_line_width(val):
# Given an ANSI prompt, return the length of its longest line, as in the number of characters it takes up
# Start by stripping off ANSI
stripped_val = strip_ansi(val)
# Now count the longest line
return max([len(x) for x in stripped_val.split('\n')])
def ansi_to_html(val):
# Split us up by ANSI escape sequences
@ -206,13 +206,13 @@ def ansi_to_html(val):
) # End capture
""", re.VERBOSE)
separated = reg.split(val)
# We have to HTML escape the text and convert ANSI escapes into HTML
# Collect it all into this array
result = []
span_open = False
# Text is at even indexes, escape sequences at odd indexes
for i in range(len(separated)):
component = separated[i]
@ -223,13 +223,13 @@ def ansi_to_html(val):
else:
# It's an escape sequence. Close the previous escape.
span_open = append_html_for_ansi_escape(component, result, span_open)
# Close final escape
if span_open: result.append('</span>')
# Remove empty elements
result = [x for x in result if x]
# Clean up empty spans, the nasty way
idx = len(result) - 1
while idx >= 1:
@ -254,7 +254,161 @@ class FishVar:
flags = []
if self.universal: flags.append('universal')
if self.exported: flags.append('exported')
return [self.name, self.value, ', '.join(flags)]
return {"name": self.name, "value": self.value, "Flags": ', '.join(flags)}
class FishBinding:
"""A class that represents keyboard binding """
def __init__(self, command, binding, readable_binding, description=None):
self.command = command
self.binding = binding
self.readable_binding = readable_binding
self.description = description
def get_json_obj(self):
return {"command" : self.command, "binding": self.binding, "readable_binding": self.readable_binding, "description": self.description }
def get_readable_binding(command):
return command
class BindingParser:
""" Class to parse codes for bind command """
#TODO: What does snext and sprevious mean ?
readable_keys= { "dc":"Delete", "npage": "Page Up", "ppage":"Page Down",
"sdc": "Shift Delete", "shome": "Shift Home",
"left": "Left Arrow", "right": "Right Arrow",
"up": "Up Arrow", "down": "Down Arrow",
"sleft": "Shift Left", "sright": "Shift Right"
}
def set_buffer(self, buffer, is_key=False):
""" Sets code to parse """
self.buffer = buffer
self.is_key = is_key
self.index = 0
def get_char(self):
""" Gets next character from buffer """
c = self.buffer[self.index]
self.index += 1
return c
def unget_char(self):
""" Goes back by one character for parsing """
self.index -= 1
def end(self):
""" Returns true if reached end of buffer """
return self.index >= len(self.buffer)
def parse_control_sequence(self):
""" Parses terminal specifiec control sequences """
result = ''
c = self.get_char()
# \e0 is used to denote start of control sequence
if c == 'O':
c = self.get_char()
# \[1\; is start of control sequence
if c == '1':
self.get_char();c = self.get_char()
if c == ";":
c = self.get_char()
# 3 is Alt
if c == '3':
result += "ALT - "
c = self.get_char()
# 5 is Ctrl
if c == '5':
result += "CTRL - "
c = self.get_char()
# 9 is Alt
if c == '9':
result += "ALT - "
c = self.get_char()
if c == 'A':
result += 'Up Arrow'
elif c == 'B':
result += 'Down Arrow'
elif c == 'C':
result += 'Right Arrow'
elif c == 'D':
result += "Left Arrow"
elif c == 'F':
result += "End"
elif c == 'H':
result += "Home"
return result
def get_readable_binding(self):
""" Gets a readable representation of binding """
if self.is_key:
try:
result = BindingParser.readable_keys[self.buffer]
except KeyError:
result = self.buffer.title()
else:
result = self.parse_binding()
return result
def parse_binding(self):
readable_command = ''
result = ''
alt = ctrl = False
while not self.end():
c = self.get_char()
if c == '\\':
c = self.get_char()
if c == 'e':
d = self.get_char()
if d == 'O':
self.unget_char()
result += self.parse_control_sequence()
elif d == '\\':
if self.get_char() == '[':
result += self.parse_control_sequence()
else:
self.unget_char()
self.unget_char()
alt = True
else:
alt = True
self.unget_char()
elif c == 'c':
ctrl = True
elif c == 'n':
result += 'Enter'
elif c == 't':
result += 'Tab'
elif c == 'b':
result += 'Backspace'
else:
result += c
else:
result += c
if ctrl:
readable_command += 'CTRL - '
if alt:
readable_command += 'ALT - '
return readable_command + result
class FishConfigHTTPRequestHandler(SimpleHTTPServer.SimpleHTTPRequestHandler):
@ -314,7 +468,9 @@ class FishConfigHTTPRequestHandler(SimpleHTTPServer.SimpleHTTPRequestHandler):
for match in re.finditer(r"^fish_color_(\S+) ?(.*)", line):
color_name, color_value = [x.strip() for x in match.group(1, 2)]
color_desc = descriptions.get(color_name, '')
result.append([color_name, color_desc, parse_color(color_value)])
data = { "name": color_name, "description" : color_desc }
data.update(parse_color(color_value))
result.append(data)
remaining.discard(color_name)
# Ensure that we have all the color names we know about, so that if the
@ -363,6 +519,39 @@ class FishConfigHTTPRequestHandler(SimpleHTTPServer.SimpleHTTPRequestHandler):
return [vars[key].get_json_obj() for key in sorted(vars.keys(), key=str.lower)]
def do_get_bindings(self):
""" Get key bindings """
# Running __fish_config_interactive print fish greeting and
# loads key bindings
greeting, err = run_fish_cmd(' __fish_config_interactive')
# Load the key bindings and then list them with bind
out, err = run_fish_cmd('__fish_config_interactive; bind')
# Remove fish greeting from output
out = out[len(greeting):]
# Put all the bindings into a list
bindings = []
binding_parser = BindingParser()
for line in out.split('\n'):
comps = line.split(' ', 2)
if len(comps) < 3:
continue
if comps[1] == '-k':
key_name, command = comps[2].split(' ', 1)
binding_parser.set_buffer(key_name, True)
fish_binding = FishBinding(command=command, binding=key_name, readable_binding=binding_parser.get_readable_binding())
else:
binding_parser.set_buffer(comps[1])
fish_binding = FishBinding(command=comps[2], binding=comps[1], readable_binding=binding_parser.get_readable_binding())
bindings.append(fish_binding)
return [ binding.get_json_obj() for binding in bindings ]
def do_get_history(self):
# Use \x1e ("record separator") to distinguish between history items. The first
# backslash is so Python passes one backslash to fish
@ -371,7 +560,6 @@ class FishConfigHTTPRequestHandler(SimpleHTTPServer.SimpleHTTPRequestHandler):
if result: result.pop() # Trim off the trailing element
return result
def do_get_color_for_variable(self, name):
"Return the color with the given name, or the empty string if there is none"
out, err = run_fish_cmd("echo -n $" + name)
@ -397,12 +585,12 @@ class FishConfigHTTPRequestHandler(SimpleHTTPServer.SimpleHTTPRequestHandler):
# It's really lame that we always return success here
out, err = run_fish_cmd('builtin history --save --delete -- ' + escape_fish_cmd(history_item_text))
return True
def do_set_prompt_function(self, prompt_func):
cmd = prompt_func + '\n' + 'funcsave fish_prompt'
out, err = run_fish_cmd(cmd)
return len(err) == 0
def do_get_prompt(self, command_to_run, prompt_function_text):
# Return the prompt output by the given command
prompt_demo_ansi, err = run_fish_cmd(command_to_run)
@ -414,7 +602,7 @@ class FishConfigHTTPRequestHandler(SimpleHTTPServer.SimpleHTTPRequestHandler):
# Return the current prompt
prompt_func, err = run_fish_cmd('functions fish_prompt')
return self.do_get_prompt('cd "' + initial_wd + '" ; fish_prompt', prompt_func.strip())
def do_get_sample_prompt(self, text):
# Return the prompt you get from the given text
cmd = text + "\n cd \"" + initial_wd + "\" \n fish_prompt\n"
@ -424,7 +612,7 @@ class FishConfigHTTPRequestHandler(SimpleHTTPServer.SimpleHTTPRequestHandler):
# Allow us to skip whitespace, etc.
if not line: return True
if line.isspace(): return True
# Parse a comment hash like '# name: Classic'
match = re.match(r"#\s*(\w+?): (.+)", line, re.IGNORECASE)
if match:
@ -434,8 +622,8 @@ class FishConfigHTTPRequestHandler(SimpleHTTPServer.SimpleHTTPRequestHandler):
return True
# Skip other hash comments
return line.startswith('#')
def read_one_sample_prompt(self, fd):
# Read one sample prompt from fd
function_lines = []
@ -448,14 +636,16 @@ class FishConfigHTTPRequestHandler(SimpleHTTPServer.SimpleHTTPRequestHandler):
# Maybe not we're not parsing hashes, or maybe we already were not
if not parsing_hashes:
function_lines.append(line)
result['function'] = ''.join(function_lines).strip()
return result
func = ''.join(function_lines).strip()
result.update(self.do_get_sample_prompt(func))
return result
def do_get_sample_prompts_list(self):
result = []
# Start with the "Current" meta-sample
result.append({'name': 'Current'})
result[0].update(self.do_get_current_prompt())
# Read all of the prompts in sample_prompts
paths = glob.iglob('sample_prompts/*.fish')
for path in paths:
@ -467,7 +657,7 @@ class FishConfigHTTPRequestHandler(SimpleHTTPServer.SimpleHTTPRequestHandler):
# Ignore unreadable files, etc
pass
return result
def font_size_for_ansi_prompt(self, prompt_demo_ansi):
width = ansi_prompt_line_width(prompt_demo_ansi)
# Pick a font size
@ -502,6 +692,8 @@ class FishConfigHTTPRequestHandler(SimpleHTTPServer.SimpleHTTPRequestHandler):
elif re.match(r"/color/(\w+)/", p):
name = re.match(r"/color/(\w+)/", p).group(1)
output = self.do_get_color_for_variable(name)
elif p == '/bindings/':
output = self.do_get_bindings()
else:
return SimpleHTTPServer.SimpleHTTPRequestHandler.do_GET(self)
@ -578,7 +770,7 @@ class FishConfigHTTPRequestHandler(SimpleHTTPServer.SimpleHTTPRequestHandler):
def log_request(self, code='-', size='-'):
""" Disable request logging """
pass
# find fish
fish_bin_dir = os.environ.get('__fish_bin_dir')
fish_bin_path = None
@ -599,7 +791,7 @@ if not fish_bin_dir:
else:
fish_bin_path = os.path.join(fish_bin_dir, 'fish')
if not os.access(fish_bin_path, os.X_OK):
print("fish could not be executed at path '%s'. Is fish installed correctly?" % fish_bin_path)
sys.exit(-1)