open api: how to launch property to function refactoring
От: Аноним  
Дата: 21.03.08 03:35
Оценка:
Приветствую, уважаемые!

Уникальный слчай: на русскоязычном форуме информации больше чем на английском, поздравляю!)

Но вопрос собственно не в этом. С грехом пополам удалось осилить rename refactoring. Как-то сложновато получилось, по сравнению с интелиджишным open api, но все же работает. Наверняка я что-то не так сделал, но об этом позже, чтоб не распыляться.

Не могут ли уважаемые хранители тайного знания проконсультировать, как бы мне программно запустить Property2Function refactoring? Причем я хочу запускать это дело в пакетном режиме: грубо говоря, я уже имею некую коллекцию IProperty и хочу их всех скопом переделать в методы — т.е. из GUI нужен только прогресс бар.

Я знаю что в апи полно *Property2Function* классов в разных нэймспейсах, но который из них лучше подходит для моей задачи...

Да, я использую последний официальный релиз Resharper — 3.1. Может стоит перейти на 4.0 EAP — может быть там программный доступ к всевозможным рефактори проще?

Всего наилучшего,
Re: open api: how to launch property to function refactoring
От: Аноним  
Дата: 21.03.08 14:47
Оценка:
Сделал первый вариант с помощью JetBrains.ReSharper.Refactorings.Convert.PropertyToFunction.PropertyToFunctionRefactoring — у него самая простая сигнатура:


        private void ConvertPropertiesToMethods(IProperty[] properties)
        {
            foreach (IProperty property in properties)
            {
                bool hasGetter = false, hasSetter = false;
                string getterName = "", setterName = "";

                if (property.IsReadable)
                {
                    hasGetter = true;
                    getterName = "get" + property.ShortName;
                }

                if (property.IsWritable)
                {
                    hasSetter = true;
                    setterName = "set" + property.ShortName;
                }

                //overrided проперти могут висеть в воздухе если их уже от рефакторили через базовый класс (?)
                if (!property.IsReadable && !property.IsWritable)
                    continue;

                PropertyToFunctionRefactoring refactoring = 
                    new PropertyToFunctionRefactoring(property, getterName, setterName,
                        hasGetter, hasSetter, NullProgressIndicator.INSTANCE);

                refactoring.Execute();
            }
        }


Все обернуто в почти все возможные куки и локи которые сумел подсмотреть на этом же форуме:


            // Ensure changes in documents are processed by code analyser
            mPsiManager.CommitAllDocuments();
            // Wait until all caches are built, abort action if user cancelled wait
            if (!mPsiManager.WaitForCaches())
                return;

            // Obtain write lock, so that no changes will occur while we are executing action
            using (WriteLockCookie.Create())
            {
                IProperty[] properties = GetAllProperties (mRootNamespace);//собираем все наши проперти

                //skip "using ModificationCookie = EnsureWritable()"... - может зря я их скипанул?

                // besides the failure above, it will also turn on UNDO functionality in VS
                using (CommandCookie.Create("ConvertPropertiesToMethods"))
                {
                    TransactionResult result = mPsiManager.DoTransaction(
                        delegate
                        {
                            //put real processing code here
                            ConvertPropertiesToMethods(properties );
                        });

                    // respond to transaction failure if any - сюда никогда не попадает, как я не пытался:)  
                    if (!result.Succeded)
                        MessageBox.Show("ConvertPropertiesToMethods failure: " + result.FailureDescription);
                }
            }


Вроде работает. Хотя я не уверен, что не накосячил где-нибудь — проверьте пожалуйста

Вопросы:


1. прогресс-индикатор. Нулевой (как сейчас) мне не подходит — предполагается запускать тул на довольно больших солюшенах — хотелось бы видеть что он не замерз и где примерно процесс находится. Пробовал CommandLineProgressIndicator — он конечно чегото показывает, но во-первых, это изварщение, а во вторых тормоза жуткие.

2. можно ли JetBrains.ReSharper.Refactorings.Property2Function.Property2FunctionRefactoring из ассембли с многообещающим названием JetBrains.ReSharper.NewRefactorings.dll заставить работать в пакетном режиме? я сходу не понял, откуда взять для него IProperty2FunctionData — только черз запуск странички GUI?


Все наилучшего
(надеюсь что мне все же ответят )
Re[2]: open api: how to launch property to function refactor
От: xvost Германия http://www.jetbrains.com/company/people/Pasynkov_Eugene.html
Дата: 21.03.08 15:31
Оценка:
Здравствуйте, Аноним, Вы писали:

А>Все обернуто в почти все возможные куки и локи которые сумел подсмотреть на этом же форуме:


А>
А>.......
А>            using (WriteLockCookie.Create())
А>.......
А>


Лучше написать так:
using (ReadLockCookie.Create())

А>1. прогресс-индикатор. Нулевой (как сейчас) мне не подходит — предполагается запускать тул на довольно больших солюшенах — хотелось бы видеть что он не замерз и где примерно процесс находится. Пробовал CommandLineProgressIndicator — он конечно чегото показывает, но во-первых, это изварщение, а во вторых тормоза жуткие.


