Skip to content

Commit

Permalink
Wrote FileMatcher class to handle globs and include/exclude lists
Browse files Browse the repository at this point in the history
  • Loading branch information
ktvoelker committed Feb 2, 2011
1 parent 011c582 commit 1b40141
Show file tree
Hide file tree
Showing 3 changed files with 248 additions and 3 deletions.
2 changes: 2 additions & 0 deletions Di/Di.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@
<Compile Include="Controller\WindowMode.cs" />
<Compile Include="BindList.cs" />
<Compile Include="Controller\MenuMode.cs" />
<Compile Include="FileMatcher.cs" />
</ItemGroup>
<ItemGroup>
<Reference Include="gtk-sharp, Version=2.12.0.0, Culture=neutral, PublicKeyToken=35e10195dab3c99f" />
Expand All @@ -63,6 +64,7 @@
<Reference Include="gdk-sharp, Version=2.12.0.0, Culture=neutral, PublicKeyToken=35e10195dab3c99f" />
<Reference Include="atk-sharp, Version=2.12.0.0, Culture=neutral, PublicKeyToken=35e10195dab3c99f" />
<Reference Include="glib-sharp, Version=2.12.0.0, Culture=neutral, PublicKeyToken=35e10195dab3c99f" />
<Reference Include="System" />
</ItemGroup>
<ItemGroup>
<Folder Include="Model\" />
Expand Down
242 changes: 242 additions & 0 deletions Di/FileMatcher.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,242 @@
//
// Regex.cs
//
// Author:
// Karl Voelker <[email protected]>
//
// Copyright (c) 2011 Karl Voelker
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Text.RegularExpressions;
namespace Di
{
public abstract class FileMatcher
{
private string includePattern = null;

private string excludePattern = null;

private Regex include = null;

private Regex exclude = null;

private Regex Include
{
get
{
if (include == null)
{
include = new Regex(includePattern);
}
return include;
}
}

private Regex Exclude
{
get
{
if (exclude == null)
{
exclude = new Regex(excludePattern);
}
return exclude;
}
}

public bool ExcludeExecutableFiles
{
get;
set;
}

public FileMatcher()
{
}

public void IncludeGlob(string glob)
{
include = null;
includePattern = RegexUnion(includePattern, glob);
}

public void ExcludeGlob(string glob)
{
exclude = null;
excludePattern = RegexUnion(excludePattern, glob);
}

private bool Match(string path)
{
return !Exclude.IsMatch(path) || Include.IsMatch(path);
}

public IList<FileInfo> MatchAll(DirectoryInfo root)
{
IList<FileInfo> result = new List<FileInfo>();
MatchAll(root, ref result);
return result;
}

private void MatchAll(DirectoryInfo dir, ref IList<FileInfo> matches)
{
if (Match(dir.FullName + Path.PathSeparator))
{
foreach (var subdir in dir.GetDirectories())
{
MatchAll(subdir, ref matches);
}
foreach (var file in dir.GetFiles())
{
if (Match(file.FullName))
{
matches.Add(file);
}
}
}
}

private static string Escape(char c)
{
switch (c)
{
case '-': // This only needs to be escaped in character classes, but it doesn't hurt.
case '.':
case '$':
case '^':
case '{':
case '[':
case '(':
case '|':
case ')':
case '*':
case '+':
case '?':
case '\\':
return "\\" + c;
default:
return c.ToString();
}
}

private static string RegexUnion(string a, string b)
{
if (a == null)
{
return b;
}
else if (b == null)
{
return a;
}
else
{
return "(?:(?:" + a + ")|(?:" + b + "))";
}
}

private static string RegexUnion(IEnumerable<string> xs)
{
return xs.FoldLeft1(RegexUnion);
}

// This converts a glob to a regex.
//
// The regex should be matched against the full name of the file or directory in question.
//
// The final path component of the glob will be matched against the final path component
// of the name being matched against.
//
// The initial path component of the glob may be matched against any path component
// of the name being matched against.
//
// Glob writers should add a trailing path separator when wishing to match a directory.
//
// When the regex is being matched against a directory, the path separator should be
// appended to the name.
private static Regex GlobToRegex(string glob)
{
if (glob[0] == Path.PathSeparator)
{
throw new ArgumentException("A glob may not start with a path separator.");
}
string pathSeparator = Escape(Path.PathSeparator);
string notPathSeparator = "[^" + pathSeparator + "]";
var pat = new StringBuilder();
// The match must begin at the beginning of the string or at a path separator.
pat.Append("(?:^|(?<=" + pathSeparator + "))");
bool inClass = false;
var classMembers = new HashSet<char>();
foreach (char c in glob)
{
if (inClass)
{
if (c == ']')
{
pat.Append('(');
bool first = true;
foreach (char m in classMembers)
{
if (first)
{
first = false;
}
else
{
pat.Append('|');
}
if (m == Path.PathSeparator)
{
throw new ArgumentException("A glob may not have a character class containing the path separator.");
}
pat.Append(Escape(m));
}
pat.Append(')');
}
else
{
classMembers.Add(c);
}
}
else if (c == '[')
{
inClass = true;
}
else if (c == '*')
{
pat.Append(notPathSeparator + "*");
}
else if (c == '?')
{
pat.Append(notPathSeparator);
}
else
{
pat.Append(Escape(c));
}
}
if (inClass)
{
throw new ArgumentException("A glob may not contain dangling character classes.");
}
pat.Append('$');
return new Regex(pat.ToString());
}
}
}

7 changes: 4 additions & 3 deletions Di/TODO.txt
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,8 @@ TODO

Add support for projects

Search up from the current directory for di-project.ini
Globs for inclusion and exclusion of files into project
Project name (show it, and nothing else other than "Di", in the title bar)
Use FileMatcher to get the list of files in the project
[include] and [exclude] sections from config are used

Add commands:
Close the current window
Expand All @@ -17,6 +16,8 @@ Add support for projects
Already-visible files should be least-preferred
But allow a file to become visible in multiple places

Write test suite for FileMatcher class

Add support for "undo"

Keep separate "undo" and "redo" stacks for each file in the project
Expand Down

0 comments on commit 1b40141

Please sign in to comment.