From d2465b1f9276c375bd72ea0e4892caf80c61ceed Mon Sep 17 00:00:00 2001 From: silverwind Date: Tue, 24 May 2022 21:33:20 +0200 Subject: [PATCH 1/4] Fix copy/paste of empty newlines again Fixes: https://github.com/go-gitea/gitea/issues/19331 Regressed by: https://github.com/go-gitea/gitea/pull/18270 Needed to do another newline addition to the Chroma output HTML to get copy/paste work again. The previous replacement conditions are probably obsolete, but as I'm not 100% sure, I opted to keep them. Specifically, the Chroma HTML change mentioned in https://github.com/go-gitea/gitea/pull/18270#issuecomment-1013350246 broke our previous newline replacement for such empty lines. Also included are a few changes to make the test more pleasant to work with. --- go.mod | 1 + go.sum | 2 + modules/highlight/highlight.go | 2 + modules/highlight/highlight_test.go | 124 ++++++++++++++-------------- 4 files changed, 68 insertions(+), 61 deletions(-) diff --git a/go.mod b/go.mod index 23e922ecef2df..996488478bef4 100644 --- a/go.mod +++ b/go.mod @@ -206,6 +206,7 @@ require ( github.com/kr/pretty v0.3.0 // indirect github.com/kr/text v0.2.0 // indirect github.com/libdns/libdns v0.2.1 // indirect + github.com/lithammer/dedent v1.1.0 // indirect github.com/magiconair/properties v1.8.5 // indirect github.com/mailru/easyjson v0.7.7 // indirect github.com/markbates/going v1.0.0 // indirect diff --git a/go.sum b/go.sum index 215450970fd61..148010199a681 100644 --- a/go.sum +++ b/go.sum @@ -1086,6 +1086,8 @@ github.com/libdns/libdns v0.2.1 h1:Wu59T7wSHRgtA0cfxC+n1c/e+O3upJGWytknkmFEDis= github.com/libdns/libdns v0.2.1/go.mod h1:yQCXzk1lEZmmCPa857bnk4TsOiqYasqpyOEeSObbb40= github.com/lightstep/lightstep-tracer-common/golang/gogo v0.0.0-20190605223551-bc2310a04743/go.mod h1:qklhhLq1aX+mtWk9cPHPzaBjWImj5ULL6C7HFJtXQMM= github.com/lightstep/lightstep-tracer-go v0.18.1/go.mod h1:jlF1pusYV4pidLvZ+XD0UBX0ZE6WURAspgAczcDHrL4= +github.com/lithammer/dedent v1.1.0 h1:VNzHMVCBNG1j0fh3OrsFRkVUwStdDArbgBWoPAffktY= +github.com/lithammer/dedent v1.1.0/go.mod h1:jrXYCQtgg0nJiN+StA2KgR7w6CiQNv9Fd/Z9BP0jIOc= github.com/lunny/dingtalk_webhook v0.0.0-20171025031554-e3534c89ef96 h1:uNwtsDp7ci48vBTTxDuwcoTXz4lwtDTe7TjCQ0noaWY= github.com/lunny/dingtalk_webhook v0.0.0-20171025031554-e3534c89ef96/go.mod h1:mmIfjCSQlGYXmJ95jFN84AkQFnVABtKuJL8IrzwvUKQ= github.com/lunny/log v0.0.0-20160921050905-7887c61bf0de/go.mod h1:3q8WtuPQsoRbatJuy3nvq/hRSvuBJrHHr+ybPPiNvHQ= diff --git a/modules/highlight/highlight.go b/modules/highlight/highlight.go index 344be78144ec5..a72f26d5f0755 100644 --- a/modules/highlight/highlight.go +++ b/modules/highlight/highlight.go @@ -203,6 +203,8 @@ func File(numLines int, fileName, language string, code []byte) []string { content = "\n" } else if content == `` { content += "\n" + } else if content == `` { + content += "\n" } content = strings.TrimSuffix(content, ``) content = strings.TrimPrefix(content, ``) diff --git a/modules/highlight/highlight_test.go b/modules/highlight/highlight_test.go index 2f305bb589c93..b3152768189cc 100644 --- a/modules/highlight/highlight_test.go +++ b/modules/highlight/highlight_test.go @@ -5,11 +5,13 @@ package highlight import ( - "reflect" + "strings" "testing" "code.gitea.io/gitea/modules/setting" + "github.com/lithammer/dedent" + "github.com/stretchr/testify/assert" "gopkg.in/ini.v1" ) @@ -20,83 +22,83 @@ func TestFile(t *testing.T) { numLines int fileName string code string - want []string + want string }{ { name: ".drone.yml", numLines: 12, fileName: ".drone.yml", - code: `kind: pipeline -name: default + code: strings.TrimSpace(dedent.Dedent(` + kind: pipeline + name: default -steps: -- name: test - image: golang:1.13 - environment: - GOPROXY: https://goproxy.cn - commands: - - go get -u - - go build -v - - go test -v -race -coverprofile=coverage.txt -covermode=atomic -`, - want: []string{ - `kind: pipeline`, - `name: default`, - ``, - `steps:`, - `- name: test`, - ` image: golang:1.13`, - ` environment:`, - ` GOPROXY: https://goproxy.cn`, - ` commands:`, - ` - go get -u`, - ` - go build -v`, - ` - go test -v -race -coverprofile=coverage.txt -covermode=atomic -`, - ` -`, - }, + steps: + - name: test + image: golang:1.13 + environment: + GOPROXY: https://goproxy.cn + commands: + - go get -u + - go build -v + - go test -v -race -coverprofile=coverage.txt -covermode=atomic + `)), + want: strings.TrimSpace(dedent.Dedent(` + kind: pipeline + name: default + + steps: + - name: test + image: golang:1.13 + environment: + GOPROXY: https://goproxy.cn + commands: + - go get -u + - go build -v + - go test -v -race -coverprofile=coverage.txt -covermode=atomic + `)), }, { name: ".drone.yml - trailing space", numLines: 13, fileName: ".drone.yml", - code: `kind: pipeline -name: default ` + ` + code: strings.Replace(strings.TrimSpace(dedent.Dedent(` + kind: pipeline + name: default -steps: -- name: test - image: golang:1.13 - environment: - GOPROXY: https://goproxy.cn - commands: - - go get -u - - go build -v - - go test -v -race -coverprofile=coverage.txt -covermode=atomic - `, - want: []string{ - `kind: pipeline`, - `name: default `, - ``, - `steps:`, - `- name: test`, - ` image: golang:1.13`, - ` environment:`, - ` GOPROXY: https://goproxy.cn`, - ` commands:`, - ` - go get -u`, - ` - go build -v`, - ` - go test -v -race -coverprofile=coverage.txt -covermode=atomic`, - ` `, - }, + steps: + - name: test + image: golang:1.13 + environment: + GOPROXY: https://goproxy.cn + commands: + - go get -u + - go build -v + - go test -v -race -coverprofile=coverage.txt -covermode=atomic + `))+"\n", "name: default", "name: default ", 1), + want: strings.TrimSpace(dedent.Dedent(` + kind: pipeline + name: default + + steps: + - name: test + image: golang:1.13 + environment: + GOPROXY: https://goproxy.cn + commands: + - go get -u + - go build -v + - go test -v -race -coverprofile=coverage.txt -covermode=atomic + + + + `)), }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - if got := File(tt.numLines, tt.fileName, "", []byte(tt.code)); !reflect.DeepEqual(got, tt.want) { - t.Errorf("File() = %v, want %v", got, tt.want) - } + got := strings.Join(File(tt.numLines, tt.fileName, "", []byte(tt.code)), "\n") + assert.Equal(t, tt.want, got) }) } } From 2e7b6fa4bb9199f38986995c4151a8382da537af Mon Sep 17 00:00:00 2001 From: silverwind Date: Tue, 24 May 2022 23:31:26 +0200 Subject: [PATCH 2/4] run go mod tidy --- go.mod | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/go.mod b/go.mod index 996488478bef4..5eaafbf5cdfb6 100644 --- a/go.mod +++ b/go.mod @@ -56,6 +56,7 @@ require ( github.com/klauspost/compress v1.15.3 github.com/klauspost/cpuid/v2 v2.0.12 github.com/lib/pq v1.10.5 + github.com/lithammer/dedent v1.1.0 github.com/lunny/dingtalk_webhook v0.0.0-20171025031554-e3534c89ef96 github.com/markbates/goth v1.72.0 github.com/mattn/go-isatty v0.0.14 @@ -206,7 +207,6 @@ require ( github.com/kr/pretty v0.3.0 // indirect github.com/kr/text v0.2.0 // indirect github.com/libdns/libdns v0.2.1 // indirect - github.com/lithammer/dedent v1.1.0 // indirect github.com/magiconair/properties v1.8.5 // indirect github.com/mailru/easyjson v0.7.7 // indirect github.com/markbates/going v1.0.0 // indirect From fbd18328a398c1fa54a3d22c6eddd994b553dfaa Mon Sep 17 00:00:00 2001 From: silverwind Date: Thu, 26 May 2022 20:43:59 +0200 Subject: [PATCH 3/4] add util.Dedent --- modules/highlight/highlight_test.go | 18 +++++++++--------- modules/util/util.go | 6 ++++++ 2 files changed, 15 insertions(+), 9 deletions(-) diff --git a/modules/highlight/highlight_test.go b/modules/highlight/highlight_test.go index b3152768189cc..e5dfedd2b3c8e 100644 --- a/modules/highlight/highlight_test.go +++ b/modules/highlight/highlight_test.go @@ -9,8 +9,8 @@ import ( "testing" "code.gitea.io/gitea/modules/setting" + "code.gitea.io/gitea/modules/util" - "github.com/lithammer/dedent" "github.com/stretchr/testify/assert" "gopkg.in/ini.v1" ) @@ -28,7 +28,7 @@ func TestFile(t *testing.T) { name: ".drone.yml", numLines: 12, fileName: ".drone.yml", - code: strings.TrimSpace(dedent.Dedent(` + code: util.Dedent(` kind: pipeline name: default @@ -41,8 +41,8 @@ func TestFile(t *testing.T) { - go get -u - go build -v - go test -v -race -coverprofile=coverage.txt -covermode=atomic - `)), - want: strings.TrimSpace(dedent.Dedent(` + `), + want: util.Dedent(` kind: pipeline name: default @@ -55,13 +55,13 @@ func TestFile(t *testing.T) { - go get -u - go build -v - go test -v -race -coverprofile=coverage.txt -covermode=atomic - `)), + `), }, { name: ".drone.yml - trailing space", numLines: 13, fileName: ".drone.yml", - code: strings.Replace(strings.TrimSpace(dedent.Dedent(` + code: strings.Replace(util.Dedent(` kind: pipeline name: default @@ -74,8 +74,8 @@ func TestFile(t *testing.T) { - go get -u - go build -v - go test -v -race -coverprofile=coverage.txt -covermode=atomic - `))+"\n", "name: default", "name: default ", 1), - want: strings.TrimSpace(dedent.Dedent(` + `)+"\n", "name: default", "name: default ", 1), + want: util.Dedent(` kind: pipeline name: default @@ -91,7 +91,7 @@ func TestFile(t *testing.T) { - `)), + `), }, } diff --git a/modules/util/util.go b/modules/util/util.go index 351a345473a9e..63da6af8fa046 100644 --- a/modules/util/util.go +++ b/modules/util/util.go @@ -12,6 +12,7 @@ import ( "strconv" "strings" + "github.com/lithammer/dedent" "golang.org/x/text/cases" "golang.org/x/text/language" ) @@ -191,3 +192,8 @@ var titleCaser = cases.Title(language.English) func ToTitleCase(s string) string { return titleCaser.String(s) } + +// Dedent removes common indentation of a multi-line string along with whitespace around it +func Dedent(s string) string { + return strings.TrimSpace(dedent.Dedent(s)) +} From a2e8819eeb408942c82bda95df5714999b54f672 Mon Sep 17 00:00:00 2001 From: silverwind Date: Thu, 26 May 2022 23:44:04 +0200 Subject: [PATCH 4/4] copy in the code --- go.mod | 1 - go.sum | 2 -- modules/util/util.go | 31 +++++++++++++++++++++++++++++-- modules/util/util_test.go | 7 +++++++ 4 files changed, 36 insertions(+), 5 deletions(-) diff --git a/go.mod b/go.mod index 5eaafbf5cdfb6..23e922ecef2df 100644 --- a/go.mod +++ b/go.mod @@ -56,7 +56,6 @@ require ( github.com/klauspost/compress v1.15.3 github.com/klauspost/cpuid/v2 v2.0.12 github.com/lib/pq v1.10.5 - github.com/lithammer/dedent v1.1.0 github.com/lunny/dingtalk_webhook v0.0.0-20171025031554-e3534c89ef96 github.com/markbates/goth v1.72.0 github.com/mattn/go-isatty v0.0.14 diff --git a/go.sum b/go.sum index 148010199a681..215450970fd61 100644 --- a/go.sum +++ b/go.sum @@ -1086,8 +1086,6 @@ github.com/libdns/libdns v0.2.1 h1:Wu59T7wSHRgtA0cfxC+n1c/e+O3upJGWytknkmFEDis= github.com/libdns/libdns v0.2.1/go.mod h1:yQCXzk1lEZmmCPa857bnk4TsOiqYasqpyOEeSObbb40= github.com/lightstep/lightstep-tracer-common/golang/gogo v0.0.0-20190605223551-bc2310a04743/go.mod h1:qklhhLq1aX+mtWk9cPHPzaBjWImj5ULL6C7HFJtXQMM= github.com/lightstep/lightstep-tracer-go v0.18.1/go.mod h1:jlF1pusYV4pidLvZ+XD0UBX0ZE6WURAspgAczcDHrL4= -github.com/lithammer/dedent v1.1.0 h1:VNzHMVCBNG1j0fh3OrsFRkVUwStdDArbgBWoPAffktY= -github.com/lithammer/dedent v1.1.0/go.mod h1:jrXYCQtgg0nJiN+StA2KgR7w6CiQNv9Fd/Z9BP0jIOc= github.com/lunny/dingtalk_webhook v0.0.0-20171025031554-e3534c89ef96 h1:uNwtsDp7ci48vBTTxDuwcoTXz4lwtDTe7TjCQ0noaWY= github.com/lunny/dingtalk_webhook v0.0.0-20171025031554-e3534c89ef96/go.mod h1:mmIfjCSQlGYXmJ95jFN84AkQFnVABtKuJL8IrzwvUKQ= github.com/lunny/log v0.0.0-20160921050905-7887c61bf0de/go.mod h1:3q8WtuPQsoRbatJuy3nvq/hRSvuBJrHHr+ybPPiNvHQ= diff --git a/modules/util/util.go b/modules/util/util.go index 63da6af8fa046..1017117874816 100644 --- a/modules/util/util.go +++ b/modules/util/util.go @@ -9,10 +9,10 @@ import ( "crypto/rand" "errors" "math/big" + "regexp" "strconv" "strings" - "github.com/lithammer/dedent" "golang.org/x/text/cases" "golang.org/x/text/language" ) @@ -193,7 +193,34 @@ func ToTitleCase(s string) string { return titleCaser.String(s) } +var ( + whitespaceOnly = regexp.MustCompile("(?m)^[ \t]+$") + leadingWhitespace = regexp.MustCompile("(?m)(^[ \t]*)(?:[^ \t\n])") +) + // Dedent removes common indentation of a multi-line string along with whitespace around it +// Based on https://github.com/lithammer/dedent func Dedent(s string) string { - return strings.TrimSpace(dedent.Dedent(s)) + var margin string + + s = whitespaceOnly.ReplaceAllString(s, "") + indents := leadingWhitespace.FindAllStringSubmatch(s, -1) + + for i, indent := range indents { + if i == 0 { + margin = indent[1] + } else if strings.HasPrefix(indent[1], margin) { + continue + } else if strings.HasPrefix(margin, indent[1]) { + margin = indent[1] + } else { + margin = "" + break + } + } + + if margin != "" { + s = regexp.MustCompile("(?m)^"+margin).ReplaceAllString(s, "") + } + return strings.TrimSpace(s) } diff --git a/modules/util/util_test.go b/modules/util/util_test.go index ca5bd87eaebe3..91b0ef9455e1d 100644 --- a/modules/util/util_test.go +++ b/modules/util/util_test.go @@ -225,3 +225,10 @@ func TestToTitleCase(t *testing.T) { assert.Equal(t, ToTitleCase(`foo bar baz`), `Foo Bar Baz`) assert.Equal(t, ToTitleCase(`FOO BAR BAZ`), `Foo Bar Baz`) } + +func TestDedent(t *testing.T) { + assert.Equal(t, Dedent(` + foo + bar + `), "foo\n\tbar") +}