У тебя консольное приложение или оно запускается как плагин к РеШарперу в студии?
С уважением, Евгений
JetBrains, Inc. "Develop with pleasure!"
Re[3]: open api: how to launch property to function refactor
От: Аноним  
Дата: 22.03.08 13:28
Оценка:
Здравствуйте, Евгений, спасибо за ответ Все-таки есть контакт

X>Лучше написать так:

X>using (ReadLockCookie.Create())

Спасибо, переделаю. WriteLock получается избыточный потому что я все равно потом CommandCookie делаю?
И, то что без ModificationCookie — это нормально? В принципе я ж перед этим комичу все, так что зачем еще раз проверять на возможность записи, тем более поменяться могут _все_ файлы солюшена — мне лень было их всех туда вписывать

А>>1. прогресс-индикатор. Нулевой (как сейчас) мне не подходит — предполагается запускать тул на довольно больших солюшенах — хотелось бы видеть что он не замерз и где примерно процесс находится. Пробовал CommandLineProgressIndicator — он конечно чегото показывает, но во-первых, это изварщение, а во вторых тормоза жуткие.


X>У тебя консольное приложение или оно запускается как плагин к РеШарперу в студии?


У меня плагин, пока. Просто PropertyToFunctionRefactoring просит JetBrains.Shell.Progress.IProgressIndicator, а там выбор небольшой: NullProgressIndicator да CommandLineProgressIndicator. Первый ничего не рисует (как и обещано в названии), а второй командное окошко создает и медленно-медленно (на порядок разница с первым вариантом) печатает там свои сообщения. Есть еще SubProgressIndicator, на самом деле это примерно то, что мне нужно, но сходу не понятно как его инициализировать.

Вообще хороший вопрос. Насчет плагин или консоль я еще не определился. Смысл какой. Я беру солюшен (может быть довольно большой, несколько проектов, больше тысячи классов) или, другой вариант, какуюто его часть, вплоть до помеченного куска файла и запускаю свой тул. Он делает, если совсем грубо, три вещи: 1) переименовывает мемберы (методы, поля, переменные) в соответсвии с определенной схемой; 2) делает всякие преобразования — из пропертей в геттеры/сеттеры, из энамов делает утилити классы и т.д. там целый список; 3) вставка комментариев в определенных местах, замена каких-то кусков текста. Я всегда думал об плагине, но если вариант с обработкой всего солюшена запустить консолью, наверное быстрее будет? Ну а если плагином, то SubProgressIndicator подошел бы идеально — верхний ползунок показывал бы общий прогресс, а нижний — прогресс конкретной стадии.

И по второму моему вопросу — я првильно взял за основу PropertyToFunctionRefactoring, может лучше на Property2FunctionRefactoring ориентироваться?

Всего наилучшего,
Костя.
Re[4]: open api: how to launch property to function refactor
От: orangy Россия
Дата: 22.03.08 14:00
Оценка:
Здравствуйте, <Аноним>, Вы писали:

А>>>1. прогресс-индикатор. Нулевой (как сейчас) мне не подходит — предполагается запускать тул на довольно больших солюшенах — хотелось бы видеть что он не замерз и где примерно процесс находится. Пробовал CommandLineProgressIndicator — он конечно чегото показывает, но во-первых, это изварщение, а во вторых тормоза жуткие.

Например, так:

using (var pw= new ProgressWindow(true))
{
 bool canceled;

            var result = (ICollection<IOccurence>)
                         pw.ExecuteTask(
                           delegate(IProgressIndicator progress)
                             {
                               // do work, return result
                             },
                           title,
                           out canceled);
            if (!canceled)
              return result;
}
... << RSDN@Home 1.2.0 alpha rev. 655>>
"Develop with pleasure!"
Re[4]: open api: how to launch property to function refactor
От: xvost Германия http://www.jetbrains.com/company/people/Pasynkov_Eugene.html
Дата: 22.03.08 16:33
Оценка:
Здравствуйте, Аноним, Вы писали:

А>Спасибо, переделаю. WriteLock получается избыточный потому что я все равно потом CommandCookie делаю?


Нет. Общий ReadLock нужен чтобы никто не взял WriteLock без твоего ведома. WriteLock берут более низкоуровневые операции. CommandCookie — это по сути групповой undo для студии

А>И, то что без ModificationCookie — это нормально? В принципе я ж перед этим комичу все, так что зачем еще раз проверять на возможность записи, тем

более поменяться могут _все_ файлы солюшена — мне лень было их всех туда вписывать

ModificationCookie — запрос на разрешение модифицировать файлы (у сорсконтроля). Брать руками его не обязательно если делается транзакция (она сама все сделает)

