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