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 }