05.01.2010, 19:16 | #1 |
Участник
|
Kashperuk Ivan: Editor script for simplifying the search in AOT
Источник: http://kashperuk.blogspot.com/2010/0...search-in.html
============== When doing code refactoring, or when investigating a particular class or form to understand the functionality behind it, it is often handy to search for variables used in its methods, looking at where and how each variable is used. It is tedious to manually go through the steps of copying the name of the variable into the clipboard, opening up the parent class/form, opening up the AOT Find dialog for it, pasting the variable name into the Selected Text field and pressing Find now. I have automated these steps, putting the code into an editor script. You can download the xpo, which contains the EditorScripts class with the additional method, by following this link. Now you should be able to very easily and quickly find the required code. The code of the editor script: X++: /// <summary> /// Editor script for finding all occurrences of the selected text in the parent node of the selected method /// </summary> /// <param name="e"> /// Reference to the AX editor /// /// <remarks> /// 2010-01-05, ivanv /// </remarks> void addIns_FindAllOccurrencesInNode(Editor e) { #AOT TreeNode treeNode; FreeText selectedText; FreeText GetSelectedTextFromEditor(Editor _e) { str 1 curSymbol; int iCopyFrom; int iCopyTo; FreeText _selectedLine; ; if (_e.markMode() != MarkMode::NoMark && _e.selectionStartCol() != _e.selectionEndCol()) { _selectedLine = strLRTrim(subStr(_e.currentLine(), _e.selectionStartCol(), _e.selectionEndCol() - _e.selectionStartCol())); } else { _selectedLine = _e.currentLine(); for (iCopyFrom = _e.columnNo()+1; iCopyFrom >= 0; iCopyFrom--) { curSymbol = subStr(_selectedLine, iCopyFrom, 1); if (!strAlpha(curSymbol) && curSymbol != '_') break; } for (iCopyTo = _e.columnNo()+1; iCopyTo <= strLen(_selectedLine); iCopyTo++) { curSymbol = subStr(_selectedLine, iCopyTo, 1); if (!strAlpha(curSymbol) && curSymbol != '_') break; } _selectedLine = (iCopyFrom < iCopyTo) ? subStr(_selectedLine, iCopyFrom + 1, iCopyTo - iCopyFrom - 1) : ''; } return _selectedLine; } void FindLinesContainingSelectedText(FreeText _selectedLine) { Args args = new Args(formstr(SysAOTFind)); FormRun sysAOTFindFormRun; FormStringControl containingTextCtrl; FormButtonControl findNowBtnCtrl; ; sysAOTFindFormRun = classFactory.formRunClass(args); sysAOTFindFormRun.init(); sysAOTFindFormRun.run(); // Set the text to find containingTextCtrl = sysAOTFindFormRun.design().controlName(identifierStr(ContainingText)); containingTextCtrl.setFocus(); containingTextCtrl.pasteText(_selectedLine); sysAOTFindFormRun.detach(); // Launch the search process findNowBtnCtrl = sysAOTFindFormRun.design().controlName(identifierStr(FindNow)); findNowBtnCtrl.clicked(); } ; selectedText = GetSelectedTextFromEditor(e); if (selectedText) { // Find the currently open method node treeNode = TreeNode::findNode(e.path()); // Find the parrent node of the method treeNode = TreeNode::findNode(xUtilElements::getNodePathRough(xUtilElements::parentElement(xUtilElements::findTreeNode(treeNode)))); if (treeNode) { treeNode.AOTnewWindow(); FindLinesContainingSelectedText(selectedText); } } }
__________________
Расскажите о новых и интересных блогах по Microsoft Dynamics, напишите личное сообщение администратору. |
|
|
За это сообщение автора поблагодарили: alex55 (5). |
05.01.2010, 22:08 | #2 |
Участник
|
Иван, я тормоз. Можно по-русски?
Что ты делаешь при рефакторинге? |
|
05.01.2010, 22:56 | #3 |
Злыдни
|
Сергей, ну нельзя на выходе из новогоднего штопора так вот сразу мозг напрягать - это вредно, тебе любой скажет Надо потихоньку, сначала какой-нибудь Best Practice почитать, а потом уже и за Кашперука приниматься
А по сути - ну вот смотрю я класс InventUpd_Estimated к примеру. И вижу я там переменную preEstimated. И чтобы понять, где и как эта переменная инициализируется мне надо найти папу - InventUpdate - и поискать в нем эту переменную с помощью Find. Иван как раз и автоматизировал эту часть - поиск переменной в родителе текущего элемента. С наступившим тебя |
|
|
За это сообщение автора поблагодарили: Kabardian (1). |
05.01.2010, 23:04 | #4 |
Талантливый разгвоздяй
|
Спасибо
Иван, спасибо! А то я уже боевую мазоль натер такой нехитрой комбинацией ;--).
|
|
05.01.2010, 23:17 | #5 |
Участник
|
Цитата:
Торможу и не понимаю. Объясните по-русски пожалуйста о чем речь идет? ЗЫ Это ж не я. Иван сам написал после праздников. Вроде интересно. Но понять не могу. |
|
06.01.2010, 09:06 | #6 |
Microsoft Dynamics
|
Поиск по тексту - это как-то не элегантно :-)
Я для тех же целей когда-то вот такой скриптик нарисовал. Скрип строит перекрестные ссылки для всего AOT объекта (если вызван из статического метода, то только по данному методу) и для всех его предков (если объект - класс). Потом показывает сслыки в форме и фильтрует записи по названию-типу-предку выделенной переменной. Если выделения нет, то показывает все ссылки. Удобно тем, что можно быстро найти где переменная объявлена или инициализирована. Некоторый недостаток в том, что код статических методов тоже попадет в перекрестные ссылки и если названия переменных совпадут, то будут найдены лишние вхождения (при поиске по тексту естественно будет то же самое). Наглядный пример: откройте в редакторе \Classes\InventUpd_Reservation\updateNow, выделите переменную movement и вызовите скрипт. Вхождения из \Classes\InventUpd_Reservation\updateReserveFromForm не будут относиться к выделенной переменной, но особых проблем не вызовут. X++: //Show cross-references, complete or filtered by selection, for a class and its super classes #TreeNodeSysNodeType void addIns_CrossReferencesSelected(Editor e) { xRefUpdateTmpReferences xTmpReferences = new xRefUpdateTmpReferences(); TreeNode currentNode = TreeNode::findNode(e.path()); SysDictClass sysDictClass; Args formRunArgs; FormRun formRun; FormDataSource fds; xRefTmpReferences refTable; xRefName selectedText; xRefPath currentNodePath; int startLine = e.selectionStartLine(); int startCol = e.selectionStartCol(); int endCol = e.selectionEndCol(); ; if (e.selectionStartLine() == e.selectionEndLine() && startCol != endCol) { e.firstSelectedLine(); selectedText = strLRTrim(subStr(e.getLine(), e.selectionStartCol(), endCol-startCol)); } currentNodePath = currentNode.treeNodePath(); if (currentNode.applObjectType() != UtilElementType::ClassStaticMethod && currentNode.applObjectType() != UtilElementType::TableStaticMethod) { while(! currentNode.AOTObjectNode()) { currentNode = currentNode.AOTparent(); } } xTmpReferences.fillTmpxRefReferences(currentNode); while (currentNode && currentNode.sysNodeType() == #NT_CLASS) { sysDictClass = new SysDictClass(new SysDictClass(currentNode.applObjectId()).extend()); currentNode = sysDictClass ? sysDictClass.treeNode() : null; if (currentNode) { xTmpReferences.fillTmpxRefReferences(currentNode); } } formRunArgs = new Args(formstr(xRefTmpReferences)); formRunArgs.parmObject(xTmpReferences); formRun = classfactory.formRunClass(formRunArgs); formRun.init(); if (selectedText) { refTable = xTmpReferences.allTmpxRefReferences(); select firstonly refTable where refTable.line == startLine && refTable.Column == startCol && refTable.name == selectedText && refTable.Path == currentNodePath; fds = formRun.dataSource(); fds.query().dataSourceNo(1).addRange(fieldNum(xRefTmpReferences, Kind)).value(queryValue(refTable.Kind)); fds.query().dataSourceNo(1).addRange(fieldNum(xRefTmpReferences, name)).value(queryValue(refTable.name)); fds.query().dataSourceNo(1).addRange(fieldNum(xRefTmpReferences, ParentName)).value(queryValue(refTable.ParentName)); formRun.design().caption(strfmt("Complete cross-reference for %1 '%2'", refTable.Kind, refTable.name)); } formRun.run(); formRun.detach(); } |
|
|
За это сообщение автора поблагодарили: mazzy (5), alex55 (5), Kabardian (3). |
06.01.2010, 17:28 | #7 |
Талантливый разгвоздяй
|
Я бы сказал, что у вас с Иваном разные подходы. Глядя на информативность 6 кликов (из них 1 двойной тратится на выделение переменной), могу сказать, что метод Ивана более информативен :
Обычно на это уходит 12 кликов (1 двойной на выделение переменной). Последний раз редактировалось Kabardian; 06.01.2010 в 17:44. |
|
06.01.2010, 21:19 | #8 |
Участник
|
Цитата:
Цитата:
Кроме того, поиск не работает в предках и потомках. Кроме того, перекрестные ссылки дают возможность отделить места, где переменная читается, от мест, где она изменяет значение. Поэтому насчет информативности - тут можно поспорить |
|
07.01.2010, 00:22 | #9 |
Талантливый разгвоздяй
|
Скрипт Ивана ищет и в предках тоже -- я проверял.
Ок, засчитывается ;--), 1:1. |
|
|
За это сообщение автора поблагодарили: mazzy (2). |
07.01.2010, 08:50 | #10 |
Microsoft Dynamics
|
А с перекрестных ссылок - еще один клик и ты в самом коде на нужном месте. О других плюсах верно сказал mazzy: ложных срабатываний гораздо меньше и сразу видно где переменная объявлена, где присвоено значение и т.п.
Ну вот для потомков набросал. Для корректной работы иерархия типов должна быть обновлена. Если иерархия не построена, то в AX2009 автоматом запустится ее обновление (в AX4 этого нет и надо убрать строчку). X++: //Show cross-references, complete or filtered by selection, for a class and its super and descendant classes #TreeNodeSysNodeType void addIns_CrossReferencesSelected(Editor e) { xRefUpdateTmpReferences xTmpReferences = new xRefUpdateTmpReferences(); TreeNode currentNode = TreeNode::findNode(e.path()); SysDictClass sysDictClass; Args formRunArgs; FormRun formRun; FormDataSource fds; xRefTmpReferences refTable; xRefName selectedText; xRefPath currentNodePath; int startLine = e.selectionStartLine(); int startCol = e.selectionStartCol(); int endCol = e.selectionEndCol(); void findDescendants(classId _classId) { xRefTypeHierarchy typeHierarchy; TreeNode descendantNode; ; while select typeHierarchy where typeHierarchy.BaseType == Types::Class && typeHierarchy.Parent == _classId { descendantNode = new SysDictClass(typeHierarchy.Id).treeNode(); xTmpReferences.fillTmpxRefReferences(descendantNode); findDescendants(typeHierarchy.Id); } } ; if (e.selectionStartLine() == e.selectionEndLine() && startCol != endCol) { e.firstSelectedLine(); selectedText = strLRTrim(subStr(e.getLine(), e.selectionStartCol(), endCol-startCol)); } currentNodePath = currentNode.treeNodePath(); if (currentNode.applObjectType() != UtilElementType::ClassStaticMethod && currentNode.applObjectType() != UtilElementType::TableStaticMethod) { while(! currentNode.AOTObjectNode()) { currentNode = currentNode.AOTparent(); } } xTmpReferences.fillTmpxRefReferences(currentNode); if (xRefTypeHierarchy::findOrCreate(Types::Class, currentNode.applObjectId()).Children) //Remove for AX4 { findDescendants(currentNode.applObjectId()); } while (currentNode && currentNode.sysNodeType() == #NT_CLASS) { sysDictClass = new SysDictClass(new SysDictClass(currentNode.applObjectId()).extend()); currentNode = sysDictClass ? sysDictClass.treeNode() : null; if (currentNode) { xTmpReferences.fillTmpxRefReferences(currentNode); } } formRunArgs = new Args(formstr(xRefTmpReferences)); formRunArgs.parmObject(xTmpReferences); formRun = classfactory.formRunClass(formRunArgs); formRun.init(); if (selectedText) { refTable = xTmpReferences.allTmpxRefReferences(); select firstonly refTable where refTable.line == startLine && refTable.Column == startCol && refTable.name == selectedText && refTable.Path == currentNodePath; fds = formRun.dataSource(); fds.query().dataSourceNo(1).addRange(fieldNum(xRefTmpReferences, Kind)).value(queryValue(refTable.Kind)); fds.query().dataSourceNo(1).addRange(fieldNum(xRefTmpReferences, name)).value(queryValue(refTable.name)); fds.query().dataSourceNo(1).addRange(fieldNum(xRefTmpReferences, ParentName)).value(queryValue(refTable.ParentName)); formRun.design().caption(strfmt("Complete cross-reference for %1 '%2'", refTable.Kind, refTable.name)); } SysUtil::editPathLogicalPos(currentNodePath, startLine, startCol); //attempt to workaround AX2009 behaviour. Remove for AX4 formRun.run(); formRun.detach(); } Последний раз редактировалось Timofey_k; 07.01.2010 в 09:01. |
|
07.01.2010, 11:22 | #11 |
Участник
|
Цитата:
Сообщение от mazzy
Эх, еще бы и для потомков...
Но может вывести ложные срабатывания. Поскольку находит вхождение в комментариях, в строках, а также в более длинных названиях переменных (Например, ищем SysDictClass, а найдет mySysDictClass и SysDictClass_Old) Кроме того, поиск не работает в предках и потомках. Кроме того, перекрестные ссылки дают возможность отделить места, где переменная читается, от мест, где она изменяет значение. Поэтому насчет информативности - тут можно поспорить К примеру, скажем, что нам необходимо в определенном классе изменить все упоминания "ItemId" на "ProductNumber". Врядли бы кому-то было приятно позже читать код, в котором используются только переменные productNumber, но во всех комментариях идет упоминание ItemId Аналогично, при рефакторинге абсолютно все равно, читается из переменной значение, или изменяется. |
|
|
За это сообщение автора поблагодарили: Kabardian (3). |
07.01.2010, 12:23 | #12 |
Талантливый разгвоздяй
|
Цитата:
Цитата:
Use // for both single and multiline (block) comments.
Используйте // для одно- и многострочных комментариев. X++: /* ... */ |
|
Теги |
developer tool, editor script, tools, законченный пример, инструменты, полезное, разработка |
|
|