AXForum  
Вернуться   AXForum > Microsoft Dynamics AX > DAX Blogs
DAX
Забыли пароль?
Зарегистрироваться Правила Справка Пользователи Сообщения за день Поиск

 
 
Опции темы Поиск в этой теме Опции просмотра
Старый 05.01.2010, 19:16   #1  
Blog bot is offline
Blog bot
Участник
 
25,475 / 846 (79) +++++++
Регистрация: 28.10.2006
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  
mazzy is offline
mazzy
Участник
Аватар для mazzy
Лучший по профессии 2015
Лучший по профессии 2014
Лучший по профессии AXAWARD 2013
Лучший по профессии 2011
Лучший по профессии 2009
 
29,472 / 4494 (208) ++++++++++
Регистрация: 29.11.2001
Адрес: Москва
Записей в блоге: 10
Иван, я тормоз. Можно по-русски?
Что ты делаешь при рефакторинге?
__________________
полезное на axForum, github, vk, coub.
Старый 05.01.2010, 22:56   #3  
Yprit is offline
Yprit
Злыдни
Аватар для Yprit
Злыдни
 
419 / 93 (4) ++++
Регистрация: 22.02.2004
Адрес: СПб
Цитата:
Сообщение от mazzy Посмотреть сообщение
Иван, я тормоз. Можно по-русски?
Что ты делаешь при рефакторинге?
Сергей, ну нельзя на выходе из новогоднего штопора так вот сразу мозг напрягать - это вредно, тебе любой скажет Надо потихоньку, сначала какой-нибудь Best Practice почитать, а потом уже и за Кашперука приниматься

А по сути - ну вот смотрю я класс InventUpd_Estimated к примеру. И вижу я там переменную preEstimated. И чтобы понять, где и как эта переменная инициализируется мне надо найти папу - InventUpdate - и поискать в нем эту переменную с помощью Find.
Иван как раз и автоматизировал эту часть - поиск переменной в родителе текущего элемента.

С наступившим тебя
За это сообщение автора поблагодарили: Kabardian (1).
Старый 05.01.2010, 23:04   #4  
Kabardian is offline
Kabardian
Талантливый разгвоздяй
Аватар для Kabardian
 
424 / 338 (12) ++++++
Регистрация: 14.12.2008
Адрес: Москва
Записей в блоге: 14
Thumbs up Спасибо
Иван, спасибо! А то я уже боевую мазоль натер такой нехитрой комбинацией ;--).
Старый 05.01.2010, 23:17   #5  
mazzy is offline
mazzy
Участник
Аватар для mazzy
Лучший по профессии 2015
Лучший по профессии 2014
Лучший по профессии AXAWARD 2013
Лучший по профессии 2011
Лучший по профессии 2009
 
29,472 / 4494 (208) ++++++++++
Регистрация: 29.11.2001
Адрес: Москва
Записей в блоге: 10
Цитата:
Сообщение от Yprit Посмотреть сообщение
Сергей, ну нельзя на выходе из новогоднего штопора так вот сразу мозг напрягать - это вредно, тебе любой скажет
У меня и не получается.
Торможу и не понимаю.

Объясните по-русски пожалуйста о чем речь идет?

ЗЫ Это ж не я. Иван сам написал после праздников. Вроде интересно. Но понять не могу.
__________________
полезное на axForum, github, vk, coub.
Старый 06.01.2010, 09:06   #6  
Timofey_k is offline
Timofey_k
Microsoft Dynamics
Аватар для Timofey_k
Соотечественники
Сотрудники Microsoft Dynamics
 
20 / 50 (2) ++++
Регистрация: 04.07.2006
Адрес: Sydney, Australia
Поиск по тексту - это как-то не элегантно :-)

Я для тех же целей когда-то вот такой скриптик нарисовал. Скрип строит перекрестные ссылки для всего 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  
Kabardian is offline
Kabardian
Талантливый разгвоздяй
Аватар для Kabardian
 
