diff --git a/undent.go b/undent.go index 7d040d0..dca5acb 100644 --- a/undent.go +++ b/undent.go @@ -5,15 +5,85 @@ package undent import ( "fmt" "io" - "regexp" ) -var matcher = regexp.MustCompile(`(?m)^([ \t]*)(?:\S)`) +const ( + tab = 9 + lf = 10 + spc = 32 +) // Bytes removes leading indentation/white-space from given string and returns // it as a byte slice. func Bytes(s string) []byte { - return []byte(String(s)) + if len(s) == 0 { + return []byte{} + } + + // find line feeds + lfs := []int{} + + if s[0] != lf { + lfs = append(lfs, -1) + } + + for i := 0; i < len(s); i++ { + if s[i] == lf { + lfs = append(lfs, i) + } + } + + // find smallest indent relative to each line-feed + min := 99999999999 + count := 0 + + for i := 0; i < len(lfs); i++ { + offset := lfs[i] + end := len(s) - 1 + if i+1 < len(lfs) { + end = lfs[i+1] + } + + if offset+1 >= end { + continue + } + + indent := 0 + lineSeek: + for n := offset + 1; n < end && indent < min; n++ { + switch s[n] { + case spc, tab: + indent++ + default: + break lineSeek + } + } + if indent < min { + min = indent + } + if indent > 0 { + count++ + } + } + + // extract each line without indentation + out := make([]byte, 0, len(s)-(min*count)) + + for i := 0; i < len(lfs); i++ { + offset := lfs[i] + 1 + end := len(s) + if i+1 < len(lfs) { + end = lfs[i+1] + 1 + } + + if offset+min < end { + out = append(out, s[offset+min:end]...) + } else if offset < end { + out = append(out, s[offset:end]...) + } + } + + return out } // Bytesf removes leading indentation/white-space from given format string @@ -25,34 +95,7 @@ func Bytesf(format string, a ...interface{}) []byte { // String removes leading indentation/white-space from given string. func String(s string) string { - if len(s) > 0 && s[0] == '\n' { - s = s[1:] - } - - matches := matcher.FindAllString(s, -1) - if len(matches) == 0 { - return s - } - - index := 0 - length := len(matches[0]) - - for i, s := range matches[1:] { - l := len(s) - if l < length { - index = i + 1 - length = l - } - } - - if length <= 1 { - return s - } - indent := matches[index][0 : length-1] - - return regexp.MustCompile( - `(?m)^`+regexp.QuoteMeta(indent), - ).ReplaceAllLiteralString(s, "") + return string(Bytes(s)) } // Stringf removes leading indentation/white-space from given format string diff --git a/undent_test.go b/undent_test.go index 7d1b10b..4de9cce 100644 --- a/undent_test.go +++ b/undent_test.go @@ -59,6 +59,21 @@ var stringTestCases = []struct { "foo": [ "bar" ] +}`, + }, + { + name: "multi-line space indented without any leading line-breaks", + s: ` { + "hello": "world", + "foo": [ + "bar" + ] + }`, + want: `{ + "hello": "world", + "foo": [ + "bar" + ] }`, }, { @@ -98,7 +113,22 @@ var stringTestCases = []struct { }`, }, { - name: "multi-line tab indented with leading line breaks", + name: "multi-line tab indented without any leading line-breaks", + s: ` { + "hello": "world", + "foo": [ + "bar" + ] + }`, + want: `{ + "hello": "world", + "foo": [ + "bar" + ] +}`, + }, + { + name: "multi-line tab indented with leading line-breaks", s: ` @@ -197,6 +227,108 @@ world foo bar`, }, + { + name: "long block of text", + s: ` + Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nunc + ornare, tellus vel luctus tristique, ipsum ante varius mauris, non + hendrerit tellus urna quis ex. Donec efficitur arcu sed iaculis + lobortis. Phasellus facilisis vitae mi quis accumsan. Suspendisse + rhoncus viverra odio ultricies rhoncus. Cras laoreet tortor + vestibulum pharetra luctus. Vivamus sit amet volutpat elit. + Suspendisse feugiat lectus id arcu sollicitudin tincidunt. Duis ut + sem auctor orci sodales varius in ac odio. Nullam finibus odio at + lacus tristique malesuada. + + Morbi nisl nulla, euismod eu enim in, tincidunt varius turpis. Morbi + ullamcorper tortor mi, ut aliquam metus posuere vel. Etiam vel dui + at quam placerat sollicitudin. Proin aliquam justo vitae mauris + gravida porta. Praesent hendrerit egestas ligula, faucibus tincidunt + tortor aliquet at. Ut luctus vehicula arcu eget cursus. Suspendisse + eget enim mollis, condimentum lacus eu, viverra nulla. Aenean vel + sapien eget enim convallis accumsan. Donec dictum ullamcorper leo + placerat sollicitudin. Pellentesque habitant morbi tristique + senectus et netus et malesuada fames ac turpis egestas. Vestibulum + volutpat mattis est, a feugiat purus feugiat at. Quisque at velit ut + mauris convallis sodales a et erat. Mauris condimentum augue sit + amet arcu sodales, aliquet ultrices ipsum tempor. Donec scelerisque + mi ligula, vel volutpat velit posuere a. Sed faucibus dui pulvinar + lorem commodo egestas. In facilisis suscipit lacus non suscipit. + + Nunc dictum est nulla, a rhoncus mi posuere id. Morbi at tempus + augue. Quisque ac nibh auctor velit auctor placerat id non eros. + Nulla condimentum quam id risus suscipit, ut fermentum mauris + lacinia. Vestibulum suscipit rutrum ex, sed vulputate nisi tempus + ultricies. Sed id ante pretium, accumsan sapien eget, malesuada + quam. Phasellus quis commodo enim. Vivamus in purus ac lorem ornare + posuere non eu quam. Sed arcu tortor, gravida quis fringilla nec, + ultricies et sem. Pellentesque arcu enim, tempor id nisl at, rhoncus + efficitur sem. Ut quis placerat quam. Donec maximus a risus sed + posuere. Curabitur pretium a diam non aliquet. + + Class aptent taciti sociosqu ad litora torquent per conubia nostra, + per inceptos himenaeos. Morbi volutpat felis leo, vel ultrices orci + bibendum ac. Integer eu mattis urna. Donec dictum vehicula + fermentum. Pellentesque a rutrum ipsum. Donec ultricies elit purus, + eget viverra tellus tristique sed. In hac habitasse platea dictumst. + Duis elementum semper elit, sit amet rhoncus lacus dictum ac. Nunc + pretium enim ac urna efficitur, eget facilisis enim interdum. + + Vivamus vel lectus lacus. Praesent vestibulum vel ligula eget + cursus. Quisque eu dignissim erat. Quisque maximus arcu eu turpis + pulvinar egestas. Nam aliquet neque sed tellus finibus mollis. + Phasellus consequat nibh nec ornare egestas. Donec at pellentesque + lorem.`, + want: `Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nunc +ornare, tellus vel luctus tristique, ipsum ante varius mauris, non +hendrerit tellus urna quis ex. Donec efficitur arcu sed iaculis +lobortis. Phasellus facilisis vitae mi quis accumsan. Suspendisse +rhoncus viverra odio ultricies rhoncus. Cras laoreet tortor +vestibulum pharetra luctus. Vivamus sit amet volutpat elit. +Suspendisse feugiat lectus id arcu sollicitudin tincidunt. Duis ut +sem auctor orci sodales varius in ac odio. Nullam finibus odio at +lacus tristique malesuada. + +Morbi nisl nulla, euismod eu enim in, tincidunt varius turpis. Morbi +ullamcorper tortor mi, ut aliquam metus posuere vel. Etiam vel dui +at quam placerat sollicitudin. Proin aliquam justo vitae mauris +gravida porta. Praesent hendrerit egestas ligula, faucibus tincidunt +tortor aliquet at. Ut luctus vehicula arcu eget cursus. Suspendisse +eget enim mollis, condimentum lacus eu, viverra nulla. Aenean vel +sapien eget enim convallis accumsan. Donec dictum ullamcorper leo +placerat sollicitudin. Pellentesque habitant morbi tristique +senectus et netus et malesuada fames ac turpis egestas. Vestibulum +volutpat mattis est, a feugiat purus feugiat at. Quisque at velit ut +mauris convallis sodales a et erat. Mauris condimentum augue sit +amet arcu sodales, aliquet ultrices ipsum tempor. Donec scelerisque +mi ligula, vel volutpat velit posuere a. Sed faucibus dui pulvinar +lorem commodo egestas. In facilisis suscipit lacus non suscipit. + +Nunc dictum est nulla, a rhoncus mi posuere id. Morbi at tempus +augue. Quisque ac nibh auctor velit auctor placerat id non eros. +Nulla condimentum quam id risus suscipit, ut fermentum mauris +lacinia. Vestibulum suscipit rutrum ex, sed vulputate nisi tempus +ultricies. Sed id ante pretium, accumsan sapien eget, malesuada +quam. Phasellus quis commodo enim. Vivamus in purus ac lorem ornare +posuere non eu quam. Sed arcu tortor, gravida quis fringilla nec, +ultricies et sem. Pellentesque arcu enim, tempor id nisl at, rhoncus +efficitur sem. Ut quis placerat quam. Donec maximus a risus sed +posuere. Curabitur pretium a diam non aliquet. + +Class aptent taciti sociosqu ad litora torquent per conubia nostra, +per inceptos himenaeos. Morbi volutpat felis leo, vel ultrices orci +bibendum ac. Integer eu mattis urna. Donec dictum vehicula +fermentum. Pellentesque a rutrum ipsum. Donec ultricies elit purus, +eget viverra tellus tristique sed. In hac habitasse platea dictumst. +Duis elementum semper elit, sit amet rhoncus lacus dictum ac. Nunc +pretium enim ac urna efficitur, eget facilisis enim interdum. + +Vivamus vel lectus lacus. Praesent vestibulum vel ligula eget +cursus. Quisque eu dignissim erat. Quisque maximus arcu eu turpis +pulvinar egestas. Nam aliquet neque sed tellus finibus mollis. +Phasellus consequat nibh nec ornare egestas. Donec at pellentesque +lorem.`, + }, } var stringfTestCases = []struct { @@ -254,6 +386,22 @@ var stringfTestCases = []struct { "foo": [ 42 ] +}`, + }, + { + name: "multi-line space indented without any leading line-breaks", + s: ` { + "hello": "%s", + "foo": [ + %d + ] + }`, + a: []interface{}{"world", 42}, + want: `{ + "hello": "world", + "foo": [ + 42 + ] }`, }, { @@ -292,6 +440,22 @@ var stringfTestCases = []struct { "foo": [ 42 ] +}`, + }, + { + name: "multi-line tab indented without any leading line-breaks", + s: ` { + "hello": "%s", + "foo": [ + %d + ] + }`, + a: []interface{}{"world", 42}, + want: `{ + "hello": "world", + "foo": [ + 42 + ] }`, }, { @@ -400,6 +564,109 @@ world foo 42`, }, + { + name: "long block of text", + s: ` + Lorem %s dolor sit amet, consectetur adipiscing elit. Nunc + ornare, tellus vel luctus tristique, ipsum ante varius mauris, non + hendrerit tellus urna quis ex. %s efficitur arcu sed iaculis + lobortis. Phasellus facilisis vitae mi quis accumsan. Suspendisse + rhoncus viverra odio ultricies rhoncus. Cras laoreet tortor + vestibulum pharetra luctus. Vivamus sit amet volutpat elit. + Suspendisse feugiat lectus id arcu sollicitudin tincidunt. Duis ut + sem auctor orci sodales varius in ac odio. Nullam finibus odio at + lacus tristique malesuada. + + Morbi nisl nulla, euismod eu enim in, tincidunt varius turpis. Morbi + ullamcorper tortor mi, ut aliquam metus posuere vel. Etiam vel dui + at quam placerat sollicitudin. Proin aliquam justo vitae mauris + gravida porta. Praesent hendrerit egestas ligula, faucibus tincidunt + tortor aliquet at. Ut luctus vehicula arcu eget cursus. Suspendisse + eget enim mollis, condimentum lacus eu, viverra nulla. Aenean vel + sapien eget enim convallis accumsan. Donec dictum ullamcorper leo + placerat sollicitudin. Pellentesque habitant morbi tristique + senectus et netus et malesuada fames ac turpis egestas. Vestibulum + volutpat mattis est, a feugiat purus feugiat at. Quisque at velit ut + mauris convallis sodales a et erat. Mauris condimentum augue sit + amet arcu sodales, aliquet ultrices ipsum tempor. Donec scelerisque + mi ligula, vel volutpat velit posuere a. Sed faucibus dui pulvinar + lorem commodo egestas. In facilisis suscipit lacus non suscipit. + + Nunc dictum est nulla, a rhoncus mi posuere id. Morbi at tempus + augue. Quisque ac nibh auctor velit auctor placerat id non eros. + Nulla condimentum quam id risus suscipit, ut fermentum mauris + lacinia. Vestibulum suscipit rutrum ex, sed vulputate nisi tempus + ultricies. Sed id ante pretium, accumsan sapien eget, malesuada + quam. Phasellus quis commodo enim. Vivamus in purus ac lorem ornare + posuere non eu quam. Sed arcu tortor, gravida quis fringilla nec, + ultricies et sem. Pellentesque arcu enim, tempor id nisl at, rhoncus + efficitur sem. Ut quis placerat quam. Donec maximus a risus sed + posuere. Curabitur pretium a diam non aliquet. + + Class aptent taciti sociosqu ad litora torquent per conubia nostra, + per inceptos himenaeos. Morbi volutpat felis leo, vel ultrices orci + bibendum ac. Integer eu mattis urna. Donec dictum vehicula + fermentum. Pellentesque a rutrum ipsum. Donec ultricies elit purus, + eget viverra tellus tristique sed. In hac habitasse platea dictumst. + Duis elementum semper elit, sit amet rhoncus lacus dictum ac. Nunc + pretium enim ac urna efficitur, eget facilisis enim interdum. + + Vivamus vel lectus lacus. Praesent vestibulum vel ligula eget + cursus. Quisque eu dignissim erat. Quisque maximus arcu eu turpis + pulvinar egestas. Nam aliquet neque sed tellus finibus mollis. + Phasellus consequat nibh nec ornare egestas. Donec at pellentesque + lorem.`, + a: []interface{}{"ipsum", "Donec"}, + want: `Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nunc +ornare, tellus vel luctus tristique, ipsum ante varius mauris, non +hendrerit tellus urna quis ex. Donec efficitur arcu sed iaculis +lobortis. Phasellus facilisis vitae mi quis accumsan. Suspendisse +rhoncus viverra odio ultricies rhoncus. Cras laoreet tortor +vestibulum pharetra luctus. Vivamus sit amet volutpat elit. +Suspendisse feugiat lectus id arcu sollicitudin tincidunt. Duis ut +sem auctor orci sodales varius in ac odio. Nullam finibus odio at +lacus tristique malesuada. + +Morbi nisl nulla, euismod eu enim in, tincidunt varius turpis. Morbi +ullamcorper tortor mi, ut aliquam metus posuere vel. Etiam vel dui +at quam placerat sollicitudin. Proin aliquam justo vitae mauris +gravida porta. Praesent hendrerit egestas ligula, faucibus tincidunt +tortor aliquet at. Ut luctus vehicula arcu eget cursus. Suspendisse +eget enim mollis, condimentum lacus eu, viverra nulla. Aenean vel +sapien eget enim convallis accumsan. Donec dictum ullamcorper leo +placerat sollicitudin. Pellentesque habitant morbi tristique +senectus et netus et malesuada fames ac turpis egestas. Vestibulum +volutpat mattis est, a feugiat purus feugiat at. Quisque at velit ut +mauris convallis sodales a et erat. Mauris condimentum augue sit +amet arcu sodales, aliquet ultrices ipsum tempor. Donec scelerisque +mi ligula, vel volutpat velit posuere a. Sed faucibus dui pulvinar +lorem commodo egestas. In facilisis suscipit lacus non suscipit. + +Nunc dictum est nulla, a rhoncus mi posuere id. Morbi at tempus +augue. Quisque ac nibh auctor velit auctor placerat id non eros. +Nulla condimentum quam id risus suscipit, ut fermentum mauris +lacinia. Vestibulum suscipit rutrum ex, sed vulputate nisi tempus +ultricies. Sed id ante pretium, accumsan sapien eget, malesuada +quam. Phasellus quis commodo enim. Vivamus in purus ac lorem ornare +posuere non eu quam. Sed arcu tortor, gravida quis fringilla nec, +ultricies et sem. Pellentesque arcu enim, tempor id nisl at, rhoncus +efficitur sem. Ut quis placerat quam. Donec maximus a risus sed +posuere. Curabitur pretium a diam non aliquet. + +Class aptent taciti sociosqu ad litora torquent per conubia nostra, +per inceptos himenaeos. Morbi volutpat felis leo, vel ultrices orci +bibendum ac. Integer eu mattis urna. Donec dictum vehicula +fermentum. Pellentesque a rutrum ipsum. Donec ultricies elit purus, +eget viverra tellus tristique sed. In hac habitasse platea dictumst. +Duis elementum semper elit, sit amet rhoncus lacus dictum ac. Nunc +pretium enim ac urna efficitur, eget facilisis enim interdum. + +Vivamus vel lectus lacus. Praesent vestibulum vel ligula eget +cursus. Quisque eu dignissim erat. Quisque maximus arcu eu turpis +pulvinar egestas. Nam aliquet neque sed tellus finibus mollis. +Phasellus consequat nibh nec ornare egestas. Donec at pellentesque +lorem.`, + }, } func TestBytes(t *testing.T) {