mirror of
https://github.com/goldenfishs/MRobot.git
synced 2026-03-31 21:07:14 +08:00
更新
This commit is contained in:
@@ -6,42 +6,8 @@ from app.tools.analyzing_ioc import analyzing_ioc
|
||||
from app.tools.code_generator import CodeGenerator
|
||||
import os
|
||||
import csv
|
||||
import shutil
|
||||
import re
|
||||
|
||||
def preserve_all_user_regions(new_code, old_code):
|
||||
""" Preserves all user-defined regions in the new code based on the old code.
|
||||
This function uses regex to find user-defined regions in the old code and replaces them in the new code.
|
||||
Args:
|
||||
new_code (str): The new code content.
|
||||
old_code (str): The old code content.
|
||||
Returns:
|
||||
str: The new code with preserved user-defined regions.
|
||||
"""
|
||||
import re
|
||||
pattern = re.compile(
|
||||
r"/\*\s*(USER [A-Z0-9_ ]+)\s*BEGIN\s*\*/(.*?)/\*\s*\1\s*END\s*\*/",
|
||||
re.DOTALL
|
||||
)
|
||||
old_regions = {m.group(1): m.group(2) for m in pattern.finditer(old_code or "")}
|
||||
def repl(m):
|
||||
region = m.group(1)
|
||||
old_content = old_regions.get(region)
|
||||
if old_content is not None:
|
||||
return m.group(0).replace(m.group(2), old_content)
|
||||
return m.group(0)
|
||||
return pattern.sub(repl, new_code)
|
||||
|
||||
def save_with_preserve(path, new_code):
|
||||
if os.path.exists(path):
|
||||
with open(path, "r", encoding="utf-8") as f:
|
||||
old_code = f.read()
|
||||
new_code = preserve_all_user_regions(new_code, old_code)
|
||||
# 确保目录存在
|
||||
os.makedirs(os.path.dirname(path), exist_ok=True)
|
||||
with open(path, "w", encoding="utf-8") as f:
|
||||
f.write(new_code)
|
||||
|
||||
class BspSimplePeripheral(QWidget):
|
||||
def __init__(self, project_path, peripheral_name, template_names):
|
||||
super().__init__()
|
||||
@@ -50,7 +16,7 @@ class BspSimplePeripheral(QWidget):
|
||||
self.template_names = template_names
|
||||
# 加载描述
|
||||
describe_path = os.path.join(CodeGenerator.get_assets_dir("User_code/bsp"), "describe.csv")
|
||||
self.descriptions = load_descriptions(describe_path)
|
||||
self.descriptions = CodeGenerator.load_descriptions(describe_path)
|
||||
self._init_ui()
|
||||
self._load_config()
|
||||
|
||||
@@ -102,7 +68,7 @@ class BspSimplePeripheral(QWidget):
|
||||
if not template_content:
|
||||
return False
|
||||
output_path = os.path.join(self.project_path, f"User/bsp/{filename}")
|
||||
save_with_preserve(output_path, template_content) # 使用保留用户区域的写入
|
||||
CodeGenerator.save_with_preserve(output_path, template_content) # 使用保留用户区域的写入
|
||||
self._save_config()
|
||||
return True
|
||||
|
||||
@@ -133,7 +99,7 @@ class BspPeripheralBase(QWidget):
|
||||
self.available_list = []
|
||||
# 新增:加载描述
|
||||
describe_path = os.path.join(CodeGenerator.get_assets_dir("User_code/bsp"), "describe.csv")
|
||||
self.descriptions = load_descriptions(describe_path)
|
||||
self.descriptions = CodeGenerator.load_descriptions(describe_path)
|
||||
self._init_ui()
|
||||
self._load_config()
|
||||
|
||||
@@ -249,7 +215,7 @@ class BspPeripheralBase(QWidget):
|
||||
template_content, f"AUTO GENERATED {self.enum_prefix}_NAME", "\n".join(enum_lines)
|
||||
)
|
||||
output_path = os.path.join(self.project_path, f"User/bsp/{self.template_names['header']}")
|
||||
save_with_preserve(output_path, content) # 使用保留用户区域的写入
|
||||
CodeGenerator.save_with_preserve(output_path, content) # 使用保留用户区域的写入
|
||||
return True
|
||||
|
||||
def _generate_source_file(self, configs, template_dir):
|
||||
@@ -283,7 +249,7 @@ class BspPeripheralBase(QWidget):
|
||||
content, f"AUTO GENERATED {self.enum_prefix}_GET_HANDLE", "\n".join(handle_lines)
|
||||
)
|
||||
output_path = os.path.join(self.project_path, f"User/bsp/{self.template_names['source']}")
|
||||
save_with_preserve(output_path, content) # 使用保留用户区域的写入
|
||||
CodeGenerator.save_with_preserve(output_path, content) # 使用保留用户区域的写入
|
||||
return True
|
||||
|
||||
def _save_config(self, configs):
|
||||
@@ -337,17 +303,6 @@ class BspPeripheralBase(QWidget):
|
||||
self._save_config(configs)
|
||||
return True
|
||||
|
||||
def load_descriptions(csv_path):
|
||||
descriptions = {}
|
||||
if os.path.exists(csv_path):
|
||||
with open(csv_path, encoding='utf-8') as f:
|
||||
reader = csv.reader(f)
|
||||
for row in reader:
|
||||
if len(row) >= 2:
|
||||
key, desc = row[0].strip(), row[1].strip()
|
||||
descriptions[key.lower()] = desc
|
||||
return descriptions
|
||||
|
||||
# 各外设的可用列表获取函数
|
||||
def get_available_i2c(project_path):
|
||||
ioc_files = [f for f in os.listdir(project_path) if f.endswith('.ioc')]
|
||||
@@ -474,7 +429,7 @@ class bsp_can(BspPeripheralBase):
|
||||
)
|
||||
|
||||
output_path = os.path.join(self.project_path, f"User/bsp/{self.template_names['source']}")
|
||||
save_with_preserve(output_path, content)
|
||||
CodeGenerator.save_with_preserve(output_path, content)
|
||||
return True
|
||||
|
||||
def _generate_single_can_init(self, init_lines, configs, fifo_assignment):
|
||||
@@ -744,7 +699,7 @@ class bsp_gpio(QWidget):
|
||||
self.available_list = self._get_all_gpio_list()
|
||||
# 加载描述
|
||||
describe_path = os.path.join(CodeGenerator.get_assets_dir("User_code/bsp"), "describe.csv")
|
||||
self.descriptions = load_descriptions(describe_path)
|
||||
self.descriptions = CodeGenerator.load_descriptions(describe_path)
|
||||
self._init_ui()
|
||||
self._load_config()
|
||||
|
||||
@@ -891,7 +846,7 @@ class bsp_gpio(QWidget):
|
||||
)
|
||||
|
||||
output_path = os.path.join(self.project_path, "User/bsp/gpio.h")
|
||||
save_with_preserve(output_path, content)
|
||||
CodeGenerator.save_with_preserve(output_path, content)
|
||||
return True
|
||||
|
||||
def _generate_source_file(self, configs, template_dir):
|
||||
@@ -932,7 +887,7 @@ class bsp_gpio(QWidget):
|
||||
)
|
||||
|
||||
output_path = os.path.join(self.project_path, "User/bsp/gpio.c")
|
||||
save_with_preserve(output_path, content)
|
||||
CodeGenerator.save_with_preserve(output_path, content)
|
||||
return True
|
||||
|
||||
|
||||
@@ -983,7 +938,7 @@ class bsp_pwm(QWidget):
|
||||
self.available_list = self._get_pwm_channels()
|
||||
# 加载描述
|
||||
describe_path = os.path.join(CodeGenerator.get_assets_dir("User_code/bsp"), "describe.csv")
|
||||
self.descriptions = load_descriptions(describe_path)
|
||||
self.descriptions = CodeGenerator.load_descriptions(describe_path)
|
||||
self._init_ui()
|
||||
self._load_config()
|
||||
|
||||
@@ -1148,7 +1103,7 @@ class bsp_pwm(QWidget):
|
||||
)
|
||||
|
||||
output_path = os.path.join(self.project_path, "User/bsp/pwm.h")
|
||||
save_with_preserve(output_path, content)
|
||||
CodeGenerator.save_with_preserve(output_path, content)
|
||||
return True
|
||||
|
||||
def _generate_source_file(self, configs, template_dir):
|
||||
@@ -1169,7 +1124,7 @@ class bsp_pwm(QWidget):
|
||||
)
|
||||
|
||||
output_path = os.path.join(self.project_path, "User/bsp/pwm.c")
|
||||
save_with_preserve(output_path, content)
|
||||
CodeGenerator.save_with_preserve(output_path, content)
|
||||
return True
|
||||
|
||||
def _save_config(self, configs):
|
||||
@@ -1245,7 +1200,7 @@ class bsp(QWidget):
|
||||
if os.path.exists(src_bsp_h):
|
||||
with open(src_bsp_h, 'r', encoding='utf-8') as f:
|
||||
content = f.read()
|
||||
save_with_preserve(dst_bsp_h, content)
|
||||
CodeGenerator.save_with_preserve(dst_bsp_h, content)
|
||||
|
||||
total = 0
|
||||
success_count = 0
|
||||
|
||||
@@ -4,66 +4,7 @@ from qfluentwidgets import InfoBar
|
||||
from PyQt5.QtCore import Qt, pyqtSignal
|
||||
from app.tools.code_generator import CodeGenerator
|
||||
import os
|
||||
import csv
|
||||
import shutil
|
||||
import re
|
||||
|
||||
def preserve_all_user_regions(new_code, old_code):
|
||||
""" Preserves all user-defined regions in the new code based on the old code.
|
||||
This function uses regex to find user-defined regions in the old code and replaces them in the new code.
|
||||
Args:
|
||||
new_code (str): The new code content.
|
||||
old_code (str): The old code content.
|
||||
Returns:
|
||||
str: The new code with preserved user-defined regions.
|
||||
"""
|
||||
pattern = re.compile(
|
||||
r"/\*\s*(USER [A-Z0-9_ ]+)\s*BEGIN\s*\*/(.*?)/\*\s*\1\s*END\s*\*/",
|
||||
re.DOTALL
|
||||
)
|
||||
old_regions = {m.group(1): m.group(2) for m in pattern.finditer(old_code or "")}
|
||||
def repl(m):
|
||||
region = m.group(1)
|
||||
old_content = old_regions.get(region)
|
||||
if old_content is not None:
|
||||
return m.group(0).replace(m.group(2), old_content)
|
||||
return m.group(0)
|
||||
return pattern.sub(repl, new_code)
|
||||
|
||||
def save_with_preserve(path, new_code):
|
||||
"""保存文件并保留用户区域"""
|
||||
if os.path.exists(path):
|
||||
with open(path, "r", encoding="utf-8") as f:
|
||||
old_code = f.read()
|
||||
new_code = preserve_all_user_regions(new_code, old_code)
|
||||
os.makedirs(os.path.dirname(path), exist_ok=True)
|
||||
with open(path, "w", encoding="utf-8") as f:
|
||||
f.write(new_code)
|
||||
|
||||
def load_descriptions(csv_path):
|
||||
"""加载组件描述信息"""
|
||||
descriptions = {}
|
||||
if os.path.exists(csv_path):
|
||||
with open(csv_path, encoding='utf-8') as f:
|
||||
reader = csv.reader(f)
|
||||
for row in reader:
|
||||
if len(row) >= 2:
|
||||
key, desc = row[0].strip(), row[1].strip()
|
||||
descriptions[key.lower()] = desc
|
||||
return descriptions
|
||||
|
||||
def load_dependencies(csv_path):
|
||||
"""加载组件依赖关系"""
|
||||
dependencies = {}
|
||||
if os.path.exists(csv_path):
|
||||
with open(csv_path, encoding='utf-8') as f:
|
||||
reader = csv.reader(f)
|
||||
for row in reader:
|
||||
if len(row) >= 2:
|
||||
component = row[0].strip()
|
||||
deps = [dep.strip() for dep in row[1:] if dep.strip()]
|
||||
dependencies[component] = deps
|
||||
return dependencies
|
||||
|
||||
|
||||
def get_component_page(component_name, project_path, component_manager=None):
|
||||
@@ -108,8 +49,8 @@ class ComponentSimple(QWidget):
|
||||
component_dir = CodeGenerator.get_assets_dir("User_code/component")
|
||||
describe_path = os.path.join(component_dir, "describe.csv")
|
||||
dependencies_path = os.path.join(component_dir, "dependencies.csv")
|
||||
self.descriptions = load_descriptions(describe_path)
|
||||
self.dependencies = load_dependencies(dependencies_path)
|
||||
self.descriptions = CodeGenerator.load_descriptions(describe_path)
|
||||
self.dependencies = CodeGenerator.load_dependencies(dependencies_path)
|
||||
|
||||
self._init_ui()
|
||||
self._load_config()
|
||||
@@ -174,7 +115,7 @@ class ComponentSimple(QWidget):
|
||||
print(f"模板文件不存在或为空: {template_path}")
|
||||
continue
|
||||
output_path = os.path.join(self.project_path, f"User/component/{filename}")
|
||||
save_with_preserve(output_path, template_content)
|
||||
CodeGenerator.save_with_preserve(output_path, template_content)
|
||||
self._save_config()
|
||||
return True
|
||||
|
||||
@@ -296,7 +237,7 @@ class component(QWidget):
|
||||
if os.path.exists(src_component_h):
|
||||
with open(src_component_h, 'r', encoding='utf-8') as f:
|
||||
content = f.read()
|
||||
save_with_preserve(dst_component_h, content)
|
||||
CodeGenerator.save_with_preserve(dst_component_h, content)
|
||||
|
||||
# 收集所有需要生成的组件和它们的依赖
|
||||
components_to_generate = set()
|
||||
@@ -373,7 +314,7 @@ class component(QWidget):
|
||||
os.makedirs(os.path.dirname(dst_path), exist_ok=True)
|
||||
with open(src_path, 'r', encoding='utf-8') as f:
|
||||
new_content = f.read()
|
||||
save_with_preserve(dst_path, new_content)
|
||||
CodeGenerator.save_with_preserve(dst_path, new_content)
|
||||
else:
|
||||
# 如果既不是文件也不是目录,跳过
|
||||
print(f"跳过不存在的依赖: {dep_path}")
|
||||
|
||||
@@ -3,49 +3,9 @@ from qfluentwidgets import BodyLabel, CheckBox, ComboBox, SubtitleLabel
|
||||
from PyQt5.QtCore import Qt
|
||||
from app.tools.code_generator import CodeGenerator
|
||||
import os
|
||||
import shutil
|
||||
import yaml
|
||||
import re
|
||||
|
||||
def preserve_all_user_regions(new_code, old_code):
|
||||
""" Preserves all user-defined regions in the new code based on the old code.
|
||||
This function uses regex to find user-defined regions in the old code and replaces them in the new code.
|
||||
Args:
|
||||
new_code (str): The new code content.
|
||||
old_code (str): The old code content.
|
||||
Returns:
|
||||
str: The new code with preserved user-defined regions.
|
||||
"""
|
||||
pattern = re.compile(
|
||||
r"/\*\s*(USER [A-Z0-9_ ]+)\s*BEGIN\s*\*/(.*?)/\*\s*\1\s*END\s*\*/",
|
||||
re.DOTALL
|
||||
)
|
||||
old_regions = {m.group(1): m.group(2) for m in pattern.finditer(old_code or "")}
|
||||
def repl(m):
|
||||
region = m.group(1)
|
||||
old_content = old_regions.get(region)
|
||||
if old_content is not None:
|
||||
return m.group(0).replace(m.group(2), old_content)
|
||||
return m.group(0)
|
||||
return pattern.sub(repl, new_code)
|
||||
|
||||
def save_with_preserve(path, new_code):
|
||||
"""保存文件并保留用户区域"""
|
||||
if os.path.exists(path):
|
||||
with open(path, "r", encoding="utf-8") as f:
|
||||
old_code = f.read()
|
||||
new_code = preserve_all_user_regions(new_code, old_code)
|
||||
os.makedirs(os.path.dirname(path), exist_ok=True)
|
||||
with open(path, "w", encoding="utf-8") as f:
|
||||
f.write(new_code)
|
||||
|
||||
def load_device_config(config_path):
|
||||
"""加载设备配置"""
|
||||
if os.path.exists(config_path):
|
||||
with open(config_path, 'r', encoding='utf-8') as f:
|
||||
return yaml.safe_load(f)
|
||||
return {}
|
||||
|
||||
def get_available_bsp_devices(project_path, bsp_type, gpio_type=None):
|
||||
"""获取可用的BSP设备,GPIO可选类型过滤"""
|
||||
bsp_config_path = os.path.join(project_path, "User/bsp/bsp_config.yaml")
|
||||
@@ -94,7 +54,7 @@ def generate_device_header(project_path, enabled_devices):
|
||||
|
||||
# 加载设备配置来获取信号信息
|
||||
config_path = os.path.join(device_dir, "config.yaml")
|
||||
device_configs = load_device_config(config_path)
|
||||
device_configs = CodeGenerator.load_device_config(config_path)
|
||||
|
||||
for device_name in enabled_devices:
|
||||
device_key = device_name.lower()
|
||||
@@ -305,7 +265,7 @@ class DeviceSimple(QWidget):
|
||||
os.makedirs(os.path.dirname(dst_path), exist_ok=True)
|
||||
with open(src_path, 'r', encoding='utf-8') as f:
|
||||
content = f.read()
|
||||
save_with_preserve(dst_path, content)
|
||||
CodeGenerator.save_with_preserve(dst_path, content)
|
||||
|
||||
elif file_type == 'source':
|
||||
# 源文件需要替换BSP设备名称
|
||||
@@ -362,7 +322,7 @@ def get_device_page(device_name, project_path):
|
||||
from app.tools.code_generator import CodeGenerator
|
||||
device_dir = CodeGenerator.get_assets_dir("User_code/device")
|
||||
config_path = os.path.join(device_dir, "config.yaml")
|
||||
device_configs = load_device_config(config_path)
|
||||
device_configs = CodeGenerator.load_device_config(config_path)
|
||||
|
||||
devices = device_configs.get('devices', {})
|
||||
device_key = device_name.lower()
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
import os
|
||||
import yaml
|
||||
import shutil
|
||||
from typing import Dict, List, Tuple
|
||||
from typing import Dict, List, Tuple, Optional
|
||||
import sys
|
||||
import os
|
||||
import re
|
||||
import csv
|
||||
class CodeGenerator:
|
||||
"""通用代码生成器"""
|
||||
|
||||
@@ -163,4 +164,304 @@ class CodeGenerator:
|
||||
print(f"警告:资源目录不存在: {full_path}")
|
||||
setattr(CodeGenerator, warning_key, True)
|
||||
|
||||
return full_path
|
||||
return full_path
|
||||
|
||||
@staticmethod
|
||||
def preserve_all_user_regions(new_code: str, old_code: str) -> str:
|
||||
"""保留用户定义的代码区域
|
||||
|
||||
在新代码中保留旧代码中所有用户定义的区域。
|
||||
用户区域使用如下格式标记:
|
||||
/* USER REGION_NAME BEGIN */
|
||||
用户代码...
|
||||
/* USER REGION_NAME END */
|
||||
|
||||
Args:
|
||||
new_code: 新的代码内容
|
||||
old_code: 旧的代码内容
|
||||
|
||||
Returns:
|
||||
str: 保留了用户区域的新代码
|
||||
"""
|
||||
if not old_code:
|
||||
return new_code
|
||||
|
||||
pattern = re.compile(
|
||||
r"/\*\s*(USER [A-Z0-9_ ]+)\s*BEGIN\s*\*/(.*?)/\*\s*\1\s*END\s*\*/",
|
||||
re.DOTALL
|
||||
)
|
||||
|
||||
# 提取旧代码中的所有用户区域
|
||||
old_regions = {m.group(1): m.group(2) for m in pattern.finditer(old_code)}
|
||||
|
||||
def repl(m):
|
||||
region_name = m.group(1)
|
||||
old_content = old_regions.get(region_name)
|
||||
if old_content is not None:
|
||||
# 替换为旧的用户内容
|
||||
return m.group(0).replace(m.group(2), old_content)
|
||||
return m.group(0)
|
||||
|
||||
return pattern.sub(repl, new_code)
|
||||
|
||||
@staticmethod
|
||||
def save_with_preserve(file_path: str, new_code: str) -> bool:
|
||||
"""保存文件并保留用户代码区域
|
||||
|
||||
如果文件已存在,会先读取旧文件内容,保留其中的用户代码区域,
|
||||
然后将新代码与保留的用户区域合并后保存。
|
||||
|
||||
Args:
|
||||
file_path: 文件路径
|
||||
new_code: 新的代码内容
|
||||
|
||||
Returns:
|
||||
bool: 保存是否成功
|
||||
"""
|
||||
try:
|
||||
# 如果文件已存在,先读取旧内容
|
||||
old_code = ""
|
||||
if os.path.exists(file_path):
|
||||
with open(file_path, "r", encoding="utf-8") as f:
|
||||
old_code = f.read()
|
||||
|
||||
# 保留用户区域
|
||||
final_code = CodeGenerator.preserve_all_user_regions(new_code, old_code)
|
||||
|
||||
# 确保目录存在
|
||||
os.makedirs(os.path.dirname(file_path), exist_ok=True)
|
||||
|
||||
# 保存文件
|
||||
with open(file_path, "w", encoding="utf-8") as f:
|
||||
f.write(final_code)
|
||||
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
print(f"保存文件失败: {file_path}, 错误: {e}")
|
||||
return False
|
||||
|
||||
@staticmethod
|
||||
def load_descriptions(csv_path: str) -> Dict[str, str]:
|
||||
"""从CSV文件加载组件或设备的描述信息
|
||||
|
||||
CSV格式:第一列为组件/设备名称,第二列为描述
|
||||
|
||||
Args:
|
||||
csv_path: CSV文件路径
|
||||
|
||||
Returns:
|
||||
Dict[str, str]: 名称到描述的映射字典
|
||||
"""
|
||||
descriptions = {}
|
||||
if os.path.exists(csv_path):
|
||||
try:
|
||||
with open(csv_path, encoding='utf-8') as f:
|
||||
reader = csv.reader(f)
|
||||
for row in reader:
|
||||
if len(row) >= 2:
|
||||
key, desc = row[0].strip(), row[1].strip()
|
||||
descriptions[key.lower()] = desc
|
||||
except Exception as e:
|
||||
print(f"加载描述文件失败: {csv_path}, 错误: {e}")
|
||||
return descriptions
|
||||
|
||||
@staticmethod
|
||||
def load_dependencies(csv_path: str) -> Dict[str, List[str]]:
|
||||
"""从CSV文件加载组件依赖关系
|
||||
|
||||
CSV格式:第一列为组件名,后续列为依赖的组件
|
||||
|
||||
Args:
|
||||
csv_path: CSV文件路径
|
||||
|
||||
Returns:
|
||||
Dict[str, List[str]]: 组件名到依赖列表的映射字典
|
||||
"""
|
||||
dependencies = {}
|
||||
if os.path.exists(csv_path):
|
||||
try:
|
||||
with open(csv_path, encoding='utf-8') as f:
|
||||
reader = csv.reader(f)
|
||||
for row in reader:
|
||||
if len(row) >= 2:
|
||||
component = row[0].strip()
|
||||
deps = [dep.strip() for dep in row[1:] if dep.strip()]
|
||||
dependencies[component] = deps
|
||||
except Exception as e:
|
||||
print(f"加载依赖文件失败: {csv_path}, 错误: {e}")
|
||||
return dependencies
|
||||
|
||||
@staticmethod
|
||||
def load_device_config(config_path: str) -> Dict:
|
||||
"""加载设备配置文件
|
||||
|
||||
Args:
|
||||
config_path: YAML配置文件路径
|
||||
|
||||
Returns:
|
||||
Dict: 配置数据字典
|
||||
"""
|
||||
return CodeGenerator.load_config(config_path)
|
||||
|
||||
@staticmethod
|
||||
def copy_dependency_file(src_path: str, dst_path: str) -> bool:
|
||||
"""复制依赖文件
|
||||
|
||||
Args:
|
||||
src_path: 源文件路径
|
||||
dst_path: 目标文件路径
|
||||
|
||||
Returns:
|
||||
bool: 复制是否成功
|
||||
"""
|
||||
try:
|
||||
os.makedirs(os.path.dirname(dst_path), exist_ok=True)
|
||||
shutil.copy2(src_path, dst_path)
|
||||
return True
|
||||
except Exception as e:
|
||||
print(f"复制文件失败: {src_path} -> {dst_path}, 错误: {e}")
|
||||
return False
|
||||
|
||||
@staticmethod
|
||||
def generate_code_from_template(template_path: str, output_path: str,
|
||||
replacements: Optional[Dict[str, str]] = None,
|
||||
preserve_user_code: bool = True) -> bool:
|
||||
"""从模板生成代码文件
|
||||
|
||||
Args:
|
||||
template_path: 模板文件路径
|
||||
output_path: 输出文件路径
|
||||
replacements: 要替换的标记字典,如 {'MARKER': 'replacement_content'}
|
||||
preserve_user_code: 是否保留用户代码区域
|
||||
|
||||
Returns:
|
||||
bool: 生成是否成功
|
||||
"""
|
||||
try:
|
||||
# 加载模板
|
||||
template_content = CodeGenerator.load_template(template_path)
|
||||
if not template_content:
|
||||
print(f"模板文件不存在或为空: {template_path}")
|
||||
return False
|
||||
|
||||
# 执行替换
|
||||
if replacements:
|
||||
for marker, replacement in replacements.items():
|
||||
template_content = CodeGenerator.replace_auto_generated(
|
||||
template_content, marker, replacement
|
||||
)
|
||||
|
||||
# 保存文件
|
||||
if preserve_user_code:
|
||||
return CodeGenerator.save_with_preserve(output_path, template_content)
|
||||
else:
|
||||
return CodeGenerator.save_file(template_content, output_path)
|
||||
|
||||
except Exception as e:
|
||||
print(f"从模板生成代码失败: {template_path} -> {output_path}, 错误: {e}")
|
||||
return False
|
||||
|
||||
@staticmethod
|
||||
def read_file_content(file_path: str) -> Optional[str]:
|
||||
"""读取文件内容
|
||||
|
||||
Args:
|
||||
file_path: 文件路径
|
||||
|
||||
Returns:
|
||||
str: 文件内容,如果失败返回None
|
||||
"""
|
||||
try:
|
||||
with open(file_path, 'r', encoding='utf-8') as f:
|
||||
return f.read()
|
||||
except Exception as e:
|
||||
print(f"读取文件失败: {file_path}, 错误: {e}")
|
||||
return None
|
||||
|
||||
@staticmethod
|
||||
def write_file_content(file_path: str, content: str) -> bool:
|
||||
"""写入文件内容
|
||||
|
||||
Args:
|
||||
file_path: 文件路径
|
||||
content: 文件内容
|
||||
|
||||
Returns:
|
||||
bool: 写入是否成功
|
||||
"""
|
||||
try:
|
||||
os.makedirs(os.path.dirname(file_path), exist_ok=True)
|
||||
with open(file_path, 'w', encoding='utf-8') as f:
|
||||
f.write(content)
|
||||
return True
|
||||
except Exception as e:
|
||||
print(f"写入文件失败: {file_path}, 错误: {e}")
|
||||
return False
|
||||
|
||||
@staticmethod
|
||||
def update_file_with_pattern(file_path: str, pattern: str, replacement: str,
|
||||
use_regex: bool = True) -> bool:
|
||||
"""更新文件中匹配模式的内容
|
||||
|
||||
Args:
|
||||
file_path: 文件路径
|
||||
pattern: 要匹配的模式
|
||||
replacement: 替换内容
|
||||
use_regex: 是否使用正则表达式
|
||||
|
||||
Returns:
|
||||
bool: 更新是否成功
|
||||
"""
|
||||
try:
|
||||
content = CodeGenerator.read_file_content(file_path)
|
||||
if content is None:
|
||||
return False
|
||||
|
||||
if use_regex:
|
||||
import re
|
||||
updated_content = re.sub(pattern, replacement, content, flags=re.DOTALL)
|
||||
else:
|
||||
updated_content = content.replace(pattern, replacement)
|
||||
|
||||
return CodeGenerator.write_file_content(file_path, updated_content)
|
||||
|
||||
except Exception as e:
|
||||
print(f"更新文件失败: {file_path}, 错误: {e}")
|
||||
return False
|
||||
|
||||
@staticmethod
|
||||
def replace_multiple_markers(content: str, replacements: Dict[str, str]) -> str:
|
||||
"""批量替换内容中的多个标记
|
||||
|
||||
Args:
|
||||
content: 要处理的内容
|
||||
replacements: 替换字典,如 {'MARKER1': 'content1', 'MARKER2': 'content2'}
|
||||
|
||||
Returns:
|
||||
str: 替换后的内容
|
||||
"""
|
||||
result = content
|
||||
for marker, replacement in replacements.items():
|
||||
result = CodeGenerator.replace_auto_generated(result, marker, replacement)
|
||||
return result
|
||||
|
||||
@staticmethod
|
||||
def extract_user_regions(code: str) -> Dict[str, str]:
|
||||
"""从代码中提取所有用户区域
|
||||
|
||||
Args:
|
||||
code: 要提取的代码内容
|
||||
|
||||
Returns:
|
||||
Dict[str, str]: 区域名称到区域内容的映射
|
||||
"""
|
||||
if not code:
|
||||
return {}
|
||||
|
||||
pattern = re.compile(
|
||||
r"/\*\s*USER CODE BEGIN ([A-Za-z0-9_ ]+)\s*\*/(.*?)/\*\s*USER CODE END \1\s*\*/",
|
||||
re.DOTALL
|
||||
)
|
||||
|
||||
return {m.group(1): m.group(2) for m in pattern.finditer(code)}
|
||||
Reference in New Issue
Block a user