github.phpd.cn/thought-machine/please@v12.2.0+incompatible/src/help/help.go (about) 1 // +build !bootstrap 2 3 // Package help prints help messages about parts of plz. 4 package help 5 6 import ( 7 "encoding/json" 8 "fmt" 9 "os" 10 "regexp" 11 "sort" 12 "strings" 13 14 "gopkg.in/op/go-logging.v1" 15 16 "cli" 17 "utils" 18 ) 19 20 var log = logging.MustGetLogger("help") 21 22 const topicsHelpMessage = ` 23 The following help topics are available: 24 25 %s` 26 27 // maxSuggestionDistance is the maximum Levenshtein edit distance we'll suggest help topics at. 28 const maxSuggestionDistance = 4 29 30 var backtickRegex = regexp.MustCompile("\\`[^\\`\n]+\\`") 31 32 // Help prints help on a particular topic. 33 // It returns true if the topic is known or false if it isn't. 34 func Help(topic string) bool { 35 if message := help(topic); message != "" { 36 printMessage(message) 37 return true 38 } 39 fmt.Printf("Sorry OP, can't halp you with %s\n", topic) 40 if message := suggest(topic); message != "" { 41 printMessage(message) 42 fmt.Printf(" Or have a look on the website: https://please.build\n") 43 } else { 44 fmt.Printf("\nMaybe have a look on the website? https://please.build\n") 45 } 46 return false 47 } 48 49 // Topics prints the list of help topics beginning with the given prefix. 50 func Topics(prefix string) { 51 for _, topic := range allTopics() { 52 if strings.HasPrefix(topic, prefix) { 53 fmt.Println(topic) 54 } 55 } 56 } 57 58 func help(topic string) string { 59 topic = strings.ToLower(topic) 60 if topic == "topics" { 61 return fmt.Sprintf(topicsHelpMessage, strings.Join(allTopics(), "\n")) 62 } 63 for _, filename := range AssetNames() { 64 if message, found := findHelpFromFile(topic, filename); found { 65 return message 66 } 67 } 68 return "" 69 } 70 71 func findHelpFromFile(topic, filename string) (string, bool) { 72 preamble, topics := loadData(filename) 73 message, found := topics[topic] 74 if !found { 75 return "", false 76 } 77 if preamble == "" { 78 return message, true 79 } 80 return fmt.Sprintf(preamble+"\n\n", topic) + message, true 81 } 82 83 func loadData(filename string) (string, map[string]string) { 84 data := MustAsset(filename) 85 f := helpFile{} 86 if err := json.Unmarshal(data, &f); err != nil { 87 log.Fatalf("Failed to load help data: %s\n", err) 88 } 89 return f.Preamble, f.Topics 90 } 91 92 // suggest looks through all known help topics and tries to make a suggestion about what the user might have meant. 93 func suggest(topic string) string { 94 return utils.PrettyPrintSuggestion(topic, allTopics(), maxSuggestionDistance) 95 } 96 97 // allTopics returns all the possible topics to get help on. 98 func allTopics() []string { 99 topics := []string{} 100 for _, filename := range AssetNames() { 101 _, data := loadData(filename) 102 for t := range data { 103 topics = append(topics, t) 104 } 105 } 106 sort.Strings(topics) 107 return topics 108 } 109 110 // helpFile is a struct we use for unmarshalling. 111 type helpFile struct { 112 Preamble string `json:"preamble"` 113 Topics map[string]string `json:"topics"` 114 } 115 116 // printMessage prints a message, with some string replacements for ANSI codes. 117 func printMessage(msg string) { 118 if cli.StdErrIsATerminal && cli.StdOutIsATerminal { 119 msg = backtickRegex.ReplaceAllStringFunc(msg, func(s string) string { 120 return "${BOLD_CYAN}" + strings.Replace(s, "`", "", -1) + "${RESET}" 121 }) 122 } 123 // Replace % to %% when not followed by anything so it doesn't become a replacement. 124 cli.Fprintf(os.Stdout, strings.Replace(msg, "% ", "%% ", -1)) 125 }