fix webconfig.py lint issues

This commit is contained in:
Kurtis Rader 2016-11-10 16:23:08 -08:00
parent 47a9f99523
commit 20bcbcc252

View File

@ -1,19 +1,28 @@
#!/usr/bin/env python
from __future__ import unicode_literals
# Whether we're Python 2
import sys
import binascii
import cgi
import glob
import multiprocessing.pool
import os
import operator
import os
import random
import re
import select
import socket
import string
import subprocess
import sys
import webbrowser
FISH_BIN_PATH = False # will be set later
IS_PY2 = sys.version_info[0] == 2
if IS_PY2:
import SimpleHTTPServer
import SocketServer
from urlparse import parse_qs
else:
import http.server as SimpleHTTPServer
import socketserver as SocketServer
@ -21,22 +30,20 @@ else:
# Disable CLI web browsers
term = os.environ.pop('TERM', None)
import webbrowser
if term:
os.environ['TERM'] = term
import subprocess
import re, cgi, select, time, glob, random, string, binascii
try:
import json
except ImportError:
import simplejson as json
FISH_BIN_PATH = False # will be set later
def run_fish_cmd(text):
from subprocess import PIPE
# ensure that fish is using UTF-8
ctype = os.environ.get("LC_ALL", os.environ.get("LC_CTYPE", os.environ.get("LANG")))
# Ensure that fish is using UTF-8.
ctype = os.environ.get("LC_ALL", os.environ.get("LC_CTYPE",
os.environ.get("LANG")))
env = None
if ctype is None or re.search(r"\.utf-?8$", ctype, flags=re.I) is None:
# override LC_CTYPE with en_US.UTF-8
@ -44,68 +51,66 @@ def run_fish_cmd(text):
# Fish makes the same assumption in config.fish
env = os.environ.copy()
env.update(LC_CTYPE="en_US.UTF-8", LANG="en_US.UTF-8")
p = subprocess.Popen([FISH_BIN_PATH], stdin=PIPE, stdout=PIPE, stderr=PIPE, env=env)
p = subprocess.Popen([FISH_BIN_PATH], stdin=PIPE, stdout=PIPE, stderr=PIPE,
env=env)
out, err = p.communicate(text.encode('utf-8'))
out = out.decode('utf-8', 'replace')
err = err.decode('utf-8', 'replace')
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',
'red' : '800000',
'green' : '008000',
'brown' : '725000',
'yellow' : '808000',
'blue' : '000080',
'magenta' : '800080',
'purple' : '800080',
'cyan' : '008080',
'grey' : 'e5e5e5',
'brgrey' : '555555',
'white' : 'c0c0c0',
'brblack' : '808080',
'brred' : 'ff0000',
'brgreen' : '00ff00',
'brbrown' : 'ffff00',
'bryellow' : 'ffff00',
'brblue' : '0000ff',
'brmagenta' : 'ff00ff',
'brpurple' : 'ff00ff',
'brcyan' : '00ffff',
'brwhite' : 'ffffff'
'black': '000000', 'red': '800000', 'green': '008000', 'brown': '725000',
'yellow': '808000', 'blue': '000080', 'magenta': '800080',
'purple': '800080', 'cyan': '008080', 'grey': 'e5e5e5', 'brgrey': '555555',
'white': 'c0c0c0', 'brblack': '808080', 'brred': 'ff0000',
'brgreen': '00ff00', 'brbrown': 'ffff00', 'bryellow': 'ffff00',
'brblue': '0000ff', 'brmagenta': 'ff00ff', 'brpurple': 'ff00ff',
'brcyan': '00ffff', 'brwhite': 'ffffff'
}
bindings_blacklist = set(["self-insert", "'begin;end'"])
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:
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 better_color(c1, c2):
""" Indicate which color is "better", i.e. prefer term256 colors """
if not c2: return c1
if not c1: return c2
if c1 == 'normal': return c2
if c2 == 'normal': return c1
if c2 in named_colors: return c1
if c1 in named_colors: return c2
if not c2:
return c1
if not c1:
return c2
if c1 == 'normal':
return c2
if c2 == 'normal':
return c1
if c2 in named_colors:
return c1
if c1 in named_colors:
return c2
return c1
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(' ')
color = 'normal'
background_color = ''
@ -119,21 +124,69 @@ def parse_color(color_str):
underline = True
elif comp.startswith('--background='):
# Background color
background_color = better_color(background_color, parse_one_color(comp[len('--background='):]))
background_color = better_color(
background_color, parse_one_color(comp[len('--background='):]))
else:
# Regular color
color = better_color(color, parse_one_color(comp))
return {"color": color, "background": background_color, "bold": bold, "underline": underline}
return {"color": color, "background": background_color, "bold": bold,
"underline": 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
if val.startswith('f') or val.startswith('0'):
return False
if val.startswith('t') or val.startswith('1'):
return True
return bool(val)
def html_color_for_ansi_color_index(val):
arr = ['black', '#AA0000', '#00AA00', '#AA5500', '#0000AA', '#AA00AA', '#00AAAA', '#AAAAAA', '#555555', '#FF5555', '#55FF55', '#FFFF55', '#5555FF', '#FF55FF', '#55FFFF', 'white', '#000000', '#00005f', '#000087', '#0000af', '#0000d7', '#0000ff', '#005f00', '#005f5f', '#005f87', '#005faf', '#005fd7', '#005fff', '#008700', '#00875f', '#008787', '#0087af', '#0087d7', '#0087ff', '#00af00', '#00af5f', '#00af87', '#00afaf', '#00afd7', '#00afff', '#00d700', '#00d75f', '#00d787', '#00d7af', '#00d7d7', '#00d7ff', '#00ff00', '#00ff5f', '#00ff87', '#00ffaf', '#00ffd7', '#00ffff', '#5f0000', '#5f005f', '#5f0087', '#5f00af', '#5f00d7', '#5f00ff', '#5f5f00', '#5f5f5f', '#5f5f87', '#5f5faf', '#5f5fd7', '#5f5fff', '#5f8700', '#5f875f', '#5f8787', '#5f87af', '#5f87d7', '#5f87ff', '#5faf00', '#5faf5f', '#5faf87', '#5fafaf', '#5fafd7', '#5fafff', '#5fd700', '#5fd75f', '#5fd787', '#5fd7af', '#5fd7d7', '#5fd7ff', '#5fff00', '#5fff5f', '#5fff87', '#5fffaf', '#5fffd7', '#5fffff', '#870000', '#87005f', '#870087', '#8700af', '#8700d7', '#8700ff', '#875f00', '#875f5f', '#875f87', '#875faf', '#875fd7', '#875fff', '#878700', '#87875f', '#878787', '#8787af', '#8787d7', '#8787ff', '#87af00', '#87af5f', '#87af87', '#87afaf', '#87afd7', '#87afff', '#87d700', '#87d75f', '#87d787', '#87d7af', '#87d7d7', '#87d7ff', '#87ff00', '#87ff5f', '#87ff87', '#87ffaf', '#87ffd7', '#87ffff', '#af0000', '#af005f', '#af0087', '#af00af', '#af00d7', '#af00ff', '#af5f00', '#af5f5f', '#af5f87', '#af5faf', '#af5fd7', '#af5fff', '#af8700', '#af875f', '#af8787', '#af87af', '#af87d7', '#af87ff', '#afaf00', '#afaf5f', '#afaf87', '#afafaf', '#afafd7', '#afafff', '#afd700', '#afd75f', '#afd787', '#afd7af', '#afd7d7', '#afd7ff', '#afff00', '#afff5f', '#afff87', '#afffaf', '#afffd7', '#afffff', '#d70000', '#d7005f', '#d70087', '#d700af', '#d700d7', '#d700ff', '#d75f00', '#d75f5f', '#d75f87', '#d75faf', '#d75fd7', '#d75fff', '#d78700', '#d7875f', '#d78787', '#d787af', '#d787d7', '#d787ff', '#d7af00', '#d7af5f', '#d7af87', '#d7afaf', '#d7afd7', '#d7afff', '#d7d700', '#d7d75f', '#d7d787', '#d7d7af', '#d7d7d7', '#d7d7ff', '#d7ff00', '#d7ff5f', '#d7ff87', '#d7ffaf', '#d7ffd7', '#d7ffff', '#ff0000', '#ff005f', '#ff0087', '#ff00af', '#ff00d7', '#ff00ff', '#ff5f00', '#ff5f5f', '#ff5f87', '#ff5faf', '#ff5fd7', '#ff5fff', '#ff8700', '#ff875f', '#ff8787', '#ff87af', '#ff87d7', '#ff87ff', '#ffaf00', '#ffaf5f', '#ffaf87', '#ffafaf', '#ffafd7', '#ffafff', '#ffd700', '#ffd75f', '#ffd787', '#ffd7af', '#ffd7d7', '#ffd7ff', '#ffff00', '#ffff5f', '#ffff87', '#ffffaf', '#ffffd7', '#ffffff', '#080808', '#121212', '#1c1c1c', '#262626', '#303030', '#3a3a3a', '#444444', '#4e4e4e', '#585858', '#626262', '#6c6c6c', '#767676', '#808080', '#8a8a8a', '#949494', '#9e9e9e', '#a8a8a8', '#b2b2b2', '#bcbcbc', '#c6c6c6', '#d0d0d0', '#dadada', '#e4e4e4', '#eeeeee']
arr = ['black', '#AA0000', '#00AA00', '#AA5500', '#0000AA', '#AA00AA',
'#00AAAA', '#AAAAAA', '#555555', '#FF5555', '#55FF55', '#FFFF55',
'#5555FF', '#FF55FF', '#55FFFF', 'white', '#000000', '#00005f',
'#000087', '#0000af', '#0000d7', '#0000ff', '#005f00', '#005f5f',
'#005f87', '#005faf', '#005fd7', '#005fff', '#008700', '#00875f',
'#008787', '#0087af', '#0087d7', '#0087ff', '#00af00', '#00af5f',
'#00af87', '#00afaf', '#00afd7', '#00afff', '#00d700', '#00d75f',
'#00d787', '#00d7af', '#00d7d7', '#00d7ff', '#00ff00', '#00ff5f',
'#00ff87', '#00ffaf', '#00ffd7', '#00ffff', '#5f0000', '#5f005f',
'#5f0087', '#5f00af', '#5f00d7', '#5f00ff', '#5f5f00', '#5f5f5f',
'#5f5f87', '#5f5faf', '#5f5fd7', '#5f5fff', '#5f8700', '#5f875f',
'#5f8787', '#5f87af', '#5f87d7', '#5f87ff', '#5faf00', '#5faf5f',
'#5faf87', '#5fafaf', '#5fafd7', '#5fafff', '#5fd700', '#5fd75f',
'#5fd787', '#5fd7af', '#5fd7d7', '#5fd7ff', '#5fff00', '#5fff5f',
'#5fff87', '#5fffaf', '#5fffd7', '#5fffff', '#870000', '#87005f',
'#870087', '#8700af', '#8700d7', '#8700ff', '#875f00', '#875f5f',
'#875f87', '#875faf', '#875fd7', '#875fff', '#878700', '#87875f',
'#878787', '#8787af', '#8787d7', '#8787ff', '#87af00', '#87af5f',
'#87af87', '#87afaf', '#87afd7', '#87afff', '#87d700', '#87d75f',
'#87d787', '#87d7af', '#87d7d7', '#87d7ff', '#87ff00', '#87ff5f',
'#87ff87', '#87ffaf', '#87ffd7', '#87ffff', '#af0000', '#af005f',
'#af0087', '#af00af', '#af00d7', '#af00ff', '#af5f00', '#af5f5f',
'#af5f87', '#af5faf', '#af5fd7', '#af5fff', '#af8700', '#af875f',
'#af8787', '#af87af', '#af87d7', '#af87ff', '#afaf00', '#afaf5f',
'#afaf87', '#afafaf', '#afafd7', '#afafff', '#afd700', '#afd75f',
'#afd787', '#afd7af', '#afd7d7', '#afd7ff', '#afff00', '#afff5f',
'#afff87', '#afffaf', '#afffd7', '#afffff', '#d70000', '#d7005f',
'#d70087', '#d700af', '#d700d7', '#d700ff', '#d75f00', '#d75f5f',
'#d75f87', '#d75faf', '#d75fd7', '#d75fff', '#d78700', '#d7875f',
'#d78787', '#d787af', '#d787d7', '#d787ff', '#d7af00', '#d7af5f',
'#d7af87', '#d7afaf', '#d7afd7', '#d7afff', '#d7d700', '#d7d75f',
'#d7d787', '#d7d7af', '#d7d7d7', '#d7d7ff', '#d7ff00', '#d7ff5f',
'#d7ff87', '#d7ffaf', '#d7ffd7', '#d7ffff', '#ff0000', '#ff005f',
'#ff0087', '#ff00af', '#ff00d7', '#ff00ff', '#ff5f00', '#ff5f5f',
'#ff5f87', '#ff5faf', '#ff5fd7', '#ff5fff', '#ff8700', '#ff875f',
'#ff8787', '#ff87af', '#ff87d7', '#ff87ff', '#ffaf00', '#ffaf5f',
'#ffaf87', '#ffafaf', '#ffafd7', '#ffafff', '#ffd700', '#ffd75f',
'#ffd787', '#ffd7af', '#ffd7d7', '#ffd7ff', '#ffff00', '#ffff5f',
'#ffff87', '#ffffaf', '#ffffd7', '#ffffff', '#080808', '#121212',
'#1c1c1c', '#262626', '#303030', '#3a3a3a', '#444444', '#4e4e4e',
'#585858', '#626262', '#6c6c6c', '#767676', '#808080', '#8a8a8a',
'#949494', '#9e9e9e', '#a8a8a8', '#b2b2b2', '#bcbcbc', '#c6c6c6',
'#d0d0d0', '#dadada', '#e4e4e4', '#eeeeee']
if val < 0 or val >= len(arr):
return ''
else:
@ -141,6 +194,8 @@ def html_color_for_ansi_color_index(val):
# Function to return special ANSI escapes like exit_attribute_mode
g_special_escapes_dict = None
def get_special_ansi_escapes():
global g_special_escapes_dict
if g_special_escapes_dict is None:
@ -152,8 +207,10 @@ def get_special_ansi_escapes():
def get_tparm(key):
val = None
key = curses.tigetstr("sgr0")
if key: val = curses.tparm(key)
if val: val = val.decode('utf-8')
if key:
val = curses.tparm(key)
if val:
val = val.decode('utf-8')
return val
# Just a few for now
@ -165,6 +222,8 @@ def get_special_ansi_escapes():
# Given a known ANSI escape sequence, convert it to HTML and append to the list
# Returns whether we have an open <span>
def append_html_for_ansi_escape(full_val, result, span_open):
# Strip off the initial \x1b[ and terminating m
@ -179,16 +238,16 @@ def append_html_for_ansi_escape(full_val, result, span_open):
match = re.match('38;5;(\d+)', val)
if match is not None:
close_span()
html_color = html_color_for_ansi_color_index(int(match.group(1)))
html_color = html_color_for_ansi_color_index(int(match.group(1)))
result.append('<span style="color: ' + html_color + '">')
return True # span now open
return True # span now open
# term8 foreground color
if val in [str(x) for x in range(30, 38)]:
close_span()
html_color = html_color_for_ansi_color_index(int(val) - 30)
html_color = html_color_for_ansi_color_index(int(val) - 30)
result.append('<span style="color: ' + html_color + '">')
return True # span now open
return True # span now open
# Try special escapes
special_escapes = get_special_ansi_escapes()
@ -201,15 +260,17 @@ def append_html_for_ansi_escape(full_val, result, span_open):
# 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
# 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
@ -217,10 +278,10 @@ def ansi_prompt_line_width(val):
def ansi_to_html(val):
# Split us up by ANSI escape sequences
# We want to catch not only the standard color codes, but also things like sgr0
# Hence this lame check
# Note that Python 2.6 doesn't have a flag param to re.split, so we have to compile it first
# Split us up by ANSI escape sequences. We want to catch not only the
# standard color codes, but also things like sgr0. Hence this lame check.
# Note that Python 2.6 doesn't have a flag param to re.split, so we have to
# compile it first.
reg = re.compile("""
( # Capture
\x1b # Escape
@ -245,10 +306,12 @@ def ansi_to_html(val):
result.append(cgi.escape(strip_ansi(component)))
else:
# It's an escape sequence. Close the previous escape.
span_open = append_html_for_ansi_escape(component, result, span_open)
span_open = append_html_for_ansi_escape(component, result,
span_open)
# Close final escape
if span_open: result.append('</span>')
if span_open:
result.append('</span>')
# Remove empty elements
result = [x for x in result if x]
@ -264,6 +327,7 @@ def ansi_to_html(val):
return ''.join(result)
class FishVar:
""" A class that represents a variable """
def __init__(self, name, value):
@ -275,15 +339,20 @@ class FishVar:
def get_json_obj(self):
# Return an array(3): name, value, flags
flags = []
if self.universal: flags.append('universal')
if self.exported: flags.append('exported')
return {"name": self.name, "value": self.value, "Flags": ', '.join(flags)}
if self.universal:
flags.append('universal')
if self.exported:
flags.append('exported')
return {"name": self.name, "value": self.value,
"Flags": ', '.join(flags)}
class FishBinding:
"""A class that represents keyboard binding """
def __init__(self, command, raw_binding, readable_binding, description=None):
self.command = command
def __init__(self, command, raw_binding, readable_binding,
description=None):
self.command = command
self.bindings = []
self.description = description
self.add_binding(raw_binding, readable_binding)
@ -294,23 +363,24 @@ class FishBinding:
i['raw_bindings'].append(raw_binding)
break
else:
self.bindings.append({'readable_binding':readable_binding, 'raw_bindings':[raw_binding]})
self.bindings.append({'readable_binding': readable_binding,
'raw_bindings': [raw_binding]})
def get_json_obj(self):
return {"command" : self.command, "bindings": self.bindings, "description": self.description}
return {"command": self.command, "bindings": self.bindings,
"description": self.description}
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",
"btab": "Shift Tab"
}
# 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",
"btab": "Shift Tab"}
def set_buffer(self, buffer):
""" Sets code to parse """
@ -348,7 +418,8 @@ class BindingParser:
# \[1\; is start of control sequence
if c == '1':
b = self.get_char(); c = self.get_char()
b = self.get_char()
c = self.get_char()
if b == '\\' and c == '~':
result += "Home"
elif c == ";":
@ -361,7 +432,8 @@ class BindingParser:
# \[4\~ is End
if c == '4':
b = self.get_char(); c = self.get_char()
b = self.get_char()
c = self.get_char()
if b == '\\' and c == '~':
result += "End"
@ -518,7 +590,7 @@ 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, '')
data = { "name": color_name, "description" : color_desc }
data = {"name": color_name, "description": color_desc}
data.update(parse_color(color_value))
result.append(data)
remaining.discard(color_name)
@ -556,18 +628,22 @@ class FishConfigHTTPRequestHandler(SimpleHTTPServer.SimpleHTTPRequestHandler):
vars = {}
for line in out.split('\n'):
comps = line.split(' ', 1)
if len(comps) < 2: continue
if len(comps) < 2:
continue
fish_var = FishVar(comps[0], comps[1])
vars[fish_var.name] = fish_var
# Mark universal variables. L means don't abbreviate.
for name in self.do_get_variable_names('set -nUL'):
if name in vars: vars[name].universal = True
if name in vars:
vars[name].universal = True
# Mark exported variables. L means don't abbreviate.
for name in self.do_get_variable_names('set -nxL'):
if name in vars: vars[name].exported = True
if name in vars:
vars[name].exported = True
return [vars[key].get_json_obj() for key in sorted(vars.keys(), key=lambda x: x.lower())]
return [vars[key].get_json_obj() for key
in sorted(vars.keys(), key=lambda x: x.lower())]
def do_get_bindings(self):
""" Get key bindings """
@ -613,29 +689,37 @@ class FishConfigHTTPRequestHandler(SimpleHTTPServer.SimpleHTTPRequestHandler):
bindings.append(fish_binding)
command_to_binding[command] = fish_binding
return [ binding.get_json_obj() for binding in bindings ]
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
# 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() # Trim off the trailing element
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"
# Return the color with the given name, or the empty string if there is
# none.
out, err = run_fish_cmd("echo -n $" + name)
return out
def do_set_color_for_variable(self, name, color, background_color, bold, underline):
if not color: color = 'normal'
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'
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
@ -647,7 +731,7 @@ class FishConfigHTTPRequestHandler(SimpleHTTPServer.SimpleHTTPRequestHandler):
def do_delete_history_item(self, history_item_text):
# It's really lame that we always return success here
cmd = ('builtin history delete --exact -- %s; builtin history save' %
escape_fish_cmd(history_item_text))
escape_fish_cmd(history_item_text))
out, err = run_fish_cmd(cmd)
return True
@ -661,29 +745,35 @@ class FishConfigHTTPRequestHandler(SimpleHTTPServer.SimpleHTTPRequestHandler):
prompt_demo_ansi, err = run_fish_cmd(command_to_run)
prompt_demo_html = ansi_to_html(prompt_demo_ansi)
prompt_demo_font_size = self.font_size_for_ansi_prompt(prompt_demo_ansi)
result = {'function': prompt_function_text, 'demo': prompt_demo_html, 'font_size': prompt_demo_font_size }
result = {'function': prompt_function_text, 'demo': prompt_demo_html,
'font_size': prompt_demo_font_size}
if extras_dict:
result.update(extras_dict)
return result
def do_get_current_prompt(self):
# Return the current prompt
# We run 'false' to demonstrate how the prompt shows the command status (#1624)
# Return the current prompt. We run 'false' to demonstrate how the
# prompt shows the command status (#1624).
prompt_func, err = run_fish_cmd('functions fish_prompt')
result = self.do_get_prompt('builtin cd "' + initial_wd + '" ; false ; fish_prompt', prompt_func.strip(), {'name': 'Current'})
result = self.do_get_prompt(
'builtin cd "' + initial_wd + '" ; false ; fish_prompt',
prompt_func.strip(), {'name': 'Current'})
return result
def do_get_sample_prompt(self, text, extras_dict):
# Return the prompt you get from the given text
# extras_dict is a dictionary whose values get merged in
# We run 'false' to demonstrate how the prompt shows the command status (#1624)
cmd = text + "\n builtin cd \"" + initial_wd + "\" \n false \n fish_prompt\n"
# Return the prompt you get from the given text. Extras_dict is a
# dictionary whose values get merged in. We run 'false' to demonstrate
# how the prompt shows the command status (#1624)
cmd = (text + "\n builtin cd \"" + initial_wd +
"\" \n false \n fish_prompt\n")
return self.do_get_prompt(cmd, text.strip(), extras_dict)
def parse_one_sample_prompt_hash(self, line, result_dict):
# Allow us to skip whitespace, etc.
if not line: return True
if line.isspace(): return True
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)
@ -695,7 +785,6 @@ class FishConfigHTTPRequestHandler(SimpleHTTPServer.SimpleHTTPRequestHandler):
# Skip other hash comments
return line.startswith('#')
def read_one_sample_prompt(self, path):
try:
with open(path, 'rb') as fd:
@ -705,10 +794,13 @@ class FishConfigHTTPRequestHandler(SimpleHTTPServer.SimpleHTTPRequestHandler):
parsing_hashes = True
unicode_lines = (line.decode('utf-8') for line in fd)
for line in unicode_lines:
# Parse hashes until parse_one_sample_prompt_hash return False
# Parse hashes until parse_one_sample_prompt_hash return
# False.
if parsing_hashes:
parsing_hashes = self.parse_one_sample_prompt_hash(line, extras_dict)
# Maybe not we're not parsing hashes, or maybe we already were not
parsing_hashes = self.parse_one_sample_prompt_hash(
line, extras_dict)
# Maybe not we're not parsing hashes, or maybe we already
# were not.
if not parsing_hashes:
function_lines.append(line)
func = ''.join(function_lines).strip()
@ -750,7 +842,8 @@ class FishConfigHTTPRequestHandler(SimpleHTTPServer.SimpleHTTPRequestHandler):
return True
def do_save_abbreviation(self, abbreviation):
out, err = run_fish_cmd('abbr --add \'%s %s\'' % (abbreviation['word'], abbreviation['phrase']))
out, err = run_fish_cmd('abbr --add \'%s %s\'' % (
abbreviation['word'], abbreviation['phrase']))
if err:
return err
else:
@ -760,21 +853,29 @@ class FishConfigHTTPRequestHandler(SimpleHTTPServer.SimpleHTTPRequestHandler):
if len(haystack) < len(needle):
return False
bits = 0
for x,y in zip(haystack, needle):
for x, y in zip(haystack, needle):
bits |= ord(x) ^ ord(y)
return bits == 0
def font_size_for_ansi_prompt(self, prompt_demo_ansi):
width = ansi_prompt_line_width(prompt_demo_ansi)
# Pick a font size
if width >= 70: font_size = '8pt'
if width >= 60: font_size = '10pt'
elif width >= 50: font_size = '11pt'
elif width >= 40: font_size = '13pt'
elif width >= 30: font_size = '15pt'
elif width >= 25: font_size = '16pt'
elif width >= 20: font_size = '17pt'
else: font_size = '18pt'
if width >= 70:
font_size = '8pt'
if width >= 60:
font_size = '10pt'
elif width >= 50:
font_size = '11pt'
elif width >= 40:
font_size = '13pt'
elif width >= 30:
font_size = '15pt'
elif width >= 25:
font_size = '16pt'
elif width >= 20:
font_size = '17pt'
else:
font_size = '18pt'
return font_size
def do_GET(self):
@ -812,7 +913,7 @@ class FishConfigHTTPRequestHandler(SimpleHTTPServer.SimpleHTTPRequestHandler):
# Return valid output
self.send_response(200)
self.send_header('Content-type','application/json')
self.send_header('Content-type', 'application/json')
self.end_headers()
self.write_to_wfile('\n')
@ -853,7 +954,9 @@ class FishConfigHTTPRequestHandler(SimpleHTTPServer.SimpleHTTPRequestHandler):
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]))
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/':
@ -873,13 +976,13 @@ class FishConfigHTTPRequestHandler(SimpleHTTPServer.SimpleHTTPRequestHandler):
output = ["Unable to set prompt"]
elif p == '/save_abbreviation/':
r = self.do_save_abbreviation(postvars)
if r == True:
if r:
output = ["OK"]
else:
output = [r]
elif p == '/remove_abbreviation/':
r = self.do_remove_abbreviation(postvars)
if r == True:
if r:
output = ["OK"]
else:
output = [r]
@ -888,7 +991,7 @@ class FishConfigHTTPRequestHandler(SimpleHTTPServer.SimpleHTTPRequestHandler):
# Return valid output
self.send_response(200)
self.send_header('Content-type','application/json')
self.send_header('Content-type', 'application/json')
self.end_headers()
self.write_to_wfile('\n')
@ -924,7 +1027,8 @@ redirect_template_html = """
fish_bin_dir = os.environ.get('__fish_bin_dir')
fish_bin_path = None
if not fish_bin_dir:
print('The __fish_bin_dir environment variable is not set. Looking in $PATH...')
print('The __fish_bin_dir environment variable is not set. '
'Looking in $PATH...')
# distutils.spawn is terribly broken, because it looks in wd before PATH,
# and doesn't actually validate that the file is even executable
for p in os.environ['PATH'].split(os.pathsep):
@ -942,7 +1046,8 @@ 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)
print("fish could not be executed at path '%s'. "
"Is fish installed correctly?" % fish_bin_path)
sys.exit(-1)
FISH_BIN_PATH = fish_bin_path
@ -950,8 +1055,8 @@ FISH_BIN_PATH = fish_bin_path
# so get the current working directory
initial_wd = os.getcwd()
# Make sure that the working directory is the one that contains the script server file,
# because the document root is the working directory
# Make sure that the working directory is the one that contains the script
# server file, because the document root is the working directory.
where = os.path.dirname(sys.argv[0])
os.chdir(where)
@ -984,16 +1089,17 @@ if PORT > 9000:
# Just look at the first letter
initial_tab = ''
if len(sys.argv) > 1:
for tab in ['functions', 'prompt', 'colors', 'variables', 'history', 'bindings', 'abbreviations']:
for tab in ['functions', 'prompt', 'colors', 'variables', 'history',
'bindings', 'abbreviations']:
if tab.startswith(sys.argv[1]):
initial_tab = '#' + tab
break
url = 'http://localhost:%d/%s/%s' % (PORT, authkey, initial_tab)
# Create temporary file to hold redirect to real server
# This prevents exposing the URL containing the authentication key on the command line
# (see CVE-2014-2914 or https://github.com/fish-shell/fish-shell/issues/1438)
# Create temporary file to hold redirect to real server. This prevents exposing
# the URL containing the authentication key on the command line (see
# CVE-2014-2914 or https://github.com/fish-shell/fish-shell/issues/1438).
if 'XDG_CACHE_HOME' in os.environ:
dirname = os.path.expanduser(os.path.expandvars('$XDG_CACHE_HOME/fish/'))
else:
@ -1004,11 +1110,12 @@ try:
os.makedirs(dirname, 0o0700)
except OSError as e:
if e.errno == 17:
pass
pass
else:
raise e
raise e
randtoken = ''.join(random.choice(string.ascii_uppercase + string.digits) for _ in range(6))
randtoken = ''.join(random.choice(string.ascii_uppercase + string.digits)
for _ in range(6))
filename = dirname + 'web_config-%s.html' % randtoken
f = open(filename, 'w')