Skip to content

Commit

Permalink
Merge branch 'release/0.7.0'
Browse files Browse the repository at this point in the history
  • Loading branch information
patriksvensson committed Jan 18, 2018
2 parents b0cd7d7 + 3035393 commit 1c3b063
Show file tree
Hide file tree
Showing 64 changed files with 1,486 additions and 340 deletions.
26 changes: 26 additions & 0 deletions src/Jarvis.Addin.Files/Constants.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
// Licensed to Spectre Systems AB under one or more agreements.
// Spectre Systems AB licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

namespace Jarvis.Addin.Files
{
internal sealed class Constants
{
internal sealed class Settings
{
public const string Version = "Files.Version";

internal sealed class Include
{
public const string Folders = "Files.Include.Folders";
public const string Extensions = "Files.Include.Extensions";
}

internal sealed class Exclude
{
public const string Folders = "Files.Exclude.Folders";
public const string Patterns = "Files.Exclude.Patterns";
}
}
}
}
4 changes: 3 additions & 1 deletion src/Jarvis.Addin.Files/FileAddin.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
// See the LICENSE file in the project root for more information.

using Autofac;
using Jarvis.Addin.Files.Drawing;
using Jarvis.Addin.Files.Icons;
using Jarvis.Addin.Files.Indexing;
using Jarvis.Addin.Files.Sources;
using Jarvis.Addin.Files.Sources.Uwp;
Expand All @@ -18,12 +18,14 @@ public void Configure(ContainerBuilder builder)
{
builder.RegisterType<FileProvider>().As<IQueryProvider>().SingleInstance();
builder.RegisterType<FileIndexer>().As<IBackgroundWorker>().As<IFileIndex>().SingleInstance();
builder.RegisterType<FileSettingsSeeder>().As<ISettingsSeeder>().SingleInstance();

// Sources
builder.RegisterType<StartMenuIndexSource>().As<IFileIndexSource>().SingleInstance();
builder.RegisterType<UwpIndexSource>().As<IFileIndexSource>().SingleInstance();
builder.RegisterType<DocumentIndexSource>().As<IFileIndexSource>().SingleInstance();

// Utilities
builder.RegisterType<AppxManifestReader>().SingleInstance();
builder.RegisterType<IconLoader>().SingleInstance();
builder.RegisterType<NativeStreamProvider>().As<INativeStreamProvider>().SingleInstance();
Expand Down
5 changes: 3 additions & 2 deletions src/Jarvis.Addin.Files/FileProvider.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,16 +10,17 @@
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Media;
using Jarvis.Addin.Files.Drawing;
using Jarvis.Addin.Files.Icons;
using Jarvis.Addin.Files.Indexing;
using Jarvis.Addin.Files.ViewModels;
using Jarvis.Core;
using JetBrains.Annotations;
using static Jarvis.Addin.Files.Sources.Uwp.ShellInterop;

namespace Jarvis.Addin.Files
{
[UsedImplicitly]
internal sealed class FileProvider : QueryProvider<FileResult>
internal sealed class FileProvider : QueryProvider<FileResult, FileSettingsViewModel>
{
private readonly IFileIndex _index;
private readonly IconLoader _loader;
Expand Down
82 changes: 82 additions & 0 deletions src/Jarvis.Addin.Files/FileSettings.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
// Licensed to Spectre Systems AB under one or more agreements.
// Spectre Systems AB licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

using System;
using System.Collections.Generic;
using System.Linq;
using Jarvis.Core;
using Spectre.System.IO;

namespace Jarvis.Addin.Files
{
internal sealed class FileSettings
{
public int Version { get; set; }
public HashSet<DirectoryPath> IncludedFolders { get; }
public HashSet<string> IncludedExtensions { get; }
public HashSet<DirectoryPath> ExcludedFolders { get; }
public HashSet<string> ExcludedPatterns { get; }

public const int CurrentVersion = 1;

public FileSettings()
{
IncludedFolders = new HashSet<DirectoryPath>(new PathComparer(false));
IncludedExtensions = new HashSet<string>(StringComparer.Ordinal);
ExcludedFolders = new HashSet<DirectoryPath>(new PathComparer(false));
ExcludedPatterns = new HashSet<string>(StringComparer.Ordinal);
}

public static FileSettings Load(ISettingsStore settings)
{
var model = new FileSettings
{
Version = settings.Get<int>(Constants.Settings.Version)
};

// Load included folders.
var includeFolders = settings.Get<string>(Constants.Settings.Include.Folders);
if (includeFolders != null)
{
model.IncludedFolders.AddRange(includeFolders
.Split(new[] { '|' }, StringSplitOptions.RemoveEmptyEntries)
.Select(s => new DirectoryPath(s)));
}

// Load included extensions.
var includeExtensions = settings.Get<string>(Constants.Settings.Include.Extensions);
if (!string.IsNullOrWhiteSpace(includeExtensions))
{
model.IncludedExtensions.AddRange(includeExtensions.Split(new[] { '|' }, StringSplitOptions.RemoveEmptyEntries));
}

// Load excluded folders.
var excludeFolders = settings.Get<string>(Constants.Settings.Exclude.Folders);
if (excludeFolders != null)
{
model.ExcludedFolders.AddRange(excludeFolders
.Split(new[] { '|' }, StringSplitOptions.RemoveEmptyEntries)
.Select(s => new DirectoryPath(s)));
}

// Load excluded patterns.
var excludePatterns = settings.Get<string>(Constants.Settings.Exclude.Patterns);
if (!string.IsNullOrWhiteSpace(excludePatterns))
{
model.ExcludedPatterns.AddRange(excludePatterns.Split(new[] { '|' }, StringSplitOptions.RemoveEmptyEntries));
}

return model;
}

public void Save(ISettingsStore settings)
{
settings.Set(Constants.Settings.Version, CurrentVersion);
settings.Set(Constants.Settings.Include.Folders, string.Join("|", IncludedFolders.Select(p => p.FullPath)));
settings.Set(Constants.Settings.Include.Extensions, string.Join("|", IncludedExtensions));
settings.Set(Constants.Settings.Exclude.Folders, string.Join("|", ExcludedFolders.Select(p => p.FullPath)));
settings.Set(Constants.Settings.Exclude.Patterns, string.Join("|", ExcludedPatterns));
}
}
}
44 changes: 44 additions & 0 deletions src/Jarvis.Addin.Files/FileSettingsSeeder.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
// Licensed to Spectre Systems AB under one or more agreements.
// Spectre Systems AB licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

using System;
using Jarvis.Core;
using Spectre.System.IO;

namespace Jarvis.Addin.Files
{
internal sealed class FileSettingsSeeder : ISettingsSeeder
{
public void Seed(ISettingsStore store)
{
var model = FileSettings.Load(store);

if (model.Version == 0)
{
Initialize(model);
model.Save(store);
}
}

private static void Initialize(FileSettings model)
{
// Included folders.
model.IncludedFolders.Add(new DirectoryPath(Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments)));
model.IncludedFolders.Add(new DirectoryPath(Environment.GetFolderPath(Environment.SpecialFolder.MyMusic)));
model.IncludedFolders.Add(new DirectoryPath(Environment.GetFolderPath(Environment.SpecialFolder.MyVideos)));
model.IncludedFolders.Add(new DirectoryPath(Environment.GetFolderPath(Environment.SpecialFolder.MyPictures)));

// Included extensions.
model.IncludedExtensions.AddRange(new[]
{
"ai", "avi", "doc", "docx", "eps", "flv", "gif", "htm", "html",
"jpeg", "jpg", "mov", "mp3", "mp4", "mpg", "mpeg", "odt", "ogg", "ogv", "pdf", "png", "ppt", "psd",
"rar", "rtf", "svg", "txt", "wav", "wma", "xls", "xlsx", "zip"
});

// Excluded patterns.
model.ExcludedPatterns.AddRange(new[] { ".git", "node_modules", "packages" });
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
using System.Threading.Tasks;
using System.Windows.Media;

namespace Jarvis.Addin.Files.Drawing
namespace Jarvis.Addin.Files.Icons
{
internal sealed class IconLoader
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,12 @@
using System.Windows.Media.Imaging;
using Jarvis.Core.Interop;

namespace Jarvis.Addin.Files.Drawing
namespace Jarvis.Addin.Files.Icons
{
internal static class ShellIconLoader
{
private const Win32.Shgfi Flags =
Win32.Shgfi.Icon | Win32.Shgfi.LargeIcon | Win32.Shgfi.UseFileAttributes;
private const Win32.Shell.Shgfi Flags =
Win32.Shell.Shgfi.Icon | Win32.Shell.Shgfi.LargeIcon | Win32.Shell.Shgfi.UseFileAttributes;

public static async Task<ImageSource> LoadImageAsync(string path, bool isDirectory)
{
Expand Down Expand Up @@ -58,9 +58,9 @@ private static ImageSource LoadImage(string path, bool isDirectory)
return null;
}

var info = default(Win32.Shfileinfo);
Win32.SHGetFileInfo(path,
isDirectory ? Win32.FileAttribute.Directory : Win32.FileAttribute.Normal,
var info = default(Win32.Shell.Shfileinfo);
Win32.Shell.SHGetFileInfo(path,
isDirectory ? Win32.Shell.FileAttribute.Directory : Win32.Shell.FileAttribute.Normal,
out info, (uint)Marshal.SizeOf(info), Flags);

var iconHandle = info.hIcon;
Expand All @@ -82,7 +82,7 @@ private static ImageSource LoadImage(string path, bool isDirectory)
}
finally
{
Win32.DestroyIcon(iconHandle);
Win32.Shell.DestroyIcon(iconHandle);
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
using ColorConverter = System.Windows.Media.ColorConverter;
using Pen = System.Windows.Media.Pen;

namespace Jarvis.Addin.Files.Drawing
namespace Jarvis.Addin.Files.Icons
{
internal static class UwpIconLoader
{
Expand Down
46 changes: 32 additions & 14 deletions src/Jarvis.Addin.Files/Indexing/FileIndexer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Caliburn.Micro;
using Jarvis.Addin.Files.Collections;
using Jarvis.Core;
using Jarvis.Core.Diagnostics;
Expand All @@ -19,25 +20,29 @@
namespace Jarvis.Addin.Files.Indexing
{
[UsedImplicitly]
internal sealed class FileIndexer : IBackgroundWorker, IFileIndex
internal sealed class FileIndexer : IBackgroundWorker, IFileIndex, IHandle<TriggerIndexMessage>
{
private readonly IJarvisLog _log;
private readonly List<IFileIndexSource> _sources;
private readonly HashSet<string> _stopWords;
private readonly ScoreComparer _comparer;
private readonly IndexedEntryComparer _entryComparer;
private readonly ManualResetEvent _trigger;

private Trie<IndexedEntry> _trie;

public string Name => "File indexing service";

public FileIndexer(IEnumerable<IFileIndexSource> sources, IJarvisLog log)
public FileIndexer(IEventAggregator events, IEnumerable<IFileIndexSource> sources, IJarvisLog log)
{
_log = new LogDecorator("FileIndexer", log);
_sources = new List<IFileIndexSource>(sources ?? Array.Empty<IFileIndexSource>());
_stopWords = new HashSet<string>(StringComparer.OrdinalIgnoreCase) { "to", "the" };
_comparer = new ScoreComparer();
_entryComparer = new IndexedEntryComparer();
_trigger = new ManualResetEvent(false);

events.Subscribe(this);
}

public Task<bool> Run(CancellationToken token)
Expand All @@ -46,8 +51,8 @@ public Task<bool> Run(CancellationToken token)
{
while (true)
{
var st = new Stopwatch();
st.Start();
var indexingWatch = new Stopwatch();
indexingWatch.Start();

var result = LoadResults(token);

Expand All @@ -57,8 +62,8 @@ public Task<bool> Run(CancellationToken token)
}

_log.Debug("Updating index...");
var st2 = new Stopwatch();
st2.Start();
var indexUpdateWatch = new Stopwatch();
indexUpdateWatch.Start();

var trie = new Trie<IndexedEntry>();
foreach (var file in result)
Expand Down Expand Up @@ -86,31 +91,39 @@ public Task<bool> Run(CancellationToken token)
}
}

st2.Stop();
_log.Debug($"Building trie took {st2.ElapsedMilliseconds}ms");
indexUpdateWatch.Stop();
_log.Debug($"Building trie took {indexUpdateWatch.ElapsedMilliseconds}ms");

_log.Debug("Writing index...");
Interlocked.Exchange(ref _trie, trie);

_log.Verbose($"Nodes: {_trie.NodeCount}");
_log.Verbose($"Items: {_trie.ItemCount}");

// Wait for a minute.
st.Stop();
_log.Debug($"Indexing done. Took {st.ElapsedMilliseconds}ms");
indexingWatch.Stop();
_log.Debug($"Indexing done. Took {indexingWatch.ElapsedMilliseconds}ms");

if (token.WaitHandle.WaitOne((int)TimeSpan.FromMinutes(5).TotalMilliseconds))
// Wait for a while.
var index = WaitHandle.WaitAny(new[] { token.WaitHandle, _trigger }, (int)TimeSpan.FromMinutes(5).TotalMilliseconds);
if (index == 0)
{
_log.Information("We were instructed to stop (2).");
break;
}

// Triggered update?
if (index == 1)
{
_log.Information("A re-index was triggered.");
_trigger.Reset();
}
}

return true;
});
}, token);
}

private List<IndexedEntry> LoadResults(CancellationToken token)
private IEnumerable<IndexedEntry> LoadResults(CancellationToken token)
{
// Ask all sources for files.
return _sources.AsParallel()
Expand Down Expand Up @@ -183,5 +196,10 @@ private static float CalculateScore(IndexedEntry entry, string query)
return Math.Min(LevenshteinScorer.Score(entry.Title, query),
LevenshteinScorer.Score(entry.Description ?? entry.Title, query));
}

public void Handle(TriggerIndexMessage message)
{
_trigger.Set();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,9 @@
// Spectre Systems AB licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

namespace Jarvis.Core.Interop
namespace Jarvis.Addin.Files.Indexing
{
public static partial class Win32
internal sealed class TriggerIndexMessage
{
public enum Hresult : uint
{
Ok = 0x0000,
}
}
}
Loading

0 comments on commit 1c3b063

Please sign in to comment.