Ctrl K
ComunidadGuías…

utils/stringmanip.go

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
package utils

import (
	"bytes"
	"regexp"
	"strings"
)

// FilterIntoGroups filters []byte slices into two groups, depending on the given filter function
func FilterIntoGroups(bytelines [][]byte, filterfunc func([]byte) bool) ([][]byte, [][]byte) {
	var special, regular [][]byte
	for _, byteline := range bytelines {
		if filterfunc(byteline) {
			// Special
			special = append(special, byteline)
		} else {
			// Regular
			regular = append(regular, byteline)
		}
	}
	return special, regular
}

/*ExtractKeywords takes a source file as `data` and a list of keywords to
 * look for. Lines without keywords are returned, together with a map
 * from keywords to []bytes from the source `data`.
 *
 * The keywords in the data must be on the form "keyword: value",
 * and can be within single-line HTML comments (<-- ... -->).
 */
func ExtractKeywords(data []byte, keywordsToLookFor []string) ([]byte, map[string][]byte) {
	var (
		keywordColon      []byte
		inCommentBlock    bool
		singleLineComment bool
		lineCounter       uint64
	)
	bnl := []byte("\n")
	commentStart := []byte("<!--")
	commentEnd := []byte("-->")
	backtick := []byte("`")
	found := make(map[string][]byte)

	//if bytes.Contains(data, backtick) {
	//}

	stopLooking := false
	// Find and separate the lines starting with one of the keywords in the special map
	_, regular := FilterIntoGroups(bytes.Split(data, bnl), func(byteline []byte) bool {
		lineCounter++
		if stopLooking {
			return false
		}
		// Check if the current line has one of the special keywords
		for _, keyword := range keywordsToLookFor {
			strippedLine := bytes.TrimSpace(byteline)
			if len(strippedLine) == 0 {
				// Empty line
				return false
			}

			// Check if we are in a HTML comment block
			singleLineComment = false
			switch {
			case bytes.HasPrefix(strippedLine, commentStart) && bytes.HasSuffix(strippedLine, commentEnd):
				inCommentBlock = false
				strippedLine = bytes.TrimSpace(strippedLine[len(commentStart) : len(strippedLine)-len(commentEnd)])
				singleLineComment = true
			case bytes.HasPrefix(strippedLine, commentStart):
				inCommentBlock = true
			case bytes.HasSuffix(strippedLine, commentEnd):
				inCommentBlock = false
			}
			// fmt.Println("LINE", string(strippedLine), "IN COMMENT BLOCK", inCommentBlock, "SINGLE LINE COMMENT", singleLineComment)

			// If we encounter a backtick (`) outside of a comment, stop looking for keywords (due to Markdown formatting of code)
			if !inCommentBlock && !singleLineComment && bytes.Contains(strippedLine, backtick) {
				stopLooking = true
				return false
			}

			keywordColon = append([]byte(keyword), ':')
			// Lines starting with "% " can be used for specifying a title, ref pandoc
			if bytes.HasPrefix(strippedLine, []byte("% ")) {
				newTitle := bytes.TrimSpace(strippedLine[2:])
				if len(newTitle) > 0 {
					found["title"] = newTitle
					return true
				}
			}
			// Check for lines that starts with "<!--", ends with "-->" and contains the keyword and a ":"
			if singleLineComment {
				// Check if one of the relevant keywords are present
				if bytes.HasPrefix(strippedLine, keywordColon) {
					// Set (possibly overwrite) the value in the map, if the keyword is found.
					// Trim the surrounding whitespace and skip the letters of the keyword itself.
					found[keyword] = bytes.TrimSpace(strippedLine[len(keyword)+1:])
					return true
				}
				// Check for lines starting with the keyword and a ":"
			} else if bytes.HasPrefix(strippedLine, keywordColon) {
				// Check if the keyword was found either in a comment block, or on the first 10 lines
				if inCommentBlock || lineCounter < 10 {
					// Set (possibly overwrite) the value in the map, if the keyword is found.
					// Trim the surrounding whitespace and skip the letters of the keyword itself.
					found[keyword] = bytes.TrimSpace(strippedLine[len(keywordColon):])
					return true
				}
			}
		}
		// Not special
		return false
	})
	// Use the regular lines as the new data (remove the special lines)
	return bytes.Join(regular, bnl), found
}

// Infostring builds up a string on the form "functionname(arg1, arg2, arg3)"
func Infostring(functionName string, args []string) string {
	s := functionName + "("
	if len(args) > 0 {
		s += "\"" + strings.Join(args, "\", \"") + "\""
	}
	return s + ")"
}

// WriteStatus writes status messages to a string Builder
// The flags argument contains the flag names, and if they are enabled or not
func WriteStatus(sb *strings.Builder, title string, flags map[string]bool) {
	// Check that at least one of the bools are true
	found := false
	for _, value := range flags {
		if value {
			found = true
			break
		}
	}
	if !found {
		return
	}

	// Write the overview to the buffer
	sb.WriteString(title)
	sb.WriteString(":")

	// Spartan way of lining up the columns
	if len(title) < 7 {
		sb.WriteString("\t")
	}
	sb.WriteString("\t\t[")

	var enabledFlags []string
	// Add all enabled flags to the list
	for name, enabled := range flags {
		if enabled {
			enabledFlags = append(enabledFlags, name)
		}
	}
	sb.WriteString(strings.Join(enabledFlags, ", "))
	sb.WriteString("]\n")
}

// ExtractLocalImagePaths can be used to find local image filenames that are included in the given HTML
func ExtractLocalImagePaths(html string) []string {
	// Regular expression to match image paths in <img src="...">
	r := regexp.MustCompile(`<img src="([^"]+)"`)
	matches := r.FindAllStringSubmatch(html, -1)
	paths := make([]string, 0, len(matches))
	for _, match := range matches {
		path := match[1]
		if len(path) >= 7 && (path[:7] == "http://" || (len(path) >= 8 && path[:8] == "https://")) {
			continue
		}
		paths = append(paths, path)
	}
	return paths
}