А>У меня плагин, пока. Просто PropertyToFunctionRefactoring просит JetBrains.Shell.Progress.IProgressIndicator, а там выбор небольшой: NullProgressIndicator да CommandLineProgressIndicator. Первый ничего не рисует (как и обещано в названии), а второй командное окошко создает и медленно-медленно (на порядок разница с первым вариантом) печатает там свои сообщения. Есть еще SubProgressIndicator, на самом деле это примерно то, что мне нужно, но сходу не понятно как его инициализировать.


Тебе нужена например вот такая конструкция:

using (var progressWindow = new SyncProgressWindow())
{
  progressWindow.ExecuteTaskNonCancelable(delegate (IProgressIndicator progress) {...});
}


Внутри делегата уже есть прогресс-бар.
Это простейший вариант и скорее всего он тебя не устроит (т.к. GUI будет замирать). Более правильно переносить все в другой поток. Об этом расскажу чуть попозже — это сделать сложнее по ряду причин

А>И по второму моему вопросу — я првильно взял за основу PropertyToFunctionRefactoring, может лучше на Property2FunctionRefactoring ориентироваться?


Property2FunctionRefactoring
С уважением, Евгений
JetBrains, Inc. "Develop with pleasure!"
Re[5]: open api: how to launch property to function refactor
От: Аноним  
Дата: 23.03.08 15:43
Оценка:
Здравствуйте, xvost, Вы писали:

X>Тебе нужена например вот такая конструкция:


X>
X>using (var progressWindow = new SyncProgressWindow())
X>{
X>  progressWindow.ExecuteTaskNonCancelable(delegate (IProgressIndicator progress) {...});
X>}
X>


Работает как часики. Единственно, я заменил анонимные методы на именные, но это дело вкуса. Код пришлю попозже, когда все более-менее причешится.

X>Внутри делегата уже есть прогресс-бар.


Да, я начал маленько (совсем маленько) понимать логику, даже поигрался разными TaskExecutor-ами и ProgressWindow-ами — прикольно.

X>Это простейший вариант и скорее всего он тебя не устроит (т.к. GUI будет замирать). Более правильно переносить все в другой поток. Об этом расскажу чуть попозже — это сделать сложнее по ряду причин


замирание GUI — это в смысле нельзя бэкграундом ничего редактировать? Меня это вполне устраивает — это именно то что мне нужно

Соответсвтенно новая порция вопросов

1. Как бы я не запускал прогресс — через окно, или через консоль — при любом раскладе по окончании процесса студия открывает (а решарпер перепаршивает) все поправленные файлы. Можно эту фичу убрать каким-нибудь ключиком? На тестовом солюшене это работает терпимо... но с трудом себе представляю тысячу открытых файлов в студии, когда дело дойдет до реальных проектов

2. SyncProgressWindow не дает (последовательно) запускать несколько задач в одном окне, хотя допустим CommandLineTaskExecutor допускает это делать:

    using (CommandLineTaskExecutor executor = new CommandLineTaskExecutor())
    {
        executor.ExecuteTaskNonCancelable(GetAllMembersTask, "GetAllMembersTask");
        executor.ExecuteTaskNonCancelable(ConvertPropertiesToMethodsTask, "ConvertPropertiesToMethodsTask");
    }


А SyncProgressWindow выдает прерывание на такую конструкцию и приходится запускать новое окно для каждой новой задачи:

    SyncProgressWindow progressWindow = new SyncProgressWindow();
    progressWindow.Text = "My window title";
    progressWindow.ExecuteTaskNonCancelable(GetAllMembersTask, "Not used title");
    progressWindow.Dispose();
    progressWindow = new SyncProgressWindow();
    progressWindow.Text = "My window title";
    progressWindow.ExecuteTaskNonCancelable(ConvertPropertiesToMethodsTask, "Not used title");
    progressWindow.Dispose();



3. Кстати, насчет CommandLineTaskExecutor. Я так понял (вернее, это мои домыслы), отдельное черное консольное окошко для него открывается только в отладочном режиме, а при реальной эксплуатации плагина весь этот текст будет сыпаться в Output окошко студии — примерно как выдается лог компилятора?? Это был бы не плохой вариант, можно сэкономить на лог файле и может быть даже чуть-чуть побыстрее GUI-бегунка??

А>>И по второму моему вопросу — я првильно взял за основу PropertyToFunctionRefactoring, может лучше на Property2FunctionRefactoring ориентироваться?


X>Property2FunctionRefactoring


Спасибо, завтра попытаюсь его прикрутить

Еще раз СПАСИБО за ответы , без вас бы я с ума сошел с этим решарпером
всего наилучшего,
Костя.
Re[6]: open api: how to launch property to function refactor
От: orangy Россия
Дата: 23.03.08 16:12
Оценка: 1 (1)
Здравствуйте, <Аноним>, Вы писали:

А>Еще раз СПАСИБО за ответы , без вас бы я с ума сошел с этим решарпером

Пожалуйста, заходите еще.

А>всего наилучшего,

А>Костя.
и вам с ума не сойти,
Разработчики ReSharper

