Skip to content

Commit

Permalink
Improve fish lexer. Add syntax highlighting for commands
Browse files Browse the repository at this point in the history
- Commands are syntax highlighted as functions
- Command options are highlighted as attributes
- Added more built-ins and operators
- Added Hashbang
- Added and improved syntax highlighting for function, variable and
keywords
- Added test files
  • Loading branch information
CIAvash authored and alecthomas committed May 11, 2021
1 parent 35539cf commit 33faf55
Show file tree
Hide file tree
Showing 3 changed files with 1,545 additions and 7 deletions.
43 changes: 36 additions & 7 deletions lexers/f/fish.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,36 +17,65 @@ var Fish = internal.Register(MustNewLazyLexer(
))

func fishRules() Rules {
keywords := []string{
`begin`, `end`, `if`, `else`, `while`, `break`, `for`, `return`, `function`, `block`,
`case`, `continue`, `switch`, `not`, `and`, `or`, `set`, `echo`, `exit`, `pwd`, `true`,
`false`, `cd`, `cdh`, `count`, `test`,
}
keywordsPattern := Words(`\b`, `\b`, keywords...)

builtins := []string{
`alias`, `bg`, `bind`, `breakpoint`, `builtin`, `argparse`, `abbr`, `string`, `command`,
`commandline`, `complete`, `contains`, `dirh`, `dirs`, `disown`, `emit`, `eval`, `exec`,
`fg`, `fish`, `fish_add_path`, `fish_breakpoint_prompt`, `fish_command_not_found`,
`fish_config`, `fish_git_prompt`, `fish_greeting`, `fish_hg_prompt`, `fish_indent`,
`fish_is_root_user`, `fish_key_reader`, `fish_mode_prompt`, `fish_opt`, `fish_pager`,
`fish_prompt`, `fish_right_prompt`, `fish_status_to_signal`, `fish_svn_prompt`,
`fish_title`, `fish_update_completions`, `fish_vcs_prompt`, `fishd`, `funced`,
`funcsave`, `functions`, `help`, `history`, `isatty`, `jobs`, `math`, `mimedb`, `nextd`,
`open`, `prompt_pwd`, `realpath`, `popd`, `prevd`, `psub`, `pushd`, `random`, `read`,
`set_color`, `source`, `status`, `suspend`, `trap`, `type`, `ulimit`, `umask`, `vared`,
`fc`, `getopts`, `hash`, `kill`, `printf`, `time`, `wait`,
}

return Rules{
"root": {
Include("basic"),
Include("data"),
Include("interp"),
Include("data"),
},
"interp": {
{`\$\(\(`, Keyword, Push("math")},
{`\(`, Keyword, Push("paren")},
{`\$#?(\w+|.)`, NameVariable, nil},
},
"basic": {
{`\b(begin|end|if|else|while|break|for|in|return|function|block|case|continue|switch|not|and|or|set|echo|exit|pwd|true|false|cd|count|test)(\s*)\b`, ByGroups(Keyword, Text), nil},
{`\b(alias|bg|bind|breakpoint|builtin|command|commandline|complete|contains|dirh|dirs|emit|eval|exec|fg|fish|fish_config|fish_indent|fish_pager|fish_prompt|fish_right_prompt|fish_update_completions|fishd|funced|funcsave|functions|help|history|isatty|jobs|math|mimedb|nextd|open|popd|prevd|psub|pushd|random|read|set_color|source|status|trap|type|ulimit|umask|vared|fc|getopts|hash|kill|printf|time|wait)\s*\b(?!\.)`, NameBuiltin, nil},
{Words(`(?<=(?:^|\A|;|&&|\|\||\||`+keywordsPattern+`)\s*)`, `(?=;?\b)`, keywords...), Keyword, nil},
{`(?<=for\s+\S+\s+)in\b`, Keyword, nil},
{Words(`\b`, `\s*\b(?!\.)`, builtins...), NameBuiltin, nil},
{`#!.*\n`, CommentHashbang, nil},
{`#.*\n`, Comment, nil},
{`\\[\w\W]`, LiteralStringEscape, nil},
{`(\b\w+)(\s*)(=)`, ByGroups(NameVariable, Text, Operator), nil},
{`[\[\]()=]`, Operator, nil},
{`[\[\]()={}]`, Operator, nil},
{`(?<=\[[^\]]+)\.\.|-(?=[^\[]+\])`, Operator, nil},
{`<<-?\s*(\'?)\\?(\w+)[\w\W]+?\2`, LiteralString, nil},
{`(?<=set\s+(?:--?[^\d\W][\w-]*\s+)?)\w+`, NameVariable, nil},
{`(?<=for\s+)\w[\w-]*(?=\s+in)`, NameVariable, nil},
{`(?<=function\s+)\w(?:[^\n])*?(?= *[-\n])`, NameFunction, nil},
{`(?<=(?:^|\b(?:and|or|sudo)\b|;|\|\||&&|\||\(|(?:\b\w+\s*=\S+\s)) *)\w[\w-]*`, NameFunction, nil},
},
"data": {
{`(?s)\$?"(\\\\|\\[0-7]+|\\.|[^"\\$])*"`, LiteralStringDouble, nil},
{`"`, LiteralStringDouble, Push("string")},
{`(?s)\$'(\\\\|\\[0-7]+|\\.|[^'\\])*'`, LiteralStringSingle, nil},
{`(?s)'.*?'`, LiteralStringSingle, nil},
{`;`, Punctuation, nil},
{`&|\||\^|<|>`, Operator, nil},
{`&&|\|\||&|\||\^|<|>`, Operator, nil},
{`\s+`, Text, nil},
{`\d+(?= |\Z)`, LiteralNumber, nil},
{"[^=\\s\\[\\]{}()$\"\\'`\\\\<&|;]+", Text, nil},
{`\b\d+\b`, LiteralNumber, nil},
{`--?[^\d][\w-]*`, NameAttribute, nil},
{".+?", Text, nil},
},
"string": {
{`"`, LiteralStringDouble, Pop(1)},
Expand Down
244 changes: 244 additions & 0 deletions lexers/testdata/fish.actual
Original file line number Diff line number Diff line change
@@ -0,0 +1,244 @@
#!/usr/bin/env fish

echo hello > output.txt

alias something=echo

sudo systemctl start postgresql

# Outputs 'image.png'.
echo (basename image.jpg .jpg).png

echo some text for testing

# Convert all JPEG files in the current directory to the
# PNG format using the 'convert' program.
for i in *.jpg; convert $i (basename $i .jpg).png; end

# Set the ``data`` variable to the contents of 'data.txt'
# without splitting it into a list.
begin; set -l IFS; set data (cat data.txt); end

# Set ``$data`` to the contents of data, splitting on NUL-bytes.
set data (cat data | string split0)

grep fish myanimallist1 | wc -l

echo input.{c,h,txt}

echo {$dogs}dog

echo (seq 10)[1 2 3]
echo (seq 10)[2..5 1..3]
echo (seq 10)[-1..1]
set PATH $PATH[-1..1]

set foo banana
foo=gagaga echo $foo # prints gagaga, while in other shells it might print "banana"
foo=gagaga somecommand $foo # prints gagaga, while in other shells it might print "banana"

function some func
echo 'function' with space
end

function dirs --description 'Print directory stack'
set -l options h/help c
argparse -n dirs --max-args=0 $options -- $argv
or return

if set -q _flag_help
__fish_print_help dirs
return 0
end

if set -q _flag_c
# Clear directory stack.
set -e -g dirstack
return 0
end

# Replace $HOME with ~.
string replace -r '^'"$HOME"'($|/)' '~$1' -- $PWD $dirstack | string join " "
end

function ytdl_files -d "Download videos from text files with youtube-dl and put them into folders"
argparse --name=ytdl_files 's/shutdown' -- $argv

for file in $argv
echo "Operating on $file"
youtube-dl -a $file -i -o (dirname $file)"/"(basename $file .txt)"/%(autonumber)s-%(title)s.%(ext)s"
end

if test -n "$_flag_shutdown"
echo "poweroff"
end
end

function cheat -d 'Get programming language cheat sheets from cheat.sh'
if test $argv[1]
curl https://cheat.sh/$argv[1]
else
curl https://cheat.sh
end
end

function dut -d 'Get top paths with most disk usage'
du -hs $argv[2]/* | sort -rh | head -$argv[1]
end

function m2d --description 'Move to desktop -- m2d program_name desktop_num'
bspc node (xdo id -N $argv[1]) -d $argv[2]
end

set -x no_proxy 'localhost,127.0.0.1'

function toggle_proxy
if not set -q HTTP_PROXY
for proxy in HTTP_PROXY HTTPS_PROXY http_proxy https_proxy
set -gx $proxy 'http://127.0.0.1:8118'
end
echo 'Proxy On'
else
set -e {HTTP_PROXY,HTTPS_PROXY,http_proxy,https_proxy}
echo 'proxy Off'
end
end

function wttr -d 'Get weather info from wttr.in'
if test $argv[1]
curl https://wttr.in/$argv[1]
else
curl https://wttr.in/
end
end

function fish_config --description "Launch fish's web based configuration"
argparse h/help -- $argv
or return

if set -q _flag_help
__fish_print_help fish_config
return 0
end

set -l cmd $argv[1]
set -e argv[1]

set -q cmd[1]
or set cmd browse

# The web-based configuration UI
# Also opened with just `fish_config` or `fish_config browse`.
if contains -- $cmd browse
set -lx __fish_bin_dir $__fish_bin_dir
if set -l python (__fish_anypython)
$python "$__fish_data_dir/tools/web_config/webconfig.py" $argv
else
echo (set_color $fish_color_error)Cannot launch the web configuration tool:(set_color normal)
echo (set_color -o)"fish_config browse"(set_color normal) requires Python.
echo Installing python will fix this, and also enable completions to be
echo automatically generated from man pages.\n
echo To change your prompt, use (set_color -o)"fish_config prompt"(set_color normal) or create a (set_color -o)"fish_prompt"(set_color normal) function.
echo To list the samples use (set_color -o)"fish_config prompt show"(set_color normal).\n

echo You can tweak your colors by setting the (set_color $fish_color_search_match)\$fish_color_\*(set_color normal) variables.
end
return 0
end

if not contains -- $cmd prompt
echo No such subcommand: $cmd >&2
return 1
end

# prompt - for prompt switching
set -l cmd $argv[1]
set -e argv[1]

if contains -- $cmd list; and set -q argv[1]
echo "Too many arguments" >&2
return 1
end

set -l prompt_dir $__fish_data_dir/sample_prompts $__fish_data_dir/tools/web_config/sample_prompts
switch $cmd
case show
set -l fish (status fish-path)
set -l prompts $prompt_dir/$argv.fish
set -q prompts[1]; or set prompts $prompt_dir/*.fish
for p in $prompts
if not test -e "$p"
continue
end
set -l promptname (string replace -r '.*/([^/]*).fish$' '$1' $p)
echo -s (set_color --underline) $promptname (set_color normal)
$fish -c "functions -e fish_right_prompt; source $p;
false
fish_prompt
echo (set_color normal)
if functions -q fish_right_prompt;
echo right prompt: (false; fish_right_prompt)
end"
echo
end
case list ''
string replace -r '.*/([^/]*).fish$' '$1' $prompt_dir/*.fish
return
case choose
if set -q argv[2]
echo "Too many arguments" >&2
return 1
end
if not set -q argv[1]
echo "Too few arguments" >&2
return 1
end

set -l have 0
for f in $prompt_dir/$argv[1].fish
if test -f $f
source $f
set have 1
break
end
end
if test $have -eq 0
echo "No such prompt: '$argv[1]'" >&2
return 1
end
case save
read -P"Overwrite prompt? [y/N]" -l yesno
if string match -riq 'y(es)?' -- $yesno
echo Overwriting
cp $__fish_config_dir/functions/fish_prompt.fish{,.bak}

if set -q argv[1]
set -l have 0
for f in $prompt_dir/$argv[1].fish
if test -f $f
set have 1
source $f
or return 2
end
end
if test $have -eq 0
echo "No such prompt: '$argv[1]'" >&2
return 1
end
end

funcsave fish_prompt
or return

functions -q fish_right_prompt
and funcsave fish_right_prompt

return
else
echo Not overwriting
return 1
end
end

return 0
end
Loading

0 comments on commit 33faf55

Please sign in to comment.