diff --git a/modules/highlight/highlight.go b/modules/highlight/highlight.go
index 344be78144..a72f26d5f0 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 == `</span><span class="w">` {
 			content += "\n</span>"
+		} else if content == `</span></span><span class="line"><span class="cl">` {
+			content += "\n"
 		}
 		content = strings.TrimSuffix(content, `<span class="w">`)
 		content = strings.TrimPrefix(content, `</span>`)
diff --git a/modules/highlight/highlight_test.go b/modules/highlight/highlight_test.go
index 2f305bb589..e5dfedd2b3 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"
+	"code.gitea.io/gitea/modules/util"
 
+	"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: util.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{
-				`<span class="line"><span class="cl"><span class="nt">kind</span><span class="p">:</span><span class="w"> </span><span class="l">pipeline</span>`,
-				`</span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">default</span>`,
-				`</span></span><span class="line"><span class="cl">`,
-				`</span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">steps</span><span class="p">:</span>`,
-				`</span></span><span class="line"><span class="cl"><span class="w"></span>- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">test</span>`,
-				`</span></span><span class="line"><span class="cl"><span class="w">	</span><span class="nt">image</span><span class="p">:</span><span class="w"> </span><span class="l">golang:1.13</span>`,
-				`</span></span><span class="line"><span class="cl"><span class="w">	</span><span class="nt">environment</span><span class="p">:</span>`,
-				`</span></span><span class="line"><span class="cl"><span class="w"></span><span class="w">		</span><span class="nt">GOPROXY</span><span class="p">:</span><span class="w"> </span><span class="l">https://goproxy.cn</span>`,
-				`</span></span><span class="line"><span class="cl"><span class="w">	</span><span class="nt">commands</span><span class="p">:</span>`,
-				`</span></span><span class="line"><span class="cl"><span class="w"></span><span class="w">	</span>- <span class="l">go get -u</span>`,
-				`</span></span><span class="line"><span class="cl"><span class="w">	</span>- <span class="l">go build -v</span>`,
-				`</span></span><span class="line"><span class="cl"><span class="w">	</span>- <span class="l">go test -v -race -coverprofile=coverage.txt -covermode=atomic</span><span class="w">
-</span></span></span>`,
-				`<span class="w">
-</span>`,
-			},
+				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: util.Dedent(`
+				<span class="line"><span class="cl"><span class="nt">kind</span><span class="p">:</span><span class="w"> </span><span class="l">pipeline</span>
+				</span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">default</span>
+				</span></span><span class="line"><span class="cl">
+				</span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">steps</span><span class="p">:</span>
+				</span></span><span class="line"><span class="cl"><span class="w"></span>- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">test</span>
+				</span></span><span class="line"><span class="cl"><span class="w">	</span><span class="nt">image</span><span class="p">:</span><span class="w"> </span><span class="l">golang:1.13</span>
+				</span></span><span class="line"><span class="cl"><span class="w">	</span><span class="nt">environment</span><span class="p">:</span>
+				</span></span><span class="line"><span class="cl"><span class="w"></span><span class="w">		</span><span class="nt">GOPROXY</span><span class="p">:</span><span class="w"> </span><span class="l">https://goproxy.cn</span>
+				</span></span><span class="line"><span class="cl"><span class="w">	</span><span class="nt">commands</span><span class="p">:</span>
+				</span></span><span class="line"><span class="cl"><span class="w"></span><span class="w">	</span>- <span class="l">go get -u</span>
+				</span></span><span class="line"><span class="cl"><span class="w">	</span>- <span class="l">go build -v</span>
+				</span></span><span class="line"><span class="cl"><span class="w">	</span>- <span class="l">go test -v -race -coverprofile=coverage.txt -covermode=atomic</span></span></span>
+			`),
 		},
 		{
 			name:     ".drone.yml - trailing space",
 			numLines: 13,
 			fileName: ".drone.yml",
-			code: `kind: pipeline
-name: default  ` + `
+			code: strings.Replace(util.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{
-				`<span class="line"><span class="cl"><span class="nt">kind</span><span class="p">:</span><span class="w"> </span><span class="l">pipeline</span>`,
-				`</span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">default  </span>`,
-				`</span></span><span class="line"><span class="cl">`,
-				`</span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">steps</span><span class="p">:</span>`,
-				`</span></span><span class="line"><span class="cl"><span class="w"></span>- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">test</span>`,
-				`</span></span><span class="line"><span class="cl"><span class="w">	</span><span class="nt">image</span><span class="p">:</span><span class="w"> </span><span class="l">golang:1.13</span>`,
-				`</span></span><span class="line"><span class="cl"><span class="w">	</span><span class="nt">environment</span><span class="p">:</span>`,
-				`</span></span><span class="line"><span class="cl"><span class="w"></span><span class="w">		</span><span class="nt">GOPROXY</span><span class="p">:</span><span class="w"> </span><span class="l">https://goproxy.cn</span>`,
-				`</span></span><span class="line"><span class="cl"><span class="w">	</span><span class="nt">commands</span><span class="p">:</span>`,
-				`</span></span><span class="line"><span class="cl"><span class="w"></span><span class="w">	</span>- <span class="l">go get -u</span>`,
-				`</span></span><span class="line"><span class="cl"><span class="w">	</span>- <span class="l">go build -v</span>`,
-				`</span></span><span class="line"><span class="cl"><span class="w">	</span>- <span class="l">go test -v -race -coverprofile=coverage.txt -covermode=atomic</span>`,
-				`</span></span><span class="line"><span class="cl"><span class="w">	</span></span></span>`,
-			},
+				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: util.Dedent(`
+				<span class="line"><span class="cl"><span class="nt">kind</span><span class="p">:</span><span class="w"> </span><span class="l">pipeline</span>
+				</span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">default  </span>
+				</span></span><span class="line"><span class="cl">
+				</span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">steps</span><span class="p">:</span>
+				</span></span><span class="line"><span class="cl"><span class="w"></span>- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">test</span>
+				</span></span><span class="line"><span class="cl"><span class="w">	</span><span class="nt">image</span><span class="p">:</span><span class="w"> </span><span class="l">golang:1.13</span>
+				</span></span><span class="line"><span class="cl"><span class="w">	</span><span class="nt">environment</span><span class="p">:</span>
+				</span></span><span class="line"><span class="cl"><span class="w"></span><span class="w">		</span><span class="nt">GOPROXY</span><span class="p">:</span><span class="w"> </span><span class="l">https://goproxy.cn</span>
+				</span></span><span class="line"><span class="cl"><span class="w">	</span><span class="nt">commands</span><span class="p">:</span>
+				</span></span><span class="line"><span class="cl"><span class="w"></span><span class="w">	</span>- <span class="l">go get -u</span>
+				</span></span><span class="line"><span class="cl"><span class="w">	</span>- <span class="l">go build -v</span>
+				</span></span><span class="line"><span class="cl"><span class="w">	</span>- <span class="l">go test -v -race -coverprofile=coverage.txt -covermode=atomic</span>
+				</span></span>
+				<span class="w">
+				</span>
+			`),
 		},
 	}
 
 	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)
 		})
 	}
 }
diff --git a/modules/util/util.go b/modules/util/util.go
index 351a345473..1017117874 100644
--- a/modules/util/util.go
+++ b/modules/util/util.go
@@ -9,6 +9,7 @@ import (
 	"crypto/rand"
 	"errors"
 	"math/big"
+	"regexp"
 	"strconv"
 	"strings"
 
@@ -191,3 +192,35 @@ var titleCaser = cases.Title(language.English)
 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 {
+	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 ca5bd87eae..91b0ef9455 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")
+}