... << RSDN@Home 1.2.0 alpha rev. 655>>
"Develop with pleasure!"
Re[7]: open api: how to launch property to function refactor
От: Аноним  
Дата: 24.03.08 03:20
Оценка:
Здравствуйте, orangy, Вы писали:
O>и вам с ума не сойти,
O>Разработчики ReSharper

+1

Я в курсе что разработчики Мы с тобой, кстати, земляки — я с Барнаула

Всего,
Костя
Re[6]: open api: how to launch property to function refactor
От: xvost Германия http://www.jetbrains.com/company/people/Pasynkov_Eugene.html
Дата: 24.03.08 07:32
Оценка: 1 (1)
Здравствуйте, Аноним, Вы писали:

А>1. Как бы я не запускал прогресс — через окно, или через консоль — при любом раскладе по окончании процесса студия открывает (а решарпер перепаршивает) все поправленные файлы. Можно эту фичу убрать каким-нибудь ключиком? На тестовом солюшене это работает терпимо... но с трудом себе представляю тысячу открытых файлов в студии, когда дело дойдет до реальных проектов



DocumentSettings.Instance.SaveDocumentsAfterModifications = true;


Только не забудь потом старое значение вернуть туда

А>2. SyncProgressWindow не дает (последовательно) запускать несколько задач в одном окне, хотя допустим CommandLineTaskExecutor допускает это делать:


Да, это так.
С уважением, Евгений
JetBrains, Inc. "Develop with pleasure!"
Re[7]: open api: how to launch property to function refactor
От: Аноним  
Дата: 24.03.08 11:18
Оценка:
Здравствуйте, xvost, Вы писали:


X>
X>DocumentSettings.Instance.SaveDocumentsAfterModifications = true;
X>


Работает, только ундо уже не дает делать — может и CommandCookie тогда не нужен?) Вообще для моего приложения логично. Если делаю солюшен целиком — включаю эту фичу — все равно глазами все изменения не пересмотришь перед сохранинием, пускай уж все и сразу сохраняет. А если делаю кусок файла или несколько файлов, — то выключаю и тогда можно посмотреть какие еще файлы затронуло и вообще полюбоваться на изменения

Всего,
Костя.
Re: родил
От: Аноним  
Дата: 24.03.08 15:34
Оценка:
вроде родил в первом приближении
привожу внизу с небольшими купюрами, может комунибудь пригодится.

