Port the show_error kitten to Go

This commit is contained in:
Kovid Goyal
2023-06-26 17:52:31 +05:30
parent da31d21ddb
commit 326b81a970
6 changed files with 134 additions and 77 deletions

View File

@@ -1,73 +0,0 @@
#!/usr/bin/env python3
# License: GPL v3 Copyright: 2018, Kovid Goyal <kovid at kovidgoyal.net>
import json
import os
import sys
import termios
from contextlib import suppress
from typing import List
from kitty.cli import parse_args
from kitty.cli_stub import ErrorCLIOptions
from kitty.fast_data_types import open_tty
from kitty.utils import hold_till_enter, no_echo, write_all
from ..tui.operations import styled
OPTIONS = '''\
--title
default=ERROR
The title for the error message.
'''.format
def real_main(args: List[str]) -> None:
msg = 'Show an error message. For internal use by kitty.'
cli_opts, items = parse_args(args[1:], OPTIONS, '', msg, 'kitty +kitten show_error', result_class=ErrorCLIOptions)
if sys.stdin.isatty():
raise SystemExit('Input data for this kitten must be piped as JSON to STDIN')
data = json.loads(sys.stdin.buffer.read())
error_message = data['msg']
if cli_opts.title:
print(styled(cli_opts.title, fg_intense=True, fg='red', bold=True))
print()
print(error_message, flush=True)
if data.get('tb'):
import select
from kittens.tui.operations import init_state, set_cursor_visible
fd, original_termios = open_tty()
msg = '\n\r\x1b[1;32mPress e to see detailed traceback or any other key to exit\x1b[m'
write_all(fd, msg)
write_all(fd, init_state(alternate_screen=False, kitty_keyboard_mode=False) + set_cursor_visible(False))
with no_echo(fd):
termios.tcdrain(fd)
while True:
rd = select.select([fd], [], [])[0]
if not rd:
break
q = os.read(fd, 1)
if q in b'eE':
break
return
if data.get('tb'):
tb = data['tb']
for ln in tb.splitlines():
print('\r\n', ln, sep='', end='')
print(end='\r\n', flush=True)
hold_till_enter()
def main(args: List[str]) -> None:
try:
with suppress(KeyboardInterrupt, EOFError):
real_main(args)
except Exception:
import traceback
traceback.print_exc()
input('Press Enter to close')
if __name__ == '__main__':
main(sys.argv)

View File

@@ -1859,7 +1859,23 @@ class Boss:
if ec != (None, None, None):
import traceback
tb = traceback.format_exc()
self.run_kitten_with_metadata('show_error', args=['--title', title], input_data=json.dumps({'msg': msg, 'tb': tb}))
cmd = [kitten_exe(), '__show_error__', kitten_exe(), '__show_error__', '--title', title]
env = {}
env['KITTEN_RUNNING_AS_UI'] = '1'
env['KITTY_CONFIG_DIRECTORY'] = config_dir
tab = self.active_tab
w = self.active_window
if w is not None and tab is not None:
tab.new_special_window(
SpecialWindow(
cmd,
stdin=json.dumps({'msg': msg, 'tb': tb}).encode(),
env=env,
overlay_for=w.id,
),
copy_colors_from=w
)
@ac('mk', 'Create a new marker')
def create_marker(self) -> None:

View File

@@ -66,9 +66,6 @@ def generate_stub() -> None:
from kittens.resize_window.main import OPTIONS
do(OPTIONS(), 'ResizeCLIOptions')
from kittens.show_error.main import OPTIONS
do(OPTIONS(), 'ErrorCLIOptions')
from kittens.unicode_input.main import OPTIONS
do(OPTIONS(), 'UnicodeCLIOptions')

View File

@@ -0,0 +1,114 @@
// License: GPLv3 Copyright: 2023, Kovid Goyal, <kovid at kovidgoyal.net>
package show_error
import (
"encoding/json"
"fmt"
"io"
"os"
"kitty/tools/cli"
"kitty/tools/cli/markup"
"kitty/tools/tty"
"kitty/tools/tui"
"kitty/tools/tui/loop"
)
var _ = fmt.Print
type Options struct {
Title string
}
type Message struct {
Msg string `json:"msg"`
Traceback string `json:"tb"`
}
func main(args []string, opts *Options) (rc int, err error) {
if tty.IsTerminal(os.Stdin.Fd()) {
return 1, fmt.Errorf("Input data for this kitten must be piped as JSON to STDIN")
}
data, err := io.ReadAll(os.Stdin)
if err != nil {
return 1, err
}
m := Message{}
err = json.Unmarshal(data, &m)
if err != nil {
return 1, err
}
f := markup.New(true)
if opts.Title != "" {
fmt.Println(f.Err(opts.Title))
fmt.Println()
}
fmt.Println(m.Msg)
show_traceback := false
if m.Traceback != "" {
lp, err := loop.New(loop.NoAlternateScreen, loop.NoRestoreColors, loop.NoMouseTracking)
if err != nil {
return 1, err
}
lp.OnInitialize = func() (string, error) {
lp.SetCursorVisible(false)
lp.QueueWriteString("\n\r\x1b[1;32mPress e to see detailed traceback or any other key to exit\x1b[m\r\n")
return "", nil
}
lp.OnFinalize = func() string {
lp.SetCursorVisible(true)
return ""
}
lp.OnKeyEvent = func(event *loop.KeyEvent) error {
if event.Type == loop.PRESS || event.Type == loop.REPEAT {
if event.MatchesPressOrRepeat("e") || event.MatchesPressOrRepeat("shift+e") || event.MatchesPressOrRepeat("E") {
show_traceback = true
lp.Quit(0)
} else {
lp.Quit(1)
}
}
if event.MatchesPressOrRepeat("enter") || event.MatchesPressOrRepeat("kp_enter") || event.MatchesPressOrRepeat("esc") || event.MatchesPressOrRepeat("ctrl+c") || event.MatchesPressOrRepeat("ctrl+d") {
event.Handled = true
lp.Quit(0)
}
return nil
}
lp.Run()
if lp.ExitCode() == 1 {
return 0, nil
}
}
if show_traceback {
fmt.Println(m.Traceback)
fmt.Println()
}
tui.HoldTillEnter(true)
return
}
func EntryPoint(root *cli.Command) *cli.Command {
sc := root.AddSubCommand(&cli.Command{
Name: "__show_error__",
Hidden: true,
Usage: "[options]",
ShortDescription: "Show an error message. Internal use.",
HelpText: "Show an error message. Used internally by kitty.",
Run: func(cmd *cli.Command, args []string) (ret int, err error) {
opts := &Options{}
err = cmd.GetOptionValues(opts)
if err != nil {
return 1, err
}
return main(args, opts)
},
})
sc.Add(cli.OptionSpec{
Name: "--title",
Default: "ERROR",
Help: "The title for the error message",
})
return sc
}

View File

@@ -20,6 +20,7 @@ import (
"kitty/tools/cmd/edit_in_kitty"
"kitty/tools/cmd/pytest"
"kitty/tools/cmd/run_shell"
"kitty/tools/cmd/show_error"
"kitty/tools/cmd/update_self"
"kitty/tools/tui"
)
@@ -58,6 +59,8 @@ func KittyToolEntryPoints(root *cli.Command) {
themes.ParseEntryPoint(root)
// run-shell
run_shell.EntryPoint(root)
// show_error
show_error.EntryPoint(root)
// __pytest__
pytest.EntryPoint(root)
// __hold_till_enter__