VS2012: Свойства файла; Как указать пользовательский инструмент по умолчанию по типу файла

#c# #visual-studio-2012 #razor #apache-spark

#c# #visual-studio-2012 #razor #apache-spark

Вопрос:

Я ищу какой-либо механизм для автоматического применения значений свойств файла (как показано в окне свойств VS) при добавлении новых файлов определенных типов в проекты VS2012.

Например, если кто-то добавит *.cshtml файл, я бы хотел, чтобы для пользовательского инструмента по умолчанию было установлено значение ‘RazorGenerator’, потому что может возникнуть проблема с ручным указанием этого параметра во всех файлах razor.

Аналогично еще раз для *.spark , где действие сборки всегда должно быть установлено на содержимое (или прерывания сборки CI).

Похоже, что в VS2012 нет встроенных настроек для настройки свойств файла по умолчанию, так что же люди делают для решения этой проблемы, которая могла бы хорошо работать для команды разработчиков?

Я ищу варианты.

Ответ №1:

Цель ANTLR 4 C # требует, чтобы аналогичные действия выполнялись при *.g4 файлах, которые добавляются в проект.

  • Установите для действия сборки значение Antlr4
  • Установите несколько дополнительных свойств в значения по умолчанию
  • Предоставьте доступ к нескольким пользовательским свойствам с помощью настройки окна Properties в Visual Studio

Основываясь на вашем вопросе, я сосредоточусь только на первом пункте.

Предоставление действия сборки по умолчанию для типа файла

На самом деле это функция, предоставляемая как часть расширения ANTLR Language Support. Расширение выполняет операции, когда файл с определенными расширениями добавляется в проект. Смотрите комментарий в начале OnAfterAddFilesEx для получения подробной информации о поведении. Этот код взят из класса, который расширяет Package и реализует IVsTrackProjectDocumentsEvents2 . Реализация остальных методов для этого интерфейса просто возвращает VSConstants.E_NOTIMPL . Обратите внимание, что этот код может использовать пользовательские методы расширения, которые определены в другом месте в моем решении. Код не предназначен для непосредственного использования, а просто служит руководством для тех, кто хочет выполнить аналогичную операцию через свое собственное расширение.

 private static readonly Guid CSharpProjectTypeGuid = Guid.Parse(PrjKind.prjKindCSharpProject);

private uint _trackDocumentsEventsCookie;

protected override void Initialize()
{
    base.Initialize();

    IVsTrackProjectDocuments2 trackDocuments2 = serviceProvider.GetTrackProjectDocuments2();
    trackDocuments2.AdviseTrackProjectDocumentsEvents(this, out _trackDocumentsEventsCookie);
}

protected override void Dispose(bool disposing)
{
    if (disposing)
    {
        if (_trackDocumentsEventsCookie != 0)
        {
            var serviceProvider = this.AsVsServiceProvider();
            IVsTrackProjectDocuments2 trackDocuments = serviceProvider.GetTrackProjectDocuments2();
            trackDocuments.UnadviseTrackProjectDocumentsEvents(_trackDocumentsEventsCookie);
            _trackDocumentsEventsCookie = 0;
        }
    }

    base.Dispose(disposing);
}

public int OnAfterAddFilesEx(int cProjects, int cFiles, IVsProject[] rgpProjects, int[] rgFirstIndices, string[] rgpszMkDocuments, VSADDFILEFLAGS[] rgFlags)
{
    /* need to make the following alterations:
        *  1. set the Build Action of *.g and *.g3 to Antlr3
        *  2. set the Build Action of *.g4 to Antlr4
        *  3. set the Custom Tool of *.g, *.g3, and *.g4 to MSBuild:Compile
        *  4. set the Custom Tool Namespace of *.g4 to $(RootNamespace)   relative folder path
        */

    for (int i = 0; i < cProjects; i  )
    {
        IVsProject project = rgpProjects[i];
        int projectFiles = (i == cProjects - 1) ? cFiles : rgFirstIndices[i   1];
        projectFiles -= rgFirstIndices[i];

        if (!IsCSharpProject(project))
            continue;

        for (int j = 0; j < projectFiles; j  )
        {
            string currentFile = rgpszMkDocuments[rgFirstIndices[i]   j];
            if (string.IsNullOrEmpty(currentFile))
                continue;

            bool grammarFile =
                currentFile.EndsWith(".tokens", StringComparison.OrdinalIgnoreCase)
                || currentFile.EndsWith(".g", StringComparison.OrdinalIgnoreCase)
                || currentFile.EndsWith(".g3", StringComparison.OrdinalIgnoreCase)
                || currentFile.EndsWith(".g4", StringComparison.OrdinalIgnoreCase);

            if (grammarFile)
            {
                OnAfterAddedGrammarFile(project, currentFile);
            }
        }
    }

    return VSConstants.S_OK;
}

