массивы вроде работают но тормозит. начата работа над оптимизацией
This commit is contained in:
parent
f271b2e82c
commit
ad7b9126b7
@ -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
|
||||
|
||||
|
170
Src/setupVars.py
170
Src/setupVars.py
@ -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)
|
||||
|
4
vars.xml
4
vars.xml
@ -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>
|
||||
|
Loading…
Reference in New Issue
Block a user