139 lines
4.1 KiB
Go
139 lines
4.1 KiB
Go
/*
|
|
* Copyright 2026 Safronov Grigorii
|
|
*
|
|
* Licensed under the CDDL, Version 1.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
*
|
|
* You may obtain a copy of the License at
|
|
* https://opensource.org/licenses/CDDL-1.0
|
|
*/
|
|
|
|
// Файл: internal/repl/pager.go
|
|
// Назначение: Простой пейджер для длинных выводов REPL.
|
|
// Если количество строк превышает threshold, вывод отправляется в
|
|
// внешний пейджер (по умолчанию `less -R`), иначе печатается напрямую.
|
|
//
|
|
// ДОБАВЛЕНО (2026-10):
|
|
// - Threshold() / SetThreshold() — геттер и сеттер порога включения
|
|
// пейджера. Используются REPL-командой `pager size <N>` /
|
|
// `pager threshold <N>`. Значения <= 0 игнорируются, чтобы случайно
|
|
// не сломать вывод.
|
|
|
|
package repl
|
|
|
|
import (
|
|
"bytes"
|
|
"fmt"
|
|
"os"
|
|
"os/exec"
|
|
"runtime"
|
|
"strings"
|
|
)
|
|
|
|
// Pager — простой пейджер.
|
|
//
|
|
// Все поля читаются/пишутся только из REPL-цикла (последовательный
|
|
// доступ), поэтому мьютекс здесь не нужен. Если пейджер начнёт
|
|
// использоваться из фоновых горутин — добавьте sync.RWMutex.
|
|
type Pager struct {
|
|
enabled bool
|
|
threshold int
|
|
external string
|
|
lines int
|
|
}
|
|
|
|
// NewPager создаёт пейджер.
|
|
func NewPager(enabled bool, threshold int) *Pager {
|
|
p := &Pager{
|
|
enabled: enabled,
|
|
threshold: threshold,
|
|
external: defaultPagerCommand(),
|
|
lines: 0,
|
|
}
|
|
return p
|
|
}
|
|
|
|
// Enabled возвращает статус пейджера.
|
|
func (p *Pager) Enabled() bool {
|
|
return p.enabled
|
|
}
|
|
|
|
// SetEnabled включает или отключает пейджер.
|
|
func (p *Pager) SetEnabled(enabled bool) {
|
|
p.enabled = enabled
|
|
}
|
|
|
|
// Threshold возвращает порог включения пейджера (в строках).
|
|
// Если вывод короче порога — пейджер не используется.
|
|
func (p *Pager) Threshold() int {
|
|
return p.threshold
|
|
}
|
|
|
|
// SetThreshold устанавливает порог включения пейджера.
|
|
// Значения <= 0 игнорируются, чтобы случайно не сломать вывод.
|
|
func (p *Pager) SetThreshold(n int) {
|
|
if n > 0 {
|
|
p.threshold = n
|
|
}
|
|
}
|
|
|
|
// Page отображает текст через пейджер, если он включён и текст достаточно
|
|
// длинный. Иначе печатает напрямую в stdout.
|
|
func (p *Pager) Page(text string) error {
|
|
if !p.enabled {
|
|
fmt.Print(text)
|
|
return nil
|
|
}
|
|
|
|
lineCount := strings.Count(text, "\n")
|
|
if lineCount < p.threshold {
|
|
fmt.Print(text)
|
|
return nil
|
|
}
|
|
|
|
if p.external == "" {
|
|
// Нет внешнего пейджера — печатаем напрямую.
|
|
fmt.Print(text)
|
|
return nil
|
|
}
|
|
|
|
return p.pipeToExternal(text)
|
|
}
|
|
|
|
// pipeToExternal отправляет текст во внешний пейджер.
|
|
func (p *Pager) pipeToExternal(text string) error {
|
|
parts := strings.Fields(p.external)
|
|
if len(parts) == 0 {
|
|
fmt.Print(text)
|
|
return nil
|
|
}
|
|
|
|
cmd := exec.Command(parts[0], parts[1:]...)
|
|
cmd.Stdin = bytes.NewBufferString(text)
|
|
cmd.Stdout = os.Stdout
|
|
cmd.Stderr = os.Stderr
|
|
|
|
if err := cmd.Run(); err != nil {
|
|
// Если внешний пейджер упал — печатаем напрямую.
|
|
fmt.Print(text)
|
|
return err
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// defaultPagerCommand выбирает команду пейджера в зависимости от ОС.
|
|
func defaultPagerCommand() string {
|
|
switch runtime.GOOS {
|
|
case "windows":
|
|
return "more"
|
|
default:
|
|
// less -R: -R сохраняет ANSI-цвета, используется на Linux/BSD/illumos.
|
|
if _, err := exec.LookPath("less"); err == nil {
|
|
return "less -R"
|
|
}
|
|
if _, err := exec.LookPath("more"); err == nil {
|
|
return "more"
|
|
}
|
|
return ""
|
|
}
|
|
} |