...

Source file src/k8s.io/kubectl/pkg/util/term/term_writer.go

Documentation: k8s.io/kubectl/pkg/util/term

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

View as plain text