term_writer.go 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140
  1. // Copyright 2019 Yunion
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. /*
  15. Copyright 2016 The Kubernetes Authors.
  16. Licensed under the Apache License, Version 2.0 (the "License");
  17. you may not use this file except in compliance with the License.
  18. You may obtain a copy of the License at
  19. http://www.apache.org/licenses/LICENSE-2.0
  20. Unless required by applicable law or agreed to in writing, software
  21. distributed under the License is distributed on an "AS IS" BASIS,
  22. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  23. See the License for the specific language governing permissions and
  24. limitations under the License.
  25. */
  26. package term
  27. import (
  28. "io"
  29. "os"
  30. wordwrap "github.com/mitchellh/go-wordwrap"
  31. "github.com/moby/term"
  32. )
  33. type wordWrapWriter struct {
  34. limit uint
  35. writer io.Writer
  36. }
  37. // NewResponsiveWriter creates a Writer that detects the column width of the
  38. // terminal we are in, and adjusts every line width to fit and use recommended
  39. // terminal sizes for better readability. Does proper word wrapping automatically.
  40. //
  41. // if terminal width >= 120 columns use 120 columns
  42. // if terminal width >= 100 columns use 100 columns
  43. // if terminal width >= 80 columns use 80 columns
  44. //
  45. // In case we're not in a terminal or if it's smaller than 80 columns width,
  46. // doesn't do any wrapping.
  47. func NewResponsiveWriter(w io.Writer) io.Writer {
  48. file, ok := w.(*os.File)
  49. if !ok {
  50. return w
  51. }
  52. fd := file.Fd()
  53. if !term.IsTerminal(fd) {
  54. return w
  55. }
  56. terminalSize := GetSize(fd)
  57. if terminalSize == nil {
  58. return w
  59. }
  60. var limit uint
  61. switch {
  62. case terminalSize.Width >= 120:
  63. limit = 120
  64. case terminalSize.Width >= 100:
  65. limit = 100
  66. case terminalSize.Width >= 80:
  67. limit = 80
  68. }
  69. return NewWordWrapWriter(w, limit)
  70. }
  71. // NewWordWrapWriter is a Writer that supports a limit of characters on every line
  72. // and does auto word wrapping that respects that limit.
  73. func NewWordWrapWriter(w io.Writer, limit uint) io.Writer {
  74. return &wordWrapWriter{
  75. limit: limit,
  76. writer: w,
  77. }
  78. }
  79. func (w wordWrapWriter) Write(p []byte) (nn int, err error) {
  80. if w.limit == 0 {
  81. return w.writer.Write(p)
  82. }
  83. original := string(p)
  84. wrapped := wordwrap.WrapString(original, w.limit)
  85. return w.writer.Write([]byte(wrapped))
  86. }
  87. // NewPunchCardWriter is a NewWordWrapWriter that limits the line width to 80 columns.
  88. func NewPunchCardWriter(w io.Writer) io.Writer {
  89. return NewWordWrapWriter(w, 80)
  90. }
  91. type maxWidthWriter struct {
  92. maxWidth uint
  93. currentWidth uint
  94. written uint
  95. writer io.Writer
  96. }
  97. // NewMaxWidthWriter is a Writer that supports a limit of characters on every
  98. // line, but doesn't do any word wrapping automatically.
  99. func NewMaxWidthWriter(w io.Writer, maxWidth uint) io.Writer {
  100. return &maxWidthWriter{
  101. maxWidth: maxWidth,
  102. writer: w,
  103. }
  104. }
  105. func (m maxWidthWriter) Write(p []byte) (nn int, err error) {
  106. for _, b := range p {
  107. if m.currentWidth == m.maxWidth {
  108. m.writer.Write([]byte{'\n'})
  109. m.currentWidth = 0
  110. }
  111. if b == '\n' {
  112. m.currentWidth = 0
  113. }
  114. _, err := m.writer.Write([]byte{b})
  115. if err != nil {
  116. return int(m.written), err
  117. }
  118. m.written++
  119. m.currentWidth++
  120. }
  121. return len(p), nil
  122. }