424 / 338 (12) ++++++
Регистрация: 14.12.2008
Адрес: Москва
Записей в блоге: 14
Цитата:
Сообщение от Timofey_k Посмотреть сообщение
Поиск по тексту - это как-то не элегантно :-)
Я бы сказал, что у вас с Иваном разные подходы. Глядя на информативность 6 кликов (из них 1 двойной тратится на выделение переменной), могу сказать, что метод Ивана более информативен :
  • Перекрестные ссылки (Cross-reference)
  • Найти (Find), самое ценное — вырезки из кода:


Обычно на это уходит 12 кликов (1 двойной на выделение переменной).
Миниатюры
Нажмите на изображение для увеличения
Название: Find.png
Просмотров: 659
Размер:	14.4 Кб
ID:	5494   Нажмите на изображение для увеличения
Название: Cross-reference .png
Просмотров: 495
Размер:	13.1 Кб
ID:	5496  


Последний раз редактировалось Kabardian; 06.01.2010 в 17:44.
Старый 06.01.2010, 21:19   #8  
mazzy is offline
mazzy
Участник
Аватар для mazzy
Лучший по профессии 2015
Лучший по профессии 2014
Лучший по профессии AXAWARD 2013
Лучший по профессии 2011
Лучший по профессии 2009
 
29,472 / 4494 (208) ++++++++++
Регистрация: 29.11.2001
Адрес: Москва
Записей в блоге: 10
Цитата:
Сообщение от Timofey_k Посмотреть сообщение
Скрип строит перекрестные ссылки для всего AOT объекта (если вызван из статического метода, то только по данному методу) и для всех его предков (если объект - класс).
Эх, еще бы и для потомков...

Цитата:
Сообщение от Kabardian Посмотреть сообщение
Я бы сказал, что у вас с Иваном разные подходы. Глядя на информативность 6 кликов (из них 1 двойной тратится на выделение переменной), могу сказать, что метод Ивана более информативен
Но может вывести ложные срабатывания. Поскольку находит вхождение в комментариях, в строках, а также в более длинных названиях переменных (Например, ищем SysDictClass, а найдет mySysDictClass и SysDictClass_Old)

Кроме того, поиск не работает в предках и потомках.
Кроме того, перекрестные ссылки дают возможность отделить места, где переменная читается, от мест, где она изменяет значение.

Поэтому насчет информативности - тут можно поспорить
__________________
полезное на axForum, github, vk, coub.
Старый 07.01.2010, 00:22   #9  
Kabardian is offline
Kabardian
Талантливый разгвоздяй
Аватар для Kabardian
 
424 / 338 (12) ++++++
Регистрация: 14.12.2008
Адрес: Москва
Записей в блоге: 14
Цитата:
Сообщение от mazzy Посмотреть сообщение
Кроме того, поиск не работает в предках и потомках.
Скрипт Ивана ищет и в предках тоже -- я проверял.
Цитата:
Сообщение от mazzy Посмотреть сообщение
Кроме того, перекрестные ссылки дают возможность отделить места, где переменная читается, от мест, где она изменяет значение.

Поэтому насчет информативности - тут можно поспорить
Ок, засчитывается ;--), 1:1.
За это сообщение автора поблагодарили: mazzy (2).
Старый 07.01.2010, 08:50   #10  
Timofey_k is offline
Timofey_k
Microsoft Dynamics
Аватар для Timofey_k
Соотечественники
Сотрудники Microsoft Dynamics
 
20 / 50 (2) ++++
Регистрация: 04.07.2006
Адрес: Sydney, Australia
Цитата:
Сообщение от Kabardian Посмотреть сообщение
Найти (Find), самое ценное — вырезки из кода
А с перекрестных ссылок - еще один клик и ты в самом коде на нужном месте. О других плюсах верно сказал mazzy: ложных срабатываний гораздо меньше и сразу видно где переменная объявлена, где присвоено значение и т.п.

Цитата:
Сообщение от 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();
}
Обнаружил пренеприятную особенность в AX2009 - при вызове xTmpReferences.fillTmpxRefReferences для какого-нибудь объекта, окно редактора с этим объектом принудительно закрывается. Пришлось тупо добавить открытие редактора в код. В AX4 это не нужно. Да и вообще есть ощущение, что этот код в AX2009 работает медленнее, чем в AX4.

