diff --git a/assets/highlighting-tests/javascript.js b/assets/highlighting-tests/javascript.js new file mode 100644 index 00000000000..53c000b1f2b --- /dev/null +++ b/assets/highlighting-tests/javascript.js @@ -0,0 +1,59 @@ +// Single-line comment +/* Multi-line + comment */ + +const single = 'single quoted string'; +const double = "double quoted string"; +const template = `template string with ${single}`; + +const decimal = 42; +const negative = -3.14e+2; +const hex = 0x2a; +const binary = 0b101010; +const octal = 0o52; + +const truthy = true; +const falsy = false; +const empty = null; +const missing = undefined; +const notANumber = NaN; +const infinite = Infinity; + +export async function greet(name) { + if (name instanceof String) { + return; + } else if (name in { user: "ok" }) { + throw new Error("unexpected"); + } + + for (let i = 0; i < 3; i++) { + while (false) { + break; + } + } + + try { + return console.log(template, name); + } catch (error) { + return void error; + } finally { + delete globalThis.temp; + } +} + +class Person extends Object { + constructor(name) { + super(); + this.name = name; + } +} + +const result = greet("world"); +switch (result) { + case true: + break; + default: + continueLabel: do { + break continueLabel; + } while (false); +} diff --git a/assets/highlighting-tests/markdown.md b/assets/highlighting-tests/markdown.md index c4845eb6801..555b182f961 100644 --- a/assets/highlighting-tests/markdown.md +++ b/assets/highlighting-tests/markdown.md @@ -66,6 +66,12 @@ Reference: ![Logo][logo-ref] echo "Hello, world" | tr a-z A-Z ``` +```javascript +export function greet(name) { + return `hello ${name}`; +} +``` + ```json { "name": "gfm-kitchen-sink", @@ -73,3 +79,8 @@ echo "Hello, world" | tr a-z A-Z "scripts": { "test": "echo ok" } } ``` + +```python +def greet(name: str) -> str: + return f"hello {name}" +``` diff --git a/assets/highlighting-tests/python.py b/assets/highlighting-tests/python.py new file mode 100644 index 00000000000..4c49bbd9a59 --- /dev/null +++ b/assets/highlighting-tests/python.py @@ -0,0 +1,48 @@ +# Single-line comment +'''Triple single quoted string''' +"""Triple double quoted string""" + +single = 'single quoted string' +double = "double quoted string" + +decimal = 42 +negative = -3.14e+2 +hex_value = 0x2A +binary_value = 0b101010 +octal_value = 0o52 +complex_value = 1.5j + +truthy = True +falsy = False +nothing = None + +@decorator +async def greet(name: str) -> None: + value = f"Hello, {name}" + + if value and name is not None: + print(value) + elif value in {"hello", "world"}: + raise ValueError("unexpected") + else: + return None + + for item in [single, double]: + while False: + break + + try: + assert item + except Exception as exc: + yield exc + finally: + pass + + +class Person: + def __init__(self, name): + self.name = name + + +result = greet("world") +lambda_value = lambda x: x + 1 diff --git a/assets/highlighting-tests/zsh.sh b/assets/highlighting-tests/zsh.sh new file mode 100644 index 00000000000..46ae4cf461a --- /dev/null +++ b/assets/highlighting-tests/zsh.sh @@ -0,0 +1,40 @@ +#!/usr/bin/env zsh +# zsh-style shell sample + +setopt autocd extendedglob + +typeset -g POWERLEVEL="lean" +readonly ZSH_THEME="agnoster" + +plugins=(git docker fzf) + +alias ll='ls -lah' +alias gs='git status' + +path=("$HOME/bin" $path) +export EDITOR="nvim" + +function mkcd() { + local dir="$1" + mkdir -p "$dir" && cd "$dir" +} + +if [[ -n "$HOME" ]]; then + echo "home is $HOME" +elif [[ -z "$HOME" ]]; then + echo "missing home" +else + echo "unexpected" +fi + +for plugin in $plugins; do + echo "$plugin" +done + +case "$ZSH_THEME" in + agnoster) echo "theme selected" ;; + *) echo "default theme" ;; +esac + +source "$HOME/.zsh_aliases" +mkcd "${HOME}/tmp" diff --git a/crates/lsh/definitions/bash.lsh b/crates/lsh/definitions/bash.lsh new file mode 100644 index 00000000000..433cb0f2ffd --- /dev/null +++ b/crates/lsh/definitions/bash.lsh @@ -0,0 +1,61 @@ +#[display_name = "Bash"] +#[path = "**/*.bash"] +#[path = "**/*.sh"] +#[path = "**/.bash_profile"] +#[path = "**/.bashrc"] +#[path = "**/.profile"] +pub fn bash() { + until /$/ { + yield other; + + if /#.*/ { + yield comment; + } else if /'/ { + until /$/ { + yield string; + if /'/ { yield string; break; } + await input; + } + } else if /"/ { + until /$/ { + yield string; + if /\\./ {} + else if /"/ { yield string; break; } + await input; + } + } else if /`/ { + until /$/ { + yield string; + if /\\./ {} + else if /`/ { yield string; break; } + await input; + } + } else if /\$\{[^}]+\}|\$[A-Za-z_]\w*|\$\d+|\$[#?*!@$-]/ { + yield variable; + } else if /(?:if|then|elif|else|fi|for|while|until|do|done|case|esac|in|select)\>/ { + if /\w+/ { + yield other; + } else { + yield keyword.control; + } + } else if /(?:declare|export|function|local|readonly|source)\>/ { + if /\w+/ { + yield other; + } else { + yield keyword.other; + } + } else if /-?(?:(?i:0x[\da-fA-F]+)|\d+\.?\d*|\.\d+)(?i:e[+-]?\d+)?/ { + if /\w+/ { + yield other; + } else { + yield constant.numeric; + } + } else if /([A-Za-z_][\w-]*)\s*\(/ { + yield $1 as method; + } else if /[A-Za-z_][\w-]*/ { + // Gobble any other tokens that should not be highlighted + } + + yield other; + } +} diff --git a/crates/lsh/definitions/javascript.lsh b/crates/lsh/definitions/javascript.lsh new file mode 100644 index 00000000000..8c496a2278e --- /dev/null +++ b/crates/lsh/definitions/javascript.lsh @@ -0,0 +1,74 @@ +#[display_name = "JavaScript"] +#[path = "**/*.cjs"] +#[path = "**/*.js"] +#[path = "**/*.jsx"] +#[path = "**/*.mjs"] +pub fn javascript() { + until /$/ { + yield other; + + if /\/\/.*/ { + yield comment; + } else if /\/\*/ { + loop { + yield comment; + await input; + if /\*\// { + yield comment; + break; + } + } + } else if /'/ { + until /$/ { + yield string; + if /\\./ {} + else if /'/ { yield string; break; } + await input; + } + } else if /"/ { + until /$/ { + yield string; + if /\\./ {} + else if /"/ { yield string; break; } + await input; + } + } else if /`/ { + loop { + yield string; + if /\\./ {} + else if /`/ { yield string; break; } + await input; + } + } else if /(?:if|else|switch|case|default|for|while|do|break|continue|try|catch|finally|throw|return)\>/ { + if /\w+/ { + yield other; + } else { + yield keyword.control; + } + } else if /(?:async|await|class|const|delete|export|extends|function|import|in|instanceof|let|new|of|super|this|typeof|var|void|yield)\>/ { + if /\w+/ { + yield other; + } else { + yield keyword.other; + } + } else if /(?:true|false|null|undefined|NaN|Infinity)\>/ { + if /\w+/ { + yield other; + } else { + yield constant.language; + } + } else if /-?(?:(?i:0x[\da-fA-F]+)|(?i:0b[01]+)|(?i:0o[0-7]+)|\d+\.?\d*|\.\d+)(?i:e[+-]?\d+)?/ { + if /\w+/ { + yield other; + } else { + yield constant.numeric; + } + } else if /([A-Za-z_$][\w$]*)\s*\(/ { + yield $1 as method; + } else if /[A-Za-z_$][\w$]*/ { + // Gobble any other tokens that should not be highlighted + } + + yield other; + } +} diff --git a/crates/lsh/definitions/markdown.lsh b/crates/lsh/definitions/markdown.lsh index 3fcc7721a15..fdb43a2e770 100644 --- a/crates/lsh/definitions/markdown.lsh +++ b/crates/lsh/definitions/markdown.lsh @@ -18,7 +18,27 @@ pub fn markdown() { yield comment; } else if /```/ { // NOTE: These checks are sorted alphabetically. - if /(?i:diff)/ { + if /(?i:bash|sh|shell)/ { + loop { + await input; + if /\s*```/ { + return; + } else { + bash(); + if /.*/ {} + } + } + } else if /(?i:zsh)/ { + loop { + await input; + if /\s*```/ { + return; + } else { + zsh(); + if /.*/ {} + } + } + } else if /(?i:diff)/ { loop { await input; if /\s*```/ { @@ -30,6 +50,16 @@ pub fn markdown() { if /.*/ {} } } + } else if /(?i:javascript|js|jsx|mjs|cjs)/ { + loop { + await input; + if /\s*```/ { + return; + } else { + javascript(); + if /.*/ {} + } + } } else if /(?i:json)/ { loop { await input; @@ -40,6 +70,16 @@ pub fn markdown() { if /.*/ {} } } + } else if /(?i:py|python)/ { + loop { + await input; + if /\s*```/ { + return; + } else { + python(); + if /.*/ {} + } + } } else if /(?i:yaml)/ { loop { await input; diff --git a/crates/lsh/definitions/python.lsh b/crates/lsh/definitions/python.lsh new file mode 100644 index 00000000000..becaed7fb89 --- /dev/null +++ b/crates/lsh/definitions/python.lsh @@ -0,0 +1,71 @@ +#[display_name = "Python"] +#[path = "**/*.py"] +#[path = "**/*.pyi"] +#[path = "**/*.pyw"] +pub fn python() { + until /$/ { + yield other; + + if /#.*/ { + yield comment; + } else if /'''/ { + loop { + yield string; + if /'''/ { yield string; break; } + await input; + } + } else if /"""/ { + loop { + yield string; + if /"""/ { yield string; break; } + await input; + } + } else if /'/ { + until /$/ { + yield string; + if /\\./ {} + else if /'/ { yield string; break; } + await input; + } + } else if /"/ { + until /$/ { + yield string; + if /\\./ {} + else if /"/ { yield string; break; } + await input; + } + } else if /(?:if|elif|else|for|while|try|except|finally|return|break|continue|raise|with|match|case|pass)\>/ { + if /\w+/ { + yield other; + } else { + yield keyword.control; + } + } else if /(?:and|as|assert|async|await|class|def|del|from|global|import|in|is|lambda|nonlocal|not|or|yield)\>/ { + if /\w+/ { + yield other; + } else { + yield keyword.other; + } + } else if /(?:True|False|None)\>/ { + if /\w+/ { + yield other; + } else { + yield constant.language; + } + } else if /-?(?:(?i:0x[\da-fA-F]+)|(?i:0b[01]+)|(?i:0o[0-7]+)|\d+\.?\d*|\.\d+)(?i:e[+-]?\d+)?(?i:j)?/ { + if /\w+/ { + yield other; + } else { + yield constant.numeric; + } + } else if /@[A-Za-z_]\w*/ { + yield markup.link; + } else if /([A-Za-z_]\w*)\s*\(/ { + yield $1 as method; + } else if /[A-Za-z_]\w*/ { + // Gobble any other tokens that should not be highlighted + } + + yield other; + } +} diff --git a/crates/lsh/definitions/zsh.lsh b/crates/lsh/definitions/zsh.lsh new file mode 100644 index 00000000000..f22a4fff245 --- /dev/null +++ b/crates/lsh/definitions/zsh.lsh @@ -0,0 +1,60 @@ +#[display_name = "Zsh"] +#[path = "**/*.zsh"] +#[path = "**/.zprofile"] +#[path = "**/.zshenv"] +#[path = "**/.zshrc"] +pub fn zsh() { + until /$/ { + yield other; + + if /#.*/ { + yield comment; + } else if /'/ { + until /$/ { + yield string; + if /'/ { yield string; break; } + await input; + } + } else if /"/ { + until /$/ { + yield string; + if /\\./ {} + else if /"/ { yield string; break; } + await input; + } + } else if /`/ { + until /$/ { + yield string; + if /\\./ {} + else if /`/ { yield string; break; } + await input; + } + } else if /\$\{[^}]+\}|\$[A-Za-z_]\w*|\$\d+|\$[#?*!@$-]/ { + yield variable; + } else if /(?:if|then|elif|else|fi|for|while|until|do|done|case|esac|in|select)\>/ { + if /\w+/ { + yield other; + } else { + yield keyword.control; + } + } else if /(?:alias|autoload|bindkey|compdef|declare|emulate|export|function|local|readonly|setopt|source|typeset|unalias|unsetopt|zmodload)\>/ { + if /\w+/ { + yield other; + } else { + yield keyword.other; + } + } else if /-?(?:(?i:0x[\da-fA-F]+)|\d+\.?\d*|\.\d+)(?i:e[+-]?\d+)?/ { + if /\w+/ { + yield other; + } else { + yield constant.numeric; + } + } else if /([A-Za-z_][\w-]*)\s*\(/ { + yield $1 as method; + } else if /[A-Za-z_][\w-]*/ { + // Gobble any other tokens that should not be highlighted + } + + yield other; + } +}