More work on pager kitten

This commit is contained in:
Kovid Goyal 2024-01-19 21:16:09 +05:30
parent b0ab5bd5eb
commit e7e401c8dd
No known key found for this signature in database
GPG Key ID: 06BC317B515ACE7C
3 changed files with 112 additions and 1 deletions

View File

@ -0,0 +1,71 @@
// License: GPLv3 Copyright: 2024, Kovid Goyal, <kovid at kovidgoyal.net>
package pager
import (
"bytes"
"errors"
"fmt"
"io"
"os"
"strings"
"golang.org/x/sys/unix"
)
var _ = fmt.Print
func read_input(input_file *os.File, input_file_name string, input_channel chan<- input_line_struct) {
const buf_capacity = 8192
var buf_array [buf_capacity]byte
output_buf := strings.Builder{}
output_buf.Grow(buf_capacity)
var err error
var n int
defer func() {
_ = input_file.Close()
last := input_line_struct{line: output_buf.String(), err: err}
if errors.Is(err, io.EOF) {
last.err = nil
}
if len(last.line) > 0 || last.err != nil {
input_channel <- last
}
close(input_channel)
}()
process_chunk := func(chunk []byte) {
for len(chunk) > 0 {
idx := bytes.IndexByte(chunk, '\n')
switch idx {
case -1:
_, _ = output_buf.Write(chunk)
chunk = nil
default:
_, _ = output_buf.Write(chunk[idx:])
chunk = chunk[idx+1:]
input_channel <- input_line_struct{line: output_buf.String()}
output_buf.Reset()
output_buf.Grow(buf_capacity)
}
}
}
read_with_retry := func(b []byte) (n int, err error) {
for {
n, err = input_file.Read(b)
if err != unix.EAGAIN && err != unix.EINTR {
break
}
}
return
}
for err != nil {
n, err = read_with_retry(buf_array[:])
if n > 0 {
process_chunk(buf_array[:n])
}
}
}

View File

@ -14,6 +14,7 @@ package pager
import (
"fmt"
"os"
"kitty/tools/cli"
"kitty/tools/tty"
@ -23,7 +24,42 @@ var _ = fmt.Print
var debugprintln = tty.DebugPrintln
var _ = debugprintln
type input_line_struct struct {
line string
err error
}
type global_state_struct struct {
input_file_name string
opts *Options
}
var global_state global_state_struct
func main(_ *cli.Command, opts_ *Options, args []string) (rc int, err error) {
global_state.opts = opts_
input_channel := make(chan input_line_struct, 4096)
var input_file *os.File
if len(args) > 1 {
return 1, fmt.Errorf("Only a single file can be viewed at a time")
}
if len(args) == 0 {
if tty.IsTerminal(os.Stdin.Fd()) {
return 1, fmt.Errorf("STDIN is a terminal and no filename specified. See --help")
}
input_file = os.Stdin
global_state.input_file_name = "/dev/stdin"
} else {
input_file, err = os.Open(args[0])
if err != nil {
return 1, err
}
if tty.IsTerminal(input_file.Fd()) {
return 1, fmt.Errorf("%s is a terminal not paging it", args[0])
}
global_state.input_file_name = args[0]
}
go read_input(input_file, global_state.input_file_name, input_channel)
return
}

View File

@ -14,7 +14,11 @@ choices=pager,scrollback
The role the pager is used for. The default is a standard less like pager.
'''.format
help_text = 'Display text in a pager with various features such as searching, copy/paste, etc. Text can some from the specified file or from STDIN.'
help_text = '''\
Display text in a pager with various features such as searching, copy/paste, etc.
Text can some from the specified file or from STDIN. If no filename is specified
and STDIN is not a TTY, it is used.
'''
usage = '[filename]'