/// <summary> /// Returns project files. /// </summary> private void GetFilesInternal(object project, List <FileModel> model, ProcessorFlags flags, CodeModelFilterFlags filter) { ThreadHelper.ThrowIfNotOnUIThread(); var dteProject = project as Project; if (dteProject == null) { throw new ArgumentNullException(nameof(project)); } if (_shellProjectService.IsProjectLoadDeferred(project, out _)) { return; } _log.LogMessage($"Collecting '{dteProject.Name}' project files", LOG_CATEGORY); // No need to process project's items if we fail to check its code model // The exception is made for solution folder *if* that's requested var languageSet = _fileTypeResolver.GetCurrentLanguage(project, out bool isWebProject); bool process = languageSet != null; if (!process) { if (flags.HasFlag(ProcessorFlags.IncludeSolutionFolderFiles) && (dteProject.Kind == Constants.vsProjectKindSolutionItems)) { _log.LogMessage($"Allow '{dteProject.Name}' solution folder processing", LOG_CATEGORY); process = true; } } if (!process && !flags.HasFlag(ProcessorFlags.KnownProjectsOnly) && _shellProjectService.IsProject(project)) { _log.LogMessage($"Allow '{dteProject.Name}' unknown project type processing", LOG_CATEGORY); process = true; } if (process) { ProjectItems projectItems = dteProject.ProjectItems; if ((projectItems != null) && (projectItems.Count > 0)) { bool isRoot = true; ProcessProjectItems( model, flags, filter, languageSet, projectItems, isWebProject, ref isRoot, null, string.Empty); } } _log.LogMessage($"Collected {model.Count} '{dteProject.Name}' project files", LOG_CATEGORY); }
/// <summary> /// Returns solution projects. /// </summary> private void GetProjectsInternal(SolutionModel model, ProcessorFlags flags, CodeModelFilterFlags filter) { ThreadHelper.ThrowIfNotOnUIThread(); var dte = _shellProjectService.GetDTE() as DTE; _log.LogMessage("Collecting solution projects"); var projects = dte.Solution.Projects; for (int index = 1; index <= projects.Count; index++) { Project project = projects.Item(index); if (_shellProjectService.IsProjectLoadDeferred(project, out bool loaded)) { continue; } // Reacquire project reference if it's been reloaded if (loaded) { project = projects.Item(index); } if (ProcessProject(project, model.Projects, flags, filter)) { ProcessProjectRecursively(project, model.Projects, flags, filter); } } model.Files = model.Projects.SelectMany(p => p.Files).ToList(); if (flags.HasFlag(ProcessorFlags.GroupLinkedFiles)) { // Get all grouped, ie duplicate, files, keep the first code file and remove the rest var groupedFiles = model.Files.GroupBy(f => f.FileNameWithPath).Where(g => g.Count() > 1).Select(g => g); foreach (var group in groupedFiles) { var file = group.FirstOrDefault(f => f.Project.Language.SupportsCompileBuildAction && _shellProjectService.CompileBuildAction(f.ProjectItem)); if (file != null) { group.ForEach(f => { if (f != file) { model.Files.Remove(f); f.Project.Files.Remove(f); AddProjectName(file, f.ProjectName); } }); } } } _log.LogMessage($"Collected {model.Projects.Count} solution projects"); }
public async Task<string> ProcessAsync(string name, string[] files, ProcessorFlags flags, string basePath) { var bundler = _container.Resolve<IAssetBundler>(); var result = await bundler.BundleAsync(_assetHelper.GetGlobComponentsForAsset(files, basePath)); var factory = _container.Resolve<IAssetCompilerFactory>(); var compiler = factory.GetCompiler(name); return await compiler.CompileAsync(result, flags.HasFlag(ProcessorFlags.Minify)); }
public async Task <string> ProcessAsync(string name, string[] files, ProcessorFlags flags, string basePath) { var bundler = _container.Resolve <IAssetBundler>(); var result = await bundler.BundleAsync(_assetHelper.GetGlobComponentsForAsset(files, basePath)); var factory = _container.Resolve <IAssetCompilerFactory>(); var compiler = factory.GetCompiler(name); return(await compiler.CompileAsync(result, flags.HasFlag(ProcessorFlags.Minify))); }
/// <summary> /// Walks qualified project items and processes all of their qualified items. /// </summary> private void ProcessProjectItems(List <FileModel> model, ProcessorFlags flags, CodeModelFilterFlags filter, LanguageSettings languageSet, ProjectItems projectItems, bool isWebProject, ref bool isRoot, ProjectItem parentItem, string parentName) { ThreadHelper.ThrowIfNotOnUIThread(); // Parent item is applicable or meant to be used for actual files only, i.e. file combos like .cs and .designer.cs, etc. if (parentItem != null) { var parentItemKind = parentItem.Kind; if ((string.Compare(parentItemKind, Constants.vsProjectItemKindPhysicalFile, StringComparison.OrdinalIgnoreCase) != 0) && (string.Compare(parentItemKind, Constants.vsProjectItemKindSolutionItems, StringComparison.OrdinalIgnoreCase) != 0)) { parentItem = null; } } var parentSubType = FileSubType.None; var parentSubTypeSet = false; if (!string.IsNullOrEmpty(parentName) && !parentName.EndsWith("\\", StringComparison.OrdinalIgnoreCase)) { parentName = parentName + "\\"; } // Save the current language and determine the new current language if (isWebProject) { var webLanguageSet = GetWebFolderLanguage(isRoot, projectItems); // Fallback scenario - use default project language so that non-code files can be resolved if (webLanguageSet?.Type != LanguageType.Unknown) { languageSet = webLanguageSet; // language should be restored for other items once we walk up the stack } } // Root processing is finished after language is checked for the root project items isRoot = false; var projectName = string.Empty; foreach (ProjectItem projectItem in projectItems) { // Skip project references as they can't contribute any files var isReferences = projectItem.Object is VSLangProj.References; var isReference = projectItem.Object is VSLangProj.Reference; if (isReferences || isReference) { continue; } var projectItemKind = projectItem.Kind; // LightSwitch type projects with virtual references node workaround if (!string.IsNullOrEmpty(projectItem.Name) && projectItem.Name.Equals(REFERENCES_NODE, StringComparison.OrdinalIgnoreCase) && (string.Compare(projectItemKind, Constants.vsProjectItemKindVirtualFolder, StringComparison.OrdinalIgnoreCase) == 0)) { continue; } if ((string.Compare(projectItemKind, Constants.vsProjectItemKindPhysicalFile, StringComparison.OrdinalIgnoreCase) == 0) || (string.Compare(projectItemKind, Constants.vsProjectItemKindSolutionItems, StringComparison.OrdinalIgnoreCase) == 0)) { if (!parentSubTypeSet) { parentSubTypeSet = true; if (parentItem != null) { parentSubType = _fileTypeResolver.GetSubType(parentItem, languageSet, isWebProject); } } var itemSubType = _fileTypeResolver.GetSubType(projectItem, languageSet, isWebProject); // Check if this is a code file var add = !flags.HasFlag(ProcessorFlags.IncludeCodeFilesOnly) || _fileTypeResolver.IsCodeSubType(itemSubType); // Check for .Designer file if (add && !flags.HasFlag(ProcessorFlags.IncludeDesignerFiles) && _fileTypeResolver.IsDesignerItem(projectItem, itemSubType, languageSet, isWebProject)) { add = false; } // Used to collect skipped files here as well... something to keep an eye out out for if (add) { var fileName = projectItem.get_FileNames(1); if (string.IsNullOrEmpty(fileName)) { add = false; } if (add) { // Try getting a misc/simple subtype for unknown and solution projects if ((languageSet?.Type == LanguageType.Unknown) && (itemSubType == FileSubType.None)) { itemSubType = _fileTypeResolver.GetExtensionSubType(projectItem, languageSet, isWebProject); } if (string.IsNullOrEmpty(projectName)) { projectName = projectItem.ContainingProject?.Name; } var itemModel = new FileModel { ProjectItem = projectItem, FileName = projectItem.Name, ParentProjectItem = parentItem, ParentName = parentName, ParentSubType = parentSubType, ItemSubType = itemSubType, FileNameWithPath = fileName, ProjectName = projectName }; if (flags.HasFlag(ProcessorFlags.IncludeFileCodeModel)) { var codeMembers = _fileProcessor.GetMembers(projectItem, flags, filter); if ((codeMembers != null) && (codeMembers.Members != null)) { itemModel.Members.AddRange(codeMembers.Members); } } model.Add(itemModel); } } ProjectItems currentProjectItems = projectItem.ProjectItems; if ((currentProjectItems != null) && (currentProjectItems.Count > 0)) { ProcessProjectItems( model, flags, filter, languageSet, currentProjectItems, false, ref isRoot, projectItem, parentName); } } else // projectItemKind { if ((string.Compare(projectItemKind, Constants.vsProjectItemKindPhysicalFolder, StringComparison.OrdinalIgnoreCase) == 0) || (string.Compare(projectItemKind, Constants.vsProjectItemKindVirtualFolder, StringComparison.OrdinalIgnoreCase) == 0)) { ProjectItems currentProjectItems = projectItem.ProjectItems; if ((currentProjectItems != null) && (currentProjectItems.Count > 0)) { ProcessProjectItems( model, flags, filter, languageSet, currentProjectItems, isWebProject, ref isRoot, projectItem, parentName + projectItem.Name); } } } } // foreach (projectItem) }
/// <summary> /// Creates code model element. /// </summary> private void AddCodeElement(ProjectItem item, CodeElement parentElement, CodeElement element, List <DPackFileCodeModel> model, ProcessorFlags flags, LanguageSettings languageSet, ICollection <vsCMElement> dteFilter, CodeModelFilterFlags filter) { ThreadHelper.ThrowIfNotOnUIThread(); EditPoint editPoint; // Line number retrieval uses EditPoints, which is expensive to process try { // Not all elements support header part TextPoint startPoint; if ((element.Kind == vsCMElement.vsCMElementClass) || (element.Kind == vsCMElement.vsCMElementModule) || (element.Kind == vsCMElement.vsCMElementInterface) || (element.Kind == vsCMElement.vsCMElementStruct) || (element.Kind == vsCMElement.vsCMElementEnum) || (element.Kind == vsCMElement.vsCMElementFunction)) { startPoint = element.GetStartPoint(vsCMPart.vsCMPartHeader); } else { startPoint = element.StartPoint; } if (startPoint == null) { _log.LogMessage($"{element.Name} element's of {element.Kind} kind StartPoint cannot be determined"); return; } // Check if this code model element belongs to the current project item if (element.ProjectItem != null) { var elementItem = element.ProjectItem; if ((item != null) && (item != elementItem)) { string itemFileName = item.get_FileNames(1); string epiFileName = elementItem.get_FileNames(1); if (string.IsNullOrEmpty(itemFileName) || string.IsNullOrEmpty(epiFileName) || (string.Compare(itemFileName, epiFileName, StringComparison.OrdinalIgnoreCase) != 0)) { _log.LogMessage($"Project item mismatch: '{item.Name}' expected but '{elementItem.Name}' found instead - {element.Name}"); return; } else { // This is a work around for project item reference change where essentially new reference // still points at the same file but reference wise these two are no longer equal item = elementItem; } } } editPoint = startPoint.CreateEditPoint(); } catch (Exception ex) { // Swallow C++ exception as some invalid code model functions raise it sometimes if (languageSet?.Language != CodeModelLanguageConstants.vsCMLanguageVC) { _log.LogMessage($"Error adding code member: {element.Name}", ex); } return; } var line = editPoint.Line; // Decide whether the element is to be filtered out or not bool add; if (dteFilter?.Count == 0) { add = true; } else { add = dteFilter.Contains(element.Kind); if (!add) { // TODO: resurrect next statement if need be - used to treat read-only fields as properties //if (dteFilter.Contains(vsCMElement.vsCMElementProperty) && (element.Kind == vsCMElement.vsCMElementVariable)) //{ // var varElt = (CodeVariable2)element; // if (varElt.ConstKind == vsCMConstKind.vsCMConstKindReadOnly) // add = true; //} } } if (add) { GetConstructorDestructorInfo(languageSet, parentElement, element, out bool constructor, out bool destructor); if ((filter != CodeModelFilterFlags.Constructors) || (filter.HasFlag(CodeModelFilterFlags.Constructors) && (constructor || destructor))) { var name = element.Name; string parentFullName; var fullName = flags.HasFlag(ProcessorFlags.IncludeMemeberFullName) ? element.FullName : string.Empty; var code = string.Empty; // Setup element's full name information if (string.IsNullOrEmpty(name)) { name = UNNAMED_NAME; } if (parentElement == null) { if (!flags.HasFlag(ProcessorFlags.IncludeMemeberFullName) && (languageSet != null) && languageSet.ParentlessFullName) { parentFullName = element.FullName; } else { parentFullName = name; } } else if ((parentElement.Kind == vsCMElement.vsCMElementClass) || (parentElement.Kind == vsCMElement.vsCMElementModule) || (parentElement.Kind == vsCMElement.vsCMElementInterface) || (parentElement.Kind == vsCMElement.vsCMElementStruct)) { parentFullName = parentElement.Name + "." + name; } else { parentFullName = name; } // Check for duplicate names if ((languageSet != null) && languageSet.CheckDuplicateNames) { foreach (var modelItem in model) { if ((modelItem.Line == line) && (modelItem.CodeModelElementKind == (int)element.Kind) && (modelItem.ParentFullName == parentFullName)) { // Duplicate found add = false; break; } } } if (add) { // Setup element's first code line information if (editPoint != null) { int lineLength = editPoint.LineLength - editPoint.LineCharOffset + 1; if (lineLength > 1) { code = editPoint.GetText(lineLength).TrimStart(' ', '\t'); } } try { var returnTypeName = constructor || destructor || (element == null) ? string.Empty : GetElementReturnTypeName(element); var xmlDoc = flags.HasFlag(ProcessorFlags.IncludeMemberXmlDoc) ? GetXmlDoc(element, languageSet) : string.Empty; char?genericSuffix = null; if ((languageSet != null) && languageSet.SupportsGenerics) { var suffix = _shellCodeModelService.GetGenericsSuffix(element); if (!string.IsNullOrEmpty(suffix)) { name = name + suffix; parentFullName = parentFullName + suffix; genericSuffix = suffix[0]; } } var modelItem = new DPackFileCodeModel { ProjectItem = element.ProjectItem, Name = name, FullName = fullName, ParentFullName = parentFullName, CodeModelElementKind = (int)element.Kind, ElementKind = _shellCodeModelService.GetElementKind(element), ElementModifier = _shellCodeModelService.GetElementModifier(element), IsConstant = _shellCodeModelService.IsConstant(element), IsStatic = _shellCodeModelService.IsStatic(element), SupportsGenerics = languageSet.SupportsGenerics, GenericsSuffix = _shellCodeModelService.GetGenericsSuffix(element), Line = line, Code = code, ReturnTypeName = returnTypeName, XmlDoc = xmlDoc }; model.Add(modelItem); } catch (COMException ex) { _log.LogMessage($"Ignored COM code model error for {name}", ex); } } // if (add) } // if (memberFilter) } // if (add) }
/// <summary> /// Processes individual project. /// </summary> /// <returns>Whether project's been processed or not.</returns> private bool ProcessProject(Project project, ICollection <ProjectModel> model, ProcessorFlags flags, CodeModelFilterFlags filter) { ThreadHelper.ThrowIfNotOnUIThread(); var process = true; var language = string.Empty; // Safety check & language retrieval try { var items = project.ProjectItems; if ((items != null) && (items.Count > 0)) { if (_shellProjectService.IsCodeModelProject(project)) { language = _shellProjectService.GetProjectLanguage(project); if (string.IsNullOrEmpty(language)) { process = false; } } else { language = _shellProjectService.GetNoCodeModelProjectLanguage(project); if (string.IsNullOrEmpty(language)) { process = false; } } } } catch (NotImplementedException) // This is an acceptable condition - keep on going { process = false; _log.LogMessage($"Project '{project?.Name}' doesn't implement code model"); } catch (Exception ex) // But this is a legit problem - keep on going anyways { process = false; _log.LogMessage("Failed to retrieve project code model / language", ex); } string projectFullName = string.Empty; if (process) { _shellProjectService.GetProjectPath(project, out string projectPath, out projectFullName); if ((string.IsNullOrEmpty(projectPath) || string.IsNullOrEmpty(projectFullName)) && _shellProjectService.IsProject(project)) { process = false; _log.LogMessage($"Project '{project?.Name}' path is not available"); } } if (process) { var projectModel = new ProjectModel { Project = project, Name = project.Name, FriendlyName = _shellProjectService.GetFriendlyProjectName(project), FileName = projectFullName, Language = _languageService.GetLanguage(language) }; if (flags.HasFlag(ProcessorFlags.IncludeFiles)) { var files = _projectProcessor.GetFiles(project, flags, filter); if (files != null) { projectModel.Files.AddRange(files); files.ForEach(f => f.Project = projectModel); } } model.Add(projectModel); } return(process); }
/// <summary> /// Returns solution projects. /// </summary> private void GetProjectsInternal(SolutionModel model, ProcessorFlags flags, CodeModelFilterFlags filter) { ThreadHelper.ThrowIfNotOnUIThread(); _log.LogMessage("Collecting solution projects", LOG_CATEGORY); var dte = _shellProjectService.GetDTE() as DTE; var serviceProvider = new Microsoft.VisualStudio.Shell.ServiceProvider(dte as Microsoft.VisualStudio.OLE.Interop.IServiceProvider); var solutionService = serviceProvider.GetService <IVsSolution, SVsSolution>(false); Guid guid = Guid.Empty; if (solutionService.GetProjectEnum((uint)__VSENUMPROJFLAGS.EPF_LOADEDINSOLUTION, ref guid, out IEnumHierarchies enumHierarchies) != VSConstants.S_OK) { return; } IVsHierarchy[] hierarchy = new IVsHierarchy[1]; List <Project> projects = new List <Project>(); while ((enumHierarchies.Next((uint)hierarchy.Length, hierarchy, out uint fetched) == VSConstants.S_OK) && (fetched == hierarchy.Length)) { if ((hierarchy.Length > 0) && (hierarchy[0] != null)) { IVsHierarchy projectHierarchy = hierarchy[0]; if ((projectHierarchy.GetProperty((uint)VSConstants.VSITEMID.Root, (int)__VSHPROPID.VSHPROPID_ExtObject, out object project) == VSConstants.S_OK) && (project is Project dteProject) && _shellProjectService.IsProject(project)) { projects.Add(dteProject); } } } _log.LogMessage("Resolving solution name", LOG_CATEGORY); var solution = dte.Solution?.FileName; if (!string.IsNullOrEmpty(solution)) { solution = Path.GetFileNameWithoutExtension(solution); } model.SolutionName = solution; _log.LogMessage($"Processing {projects.Count} collected solution projects", LOG_CATEGORY); foreach (Project project in projects) { if (_shellProjectService.IsProjectLoadDeferred(project, out _)) { continue; } if (ProcessProject(project, model.Projects, flags, filter)) { ProcessProjectRecursively(project, model.Projects, flags, filter); } } model.Files = model.Projects.SelectMany(p => p.Files).ToList(); if (flags.HasFlag(ProcessorFlags.GroupLinkedFiles)) { // Get all grouped, ie duplicate, files, keep the first code file and remove the rest var groupedFiles = model.Files.GroupBy(f => f.FileNameWithPath).Where(g => g.Count() > 1).Select(g => g); foreach (var group in groupedFiles) { var file = group.FirstOrDefault(f => f.Project.Language.SupportsCompileBuildAction && _shellProjectService.CompileBuildAction(f.ProjectItem)); if (file != null) { group.ForEach(f => { if (f != file) { model.Files.Remove(f); f.Project.Files.Remove(f); AddProjectName(file, f.ProjectName); } }); } } } _log.LogMessage($"Processed {model.Projects.Count} qualified solution projects", LOG_CATEGORY); }