Skip to content

Commit df0ef07

Browse files
tucksaunfabpot
authored andcommitted
Extract code from Symfony CLI
0 parents  commit df0ef07

21 files changed

+2488
-0
lines changed

LICENSE

Lines changed: 661 additions & 0 deletions
Large diffs are not rendered by default.

block.go

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
package terminal
2+
3+
import (
4+
"bytes"
5+
"fmt"
6+
"strings"
7+
)
8+
9+
func FormatBlockMessage(format string, msg string) string {
10+
var buf bytes.Buffer
11+
12+
width, _ := GetSize()
13+
14+
lines, maxLen := splitsBlockLines(msg, width-4) // 2 spaces on the left, 2 on the right
15+
fullPadding := strings.Repeat(" ", maxLen+4)
16+
17+
buf.WriteString(fmt.Sprintf("<%s>", format))
18+
buf.WriteString(fullPadding)
19+
buf.WriteString("</>\n")
20+
for _, line := range lines {
21+
buf.WriteString(fmt.Sprintf("<%s> ", format))
22+
lenLine, _ := Stdout.GetFormatter().Format([]byte(line), &buf)
23+
if n := maxLen - lenLine; n >= 0 {
24+
buf.WriteString(strings.Repeat(" ", n))
25+
}
26+
buf.WriteString(" </>\n")
27+
}
28+
buf.WriteString(fmt.Sprintf("<%s>", format))
29+
buf.WriteString(fullPadding)
30+
buf.WriteString("</>\n")
31+
32+
return buf.String()
33+
}
34+
35+
func splitsBlockLines(msg string, width int) ([]string, int) {
36+
lines := []string{}
37+
maxLen := 0
38+
// this can happen in headless mode, like running tests for example
39+
if width <= 0 {
40+
width = 80
41+
}
42+
43+
for _, line := range strings.Split(msg, "\n") {
44+
line = strings.Replace(line, "\t", " ", -1)
45+
lastLinePos := 0
46+
inAnOpeningTag := false
47+
inAClosingTag := false
48+
inATagBody := false
49+
length := 0
50+
var lastChar rune
51+
for pos, char := range line {
52+
if char == '<' && lastChar != '\\' {
53+
if len(line) > pos+1 && line[pos+1] == '/' {
54+
inAClosingTag = true
55+
inATagBody = false
56+
} else {
57+
inAnOpeningTag = true
58+
}
59+
}
60+
61+
if !inAClosingTag && !inAnOpeningTag {
62+
length += 1
63+
}
64+
65+
if char == '>' && lastChar != '\\' {
66+
if inAnOpeningTag {
67+
inAnOpeningTag = false
68+
inATagBody = true
69+
} else {
70+
inAClosingTag = false
71+
inATagBody = false
72+
}
73+
}
74+
75+
if length >= width && !inAClosingTag && !inAnOpeningTag && !inATagBody {
76+
lines = append(lines, line[lastLinePos:pos+1])
77+
maxLen = width
78+
lastLinePos = pos + 1
79+
length = 0
80+
}
81+
82+
lastChar = char
83+
}
84+
85+
if lastLinePos < len(line) {
86+
lines = append(lines, line[lastLinePos:])
87+
if length > maxLen {
88+
maxLen = length
89+
}
90+
91+
}
92+
}
93+
94+
return lines, maxLen
95+
}

block_test.go

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
package terminal
2+
3+
import (
4+
. "gopkg.in/check.v1"
5+
)
6+
7+
type OutputBlockSuite struct{}
8+
9+
var _ = Suite(&OutputBlockSuite{})
10+
11+
func (ts *OutputBlockSuite) TestSplitsBlockLines(c *C) {
12+
lines, maxLen := splitsBlockLines("Foo Bazz", 3)
13+
c.Assert(lines, DeepEquals, []string{"Foo", " Ba", "zz"})
14+
c.Assert(maxLen, Equals, 3)
15+
16+
lines, maxLen = splitsBlockLines("<href=https://example.com>Foo</>", 3)
17+
c.Assert(lines, DeepEquals, []string{"<href=https://example.com>Foo</>"})
18+
c.Assert(maxLen, Equals, 3)
19+
20+
lines, maxLen = splitsBlockLines("<href=https://example.com>Foo</>Bar", 3)
21+
c.Assert(lines, DeepEquals, []string{"<href=https://example.com>Foo</>", "Bar"})
22+
c.Assert(maxLen, Equals, 3)
23+
24+
lines, maxLen = splitsBlockLines("<href=https://example.com>Foo Bar</>Baz", 3)
25+
c.Assert(lines, DeepEquals, []string{"<href=https://example.com>Foo Bar</>", "Baz"})
26+
c.Assert(maxLen, Equals, 3)
27+
}
28+
29+
func (ts *OutputBlockSuite) TestSplitsBlockLinesDonotPanic(c *C) {
30+
lines, maxLen := splitsBlockLines("Foo Baz<", 4)
31+
c.Assert(lines, DeepEquals, []string{"Foo ", "Baz<"})
32+
c.Assert(maxLen, Equals, 4)
33+
}

ci.go

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
package terminal
2+
3+
import "os"
4+
5+
// from https://github.com/watson/ci-info/blob/master/vendors.json
6+
var ciEnvs = []string{
7+
"APPVEYOR",
8+
"SYSTEM_TEAMFOUNDATIONCOLLECTIONURI",
9+
"bamboo_planKey",
10+
"BITBUCKET_COMMIT",
11+
"BITRISE_IO",
12+
"BUDDY_WORKSPACE_ID",
13+
"BUILDKITE",
14+
"CIRCLECI",
15+
"CIRRUS_CI",
16+
"CODEBUILD_BUILD_ARN",
17+
"CI_NAME",
18+
"DRONE",
19+
"DSARI",
20+
"GITLAB_CI",
21+
"GO_PIPELINE_LABEL",
22+
"HUDSON_URL",
23+
"JENKINS_URL",
24+
"BUILD_ID",
25+
"MAGNUM",
26+
"NETLIFY_BUILD_BASE",
27+
"NEVERCODE",
28+
"SAILCI",
29+
"SEMAPHORE",
30+
"SHIPPABLE",
31+
"TDDIUM",
32+
"STRIDER",
33+
"TASK_ID",
34+
"RUN_ID",
35+
"TEAMCITY_VERSION",
36+
"TRAVIS",
37+
"GITHUB_ACTIONS",
38+
"NOW_BUILDER",
39+
"APPCENTER_BUILD_ID",
40+
}
41+
42+
func IsCI() bool {
43+
if os.Getenv("DEBIAN_FRONTEND") == "noninteractive" {
44+
return true
45+
}
46+
47+
for _, env := range ciEnvs {
48+
if _, hasEnv := os.LookupEnv(env); hasEnv {
49+
return true
50+
}
51+
}
52+
53+
return false
54+
}

0 commit comments

Comments
 (0)