Enumerate all files in current visual studio project - c#

I'm trying to write a simple Visual Studio 2012 extension. I have generated the extension template and can bring up a dialog box from a tool menu.
I'd like to enumerate all files in the currently open project and then filter them according to some rules. What I'm looking for is a code snippet to return IEnumerable. FileHandle should have the following interface or something similar.
interface IFileHandle {
// Return the string
string Path;
// Open the file in the editor
void OpenEditorFor();
}
FYI I'm trying to build a fuzzy file finder for visual studio. The current file search is less than suitable as you have to have exact match. I can handle writing the indexer and the fuzzy searcher but the interface to Visual Studio extension writing is a bit cryptic at the moment.

This seems to be the simple answer. In the context of a visual
studio extension will return all files.
public IEnumerable<ProjectItem> Recurse(ProjectItems i)
{
if (i!=null)
{
foreach (ProjectItem j in i)
{
foreach (ProjectItem k in Recurse(j))
{
yield return k;
}
}
}
}
public IEnumerable<ProjectItem> Recurse(ProjectItem i)
{
yield return i;
foreach (ProjectItem j in Recurse(i.ProjectItems ))
{
yield return j;
}
}
public IEnumerable<ProjectItem> SolutionFiles()
{
Solution2 soln = (Solution2)_applicationObject.Solution;
foreach (Project project in soln.Projects)
{
foreach (ProjectItem item in Recurse(project.ProjectItems))
{
yield return item;
}
}
}
You can then do neat tricks with it like implement the search function at the core of my CommandT clone.
private static string Pattern(string src)
{
return ".*" + String.Join(".*", src.ToCharArray());
}
private static bool RMatch(string src, string dest)
{
try
{
return Regex.Match(dest, Pattern(src), RegexOptions.IgnoreCase).Success;
}
catch (Exception e)
{
return false;
}
}
private static List<string> RSearch(
string word,
IEnumerable<string> wordList,
double fuzzyness)
{
// Tests have prove that the !LINQ-variant is about 3 times
// faster!
List<string> foundWords =
(
from s in wordList
where RMatch(word, s) == true
orderby s.Length ascending
select s
).ToList();
return foundWords;
}
which is used like
var list = RSearch("bnd", SolutionFiles().Select(x=>x.Name))

Related

Handle network fail while copying files in C#

I am writing a C# app that copy files over a network, the problem is that the size of the files and folders to copy is more than 1 TB.
My method is as follows
public static void SubmitDocsToRepository(string p_FilePaths)
{
IEnumerable<(string,string)> directoryLevels = GetAllFolders(p_FilePaths);
IEnumerable<(string,string)> filesLevels = GetAllFiles(p_FilePaths);
foreach (var tuple in directoryLevels)
Folder copy logic
foreach (var tuple in filesLevels)
File copy logic
}
Which would work fine, but assuming something would happen to the network or remote server or the electric power gets lost for whatever reason what should I add to this code to allow me to continue where I left off, especially how could I retrace my steps to where I was.
It could be something like this:
public static void SubmitDocsToRepository(string p_FilePaths)
{
IEnumerable<(string, string)> directoryLevels = GetAllFolders(p_FilePaths);
IEnumerable<(string, string)> filesLevels = GetAllFiles(p_FilePaths);
foreach (var tuple in directoryLevels)
while (!CopyDirectory(tuple)) ;
foreach (var tuple in filesLevels)
while (!CopyFile(tuple)) ;
}
static bool CopyDirectory((string, string)tuple)
{
try
{
// Copy logic
}
catch
{
// Some logging here
return false;
}
return true;
}
static bool CopyFile((string, string) tuple)
{
try
{
// Copy logic
}
catch
{
// Some logging here
return false;
}
return true;
}

Getting Value cannot be null. parameter name: source on release but not in debug

