@@ -114,42 +114,74 @@ define(function (require, exports, module) {
114114 }
115115
116116 /**
117- * this function handles the text edit in the source code when user updates the text in the live preview
118- *
119- * @param {Object } message - the message object
120- * - livePreviewEditEnabled: true
121- * - livePreviewTextEdit: true
122- * - element: element
123- * - newContent: element.outerHTML (the edited content from live preview)
124- * - tagId: Number (data-brackets-id of the edited element)
125- * - isEditSuccessful: boolean (false when user pressed Escape to cancel, otherwise true always)
117+ * helper function to get editor and validate basic requirements
118+ * @param {Number } tagId - the data-brackets-id of the element
126119 */
127- function _editTextInSource ( message ) {
120+ function _getEditorAndValidate ( tagId ) {
128121 const currLiveDoc = LiveDevMultiBrowser . getCurrentLiveDoc ( ) ;
129- if ( ! currLiveDoc || ! currLiveDoc . editor || ! message . tagId ) {
130- return ;
122+ if ( ! currLiveDoc || ! currLiveDoc . editor ) {
123+ return null ;
131124 }
125+ // for undo/redo operations, tagId might not be needed, so we only check it if provided
126+ if ( tagId !== undefined && ! tagId ) {
127+ return null ;
128+ }
129+ return currLiveDoc . editor ;
130+ }
132131
133- const editor = currLiveDoc . editor ;
134-
132+ /**
133+ * helper function to get element range from tagId
134+ *
135+ * @param {Object } editor - the editor instance
136+ * @param {Number } tagId - the data-brackets-id of the element
137+ * @returns {Object|null } - object with startPos and endPos, or null if not found
138+ */
139+ function _getElementRange ( editor , tagId ) {
135140 // get the start range from the getPositionFromTagId function
136141 // and we get the end range from the findMatchingTag function
137142 // NOTE: we cannot get the end range from getPositionFromTagId
138143 // because on non-beautified code getPositionFromTagId may not provide correct end position
139- const startRange = HTMLInstrumentation . getPositionFromTagId ( editor , message . tagId ) ;
144+ const startRange = HTMLInstrumentation . getPositionFromTagId ( editor , tagId ) ;
140145 if ( ! startRange ) {
141- return ;
146+ return null ;
142147 }
143148
144149 const endRange = CodeMirror . findMatchingTag ( editor . _codeMirror , startRange . from ) ;
145150 if ( ! endRange ) {
146- return ;
151+ return null ;
147152 }
148153
149154 const startPos = startRange . from ;
150155 // for empty tags endRange.close might not exist, for ex: img tag
151156 const endPos = endRange . close ? endRange . close . to : endRange . open . to ;
152157
158+ return { startPos, endPos } ;
159+ }
160+
161+ /**
162+ * this function handles the text edit in the source code when user updates the text in the live preview
163+ *
164+ * @param {Object } message - the message object
165+ * - livePreviewEditEnabled: true
166+ * - livePreviewTextEdit: true
167+ * - element: element
168+ * - newContent: element.outerHTML (the edited content from live preview)
169+ * - tagId: Number (data-brackets-id of the edited element)
170+ * - isEditSuccessful: boolean (false when user pressed Escape to cancel, otherwise true always)
171+ */
172+ function _editTextInSource ( message ) {
173+ const editor = _getEditorAndValidate ( message . tagId ) ;
174+ if ( ! editor ) {
175+ return ;
176+ }
177+
178+ const range = _getElementRange ( editor , message . tagId ) ;
179+ if ( ! range ) {
180+ return ;
181+ }
182+
183+ const { startPos, endPos } = range ;
184+
153185 const text = editor . getTextBetween ( startPos , endPos ) ;
154186
155187 // if the edit was cancelled (mainly by pressing Escape key)
@@ -176,33 +208,17 @@ define(function (require, exports, module) {
176208 */
177209 function _duplicateElementInSourceByTagId ( tagId ) {
178210 // this is to get the currently live document that is being served in the live preview
179- const currLiveDoc = LiveDevMultiBrowser . getCurrentLiveDoc ( ) ;
180- if ( ! currLiveDoc ) {
211+ const editor = _getEditorAndValidate ( tagId ) ;
212+ if ( ! editor ) {
181213 return ;
182214 }
183215
184- const editor = currLiveDoc . editor ;
185- if ( ! editor || ! tagId ) {
216+ const range = _getElementRange ( editor , tagId ) ;
217+ if ( ! range ) {
186218 return ;
187219 }
188220
189- // get the start range from the getPositionFromTagId function
190- // and we get the end range from the findMatchingTag function
191- // NOTE: we cannot get the end range from getPositionFromTagId
192- // because on non-beautified code getPositionFromTagId may not provide correct end position
193- const startRange = HTMLInstrumentation . getPositionFromTagId ( editor , tagId ) ;
194- if ( ! startRange ) {
195- return ;
196- }
197-
198- const endRange = CodeMirror . findMatchingTag ( editor . _codeMirror , startRange . from ) ;
199- if ( ! endRange ) {
200- return ;
201- }
202-
203- const startPos = startRange . from ;
204- // for empty tags endRange.close might not exist, for ex: img tag
205- const endPos = endRange . close ? endRange . close . to : endRange . open . to ;
221+ const { startPos, endPos } = range ;
206222
207223 // this is the actual source code for the element that we need to duplicate
208224 const text = editor . getTextBetween ( startPos , endPos ) ;
@@ -237,33 +253,17 @@ define(function (require, exports, module) {
237253 */
238254 function _deleteElementInSourceByTagId ( tagId ) {
239255 // this is to get the currently live document that is being served in the live preview
240- const currLiveDoc = LiveDevMultiBrowser . getCurrentLiveDoc ( ) ;
241- if ( ! currLiveDoc ) {
242- return ;
243- }
244-
245- const editor = currLiveDoc . editor ;
246- if ( ! editor || ! tagId ) {
256+ const editor = _getEditorAndValidate ( tagId ) ;
257+ if ( ! editor ) {
247258 return ;
248259 }
249260
250- // get the start range from the getPositionFromTagId function
251- // and we get the end range from the findMatchingTag function
252- // NOTE: we cannot get the end range from getPositionFromTagId
253- // because on non-beautified code getPositionFromTagId may not provide correct end position
254- const startRange = HTMLInstrumentation . getPositionFromTagId ( editor , tagId ) ;
255- if ( ! startRange ) {
261+ const range = _getElementRange ( editor , tagId ) ;
262+ if ( ! range ) {
256263 return ;
257264 }
258265
259- const endRange = CodeMirror . findMatchingTag ( editor . _codeMirror , startRange . from ) ;
260- if ( ! endRange ) {
261- return ;
262- }
263-
264- const startPos = startRange . from ;
265- // for empty tags endRange.close might not exist, for ex: img tag
266- const endPos = endRange . close ? endRange . close . to : endRange . open . to ;
266+ const { startPos, endPos } = range ;
267267
268268 editor . document . batchOperation ( function ( ) {
269269 editor . replaceRange ( "" , startPos , endPos ) ;
@@ -348,78 +348,60 @@ define(function (require, exports, module) {
348348 */
349349 function _moveElementInSource ( sourceId , targetId , insertAfter , insertInside = false ) {
350350 // this is to get the currently live document that is being served in the live preview
351- const currLiveDoc = LiveDevMultiBrowser . getCurrentLiveDoc ( ) ;
352- if ( ! currLiveDoc ) {
351+ const editor = _getEditorAndValidate ( sourceId ) ;
352+ if ( ! editor || ! targetId ) {
353353 return ;
354354 }
355355
356- const editor = currLiveDoc . editor ;
357- if ( ! editor || ! sourceId || ! targetId ) {
356+ const sourceRange = _getElementRange ( editor , sourceId ) ;
357+ if ( ! sourceRange ) {
358358 return ;
359359 }
360360
361- // get the start range from the getPositionFromTagId function
362- // and we get the end range from the findMatchingTag function
363- // NOTE: we cannot get the end range from getPositionFromTagId
364- // because on non-beautified code getPositionFromTagId may not provide correct end position
365- const sourceStartRange = HTMLInstrumentation . getPositionFromTagId ( editor , sourceId ) ;
366- if ( ! sourceStartRange ) {
361+ const targetRange = _getElementRange ( editor , targetId ) ;
362+ if ( ! targetRange ) {
367363 return ;
368364 }
369365
370- const sourceEndRange = CodeMirror . findMatchingTag ( editor . _codeMirror , sourceStartRange . from ) ;
371- if ( ! sourceEndRange ) {
372- return ;
373- }
374-
375- const targetStartRange = HTMLInstrumentation . getPositionFromTagId ( editor , targetId ) ;
376- if ( ! targetStartRange ) {
377- return ;
378- }
379-
380- const targetEndRange = CodeMirror . findMatchingTag ( editor . _codeMirror , targetStartRange . from ) ;
381- if ( ! targetEndRange ) {
382- return ;
383- }
384-
385- const sourceRange = {
386- from : sourceStartRange . from ,
387- to : sourceEndRange . close ? sourceEndRange . close . to : sourceEndRange . open . to
366+ // convert to the format expected by the rest of the function
367+ const sourceRangeObj = {
368+ from : sourceRange . startPos ,
369+ to : sourceRange . endPos
388370 } ;
389371
390- const targetRange = {
391- from : targetStartRange . from ,
392- to : targetEndRange . close ? targetEndRange . close . to : targetEndRange . open . to
372+ const targetRangeObj = {
373+ from : targetRange . startPos ,
374+ to : targetRange . endPos
393375 } ;
394376
395- const sourceText = editor . getTextBetween ( sourceRange . from , sourceRange . to ) ;
396- const targetIndent = editor . getTextBetween ( { line : targetRange . from . line , ch : 0 } , targetRange . from ) ;
377+ const sourceText = editor . getTextBetween ( sourceRangeObj . from , sourceRangeObj . to ) ;
378+ const targetIndent = editor . getTextBetween ( { line : targetRangeObj . from . line , ch : 0 } , targetRangeObj . from ) ;
397379
398380 // Check if source is before target to determine order of operations
399381 // check if the source is before target or after the target
400382 // we need this because
401383 // If source is before target → we need to insert first, then remove
402384 // If target is before source → remove first, then insert
403385 const sourceBeforeTarget =
404- sourceRange . from . line < targetRange . from . line ||
405- ( sourceRange . from . line === targetRange . from . line && sourceRange . from . ch < targetRange . from . ch ) ;
386+ sourceRangeObj . from . line < targetRangeObj . from . line ||
387+ ( sourceRangeObj . from . line === targetRangeObj . from . line && sourceRangeObj . from . ch < targetRangeObj . from . ch ) ;
406388
407389 // creating a batch operation so that undo in live preview works fine
408390 editor . document . batchOperation ( function ( ) {
409391 if ( sourceBeforeTarget ) {
410392 // this handles the case when source is before target: insert first, then remove
411393 if ( insertInside ) {
412394 // Insert as child inside the target element
413- const targetText = editor . getTextBetween ( targetRange . from , targetRange . to ) ;
395+ const targetText = editor . getTextBetween ( targetRangeObj . from , targetRangeObj . to ) ;
414396 const targetElement = targetText . trim ( ) ;
415397
416398 // Find the position just after the opening tag
417399 const openingTagMatch = targetElement . match ( / ^ < [ ^ > ] * > / ) ;
418400 if ( openingTagMatch ) {
419401 const openingTag = openingTagMatch [ 0 ] ;
420402 const insertPos = {
421- line : targetRange . from . line ,
422- ch : targetRange . from . ch + openingTag . length
403+ line : targetRangeObj . from . line ,
404+ ch : targetRangeObj . from . ch + openingTag . length
423405 } ;
424406
425407 // Add proper indentation for child element
@@ -428,73 +410,57 @@ define(function (require, exports, module) {
428410 }
429411 } else if ( insertAfter ) {
430412 const insertPos = {
431- line : targetRange . to . line ,
432- ch : targetRange . to . ch
413+ line : targetRangeObj . to . line ,
414+ ch : targetRangeObj . to . ch
433415 } ;
434416 _insertElementWithIndentation ( editor , insertPos , true , targetIndent , sourceText ) ;
435417 } else {
436418 // insert before target
437- _insertElementWithIndentation ( editor , targetRange . from , false , targetIndent , sourceText ) ;
419+ _insertElementWithIndentation ( editor , targetRangeObj . from , false , targetIndent , sourceText ) ;
438420 }
439421
440422 // Now remove the source element (NOTE: the positions have shifted)
441- const updatedSourceStartRange = HTMLInstrumentation . getPositionFromTagId ( editor , sourceId ) ;
442- if ( updatedSourceStartRange ) {
443- const updatedSourceEndRange = CodeMirror . findMatchingTag (
444- editor . _codeMirror , updatedSourceStartRange . from
445- ) ;
446-
447- if ( updatedSourceEndRange ) {
448- const updatedSourceRange = {
449- from : updatedSourceStartRange . from ,
450- to : updatedSourceEndRange . close
451- ? updatedSourceEndRange . close . to
452- : updatedSourceEndRange . open . to
453- } ;
454- editor . replaceRange ( "" , updatedSourceRange . from , updatedSourceRange . to ) ;
455- _cleanupAfterRemoval ( editor , updatedSourceRange ) ;
456- }
423+ const updatedSourceRange = _getElementRange ( editor , sourceId ) ;
424+ if ( updatedSourceRange ) {
425+ const updatedSourceRangeObj = {
426+ from : updatedSourceRange . startPos ,
427+ to : updatedSourceRange . endPos
428+ } ;
429+ editor . replaceRange ( "" , updatedSourceRangeObj . from , updatedSourceRangeObj . to ) ;
430+ _cleanupAfterRemoval ( editor , updatedSourceRangeObj ) ;
457431 }
458432 } else {
459433 // This handles the case when target is before source: remove first, then insert
460434 // Store source range before removal
461- const originalSourceRange = { ...sourceRange } ;
435+ const originalSourceRange = { ...sourceRangeObj } ;
462436
463437 // Remove the source element first
464- editor . replaceRange ( "" , sourceRange . from , sourceRange . to ) ;
438+ editor . replaceRange ( "" , sourceRangeObj . from , sourceRangeObj . to ) ;
465439 _cleanupAfterRemoval ( editor , originalSourceRange ) ;
466440
467441 // Recalculate target range after source removal as the positions have shifted
468- const updatedTargetStartRange = HTMLInstrumentation . getPositionFromTagId ( editor , targetId ) ;
469- if ( ! updatedTargetStartRange ) {
470- return ;
471- }
472-
473- const updatedTargetEndRange = CodeMirror . findMatchingTag (
474- editor . _codeMirror , updatedTargetStartRange . from
475- ) ;
476-
477- if ( ! updatedTargetEndRange ) {
442+ const updatedTargetRange = _getElementRange ( editor , targetId ) ;
443+ if ( ! updatedTargetRange ) {
478444 return ;
479445 }
480446
481- const updatedTargetRange = {
482- from : updatedTargetStartRange . from ,
483- to : updatedTargetEndRange . close ? updatedTargetEndRange . close . to : updatedTargetEndRange . open . to
447+ const updatedTargetRangeObj = {
448+ from : updatedTargetRange . startPos ,
449+ to : updatedTargetRange . endPos
484450 } ;
485451
486452 if ( insertInside ) {
487453 // Insert as child inside the target element
488- const targetText = editor . getTextBetween ( updatedTargetRange . from , updatedTargetRange . to ) ;
454+ const targetText = editor . getTextBetween ( updatedTargetRangeObj . from , updatedTargetRangeObj . to ) ;
489455 const targetElement = targetText . trim ( ) ;
490456
491457 // Find the position just after the opening tag
492458 const openingTagMatch = targetElement . match ( / ^ < [ ^ > ] * > / ) ;
493459 if ( openingTagMatch ) {
494460 const openingTag = openingTagMatch [ 0 ] ;
495461 const insertPos = {
496- line : updatedTargetRange . from . line ,
497- ch : updatedTargetRange . from . ch + openingTag . length
462+ line : updatedTargetRangeObj . from . line ,
463+ ch : updatedTargetRangeObj . from . ch + openingTag . length
498464 } ;
499465
500466 // Add proper indentation for child element
@@ -503,13 +469,13 @@ define(function (require, exports, module) {
503469 }
504470 } else if ( insertAfter ) {
505471 const insertPos = {
506- line : updatedTargetRange . to . line ,
507- ch : updatedTargetRange . to . ch
472+ line : updatedTargetRangeObj . to . line ,
473+ ch : updatedTargetRangeObj . to . ch
508474 } ;
509475 _insertElementWithIndentation ( editor , insertPos , true , targetIndent , sourceText ) ;
510476 } else {
511477 // Insert before target
512- _insertElementWithIndentation ( editor , updatedTargetRange . from , false , targetIndent , sourceText ) ;
478+ _insertElementWithIndentation ( editor , updatedTargetRangeObj . from , false , targetIndent , sourceText ) ;
513479 }
514480 }
515481 } ) ;
@@ -520,13 +486,11 @@ define(function (require, exports, module) {
520486 * @param {String } undoOrRedo - "undo" when to undo, and "redo" for redo
521487 */
522488 function handleUndoRedoOperation ( undoOrRedo ) {
523- const currLiveDoc = LiveDevMultiBrowser . getCurrentLiveDoc ( ) ;
524- if ( ! currLiveDoc || ! currLiveDoc . editor ) {
489+ const editor = _getEditorAndValidate ( ) ; // no tagId needed for undo/redo
490+ if ( ! editor ) {
525491 return ;
526492 }
527493
528- const editor = currLiveDoc . editor ;
529-
530494 if ( undoOrRedo === "undo" ) {
531495 editor . undo ( ) ;
532496 } else if ( undoOrRedo === "redo" ) {
0 commit comments