diff options
| author | tjp <tjp@ctrl-c.club> | 2024-01-03 18:52:18 -0700 |
|---|---|---|
| committer | tjp <tjp@ctrl-c.club> | 2024-01-03 18:52:18 -0700 |
| commit | d2b94e4b874aa2639bb22eb88494cdda73d40f22 (patch) | |
| tree | 80b8193e878038b5ade592e25bd42d0aba07f8a3 | |
| parent | 859f74231f2b48d2dcf6a29682e7651b504fda12 (diff) | |
help command, quiet mode, and s/set/select
| -rw-r--r-- | README.gmi | 51 | ||||
| -rw-r--r-- | actions.go | 58 | ||||
| -rw-r--r-- | command.go | 6 | ||||
| -rw-r--r-- | files.go | 18 | ||||
| -rw-r--r-- | handlers.go | 6 | ||||
| -rw-r--r-- | help.go | 271 | ||||
| -rw-r--r-- | main.go | 1 | ||||
| -rw-r--r-- | state.go | 2 | ||||
| -rw-r--r-- | tour.go | 2 |
9 files changed, 343 insertions, 72 deletions
@@ -1,30 +1,30 @@ # X-1: fly around the small web at the speed of sound ```ASCII art image of a Bell X-1 in flight - ... - *=:::. - ---======- -+-..... - .#@@@@@@@= .+=-:....:++ - +@@@@@@@@# :-::.. ...*+ - :%@@@@@@@@@. =*+++==:.... - .*@@@@@@@@@@= -#%###**++-:.. - -@@@@@@@@@@@% .::-=*#@@#*#@@@@@@@%-. - ..:::----=====++#@@@@@@@@@@@@%%%@@@@@@@@@@%%#**@@@@@@@= - .-+#%%@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@- - =*@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@##%@@*%@@@@@@@@@@- .. - -+*. .#@@@@@@@@@@@@@@@@@@@@@@@@@@@%@@@@@@%%%%###- =- .=.==----==--:... - ...:::::=#@@@@#**+*##@@@#*%%%%%%%%##############******+==-:-=-:.-==-:.. ... - .:.:-==++++====::-----::---:-+%@@@@@@@@@@@@@#-... - . ...............=@@@@@@@@@@@@@@+.... - -@@@@@@@@@@@@@#. - -@@@@@@@@@@@@%- - -@@%%##@@@@@@= - =%++*=-=%@@@*. - =--*%%%::#@#: - *@+=#-+*%@%- - .*%****###@+. - ::..:::.::*@@@%%%%%#: - + ... + *=:::. + ---======- -+-..... + .#@@@@@@@= .+=-:....:++ + +@@@@@@@@# :-::.. ...*+ + :%@@@@@@@@@. =*+++==:.... + .*@@@@@@@@@@= -#%###**++-:.. + -@@@@@@@@@@@% .::-=*#@@#*#@@@@@@@%-. + ..:::----=====++#@@@@@@@@@@@@%%%@@@@@@@@@@%%#**@@@@@@@= + .-+#%%@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@- + =*@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@##%@@*%@@@@@@@@@@- .. + -+*. .#@@@@@@@@@@@@@@@@@@@@@@@@@@@%@@@@@@%%%%###- =- .=.==----==--:... +...:::::=#@@@@#**+*##@@@#*%%%%%%%%##############******+==-:-=-:.-==-:.. ... + .:.:-==++++====::-----::---:-+%@@@@@@@@@@@@@#-... + . ...............=@@@@@@@@@@@@@@+.... + -@@@@@@@@@@@@@#. + -@@@@@@@@@@@@%- + -@@%%##@@@@@@= + =%++*=-=%@@@*. + =--*%%%::#@#: + *@+=#-+*%@%- + .*%****###@+. + ::..:::.::*@@@%%%%%#: + ``` ## Prior art @@ -111,7 +111,7 @@ On any other nagivation, this context is cleared and next/previous actions won't * "tour p[revious]" goes back to the previous link in the tour * "tour g[o] i" takes an index and jumps to that position in the tour -* "tour s[et] x" takes a name to select *which* tour should become active - any unique prefix will work, but the precise name will be used if it doesn't match any existing tour +* "tour s[elect] x" takes a name to select *which* tour should become active - any unique prefix will work, but the precise name will be used if it doesn't match any existing tour * without this name it re-selects the default tour * the default tour is always active and empty upon startup * the named tours have their state preserved upon any change to them @@ -122,6 +122,7 @@ The config file is located at XDG_DATA_HOME/x-1/config.toml. This is usually und It contains the following configuration options: +* quiet (bool): disables automatic printing of pages upon navigation. default "false" * vim_keys (bool): whether to activate vim keybindings for the readline prompt. default "true" * default_scheme (string): the URL scheme to use in the "go" command when none is provided. default "gemini" * soft_wrap (int): the number of columns to wrap, or -1 to not add soft wrapping. default 72 @@ -11,7 +11,6 @@ import ( "path/filepath" "strconv" "strings" - "sync" "syscall" "tildegit.org/tjp/sliderule" @@ -87,7 +86,7 @@ func Reload(state *BrowserState, conf *Config) error { return err } - return Print(state) + return print(state) } func back(state *BrowserState) error { @@ -104,8 +103,7 @@ func Back(state *BrowserState) error { return err } - _, _ = fmt.Fprintf(os.Stdout, "Back: %s\n", state.Url.String()) - return Print(state) + return print(state) } func Forward(state *BrowserState) error { @@ -115,9 +113,7 @@ func Forward(state *BrowserState) error { state.History = state.Forward state.Modal = nil - _, _ = fmt.Fprintf(os.Stdout, "Forward: %s\n", state.Url.String()) - - return Print(state) + return print(state) } func Next(state *BrowserState, conf *Config) error { @@ -136,8 +132,6 @@ func Next(state *BrowserState, conf *Config) error { u := state.Url.ResolveReference(state.Links[index].Target) - _, _ = fmt.Fprintf(os.Stdout, "Next: %s\n", u.String()) - return Navigate(state, u, index, conf) } @@ -157,8 +151,6 @@ func Previous(state *BrowserState, conf *Config) error { u := state.Url.ResolveReference(state.Links[index].Target) - _, _ = fmt.Fprintf(os.Stdout, "Previous: %s\n", u.String()) - return Navigate(state, u, index, conf) } @@ -182,8 +174,6 @@ func Root(state *BrowserState, tilde bool, conf *Config) error { u.Path = base } - _, _ = fmt.Fprintf(os.Stdout, "Root: %s\n", u.String()) - return Navigate(state, &u, -1, conf) } @@ -200,8 +190,6 @@ func Up(state *BrowserState, conf *Config) error { u.Path = u.Path[:strings.LastIndex(u.Path, "/")+1] - _, _ = fmt.Fprintf(os.Stdout, "Up: %s\n", u.String()) - return Navigate(state, &u, -1, conf) } @@ -211,8 +199,6 @@ func Go(state *BrowserState, dest string, conf *Config) error { return err } - _, _ = fmt.Fprintf(os.Stdout, "Go: %s\n", u.String()) - return Navigate(state, u, idx, conf) } @@ -288,7 +274,11 @@ func parseURL(str string, state *BrowserState, defaultScheme string) (*url.URL, return u, i, nil } -func Print(state *BrowserState) error { +func print(state *BrowserState) error { + if state.Quiet { + return nil + } + if state.Body == nil && state.Modal == nil { return ErrMustBeOnAPage } @@ -300,6 +290,13 @@ func Print(state *BrowserState) error { return err } +func Print(state *BrowserState) error { + q := state.Quiet + defer func() { state.Quiet = q }() + state.Quiet = false + return print(state) +} + func Links(state *BrowserState, conf *Config) error { if state.Links == nil { return ErrMustBeOnAPage @@ -389,8 +386,8 @@ func TourCmd(state *BrowserState, args []string, conf *Config) error { return TourAdd(state, conf, args[1:]) case "show": return TourShow(state) - case "set": - return TourSet(state, args[1]) + case "select": + return TourSelect(state, args[1]) case "next": return TourNext(state, conf) case "previous": @@ -419,23 +416,8 @@ func Pipe(state *BrowserState, cmdStr string) error { cmd := exec.Command(sh, "-c", cmdStr) cmd.Stdin = bytes.NewBuffer(state.Body) - r, w := io.Pipe() - cmd.Stdout = w - cmd.Stderr = w - - cmd.Start() - - wg := &sync.WaitGroup{} - wg.Add(1) - var copyErr error - go func() { - defer wg.Done() - _, copyErr = io.Copy(os.Stdout, r) - }() - - waitErr := cmd.Wait() - _ = w.Close() - wg.Wait() + cmd.Stdout = os.Stdout + cmd.Stderr = os.Stderr - return errors.Join(waitErr, copyErr) + return cmd.Run() } @@ -206,8 +206,8 @@ func parseTourArgs(line string) ([]string, error) { return fields, nil } case 's': - if strings.HasPrefix("set", fields[0]) { - fields[0] = "set" + if strings.HasPrefix("select", fields[0]) { + fields[0] = "select" if len(fields) == 1 { return append(fields, ""), nil } @@ -275,6 +275,8 @@ func RunCommand(conf *Config, cmd *Command, state *BrowserState) error { return Up(state, conf) case "go": return Go(state, cmd.Args[0], conf) + case "help": + return Help(state, cmd.Args[0]) case "pipe": return Pipe(state, cmd.Args[0]) case "print": @@ -13,11 +13,16 @@ import ( "github.com/BurntSushi/toml" ) -type Config struct { +type ConfigMain struct { DefaultScheme string `toml:"default_scheme"` SoftWrap int `toml:"soft_wrap"` DownloadFolder string `toml:"download_folder"` VimKeys bool `toml:"vim_keys"` + Quiet bool `toml:"quiet"` +} + +type Config struct { + ConfigMain `toml:"main"` } func getConfig() (*Config, error) { @@ -33,10 +38,13 @@ func getConfig() (*Config, error) { } c := Config{ - VimKeys: true, - DefaultScheme: "gemini", - SoftWrap: 80, - DownloadFolder: home, + ConfigMain{ + VimKeys: true, + DefaultScheme: "gemini", + SoftWrap: 80, + DownloadFolder: home, + Quiet: false, + }, } if _, err := toml.DecodeFile(path, &c); err != nil { return nil, err diff --git a/handlers.go b/handlers.go index 587af68..e68c5f5 100644 --- a/handlers.go +++ b/handlers.go @@ -125,7 +125,11 @@ func parseGemtextDoc(body []byte, softWrap int) (string, []Link, error) { Text: ll.Label(), Target: u, }) - if _, err := b.WriteString(fmt.Sprintf("[%d]%s %s\n", i, linkSpaces(i), ll.Label())); err != nil { + label := ll.Label() + if len(label) == 0 { + label = ll.URL() + } + if _, err := b.WriteString(fmt.Sprintf("[%d]%s %s\n", i, linkSpaces(i), label)); err != nil { return "", nil, err } i += 1 @@ -0,0 +1,271 @@ +package main + +import ( + "errors" + "fmt" + "os" +) + +var ErrNotAHelpTopic = errors.New("don't recognize that help topic") + +func Help(state *BrowserState, topic string) error { + if topic == "" { + topic = "topics" + } + + out, ok := helpTopics[topic] + if !ok { + return ErrNotAHelpTopic + } + + _, err := fmt.Fprintln(os.Stdout, out) + return err +} + +var helpTopics = map[string]string{ + "topics": ` +help topics +----------- +commands: Basics of x-1 commands, and a full listing of them. Each + command is also its own help topic. +urls: The forms of URLs which can be entered into x-1 commands. +mark: Information on the "mark" meta-command. +tour: Information about the "tour" meta-command. +config: The x-1 configuration file. +`[1:], + + "commands": ` +x-1 prompt commands +------------------- +root Root up +back forward +next previous +reload print pipe +help links history +tour mark +go save quit + +Any command may be written as any prefix sufficiently long to be unique. +So for instance, "reload" can be specified by just typing "re". This is +clarified in the help pages with a form that shows which characters are +required, such as "re[load]" or "q[uit]". + +The empty command (just hitting Enter at the prompt) is interpreted as +"print". + +Typing just any URL is interpreted as a "go" command to that URL. See +"help urls" for more information on forms of allowed URLs. + +Consult "help COMMAND" for more information on any single command. +`[1:], + + "urls": ` +x-1 urls +-------- +Commands which take URLs can have them specified in different forms: + * full absolute URLs with or without a scheme, + * relative URLs will be interpreted relative to the current page, + * "." always refers to the current page's URL, + * positive integers follow numbered links on the current page, + * "m:NAME" follows the bookmark with the given name (or identified + by a unique prefix of the name), + * "t:N" is the link in the current tour with number N, + * and "t[NAME]:N" is the link in the named tour (or again with the + given name prefix) at number N. + +The "tour add" command also supports URL ranges in a few forms: + * "*" refers to all the links in the current page, + * and "M-N" refers to the links numbered M through N (inclusive on + both ends) on the current page. +`[1:], + + "config": ` +x-1 config file +--------------- +The configuration file for x-1 is in TOML format and lives under +$XDG_CONFIG_HOME (or $HOME/.config) in "x-1/config.toml". + +The section "[main]" contains general configuration options: + * vim_keys (boolean): Whether to use vim keybindings for the readline + prompt. Defaults to true. + * default_scheme (string): The scheme to use in absolute URLs which + don't provide one. The default is "gemini". + * soft_wrap (int): The number of columns at which to wrap long lines. + Be aware that additional columns will be used on the left to display + link indices. + * download_folder (string): The folder in which to store files saved + by the "save" command. This string may also start with "~", which + stands in for $HOME. The default is "~" (or $HOME). + * quiet (boolean): Disables automatically printing the page after any + navigation action. The default is false. + +`[1:], + + "mark": ` +m[ark] +------ +Marks are URLs saved and associated with a name which can be used to +look them up again. Marks are preserved across x-1 sessions. + +The mark meta-command has multiple sub-commands which can be used to +manage and navigate to your saved marks. "m[ark] X" with any mark name +or unique prefix of a name can be used as "mark go". + +m[ark] a[dd] NAME URL: adds a new name/url pair to your saved marks. +m[ark] g[o] NAME: navigates to the named mark's URL. +m[ark] l[ist]: shows the list of marks and their URLs. +`[1:], + + "tour": ` +t[our] +------ +Tours are ordered lists of links. You can create a tour and save it +under a name in which case it will be preserved across x-1 sessions, or +there is always a "default" tour which is always active and empty at +startup. + +Tour is another meta-command with multiple sub-commands for managing and +navigating tours. "t[our]" alone with no sub-command implies "tour +next", and "t[our]" followed by one or more URLs behaves as "tour add". + +t[our] a[dd] URL...: add urls to the end of the current tour. +t[our] a[dd] n[ext] URL...: add urls to the next position in the tour. +t[our] n[ext]: visit the next link in the current tour. +t[our] p[revious]: visit the previous link in the current tour. +t[our] sh[ow]: display the links in the current tour. +t[our] c[lear]: empty out the current tour. +t[our] g[o] N: jump to integer position N in the current tour. +t[our] s[elect] [NAME]: make the named tour active (optionally named + by a unique prefix), or without a name, selects the default tour. +`[1:], + + "root": ` +r[oot] +------ +Navigates to the root of the current site. + +On most sites this will be the domain level, but if you are within a URL +path beginning with a tilde-name "/~name", it will navigate up to the +root of that tilde-name path. + +The "R[oot]" variant does the same thing but will ignore tilde paths. +`[1:], + + "Root": ` +R[oot] +------ +Navigate up to the root of the current domain. +`[1:], + + "up": ` +u[p] +---- +Navigates to the parent directory path of the current page's path. +`[1:], + + "back": ` +b[ack] +------ +Navigates to the previous page in the browser history. +`[1:], + + "forward": ` +f[orward] +--------- +Navigates to the next page in the browser history. +`[1:], + + "next": ` +n[ext] +------ +Navigates to the next link in the previous page. + +This command actually executes "back", then follows the link which comes +after the link that was previously used. + +It will fail if some other means of navigation was used to get to the +current page, such as "go" or a mark or tour. +`[1:], + + "previous": ` +pre[vious] +---------- +Navigates to the previous link on the previous page. + +This command executes "back" and then follows the link which comes +before the one that was previously used to get to the current page. + +It will fail if some other means of navigation was used to get here, +such as "go" or a mark or tour. +`[1:], + + "reload": ` +re[load] +-------- +Re-requests and displays the current page. +`[1:], + + "print": ` +p[rint] +------- +Displays the current page. + +This will happen anyway by default with any navigation action, but the +"quiet" configuration option can disable that. +`[1:], + + "pipe": ` +pi[pe] CMD +---------- +Run a shell command, sending the current page into its standard input. + +The cmd may contain spaces and will be run in a shell with +"sh -c 'CMD'". +`[1:], + + "help": ` +h[elp] [TOPIC] +-------------- +Display help text. + +Without TOPIC, it will display "help topics" which lists some useful +starting points. +`[1:], + + "links": ` +l[inks] +------- +Shortens the current page by only displaying the links in it. +`[1:], + + "history": ` +h[istory] +--------- +Displays the current history stack. + +Navigate across it with "back" and "forward" commands. +`[1:], + + "go": ` +g[o] URL +-------- +Navigates to any URL. + +See "help urls" for more on the forms supported. +`[1:], + + "save": ` +s[ave] FILENAME +--------------- +Saves the current page under a given filename. + +The directory can be controlled with the "download_folder" option in the +config file, but defaults to the user's home directory. +`[1:], + + "quit": ` +q[uit] +------ +Quits x-1 immediately. +`[1:], +} @@ -16,6 +16,7 @@ func main() { } state := NewBrowserState() + state.Quiet = conf.Quiet rl, err := readline.New(Prompt) if err != nil { @@ -17,6 +17,8 @@ type BrowserState struct { DefaultTour Tour CurrentTour *Tour + Quiet bool + Readline *readline.Instance } @@ -179,7 +179,7 @@ func TourGo(state *BrowserState, conf *Config, pos string) error { return Navigate(state, tour.Links[i], -1, conf) } -func TourSet(state *BrowserState, name string) error { +func TourSelect(state *BrowserState, name string) error { tour, err := findTour(state, name) if err == nil { state.CurrentTour = tour |
