1
+ class Autocomplete {
2
+
3
+ constructor ( options ) {
4
+ this . options = options ;
5
+ }
6
+
7
+ static replace ( text , position , replaceValue , removeTrailing = false , afterSpace = false ) {
8
+ let beginning = text . substring ( 0 , position ) ;
9
+ let trailing = text . substring ( position ) ;
10
+
11
+ let lastIndexOf = this . lastWhitespaceIndex ( beginning ) ;
12
+ let start = beginning . substr ( 0 , lastIndexOf + 1 ) ;
13
+ let end = trailing ;
14
+ if ( removeTrailing ) {
15
+ let indexOf = trailing . indexOf ( ' ' ) ;
16
+
17
+ end = '' ;
18
+ if ( indexOf !== - 1 )
19
+ end = trailing . substr ( indexOf ) ;
20
+ }
21
+
22
+ if ( afterSpace && ! end . startsWith ( ' ' ) )
23
+ end = ' ' + end ;
24
+
25
+ return {
26
+ before : beginning . length - start . length ,
27
+ after : trailing . length - end . length ,
28
+ text : start + replaceValue + end ,
29
+ } ;
30
+ }
31
+
32
+ static lastWhitespaceIndex ( beginning ) {
33
+ let lastSpaceIndex = beginning . lastIndexOf ( ' ' ) ;
34
+ let lastLineBreakIndex = beginning . lastIndexOf ( '\n' ) ;
35
+ return lastSpaceIndex > lastLineBreakIndex ? lastSpaceIndex : lastLineBreakIndex ;
36
+ }
37
+
38
+ suggest ( text , position ) {
39
+ let beginning = text . substring ( 0 , position ) ;
40
+ // let trailing = text.substring(position);
41
+
42
+ let lastWord = beginning ;
43
+ let lastIndexOf = Autocomplete . lastWhitespaceIndex ( beginning ) ;
44
+ if ( lastIndexOf !== - 1 ) {
45
+ lastWord = beginning . substr ( lastIndexOf + 1 ) ;
46
+ }
47
+
48
+ let suggests = [ ] , found = false ;
49
+
50
+ for ( let option of this . options ) {
51
+ let step = option ;
52
+ let list = step . list ;
53
+ if ( typeof list !== 'function' ) {
54
+ const data = list ;
55
+ if ( list instanceof Array ) {
56
+ list = search => {
57
+ let filtered = [ ] ;
58
+
59
+ let filter = require ( 'lodash/fp/filter' ) ;
60
+ if ( search )
61
+ filtered = filter ( o => o . startsWith ( search ) ) ( data ) ;
62
+ else if ( search === '' )
63
+ filtered = data ;
64
+
65
+ return new Promise ( resolve => {
66
+ resolve ( filtered ) ;
67
+ } ) ;
68
+ } ;
69
+ } else {
70
+ list = search => {
71
+ let filtered = [ ] ;
72
+
73
+ let cloneDeep = require ( 'lodash/fp/cloneDeep' ) ;
74
+ let keys = require ( 'lodash/fp/keys' ) ;
75
+ let filter = require ( 'lodash/fp/filter' ) ;
76
+ let start = '' ;
77
+ step = cloneDeep ( option ) ;
78
+ while ( ! ( step . list instanceof Array ) ) {
79
+ let changed = false ;
80
+ for ( let key of keys ( step . list ) ) {
81
+ if ( search . startsWith ( start + key ) ) {
82
+ let next_step = step . list [ key ] ;
83
+ if ( typeof next_step === 'object' ) {
84
+ if ( next_step . hasOwnProperty ( 'list' ) ) {
85
+ if ( ! next_step . hasOwnProperty ( 'trigger' ) || typeof next_step . trigger !== 'string' )
86
+ next_step . trigger = step . list . __trigger__ ;
87
+ if ( ! next_step . hasOwnProperty ( 'color' ) || typeof next_step . color !== 'string' )
88
+ next_step . color = step . list . __color__ ;
89
+ changed = true ;
90
+ step = next_step ;
91
+ start += key + step . trigger ;
92
+ }
93
+ }
94
+ }
95
+ }
96
+ if ( ! changed )
97
+ if ( typeof step === 'function' )
98
+ return new Promise ( resolve => {
99
+ filtered = step ( search ) ;
100
+ resolve ( filtered ) ;
101
+ } ) ;
102
+ else if ( ! ( step . list instanceof Array ) )
103
+ step . list = keys ( step . list ) ;
104
+ }
105
+ if ( option !== step ) {
106
+ step . trigger = option . trigger + start ;
107
+ if ( search . startsWith ( start ) )
108
+ search = search . substr ( start . length ) ;
109
+ }
110
+ if ( search )
111
+ filtered = filter ( o => o !== '__trigger__' && o !== '__color__' && o . startsWith ( search ) ) ( step . list ) ;
112
+ else if ( search === '' )
113
+ filtered = filter ( o => o !== '__trigger__' && o !== '__color__' ) ( step . list ) ;
114
+
115
+ return new Promise ( resolve => {
116
+ resolve ( filtered ) ;
117
+ } ) ;
118
+ } ;
119
+ }
120
+ }
121
+
122
+ let result = undefined ;
123
+ if ( ! step . pattern )
124
+ result = lastWord . startsWith ( step . trigger ) ? lastWord . substr ( step . trigger . length ) : false ;
125
+ else {
126
+ let pattern = new RegExp ( step . pattern ) ;
127
+ pattern = new RegExp ( step . trigger + pattern . source , pattern . flags ) ;
128
+ result = pattern . exec ( lastWord ) ;
129
+ }
130
+
131
+ if ( result || result === '' ) {
132
+ found = true ;
133
+ list ( result ) . then ( filtered => {
134
+ suggests . push ( ...filtered . map ( d => {
135
+ return { t : step . trigger , c : step . color , d}
136
+ } ) ) ;
137
+ } ) ;
138
+ }
139
+ }
140
+
141
+ return new Promise ( ( resolve , reject ) => {
142
+ if ( found )
143
+ resolve ( suggests ) ;
144
+ else
145
+ reject ( new Error ( 'not found!' ) ) ;
146
+ } ) ;
147
+ }
148
+ }
149
+
150
+ module . exports = Autocomplete ;
0 commit comments