Cначала ActionHandler — здесь я по-возможности оставил только решарперовскую обвеску — куки-локи и прогрессы.

    /// <summary>
    /// Handles BatchAction action, see Actions.xml
    /// </summary>
    /// <remarks>
    /// The class contains Reshrper's plugin workflow logics: ActionHandler implementation,
    /// ReadLock- and Command- cookies, ProgressIndicator, etc.
    /// 
    /// Concrete refactoring logic placed in separate classes. 
    /// </remarks>
    [ActionHandler("Aspose.Porter.BatchAction")]
    public class BatchAction : IActionHandler
    {
        ///<summary>
        /// Updates action visual presentation. If presentation.Enabled is set to false, Execute
        /// will not be called.
        ///</summary>
        ///<param name="context">DataContext</param>
        ///<param name="presentation">presentation to update</param>
        ///<param name="nextUpdate">delegate to call</param>
        public bool Update(IDataContext context, ActionPresentation presentation, DelegateUpdate nextUpdate)
        {
            // check, that current context has solution opened
            return context.CheckAllNotNull(DataConstants.SOLUTION);
        }

        ///<summary>
        /// Executes action. Called after Update, that set ActionPresentation.Enabled to true.
        ///</summary>
        ///<param name="context">DataContext</param>
        ///<param name="nextExecute">delegate to call</param>
        public void Execute(IDataContext context, DelegateExecute nextExecute)
        {
            //Get solution and psi manager from context in which action is executed
            if (!CheckInit(context))
                return;

            // Ensure changes in documents are processed by code analyser
            mPsiManager.CommitAllDocuments();
            // Wait until all caches are built, abort action if user cancelled wait
            if (!mPsiManager.WaitForCaches())
                return;

            //Ensure VS doesn't open _all_ changed documents.
            //Note: when this option is on, user can't undo the Command even if we using CommandCookie.
            DocumentSettings.Instance.SaveDocumentsAfterModifications = true;

            // Obtain write lock, so that no changes will occur while we are executing action
            using (ReadLockCookie.Create())
            {
                //option with comand line progress window, not used now
//                using (CommandLineTaskExecutor executor = new CommandLineTaskExecutor())
//                {
//                    executor.ExecuteTaskNonCancelable(CollectMembersTask, "CollectMembersTask");
//                    executor.ExecuteTaskNonCancelable(ConvertPropertiesToMethodsTask, "ConvertPropertiesToMethodsTask");
//                    // etc.  
//                }

                //option with GUI progress window
                //contrary to comand line option we can't launch several tasks in one window
                using (SyncProgressWindow progressWindow = new SyncProgressWindow())
                    progressWindow.ExecuteTaskNonCancelable(CollectMembersTask, "Not used title");

                using (SyncProgressWindow progressWindow = new SyncProgressWindow())
                    progressWindow.ExecuteTaskNonCancelable(RenameMethodsTask, "Not used title");
                
                using (SyncProgressWindow progressWindow = new SyncProgressWindow())
                    progressWindow.ExecuteTaskNonCancelable(RenameConstantsTask, "Not used title");

                using (SyncProgressWindow progressWindow = new SyncProgressWindow())
                    progressWindow.ExecuteTaskNonCancelable(ConvertPropertiesToMethodsTask, "Not used title");

                using (SyncProgressWindow progressWindow = new SyncProgressWindow())
                    progressWindow.ExecuteTaskNonCancelable(ConvertEnumsTask, "Not used title");
            }

            //@todo remove debug - are we need this here?
            // Ensure changes in documents are processed by code analyser
            mPsiManager.CommitAllDocuments();

            //Return back VS's defaults.
            DocumentSettings.Instance.SaveDocumentsAfterModifications = false;

            //Write log to file
            using (StreamWriter writer = new StreamWriter(mLogFile))
                writer.Write(mLogMessage);

            //Display log file inside VS
            EditorManager editorManager = EditorManager.GetInstance(mSolution);
            editorManager.OpenProjectFile(mLogFile, true);
        }

        //TaskWithProgress.Invoke delegate
        //simple progress without percent calculations used
        private object CollectMembersTask(IProgressIndicator progress)
        {
            progress.TaskName = "0/5 Collecting all the members that should be changed:";
            progress.Start();

            mCollector = new MemberCollector(mSolution, mPsiManager, mRootNamespace);
            mCollector.CollectMembers();

            progress.Stop();
            return true;
        }

        //some tasks skipped...

        //TaskWithProgress.Invoke() delegate
        private object ConvertPropertiesToMethodsTask(IProgressIndicator progress)
        {
            progress.TaskName = "3/5 Converting Properties To Methods:";
            progress.Start(mProperties.Count);
            
            // besides the failure above, it will also turn on UNDO functionality in VS
            using (CommandCookie.Create("ConvertPropertiesToMethods"))
            {
                PropertiesConverter propertiesConverter = new PropertiesConverter();
                TransactionResult result =
                    mPsiManager.DoTransaction(propertiesConverter.Convert, mCollector.Properties, progress);

                // respond to transaction failure if any
                if (!result.Succeded)
                    MessageBox.Show("ConvertPropertiesToMethods failure: " + result.FailureDescription);
            }

            progress.Stop();
            return true;
        }

        /// <summary>
        /// Check Solution and PSI manager is available and language is C#.
        /// Initializes solution and psi manager fields.
        /// </summary>
        private bool CheckInit(IDataContext context)
        {
            mSolution = context.GetData(DataConstants.SOLUTION);
            mPsiManager = PsiManager.GetInstance(mSolution);
            PsiLanguageType languageType = context.GetData(DataConstants.PSI_LANGUAGE_TYPE);

            return mSolution != null &&
                   mPsiManager != null &&
                   languageType != null &&
                   languageType.Name == "CSHARP";
        }

        private ISolution mSolution;
        private PsiManager mPsiManager;
        private readonly string mRootNamespace = "";

        private MemberCollector mCollector;
       
        //some fields skipped
}


Класс, который бегает по кэшированному дереву (мне показалось так будет быстрее, чем по живому дереву скакать?) и собирает все мемберы которые надо покорежить.


    /// <summary>
    /// Collecting all the members that should be changed.
    /// </summary>
    public class MemberCollector
    {
        public MemberCollector(ISolution solution, PsiManager psiManager, string rootNamespace)
        {
            mSolution = solution;
            mPsiManager = psiManager;
            mRootNamespace = rootNamespace;
        }

        public void CollectMembers()
        {
            //Get root namespace
            DeclarationsCacheScope solutionScope = DeclarationsCacheScope.SolutionScope(mSolution, false);
            IDeclarationsCache solutionCache = mPsiManager.GetDeclarationsCache(solutionScope, true);
            INamespace ns = solutionCache.GetNamespace(mRootNamespace);

            //Iterate all members, collect elements to be performed
            IterateNamespace(solutionCache, ns);
        }

        private void IterateNamespace(IDeclarationsCache declarationsCache, INamespace ns)
        {
            foreach (IDeclaredElement element in ns.GetNestedElements(declarationsCache))
            {
                if (element is INamespace)
                {
                    INamespace nameSpace = (INamespace)element;
                    //recursive call
                    IterateNamespace(declarationsCache, nameSpace);
                }
                else if (element is ITypeElement)
                {
                    IterateElement(element);
                }
            }
        }

        private void IterateElement(IDeclaredElement element)
        {
            if (element is ITypeElement)
            {
                ITypeElement type = (ITypeElement)element;
                foreach (ITypeMember member in type.GetMembers())
                {
                    if (member is IField)
                    {
                        string newName = NameUtils.ChangeFieldName((IField)member);
                        if (member.ShortName != newName)
                            mFields.Add((IField)member, newName);
                    }
                    if (member is IMethod)
                    {
                        string newName = NameUtils.ChangeMethodName(member.ShortName);
                        if (member.ShortName != newName)
                            mMethods.Add((IMethod)member, newName);
                    }
                    if (member is IProperty)
                    {
                        mProperties.Add((IProperty)member);
                    }
                    //nested types
                    if (member is ITypeElement)
                    {
                        IterateElement(member);
                    }
                }
            }
        }

        public Collection<IProperty> Properties
        {
            get { return mProperties; }
        }

        public Dictionary<IMethod, string> Methods
        {
            get { return mMethods; }
        }

        public Dictionary<IField, string> Fields
        {
            get { return mFields; }
        }

        private ISolution mSolution;
        private PsiManager mPsiManager;
        private readonly string mRootNamespace;

        /// <summary>
        /// Collections of members whose name should be changed:
        /// Dictionary<ITypeMember member, string newName>.
        /// </summary>
        private Dictionary<IField, string> mFields = new Dictionary<IField, string>();
        private Dictionary<IMethod, string> mMethods = new Dictionary<IMethod, string>();
        private Collection<IProperty> mProperties = new Collection<IProperty>();
    }


