@@ -49,6 +49,8 @@ import { Script, createContext, runInContext } from 'vm';
49
49
import { installPasteSupport } from './repl-paste-support' ;
50
50
import util from 'util' ;
51
51
52
+ import { MongoDBAutocompleter } from '@mongodb-js/mongodb-ts-autocomplete' ;
53
+
52
54
declare const __non_webpack_require__ : any ;
53
55
54
56
/**
@@ -131,6 +133,53 @@ type Mutable<T> = {
131
133
- readonly [ P in keyof T ] : T [ P ] ;
132
134
} ;
133
135
136
+ function filterStartingWith ( {
137
+ kind,
138
+ name,
139
+ trigger,
140
+ } : {
141
+ kind : string ;
142
+ name : string ;
143
+ trigger : string ;
144
+ } ) : boolean {
145
+ name = name . toLocaleLowerCase ( ) ;
146
+ trigger = trigger . toLocaleLowerCase ( ) ;
147
+
148
+ /*
149
+ 1. If the trigger was blank and the kind is not property/method filter out the
150
+ result. This way if you autocomplete db.test.find({ you don't get all the
151
+ global variables, just the known collection field names and mql but you can
152
+ still autocomplete global variables and functions if you type part of the
153
+ name.
154
+ 2. Don't filter out exact matches (where filter === name) so that we match the
155
+ behaviour of the node completer.
156
+ 3. Make sure the name starts with the trigger, otherwise it will return every
157
+ possible property/name that's available at that level. ie. all the "peer"
158
+ properties of the things that match.
159
+ */
160
+ //console.log(name, kind);
161
+ // TODO: This can be improved further if we first see if there are any
162
+ // property/method kind completions and then just use those, then if there
163
+ // aren't return all completions. The reason is that db.test.find({m makes it
164
+ // through this filter and then you get all globals starting with m anyway.
165
+ // But to properly solve it we need more context. ie. if you're after { (ie.
166
+ // inside an object literal) and you're to the left of a : (or there isn't
167
+ // one) then you probably don't want globals regardless. If you're to the
168
+ // right of a : it is probably fine because you could be using a variable.
169
+ return (
170
+ ( trigger !== '' || kind === 'property' || kind === 'method' ) &&
171
+ name . startsWith ( trigger )
172
+ ) ;
173
+ }
174
+
175
+ function transformAutocompleteResults (
176
+ line : string ,
177
+ results : { result : string } [ ]
178
+ ) : [ string [ ] , string ] | [ string [ ] , string , 'exclusive' ] {
179
+ // TODO: actually use 'exclusive' when we should
180
+ return [ results . map ( ( result ) => result . result ) , line ] ;
181
+ }
182
+
134
183
/**
135
184
* An instance of a `mongosh` REPL, without any of the actual I/O.
136
185
* Specifically, code called by this class should not do any
@@ -430,10 +479,22 @@ class MongoshNodeRepl implements EvaluationListener {
430
479
this . outputFinishString += installPasteSupport ( repl ) ;
431
480
432
481
const origReplCompleter = promisify ( repl . completer . bind ( repl ) ) ; // repl.completer is callback-style
433
- const mongoshCompleter = completer . bind (
434
- null ,
435
- instanceState . getAutocompleteParameters ( )
436
- ) ;
482
+ let newMongoshCompleter : MongoDBAutocompleter ;
483
+ let oldMongoshCompleter : (
484
+ line : string
485
+ ) => Promise < [ string [ ] , string , 'exclusive' ] | [ string [ ] , string ] > ;
486
+ if ( process . env . USE_NEW_AUTOCOMPLETE ) {
487
+ const autocompletionContext = instanceState . getAutocompletionContext ( ) ;
488
+ newMongoshCompleter = new MongoDBAutocompleter ( {
489
+ context : autocompletionContext ,
490
+ autocompleterOptions : { filter : filterStartingWith } ,
491
+ } ) ;
492
+ } else {
493
+ oldMongoshCompleter = completer . bind (
494
+ null ,
495
+ instanceState . getAutocompleteParameters ( )
496
+ ) ;
497
+ }
437
498
const innerCompleter = async (
438
499
text : string
439
500
) : Promise < [ string [ ] , string ] > => {
@@ -442,8 +503,19 @@ class MongoshNodeRepl implements EvaluationListener {
442
503
[ replResults , replOrig ] ,
443
504
[ mongoshResults , , mongoshResultsExclusive ] ,
444
505
] = await Promise . all ( [
445
- ( async ( ) => ( await origReplCompleter ( text ) ) || [ [ ] ] ) ( ) ,
446
- ( async ( ) => await mongoshCompleter ( text ) ) ( ) ,
506
+ ( async ( ) => {
507
+ const nodeResults = ( await origReplCompleter ( text ) ) || [ [ ] ] ;
508
+ return nodeResults ;
509
+ } ) ( ) ,
510
+ ( async ( ) => {
511
+ if ( process . env . USE_NEW_AUTOCOMPLETE ) {
512
+ const results = await newMongoshCompleter . autocomplete ( text ) ;
513
+ const transformed = transformAutocompleteResults ( text , results ) ;
514
+ return transformed ;
515
+ } else {
516
+ return oldMongoshCompleter ( text ) ;
517
+ }
518
+ } ) ( ) ,
447
519
] ) ;
448
520
this . bus . emit ( 'mongosh:autocompletion-complete' ) ; // For testing.
449
521
0 commit comments