...

Source file src/github.com/huandu/xstrings/format.go

Documentation: github.com/huandu/xstrings

     1  // Copyright 2015 Huan Du. All rights reserved.
     2  // Licensed under the MIT license that can be found in the LICENSE file.
     3  
     4  package xstrings
     5  
     6  import (
     7  	"unicode/utf8"
     8  )
     9  
    10  // ExpandTabs can expand tabs ('\t') rune in str to one or more spaces dpending on
    11  // current column and tabSize.
    12  // The column number is reset to zero after each newline ('\n') occurring in the str.
    13  //
    14  // ExpandTabs uses RuneWidth to decide rune's width.
    15  // For example, CJK characters will be treated as two characters.
    16  //
    17  // If tabSize <= 0, ExpandTabs panics with error.
    18  //
    19  // Samples:
    20  //
    21  //	ExpandTabs("a\tbc\tdef\tghij\tk", 4) => "a   bc  def ghij    k"
    22  //	ExpandTabs("abcdefg\thij\nk\tl", 4)  => "abcdefg hij\nk   l"
    23  //	ExpandTabs("z中\t文\tw", 4)           => "z中 文  w"
    24  func ExpandTabs(str string, tabSize int) string {
    25  	if tabSize <= 0 {
    26  		panic("tab size must be positive")
    27  	}
    28  
    29  	var r rune
    30  	var i, size, column, expand int
    31  	var output *stringBuilder
    32  
    33  	orig := str
    34  
    35  	for len(str) > 0 {
    36  		r, size = utf8.DecodeRuneInString(str)
    37  
    38  		if r == '\t' {
    39  			expand = tabSize - column%tabSize
    40  
    41  			if output == nil {
    42  				output = allocBuffer(orig, str)
    43  			}
    44  
    45  			for i = 0; i < expand; i++ {
    46  				output.WriteRune(' ')
    47  			}
    48  
    49  			column += expand
    50  		} else {
    51  			if r == '\n' {
    52  				column = 0
    53  			} else {
    54  				column += RuneWidth(r)
    55  			}
    56  
    57  			if output != nil {
    58  				output.WriteRune(r)
    59  			}
    60  		}
    61  
    62  		str = str[size:]
    63  	}
    64  
    65  	if output == nil {
    66  		return orig
    67  	}
    68  
    69  	return output.String()
    70  }
    71  
    72  // LeftJustify returns a string with pad string at right side if str's rune length is smaller than length.
    73  // If str's rune length is larger than length, str itself will be returned.
    74  //
    75  // If pad is an empty string, str will be returned.
    76  //
    77  // Samples:
    78  //
    79  //	LeftJustify("hello", 4, " ")    => "hello"
    80  //	LeftJustify("hello", 10, " ")   => "hello     "
    81  //	LeftJustify("hello", 10, "123") => "hello12312"
    82  func LeftJustify(str string, length int, pad string) string {
    83  	l := Len(str)
    84  
    85  	if l >= length || pad == "" {
    86  		return str
    87  	}
    88  
    89  	remains := length - l
    90  	padLen := Len(pad)
    91  
    92  	output := &stringBuilder{}
    93  	output.Grow(len(str) + (remains/padLen+1)*len(pad))
    94  	output.WriteString(str)
    95  	writePadString(output, pad, padLen, remains)
    96  	return output.String()
    97  }
    98  
    99  // RightJustify returns a string with pad string at left side if str's rune length is smaller than length.
   100  // If str's rune length is larger than length, str itself will be returned.
   101  //
   102  // If pad is an empty string, str will be returned.
   103  //
   104  // Samples:
   105  //
   106  //	RightJustify("hello", 4, " ")    => "hello"
   107  //	RightJustify("hello", 10, " ")   => "     hello"
   108  //	RightJustify("hello", 10, "123") => "12312hello"
   109  func RightJustify(str string, length int, pad string) string {
   110  	l := Len(str)
   111  
   112  	if l >= length || pad == "" {
   113  		return str
   114  	}
   115  
   116  	remains := length - l
   117  	padLen := Len(pad)
   118  
   119  	output := &stringBuilder{}
   120  	output.Grow(len(str) + (remains/padLen+1)*len(pad))
   121  	writePadString(output, pad, padLen, remains)
   122  	output.WriteString(str)
   123  	return output.String()
   124  }
   125  
   126  // Center returns a string with pad string at both side if str's rune length is smaller than length.
   127  // If str's rune length is larger than length, str itself will be returned.
   128  //
   129  // If pad is an empty string, str will be returned.
   130  //
   131  // Samples:
   132  //
   133  //	Center("hello", 4, " ")    => "hello"
   134  //	Center("hello", 10, " ")   => "  hello   "
   135  //	Center("hello", 10, "123") => "12hello123"
   136  func Center(str string, length int, pad string) string {
   137  	l := Len(str)
   138  
   139  	if l >= length || pad == "" {
   140  		return str
   141  	}
   142  
   143  	remains := length - l
   144  	padLen := Len(pad)
   145  
   146  	output := &stringBuilder{}
   147  	output.Grow(len(str) + (remains/padLen+1)*len(pad))
   148  	writePadString(output, pad, padLen, remains/2)
   149  	output.WriteString(str)
   150  	writePadString(output, pad, padLen, (remains+1)/2)
   151  	return output.String()
   152  }
   153  
   154  func writePadString(output *stringBuilder, pad string, padLen, remains int) {
   155  	var r rune
   156  	var size int
   157  
   158  	repeats := remains / padLen
   159  
   160  	for i := 0; i < repeats; i++ {
   161  		output.WriteString(pad)
   162  	}
   163  
   164  	remains = remains % padLen
   165  
   166  	if remains != 0 {
   167  		for i := 0; i < remains; i++ {
   168  			r, size = utf8.DecodeRuneInString(pad)
   169  			output.WriteRune(r)
   170  			pad = pad[size:]
   171  		}
   172  	}
   173  }
   174  

View as plain text