github.com/elves/elvish@v0.15.0/pkg/edit/insert_api.go (about)

     1  package edit
     2  
     3  import (
     4  	"github.com/elves/elvish/pkg/cli"
     5  	"github.com/elves/elvish/pkg/eval"
     6  	"github.com/elves/elvish/pkg/eval/vals"
     7  	"github.com/elves/elvish/pkg/eval/vars"
     8  	"github.com/xiaq/persistent/hashmap"
     9  )
    10  
    11  //elvdoc:var abbr
    12  //
    13  // A map from (simple) abbreviations to their expansions.
    14  //
    15  // An abbreviation is replaced by its expansion when it is typed in full
    16  // and consecutively, without being interrupted by the use of other editing
    17  // functionalities, such as cursor movements.
    18  //
    19  // If more than one abbreviations would match, the longest one is used.
    20  //
    21  // Examples:
    22  //
    23  // ```elvish
    24  // edit:abbr['||'] = '| less'
    25  // edit:abbr['>dn'] = '2>/dev/null'
    26  // ```
    27  //
    28  // With the definitions above, typing `||` anywhere expands to `| less`, and
    29  // typing `>dn` anywhere expands to `2>/dev/null`. However, typing a `|`, moving
    30  // the cursor left, and typing another `|` does **not** expand to `| less`,
    31  // since the abbreviation `||` was not typed consecutively.
    32  //
    33  // @cf edit:small-word-abbr
    34  
    35  //elvdoc:var small-word-abbr
    36  //
    37  // A map from small-word abbreviations and their expansions.
    38  //
    39  // A small-word abbreviation is replaced by its expansion after it is typed in
    40  // full and consecutively, and followed by another character (the *trigger*
    41  // character). Furthermore, the expansion requires the following conditions to
    42  // be satisfied:
    43  //
    44  // -   The end of the abbreviation must be adjacent to a small-word boundary,
    45  //     i.e. the last character of the abbreviation and the trigger character
    46  //     must be from two different small-word categories.
    47  //
    48  // -   The start of the abbreviation must also be adjacent to a small-word
    49  //     boundary, unless it appears at the beginning of the code buffer.
    50  //
    51  // -   The cursor must be at the end of the buffer.
    52  //
    53  // If more than one abbreviations would match, the longest one is used.
    54  //
    55  // As an example, with the following configuration:
    56  //
    57  // ```elvish
    58  // edit:small-word-abbr['gcm'] = 'git checkout master'
    59  // ```
    60  //
    61  // In the following scenarios, the `gcm` abbreviation is expanded:
    62  //
    63  // -   With an empty buffer, typing `gcm` and a space or semicolon;
    64  //
    65  // -   When the buffer ends with a space, typing `gcm` and a space or semicolon.
    66  //
    67  // The space or semicolon after `gcm` is preserved in both cases.
    68  //
    69  // In the following scenarios, the `gcm` abbreviation is **not** expanded:
    70  //
    71  // -   With an empty buffer, typing `Xgcm` and a space or semicolon (start of
    72  //     abbreviation is not adjacent to a small-word boundary);
    73  //
    74  // -   When the buffer ends with `X`, typing `gcm` and a space or semicolon (end
    75  //     of abbreviation is not adjacent to a small-word boundary);
    76  //
    77  // -   When the buffer is non-empty, move the cursor to the beginning, and typing
    78  //     `gcm` and a space (cursor not at the end of the buffer).
    79  //
    80  // This example shows the case where the abbreviation consists of a single small
    81  // word of alphanumerical characters, but that doesn't have to be the case. For
    82  // example, with the following configuration:
    83  //
    84  // ```elvish
    85  // edit:small-word-abbr['>dn'] = ' 2>/dev/null'
    86  // ```
    87  //
    88  // The abbreviation `>dn` starts with a punctuation character, and ends with an
    89  // alphanumerical character. This means that it is expanded when it borders
    90  // a whitespace or alphanumerical character to the left, and a whitespace or
    91  // punctuation to the right; for example, typing `ls>dn;` will expand it.
    92  //
    93  // Some extra examples of small-word abbreviations:
    94  //
    95  // ```elvish
    96  // edit:small-word-abbr['gcp'] = 'git cherry-pick -x'
    97  // edit:small-word-abbr['ll'] = 'ls -ltr'
    98  // ```
    99  //
   100  // If both a [simple abbreviation](#editabbr) and a small-word abbreviation can
   101  // be expanded, the simple abbreviation has priority.
   102  //
   103  // @cf edit:abbr
   104  
   105  func initInsertAPI(appSpec *cli.AppSpec, nt notifier, ev *eval.Evaler, nb eval.NsBuilder) {
   106  	abbr := vals.EmptyMap
   107  	abbrVar := vars.FromPtr(&abbr)
   108  	appSpec.Abbreviations = makeMapIterator(abbrVar)
   109  
   110  	SmallWordAbbr := vals.EmptyMap
   111  	SmallWordAbbrVar := vars.FromPtr(&SmallWordAbbr)
   112  	appSpec.SmallWordAbbreviations = makeMapIterator(SmallWordAbbrVar)
   113  
   114  	binding := newBindingVar(EmptyBindingMap)
   115  	appSpec.OverlayHandler = newMapBinding(nt, ev, binding)
   116  
   117  	quotePaste := newBoolVar(false)
   118  	appSpec.QuotePaste = func() bool { return quotePaste.GetRaw().(bool) }
   119  
   120  	toggleQuotePaste := func() {
   121  		quotePaste.Set(!quotePaste.Get().(bool))
   122  	}
   123  
   124  	nb.Add("abbr", abbrVar)
   125  	nb.Add("small-word-abbr", SmallWordAbbrVar)
   126  	nb.AddGoFn("<edit>", "toggle-quote-paste", toggleQuotePaste)
   127  	nb.AddNs("insert", eval.NsBuilder{
   128  		"binding":     binding,
   129  		"quote-paste": quotePaste,
   130  	}.Ns())
   131  }
   132  
   133  func makeMapIterator(mv vars.PtrVar) func(func(a, b string)) {
   134  	return func(f func(a, b string)) {
   135  		for it := mv.GetRaw().(hashmap.Map).Iterator(); it.HasElem(); it.Next() {
   136  			k, v := it.Elem()
   137  			ks, kok := k.(string)
   138  			vs, vok := v.(string)
   139  			if !kok || !vok {
   140  				continue
   141  			}
   142  			f(ks, vs)
   143  		}
   144  	}
   145  }