44 InsertTextFormat ,
55 MarkupKind ,
66 DiagnosticSeverity ,
7+ Range ,
8+ Position ,
79} from 'vscode-languageserver/node' ;
810import type {
911 CancellationToken ,
@@ -60,9 +62,9 @@ export default class MongoDBService {
6062 _connectionOptions ?: MongoClientOptions ;
6163
6264 _databaseCompletionItems : CompletionItem [ ] = [ ] ;
63- _collectionCompletionItems : { [ database : string ] : CompletionItem [ ] } = { } ;
6465 _shellSymbolCompletionItems : { [ symbol : string ] : CompletionItem [ ] } = { } ;
6566 _globalSymbolCompletionItems : CompletionItem [ ] = [ ] ;
67+ _collections : { [ database : string ] : string [ ] } = { } ;
6668 _fields : { [ namespace : string ] : string [ ] } = { } ;
6769
6870 _visitor : Visitor ;
@@ -487,25 +489,15 @@ export default class MongoDBService {
487489 * Get and cache collection and field names based on the namespace.
488490 */
489491 async _getCompletionValuesAndUpdateCache (
490- textFromEditor : string ,
491- position : { line : number ; character : number } ,
492492 currentDatabaseName : string | null ,
493493 currentCollectionName : string | null
494494 ) {
495- if (
496- currentDatabaseName &&
497- ! this . _collectionCompletionItems [ currentDatabaseName ]
498- ) {
495+ if ( currentDatabaseName && ! this . _collections [ currentDatabaseName ] ) {
499496 // Get collection names for the current database.
500497 const collections = await this . _getCollections ( currentDatabaseName ) ;
501498
502499 // Create and cache collection completion items.
503- this . _cacheCollectionCompletionItems (
504- textFromEditor ,
505- position ,
506- currentDatabaseName ,
507- collections
508- ) ;
500+ this . _cacheCollections ( currentDatabaseName , collections ) ;
509501 }
510502
511503 if ( currentDatabaseName && currentCollectionName ) {
@@ -750,18 +742,76 @@ export default class MongoDBService {
750742 }
751743 }
752744
745+ /**
746+ * Convert cached collection names into completion items.
747+ * We do not cache completion items as we do for other entities,
748+ * because some of the collection names have special characters
749+ * and must be edited to the bracket notation based on the current line content.
750+ */
751+ _getCollectionCompletionItems ( {
752+ databaseName,
753+ currentLineText,
754+ position,
755+ } : {
756+ databaseName : string ;
757+ currentLineText : string ;
758+ position : { line : number ; character : number } ;
759+ } ) {
760+ return this . _collections [ databaseName ] . map ( ( collectionName ) => {
761+ if ( this . _isValidPropertyName ( collectionName ) ) {
762+ return {
763+ label : collectionName ,
764+ kind : CompletionItemKind . Folder ,
765+ preselect : true ,
766+ } ;
767+ }
768+
769+ return {
770+ label : collectionName ,
771+ kind : CompletionItemKind . Folder ,
772+ // The current line text, e.g. `{ db. } // Comment`.
773+ filterText : currentLineText ,
774+ textEdit : {
775+ range : {
776+ start : { line : position . line , character : 0 } ,
777+ end : {
778+ line : position . line ,
779+ character : currentLineText . length ,
780+ } ,
781+ } ,
782+ // The completion item with the collection name converted into the bracket notation.
783+ newText : [
784+ currentLineText . slice ( 0 , position . character - 1 ) ,
785+ `['${ collectionName } ']` ,
786+ currentLineText . slice ( position . character , currentLineText . length ) ,
787+ ] . join ( '' ) ,
788+ } ,
789+ preselect : true ,
790+ } ;
791+ } ) ;
792+ }
793+
753794 /**
754795 * If the current node is 'db.<trigger>'.
755796 */
756- _provideDbSymbolCompletionItems ( state : CompletionState ) {
797+ _provideDbSymbolCompletionItems (
798+ state : CompletionState ,
799+ currentLineText : string ,
800+ position : { line : number ; character : number }
801+ ) {
757802 // If we found 'use("db")' and the current node is 'db.<trigger>'.
758803 if ( state . isDbSymbol && state . databaseName ) {
759804 this . _connection . console . log (
760805 'VISITOR found db symbol and collection name completions'
761806 ) ;
807+
762808 return [
763809 ...this . _shellSymbolCompletionItems . Database ,
764- ...this . _collectionCompletionItems [ state . databaseName ] ,
810+ ...this . _getCollectionCompletionItems ( {
811+ databaseName : state . databaseName ,
812+ currentLineText,
813+ position,
814+ } ) ,
765815 ] ;
766816 }
767817
@@ -776,10 +826,18 @@ export default class MongoDBService {
776826 * If the current node can be used as a collection name
777827 * e.g. 'db.<trigger>.find()' or 'let a = db.<trigger>'.
778828 */
779- _provideCollectionNameCompletionItems ( state : CompletionState ) {
829+ _provideCollectionNameCompletionItems (
830+ state : CompletionState ,
831+ currentLineText : string ,
832+ position : { line : number ; character : number }
833+ ) {
780834 if ( state . isCollectionName && state . databaseName ) {
781835 this . _connection . console . log ( 'VISITOR found collection name completions' ) ;
782- return this . _collectionCompletionItems [ state . databaseName ] ;
836+ return this . _getCollectionCompletionItems ( {
837+ databaseName : state . databaseName ,
838+ currentLineText,
839+ position,
840+ } ) ;
783841 }
784842 }
785843
@@ -797,26 +855,37 @@ export default class MongoDBService {
797855 * Parse code from a playground to identify
798856 * where the cursor is and suggests only suitable completion items.
799857 */
800- async provideCompletionItems (
801- textFromEditor : string ,
802- position : { line : number ; character : number }
803- ) : Promise < CompletionItem [ ] > {
858+ async provideCompletionItems ( {
859+ document,
860+ position,
861+ } : {
862+ document ?: Document ;
863+ position : { line : number ; character : number } ;
864+ } ) : Promise < CompletionItem [ ] > {
804865 this . _connection . console . log (
805866 `Provide completion items for a position: ${ util . inspect ( position ) } `
806867 ) ;
807868
808- const state = this . _visitor . parseASTForCompletion ( textFromEditor , position ) ;
869+ const state = this . _visitor . parseASTForCompletion (
870+ document ?. getText ( ) ,
871+ position
872+ ) ;
809873 this . _connection . console . log (
810874 `VISITOR completion state: ${ util . inspect ( state ) } `
811875 ) ;
812876
813877 await this . _getCompletionValuesAndUpdateCache (
814- textFromEditor ,
815- position ,
816878 state . databaseName ,
817879 state . collectionName
818880 ) ;
819881
882+ const currentLineText = document ?. getText (
883+ Range . create (
884+ Position . create ( position . line , 0 ) ,
885+ Position . create ( position . line + 1 , 0 )
886+ )
887+ ) ;
888+
820889 const completionOptions = [
821890 this . _provideStageCompletionItems . bind ( this , state ) ,
822891 this . _provideQueryOperatorCompletionItems . bind ( this , state ) ,
@@ -827,8 +896,18 @@ export default class MongoDBService {
827896 this . _provideFindCursorCompletionItems . bind ( this , state ) ,
828897 this . _provideAggregationCursorCompletionItems . bind ( this , state ) ,
829898 this . _provideGlobalSymbolCompletionItems . bind ( this , state ) ,
830- this . _provideDbSymbolCompletionItems . bind ( this , state ) ,
831- this . _provideCollectionNameCompletionItems . bind ( this , state ) ,
899+ this . _provideDbSymbolCompletionItems . bind (
900+ this ,
901+ state ,
902+ currentLineText ,
903+ position
904+ ) ,
905+ this . _provideCollectionNameCompletionItems . bind (
906+ this ,
907+ state ,
908+ currentLineText ,
909+ position
910+ ) ,
832911 this . _provideDbNameCompletionItems . bind ( this , state ) ,
833912 ] ;
834913
@@ -960,53 +1039,10 @@ export default class MongoDBService {
9601039 }
9611040
9621041 /**
963- * Convert collection names to Completion Items and cache them .
1042+ * Cache collection names.
9641043 */
965- _cacheCollectionCompletionItems (
966- textFromEditor : string ,
967- position : { line : number ; character : number } ,
968- database : string ,
969- collections : Document [ ]
970- ) : void {
971- this . _collectionCompletionItems [ database ] = collections . map ( ( item ) => {
972- if ( this . _isValidPropertyName ( item . name ) ) {
973- return {
974- label : item . name ,
975- kind : CompletionItemKind . Folder ,
976- preselect : true ,
977- } ;
978- }
979-
980- // Convert invalid property names to array-like format.
981- const filterText = textFromEditor . split ( '\n' ) [ position . line ] ;
982-
983- return {
984- label : item . name ,
985- kind : CompletionItemKind . Folder ,
986- // Find the line with invalid property name.
987- filterText : [
988- filterText . slice ( 0 , position . character ) ,
989- `.${ item . name } ` ,
990- filterText . slice ( position . character , filterText . length ) ,
991- ] . join ( '' ) ,
992- textEdit : {
993- range : {
994- start : { line : position . line , character : 0 } ,
995- end : {
996- line : position . line ,
997- character : filterText . length ,
998- } ,
999- } ,
1000- // Replace with array-like format.
1001- newText : [
1002- filterText . slice ( 0 , position . character - 1 ) ,
1003- `['${ item . name } ']` ,
1004- filterText . slice ( position . character , filterText . length ) ,
1005- ] . join ( '' ) ,
1006- preselect : true ,
1007- } ,
1008- } ;
1009- } ) ;
1044+ _cacheCollections ( database : string , collections : Document [ ] ) : void {
1045+ this . _collections [ database ] = collections . map ( ( item ) => item . name ) ;
10101046 }
10111047
10121048 _clearCachedFields ( ) : void {
@@ -1018,7 +1054,7 @@ export default class MongoDBService {
10181054 }
10191055
10201056 _clearCachedCollections ( ) : void {
1021- this . _collectionCompletionItems = { } ;
1057+ this . _collections = { } ;
10221058 }
10231059
10241060 async _clearCurrentConnection ( ) : Promise < void > {
0 commit comments