mirror of
https://github.com/fish-shell/fish-shell.git
synced 2026-05-17 16:11:15 -03:00
Implemented history deletion from fish_config
Fixes https://github.com/fish-shell/fish-shell/issues/250
This commit is contained in:
BIN
share/tools/web_config/delete.png
Normal file
BIN
share/tools/web_config/delete.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 2.4 KiB |
@@ -106,6 +106,7 @@ body {
|
||||
padding-left: 30px;
|
||||
padding-right: 30px;
|
||||
border-radius: 5;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
#detail_function {
|
||||
@@ -221,6 +222,11 @@ body {
|
||||
word-wrap: break-word;
|
||||
}
|
||||
|
||||
/* The CSS we apply when a table row is filtered */
|
||||
.data_table_row_filtered {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.no_overflow {
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
@@ -275,6 +281,37 @@ body {
|
||||
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;
|
||||
}
|
||||
|
||||
.table_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;
|
||||
}
|
||||
|
||||
</style>
|
||||
|
||||
<script type="text/javascript" src="jquery.js"></script>
|
||||
@@ -307,20 +344,27 @@ function request_failed(jqXHR, textStatus, errorThrown) {
|
||||
}
|
||||
|
||||
/* 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. */
|
||||
function run_get_request(url, handler) {
|
||||
function run_get_request_with_bulk_handler(url, handler) {
|
||||
$.ajax({
|
||||
type: "GET",
|
||||
url: url,
|
||||
success: function(data){
|
||||
$('#global_error').text('')
|
||||
$.each($.parseJSON(data), function(idx, contents) {
|
||||
handler(contents)
|
||||
})
|
||||
handler($.parseJSON(data))
|
||||
},
|
||||
error: request_failed
|
||||
})
|
||||
}
|
||||
|
||||
function run_get_request(url, handler) {
|
||||
run_get_request_with_bulk_handler(url, function(json_contents){
|
||||
$.each(json_contents, function(idx, contents){
|
||||
handler(contents)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
/* As above but with POST request. */
|
||||
function run_post_request(url, data_map, handler) {
|
||||
$.ajax({
|
||||
@@ -469,16 +513,33 @@ function switch_tab(new_tab) {
|
||||
$('#detail_function').show()
|
||||
$('#master_detail_table').show()
|
||||
} else if (new_tab == 'tab_variables') {
|
||||
run_get_request('/variables/', function(contents){
|
||||
var name = contents[0]
|
||||
var value = contents[1]
|
||||
var flags = contents[2]
|
||||
create_data_table_element([name, value])
|
||||
run_get_request_with_bulk_handler('/variables/', function(json_contents){
|
||||
var rows = new Array()
|
||||
for (var i = 0; i < json_contents.length; i++) {
|
||||
var contents = json_contents[i]
|
||||
var name = contents[0]
|
||||
var value = contents[1]
|
||||
var flags = contents[2]
|
||||
var row = create_data_table_element_text([name, value], false)
|
||||
rows[i] = row
|
||||
}
|
||||
$('#data_table').append(rows.join(''))
|
||||
})
|
||||
$('#data_table').show()
|
||||
} else if (new_tab == 'tab_history') {
|
||||
run_get_request('/history/', function(contents){
|
||||
create_data_table_element([contents])
|
||||
// Clear the history map
|
||||
history_element_map.length = 0
|
||||
run_get_request_with_bulk_handler('/history/', function(json_contents){
|
||||
start = new Date().getTime()
|
||||
var rows = new Array()
|
||||
for (var i = 0; i < json_contents.length; i++) {
|
||||
var history_text = json_contents[i]
|
||||
rows[i] = create_data_table_element_text([history_text], true)
|
||||
history_element_map[last_global_element_identifier] = history_text
|
||||
}
|
||||
$('#data_table').append(rows.join(''))
|
||||
end = new Date().getTime()
|
||||
//alert(rows.length + " rows in " + (end - start) + " msec")
|
||||
})
|
||||
$('#data_table').show()
|
||||
} else {
|
||||
@@ -999,36 +1060,53 @@ function toggle_overflow(who) {
|
||||
$(who).toggleClass('no_overflow')
|
||||
}
|
||||
|
||||
function escape_HTML(foo) {
|
||||
return foo.replace(/&/g,'&').replace(/</g,'<').replace(/>/g,'>');
|
||||
}
|
||||
|
||||
/* Given the image, walk up to the table */
|
||||
function tell_fish_to_delete_element(idx) {
|
||||
var row_elem = $('#data_table_row_' + idx)
|
||||
var txt = history_element_map[idx]
|
||||
run_post_request('/delete_history_item/', {
|
||||
what: txt
|
||||
}, function(contents){
|
||||
if (contents == "OK") {
|
||||
row_elem.remove();
|
||||
} else {
|
||||
show_error(contents)
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
/* Creates a new row in the data table */
|
||||
function create_data_table_element(contents_list) {
|
||||
var row = $('<tr>', {
|
||||
class: 'data_table_row'
|
||||
})
|
||||
var last_global_element_identifier = 0
|
||||
var history_element_map = new Array();
|
||||
|
||||
function create_data_table_element_text(contents_list, show_delete_button) {
|
||||
var element_identifier = (++last_global_element_identifier).toString()
|
||||
lines = new Array()
|
||||
var result_str = '<tr class="data_table_row" id="data_table_row_' + element_identifier + '">'
|
||||
for (idx = 0; idx < contents_list.length; idx++) {
|
||||
/* If we have more than one, then align the first one right, subsequent ones left */
|
||||
if (idx == 0 && contents_list.length > 1) {
|
||||
cell = $('<td>', {
|
||||
class: 'data_table_cell no_overflow',
|
||||
style: 'text-align: right; padding-right: 30px;'
|
||||
});
|
||||
|
||||
result_str += '<td class="data_table_cell no_overflow" style="text-align: right; padding-right: 30px;">'
|
||||
} else {
|
||||
cell = $('<td>', {
|
||||
class: 'data_table_cell no_overflow',
|
||||
style: 'text-align: left',
|
||||
onClick: 'toggle_overflow(this)'
|
||||
});
|
||||
result_str += '<td class="data_table_cell no_overflow" style="text-align: left; padding-right: 30px;" onClick:"toggle_overflow(this)">'
|
||||
}
|
||||
text_list = contents_list[idx].split("\n")
|
||||
for (j=0; j < text_list.length; j++) {
|
||||
cell.append($('<p>', {
|
||||
text: text_list[j]
|
||||
}))
|
||||
if (j > 0) result_str += '<br>'
|
||||
result_str += escape_HTML(text_list[j]);
|
||||
}
|
||||
|
||||
row.append(cell)
|
||||
result_str += '</td>'
|
||||
}
|
||||
$('#data_table').append(row)
|
||||
if (show_delete_button) {
|
||||
result_str += '<td class="data_table_cell" style="text-align: right; width: 25px"><a onClick="tell_fish_to_delete_element(' + element_identifier + ')"><img class="delete_icon" src="delete.png"></a></td>'
|
||||
}
|
||||
result_str += '</tr>'
|
||||
return result_str
|
||||
}
|
||||
|
||||
/* Put stuff in colorpicker_term256 */
|
||||
@@ -1061,6 +1139,54 @@ function populate_colorpicker_term256() {
|
||||
}
|
||||
}
|
||||
|
||||
/* Update the filter text box */
|
||||
function update_table_filter_text_box(allow_transient_message) {
|
||||
var box = $('.table_filter_text_box')
|
||||
var has_transient = box.hasClass('text_box_transient')
|
||||
if (! allow_transient_message && has_transient) {
|
||||
box.val('')
|
||||
box.removeClass('text_box_transient')
|
||||
has_transient = false
|
||||
} else if (allow_transient_message && ! has_transient && ! box.val().length) {
|
||||
box.val('Filter')
|
||||
box.addClass('text_box_transient')
|
||||
has_transient = true
|
||||
}
|
||||
|
||||
var search_text = box.val()
|
||||
if (has_transient || search_text.length == 0) {
|
||||
/* Unfilter all */
|
||||
$('.data_table_row_filtered').removeClass('data_table_row_filtered')
|
||||
} else {
|
||||
/* Helper function to return whether a node (or its descendants) matches the given text */
|
||||
function match_text(node) {
|
||||
if (node.nodeType == 3) {
|
||||
return node.nodeValue.indexOf(search_text) != -1
|
||||
} else {
|
||||
for (var i = 0, len = node.childNodes.length; i < len; ++i) {
|
||||
if (match_text(node.childNodes[i])) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
$('.data_table_row').each(function(idx) {
|
||||
var row = $(this)
|
||||
var is_hidden = row.hasClass('data_table_row_filtered')
|
||||
var should_be_hidden = ! match_text(this)
|
||||
if (is_hidden && ! should_be_hidden) {
|
||||
row.removeClass('data_table_row_filtered')
|
||||
} else if (! is_hidden && should_be_hidden) {
|
||||
row.addClass('data_table_row_filtered')
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
$(document).ready(function() {
|
||||
populate_colorpicker_term256()
|
||||
switch_tab('tab_colors')
|
||||
@@ -1096,7 +1222,13 @@ $(document).ready(function() {
|
||||
<div id="detail_function"></div>
|
||||
</div>
|
||||
</div>
|
||||
<table id="data_table"><tr><td></td></tr>
|
||||
<table id="data_table">
|
||||
<div id="table_filter_container">
|
||||
<input type="text" class="table_filter_text_box text_box_transient" value="Filter" onInput="update_table_filter_text_box(false)" onFocus="update_table_filter_text_box(false)" onBlur="update_table_filter_text_box(true)">
|
||||
</div>
|
||||
<tr><td>
|
||||
</td></tr>
|
||||
<tr><td></td></tr>
|
||||
</table>
|
||||
<div class="footer">
|
||||
</div>
|
||||
|
||||
9270
share/tools/web_config/jquery.js
vendored
9270
share/tools/web_config/jquery.js
vendored
File diff suppressed because one or more lines are too long
@@ -1,27 +1,34 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
try: #Python2
|
||||
# Whether we're Python 2
|
||||
import sys
|
||||
IS_PY2 = sys.version_info[0] == 2
|
||||
|
||||
if IS_PY2:
|
||||
import SimpleHTTPServer
|
||||
except ImportError: #Python3
|
||||
import http.server as SimpleHTTPServer
|
||||
try: #Python2
|
||||
import SocketServer
|
||||
except ImportError: #Python3
|
||||
else:
|
||||
import http.server as SimpleHTTPServer
|
||||
import socketserver as SocketServer
|
||||
import webbrowser
|
||||
import subprocess
|
||||
import re, json, socket, os, sys, cgi, select
|
||||
import re, json, socket, os, sys, cgi, select, time
|
||||
|
||||
def run_fish_cmd(text):
|
||||
from subprocess import PIPE
|
||||
p = subprocess.Popen(["fish"], stdin=PIPE, stdout=PIPE, stderr=PIPE)
|
||||
try: #Python2
|
||||
out, err = p.communicate(text)
|
||||
except TypeError: #Python3
|
||||
if IS_PY2:
|
||||
out, err = p.communicate(text)
|
||||
else:
|
||||
out, err = p.communicate(bytes(text, 'utf-8'))
|
||||
out = str(out, 'utf-8')
|
||||
err = str(err, 'utf-8')
|
||||
return(out, err)
|
||||
|
||||
def escape_fish_cmd(text):
|
||||
# Replace one backslash with two, and single quotes with backslash-quote
|
||||
escaped = text.replace('\\', '\\\\').replace("'", "\\'")
|
||||
return "'" + escaped + "'"
|
||||
|
||||
named_colors = {
|
||||
'black' : '000000',
|
||||
@@ -105,6 +112,13 @@ class FishVar:
|
||||
|
||||
class FishConfigHTTPRequestHandler(SimpleHTTPServer.SimpleHTTPRequestHandler):
|
||||
|
||||
def write_to_wfile(self, txt):
|
||||
if IS_PY2:
|
||||
self.wfile.write(txt)
|
||||
else: # Python 3
|
||||
self.wfile.write(bytes(txt, 'utf-8'))
|
||||
|
||||
|
||||
def do_get_colors(self):
|
||||
# Looks for fish_color_*.
|
||||
# Returns an array of lists [color_name, color_description, color_value]
|
||||
@@ -207,8 +221,8 @@ class FishConfigHTTPRequestHandler(SimpleHTTPServer.SimpleHTTPRequestHandler):
|
||||
# Use \x1e ("record separator") to distinguish between history items. The first
|
||||
# backslash is so Python passes one backslash to fish
|
||||
out, err = run_fish_cmd('for val in $history; echo -n $val \\x1e; end')
|
||||
result = out.split('\x1e')
|
||||
if result: result.pop()
|
||||
result = out.split(' \x1e')
|
||||
if result: result.pop() # Trim off the trailing element
|
||||
return result
|
||||
|
||||
|
||||
@@ -233,6 +247,11 @@ class FishConfigHTTPRequestHandler(SimpleHTTPServer.SimpleHTTPRequestHandler):
|
||||
out, err = run_fish_cmd('functions ' + func_name)
|
||||
return out
|
||||
|
||||
def do_delete_history_item(self, history_item_text):
|
||||
# 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_GET(self):
|
||||
p = self.path
|
||||
if p == '/colors/':
|
||||
@@ -242,7 +261,10 @@ class FishConfigHTTPRequestHandler(SimpleHTTPServer.SimpleHTTPRequestHandler):
|
||||
elif p == '/variables/':
|
||||
output = self.do_get_variables()
|
||||
elif p == '/history/':
|
||||
# start = time.time()
|
||||
output = self.do_get_history()
|
||||
# end = time.time()
|
||||
# print "History: ", end - start
|
||||
elif re.match(r"/color/(\w+)/", p):
|
||||
name = re.match(r"/color/(\w+)/", p).group(1)
|
||||
output = self.do_get_color_for_variable(name)
|
||||
@@ -252,22 +274,16 @@ class FishConfigHTTPRequestHandler(SimpleHTTPServer.SimpleHTTPRequestHandler):
|
||||
# Return valid output
|
||||
self.send_response(200)
|
||||
self.send_header('Content-type','text/html')
|
||||
try: #Python2
|
||||
self.wfile.write('\n')
|
||||
except TypeError: #Python3
|
||||
self.wfile.write(bytes('\n', 'utf-8'))
|
||||
self.write_to_wfile('\n')
|
||||
|
||||
# Output JSON
|
||||
try: #Python2
|
||||
self.wfile.write(json.dumps(output))
|
||||
except TypeError: #Python3
|
||||
self.wfile.write(bytes(json.dumps(output), 'utf-8'))
|
||||
self.write_to_wfile(json.dumps(output))
|
||||
|
||||
def do_POST(self):
|
||||
p = self.path
|
||||
try: #Python2
|
||||
if IS_PY2:
|
||||
ctype, pdict = cgi.parse_header(self.headers.getheader('content-type'))
|
||||
except AttributeError: #Python3
|
||||
else: # Python 3
|
||||
ctype, pdict = cgi.parse_header(self.headers['content-type'])
|
||||
if ctype == 'multipart/form-data':
|
||||
postvars = cgi.parse_multipart(self.rfile, pdict)
|
||||
@@ -310,22 +326,25 @@ class FishConfigHTTPRequestHandler(SimpleHTTPServer.SimpleHTTPRequestHandler):
|
||||
what = postvars.get(b'what')
|
||||
what[0] = str(what[0]).lstrip("b'").rstrip("'")
|
||||
output = [self.do_get_function(what[0])]
|
||||
elif p == '/delete_history_item/':
|
||||
what = postvars.get('what')
|
||||
if what == None: #Will be None for python3
|
||||
what = postvars.get(b'what')
|
||||
what[0] = str(what[0]).lstrip("b'").rstrip("'")
|
||||
if self.do_delete_history_item(what[0]):
|
||||
output = ["OK"]
|
||||
else:
|
||||
output = ["Unable to delete history item"]
|
||||
else:
|
||||
return SimpleHTTPServer.SimpleHTTPRequestHandler.do_POST(self)
|
||||
|
||||
# Return valid output
|
||||
self.send_response(200)
|
||||
self.send_header('Content-type','text/html')
|
||||
try: #Python2
|
||||
self.wfile.write('\n')
|
||||
except TypeError: #Python3
|
||||
self.wfile.write(bytes('\n', 'utf-8'))
|
||||
|
||||
self.write_to_wfile('\n')
|
||||
|
||||
# Output JSON
|
||||
try: #Python2
|
||||
self.wfile.write(json.dumps(output))
|
||||
except TypeError: #Python3
|
||||
self.wfile.write(bytes(json.dumps(output), 'utf-8'))
|
||||
self.write_to_wfile(json.dumps(output))
|
||||
|
||||
def log_request(self, code='-', size='-'):
|
||||
""" Disable request logging """
|
||||
|
||||
Reference in New Issue
Block a user