diff --git a/output/main.go b/output/main.go
index d91b72a..bb880ca 100644
--- a/output/main.go
+++ b/output/main.go
@@ -4 +4 @@ import (
- "bytes"
+ "encoding/csv"
@@ -6 +5,0 @@ import (
- "encoding/xml"
@@ -8 +7,4 @@ import (
- "reflect"
+ "logc/parser"
+ "os"
+ "regexp"
+ "sort"
@@ -10 +11,0 @@ import (
- "text/template"
@@ -13,11 +14,12 @@ import (
-// TextEncoder converts a struct to plain text based on struct tags, handling nested structs with indentation.
-func Text(v interface{}) string {
- var result strings.Builder
- val := reflect.ValueOf(v)
- typ := val.Type()
-
- // Handle pointer types
- if val.Kind() == reflect.Ptr {
- val = val.Elem()
- typ = val.Type()
- }
+func limitLines(lines []string, length int) []string {
+ var result []string
+ for _, line := range lines {
+ for len(line) > length {
+ // Find the last space within the allowed length
+ cut := length
+ for i := length; i > 0; i-- {
+ if line[i] == ' ' {
+ cut = i
+ break
+ }
+ }
@@ -25 +27,10 @@ func Text(v interface{}) string {
- processValue(val, typ, 0, &result)
+ // If there's no space within the limit, break at the exact length
+ if cut == length {
+ result = append(result, line[:cut])
+ line = line[cut:]
+ } else {
+ // Otherwise, break at the last space and remove it
+ result = append(result, line[:cut])
+ line = line[cut+1:] // Skip the space
+ }
+ }
@@ -27 +38,9 @@ func Text(v interface{}) string {
- return result.String()
+ // Append any remaining text
+ if len(line) > 0 {
+ result = append(result, line)
+ }
+ }
+ for i, v := range result {
+ result[i] = strings.TrimSpace(v)
+ }
+ return result
@@ -30,2 +49,3 @@ func Text(v interface{}) string {
-func processValue(val reflect.Value, typ reflect.Type, indentLevel int, result *strings.Builder) {
- indent := strings.Repeat("\t", indentLevel)
+// Group the comments by their tags and format the output with colors
+func FormatText(comments []parser.Comment, lineLength int) string {
+ grouped := make(map[string][]parser.Comment)
@@ -33,5 +53,4 @@ func processValue(val reflect.Value, typ reflect.Type, indentLevel int, result *
- switch val.Kind() {
- case reflect.Struct:
- for i := 0; i < val.NumField(); i++ {
- field := val.Field(i)
- fieldType := typ.Field(i)
+ // Group comments by tag
+ for _, comment := range comments {
+ grouped[comment.Headers[0]] = append(grouped[comment.Headers[0]], comment)
+ }
@@ -39,5 +58,4 @@ func processValue(val reflect.Value, typ reflect.Type, indentLevel int, result *
- // Get the 'text' tag, fallback to 'xml' tag if not present
- tag := fieldType.Tag.Get("text")
- if tag == "" {
- tag = fieldType.Tag.Get("xml")
- }
+ groupedSorted := []string{}
+ for h := range grouped {
+ groupedSorted = append(groupedSorted, h)
+ }
@@ -45,4 +63 @@ func processValue(val reflect.Value, typ reflect.Type, indentLevel int, result *
- // Skip if tag is "-"
- if tag == "-" {
- continue
- }
+ sort.Strings(groupedSorted)
@@ -50,4 +65,14 @@ func processValue(val reflect.Value, typ reflect.Type, indentLevel int, result *
- // Use field name if no tag
- fieldName := fieldType.Name
- if tag == "" {
- tag = fieldName
+ // Format the grouped comments for display
+ var output strings.Builder
+ for _, tag := range groupedSorted {
+ tagComments := grouped[tag]
+ output.WriteString(fmt.Sprintf("%s\n", tag)) // Tag with customizable color
+ output.WriteString(strings.Repeat("-", len(tag)+5) + "\n")
+ for _, comment := range tagComments {
+ output.WriteString(fmt.Sprintf("\tFile: %s:%d\n", comment.File, comment.Line)) // File path with customizable color
+ output.WriteString(fmt.Sprintf("\t\t%s\n", strings.Join(limitLines(strings.Split(comment.Data[0], "\n"), lineLength-2), "\n\t\t")))
+
+ // !MAN: This is a test
+ for i := 1; i < len(comment.Headers); i++ {
+ output.WriteString(fmt.Sprintf("\t\t%s\n", comment.Headers[i])) // File path with customizable color
+ output.WriteString(fmt.Sprintf("\t\t\t%s\n\n", strings.Join(limitLines(strings.Split(comment.Data[i], "\n"), lineLength-3), "\n\t\t")))
@@ -54,0 +80,6 @@ func processValue(val reflect.Value, typ reflect.Type, indentLevel int, result *
+ output.WriteString("\n")
+ // output.WriteString(fmt.Sprintf("\t%s\n", comment.Capture))
+ }
+ }
+ return output.String()
+}
@@ -56,21 +87,10 @@ func processValue(val reflect.Value, typ reflect.Type, indentLevel int, result *
- currentTag := tag
-
- // Handle slices
- if field.Kind() == reflect.Slice {
- for j := 0; j < field.Len(); j++ {
- item := field.Index(j)
- itemType := item.Type()
-
- if item.Kind() == reflect.Struct || (item.Kind() == reflect.Ptr && item.Elem().Kind() == reflect.Struct) {
- // Nested struct or pointer to struct in slice
- if strings.Contains(tag, "omitempty") && isEmptyValue(item) {
- continue
- } else {
- currentTagWithoutOmit := strings.ReplaceAll(currentTag, ",omitempty", "")
- result.WriteString(fmt.Sprintf("%s%s:\n", indent, currentTagWithoutOmit))
- if item.Kind() == reflect.Ptr {
- processValue(item.Elem(), itemType.Elem(), indentLevel+1, result)
- } else {
- processValue(item, itemType, indentLevel+1, result)
- }
- }
+// !ISSUE: Remove []
+func GetHeaders(comments []parser.Comment) []string {
+ headerMap := map[string]int{}
+ for _, v := range comments {
+ for _, h := range v.Headers {
+ if h[0] == '[' {
+ tags := strings.Split(h[1:strings.Index(h, "]")], ",")
+ for _, t := range tags {
+ if t[0] == '!' {
+ headerMap[t[1:]] = 0
@@ -78,22 +98 @@ func processValue(val reflect.Value, typ reflect.Type, indentLevel int, result *
- lines := strings.TrimSpace(fmt.Sprintf("%v", item.Interface()))
-
- if strings.Contains(tag, "omitempty") && lines == "" {
- continue
- } else {
- currentTagWithoutOmit := strings.ReplaceAll(currentTag, ",omitempty", "")
- result.WriteString(fmt.Sprintf("%s%s: %v\n", indent, currentTagWithoutOmit, lines))
- }
- }
- }
- } else if field.Kind() == reflect.Struct || (field.Kind() == reflect.Ptr && field.Elem().Kind() == reflect.Struct) {
- // Nested struct or pointer to struct
- if strings.Contains(tag, "omitempty") && isEmptyValue(field) {
- continue
- } else {
- currentTagWithoutOmit := strings.ReplaceAll(currentTag, ",omitempty", "")
- result.WriteString(fmt.Sprintf("%s%s:\n", indent, currentTagWithoutOmit))
- fieldTypeToPass := fieldType.Type
- fieldValToPass := field
- if field.Kind() == reflect.Ptr {
- fieldTypeToPass = fieldType.Type.Elem()
- fieldValToPass = field.Elem()
+ headerMap[t] = 0
@@ -101 +99,0 @@ func processValue(val reflect.Value, typ reflect.Type, indentLevel int, result *
- processValue(fieldValToPass, fieldTypeToPass, indentLevel+1, result)
@@ -104,9 +102 @@ func processValue(val reflect.Value, typ reflect.Type, indentLevel int, result *
- // Basic type
- lines := strings.TrimSpace(fmt.Sprintf("%v", field.Interface()))
-
- if strings.Contains(tag, "omitempty") && lines == "" {
- continue
- } else {
- currentTagWithoutOmit := strings.ReplaceAll(currentTag, ",omitempty", "")
- result.WriteString(fmt.Sprintf("%s%s: %v\n", indent, currentTagWithoutOmit, lines))
- }
+ headerMap[h] = 0
@@ -113,0 +104 @@ func processValue(val reflect.Value, typ reflect.Type, indentLevel int, result *
+
@@ -115,2 +105,0 @@ func processValue(val reflect.Value, typ reflect.Type, indentLevel int, result *
- default:
- result.WriteString(fmt.Sprintf("%v", val.Interface()))
@@ -117,0 +107,20 @@ func processValue(val reflect.Value, typ reflect.Type, indentLevel int, result *
+ headers := []string{}
+ for k := range headerMap {
+ headers = append(headers, k)
+ }
+ return headers
+}
+
+// Output function for text
+func Text(comments []parser.Comment) {
+ fmt.Println(FormatText(comments, 90))
+}
+
+// Output function for JSON
+func JSON(comments []parser.Comment) {
+ data, err := json.MarshalIndent(comments, "", " ")
+ if err != nil {
+ fmt.Println("Error generating JSON:", err)
+ return
+ }
+ fmt.Println(string(data))
@@ -120,18 +129,31 @@ func processValue(val reflect.Value, typ reflect.Type, indentLevel int, result *
-func isEmptyValue(v reflect.Value) bool {
- switch v.Kind() {
- case reflect.Array, reflect.Map, reflect.Slice, reflect.String:
- return v.Len() == 0
- case reflect.Bool:
- return !v.Bool()
- case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
- return v.Int() == 0
- case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
- return v.Uint() == 0
- case reflect.Float32, reflect.Float64:
- return v.Float() == 0
- case reflect.Interface, reflect.Ptr:
- return v.IsNil()
- case reflect.Struct:
- for i := 0; i < v.NumField(); i++ {
- if !isEmptyValue(v.Field(i)) {
- return false
+// !TODO: Add markdown Output
+func HTML(comments []parser.Comment) string {
+ grouped := make(map[string][]parser.Comment)
+
+ // Group comments by tag
+ for _, comment := range comments {
+ grouped[comment.Headers[0]] = append(grouped[comment.Headers[0]], comment)
+ }
+
+ groupedSorted := []string{}
+ for h := range grouped {
+ groupedSorted = append(groupedSorted, h)
+ }
+
+ sort.Strings(groupedSorted)
+
+ // Format the grouped comments for display
+ var output strings.Builder
+ for _, tag := range groupedSorted {
+ tagComments := grouped[tag]
+ output.WriteString(fmt.Sprintf("%s
\n", tag))
+ for _, comment := range tagComments {
+ output.WriteString(fmt.Sprintf("File: %s:%d
\n", comment.File, comment.Line)) // File path with customizable color
+ output.WriteString(fmt.Sprintf("%s
\n", strings.ReplaceAll(comment.Data[0], "\n", "")))
+
+ // !MAN: This is a test
+ output.WriteString("")
+
+ for i := 1; i < len(comment.Headers); i++ {
+ output.WriteString(fmt.Sprintf("- \n%s\n", comment.Headers[i])) // File path with customizable color
+ output.WriteString(fmt.Sprintf("%s\n
\n\n", strings.ReplaceAll(comment.Data[i], "\n", "")))
@@ -138,0 +161,2 @@ func isEmptyValue(v reflect.Value) bool {
+ output.WriteString("
\n")
+
@@ -140 +163,0 @@ func isEmptyValue(v reflect.Value) bool {
- return true
@@ -142 +165,2 @@ func isEmptyValue(v reflect.Value) bool {
- return false
+
+ return output.String()
@@ -145,3 +169,21 @@ func isEmptyValue(v reflect.Value) bool {
-func JSON(v interface{}) string {
- b, _ := json.Marshal(v)
- return string(b)
+func GroupByHeaders(comments []parser.Comment, headers string) map[string][]parser.Comment {
+ grouped := make(map[string][]parser.Comment)
+ headersSplit := strings.Split(headers, ",")
+ // Group comments by tag
+ for _, comment := range comments {
+ for _, header := range comment.Headers {
+ if len(headersSplit) > 1 {
+ for _, rqh := range headersSplit {
+ if rqh == header {
+ grouped[header] = append(grouped[header], comment)
+ }
+ }
+ } else {
+
+ grouped[header] = append(grouped[header], comment)
+ }
+
+ }
+ }
+
+ return grouped
@@ -148,0 +191,7 @@ func JSON(v interface{}) string {
+// !OUTPUT: Used by the -format=csv flag to transform data into CSV
+// + just some more text
+// + Usage: Pass a slice of comments to output.CSV and it will write a formatted version to
+// + the console
+// + [!MAN]Def: [141:168](DEVMAN)
+// + [DEVMAN]Note: Here the loop breaks early to prevent runaway
+// + [DEVMAN,MAN]INFO: Just some more information
@@ -150,3 +199,27 @@ func JSON(v interface{}) string {
-func XML(v interface{}) string {
- b, _ := xml.Marshal(v)
- return string(b)
+func CSV(comments []parser.Comment) {
+ writer := csv.NewWriter(os.Stdout)
+ defer writer.Flush()
+ // !DEVMAN: Text
+ headers := GetHeaders(comments)
+ // !ERROR: this will break the capture
+ writer.Write(headers)
+ for _, comment := range comments {
+ out := []string{}
+ for _, v := range headers {
+ index := -1
+ for i, h := range comment.Headers {
+ if h == v {
+ index = i
+ break
+ }
+ }
+ // add a line
+
+ if index == -1 {
+ out = append(out, "")
+ } else {
+ out = append(out, comment.Data[index])
+ }
+ }
+ writer.Write(out)
+ }
@@ -155,2 +228,10 @@ func XML(v interface{}) string {
-func FormatPath(path string, data interface{}) string {
- var output bytes.Buffer
+// ANSI color codes for different tab levels
+var colors = []string{
+ "\033[0m", // Default
+ "\033[34m", // Blue
+ "\033[32m", // Green
+ "\033[33m", // Yellow
+ "\033[31m", // Red
+ "\033[35m", // Magenta
+ "\033[36m", // Cyan
+}
@@ -158,2 +239,3 @@ func FormatPath(path string, data interface{}) string {
- templ := template.Must(template.New("fp").Parse(path))
- templ.Execute(&output, data)
+func HighlightText(paragraph string) string {
+ lines := strings.Split(paragraph, "\n")
+ re := regexp.MustCompile(`^(\t*)`) // Regex to match leading tabs
@@ -161 +243,18 @@ func FormatPath(path string, data interface{}) string {
- return output.String()
+ var result strings.Builder
+ for _, line := range lines {
+ matches := re.FindString(line)
+ tabCount := len(matches)
+
+ // Apply color based on tab count, ensuring it doesn't exceed available colors
+ var color string
+ if tabCount < len(colors) {
+ color = colors[tabCount]
+ } else {
+ color = colors[len(colors)-1]
+ }
+ reset := "\033[0m"
+
+ // Apply color to the entire line
+ result.WriteString(color + line + reset + "\n")
+ }
+ return result.String()
package output
import (
"bytes"
"encoding/json"
"encoding/xml"
"fmt"
"reflect"
"strings"
"text/template"
)
// TextEncoder converts a struct to plain text based on struct tags, handling nested structs with indentation.
func Text(v interface{}) string {
var result strings.Builder
val := reflect.ValueOf(v)
typ := val.Type()
// Handle pointer types
if val.Kind() == reflect.Ptr {
val = val.Elem()
typ = val.Type()
}
processValue(val, typ, 0, &result)
return result.String()
}
func processValue(val reflect.Value, typ reflect.Type, indentLevel int, result *strings.Builder) {
indent := strings.Repeat("\t", indentLevel)
switch val.Kind() {
case reflect.Struct:
for i := 0; i < val.NumField(); i++ {
field := val.Field(i)
fieldType := typ.Field(i)
// Get the 'text' tag, fallback to 'xml' tag if not present
tag := fieldType.Tag.Get("text")
if tag == "" {
tag = fieldType.Tag.Get("xml")
}
// Skip if tag is "-"
if tag == "-" {
continue
}
// Use field name if no tag
fieldName := fieldType.Name
if tag == "" {
tag = fieldName
}
currentTag := tag
// Handle slices
if field.Kind() == reflect.Slice {
for j := 0; j < field.Len(); j++ {
item := field.Index(j)
itemType := item.Type()
if item.Kind() == reflect.Struct || (item.Kind() == reflect.Ptr && item.Elem().Kind() == reflect.Struct) {
// Nested struct or pointer to struct in slice
if strings.Contains(tag, "omitempty") && isEmptyValue(item) {
continue
} else {
currentTagWithoutOmit := strings.ReplaceAll(currentTag, ",omitempty", "")
result.WriteString(fmt.Sprintf("%s%s:\n", indent, currentTagWithoutOmit))
if item.Kind() == reflect.Ptr {
processValue(item.Elem(), itemType.Elem(), indentLevel+1, result)
} else {
processValue(item, itemType, indentLevel+1, result)
}
}
} else {
lines := strings.TrimSpace(fmt.Sprintf("%v", item.Interface()))
if strings.Contains(tag, "omitempty") && lines == "" {
continue
} else {
currentTagWithoutOmit := strings.ReplaceAll(currentTag, ",omitempty", "")
result.WriteString(fmt.Sprintf("%s%s: %v\n", indent, currentTagWithoutOmit, lines))
}
}
}
} else if field.Kind() == reflect.Struct || (field.Kind() == reflect.Ptr && field.Elem().Kind() == reflect.Struct) {
// Nested struct or pointer to struct
if strings.Contains(tag, "omitempty") && isEmptyValue(field) {
continue
} else {
currentTagWithoutOmit := strings.ReplaceAll(currentTag, ",omitempty", "")
result.WriteString(fmt.Sprintf("%s%s:\n", indent, currentTagWithoutOmit))
fieldTypeToPass := fieldType.Type
fieldValToPass := field
if field.Kind() == reflect.Ptr {
fieldTypeToPass = fieldType.Type.Elem()
fieldValToPass = field.Elem()
}
processValue(fieldValToPass, fieldTypeToPass, indentLevel+1, result)
}
} else {
// Basic type
lines := strings.TrimSpace(fmt.Sprintf("%v", field.Interface()))
if strings.Contains(tag, "omitempty") && lines == "" {
continue
} else {
currentTagWithoutOmit := strings.ReplaceAll(currentTag, ",omitempty", "")
result.WriteString(fmt.Sprintf("%s%s: %v\n", indent, currentTagWithoutOmit, lines))
}
}
}
default:
result.WriteString(fmt.Sprintf("%v", val.Interface()))
}
}
func isEmptyValue(v reflect.Value) bool {
switch v.Kind() {
case reflect.Array, reflect.Map, reflect.Slice, reflect.String:
return v.Len() == 0
case reflect.Bool:
return !v.Bool()
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
return v.Int() == 0
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
return v.Uint() == 0
case reflect.Float32, reflect.Float64:
return v.Float() == 0
case reflect.Interface, reflect.Ptr:
return v.IsNil()
case reflect.Struct:
for i := 0; i < v.NumField(); i++ {
if !isEmptyValue(v.Field(i)) {
return false
}
}
return true
}
return false
}
func JSON(v interface{}) string {
b, _ := json.Marshal(v)
return string(b)
}
func XML(v interface{}) string {
b, _ := xml.Marshal(v)
return string(b)
}
func FormatPath(path string, data interface{}) string {
var output bytes.Buffer
templ := template.Must(template.New("fp").Parse(path))
templ.Execute(&output, data)
return output.String()
}