вообщето ITypeMember имеет проперти котоые отдают все поля, методы, конструкторы и т.д. этого типа, но мне показалось что перебрать всех в одном цикле будет удобнее и быстрее 8), в общем уже неохота переделывать.

Ну и самый мощный класс, который проперти в методы конвертит:

       /// <summary>
        /// Converts all properties in given table to methods.
        /// </summary>
        /// <remarks>
        /// JetBrains.ReSharper.Psi.TransactionHandler.Invoke() delegate. Should be used inside 
        /// transaction and wrapped by Read and Command cookies - <see cref="BatchPortingAction.ConvertPropertiesToMethodsTask"/>.
        /// </remarks>
        public void Convert(object[] args)
        {
            //(IProperty[] properties, IProgressIndicator progress)
            Debug.Assert(args[0] is IProperty[]);
            IProperty[] properties = (IProperty[])args[0];
            Debug.Assert(args[1] is IProgressIndicator);
            IProgressIndicator progress = (IProgressIndicator)args[0];

            foreach (IProperty property in properties)
            {
                //PropertyToFunctionRefactoring converts overridden properties without
                //questions so some of them can be already converted from base type
                if (!property.IsReadable && !property.IsWritable)
                {
                    progress.Advance(1);
                    continue;
                }

                progress.CurrentItemText = property.GetContainingType().CLRName + "." + property.ShortName;
                //do the thing
                ConvertPropertyToMethod(property);

                progress.Advance(1);
            }
        }

        private void ConvertPropertyToMethod(IProperty property)
        {
            //@todo 1 sk check for namesake indexer signatures
            //@todo 1 sk correct boolean prefixes
            //@todo 1 sk check for name conflicts when new getter/setter gets the same name as existing method

            bool hasGetter = false, hasSetter = false;
            string getterName = "", setterName = "";

            if (property.IsReadable)
            {
                hasGetter = true;
                getterName = "get" + property.ShortName;
            }

            if (property.IsWritable)
            {
                hasSetter = true;
                setterName = "set" + property.ShortName;
            }

            //@todo remove debug
            string debugStr = string.Format("property {0}.{1} changed to getter/setter: {2}/{3}{4}",
                                            property.GetContainingType().CLRName, property.ShortName, getterName, setterName, CR);

            PropertyToFunctionRefactoring refactoring =
                new PropertyToFunctionRefactoring(property, getterName, setterName,
                                                  hasGetter, hasSetter, NullProgressIndicator.INSTANCE);

            refactoring.Execute();
        }

    }


Ну и традиционная партия вопросов

1. на свежий взгляд разработчиков решарпера — нет ли в этом коде какой-нибудь фигни?

2. правильно я использую кэшированное дерево? получается я один раз читаю этот кэш, а потом несколько раз прогоняю по нему разные рефактори. я исхожу из того что разные имена полей, методов и пропертей друг друга не касаются и будут модифицироваться разные ноды дерева. единственное в чем я сомневаюсь, это когда запущу переделку энамов в int константы — получится, что поменяются сигнатуры методов использующих энамы. Не будет ли конфликта из-за того что имя метода уже поменялось к тому времени как я захочу поменять его сигнатуру? но опять же, сам метод же ни куда не денется, поменяется только его ShortName...

3. давно хотел спросить, нельзя ли Property2Function refactory настроить так чтобы она писала геттер выше, а сеттер ниже? Как делается сейчас — сеттер перед геттером — мне (имо) кажется не общепринятым.

Всего,
Костя.
Re[2]: родил
От: V.Petrovski Беларусь  
Дата: 25.03.08 08:06
Оценка: 1 (1)
Здравствуйте, <Аноним>, Вы писали:

[skipped]
Я бы не создавал 5 раз новое окно для погресса, а сделал вот так:

