github.com/huner2/gomarkdoc@v0.3.6/lang/util.go (about)

     1  package lang
     2  
     3  import (
     4  	"go/ast"
     5  	"go/printer"
     6  	"go/token"
     7  	"regexp"
     8  	"strings"
     9  	"unicode"
    10  )
    11  
    12  func printNode(node ast.Node, fs *token.FileSet) (string, error) {
    13  	cfg := printer.Config{
    14  		Mode:     printer.UseSpaces,
    15  		Tabwidth: 4,
    16  	}
    17  
    18  	var out strings.Builder
    19  	if err := cfg.Fprint(&out, fs, node); err != nil {
    20  		return "", err
    21  	}
    22  
    23  	return out.String(), nil
    24  }
    25  
    26  func runeIsUpper(r rune) bool {
    27  	return r >= 'A' && r <= 'Z'
    28  }
    29  
    30  const lowerToUpper = 'a' - 'A'
    31  
    32  func runeToUpper(r rune) rune {
    33  	return r - lowerToUpper
    34  }
    35  
    36  func splitCamel(text string) string {
    37  	var builder strings.Builder
    38  	var previousRune rune
    39  	var wordLength int
    40  	for i, r := range text {
    41  		if i == 0 {
    42  			previousRune = runeToUpper(r)
    43  			continue
    44  		}
    45  
    46  		switch {
    47  		case runeIsUpper(previousRune) && !runeIsUpper(r) && wordLength > 0:
    48  			// If we have a capital followed by a lower, that capital should
    49  			// begin a word. Throw a space before the runes if there is a word
    50  			// there.
    51  			builder.WriteRune(' ')
    52  			builder.WriteRune(previousRune)
    53  			wordLength = 1
    54  		case !runeIsUpper(previousRune) && runeIsUpper(r):
    55  			// If we have a lower followed by a capital, the capital should
    56  			// begin a word. Throw a space in between the runes. We don't have
    57  			// to check word length because we're writing the previous rune to
    58  			// the previous word, automaticall giving it a length of 1.
    59  			builder.WriteRune(previousRune)
    60  			builder.WriteRune(' ')
    61  			wordLength = 0
    62  		default:
    63  			// Otherwise, just throw the rune onto the previous word
    64  			builder.WriteRune(previousRune)
    65  			wordLength++
    66  		}
    67  
    68  		previousRune = r
    69  	}
    70  
    71  	// Write the last rune
    72  	if previousRune != 0 {
    73  		builder.WriteRune(previousRune)
    74  	}
    75  
    76  	return builder.String()
    77  }
    78  
    79  func extractSummary(doc string) string {
    80  	firstParagraph := normalizeDoc(doc)
    81  
    82  	// Trim to first paragraph if there are multiple
    83  	if idx := strings.Index(firstParagraph, "\n\n"); idx != -1 {
    84  		firstParagraph = firstParagraph[:idx]
    85  	}
    86  
    87  	var builder strings.Builder
    88  	var lookback1 rune
    89  	var lookback2 rune
    90  	var lookback3 rune
    91  	for _, r := range formatDocParagraph(firstParagraph) {
    92  		// We terminate the sequence if we see a space preceded by a '.' which
    93  		// does not have exactly one word character before it (to avoid
    94  		// treating initials as the end of a sentence).
    95  		isPeriod := r == ' ' && lookback1 == '.'
    96  		isInitial := unicode.IsUpper(lookback2) && !unicode.IsLetter(lookback3) && !unicode.IsDigit(lookback3)
    97  		if isPeriod && !isInitial {
    98  			break
    99  		}
   100  
   101  		// Write the rune
   102  		builder.WriteRune(r)
   103  
   104  		// Update tracking variables
   105  		lookback3 = lookback2
   106  		lookback2 = lookback1
   107  		lookback1 = r
   108  	}
   109  
   110  	// Make the summary end with a period if it is nonempty and doesn't already.
   111  	if lookback1 != '.' && lookback1 != 0 {
   112  		builder.WriteRune('.')
   113  	}
   114  
   115  	return builder.String()
   116  }
   117  
   118  var crlfRegex = regexp.MustCompile("\r\n")
   119  
   120  func normalizeDoc(doc string) string {
   121  	return strings.TrimSpace(crlfRegex.ReplaceAllString(doc, "\n"))
   122  }
   123  
   124  func formatDocParagraph(paragraph string) string {
   125  	var mergedParagraph strings.Builder
   126  	for i, line := range strings.Split(paragraph, "\n") {
   127  		if i > 0 {
   128  			mergedParagraph.WriteRune(' ')
   129  		}
   130  
   131  		mergedParagraph.WriteString(strings.TrimSpace(line))
   132  	}
   133  
   134  	return mergedParagraph.String()
   135  }