12// Choose how to write the function name in the file.
14// e.g. test functions can be long and have multiple parts, so we just
15// pull out the most meaningful part.
16func chooseDisplayName(functionName string) string {
17 parts := strings.Split(functionName, ".")
19 // If this is an anonymous function, the name will be something like
20 // "func1", which is unhelpful.
22 // Throw away that part and get the next part.
23 if m, _ := regexp.MatchString("^func[0-9]+$", parts[len(parts)-1]); len(parts) > 1 && m {
25 return parts[len(parts)-2]
28 return parts[len(parts)-1]
31func getFunctionName() string {
32 pc, _, _, ok := runtime.Caller(2)
37 fn := runtime.FuncForPC(pc)
42 return chooseDisplayName(fn.Name())
45func getExpression() string {
46 _, file, line, ok := runtime.Caller(2)
51 f, err := os.Open(file)
57 scanner := bufio.NewScanner(f)
61 if currentLine == line {
62 thisLine := strings.TrimSpace(scanner.Text())
63 thisLine, _ = strings.CutPrefix(thisLine, "q.Q(")
64 thisLine, _ = strings.CutSuffix(thisLine, ")")
70 if err := scanner.Err(); err != nil {
77func toString(value any, a ...any) string {
78 switch v := value.(type) {
81 return fmt.Sprintf("%q", v)
83 v = strings.ReplaceAll(v, "%+v", "\x1b[39m%+v\x1b[39m")
84 v = strings.ReplaceAll(v, "%v", "\x1b[39m%v\x1b[39m")
85 v = strings.ReplaceAll(v, "%t", "\x1b[39m%t\x1b[39m")
86 return fmt.Sprintf(v, a...)
88 case int, int8, int16, int32, int64,
89 uint, uint8, uint16, uint32, uint64,
90 float32, float64, bool:
91 return fmt.Sprintf("%v", v)
95 return fmt.Sprintf("%+v", v) // fallback
99func Q(value any, a ...any) {
100 f, err := os.OpenFile("/tmp/q.txt", os.O_APPEND|os.O_WRONLY|os.O_CREATE, 0600)
106 functionName := getFunctionName()
107 expression := getExpression()
111 if expression[0] == '"' && expression[len(expression)-1] == '"' {
112 line = "\x1b[32m" + functionName + "\x1b[39m: " + toString(value, a...) + "\n\n"
114 line = "\x1b[32m" + functionName + "\x1b[39m: " + expression + " = \x1b[36m" + toString(value, a...) + "\x1b[39m\n\n"
117 if _, err = f.WriteString(line); err != nil {