4
4
InsertTextFormat ,
5
5
MarkupKind ,
6
6
DiagnosticSeverity ,
7
+ Range ,
8
+ Position ,
7
9
} from 'vscode-languageserver/node' ;
8
10
import type {
9
11
CancellationToken ,
@@ -60,9 +62,9 @@ export default class MongoDBService {
60
62
_connectionOptions ?: MongoClientOptions ;
61
63
62
64
_databaseCompletionItems : CompletionItem [ ] = [ ] ;
63
- _collectionCompletionItems : { [ database : string ] : CompletionItem [ ] } = { } ;
64
65
_shellSymbolCompletionItems : { [ symbol : string ] : CompletionItem [ ] } = { } ;
65
66
_globalSymbolCompletionItems : CompletionItem [ ] = [ ] ;
67
+ _collections : { [ database : string ] : string [ ] } = { } ;
66
68
_fields : { [ namespace : string ] : string [ ] } = { } ;
67
69
68
70
_visitor : Visitor ;
@@ -487,25 +489,15 @@ export default class MongoDBService {
487
489
* Get and cache collection and field names based on the namespace.
488
490
*/
489
491
async _getCompletionValuesAndUpdateCache (
490
- textFromEditor : string ,
491
- position : { line : number ; character : number } ,
492
492
currentDatabaseName : string | null ,
493
493
currentCollectionName : string | null
494
494
) {
495
- if (
496
- currentDatabaseName &&
497
- ! this . _collectionCompletionItems [ currentDatabaseName ]
498
- ) {
495
+ if ( currentDatabaseName && ! this . _collections [ currentDatabaseName ] ) {
499
496
// Get collection names for the current database.
500
497
const collections = await this . _getCollections ( currentDatabaseName ) ;
501
498
502
499
// Create and cache collection completion items.
503
- this . _cacheCollectionCompletionItems (
504
- textFromEditor ,
505
- position ,
506
- currentDatabaseName ,
507
- collections
508
- ) ;
500
+ this . _cacheCollections ( currentDatabaseName , collections ) ;
509
501
}
510
502
511
503
if ( currentDatabaseName && currentCollectionName ) {
@@ -750,18 +742,76 @@ export default class MongoDBService {
750
742
}
751
743
}
752
744
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
+
753
794
/**
754
795
* If the current node is 'db.<trigger>'.
755
796
*/
756
- _provideDbSymbolCompletionItems ( state : CompletionState ) {
797
+ _provideDbSymbolCompletionItems (
798
+ state : CompletionState ,
799
+ currentLineText : string ,
800
+ position : { line : number ; character : number }
801
+ ) {
757
802
// If we found 'use("db")' and the current node is 'db.<trigger>'.
758
803
if ( state . isDbSymbol && state . databaseName ) {
759
804
this . _connection . console . log (
760
805
'VISITOR found db symbol and collection name completions'
761
806
) ;
807
+
762
808
return [
763
809
...this . _shellSymbolCompletionItems . Database ,
764
- ...this . _collectionCompletionItems [ state . databaseName ] ,
810
+ ...this . _getCollectionCompletionItems ( {
811
+ databaseName : state . databaseName ,
812
+ currentLineText,
813
+ position,
814
+ } ) ,
765
815
] ;
766
816
}
767
817
@@ -776,10 +826,18 @@ export default class MongoDBService {
776
826
* If the current node can be used as a collection name
777
827
* e.g. 'db.<trigger>.find()' or 'let a = db.<trigger>'.
778
828
*/
779
- _provideCollectionNameCompletionItems ( state : CompletionState ) {
829
+ _provideCollectionNameCompletionItems (
830
+ state : CompletionState ,
831
+ currentLineText : string ,
832
+ position : { line : number ; character : number }
833
+ ) {
780
834
if ( state . isCollectionName && state . databaseName ) {
781
835
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
+ } ) ;
783
841
}
784
842
}
785
843
@@ -797,26 +855,37 @@ export default class MongoDBService {
797
855
* Parse code from a playground to identify
798
856
* where the cursor is and suggests only suitable completion items.
799
857
*/
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 [ ] > {
804
865
this . _connection . console . log (
805
866
`Provide completion items for a position: ${ util . inspect ( position ) } `
806
867
) ;
807
868
808
- const state = this . _visitor . parseASTForCompletion ( textFromEditor , position ) ;
869
+ const state = this . _visitor . parseASTForCompletion (
870
+ document ?. getText ( ) ,
871
+ position
872
+ ) ;
809
873
this . _connection . console . log (
810
874
`VISITOR completion state: ${ util . inspect ( state ) } `
811
875
) ;
812
876
813
877
await this . _getCompletionValuesAndUpdateCache (
814
- textFromEditor ,
815
- position ,
816
878
state . databaseName ,
817
879
state . collectionName
818
880
) ;
819
881
882
+ const currentLineText = document ?. getText (
883
+ Range . create (
884
+ Position . create ( position . line , 0 ) ,
885
+ Position . create ( position . line + 1 , 0 )
886
+ )
887
+ ) ;
888
+
820
889
const completionOptions = [
821
890
this . _provideStageCompletionItems . bind ( this , state ) ,
822
891
this . _provideQueryOperatorCompletionItems . bind ( this , state ) ,
@@ -827,8 +896,18 @@ export default class MongoDBService {
827
896
this . _provideFindCursorCompletionItems . bind ( this , state ) ,
828
897
this . _provideAggregationCursorCompletionItems . bind ( this , state ) ,
829
898
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
+ ) ,
832
911
this . _provideDbNameCompletionItems . bind ( this , state ) ,
833
912
] ;
834
913
@@ -960,53 +1039,10 @@ export default class MongoDBService {
960
1039
}
961
1040
962
1041
/**
963
- * Convert collection names to Completion Items and cache them .
1042
+ * Cache collection names.
964
1043
*/
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 ) ;
1010
1046
}
1011
1047
1012
1048
_clearCachedFields ( ) : void {
@@ -1018,7 +1054,7 @@ export default class MongoDBService {
1018
1054
}
1019
1055
1020
1056
_clearCachedCollections ( ) : void {
1021
- this . _collectionCompletionItems = { } ;
1057
+ this . _collections = { } ;
1022
1058
}
1023
1059
1024
1060
async _clearCurrentConnection ( ) : Promise < void > {
0 commit comments