@@ -36,13 +36,16 @@ import {
3636 isJSONObject ,
3737 isJSONPatchAdd ,
3838 isJSONPatchReplace ,
39+ type JSONPatchOperation ,
3940 type JSONPath ,
4041 parsePath
4142} from 'immutable-json-patch'
4243import { isObject , isObjectOrArray } from '$lib/utils/typeUtils.js'
4344import { expandAll , expandNone , expandPath , expandSmart } from '$lib/logic/documentState.js'
4445import { initial , isEmpty , last } from 'lodash-es'
4546import { fromTableCellPosition , toTableCellPosition } from '$lib/logic/table.js'
47+ import { parsePartialJson } from '$lib/utils/jsonUtils'
48+ import { MAX_MULTILINE_PASTE_SIZE } from '$lib/constants'
4649
4750const debug = createDebug ( 'jsoneditor:actions' )
4851
@@ -114,6 +117,7 @@ interface OnPasteAction {
114117 parser : JSONParser
115118 onPatch : OnPatch
116119 onChangeText : OnChangeText
120+ onPasteMultilineText : ( pastedText : string ) => void
117121 openRepairModal : RepairModalCallback
118122}
119123
@@ -126,6 +130,7 @@ export function onPaste({
126130 parser,
127131 onPatch,
128132 onChangeText,
133+ onPasteMultilineText,
129134 openRepairModal
130135} : OnPasteAction ) {
131136 if ( readOnly ) {
@@ -138,7 +143,9 @@ export function onPaste({
138143
139144 const operations = insert ( json , ensureSelection , pastedText , parser )
140145
141- debug ( 'paste' , { pastedText, operations, ensureSelection } )
146+ const pasteMultilineText = isMultilineTextPastedAsArray ( clipboardText , operations , parser )
147+
148+ debug ( 'paste' , { pastedText, operations, ensureSelection, pasteMultilineText } )
142149
143150 onPatch ( operations , ( patchedJson , patchedState ) => {
144151 let updatedState = patchedState
@@ -159,6 +166,10 @@ export function onPaste({
159166 state : updatedState
160167 }
161168 } )
169+
170+ if ( pasteMultilineText ) {
171+ onPasteMultilineText ( pastedText )
172+ }
162173 } else {
163174 // no json: empty document, or the contents is invalid text
164175 debug ( 'paste text' , { pastedText } )
@@ -186,6 +197,48 @@ export function onPaste({
186197 }
187198}
188199
200+ /**
201+ * When pasting text, we cannot always know how whether the text was intended as
202+ * a list with items that should be parsed into a JSON Array (after jsonrepair),
203+ * or a text with multiple lines that should be parsed into a single string.
204+ *
205+ * This function checks whether we're dealing with such a case, after which
206+ * we can show a message to the user asking about the intended behavior.
207+ */
208+ export function isMultilineTextPastedAsArray (
209+ clipboardText : string ,
210+ operators : JSONPatchOperation [ ] ,
211+ parser : JSONParser ,
212+ maxSize = MAX_MULTILINE_PASTE_SIZE
213+ ) : boolean {
214+ if ( clipboardText . length > maxSize ) {
215+ // we don't want this feature detecting multiline text to impact performance, hence this max
216+ return false
217+ }
218+
219+ const containsNewline = / \n / . test ( clipboardText )
220+ if ( ! containsNewline ) {
221+ return false
222+ }
223+
224+ const replaceArrayOperation = operators . some (
225+ ( operator ) => operator . op === 'replace' && Array . isArray ( operator . value )
226+ )
227+ const multipleAddOperations = operators . filter ( ( operator ) => operator . op === 'add' ) . length > 1
228+ const pastingArray = replaceArrayOperation || multipleAddOperations
229+ if ( ! pastingArray ) {
230+ return false
231+ }
232+
233+ try {
234+ parsePartialJson ( clipboardText , parser . parse )
235+
236+ return false
237+ } catch {
238+ return true
239+ }
240+ }
241+
189242export interface OnRemoveAction {
190243 json : unknown | undefined
191244 text : string | undefined
0 commit comments