Pull to refresh

Небольшой Add-In для Visual Studio

Reading time7 min
Views6.9K
Когда solution-файл содержит достаточно большое число проектов, сборка бинарных файлов превращается в процесс, требующий ощутимого количества времени, а отчет о сборке в простыню размером в несколько мегабайт. Лично у меня подобная строка в самом конце при таких масштабах вызывает недоумение:

========== Build: 258 succeeded, 1 failed, 40 up-to-date, 1 skipped ==========

А помимо недоумения закономерный вопрос: а что, собственно, сломалось? Есть, конечно, вкладка «Error list», но она к сожалению не показывает названий проектов — только файлы, а при таком объеме исходного кода, да с учетом того факта, что над этим solution'ом работает большая команда, довольно проблематично определять на память принадлежность того или иного файла к определенному проекту (читай определять виновных в сломанной сборке). Пролистывать же отчет в поисках имени проекта, содержащего ошибки, на мой взгляд, не совсем целесообразно.

Принимая во внимание всё вышесказанное, я решил совместить приятное с полезным, получив небольшое представление о том, что же такое VS Exstensibility, снабдив при этом студию маленьким «удобством». «Удобство» должно позволять одним кликом мыши отсеять все ненужное из отчета о сборке, оставив только сообщения об ошибках и имена проектов, их содержащие.


Всё оказалось довольно просто. Начиная с готового шаблона проекта:

image

и заканчивая довольно очевидным набором классов, который позволяет практически не заглядывать в документацию, пользуясь лишь подсказками редактора.

После создания проекта генерируется довольно много кода (выбор языка предоставляется; в моем случае — C#). По сути интересны там только два места: методы OnConnect и Exec, где первый, как несложно догадаться инициализация, а второй непосредственная реализация нашей логики.

OnConnect нам необходим только для того, чтобы сделать новый пункт в меню «Tools». По факту, в сгенерированном для нас коде всё это уже есть, нам остаётсяь только подкорректировать само создание команды:
        try
        {
          //Add a command to the Commands collection:
          Command command = commands.AddNamedCommand2(_addInInstance,
            "BuildStat",
            "Show failed build parts",
            "Provides information about failed parts of performed solution build",
            true,
            56,
            ref contextGUIDS,
            (int)vsCommandStatus.vsCommandStatusSupported + (int)vsCommandStatus.vsCommandStatusEnabled,
            (int)vsCommandStyle.vsCommandStylePictAndText,
            vsCommandControlType.vsCommandControlTypeButton);

          //Add a control for the command to the tools menu:
          if((command != null) && (toolsPopup != null))
          {
            command.AddControl(toolsPopup.CommandBar, 1);
          }
        }
        catch(System.ArgumentException)
        {
          //If we are here, then the exception is probably because a command with that name
          // already exists. If so there is no need to recreate the command and we can
          // safely ignore the exception.
        }

* This source code was highlighted with Source Code Highlighter.


В итоге получим:

image

Остается реализовать фильтрацию отчета о сборке. Для этого, нам необходимо получить сам отчет из окна «Output/Build», оставить только необходимое, создать в окне «Output» новую панель и заполнить ее результатами работы. Соответственно, метод Exec примет следующий вид:
    public void Exec(string commandName, vsCommandExecOption executeOption, ref object varIn, ref object varOut, ref bool handled)
    {
      handled = false;
      if(executeOption == vsCommandExecOption.vsCommandExecOptionDoDefault)
      {
        if(commandName == "BuildStat.Connect.BuildStat")
        {
          handled = true;

          foreach (OutputWindowPane pane in _applicationObject.ToolWindows.OutputWindow.OutputWindowPanes)
          {
            if(pane.Name == "Build")
            {
              // нашлась нужная нам вкладка

              TextDocument textDocument = pane.TextDocument;
              textDocument.Selection.StartOfDocument(false);
              textDocument.Selection.EndOfDocument(true);

              String buildLog = textDocument.Selection.Text;
              if(buildLog.Length > 0)
              {
                string parsedLog = Parse(buildLog);
                if (parsedLog.Length > 0)
                  Output(parsedLog);
              }
              
              break;
            }
          }

          return;
        }
      }
    }

* This source code was highlighted with Source Code Highlighter.


Немного работы со строками:

    private string Parse(string buildLog)
    {
      String parseResult = "";
      StringReader logReader = new StringReader(buildLog);

      Regex failedProjects = new Regex("[1-9][0-9]* error\\(s\\), [0-9]+ warning\\(s\\)");

      for (string line = logReader.ReadLine(); line != null; line = logReader.ReadLine())
      {
        if(failedProjects.IsMatch(line))
        {
          parseResult += line;
          parseResult += "\r\n";
        }

        if ((line.IndexOf(": error") != -1) || (line.IndexOf(": fatal error") != -1))
        {
          parseResult += line;
          parseResult += "\r\n";
        }
      }

      return parseResult;
    }

* This source code was highlighted with Source Code Highlighter.


И сам вывод на экран:

    private void Output(string outputString)
    {
      OutputWindowPane myPane = null;

      foreach (OutputWindowPane pane in _applicationObject.ToolWindows.OutputWindow.OutputWindowPanes)
      {
        if (pane.Name == "Build [failed]")
        {
          myPane = pane;
          break;
        }
      }

      if (myPane == null)
        myPane = _applicationObject.ToolWindows.OutputWindow.OutputWindowPanes.Add("Build [failed]");

      myPane.Clear();
      myPane.OutputString(outputString);

      myPane.Activate();
    }

* This source code was highlighted with Source Code Highlighter.


И вот он результат:

image

Для установки модуля необходимо присутствующий в проекте .AddIn файл скопировать в «Мои документы\Visual Studio <версия>\Addins», не забыв при этом поправить в нем необходимые поля, например, пути.

P.S. Ощущение того, что изобрел велосипед, присутствует, но совокупность таких факторов, как всего 30 минут потраченного времени и небольшая порция полученного удовольствия все компенсирует.
Tags:
Hubs:
+24
Comments16

Articles