Skip to content

Commit b9d37ee

Browse files
committed
refactor: make code more DRY
1 parent aae7c19 commit b9d37ee

File tree

1 file changed

+105
-141
lines changed

1 file changed

+105
-141
lines changed

src/LiveDevelopment/LivePreviewEdit.js

Lines changed: 105 additions & 141 deletions
Original file line numberDiff line numberDiff line change
@@ -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

Comments
 (0)