github.com/ladydascalie/elvish@v0.0.0-20170703214355-2964dd3ece7f/edit/ui/key.go (about)

     1  package ui
     2  
     3  import (
     4  	"bytes"
     5  	"errors"
     6  	"fmt"
     7  	"strings"
     8  
     9  	"github.com/elves/elvish/eval"
    10  	"github.com/elves/elvish/util"
    11  )
    12  
    13  var ErrKeyMustBeString = errors.New("key must be string")
    14  
    15  // Key represents a single keyboard input, typically assembled from a escape
    16  // sequence.
    17  type Key struct {
    18  	Rune rune
    19  	Mod  Mod
    20  }
    21  
    22  // Default is used in the key binding table to indicate default binding.
    23  var Default = Key{DefaultBindingRune, 0}
    24  
    25  // Mod represents a modifier key.
    26  type Mod byte
    27  
    28  // Values for Mod.
    29  const (
    30  	// Shift is the shift modifier. It is only applied to special keys (e.g.
    31  	// Shift-F1). For instance 'A' and '@' which are typically entered with the
    32  	// shift key pressed, are not considered to be shift-modified.
    33  	Shift Mod = 1 << iota
    34  	// Alt is the alt modifier, traditionally known as the meta modifier.
    35  	Alt
    36  	Ctrl
    37  )
    38  
    39  // Special negative runes to represent function keys, used in the Rune field of
    40  // the Key struct.
    41  const (
    42  	F1 rune = -iota - 1
    43  	F2
    44  	F3
    45  	F4
    46  	F5
    47  	F6
    48  	F7
    49  	F8
    50  	F9
    51  	F10
    52  	F11
    53  	F12
    54  
    55  	Up
    56  	Down
    57  	Right
    58  	Left
    59  
    60  	Home
    61  	Insert
    62  	Delete
    63  	End
    64  	PageUp
    65  	PageDown
    66  
    67  	DefaultBindingRune // A special value to represent default binding.
    68  
    69  	// Some function key names are just aliases for their ASCII representation
    70  
    71  	Tab       = '\t'
    72  	Enter     = '\n'
    73  	Backspace = 0x7f
    74  )
    75  
    76  // functionKey stores the names of function keys, where the name of a function
    77  // key k is stored at index -k. For instance, functionKeyNames[-F1] = "F1".
    78  var functionKeyNames = [...]string{
    79  	"(Invalid)",
    80  	"F1", "F2", "F3", "F4", "F5", "F6", "F7", "F8", "F9", "F10", "F11", "F12",
    81  	"Up", "Down", "Right", "Left",
    82  	"Home", "Insert", "Delete", "End", "PageUp", "PageDown", "default",
    83  }
    84  
    85  // keyNames stores the name of function keys with a positive rune.
    86  var keyNames = map[rune]string{
    87  	Tab: "Tab", Enter: "Enter", Backspace: "Backspace",
    88  }
    89  
    90  func (k Key) String() string {
    91  	var b bytes.Buffer
    92  	if k.Mod&Ctrl != 0 {
    93  		b.WriteString("Ctrl-")
    94  	}
    95  	if k.Mod&Alt != 0 {
    96  		b.WriteString("Alt-")
    97  	}
    98  	if k.Mod&Shift != 0 {
    99  		b.WriteString("Shift-")
   100  	}
   101  	if k.Rune > 0 {
   102  		if name, ok := keyNames[k.Rune]; ok {
   103  			b.WriteString(name)
   104  		} else {
   105  			b.WriteRune(k.Rune)
   106  		}
   107  	} else {
   108  		i := int(-k.Rune)
   109  		if i >= len(functionKeyNames) {
   110  			fmt.Fprintf(&b, "(bad function key %d)", i)
   111  		} else {
   112  			b.WriteString(functionKeyNames[-k.Rune])
   113  		}
   114  	}
   115  	return b.String()
   116  }
   117  
   118  // modifierByName maps a name to an modifier. It is used for parsing keys where
   119  // the modifier string is first turned to lower case, so that all of C, c,
   120  // CTRL, Ctrl and ctrl can represent the Ctrl modifier.
   121  var modifierByName = map[string]Mod{
   122  	"s": Shift, "shift": Shift,
   123  	"a": Alt, "alt": Alt,
   124  	"m": Alt, "meta": Alt,
   125  	"c": Ctrl, "ctrl": Ctrl,
   126  }
   127  
   128  // parseKey parses a key. The syntax is:
   129  //
   130  // Key = { Mod ('+' | '-') } BareKey
   131  //
   132  // BareKey = FunctionKeyName | SingleRune
   133  func parseKey(s string) (Key, error) {
   134  	var k Key
   135  	// parse modifiers
   136  	for {
   137  		i := strings.IndexAny(s, "+-")
   138  		if i == -1 {
   139  			break
   140  		}
   141  		modname := strings.ToLower(s[:i])
   142  		mod, ok := modifierByName[modname]
   143  		if !ok {
   144  			return Key{}, fmt.Errorf("bad modifier: %q", modname)
   145  		}
   146  		k.Mod |= mod
   147  		s = s[i+1:]
   148  	}
   149  
   150  	if len(s) == 1 {
   151  		k.Rune = rune(s[0])
   152  		// XXX The following assumptions about keys with Ctrl are not checked
   153  		// with all terminals.
   154  		if k.Mod&Ctrl != 0 {
   155  			// Keys with Ctrl as one of the modifiers and a single ASCII letter
   156  			// as the base rune do not distinguish between cases. So we
   157  			// normalize the base rune to upper case.
   158  			if 'a' <= k.Rune && k.Rune <= 'z' {
   159  				k.Rune += 'A' - 'a'
   160  			}
   161  			// Tab is equivalent to Ctrl-I and Ctrl-J is equivalent to Enter.
   162  			// Normalize Ctrl-I to Tab and Ctrl-J to Enter.
   163  			if k.Rune == 'I' {
   164  				k.Mod &= ^Ctrl
   165  				k.Rune = Tab
   166  			} else if k.Rune == 'J' {
   167  				k.Mod &= ^Ctrl
   168  				k.Rune = Enter
   169  			}
   170  		}
   171  		return k, nil
   172  	}
   173  
   174  	for r, name := range keyNames {
   175  		if s == name {
   176  			k.Rune = r
   177  			return k, nil
   178  		}
   179  	}
   180  
   181  	for i, name := range functionKeyNames[1:] {
   182  		if s == name {
   183  			k.Rune = rune(-i - 1)
   184  			return k, nil
   185  		}
   186  	}
   187  
   188  	return Key{}, fmt.Errorf("bad key: %q", s)
   189  }
   190  
   191  // ToKey converts an elvish String to a Key. If the passed Value is not a
   192  // String, it throws an error.
   193  func ToKey(idx eval.Value) Key {
   194  	skey, ok := idx.(eval.String)
   195  	if !ok {
   196  		util.Throw(ErrKeyMustBeString)
   197  	}
   198  	key, err := parseKey(string(skey))
   199  	if err != nil {
   200  		util.Throw(err)
   201  	}
   202  	return key
   203  }