Последний раз редактировалось Timofey_k; 07.01.2010 в 09:01.
Старый 07.01.2010, 11:22   #11  
kashperuk is offline
kashperuk
Участник
Аватар для kashperuk
MCBMSS
Соотечественники
Сотрудники Microsoft Dynamics
Лучший по профессии 2017
Лучший по профессии 2015
Лучший по профессии 2014
Лучший по профессии 2011
Лучший по профессии 2009
 
4,361 / 2084 (78) +++++++++
Регистрация: 30.05.2004
Адрес: Atlanta, GA, USA
Цитата:
Сообщение от mazzy Посмотреть сообщение
Эх, еще бы и для потомков...


Но может вывести ложные срабатывания. Поскольку находит вхождение в комментариях, в строках, а также в более длинных названиях переменных (Например, ищем SysDictClass, а найдет mySysDictClass и SysDictClass_Old)

Кроме того, поиск не работает в предках и потомках.
Кроме того, перекрестные ссылки дают возможность отделить места, где переменная читается, от мест, где она изменяет значение.

Поэтому насчет информативности - тут можно поспорить
На самом деле, при массовом рефакторинге как раз необходимо, чтобы в результат поиска попали и комментарии, и переменные, которые называются так же, но с добавлением суффикса/префикса.

К примеру, скажем, что нам необходимо в определенном классе изменить все упоминания "ItemId" на "ProductNumber".
Врядли бы кому-то было приятно позже читать код, в котором используются только переменные productNumber, но во всех комментариях идет упоминание ItemId

Аналогично, при рефакторинге абсолютно все равно, читается из переменной значение, или изменяется.
За это сообщение автора поблагодарили: Kabardian (3).
Старый 07.01.2010, 12:23   #12  
Kabardian is offline
Kabardian
Талантливый разгвоздяй
Аватар для Kabardian
 
424 / 338 (12) ++++++
Регистрация: 14.12.2008
Адрес: Москва
Записей в блоге: 14
Цитата:
Сообщение от Timofey_k Посмотреть сообщение
А с перекрестных ссылок - еще один клик и ты в самом коде на нужном месте.
Суть в обзорном просмотре всех возможных вхождений в коде. Здесь еще больше чувствуется польза best-practices, особенно касательно комментариев:
Цитата:
Use // for both single and multiline (block) comments.
Используйте // для одно- и многострочных комментариев.
Не смогут вкусить всю прелесть инструмента Найти / Find те, кто использует для тех же целей конструкцию вида
X++:
/* ... */
Теги
developer tool, editor script, tools, законченный пример, инструменты, полезное, разработка

 

Похожие темы
Тема Автор Раздел Ответов Посл. сообщение
Kashperuk Ivan: AxGoToDeclarationI haven't posted in a while. W... Blog bot DAX Blogs 2 07.06.2010 00:06
Kashperuk Ivan: Running a class from AOT or "How to assign a class to an action menu item?" Blog bot DAX Blogs 0 22.10.2009 20:05
Kashperuk Ivan: “Go to main table” on a RunBase dialog control Blog bot DAX Blogs 0 25.08.2009 08:05
Kashperuk Ivan: Description of ClassBuild class:Today I want to ... Blog bot DAX Blogs 0 26.01.2007 05:51
axaptafreak: AOT search within search results Blog bot DAX Blogs 0 16.11.2006 17:40

Ваши права в разделе
Вы не можете создавать новые темы
Вы не можете отвечать в темах
Вы не можете прикреплять вложения
Вы не можете редактировать свои сообщения

BB коды Вкл.
Смайлы Вкл.
[IMG] код Вкл.
HTML код Выкл.
Быстрый переход

Рейтинг@Mail.ru
Часовой пояс GMT +3, время: 22:22.
Powered by vBulletin® v3.8.5. Перевод: zCarot
Контактная информация, Реклама.