初步完成
This commit is contained in:
@@ -100,6 +100,43 @@ function getNodeSourceText(source, node) {
|
||||
return source.substring(start, end)
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取字符串的缩进级别(空格数)
|
||||
*/
|
||||
function getIndentLevel(str) {
|
||||
const match = str.match(/^(\s*)/)
|
||||
return match ? match[1].length : 0
|
||||
}
|
||||
|
||||
/**
|
||||
* 调整源代码的缩进,保持相对缩进关系
|
||||
* @param {string} sourceText - 源元素文本
|
||||
* @param {string} targetIndent - 目标位置的缩进
|
||||
* @returns {string} - 调整后的文本
|
||||
*/
|
||||
function adjustIndentation(sourceText, targetIndent) {
|
||||
const lines = sourceText.split('\n')
|
||||
if (lines.length === 0) return sourceText
|
||||
|
||||
// 获取第一行的原始缩进
|
||||
const firstLineIndent = getIndentLevel(lines[0])
|
||||
|
||||
// 计算缩进差值
|
||||
const targetIndentLevel = targetIndent.length
|
||||
const indentDiff = targetIndentLevel - firstLineIndent
|
||||
|
||||
// 对每一行应用缩进差值
|
||||
const adjustedLines = lines.map((line, index) => {
|
||||
if (!line.trim()) return '' // 空行保持空
|
||||
|
||||
const currentIndent = getIndentLevel(line)
|
||||
const newIndent = Math.max(0, currentIndent + indentDiff)
|
||||
return ' '.repeat(newIndent) + line.trimStart()
|
||||
})
|
||||
|
||||
return adjustedLines.join('\n')
|
||||
}
|
||||
|
||||
/**
|
||||
* 将元素移动到目标元素内部
|
||||
* 如果目标已有子元素,则放到最后
|
||||
@@ -109,81 +146,62 @@ function moveElementInside(templateContent, sourceNode, targetNode, sourcePath,
|
||||
const sourceEnd = sourceNode.loc.end.offset
|
||||
const sourceText = templateContent.substring(sourceStart, sourceEnd)
|
||||
|
||||
// 找到源元素行的开始位置(用于删除整行)
|
||||
const sourceLineStart = templateContent.lastIndexOf('\n', sourceStart - 1) + 1
|
||||
// 1. 计算删除范围
|
||||
let deleteStart = sourceStart
|
||||
let deleteEnd = sourceEnd
|
||||
|
||||
// 获取目标元素的缩进(子元素需要比父元素多一层缩进)
|
||||
// 向前查找这一行的开始
|
||||
const lineStart = templateContent.lastIndexOf('\n', sourceStart - 1) + 1
|
||||
const beforeElement = templateContent.substring(lineStart, sourceStart)
|
||||
|
||||
// 如果元素前面只有空白,则从行开始删除
|
||||
if (/^\s*$/.test(beforeElement)) {
|
||||
deleteStart = lineStart
|
||||
}
|
||||
|
||||
// 向后查找是否有换行
|
||||
if (templateContent[sourceEnd] === '\n') {
|
||||
deleteEnd = sourceEnd + 1
|
||||
}
|
||||
|
||||
// 2. 获取目标元素的缩进(子元素需要比父元素多一层缩进)
|
||||
const targetLineStart = templateContent.lastIndexOf('\n', targetNode.loc.start.offset - 1) + 1
|
||||
const targetIndent = templateContent.substring(targetLineStart, targetNode.loc.start.offset)
|
||||
const childIndent = targetIndent + ' ' // 子元素缩进
|
||||
const childIndent = targetIndent + ' '
|
||||
|
||||
// 找到目标元素的结束标签位置
|
||||
// 目标元素内容的结束位置(结束标签前)
|
||||
// 3. 找到目标元素的结束标签位置
|
||||
const targetText = templateContent.substring(targetNode.loc.start.offset, targetNode.loc.end.offset)
|
||||
const targetTag = targetNode.tag // 'el-row' 或 'el-col'
|
||||
const closeTagPattern = `</${targetTag}>`
|
||||
const closeTagPattern = `</${targetNode.tag}>`
|
||||
const closeTagIndex = targetText.lastIndexOf(closeTagPattern)
|
||||
|
||||
if (closeTagIndex === -1) {
|
||||
console.error('[moveElementInside] 未找到结束标签:', targetTag)
|
||||
console.error('[moveElementInside] 未找到结束标签:', targetNode.tag)
|
||||
return templateContent
|
||||
}
|
||||
|
||||
// 计算插入位置(在结束标签之前)
|
||||
const insertPositionInTarget = closeTagIndex
|
||||
const insertPositionInTemplate = targetNode.loc.start.offset + insertPositionInTarget
|
||||
// 插入位置(在结束标签之前)
|
||||
const insertPosition = targetNode.loc.start.offset + closeTagIndex
|
||||
|
||||
// 调整源元素的缩进
|
||||
const sourceLines = sourceText.split('\n')
|
||||
const adjustedSourceLines = sourceLines.map((line, index) => {
|
||||
if (index === 0) {
|
||||
return childIndent + line.trimStart()
|
||||
}
|
||||
// 保持相对缩进
|
||||
return childIndent + line.trimStart()
|
||||
})
|
||||
const adjustedSourceText = adjustedSourceLines.join('\n')
|
||||
// 4. 调整源元素的缩进(保持相对缩进)
|
||||
const adjustedSourceText = adjustIndentation(sourceText, childIndent)
|
||||
|
||||
// 根据源和目标的位置关系进行操作
|
||||
if (sourceStart < targetNode.loc.start.offset) {
|
||||
// 源在目标前面:先删除源,再插入
|
||||
|
||||
// 删除源元素
|
||||
const beforeSource = templateContent.substring(0, sourceLineStart)
|
||||
const afterSource = templateContent.substring(sourceEnd)
|
||||
let afterSourceTrimmed = afterSource.startsWith('\n') ? afterSource.substring(1) : afterSource
|
||||
const withoutSource = beforeSource + afterSourceTrimmed
|
||||
|
||||
// 计算新的插入位置
|
||||
const removedLength = templateContent.length - withoutSource.length
|
||||
const newInsertPosition = insertPositionInTemplate - removedLength
|
||||
|
||||
// 插入到目标内部
|
||||
const insertText = adjustedSourceText + '\n' + targetIndent
|
||||
|
||||
return withoutSource.substring(0, newInsertPosition) +
|
||||
insertText +
|
||||
withoutSource.substring(newInsertPosition)
|
||||
// 构建插入文本
|
||||
const insertText = adjustedSourceText + '\n' + targetIndent
|
||||
|
||||
// 5. 执行操作(从后向前处理)
|
||||
if (deleteStart > insertPosition) {
|
||||
// 删除位置在插入位置后面:先删除,再插入
|
||||
let result = templateContent.substring(0, deleteStart) + templateContent.substring(deleteEnd)
|
||||
result = result.substring(0, insertPosition) + insertText + result.substring(insertPosition)
|
||||
return result
|
||||
} else {
|
||||
// 源在目标后面:先插入,再删除
|
||||
// 删除位置在插入位置前面:先插入,再删除
|
||||
const deletedLength = deleteEnd - deleteStart
|
||||
const adjustedInsertPos = insertPosition - deletedLength
|
||||
|
||||
// 插入到目标内部
|
||||
const insertText = adjustedSourceText + '\n' + targetIndent
|
||||
const withInsert = templateContent.substring(0, insertPositionInTemplate) +
|
||||
insertText +
|
||||
templateContent.substring(insertPositionInTemplate)
|
||||
|
||||
// 计算源元素新位置
|
||||
const insertedLength = insertText.length
|
||||
const newSourceLineStart = sourceLineStart + insertedLength
|
||||
const newSourceEnd = sourceEnd + insertedLength
|
||||
|
||||
// 删除源元素
|
||||
const beforeNewSource = withInsert.substring(0, newSourceLineStart)
|
||||
const afterNewSource = withInsert.substring(newSourceEnd)
|
||||
let afterTrimmed = afterNewSource.startsWith('\n') ? afterNewSource.substring(1) : afterNewSource
|
||||
|
||||
return beforeNewSource + afterTrimmed
|
||||
let result = templateContent.substring(0, deleteStart) + templateContent.substring(deleteEnd)
|
||||
result = result.substring(0, adjustedInsertPos) + insertText + result.substring(adjustedInsertPos)
|
||||
return result
|
||||
}
|
||||
}
|
||||
|
||||
@@ -222,21 +240,16 @@ export function moveElement(vueContent, options) {
|
||||
const templateContent = templateBlock.content
|
||||
|
||||
// 找到 <template> 内容在原文件中的实际位置
|
||||
// templateBlock.loc 指向整个 <template>...</template> 块
|
||||
// 我们需要找到内容的起始和结束位置
|
||||
const templateTagStart = vueContent.indexOf('<template')
|
||||
const templateTagEnd = vueContent.indexOf('>', templateTagStart) + 1
|
||||
const templateCloseStart = vueContent.indexOf('</template>', templateTagEnd)
|
||||
|
||||
// template内容在原文件中的位置
|
||||
const contentStart = templateTagEnd
|
||||
const contentEnd = templateCloseStart
|
||||
|
||||
// 2. 解析template获取AST
|
||||
const templateAST = parseTemplate(templateContent, {
|
||||
// 保留注释
|
||||
comments: true,
|
||||
// 保留空白(用于保持格式)
|
||||
whitespace: 'preserve'
|
||||
})
|
||||
|
||||
@@ -245,20 +258,14 @@ export function moveElement(vueContent, options) {
|
||||
const targetNode = findElementByPath(templateAST, targetPath)
|
||||
|
||||
if (!sourceNode) {
|
||||
return {
|
||||
success: false,
|
||||
error: `未找到源元素: ${sourcePath}`
|
||||
}
|
||||
return { success: false, error: `未找到源元素: ${sourcePath}` }
|
||||
}
|
||||
|
||||
if (!targetNode) {
|
||||
return {
|
||||
success: false,
|
||||
error: `未找到目标元素: ${targetPath}`
|
||||
}
|
||||
return { success: false, error: `未找到目标元素: ${targetPath}` }
|
||||
}
|
||||
|
||||
// 4. 获取元素在template中的位置
|
||||
// 4. 获取元素在template中的精确位置
|
||||
const sourceStart = sourceNode.loc.start.offset
|
||||
const sourceEnd = sourceNode.loc.end.offset
|
||||
const sourceText = templateContent.substring(sourceStart, sourceEnd)
|
||||
@@ -270,18 +277,32 @@ export function moveElement(vueContent, options) {
|
||||
console.log(`[moveElement] 目标: ${targetPath} [${targetStart}-${targetEnd}]`)
|
||||
console.log(`[moveElement] 方向: ${direction}`)
|
||||
|
||||
// 5. 计算插入位置和新内容
|
||||
let newTemplateContent
|
||||
// 5. 计算删除范围(包括前面的缩进和后面的换行)
|
||||
// 找到源元素所在行的开始
|
||||
let deleteStart = sourceStart
|
||||
let deleteEnd = sourceEnd
|
||||
|
||||
// 检测源元素前面的空白(用于保持缩进)
|
||||
const sourceLineStart = templateContent.lastIndexOf('\n', sourceStart - 1) + 1
|
||||
const sourceIndent = templateContent.substring(sourceLineStart, sourceStart)
|
||||
// 向前查找这一行的开始(换行符后的第一个字符)
|
||||
const lineStart = templateContent.lastIndexOf('\n', sourceStart - 1) + 1
|
||||
const beforeElement = templateContent.substring(lineStart, sourceStart)
|
||||
|
||||
// 检测目标元素的缩进
|
||||
// 如果元素前面只有空白,则从行开始删除
|
||||
if (/^\s*$/.test(beforeElement)) {
|
||||
deleteStart = lineStart
|
||||
}
|
||||
|
||||
// 向后查找是否有换行
|
||||
if (templateContent[sourceEnd] === '\n') {
|
||||
deleteEnd = sourceEnd + 1
|
||||
}
|
||||
|
||||
// 获取目标元素的缩进
|
||||
const targetLineStart = templateContent.lastIndexOf('\n', targetStart - 1) + 1
|
||||
const targetIndent = templateContent.substring(targetLineStart, targetStart)
|
||||
|
||||
// 处理 'inside' 方向(放入目标元素内部)
|
||||
let newTemplateContent
|
||||
|
||||
// 6. 处理 'inside' 方向
|
||||
if (direction === 'inside') {
|
||||
newTemplateContent = moveElementInside(
|
||||
templateContent,
|
||||
@@ -290,94 +311,51 @@ export function moveElement(vueContent, options) {
|
||||
sourcePath,
|
||||
targetPath
|
||||
)
|
||||
} else if (sourceStart < targetStart) {
|
||||
// 源在目标前面:先处理目标位置,再删除源
|
||||
const insertPosition = (direction === 'bottom' || direction === 'right')
|
||||
? targetEnd
|
||||
: targetStart
|
||||
|
||||
// 构建新内容
|
||||
let parts = []
|
||||
|
||||
// 删除源元素(包括前后的空白)
|
||||
const beforeSource = templateContent.substring(0, sourceLineStart)
|
||||
const afterSource = templateContent.substring(sourceEnd)
|
||||
|
||||
// 合并时跳过源元素后的换行
|
||||
let afterSourceTrimmed = afterSource
|
||||
if (afterSource.startsWith('\n')) {
|
||||
afterSourceTrimmed = afterSource.substring(1)
|
||||
}
|
||||
|
||||
const withoutSource = beforeSource + afterSourceTrimmed
|
||||
|
||||
// 计算新的目标位置(因为删除了源,位置会变化)
|
||||
const removedLength = templateContent.length - withoutSource.length
|
||||
const newInsertPosition = insertPosition - removedLength
|
||||
|
||||
// 在新位置插入源元素
|
||||
const insertText = (direction === 'bottom' || direction === 'right')
|
||||
? '\n' + targetIndent + sourceText
|
||||
: sourceText + '\n' + targetIndent
|
||||
|
||||
newTemplateContent =
|
||||
withoutSource.substring(0, newInsertPosition) +
|
||||
insertText +
|
||||
withoutSource.substring(newInsertPosition)
|
||||
|
||||
} else {
|
||||
// 源在目标后面:先插入,再删除
|
||||
const insertPosition = (direction === 'bottom' || direction === 'right')
|
||||
? targetEnd
|
||||
: targetStart
|
||||
// 7. 计算插入位置
|
||||
const insertAfterTarget = (direction === 'bottom' || direction === 'right')
|
||||
const insertPosition = insertAfterTarget ? targetEnd : targetStart
|
||||
|
||||
// 先在目标位置插入
|
||||
const insertText = (direction === 'bottom' || direction === 'right')
|
||||
? '\n' + targetIndent + sourceText
|
||||
: sourceText + '\n' + targetIndent
|
||||
// 构建插入文本(使用目标缩进,保持相对缩进)
|
||||
const adjustedSource = adjustIndentation(sourceText, targetIndent)
|
||||
|
||||
const withInsert =
|
||||
templateContent.substring(0, insertPosition) +
|
||||
insertText +
|
||||
templateContent.substring(insertPosition)
|
||||
const insertText = insertAfterTarget
|
||||
? '\n' + adjustedSource
|
||||
: adjustedSource + '\n'
|
||||
|
||||
// 计算源元素新位置(因为插入了内容,位置会变化)
|
||||
const insertedLength = insertText.length
|
||||
const newSourceStart = sourceLineStart + insertedLength
|
||||
const newSourceEnd = sourceEnd + insertedLength
|
||||
|
||||
// 删除源元素
|
||||
const beforeNewSource = withInsert.substring(0, newSourceStart)
|
||||
const afterNewSource = withInsert.substring(newSourceEnd)
|
||||
|
||||
// 跳过换行
|
||||
let afterTrimmed = afterNewSource
|
||||
if (afterTrimmed.startsWith('\n')) {
|
||||
afterTrimmed = afterTrimmed.substring(1)
|
||||
// 8. 执行操作(从后向前处理,避免偏移量问题)
|
||||
if (deleteStart > insertPosition) {
|
||||
// 删除位置在插入位置后面:先删除,再插入
|
||||
newTemplateContent = templateContent.substring(0, deleteStart) +
|
||||
templateContent.substring(deleteEnd)
|
||||
newTemplateContent = newTemplateContent.substring(0, insertPosition) +
|
||||
insertText +
|
||||
newTemplateContent.substring(insertPosition)
|
||||
} else {
|
||||
// 删除位置在插入位置前面:先插入,再删除(需要调整偏移)
|
||||
const deletedLength = deleteEnd - deleteStart
|
||||
const adjustedInsertPos = insertPosition - deletedLength
|
||||
|
||||
newTemplateContent = templateContent.substring(0, deleteStart) +
|
||||
templateContent.substring(deleteEnd)
|
||||
newTemplateContent = newTemplateContent.substring(0, adjustedInsertPos) +
|
||||
insertText +
|
||||
newTemplateContent.substring(adjustedInsertPos)
|
||||
}
|
||||
|
||||
newTemplateContent = beforeNewSource + afterTrimmed
|
||||
}
|
||||
|
||||
// 6. 重建Vue文件:保留 <template> 标签,只替换内容
|
||||
const beforeContent = vueContent.substring(0, contentStart)
|
||||
const afterContent = vueContent.substring(contentEnd)
|
||||
|
||||
const newVueContent = beforeContent + newTemplateContent + afterContent
|
||||
// 9. 重建Vue文件
|
||||
const newVueContent = vueContent.substring(0, contentStart) +
|
||||
newTemplateContent +
|
||||
vueContent.substring(contentEnd)
|
||||
|
||||
console.log('[moveElement] 文件更新成功')
|
||||
|
||||
return {
|
||||
success: true,
|
||||
content: newVueContent
|
||||
}
|
||||
return { success: true, content: newVueContent }
|
||||
|
||||
} catch (error) {
|
||||
console.error('[moveElement] 错误:', error)
|
||||
return {
|
||||
success: false,
|
||||
error: error.message
|
||||
}
|
||||
return { success: false, error: error.message }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user