//option with GUI progress window
//contrary to comand line option we can't launch several tasks in one window
using (SyncProgressWindow progressWindow = new SyncProgressWindow())
    progressWindow.ExecuteTaskNonCancelable(Execute, "Not used title");
        
private object Execute(IProgressIndicator progress)
{
    try
    {
        progress.Start(5);
        
        using (SubProgressIndicator i = new SubProgressIndicator(progress, 1.0))
            CollectMembersTask(i);
        
        using (SubProgressIndicator i = new SubProgressIndicator(progress, 1.0))
            RenameMethodsTask(i);
        
        using (SubProgressIndicator i = new SubProgressIndicator(progress, 1.0))
            RenameConstantsTask(i);
        
        using (SubProgressIndicator i = new SubProgressIndicator(progress, 1.0))
            ConvertPropertiesToMethodsTask(i);
        
        using (SubProgressIndicator i = new SubProgressIndicator(progress, 1.0))
            ConvertEnumsTask(i);
        
        return null;
    }
    finally
    {
        progress.Stop();
    }
}


SafeDevelop
Re[3]: родил
От: Аноним  
Дата: 25.03.08 12:42
Оценка:
Здравствуйте, V.Petrovski, Вы писали:

VP>Здравствуйте, <Аноним>, Вы писали:


VP>[skipped]

VP>Я бы не создавал 5 раз новое окно для погресса, а сделал вот так:

шикарно, это именно то что мне надо было
Re[4]: родил
От: Аноним  
Дата: 26.03.08 14:28
Оценка:
А как мне у кэшированого элемента поменять права доступа (напр. с public на internal) и прочие модификаторы (readonly, static, ...)?
Т.е. с помощью IDeclarationsCache я получаю корневой IDeclaredElement и гуляю дальше по нему — видеть все эти модификаторы я могу — геттеры на них есть, а сеттеров нет, или они гдето глубже спрятаны. Или надо по другому дереву гулять?
Re[5]: родил
От: qxWork Голландия http://www.jetbrains.com/company/people/Coox_Sergey.html
Дата: 26.03.08 16:53
Оценка: 1 (1)
Здравствуйте, Аноним, Вы писали:

А>А как мне у кэшированого элемента поменять права доступа (напр. с public на internal) и прочие модификаторы (readonly, static, ...)?

А>Т.е. с помощью IDeclarationsCache я получаю корневой IDeclaredElement и гуляю дальше по нему — видеть все эти модификаторы я могу — геттеры на них есть, а сеттеров нет, или они гдето глубже спрятаны. Или надо по другому дереву гулять?

перейди к декларациям, с элементами кэша ты ничего сделать не сможешь (например, если они из ассембли)
Пример
{code}
IDeclaredElement declaredElement;
foreach( var decl in declaredElement.GetDeclarations() ) {
var modifiersOwner = decl as IModifiersOwnerDeclaration;
if( modifiersOwner != null )
modifiersOwner.SetAccesRights( AccessRights.PUBLIC );
}
{code}
Re[6]: родил
От: k.sid  
Дата: 28.03.08 04:31
Оценка:
Здравствуйте, qxWork, Вы писали:

W>перейди к декларациям, с элементами кэша ты ничего сделать не сможешь (например, если они из ассембли)

W>Пример

Спасибо, помогло

заодно упорядочил маленько (для себя) иерархии AST и иерархии сущностей. Поначалу немного сбивает с толку нестандартная техника — иерархия построена на интерфейсах, что допускает множественное наследование. Но со временем привыкаешь Вот что у меня примерно вышло:

1. AST — это то что сидит в Jetbrains.Resharper.PSI.Tree: IElement, IDeclaration — верхний уровень, их потомки ITypeDeclaration и ITypeMemberDeclaration — середина, и листья типа IClassDeclaration, IMethodDeclaration и т.д. (оставляю за скобками всякие ITreeNode и *CommentNode, т.к. до них пока еще мои интересы не дошли

2. А иерархия сущностей (у ней есть какая-нибудь аббревиатура?) — это то что сидит в Jetbrains.Resharper.PSI. Тут все проще: на верху IDeclaredElement, в середине ITypeElement и ITypeMember и в самом низу листья типа IClass, IMethod, IField и т.д.

3. Можно легко "перелазить" с одного дерева на другое: IDeclaredElement.GetDeclarations() — переходим от сущностей к AST; IDeclaration.DeclaredElement — от AST к сущностям.

4. По иерархии сущностей мы гуляем используя INamespace.GetNestedElements(declarationsCache) + ITypeElement.GetMembers() — для меня вполне удобно, как бы более объектно-ориентированно, чтоли. По AST можно гулять несколькими способами, сейчас я использую IRecursiveElementProcessor. Но зачастую удобнее (опять же для меня) дойти до нужного узла по иерархии сущностей и только от туда "перелезть" на AST. Например, грубо говоря, мне надо поменять все (или, еще лучше, конкретные) readonly поля на константы (т.е. модификатор readonly на const) — я могу: 1)прошерстить все AST (в котором на порядок больше узлов, чем в иерархии сущностей) в поиске этих конкретных полей и модификаторов или 2) дойти по иерархии сущностей до искомых полей и только там перелезть на AST для подмены модификаторов. Второй подход должен быть(?) более производительным, хотя в количестве строчек кода разницы практически не будет.


