@@ -228,7 +228,55 @@ export class DefaultCompletionService implements CompletionService {
228
228
endArrayNodeChar = ']' ; // eslint-disable-line @typescript-eslint/no-unused-vars
229
229
}
230
230
231
- const result = await this . settings ?. documentCache ?. get ( textDocument ) ;
231
+ const offset = textDocument . offsetAt ( position ) ;
232
+
233
+ /*
234
+ process errored YAML input badly handled by YAML parser (see https://github.com/swagger-api/apidom/issues/194)
235
+ similarly to what done in swagger-editor: check if we are in a partial "prefix" scenario, in this case add a `:`
236
+ to the line and parse that line instead.
237
+
238
+ ```
239
+ info:
240
+ foo<caret here>
241
+ ..
242
+ ```
243
+
244
+ */
245
+ let processedText ;
246
+ if ( ! isJson ) {
247
+ const lineContentRange = DefaultCompletionService . getNonEmptyContentRange (
248
+ textDocument ,
249
+ offset ,
250
+ ) ;
251
+ const lineNonEmptyContent = lineContentRange ? textDocument . getText ( lineContentRange ) : '' ;
252
+ const lineContent = lineContentRange
253
+ ? DefaultCompletionService . getLine ( textDocument , offset )
254
+ : '' ;
255
+ const lineIndent = DefaultCompletionService . getIndentation ( lineContent ) ;
256
+
257
+ const prevLineOffset = DefaultCompletionService . getPreviousLineOffset ( textDocument , offset ) ;
258
+ const prevLineContent = DefaultCompletionService . getLine ( textDocument , prevLineOffset ) ;
259
+ const prevIndent = DefaultCompletionService . getIndentation ( prevLineContent ) ;
260
+ const nextLineOffset = DefaultCompletionService . getNextLineOffset ( textDocument , offset ) ;
261
+ const nextLineContent = DefaultCompletionService . getLine ( textDocument , nextLineOffset ) ;
262
+ const nextIndent = DefaultCompletionService . getIndentation ( nextLineContent ) ;
263
+ // must not be an array item AND not end with `:`
264
+ const isValueNode = DefaultCompletionService . isValueNode ( textDocument , offset ) ;
265
+ if (
266
+ ! isValueNode &&
267
+ lineNonEmptyContent &&
268
+ lineNonEmptyContent . length > 0 &&
269
+ ! lineNonEmptyContent . startsWith ( '-' ) &&
270
+ ! lineNonEmptyContent . endsWith ( ':' ) &&
271
+ ( prevIndent < lineIndent || nextIndent < prevIndent )
272
+ ) {
273
+ processedText = `${ textDocument . getText ( ) . slice ( 0 , offset ) } :${ textDocument
274
+ . getText ( )
275
+ . slice ( offset ) } `;
276
+ }
277
+ }
278
+
279
+ const result = await this . settings ?. documentCache ?. get ( textDocument , processedText ) ;
232
280
if ( ! result ) return CompletionList . create ( ) ;
233
281
const { api } = result ;
234
282
// if we cannot parse nothing to do
@@ -240,7 +288,6 @@ export class DefaultCompletionService implements CompletionService {
240
288
isIncomplete : false ,
241
289
} ;
242
290
243
- const offset = textDocument . offsetAt ( position ) ;
244
291
let targetOffset = offset ;
245
292
let emptyLine = false ;
246
293
@@ -415,8 +462,22 @@ export class DefaultCompletionService implements CompletionService {
415
462
}
416
463
}
417
464
}
465
+ // check if we are at the end of text, get root node if that's the case
466
+ const endOfText =
467
+ ! isJson &&
468
+ textDocument . getText ( ) . length > 0 &&
469
+ ( targetOffset >= textDocument . getText ( ) . length ||
470
+ textDocument . getText ( ) . substring ( offset , textDocument . getText ( ) . length ) . trim ( ) . length ===
471
+ 0 ) &&
472
+ '\r\n' . indexOf ( textDocument . getText ( ) . charAt ( targetOffset - 1 ) ) !== - 1 ;
473
+
474
+ if ( endOfText ) {
475
+ targetOffset = 0 ;
476
+ }
418
477
// find the current node
419
- const node = findAtOffset ( { offset : targetOffset , includeRightBound : true } , api ) ;
478
+ const node = endOfText
479
+ ? api
480
+ : findAtOffset ( { offset : targetOffset , includeRightBound : true } , api ) ;
420
481
// only if we have a node
421
482
if ( node ) {
422
483
const caretContext = this . resolveCaretContext ( node , targetOffset ) ;
@@ -469,10 +530,12 @@ export class DefaultCompletionService implements CompletionService {
469
530
} ,
470
531
} ;
471
532
533
+ const word = DefaultCompletionService . getCurrentWord ( textDocument , offset ) ;
472
534
if (
473
535
( isObject ( completionNode ) || ( isArray ( completionNode ) && isJson ) ) &&
474
536
( CompletionNodeContext . OBJECT === completionNodeContext ||
475
- CompletionNodeContext . VALUE_OBJECT === completionNodeContext ) &&
537
+ CompletionNodeContext . VALUE_OBJECT === completionNodeContext ||
538
+ processedText ) &&
476
539
( caretContext === CaretContext . KEY_INNER ||
477
540
caretContext === CaretContext . KEY_START ||
478
541
caretContext === CaretContext . KEY_END ||
@@ -532,6 +595,14 @@ export class DefaultCompletionService implements CompletionService {
532
595
a filterText with the content of the target range
533
596
*/
534
597
// item.filterText = text.substring(location.offset, location.offset + location.length);
598
+ if (
599
+ word &&
600
+ word . length > 0 &&
601
+ item . insertText ?. replace ( / ^ [ ' " ] { 1 } / g, '' ) . startsWith ( word )
602
+ ) {
603
+ item . preselect = true ;
604
+ }
605
+
535
606
if ( overwriteRange ) {
536
607
item . filterText = text . substring (
537
608
textDocument . offsetAt ( overwriteRange . start ) ,
@@ -590,7 +661,6 @@ export class DefaultCompletionService implements CompletionService {
590
661
nodeValueFromText . charAt ( 0 ) === '"' || nodeValueFromText . charAt ( 0 ) === "'"
591
662
? nodeValueFromText . charAt ( 0 )
592
663
: undefined ;
593
- const word = DefaultCompletionService . getCurrentWord ( textDocument , offset ) ;
594
664
proposed [ completionNode . toValue ( ) ] = CompletionItem . create ( '__' ) ;
595
665
proposed [ nodeValueFromText ] = CompletionItem . create ( '__' ) ;
596
666
// if node is not empty we must replace text
@@ -670,21 +740,21 @@ export class DefaultCompletionService implements CompletionService {
670
740
return completionList ;
671
741
}
672
742
673
- private static getCurrentWord ( document : TextDocument , offset : number ) {
743
+ private static getCurrentWord ( document : TextDocument | string , offset : number ) {
674
744
let i = offset - 1 ;
675
- const text = document . getText ( ) ;
745
+ const text = typeof document === 'string' ? document : document . getText ( ) ;
676
746
while ( i >= 0 && ' \t\n\r\v"\':{[,]}' . indexOf ( text . charAt ( i ) ) === - 1 ) {
677
747
i -= 1 ;
678
748
}
679
749
return text . substring ( i + 1 , offset ) ;
680
750
}
681
751
682
752
private static getRightAfterColonOffset (
683
- document : TextDocument ,
753
+ document : TextDocument | string ,
684
754
offset : number ,
685
755
mustBeEmpty : boolean ,
686
756
) : number {
687
- const text = document . getText ( ) ;
757
+ const text = typeof document === 'string' ? document : document . getText ( ) ;
688
758
let i = offset - 1 ;
689
759
while ( i >= 0 && ':' . indexOf ( text . charAt ( i ) ) === - 1 ) {
690
760
i -= 1 ;
@@ -706,12 +776,24 @@ export class DefaultCompletionService implements CompletionService {
706
776
return rightAfterColon ;
707
777
}
708
778
779
+ private static isValueNode ( document : TextDocument | string , offset : number ) : boolean {
780
+ const text = typeof document === 'string' ? document : document . getText ( ) ;
781
+ let i = offset - 1 ;
782
+ while ( i >= 0 && ':\r\n' . indexOf ( text . charAt ( i ) ) === - 1 ) {
783
+ i -= 1 ;
784
+ }
785
+ if ( '\r\n' . indexOf ( text . charAt ( i ) ) === - 1 ) {
786
+ return true ;
787
+ }
788
+ return false ;
789
+ }
790
+
709
791
private static getRightAfterDashOffset (
710
- document : TextDocument ,
792
+ document : TextDocument | string ,
711
793
offset : number ,
712
794
mustBeEmpty : boolean ,
713
795
) : number {
714
- const text = document . getText ( ) ;
796
+ const text = typeof document === 'string' ? document : document . getText ( ) ;
715
797
let i = offset - 1 ;
716
798
while ( i >= 0 && '-' . indexOf ( text . charAt ( i ) ) === - 1 ) {
717
799
i -= 1 ;
@@ -733,8 +815,8 @@ export class DefaultCompletionService implements CompletionService {
733
815
return rightAfterDash ;
734
816
}
735
817
736
- private static getLine ( document : TextDocument , offset : number ) : string {
737
- const text = document . getText ( ) ;
818
+ private static getLine ( document : TextDocument | string , offset : number ) : string {
819
+ const text = typeof document === 'string' ? document : document . getText ( ) ;
738
820
let i = offset - 1 ;
739
821
while ( i >= 0 && '\r\n' . indexOf ( text . charAt ( i ) ) === - 1 ) {
740
822
i -= 1 ;
@@ -748,8 +830,8 @@ export class DefaultCompletionService implements CompletionService {
748
830
return text . substring ( start + 1 , end ) ;
749
831
}
750
832
751
- private static getLineAfterOffset ( document : TextDocument , offset : number ) : string {
752
- const text = document . getText ( ) ;
833
+ private static getLineAfterOffset ( document : TextDocument | string , offset : number ) : string {
834
+ const text = typeof document === 'string' ? document : document . getText ( ) ;
753
835
let i = offset ;
754
836
while ( text . charAt ( i ) . length > 0 && '\n\r' . indexOf ( text . charAt ( i ) ) === - 1 ) {
755
837
i += 1 ;
@@ -759,13 +841,17 @@ export class DefaultCompletionService implements CompletionService {
759
841
}
760
842
761
843
private static getNonEmptyContentRange (
762
- document : TextDocument ,
844
+ document : TextDocument | string ,
763
845
offset : number ,
764
846
) : Range | undefined {
765
847
if ( offset < 0 ) {
766
848
return undefined ;
767
849
}
768
- const text = document . getText ( ) ;
850
+ const text = typeof document === 'string' ? document : document . getText ( ) ;
851
+ const doc =
852
+ typeof document === 'string'
853
+ ? TextDocument . create ( 'foo://bar/spec.yaml' , 'json' , 0 , document )
854
+ : document ;
769
855
let i = offset - 1 ;
770
856
// go to beginning of line
771
857
while ( i >= 0 && '\r\n' . indexOf ( text . charAt ( i ) ) === - 1 ) {
@@ -782,20 +868,19 @@ export class DefaultCompletionService implements CompletionService {
782
868
i += 1 ;
783
869
}
784
870
// go back to the first non space
785
- // go to the first non space
786
871
while ( i > start && ' \t\n\r\v' . indexOf ( text . charAt ( i ) ) !== - 1 ) {
787
872
i -= 1 ;
788
873
}
789
874
const end = i + 1 ;
790
875
if ( end - start < 1 ) {
791
876
return undefined ;
792
877
}
793
- const result = Range . create ( document . positionAt ( start ) , document . positionAt ( end ) ) ;
878
+ const result = Range . create ( doc . positionAt ( start ) , doc . positionAt ( end ) ) ;
794
879
return result ;
795
880
}
796
881
797
- private static getPreviousLineOffset ( document : TextDocument , offset : number ) : number {
798
- const text = document . getText ( ) ;
882
+ private static getPreviousLineOffset ( document : TextDocument | string , offset : number ) : number {
883
+ const text = typeof document === 'string' ? document : document . getText ( ) ;
799
884
let i = offset - 1 ;
800
885
while ( i >= 0 && '\r\n' . indexOf ( text . charAt ( i ) ) === - 1 ) {
801
886
i -= 1 ;
@@ -806,8 +891,8 @@ export class DefaultCompletionService implements CompletionService {
806
891
return i ;
807
892
}
808
893
809
- private static getNextLineOffset ( document : TextDocument , offset : number ) : number {
810
- const text = document . getText ( ) ;
894
+ private static getNextLineOffset ( document : TextDocument | string , offset : number ) : number {
895
+ const text = typeof document === 'string' ? document : document . getText ( ) ;
811
896
let i = offset ;
812
897
while ( i < text . length && '\r\n' . indexOf ( text . charAt ( i ) ) === - 1 ) {
813
898
i += 1 ;
@@ -822,26 +907,28 @@ export class DefaultCompletionService implements CompletionService {
822
907
return i + 1 ;
823
908
}
824
909
825
- private static isLastField ( document : TextDocument , offset : number ) : boolean {
826
- const text = document . getText ( ) ;
910
+ private static isLastField ( document : TextDocument | string , offset : number ) : boolean {
911
+ const text = typeof document === 'string' ? document : document . getText ( ) ;
912
+ const doc =
913
+ typeof document === 'string'
914
+ ? TextDocument . create ( 'foo://bar/spec.yaml' , 'json' , 0 , document )
915
+ : document ;
827
916
let i = offset ;
828
917
while ( i < text . length && '}]' . indexOf ( text . charAt ( i ) ) === - 1 ) {
829
918
i += 1 ;
830
919
}
831
- const after = document . getText (
832
- Range . create ( document . positionAt ( offset ) , document . positionAt ( offset + i ) ) ,
833
- ) ;
920
+ const after = doc . getText ( Range . create ( doc . positionAt ( offset ) , doc . positionAt ( offset + i ) ) ) ;
834
921
if ( after . trim ( ) . length === 0 ) {
835
922
return true ;
836
923
}
837
924
return false ;
838
925
}
839
926
840
- private static isEmptyLine ( document : TextDocument , offset : number ) : boolean {
927
+ private static isEmptyLine ( document : TextDocument | string , offset : number ) : boolean {
841
928
return DefaultCompletionService . getLine ( document , offset ) . trim ( ) . length === 0 ;
842
929
}
843
930
844
- private static isEmptyOrCommaValue ( document : TextDocument , offset : number ) : boolean {
931
+ private static isEmptyOrCommaValue ( document : TextDocument | string , offset : number ) : boolean {
845
932
const line = DefaultCompletionService . getLineAfterOffset ( document , offset ) . trim ( ) ;
846
933
if ( line . length === 0 ) {
847
934
return true ;
0 commit comments