UNPKG

4.92 kBJavaScriptView Raw
1const hasOwnProperty = require('has-own-prop')
2const {isArray} = require('core-util-is')
3
4const {
5 SYMBOL_PREFIXES,
6
7 UNDEFINED,
8
9 symbol,
10 define,
11 copy_comments
12} = require('./common')
13
14
15const swap_comments = (array, from, to) => {
16 if (from === to) {
17 return
18 }
19
20 SYMBOL_PREFIXES.forEach(prefix => {
21 const target_prop = symbol(prefix, to)
22 if (!hasOwnProperty(array, target_prop)) {
23 copy_comments(array, array, to, from, prefix)
24 return
25 }
26
27 const comments = array[target_prop]
28 copy_comments(array, array, to, from, prefix)
29 define(array, symbol(prefix, from), comments)
30 })
31}
32
33const reverse_comments = array => {
34 const {length} = array
35 let i = 0
36 const max = length / 2
37
38 for (; i < max; i ++) {
39 swap_comments(array, i, length - i - 1)
40 }
41}
42
43const move_comment = (target, source, i, offset, remove) => {
44 SYMBOL_PREFIXES.forEach(prefix => {
45 copy_comments(target, source, i + offset, i, prefix, remove)
46 })
47}
48
49const move_comments = (
50 // `Array` target array
51 target,
52 // `Array` source array
53 source,
54 // `number` start index
55 start,
56 // `number` number of indexes to move
57 count,
58 // `number` offset to move
59 offset,
60 // `boolean` whether should remove the comments from source
61 remove
62) => {
63 if (offset > 0) {
64 let i = count
65 // | count | offset |
66 // source: -------------
67 // target: -------------
68 // | remove |
69 // => remove === offset
70
71 // From [count - 1, 0]
72 while (i -- > 0) {
73 move_comment(target, source, start + i, offset, remove && i < offset)
74 }
75 return
76 }
77
78 let i = 0
79 const min_remove = count + offset
80 // | remove | count |
81 // -------------
82 // -------------
83 // | offset |
84
85 // From [0, count - 1]
86 while (i < count) {
87 const ii = i ++
88 move_comment(target, source, start + ii, offset, remove && i >= min_remove)
89 }
90}
91
92class CommentArray extends Array {
93 // - deleteCount + items.length
94
95 // We should avoid `splice(begin, deleteCount, ...items)`,
96 // because `splice(0, undefined)` is not equivalent to `splice(0)`,
97 // as well as:
98 // - slice
99 splice (...args) {
100 const {length} = this
101 const ret = super.splice(...args)
102
103 // #16
104 // If no element removed, we might still need to move comments,
105 // because splice could add new items
106
107 // if (!ret.length) {
108 // return ret
109 // }
110
111 // JavaScript syntax is silly
112 // eslint-disable-next-line prefer-const
113 let [begin, deleteCount, ...items] = args
114
115 if (begin < 0) {
116 begin += length
117 }
118
119 if (arguments.length === 1) {
120 deleteCount = length - begin
121 } else {
122 deleteCount = Math.min(length - begin, deleteCount)
123 }
124
125 const {
126 length: item_length
127 } = items
128
129 // itemsToDelete: -
130 // itemsToAdd: +
131 // | dc | count |
132 // =======-------------============
133 // =======++++++============
134 // | il |
135 const offset = item_length - deleteCount
136 const start = begin + deleteCount
137 const count = length - start
138
139 move_comments(this, this, start, count, offset, true)
140
141 return ret
142 }
143
144 slice (...args) {
145 const {length} = this
146 const array = super.slice(...args)
147 if (!array.length) {
148 return new CommentArray()
149 }
150
151 let [begin, before] = args
152
153 // Ref:
154 // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/slice
155 if (before === UNDEFINED) {
156 before = length
157 } else if (before < 0) {
158 before += length
159 }
160
161 if (begin < 0) {
162 begin += length
163 } else if (begin === UNDEFINED) {
164 begin = 0
165 }
166
167 move_comments(array, this, begin, before - begin, - begin)
168
169 return array
170 }
171
172 unshift (...items) {
173 const {length} = this
174 const ret = super.unshift(...items)
175 const {
176 length: items_length
177 } = items
178
179 if (items_length > 0) {
180 move_comments(this, this, 0, length, items_length, true)
181 }
182
183 return ret
184 }
185
186 shift () {
187 const ret = super.shift()
188 const {length} = this
189
190 move_comments(this, this, 1, length, - 1, true)
191
192 return ret
193 }
194
195 reverse () {
196 super.reverse()
197
198 reverse_comments(this)
199
200 return this
201 }
202
203 pop () {
204 const ret = super.pop()
205
206 // Removes comments
207 const {length} = this
208 SYMBOL_PREFIXES.forEach(prefix => {
209 const prop = symbol(prefix, length)
210 delete this[prop]
211 })
212
213 return ret
214 }
215
216 concat (...items) {
217 let {length} = this
218 const ret = super.concat(...items)
219
220 if (!items.length) {
221 return ret
222 }
223
224 items.forEach(item => {
225 const prev = length
226 length += isArray(item)
227 ? item.length
228 : 1
229
230 if (!(item instanceof CommentArray)) {
231 return
232 }
233
234 move_comments(ret, item, 0, item.length, prev)
235 })
236
237 return ret
238 }
239}
240
241module.exports = {
242 CommentArray
243}