@@ -3,81 +3,72 @@ import { yamlDocumentsCache } from '../parser/yaml-documents';
3
3
import { TextDocument } from 'vscode-languageserver-textdocument' ;
4
4
import { ASTNode } from 'vscode-json-languageservice' ;
5
5
6
- export function getSelectionRanges ( document : TextDocument , positions : Position [ ] ) : SelectionRange [ ] | undefined {
7
- if ( ! document ) {
8
- return ;
9
- }
6
+ export function getSelectionRanges ( document : TextDocument , positions : Position [ ] ) : SelectionRange [ ] {
10
7
const doc = yamlDocumentsCache . getYamlDocument ( document ) ;
11
8
return positions . map ( ( position ) => {
12
9
const ranges = getRanges ( position ) ;
13
- let current : SelectionRange ;
10
+ let current : SelectionRange | undefined ;
14
11
for ( const range of ranges ) {
15
12
current = SelectionRange . create ( range , current ) ;
16
13
}
17
- if ( ! current ) {
18
- current = SelectionRange . create ( {
19
- start : position ,
20
- end : position ,
21
- } ) ;
22
- }
23
- return current ;
14
+ return current ?? SelectionRange . create ( { start : position , end : position } ) ;
24
15
} ) ;
25
16
26
17
function getRanges ( position : Position ) : Range [ ] {
27
18
const offset = document . offsetAt ( position ) ;
28
19
const result : Range [ ] = [ ] ;
29
20
for ( const ymlDoc of doc . documents ) {
30
- let currentNode : ASTNode ;
31
- let firstNodeOffset : number ;
32
- let isFirstNode = true ;
21
+ let currentNode : ASTNode | undefined ;
22
+ let overrideStartOffset : number | undefined ;
33
23
ymlDoc . visit ( ( node ) => {
34
24
const endOffset = node . offset + node . length ;
35
25
// Skip if end offset doesn't even reach cursor position
36
26
if ( endOffset < offset ) {
37
27
return true ;
38
28
}
39
- let startOffset = node . offset ;
40
- // Recheck start offset with the trimmed one in case of this
41
- // key:
42
- // - value
43
- // ↑
44
- if ( startOffset > offset ) {
45
- const nodePosition = document . positionAt ( startOffset ) ;
46
- if ( nodePosition . line !== position . line ) {
47
- return true ;
48
- }
49
- const lineBeginning = { line : nodePosition . line , character : 0 } ;
50
- const text = document . getText ( {
51
- start : lineBeginning ,
52
- end : nodePosition ,
53
- } ) ;
54
- if ( text . trim ( ) . length !== 0 ) {
29
+ // Skip if we're ending at new line
30
+ // times:
31
+ // - second: 1
32
+ // millisecond: 10
33
+ // | - second: 2
34
+ // ↑ millisecond: 0
35
+ // (| is actually part of { second: 1, millisecond: 10 })
36
+ // \r\n doesn't matter here
37
+ if ( getTextFromOffsets ( endOffset - 1 , endOffset ) === '\n' ) {
38
+ if ( endOffset - 1 < offset ) {
55
39
return true ;
56
40
}
57
- startOffset = document . offsetAt ( lineBeginning ) ;
58
- if ( startOffset > offset ) {
41
+ }
42
+
43
+ let startOffset = node . offset ;
44
+ if ( startOffset > offset ) {
45
+ // Recheck start offset for some special cases
46
+ const newOffset = getStartOffsetForSpecialCases ( node , position ) ;
47
+ if ( ! newOffset || newOffset > offset ) {
59
48
return true ;
60
49
}
50
+ startOffset = newOffset ;
61
51
}
52
+
62
53
// Allow equal for children to override
63
54
if ( ! currentNode || startOffset >= currentNode . offset ) {
64
55
currentNode = node ;
65
- firstNodeOffset = startOffset ;
56
+ overrideStartOffset = startOffset ;
66
57
}
67
58
return true ;
68
59
} ) ;
69
60
while ( currentNode ) {
70
- const startOffset = isFirstNode ? firstNodeOffset : currentNode . offset ;
61
+ const startOffset = overrideStartOffset ?? currentNode . offset ;
71
62
const endOffset = currentNode . offset + currentNode . length ;
72
63
const range = {
73
64
start : document . positionAt ( startOffset ) ,
74
65
end : document . positionAt ( endOffset ) ,
75
66
} ;
76
67
const text = document . getText ( range ) ;
77
- const trimmedText = text . trimEnd ( ) ;
78
- const trimmedLength = text . length - trimmedText . length ;
79
- if ( trimmedLength > 0 ) {
80
- range . end = document . positionAt ( endOffset - trimmedLength ) ;
68
+ const trimmedText = trimEndNewLine ( text ) ;
69
+ const trimmedEndOffset = startOffset + trimmedText . length ;
70
+ if ( trimmedEndOffset >= offset ) {
71
+ range . end = document . positionAt ( trimmedEndOffset ) ;
81
72
}
82
73
// Add a jump between '' "" {} []
83
74
const isSurroundedBy = ( startCharacter : string , endCharacter ?: string ) : boolean => {
@@ -95,7 +86,7 @@ export function getSelectionRanges(document: TextDocument, positions: Position[]
95
86
}
96
87
result . push ( range ) ;
97
88
currentNode = currentNode . parent ;
98
- isFirstNode = false ;
89
+ overrideStartOffset = undefined ;
99
90
}
100
91
// A position can't be in multiple documents
101
92
if ( result . length > 0 ) {
@@ -104,4 +95,48 @@ export function getSelectionRanges(document: TextDocument, positions: Position[]
104
95
}
105
96
return result . reverse ( ) ;
106
97
}
98
+
99
+ function getStartOffsetForSpecialCases ( node : ASTNode , position : Position ) : number | undefined {
100
+ const nodeStartPosition = document . positionAt ( node . offset ) ;
101
+ if ( nodeStartPosition . line !== position . line ) {
102
+ return ;
103
+ }
104
+
105
+ if ( node . parent ?. type === 'array' ) {
106
+ // array:
107
+ // - value
108
+ // ↑
109
+ if ( getTextFromOffsets ( node . offset - 2 , node . offset ) === '- ' ) {
110
+ return node . offset - 2 ;
111
+ }
112
+ }
113
+
114
+ if ( node . type === 'array' || node . type === 'object' ) {
115
+ // array:
116
+ // - value
117
+ // ↑
118
+ const lineBeginning = { line : nodeStartPosition . line , character : 0 } ;
119
+ const text = document . getText ( { start : lineBeginning , end : nodeStartPosition } ) ;
120
+ if ( text . trim ( ) . length === 0 ) {
121
+ return document . offsetAt ( lineBeginning ) ;
122
+ }
123
+ }
124
+ }
125
+
126
+ function getTextFromOffsets ( startOffset : number , endOffset : number ) : string {
127
+ return document . getText ( {
128
+ start : document . positionAt ( startOffset ) ,
129
+ end : document . positionAt ( endOffset ) ,
130
+ } ) ;
131
+ }
132
+ }
133
+
134
+ function trimEndNewLine ( str : string ) : string {
135
+ if ( str . endsWith ( '\r\n' ) ) {
136
+ return str . substring ( 0 , str . length - 2 ) ;
137
+ }
138
+ if ( str . endsWith ( '\n' ) ) {
139
+ return str . substring ( 0 , str . length - 1 ) ;
140
+ }
141
+ return str ;
107
142
}
0 commit comments