I'm creating a Revit plugin that reads and writes modelinformation to a database, and it all works fine in debug mode, but when I release the project and run Revit with the plugin outside visual studio, I'm getting an error when the plugin tries to read data from the database.
The code runs on DocumenetOpened event and looks like this:
public void application_DocumentOpenedEvent(object sender, DocumentOpenedEventArgs e)
{
UIApplication uiapp = new UIApplication(sender as Autodesk.Revit.ApplicationServices.Application);
Document doc = uiapp.ActiveUIDocument.Document;
//ModelGUID COMMAND
var command = new ModelCheckerCommandExec();
command.Execute(uiapp);
}
It then fails on the following line:
ModelsList = (DatabaseHelper.ReadNonAsync<RevitModel>())
.Where(m => m.ModelGUID == DataStores.ModelData.ModelGUID).ToList();
In this code block that gets executed:
public class ModelCheckerCommandExec : IExternalCommand
{
public Result Execute(ExternalCommandData commandData, ref string message, ElementSet elements)
{
return Execute(commandData.Application);
}
public Result Execute(UIApplication uiapp)
{
Document doc = uiapp.ActiveUIDocument.Document;
Transaction trans = new Transaction(doc);
try
{
trans.Start("ModelGUID");
ModelGUIDCommand.GetAndSetGUID(doc);
trans.Commit();
var ModelsList = new List<RevitModel>();
ModelsList = (DatabaseHelper.ReadNonAsync<RevitModel>()).ToList();//.Where(m => m.ModelGUID == DataStores.ModelData.ModelGUID).ToList(); // Read method only finds models the are similar to the DataStore.ModelDate.DBId;
if (ModelsList.Count == 1)
{
trans.Start("DataFromDB");
doc.ProjectInformation.Name = ModelsList[0].ProjectName;
doc.ProjectInformation.Number = ModelsList[0].ModelNumber;
doc.ProjectInformation.Status = ModelsList[0].ModelStatus;
doc.ProjectInformation.IssueDate = ModelsList[0].ProjectIssueDate;
doc.ProjectInformation.ClientName = ModelsList[0].ClientName;
doc.ProjectInformation.Address = ModelsList[0].ProjectAddress;
doc.ProjectInformation.LookupParameter("Cadastral Data").Set(ModelsList[0].ProjectIssueDate);
doc.ProjectInformation.LookupParameter("Current Version").Set(ModelsList[0].CurrentVersion);
doc.ProjectInformation.BuildingName = ModelsList[0].BuildingName;
DataStores.ModelData.ModelManager1 = ModelsList[0].ModelManagerOne;
DataStores.ModelData.ModelManager1Id = ModelsList[0].ModelManagerOneId;
DataStores.ModelData.ModelManager2 = ModelsList[0].ModelManagerTwo;
DataStores.ModelData.ModelManager2Id = ModelsList[0].ModelManagerTwoId;
trans.Commit();
}
return Result.Succeeded;
}
catch (Exception ex)
{
TaskDialog.Show("Error", ex.Message);
return Result.Failed;
}
}
}
The "ReadNonAsync" method is as follows:
public static List<T> ReadNonAsync<T>() where T : IHasId
{
using (var client = new HttpClient())
{
var result = client.GetAsync($"{dbPath}{Properties.Settings.Default.CompanyName}_{typeof(T).Name.ToLower()}.json?access_token={DataStores.IdToken.UserIdToken}").GetAwaiter().GetResult();
var jsonResult = result.Content.ReadAsStringAsync().GetAwaiter().GetResult();
if (result.IsSuccessStatusCode)
{
var objects = JsonConvert.DeserializeObject<Dictionary<string, T>>(jsonResult);
List<T> list = new List<T>();
if (objects != null)
{
foreach (var o in objects)
{
o.Value.Id = o.Key;
list.Add(o.Value);
}
}
return list;
}
else
{
return null;
}
}
}
In the rest of my code I use a async Read method which works, so I'm wondering wether or not that's the issue, but Revit wont let me use an async method inside an Execute method.
How do I debug this issue correctly, and why could there be code working in debug that doesn't work in "live" versions?
I found a solution!
The issue:
The reason for the error was that when I run the software in debug-mode, a file path of "xxx.txt" finds files in the solution folder, but when I run the software "xxx.txt" points to the folder of the software and not the .dll -
So in my case it pointed to "c:\Program Files\Autodesk\Revit\2021".
The fix:
Hard coding the path, or by getting the path of the executing .dll
Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location)
Debugging/Troubleshooting:
I found the issue by inserting dialogboxes with errormessages in all my try/catch statements.

Resharper - Go To Implementation listing reference twice

