diff --git a/main.go b/main.go index 6915dae..ef8f4e4 100644 --- a/main.go +++ b/main.go @@ -33,6 +33,7 @@ var ( readmeNames = []string{"README.md", "README", "Readme.md", "Readme", "readme.md", "readme"} configFile string pager bool + tui bool style string width uint showAllFiles bool @@ -159,9 +160,14 @@ func validateOptions(cmd *cobra.Command) error { width = viper.GetUint("width") mouse = viper.GetBool("mouse") pager = viper.GetBool("pager") + tui = viper.GetBool("tui") showAllFiles = viper.GetBool("all") preserveNewLines = viper.GetBool("preserveNewLines") + if pager && tui { + return errors.New("glow: cannot use both pager and tui") + } + // validate the glamour style style = viper.GetString("style") if err := validateStyle(style); err != nil { @@ -298,7 +304,8 @@ func executeCLI(cmd *cobra.Command, src *source, w io.Writer) error { } // display - if pager || cmd.Flags().Changed("pager") { + switch { + case pager || cmd.Flags().Changed("pager"): pagerCmd := os.Getenv("PAGER") if pagerCmd == "" { pagerCmd = "less -r" @@ -309,13 +316,15 @@ func executeCLI(cmd *cobra.Command, src *source, w io.Writer) error { c.Stdin = strings.NewReader(out) c.Stdout = os.Stdout return c.Run() + case tui || cmd.Flags().Changed("tui"): + return runTUI(src.URL) + default: + _, err = fmt.Fprint(w, out) + return err } - - _, err = fmt.Fprint(w, out) - return err } -func runTUI(workingDirectory string) error { +func runTUI(path string) error { // Read environment to get debugging stuff cfg, err := env.ParseAs[ui.Config]() if err != nil { @@ -327,7 +336,7 @@ func runTUI(workingDirectory string) error { cfg.GlamourStyle = style } - cfg.WorkingDirectory = workingDirectory + cfg.Path = path cfg.ShowAllFiles = showAllFiles cfg.ShowLineNumbers = showLineNumbers cfg.GlamourMaxWidth = width @@ -370,6 +379,7 @@ func init() { // "Glow Classic" cli arguments rootCmd.PersistentFlags().StringVar(&configFile, "config", "", fmt.Sprintf("config file (default %s)", viper.GetViper().ConfigFileUsed())) rootCmd.Flags().BoolVarP(&pager, "pager", "p", false, "display with pager") + rootCmd.Flags().BoolVarP(&tui, "tui", "t", false, "display with tui") rootCmd.Flags().StringVarP(&style, "style", "s", styles.AutoStyle, "style name or JSON path") rootCmd.Flags().UintVarP(&width, "width", "w", 0, "word-wrap at width (set to 0 to disable)") rootCmd.Flags().BoolVarP(&showAllFiles, "all", "a", false, "show system files and directories (TUI-mode only)") @@ -379,6 +389,8 @@ func init() { _ = rootCmd.Flags().MarkHidden("mouse") // Config bindings + _ = viper.BindPFlag("pager", rootCmd.Flags().Lookup("pager")) + _ = viper.BindPFlag("tui", rootCmd.Flags().Lookup("tui")) _ = viper.BindPFlag("style", rootCmd.Flags().Lookup("style")) _ = viper.BindPFlag("width", rootCmd.Flags().Lookup("width")) _ = viper.BindPFlag("debug", rootCmd.Flags().Lookup("debug")) diff --git a/ui/config.go b/ui/config.go index 4d4eae7..001d5b8 100644 --- a/ui/config.go +++ b/ui/config.go @@ -11,8 +11,8 @@ type Config struct { EnableMouse bool PreserveNewLines bool - // Which directory should we start from? - WorkingDirectory string + // Working directory or file path + Path string // For debugging the UI HighPerformancePager bool `env:"GLOW_HIGH_PERFORMANCE_PAGER" envDefault:"true"` diff --git a/ui/ui.go b/ui/ui.go index a270a3e..874a15e 100644 --- a/ui/ui.go +++ b/ui/ui.go @@ -144,17 +144,50 @@ func newModel(cfg Config) tea.Model { cfg: cfg, } - return model{ + m := model{ common: &common, state: stateShowStash, pager: newPagerModel(&common), stash: newStashModel(&common), } + + info, err := os.Stat(cfg.Path) + if err != nil { + log.Error("unable to stat file", "file", m.common.cfg.Path, "error", err) + m.fatalErr = err + return m + } + if info.IsDir() { + m.state = stateShowStash + } else { + cwd, _ := os.Getwd() + m.state = stateShowDocument + m.pager.currentDocument = markdown{ + localPath: cfg.Path, + Note: stripAbsolutePath(cfg.Path, cwd), + Modtime: info.ModTime(), + } + } + + return m } func (m model) Init() tea.Cmd { cmds := []tea.Cmd{m.stash.spinner.Tick} - cmds = append(cmds, findLocalFiles(*m.common)) + + switch m.state { + case stateShowStash: + cmds = append(cmds, findLocalFiles(*m.common)) + case stateShowDocument: + content, err := os.ReadFile(m.common.cfg.Path) + if err != nil { + log.Error("unable to read file", "file", m.common.cfg.Path, "error", err) + return func() tea.Msg { return errMsg{err} } + } + body := string(utils.RemoveFrontmatter(content)) + cmds = append(cmds, renderWithGlamour(m.pager, body)) + } + return tea.Batch(cmds...) } @@ -314,7 +347,7 @@ func findLocalFiles(m commonModel) tea.Cmd { return func() tea.Msg { log.Info("findLocalFiles") var ( - cwd = m.cfg.WorkingDirectory + cwd = m.cfg.Path err error ) @@ -380,17 +413,16 @@ func waitForStatusMessageTimeout(appCtx applicationContext, t *time.Timer) tea.C // document. Note that we could be doing things like checking if the file is // a directory, but we trust that gitcha has already done that. func localFileToMarkdown(cwd string, res gitcha.SearchResult) *markdown { - md := &markdown{ + return &markdown{ localPath: res.Path, Note: stripAbsolutePath(res.Path, cwd), Modtime: res.Info.ModTime(), } - - return md } func stripAbsolutePath(fullPath, cwd string) string { - return strings.ReplaceAll(fullPath, cwd+string(os.PathSeparator), "") + path, _ := filepath.Rel(cwd, fullPath) + return path } // Lightweight version of reflow's indent function.