diff --git a/.gitignore b/.gitignore index cb9380e..6f40a17 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ bombadillo *.asciinema +*.swp diff --git a/DEVELOPING.md b/DEVELOPING.md index 4b05842..6ecdc8b 100644 --- a/DEVELOPING.md +++ b/DEVELOPING.md @@ -25,10 +25,10 @@ Changes are implemented to the default branch when: ### Process for introducing a new change -Please refer to our [notes on contributing](README.md#contributing) to get an understanding of how new changes are initiated, the type of changes accepted and the review process. +Before you begin, please refer to our [notes on contributing](README.md#contributing) to get an understanding of how new changes are initiated, the type of changes accepted and the review process. 1. Create a new feature branch based on the **develop** branch. -1. Raise a pull request (PR) targeting the **develop** branch. +1. Raise a pull request (PR) targeting the current release branch (confirm this in the issue comments before proceeding). 1. The PR is reviewed. 1. If the PR is approved, it is merged. 1. The version number is incremented, along with any other release activity. diff --git a/client.go b/client.go index 1765951..2617247 100644 --- a/client.go +++ b/client.go @@ -344,23 +344,26 @@ func (c *client) simpleCommand(action string) { case "SEARCH": c.search("", "", "?") case "HELP", "?": - go c.Visit(helplocation) + c.Visit(helplocation) default: - c.SetMessage(fmt.Sprintf("Unknown action %q", action), true) + c.SetMessage(syntaxErrorMessage(action), true) c.DrawMessage() } } func (c *client) doCommand(action string, values []string) { - if length := len(values); length != 1 { - c.SetMessage(fmt.Sprintf("Expected 1 argument, received %d", len(values)), true) - c.DrawMessage() - return - } - switch action { - case "CHECK", "C": + case "C", "CHECK": c.displayConfigValue(values[0]) + c.DrawMessage() + case "HELP", "?": + if val, ok := ERRS[values[0]]; ok { + c.SetMessage("Usage: " + val, false) + } else { + msg := fmt.Sprintf("%q is not a valid command; help syntax: %s", values[0], ERRS[action]) + c.SetMessage(msg, false) + } + c.DrawMessage() case "PURGE", "P": err := c.Certs.Purge(values[0]) if err != nil { @@ -405,24 +408,22 @@ func (c *client) doCommand(action string, values []string) { c.saveFile(u, fn) default: - c.SetMessage(fmt.Sprintf("Unknown action %q", action), true) + c.SetMessage(syntaxErrorMessage(action), true) c.DrawMessage() } } func (c *client) doCommandAs(action string, values []string) { - if len(values) < 2 { - c.SetMessage(fmt.Sprintf("Expected 2+ arguments, received %d", len(values)), true) - c.DrawMessage() - return - } - - if values[0] == "." { - values[0] = c.PageState.History[c.PageState.Position].Location.Full - } - switch action { case "ADD", "A": + if len(values) < 2 { + c.SetMessage(syntaxErrorMessage(action), true) + c.DrawMessage() + return + } + if values[0] == "." { + values[0] = c.PageState.History[c.PageState.Position].Location.Full + } msg, err := c.BookMarks.Add(values) if err != nil { c.SetMessage(err.Error(), true) @@ -441,8 +442,18 @@ func (c *client) doCommandAs(action string, values []string) { c.Draw() } case "SEARCH": + if len(values) < 2 { + c.SetMessage(syntaxErrorMessage(action), true) + c.DrawMessage() + return + } c.search(strings.Join(values, " "), "", "") case "SET", "S": + if len(values) < 2 { + c.SetMessage(syntaxErrorMessage(action), true) + c.DrawMessage() + return + } if _, ok := c.Options[values[0]]; ok { val := strings.Join(values[1:], " ") if !validateOpt(values[0], val) { @@ -473,7 +484,7 @@ func (c *client) doCommandAs(action string, values []string) { c.SetMessage(fmt.Sprintf("Unable to set %s, it does not exist", values[0]), true) c.DrawMessage() default: - c.SetMessage(fmt.Sprintf("Unknown command structure"), true) + c.SetMessage(syntaxErrorMessage(action), true) c.DrawMessage() } } @@ -523,7 +534,7 @@ func (c *client) doLinkCommandAs(action, target string, values []string) { out = append(out, values...) c.doCommandAs(action, out) default: - c.SetMessage(fmt.Sprintf("Unknown command structure"), true) + c.SetMessage(syntaxErrorMessage(action), true) c.DrawMessage() } } @@ -655,7 +666,7 @@ func (c *client) doLinkCommand(action, target string) { } c.saveFile(u, fn) default: - c.SetMessage("Unknown command structure", true) + c.SetMessage(syntaxErrorMessage(action), true) c.DrawMessage() } @@ -977,6 +988,7 @@ func (c *client) handleGemini(u Url) { case 2: // Success if capsule.MimeMaj == "text" || (c.Options["showimages"] == "true" && capsule.MimeMaj == "image") { + u.Mime = capsule.MimeMin pg := MakePage(u, capsule.Content, capsule.Links) pg.FileType = capsule.MimeMaj pg.WrapContent(c.Width-1, (c.Options["theme"] == "color")) @@ -1203,6 +1215,13 @@ func findAvailableFileName(fpath, fname string) (string, error) { return savePath, nil } +func syntaxErrorMessage(action string) string { + if val, ok := ERRS[action]; ok { + return fmt.Sprintf("Incorrect syntax. Try: %s", val) + } + return fmt.Sprintf("Unknown command %q", action) +} + func updateTimeouts(timeoutString string) error { sec, err := strconv.Atoi(timeoutString) if err != nil { diff --git a/cmdparse/parser.go b/cmdparse/parser.go index dc5870a..39e08ee 100644 --- a/cmdparse/parser.go +++ b/cmdparse/parser.go @@ -94,10 +94,10 @@ func (p *Parser) parseAction() (*Command, error) { case Value: cm.Target = t.val cm.Type = DOLINK - case Word: + case Word, Action: cm.Value = append(cm.Value, t.val) cm.Type = DO - case Action, Whitespace: + case Whitespace: return nil, fmt.Errorf("Found %q (%d), expected value", t.val, t.kind) } t = p.scan() diff --git a/cui/cui.go b/cui/cui.go index c3ee14a..1ed29d7 100644 --- a/cui/cui.go +++ b/cui/cui.go @@ -102,6 +102,7 @@ func Getch() rune { func GetLine(prefix string) (string, error) { termios.SetLineMode() + defer termios.SetCharMode() reader := bufio.NewReader(os.Stdin) fmt.Print(prefix) @@ -110,7 +111,6 @@ func GetLine(prefix string) (string, error) { return "", err } - termios.SetCharMode() return text[:len(text)-1], nil } diff --git a/gemini/gemini.go b/gemini/gemini.go index db02ca5..526aca6 100644 --- a/gemini/gemini.go +++ b/gemini/gemini.go @@ -361,6 +361,7 @@ func parseGemini(b, currentUrl string) (string, []string) { links := make([]string, 0, 10) inPreBlock := false + spacer := " " outputIndex := 0 for i, ln := range splitContent { @@ -371,7 +372,7 @@ func parseGemini(b, currentUrl string) (string, []string) { alt := strings.TrimSpace(ln) if len(alt) > 3 { alt = strings.TrimSpace(alt[3:]) - splitContent[outputIndex] = fmt.Sprintf("[ %s ]", alt) + splitContent[outputIndex] = fmt.Sprintf("%s[ALT][ %s ]", spacer, alt) outputIndex++ } } else if isPreBlockDeclaration { @@ -401,7 +402,12 @@ func parseGemini(b, currentUrl string) (string, []string) { if inPreBlock && (BlockBehavior == "alt" || BlockBehavior == "neither") { continue } - splitContent[outputIndex] = ln + var leader, tail string = "", "" + if len(ln) > 0 && ln[0] == '#' { + leader = "\033[1m" + tail = "\033[0m" + } + splitContent[outputIndex] = fmt.Sprintf("%s%s%s%s", spacer, leader, ln, tail) outputIndex++ } } diff --git a/gopher/gopher.go b/gopher/gopher.go index fbe579f..adf36e9 100644 --- a/gopher/gopher.go +++ b/gopher/gopher.go @@ -144,7 +144,8 @@ func parseMap(text string) (string, []string) { } else { link := buildLink(line[2], line[3], string(line[0][0]), line[1]) links = append(links, link) - linktext := fmt.Sprintf("(%s) %2d %s", getType(string(line[0][0])), len(links), title) + linkNum := fmt.Sprintf("[%d]",len(links)) + linktext := fmt.Sprintf("%s %5s %s", getType(string(line[0][0])), linkNum, title) splitContent[i] = linktext } } diff --git a/help.go b/help.go new file mode 100644 index 0000000..ece82db --- /dev/null +++ b/help.go @@ -0,0 +1,28 @@ +package main + +// ERRS maps commands to their syntax error message +var ERRS = map[string]string{ + "A": "`a [target] [name...]`", + "ADD": "`add [target] [name...]`", + "D": "`d [bookmark-id]`", + "DELETE": "`delete [bookmark-id]`", + "B": "`b [[bookmark-id]]`", + "BOOKMARKS": "`bookmarks [[bookmark-id]]`", + "C": "`c [link_id]` or `c [setting]`", + "CHECK": "`check [link_id]` or `check [setting]`", + "H": "`h`", + "HOME": "`home`", + "P": "`p [host]`", + "PURGE": "`purge [host]`", + "Q": "`q`", + "QUIT": "`quit`", + "R": "`r`", + "RELOAD": "`reload`", + "SEARCH": "`search [[keyword(s)...]]`", + "S": "`s [setting] [value]`", + "SET": "`set [setting] [value]`", + "W": "`w [target]`", + "WRITE": "`write [target]`", + "?": "`? [[command]]`", + "HELP": "`help [[command]]`", +} diff --git a/http/http_render.go b/http/http_render.go index f68ee2d..58a8813 100644 --- a/http/http_render.go +++ b/http/http_render.go @@ -33,7 +33,7 @@ func Visit(webmode, url string, width int) (Page, error) { return Page{}, fmt.Errorf("Invalid webmode setting") } c, err := exec.Command(webmode, "-dump", w, fmt.Sprintf("%d", width), url).Output() - if err != nil { + if err != nil && c == nil { return Page{}, err } return parseLinks(string(c)), nil diff --git a/page.go b/page.go index 890f018..ac31168 100644 --- a/page.go +++ b/page.go @@ -72,11 +72,17 @@ func (p *Page) WrapContent(width int, color bool) { } width = min(width, 100) counter := 0 - spacer := " " + spacer := "" var content strings.Builder var esc strings.Builder escape := false content.Grow(len(p.RawContent)) + + if p.Location.Mime == "1" { // gopher document + spacer = " " + } else if strings.HasSuffix(p.Location.Mime, "gemini") { //gemini document + spacer = " " + } for _, ch := range []rune(p.RawContent) { if escape { if color { @@ -125,10 +131,8 @@ func (p *Page) WrapContent(width int, color bool) { } else { content.WriteRune('\n') counter = 0 - if p.Location.Mime == "1" { - content.WriteString(spacer) - counter += len(spacer) - } + content.WriteString(spacer) + counter += len(spacer) content.WriteRune(ch) counter++ }