In one of my solutions, when I right click a symbol and choose "Go To Implementation" for an object defined in one of the other solution projects, it lists the reference twice and forces me to choose one.
Based on the icons, it appears that one of the items in the list represents the project, and the other represents a dll. It doesn't matter which one I click - it goes to the same source file.
I only have the library reference once in this particular project - it is referencing the project.
What would cause this to happen? Some sort of circular reference issue perhaps?
As far as I can tell, this can also happen if you have a solution with several projects, where a certain project is referenced as project and also as pure file by two other projects in the solution.
Another advice that I can give if something is broken with ReSharper, is to clear the cache.
I had this problem and I just fixed it.
First, try do a Clean Solution and then a Build.
In my case, one rogue Project in my solution was compiled using an older version of the .NET framework than the other Projects, so when Resharper added a reference to my other Projects for me, it must have added it as a dll reference instead of as a Project reference.
My fix was
Upgrade old Project to the same version of .NET framework as the other Projects
Remove references to other Projects from that old Project
Add references to the other Projects again (as Project references this time)
Clean solution
Build solution
Done.
I've found a couple different cases that cause this problem, and got so annoyed that I wrote a little console app to scan my solution and find the problems for me. Here it is for anyone who might find this useful. To run it pass it the path to your solution folder and it will print out the issues on the console. It's very "quick and dirty" but it found the issues for me.
class Program
{
static void Main(string[] args)
{
if (args != null && args.Any())
{
foreach (var s in args)
{
Console.WriteLine("Checking " + s);
DirectoryInfo dir = new DirectoryInfo(s);
var files = dir.GetFiles("*.csproj", SearchOption.AllDirectories);
var projects = files.Select(x => new Project(x)).ToList();
var grouped = projects.GroupBy(x => x.TargetFrameworkVersion);
if(grouped.Count()>1)
{
Console.WriteLine("Solution contains multiple versions of Target Frameworks, this may cause duplicate assemblies in R# cache");
foreach (var group in grouped)
{
Console.WriteLine(group.Key);
foreach (var project in group)
{
Console.WriteLine(project.AssemblyName);
}
}
}
//loop through for debugging
foreach (var project in projects)
{
foreach (var reference in project.References)
{
foreach (var checkProject in projects)
{
if (checkProject.AssemblyName == reference)
{
Console.WriteLine("Reference in" + project.FileName + " referencing " +
reference+" that should be a ProjectReference, this may cause duplicate entries in R# Cache");
}
}
}
}
}
Console.WriteLine("Complete");
Console.ReadLine();
}
else
{
Console.WriteLine("You must provide a path to scan for csproj files");
}
}
}
public class Project
{
public string FileName { get; set; }
public string AssemblyName { get; set; }
public string ProjectGuid { get; set; }
public string TargetFrameworkVersion { get; set; }
public IList<string> References { get; set; }
private FileInfo _file;
private XmlDocument _document;
private XmlNamespaceManager _namespaceManager;
public Project(FileInfo file)
{
_file = file;
FileName = _file.FullName;
_document = new XmlDocument();
_document.Load(_file.FullName);
_namespaceManager = new XmlNamespaceManager(_document.NameTable);
_namespaceManager.AddNamespace("msbld", "http://schemas.microsoft.com/developer/msbuild/2003");
var projectGuidNode = _document.SelectSingleNode("//msbld:ProjectGuid", _namespaceManager);
ProjectGuid = projectGuidNode.InnerText;
var assemblyNameNode = _document.SelectSingleNode("//msbld:AssemblyName", _namespaceManager);
AssemblyName = assemblyNameNode.InnerText;
var targetFrameworkNode = _document.SelectSingleNode("//msbld:TargetFrameworkVersion", _namespaceManager);
TargetFrameworkVersion = targetFrameworkNode.InnerText;
References = new List<string>();
var referenceNodes = _document.SelectNodes("//msbld:Reference", _namespaceManager);
foreach (var node in referenceNodes)
{
var element = (XmlElement) node;
//file references
if (element.HasChildNodes)
{
foreach (var child in element.ChildNodes)
{
var childElement = (XmlElement)child;
if (childElement.Name == "HintPath")
{
var value = childElement.InnerText;
value = value.Substring(value.LastIndexOf("\\") + 1);
value = value.Replace(".dll", "");
References.Add(value);
}
}
}
//gac references
else
{
foreach (var attr in element.Attributes)
{
var attribute = (XmlAttribute)attr;
if (attribute.Name == "Include")
{
var value = attribute.Value;
string reference = value;
if (value.Contains(','))
{
reference = value.Substring(0, value.IndexOf(','));
}
References.Add(reference);
break;
}
}
}
}
}
}

Replicating Directory.GetFiles(String, String) in Java