Кстати, у меня возникла некая путаница в терминах: AST(Abstract Syntax Tree) и PSI(?) — это одно и тоже? А у иерархии сущностей какая аббревиатура?)

И заодно маленько не понятно, почему я могу, допустим, поменять имя IClass-а, IMethod-а и т.д. (т.е. через иерархию сущностей), но не могу поменять их модификаторы?

Т.е., получается вроде как AST — это первичная вещь, а иерархия сущностей генерится с нее и будет валидна ровно до тех пор пока AST не поменяется, вернее пока не поменяется тот узел/узлы с которых генерилась эта конкретная сущность?

Всего наилучшего,
Костя

Кстати, наконец смог зарегистрироваться на сервере, через рабочий адрес, — мой домашний провайдер почемуто упорно режет письма с вашего сайта.
Re[7]: родил
От: xvost Германия http://www.jetbrains.com/company/people/Pasynkov_Eugene.html
Дата: 28.03.08 05:12
Оценка:
Здравствуйте, k.sid, Вы писали:

KS>Кстати, у меня возникла некая путаница в терминах: AST(Abstract Syntax Tree) и PSI(?) — это одно и тоже? А у иерархии сущностей какая аббревиатура?)


Не совсем. AST — это синтаксическое дерево, результат парсинга исходного текста.
PSI (Program Structure Interface) — Это все вместе. И AST, и "иерархия сущностей", и методы работы с ними, и т.д. Просто обобщенной название подсистемы проекта.

KS>И заодно маленько не понятно, почему я могу, допустим, поменять имя IClass-а, IMethod-а и т.д. (т.е. через иерархию сущностей), но не могу поменять их модификаторы?


Не можешь. Иерархия сущностей (IDeclaredElement) — исключительно readonly. Что-либо менять можно только в AST, что имеет прямое отражение в тексте файла. И declared element'ы из этого потом выводятся.

KS>Т.е., получается вроде как AST — это первичная вещь, а иерархия сущностей генерится с нее и будет валидна ровно до

тех пор пока AST не поменяется, вернее пока не поменяется тот узел/узлы с которых генерилась эта конкретная сущность?

Именно так. Плюс в дополнение declared element'ы генерируются из метаданных assembly'ей.

Только насчет "первичности" — первичен ТЕКСТ пользовательской программы. AST из него тоже строится (т.е. парсится текст) по мере необходимости
С уважением, Евгений
JetBrains, Inc. "Develop with pleasure!"
Re[8]: родил
От: k.sid  
Дата: 28.03.08 05:30
Оценка:
Здравствуйте, xvost, Вы писали:

KS>>И заодно маленько не понятно, почему я могу, допустим, поменять имя IClass-а, IMethod-а и т.д. (т.е. через иерархию сущностей), но не могу поменять их модификаторы?


X>Не можешь. Иерархия сущностей (IDeclaredElement) — исключительно readonly. Что-либо менять можно только в AST, что имеет прямое отражение в тексте файла. И declared element'ы из этого потом выводятся.


Ааа, ну да, я же имена меняю не напрямую, а через рефактори которой скармливаю сущности.
Re: open api: отладка без GUI
От: k.sid  
Дата: 08.04.08 08:57
Оценка:
Приветствую, уважаемые!

Давненько я вас не терзал Сам уже почти начал справляться, но критическая масса вопросов переплескивает

Начну с самого для меня болезненного. Я писал, у меня плагин работает в пакетном режиме — тупо шерстит весь солюшен и гуи ему по большому счету не нужен. Все бы ничего, но при отладке это гуи добивает больше всего: сначала ждешь пока загризится отладочная студия; затем тыкаешь в меню, чтоб запустить плагин; затем ждешь, когда студия выгрузится; в случае эксепшенов, смотришь в это окошко репортера — вылавливаешь там номера строчек, откуда прилетело... Не технологично как-то получается

Соответственно, вопрос. Есть ли у решарпера режим типа пакетного, без запуска студийного гуи? Или, еще вариант, можно ли перегружать плагин, не перегружая отладочную студию?

Ну и еще вопрос из области гуи Насчет меню. Делаю все как описано. Вставляется без проблем. Но. Старые меню остаются. Т.е. решил я переименовать плагин, передвинуть меню в другое место и т.д. — пожалуйста новый переименованный пункт появляется, но старый _остается тоже_, сереньким. У меня этих сереньких уже достаточно накопилось

Ну, и чтоб добить эти меню. Во всех описаниях плагинское подменю вставляется внутрь меню ReSharper. А как вставить свое меню в верхний уровень, допустим перед Help или после Resharper?

Всего наилучшего,
Костя.
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.