private static bool IsCSharpProject(IVsProject project)
{
    IVsAggregatableProject aggregatableProject = project as IVsAggregatableProject;
    if (aggregatableProject == null)
        return false;

    string guidsString = null;
    if (ErrorHandler.Failed(ErrorHandler.CallWithCOMConvention(() => aggregatableProject.GetAggregateProjectTypeGuids(out guidsString))))
        return false;

    if (string.IsNullOrWhiteSpace(guidsString))
        return false;

    string[] guids = guidsString.Split(';');
    foreach (var guidString in guids)
    {
        Guid guid;
        if (Guid.TryParse(guidString, out guid) amp;amp; guid == CSharpProjectTypeGuid)
            return true;
    }

    return false;
}

private void OnAfterAddedGrammarFile(IVsProject project, string currentFile)
{
    int found;
    VSDOCUMENTPRIORITY[] priority = new VSDOCUMENTPRIORITY[1];
    uint itemId;
    if (ErrorHandler.Failed(project.IsDocumentInProject(currentFile, out found, priority, out itemId)))
        return;

    if (found == 0 || priority[0] != VSDOCUMENTPRIORITY.DP_Standard)
        return;

    string desiredItemType = "Antlr3";
    if (string.Equals(Path.GetExtension(currentFile), ".tokens", StringComparison.OrdinalIgnoreCase))
        desiredItemType = "AntlrTokens";
    else if (string.Equals(Path.GetExtension(currentFile), ".g4", StringComparison.OrdinalIgnoreCase))
        desiredItemType = "Antlr4";

    IVsHierarchy hierarchy = project as IVsHierarchy;
    if (hierarchy != null)
    {
        object browseObject = null;
        PropertyDescriptorCollection propertyDescriptors = null;
        int hr = ErrorHandler.CallWithCOMConvention(() => hierarchy.GetProperty(itemId, (int)__VSHPROPID.VSHPROPID_BrowseObject, out browseObject));
        if (ErrorHandler.Succeeded(hr))
            propertyDescriptors = TypeDescriptor.GetProperties(browseObject);

        object obj;
        hr = hierarchy.GetProperty(itemId, (int)__VSHPROPID4.VSHPROPID_BuildAction, out obj);
        if (ErrorHandler.Succeeded(hr))
        {
            string buildAction = obj != null ? obj.ToString() : null;
            if (string.IsNullOrWhiteSpace(buildAction) || string.Equals(buildAction, "None", StringComparison.OrdinalIgnoreCase))
            {
                hr = ErrorHandler.CallWithCOMConvention(() => hierarchy.SetProperty(itemId, (int)__VSHPROPID4.VSHPROPID_BuildAction, desiredItemType));
            }
        }

        if (ErrorHandler.Failed(hr) amp;amp; propertyDescriptors != null)
        {
            PropertyDescriptor itemTypeDescriptor = propertyDescriptors["ItemType"] ?? propertyDescriptors["BuildAction"];
            if (itemTypeDescriptor != null)
            {
                obj = itemTypeDescriptor.GetValue(browseObject);
                string buildAction = (string)itemTypeDescriptor.Converter.ConvertToInvariantString(obj);
                if (string.IsNullOrWhiteSpace(buildAction) || string.Equals(buildAction, "None", StringComparison.OrdinalIgnoreCase))
                {
                    try
                    {
                        obj = itemTypeDescriptor.Converter.ConvertFromInvariantString(desiredItemType);
                        itemTypeDescriptor.SetValue(browseObject, obj);
                    }
                    catch (NotSupportedException)
                    {
                    }
                }
            }
        }

        if (propertyDescriptors != null)
        {
            PropertyDescriptor customToolDescriptor = propertyDescriptors["CustomTool"];
            if (customToolDescriptor != null)
            {
                obj = customToolDescriptor.GetValue(browseObject);
                string customTool = customToolDescriptor.Converter.ConvertToInvariantString(obj);
                if (string.IsNullOrWhiteSpace(customTool))
                {
                    try
                    {
                        obj = customToolDescriptor.Converter.ConvertToInvariantString("MSBuild:Compile");
                        customToolDescriptor.SetValue(browseObject, obj);
                    }
                    catch (NotSupportedException)
                    {
                    }
                }
            }

            PropertyDescriptor customToolNamespaceDescriptor = propertyDescriptors["CustomToolNamespace"];
            if (customToolNamespaceDescriptor != null)
            {
                object defaultNamespace;
                hr = hierarchy.GetProperty(itemId, (int)__VSHPROPID.VSHPROPID_DefaultNamespace, out defaultNamespace);
                if (ErrorHandler.Succeeded(hr) amp;amp; !string.IsNullOrEmpty(defaultNamespace as string))
                {
                    obj = customToolNamespaceDescriptor.GetValue(browseObject);
                    string customToolNamespace = customToolNamespaceDescriptor.Converter.ConvertToInvariantString(obj);
                    if (string.IsNullOrWhiteSpace(customToolNamespace))
                    {
                        try
                        {
                            obj = customToolNamespaceDescriptor.Converter.ConvertToInvariantString(defaultNamespace);
                            customToolNamespaceDescriptor.SetValue(browseObject, obj);
                        }
                        catch (NotSupportedException)
                        {
                        }
                    }
                }
            }
        }
    }
}
  

Комментарии:

1. Это интересный подход, и мне, возможно, придется попробовать его, нет ли более простого способа?