github.com/markusbkk/elvish@v0.0.0-20231204143114-91dc52438621/pkg/cli/term/setup.go (about)

     1  package term
     2  
     3  import (
     4  	"fmt"
     5  	"os"
     6  
     7  	"github.com/markusbkk/elvish/pkg/sys"
     8  	"github.com/markusbkk/elvish/pkg/wcwidth"
     9  )
    10  
    11  // Setup sets up the terminal so that it is suitable for the Reader and
    12  // Writer to use. It returns a function that can be used to restore the
    13  // original terminal config.
    14  func Setup(in, out *os.File) (func() error, error) {
    15  	return setup(in, out)
    16  }
    17  
    18  // SetupGlobal sets up the terminal for the entire Elvish session.
    19  func SetupGlobal() func() {
    20  	return setupGlobal()
    21  }
    22  
    23  // Sanitize sanitizes the terminal after an external command has executed.
    24  func Sanitize(in, out *os.File) {
    25  	sanitize(in, out)
    26  }
    27  
    28  const (
    29  	lackEOLRune    = '\u23ce'
    30  	lackEOL        = "\033[7m" + string(lackEOLRune) + "\033[m"
    31  	enableSGRMouse = false
    32  )
    33  
    34  // setupVT performs setup for VT-like terminals.
    35  func setupVT(out *os.File) error {
    36  	_, width := sys.WinSize(out)
    37  
    38  	s := ""
    39  	/*
    40  		Write a lackEOLRune if the cursor is not in the leftmost column. This is
    41  		done as follows:
    42  
    43  		1. Turn on autowrap;
    44  
    45  		2. Write lackEOL along with enough padding, so that the total width is
    46  		   equal to the width of the screen.
    47  
    48  		   If the cursor was in the first column, we are still in the same line,
    49  		   just off the line boundary. Otherwise, we are now in the next line.
    50  
    51  		3. Rewind to the first column, write one space and rewind again. If the
    52  		   cursor was in the first column to start with, we have just erased the
    53  		   LackEOL character. Otherwise, we are now in the next line and this is
    54  		   a no-op. The LackEOL character remains.
    55  	*/
    56  	s += fmt.Sprintf("\033[?7h%s%*s\r \r", lackEOL, width-wcwidth.OfRune(lackEOLRune), "")
    57  
    58  	/*
    59  		Turn off autowrap.
    60  
    61  		The terminals sometimes has different opinions about how wide some
    62  		characters are (notably emojis and some dingbats) with elvish. When that
    63  		happens, elvish becomes wrong about where the cursor is when it writes
    64  		its output, and the effect can be disastrous.
    65  
    66  		If we turn off autowrap, the terminal won't insert any newlines behind
    67  		the scene, so elvish is always right about which line the cursor is.
    68  		With a bit more caution, this can restrict the consequence of the
    69  		mismatch within one line.
    70  	*/
    71  	s += "\033[?7l"
    72  
    73  	// Turn on SGR-style mouse tracking.
    74  	if enableSGRMouse {
    75  		s += "\033[?1000;1006h"
    76  	}
    77  
    78  	// Enable bracketed paste.
    79  	s += "\033[?2004h"
    80  
    81  	_, err := out.WriteString(s)
    82  	return err
    83  }
    84  
    85  // restoreVT performs restore for VT-like terminals.
    86  func restoreVT(out *os.File) error {
    87  	s := ""
    88  	// Turn on autowrap.
    89  	s += "\033[?7h"
    90  	// Turn off mouse tracking.
    91  	if enableSGRMouse {
    92  		s += "\033[?1000;1006l"
    93  	}
    94  	// Disable bracketed paste.
    95  	s += "\033[?2004l"
    96  	// Move the cursor to the first row, even if we haven't written anything
    97  	// visible. This is because the terminal driver might not be smart enough to
    98  	// recognize some escape sequences as invisible and wrongly assume that we
    99  	// are not in the first column, which can mess up with tabs. See
   100  	// https://github.com/markusbkk/elvish/pkg/issues/629 for an example.
   101  	s += "\r"
   102  	_, err := out.WriteString(s)
   103  	return err
   104  }