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  }