background code checker for golang
at main 3.8 kB view raw
1package gust 2 3import ( 4 "bufio" 5 "regexp" 6 "strings" 7) 8 9var ( 10 errorWithColumn = regexp.MustCompile(`(?:\S+: )?([^:]+):(\d+):(\d+): (.+)`) 11 errorWithoutColumn = regexp.MustCompile(`(?:\S+: )?([^:]+):(\d+): (.+)`) 12 cantLoadPackage = regexp.MustCompile(`can't load package: (.+)`) 13 continuation = regexp.MustCompile(`^\s+(.+)`) 14) 15 16type CompilerMessage struct { 17 Type string // "error" or "warning" 18 Location Location 19 Message string // error message 20 Raw string // raw message 21 Priority int // for sorting 22 Context Lines 23} 24 25type Location struct { 26 File string 27 Line string 28 Column string 29} 30 31func Parse(output string) ([]CompilerMessage, []string) { 32 var messages []CompilerMessage 33 var currentMultilineMsg *CompilerMessage 34 var extra []string 35 36 scanner := bufio.NewScanner(strings.NewReader(output)) 37 for scanner.Scan() { 38 line := scanner.Text() 39 40 if strings.TrimSpace(line) == "" { 41 continue 42 } 43 44 if strings.HasPrefix(line, "#") { 45 extra = append(extra, line) 46 continue 47 } 48 49 if strings.Contains(line, "panic:") { 50 extra = append(extra, line) 51 continue 52 } 53 54 if strings.HasPrefix(line, "go:") { 55 msg := CompilerMessage{ 56 Type: "error", 57 Location: Location{}, 58 Message: strings.TrimPrefix(line, "go: "), 59 Raw: line, 60 Priority: 999, 61 } 62 63 messages = append(messages, msg) 64 } 65 66 if currentMultilineMsg != nil && continuation.MatchString(line) { 67 matches := continuation.FindStringSubmatch(line) 68 if len(matches) >= 2 { 69 currentMultilineMsg.Message += "\n" + matches[1] 70 continue 71 } 72 } 73 74 currentMultilineMsg = nil 75 76 if matches := errorWithColumn.FindStringSubmatch(line); len(matches) >= 5 { 77 msg := CompilerMessage{ 78 Type: determineType(matches[4]), 79 Location: Location{ 80 File: matches[1], 81 Line: matches[2], 82 Column: matches[3], 83 }, 84 Message: matches[4], 85 Raw: line, 86 Priority: priorityFor(matches[4]), 87 } 88 89 messages = append(messages, msg) 90 currentMultilineMsg = &messages[len(messages)-1] 91 continue 92 } 93 94 if matches := errorWithoutColumn.FindStringSubmatch(line); len(matches) >= 4 { 95 msg := CompilerMessage{ 96 Type: determineType(matches[3]), 97 Location: Location{ 98 File: matches[1], 99 Line: matches[2], 100 }, 101 Message: matches[3], 102 Raw: line, 103 Priority: priorityFor(matches[3]), 104 } 105 106 messages = append(messages, msg) 107 currentMultilineMsg = &messages[len(messages)-1] 108 continue 109 } 110 111 if matches := cantLoadPackage.FindStringSubmatch(line); len(matches) >= 2 { 112 msg := CompilerMessage{ 113 Type: "error", 114 Message: "can't load package: " + matches[1], 115 Raw: line, 116 Priority: 1, // High priority error 117 } 118 119 messages = append(messages, msg) 120 currentMultilineMsg = &messages[len(messages)-1] 121 continue 122 } 123 124 if strings.Contains(line, "error") || strings.Contains(line, "failed to build") { 125 msg := CompilerMessage{ 126 Type: "error", 127 Message: line, 128 Raw: line, 129 Priority: 10, // Lower priority for generic errors 130 } 131 132 messages = append(messages, msg) 133 currentMultilineMsg = &messages[len(messages)-1] 134 } 135 } 136 137 return messages, extra 138} 139 140func determineType(message string) string { 141 lowercase := strings.ToLower(message) 142 if strings.Contains(lowercase, "error") { 143 return "error" 144 } else if strings.Contains(lowercase, "warning") || strings.Contains(lowercase, "vet") { 145 return "warning" 146 } 147 return "error" 148} 149 150func priorityFor(message string) int { 151 lowercase := strings.ToLower(message) 152 if strings.Contains(lowercase, "warning") { 153 return 0 // Warnings first 154 } else if strings.Contains(lowercase, "syntax error") { 155 return 1 // Syntax errors next 156 } else if strings.Contains(lowercase, "undefined") { 157 return 2 // Undefined references next 158 } else { 159 return 5 // Other errors 160 } 161}