summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--README.gmi51
-rw-r--r--actions.go58
-rw-r--r--command.go6
-rw-r--r--files.go18
-rw-r--r--handlers.go6
-rw-r--r--help.go271
-rw-r--r--main.go1
-rw-r--r--state.go2
-rw-r--r--tour.go2
9 files changed, 343 insertions, 72 deletions
diff --git a/README.gmi b/README.gmi
index 0264565..3c160c8 100644
--- a/README.gmi
+++ b/README.gmi
@@ -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
diff --git a/actions.go b/actions.go
index d9a5d06..afb8289 100644
--- a/actions.go
+++ b/actions.go
@@ -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()
}
diff --git a/command.go b/command.go
index 46390da..5862a57 100644
--- a/command.go
+++ b/command.go
@@ -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":
diff --git a/files.go b/files.go
index f818ddb..842a030 100644
--- a/files.go
+++ b/files.go
@@ -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
diff --git a/help.go b/help.go
new file mode 100644
index 0000000..3de618b
--- /dev/null
+++ b/help.go
@@ -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:],
+}
diff --git a/main.go b/main.go
index d2740e1..2916be7 100644
--- a/main.go
+++ b/main.go
@@ -16,6 +16,7 @@ func main() {
}
state := NewBrowserState()
+ state.Quiet = conf.Quiet
rl, err := readline.New(Prompt)
if err != nil {
diff --git a/state.go b/state.go
index 1af2503..1ec000d 100644
--- a/state.go
+++ b/state.go
@@ -17,6 +17,8 @@ type BrowserState struct {
DefaultTour Tour
CurrentTour *Tour
+ Quiet bool
+
Readline *readline.Instance
}
diff --git a/tour.go b/tour.go
index d74bef4..328af5e 100644
--- a/tour.go
+++ b/tour.go
@@ -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