массивы вроде работают но тормозит. начата работа над оптимизацией

This commit is contained in:
Razvalyaev 2025-07-11 09:44:33 +03:00
parent f271b2e82c
commit ad7b9126b7
3 changed files with 303 additions and 108 deletions

View File

@ -24,7 +24,7 @@ class VariableSelectorDialog(QDialog):
self.typedefs = typedefs
self.expanded_vars = []
self.var_map = {v['name']: v for v in all_vars}
self.node_index = {}
self.xml_path = xml_path # сохраняем путь к xml
# --- Добавляем чекбокс для автодополнения ---
@ -91,9 +91,32 @@ class VariableSelectorDialog(QDialog):
self.setLayout(layout)
self.expanded_vars = setupVars.expand_vars(self.all_vars, self.structs, self.typedefs)
self.build_completion_list()
self.populate_tree()
def get_full_item_name(self, item):
names = []
while item:
names.append(item.text(0))
item = item.parent()
return '.'.join(reversed(names))
def build_completion_list(self):
# Собираем список полных имён всех переменных и вложенных полей
completions = []
def recurse(var, prefix=''):
fullname = f"{prefix}.{var['name']}" if prefix else var['name']
completions.append(fullname)
for child in var.get('children', []):
recurse(child, fullname)
for v in self.expanded_vars:
recurse(v)
self.all_completions = completions
def add_tree_item_recursively(self, parent, var):
"""
Рекурсивно добавляет переменную и её дочерние поля в дерево.
@ -105,6 +128,8 @@ class VariableSelectorDialog(QDialog):
item = QTreeWidgetItem([name, type_str])
item.setData(0, Qt.UserRole, name)
full_name = self.get_full_item_name(item)
self.node_index[full_name.lower()] = item
# Делаем bitfield-поля неактивными
if "(bitfield:" in type_str:
@ -130,6 +155,7 @@ class VariableSelectorDialog(QDialog):
def populate_tree(self):
self.tree.clear()
self.node_index.clear()
for var in self.expanded_vars:
self.add_tree_item_recursively(None, var)
@ -209,74 +235,130 @@ class VariableSelectorDialog(QDialog):
item = self.tree.topLevelItem(i)
hide_all(item)
show_matching_path(item, 0)
def update_completions(self, text = None):
def find_node_by_path(self, root_vars, path_list):
current_level = root_vars
node = None
for part in path_list:
node = None
for var in current_level:
if var['name'] == part:
node = var
break
if node is None:
return None
current_level = node.get('children', [])
return node
def update_completions(self, text=None):
if text is None:
text = self.search_input.text().strip()
else:
text = text.strip()
parts = self.split_path(text)
path_parts = parts[:-1]
prefix = parts[-1].lower() if not text.endswith(('.', '>')) else ''
path_parts = parts[:-1] if parts else []
prefix = parts[-1].lower() if parts else ''
ends_with_sep = text.endswith('.') or text.endswith('->') or text.endswith('[')
is_index_suggestion = text.endswith('[')
# Если путь есть (например: project.adc или project.adc.), ищем внутри него
search_deep = len(path_parts) > 0
completions = []
def find_path_items(path_parts):
def find_path_items(parts):
items = [self.tree.topLevelItem(i) for i in range(self.tree.topLevelItemCount())]
for part in path_parts:
for part in parts:
part_lower = part.lower()
matched = []
for item in items:
# Берём последний фрагмент имени item, разделённого точками
item_name_part = self.split_path(item.text(0))[-1].lower()
if item_name_part == part_lower:
name_parts = self.split_path(item.text(0).lower())
if name_parts and name_parts[-1] == part_lower:
matched.append(item)
if not matched:
return []
items = []
# Собираем детей для следующего уровня поиска
for node in matched:
for i in range(node.childCount()):
items.append(node.child(i))
return matched
if not search_deep:
# Без точки — ищем только в топ-уровне, фильтруя по prefix
items = []
if is_index_suggestion:
# Предлагаем индексы (ищем всех детей с форматом foo[0], foo[1], ...)
base_text = text[:-1] # убираем '[', получаем, например, 'foo'
parent_node = self.find_node_by_fullname(base_text)
if not parent_node:
parent_node = self.find_node_by_fullname(base_text.rstrip('0123456789[]'))
if parent_node:
seen = set()
for i in range(parent_node.childCount()):
child = parent_node.child(i)
cname = child.text(0)
m = re.match(rf'^{re.escape(base_text)}\[(\d+)\]$', cname)
if m and cname not in seen:
completions.append(cname)
seen.add(cname)
self.completer.setModel(QStringListModel(completions))
return completions
if ends_with_sep:
# Завершён путь (например: project.adc[0].), показываем детей
node = self.find_node_by_fullname(text[:-1])
if node:
for i in range(node.childCount()):
completions.append(node.child(i).text(0))
elif not path_parts:
# Первый уровень — по вхождению
for i in range(self.tree.topLevelItemCount()):
item = self.tree.topLevelItem(i)
name_part = self.split_path(item.text(0))[-1].lower()
if name_part.startswith(prefix):
items.append(item)
completions = [item.text(0) for item in items]
name = item.text(0).lower()
if prefix in name:
completions.append(item.text(0))
else:
# С точкой — углубляемся по пути и показываем имена детей
if len(path_parts) == 0:
items = [self.tree.topLevelItem(i) for i in range(self.tree.topLevelItemCount())]
else:
items = find_path_items(path_parts)
completions = []
for item in items:
# Углубляемся: на последнем уровне используем startswith(prefix)
matched_items = find_path_items(path_parts)
for item in matched_items:
for i in range(item.childCount()):
child = item.child(i)
name_part = self.split_path(child.text(0))[-1].lower()
if prefix == '' or name_part.startswith(prefix):
completions.append(child.text(0))
name = child.text(0)
name_parts = self.split_path(name)
if not name_parts:
continue
last_part = name_parts[-1].lower()
if prefix == '' or last_part.startswith(prefix):
completions.append(name)
self.completer.setModel(QStringListModel(completions))
return completions
# Функция для поиска узла с полным именем
def find_node_by_fullname(self, name):
return self.node_index.get(name.lower())
def insert_completion(self, text):
node = self.find_node_by_fullname(text)
if node and node.childCount() > 0 and not (text.endswith('.') or text.endswith('->') or text.endswith('[')):
# Определяем разделитель по имени первого ребёнка
child_name = node.child(0).text(0)
if child_name.startswith(text + '->'):
text += '->'
elif child_name.startswith(text + '.'):
text += '.'
elif '[' in child_name:
text += '[' # для массивов
else:
text += '.' # fallback
self.search_input.setText(text)
self.search_input.setCursorPosition(len(text))
self.update_completions()
self.completer.complete()
else:
self.search_input.setText(text)
self.search_input.setCursorPosition(len(text))
def eventFilter(self, obj, event):
if obj == self.search_input and isinstance(event, QKeyEvent):
if event.key() == Qt.Key_Space and event.modifiers() & Qt.ControlModifier:
@ -325,36 +407,6 @@ class VariableSelectorDialog(QDialog):
return super().eventFilter(obj, event)
# Функция для поиска узла с полным именем
def find_node_by_fullname(self, name):
stack = [self.tree.topLevelItem(i) for i in range(self.tree.topLevelItemCount())]
while stack:
node = stack.pop()
if node.text(0).lower() == name.lower():
return node
for i in range(node.childCount()):
stack.append(node.child(i))
return None
def insert_completion(self, text):
node = self.find_node_by_fullname(text)
if node and node.childCount() > 0 and not (text.endswith('.') or text.endswith('->')):
# Определяем разделитель по имени первого ребёнка
child_name = node.child(0).text(0)
if child_name.startswith(text + '->'):
text += '->'
else:
text += '.'
self.search_input.setText(text)
self.search_input.setCursorPosition(len(text))
self.update_completions()
self.completer.complete()
else:
self.search_input.setText(text)
self.search_input.setCursorPosition(len(text))
def on_search_text_changed(self, text):
if self.autocomplete_checkbox.isChecked():
completions = self.update_completions(text)
@ -532,6 +584,49 @@ class VariableSelectorDialog(QDialog):
self.settings.setValue("autocomplete_enabled", self.autocomplete_checkbox.isChecked())
def split_path(self, path):
# Разбиваем по точке или по -> (учитываем, что -> длиной 2 символа)
return re.split(r'\.|->', path)
"""
Разбивает путь на компоненты:
- 'foo[2].bar[1]->baz' ['foo', [2]', 'bar', '[1]' 'baz']
"""
tokens = []
token = ''
i = 0
while i < len(path):
c = path[i]
# Разделители: '->' и '.'
if c == '-' and path[i:i+2] == '->':
if token:
tokens.append(token)
token = ''
i += 2
continue
elif c == '.':
if token:
tokens.append(token)
token = ''
i += 1
continue
elif c == '[':
# Заканчиваем текущий токен, если есть
if token:
tokens.append(token)
token = ''
# Собираем индекс [N]
idx = ''
while i < len(path) and path[i] != ']':
idx += path[i]
i += 1
if i < len(path) and path[i] == ']':
idx += ']'
i += 1
tokens.append(idx)
continue
else:
token += c
i += 1
if token:
tokens.append(token)
return tokens

View File

@ -153,37 +153,158 @@ def parse_structs(filename):
def parse_array_dims(type_str):
"""Возвращает базовый тип и список размеров массива"""
dims = list(map(int, re.findall(r'\[(\d+)\]', type_str)))
base_type = re.sub(r'\[\d+\]', '', type_str).strip()
return base_type, dims
def generate_array_names(prefix, dims, depth=0):
"""Рекурсивно генерирует имена для всех элементов многомерного массива"""
if not dims:
return [prefix]
result = []
for i in range(dims[0]):
new_prefix = f"{prefix}[{i}]"
children = generate_array_names(new_prefix, dims[1:], depth + 1)
result.append({
'name': new_prefix,
'children': children if len(dims) > 1 else None
})
return result
def flatten_array_tree(array_tree):
"""Разворачивает дерево массивов в линейный список с вложенными children"""
result = []
for node in array_tree:
entry = {'name': node['name']}
if node['children']:
entry['children'] = flatten_array_tree(node['children'])
result.append(entry)
return result
def expand_struct_recursively(prefix, type_str, structs, typedefs, var_attrs, depth=0):
if depth > 10:
return []
# Если type_str — словарь структуры
# Вспомогательная функция для обработки массивов
def process_array(prefix, base_type, dims):
array_tree = generate_array_names(prefix, dims)
array_flat = flatten_array_tree(array_tree)
for node in array_flat:
sub_items = expand_struct_recursively(node['name'], base_type, structs, typedefs, var_attrs, depth + 1)
if sub_items:
node['children'] = sub_items
else:
node.update({
'type': base_type,
'pt_type': '',
'iq_type': '',
'return_type': '',
'file': var_attrs.get('file'),
'extern': var_attrs.get('extern'),
'static': var_attrs.get('static'),
})
return array_flat
# Если type_str — уже распарсенная структура (dict)
if isinstance(type_str, dict):
fields = type_str
else:
# Проверяем, массив ли это
base_type, array_dims = parse_array_dims(type_str)
if array_dims:
return process_array(prefix, base_type, array_dims)
# Ищем структуру по имени типа
base_type = scanVars.strip_ptr_and_array(type_str)
fields = structs.get(base_type)
if not isinstance(fields, dict):
# Не структура и не массив — просто возвращаем пустой список
return []
children = []
for field_name, field_value in fields.items():
# Пропускаем поле 'type', оно служит для хранения имени типа
if field_name == 'type':
continue
# Определяем разделитель между prefix и полем
# Формируем полное имя поля
if prefix.endswith('*'):
separator = '->'
# Для красоты можно убрать пробелы у указателя
# например, если prefix="ptr*" -> "ptr->field"
full_name = f"{prefix[:-1]}{separator}{field_name}"
else:
separator = '.'
full_name = f"{prefix}{separator}{field_name}"
# Определяем тип поля
if isinstance(field_value, dict) and isinstance(field_value.get('type'), str):
field_type_str = field_value['type']
elif isinstance(field_value, str):
field_type_str = field_value
else:
field_type_str = None
# Обработка, если поле — строка (тип или массив)
if field_type_str:
base_subtype, sub_dims = parse_array_dims(field_type_str)
if sub_dims:
# Массив — раскрываем элементы
array_parent = {
'name': full_name,
'type': field_type_str,
'pt_type': '',
'iq_type': '',
'return_type': '',
'file': var_attrs.get('file'),
'extern': var_attrs.get('extern'),
'static': var_attrs.get('static'),
}
array_children = []
flat_names = generate_array_names(full_name, sub_dims)
for node in flat_names:
# node — dict с ключом 'name' и (возможно) 'children'
sub_items = expand_struct_recursively(node['name'], base_subtype, structs, typedefs, var_attrs, depth + 1)
child_node = {
'name': node['name'],
'type': base_subtype,
'pt_type': '',
'iq_type': '',
'return_type': '',
'file': var_attrs.get('file'),
'extern': var_attrs.get('extern'),
'static': var_attrs.get('static'),
}
if sub_items:
child_node['children'] = sub_items
array_children.append(child_node)
array_parent['children'] = array_children
children.append(array_parent)
continue
# Игнорируем указатели на функции
if "(" in field_type_str and "*" in field_type_str and ")" in field_type_str:
continue
# Обычное поле (не массив, не функция)
child = {
'name': full_name,
'type': field_type_str,
'pt_type': '',
'iq_type': '',
'return_type': '',
'file': var_attrs.get('file'),
'extern': var_attrs.get('extern'),
'static': var_attrs.get('static'),
}
children.append(child)
continue
# Если поле — dict без 'type' или со сложной структурой, обрабатываем как вложенную структуру
if isinstance(field_value, dict):
# Если вложенная структура — берем её имя типа из поля 'type' или пустую строку
type_name = field_value.get('type', '')
child = {
'name': full_name,
@ -195,35 +316,14 @@ def expand_struct_recursively(prefix, type_str, structs, typedefs, var_attrs, de
'extern': var_attrs.get('extern'),
'static': var_attrs.get('static'),
}
if '*' in type_name and not full_name.endswith('*'):
full_name += '*'
# Рекурсивно раскрываем вложенные поля
subchildren = expand_struct_recursively(full_name, field_value, structs, typedefs, var_attrs, depth + 1)
if subchildren:
child['children'] = subchildren
else:
# Простое поле — строка типа
# Пропускаем указатели на функции
if isinstance(field_value, str) and "(" in field_value and "*" in field_value and ")" in field_value:
continue
child = {
'name': full_name,
'type': field_value,
'pt_type': '',
'iq_type': '',
'return_type': '',
'file': var_attrs.get('file'),
'extern': var_attrs.get('extern'),
'static': var_attrs.get('static'),
}
children.append(child)
children.append(child)
return children
def expand_vars(vars_list, structs, typedefs):
"""
Раскрывает структуры и массивы структур в деревья.
@ -233,26 +333,26 @@ def expand_vars(vars_list, structs, typedefs):
for var in vars_list:
pt_type = var.get('pt_type', '')
raw_type = var.get('type', '')
base_type = scanVars.strip_ptr_and_array(raw_type)
fields = structs.get(base_type)
if pt_type.startswith('pt_ptr_') and isinstance(fields, dict):
if var['name'] == 'project':
a = 1
if pt_type.startswith('pt_ptr_'):
new_var = var.copy()
new_var['children'] = expand_struct_recursively(var['name']+'*', raw_type, structs, typedefs, var)
expanded.append(new_var)
if pt_type.startswith('pt_arr_') and isinstance(fields, dict):
if pt_type.startswith('pt_arr_'):
new_var = var.copy()
new_var['children'] = expand_struct_recursively(var['name'], raw_type, structs, typedefs, var)
expanded.append(new_var)
elif pt_type == 'pt_struct' and isinstance(fields, dict):
elif pt_type == 'pt_struct':
new_var = var.copy()
new_var['children'] = expand_struct_recursively(var['name'], raw_type, structs, typedefs, var)
expanded.append(new_var)
elif pt_type == 'pt_union' and isinstance(fields, dict):
elif pt_type == 'pt_union':
new_var = var.copy()
new_var['children'] = expand_struct_recursively(var['name'], raw_type, structs, typedefs, var)
expanded.append(new_var)

View File

@ -1,5 +1,5 @@
<?xml version='1.0' encoding='utf-8'?>
<analysis makefile_path="Debug/makefile" proj_path="E:/.WORK/TMS/TMS_new_bus" structs_path="Src/DebugTools/structs.xml">
<analysis makefile_path="Debug/makefile" proj_path="F:/Work/Projects/TMS/TMS_new_bus" structs_path="Src/DebugTools/structs.xml">
<variables>
<var name="ADC0finishAddr">
<show_var>true</show_var>
@ -3592,7 +3592,7 @@
<var name="project.cds_tk.count_elements_pbus">
<show_var>true</show_var>
<enable>true</enable>
<shortname>project.cds_tk.count_elements=_pbus</shortname>
<shortname>project.cds_tk.count_elements_pbus</shortname>
<pt_type>pt_uint64</pt_type>
<iq_type>t_iq_none</iq_type>
<return_type>t_iq_none</return_type>