Overview
I'm in the process of porting a simple utility app over from C# into Java and as part of this I'm writing some helper methods for items where I prefer C#'s semantics. Once such case is Directory.GetFiles.
Current Code
import java.io.File;
import java.io.FilenameFilter;
public class Directory {
public static String[] GetFiles(String path) {
File directory = new File(path);
return directory.list(new FilenameFilter() {
#Override
public boolean accept(File dir, String filename) {
return new File(dir, filename).isFile();
}
});
}
}
Question
Whilst the above all seems fine, and replicates one of the GetFiles overloads, I'm stuck on how best to write a method that replicates the functionality of C#'s Directory.GetFiles(String, String).
This method should take a path string, as well as a searchPattern, which is used to return only files matching that particular pattern.
For example, each of the following should work:
// Used to get all JavaScript files
Directory.GetFiles("~/Documents/", "*.js");
// Get all CSS files in the styles sub-folder.
Directory.GetFiles("~/Documents/", "styles/*.css");
You could modify the pattern by placing a period before each asterisk and question mark, and then use this as a regular expression to determine which files you should return. So, you could write something like
public static String[] GetFiles(final String path, final String searchPattern) {
final Pattern re = Pattern.compile(searchPattern.replace("*", ".*").replace("?", ".?"));
return new File(path).list(new FilenameFilter() {
#Override
public boolean accept(File dir, String name) {
return new File(dir, name).isFile() && re.matcher(name).matches();
}
});
}
Using Adam Michalcin's answer as a base, I've come up with the following (note these methods return an array of absolute paths:
import java.io.File;
import java.io.FilenameFilter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.regex.Pattern;
public class Directory {
public static String[] GetFiles(String path) {
File directory = new File(path);
File[] matchingFiles = directory.listFiles(new FilenameFilter() {
#Override
public boolean accept(File dir, String filename) {
return new File(dir, filename).isFile();
}
});
ArrayList<String> files = new ArrayList<String>();
for (File file : matchingFiles) {
files.add(file.getAbsolutePath());
}
return (String[])files.toArray(new String[files.size()]);
}
public static String[] GetFiles(String path, final String searchPattern) {
// Split the searchPattern incase we have directories in there
ArrayList<String> pattern = new ArrayList<String>(Arrays.asList(searchPattern.split("/")));
// Take the last element out from the array, as this will be the file pattern
String filePattern = pattern.remove(pattern.size() - 1);
// Insert the base path into the remaining list
pattern.add(0, path);
// Now lets concat the lot to create a base path
path = Path.combine((String[])pattern.toArray(new String[pattern.size()]));
final Pattern regEx = Pattern.compile(filePattern.replace("*", ".*").replace("?", ".?"));
File directory = new File(path);
File[] matchingFiles = directory.listFiles(new FilenameFilter() {
#Override
public boolean accept(File dir, String filename) {
return new File(dir, filename).isFile() && regEx.matcher(filename).matches();
}
});
ArrayList<String> files = new ArrayList<String>();
for (File file : matchingFiles) {
files.add(file.getAbsolutePath());
}
return (String[])files.toArray(new String[files.size()]);
}
}
This also utilizes another helper I've put in place (Path.combine):
import java.io.File;
public class Path {
public static String combine(String... paths)
{
File file = new File(paths[0]);
for (int i = 1; i < paths.length ; i++) {
file = new File(file, paths[i]);
}
return file.getPath();
}
}
Usage
Directory.GetFiles("/Users/beardtwizzle/", "/Documents/javascripts/*.js");
This does still the other character problem that Bruno Silva mentions. So will try and bake a fix in for that too.

Clean approach to finding a folder's parent

I have this piece of code that searches for a folder given a starting directory. Once the folder is found I need the name of it's parent. The following piece of code works but it is terribly ugly. I have a flag, "sessionFound" to assist with breaking from nested foreach loops. The following works. I was hoping I could get some eyes on this and see if I could get some suggestions on how to make this less verbose and a bit more concise.
Thanks.
private void SetProjectFolder(string sessionid)
{
//IoC container gives the root directory to begin search.
string[] supportDirs = Directory.GetDirectories(ApplicationContainer.SupportDirectory);
bool sessionFound = false;
foreach (string directory in supportDirs)
{
if (!sessionFound)
{
foreach (string folder in Directory.GetDirectories(directory))
{
if (!sessionFound)
{
foreach (string productSubFolder in Directory.GetDirectories(folder))
{
if (productSubFolder.Contains(sessionid))
{
_productName = Directory.GetParent(productSubFolder).Parent.Name;
sessionFound = true;
break;
}
}
}
else
{
break;
}
}
}
else
{
break;
}
}
}
Try this:
private void SetProjectFolder(string sessionid)
{
//This will simulate the contains statement
string searchPattern = string.Format("*{0}*", sessionid);
string[] supportDirs = Directory.GetDirectories(ApplicationContainer.SupportDirectory, searchPattern);
foreach (string filteredFolder in supportDirs)
{
_productName = Directory.GetParent(filteredFolder).Name;
break;
}
}
By removing sessionFound you end up with a pretty clear and straightforward code:
private void SetProjectFolder(string sessionid)
{
//IoC container gives the root directory to begin search.
string[] supportDirs = Directory.GetDirectories(ApplicationContainer.SupportDirectory);
// search for product subfolder
foreach (string directory in supportDirs)
{
foreach (string folder in Directory.GetDirectories(directory))
{
foreach (string productSubFolder in Directory.GetDirectories(folder))
{
if (productSubFolder.Contains(sessionid))
{
// product sub-folder found, set it and exit
_productName = Directory.GetParent(productSubFolder).Parent.Name;
return;
}
}
}
}
// product sub-folder not found
!!! handle error path
}
In case your directory structure is static in the sense of the abovementioned search algorithm, I don't think there's something wrong with the nested foreach statements. Maybe you could extract the code starting with the middle foreach into a separate method like string FindProductSubfolderInFolder(Directory).

Categories

Resources