public LineMatches(ICombinedSearchMetadataCollection searchMetadataCollection) { _strings = searchMetadataCollection.Combined .Connect() .IgnoreUpdateWhen((current, previous) => SearchMetadata.EffectsHighlightComparer.Equals(current, previous)) .QueryWhenChanged(query => query.Items.OrderBy(si => si.Position)) .Replay(1) .RefCount(); }
public TextFormatter(ICombinedSearchMetadataCollection searchMetadataCollection) { _strings = searchMetadataCollection.Combined .Connect(meta => meta.Highlight && !meta.IsExclusion) .IgnoreUpdateWhen((current, previous) => SearchMetadata.EffectsHighlightComparer.Equals(current, previous)) .QueryWhenChanged(query => query.Items.OrderBy(m => m.Position)) .Replay(1) .RefCount(); }
public InlineViewer Create(ICombinedSearchMetadataCollection combinedSearchMetadataCollection, IObservable<ILineProvider> lineProvider, IObservable<LineProxy> selectedChanged) { var args = new IArgument[] { new Argument<IObservable<ILineProvider>>(lineProvider), new Argument<IObservable<LineProxy>>(selectedChanged), new Argument<ICombinedSearchMetadataCollection>(combinedSearchMetadataCollection) }; return _objectProvider.Get<InlineViewer>(args); }
public InlineViewer Create(ICombinedSearchMetadataCollection combinedSearchMetadataCollection, IObservable <ILineProvider> lineProvider, IObservable <LineProxy> selectedChanged) { var args = new IArgument[] { new Argument <IObservable <ILineProvider> >(lineProvider), new Argument <IObservable <LineProxy> >(selectedChanged), new Argument <ICombinedSearchMetadataCollection>(combinedSearchMetadataCollection) }; return(_objectProvider.Get <InlineViewer>(args)); }
public SearchInfoCollection(ICombinedSearchMetadataCollection combinedSearchMetadataCollection, ISearchMetadataFactory searchMetadataFactory, IFileWatcher fileWatcher) { _localMetadataCollection = combinedSearchMetadataCollection.Local; _combinedSearchMetadataCollection = combinedSearchMetadataCollection; _searchMetadataFactory = searchMetadataFactory; _fileWatcher = fileWatcher; var exclusionPredicate = combinedSearchMetadataCollection.Combined.Connect() .IncludeUpdateWhen((current, previous) => !SearchMetadata.EffectsFilterComparer.Equals(current, previous)) .Filter(meta => meta.IsExclusion) .ToCollection() .Select(searchMetadataItems => { Func <string, bool> predicate = null; if (searchMetadataItems.Count == 0) { return(predicate); } var predicates = searchMetadataItems.Select(meta => meta.BuildPredicate()).ToArray(); predicate = str => { return(!predicates.Any(item => item(str))); }; return(predicate); }).StartWith((Func <string, bool>)null) .Replay(1).RefCount(); All = exclusionPredicate.Select(predicate => { if (predicate == null) { return(_fileWatcher.Latest.Index()); } return(_fileWatcher.Latest.Search(predicate)); }).Switch().Replay(1).RefCount(); //create a collection with 1 item, which is used to show entire file var systemSearches = new SourceCache <SearchInfo, string>(t => t.SearchText); systemSearches.AddOrUpdate(new SearchInfo("<All>", false, All, SearchType.All)); //create a collection of all possible user filters var userSearches = combinedSearchMetadataCollection.Combined .Connect(meta => meta.Filter) .IgnoreUpdateWhen((current, previous) => SearchMetadata.EffectsFilterComparer.Equals(current, previous)) .Transform(meta => { var latest = exclusionPredicate .Select(exclpredicate => { Func <string, bool> resultingPredicate; if (exclpredicate == null) { resultingPredicate = meta.BuildPredicate(); } else { var toMatch = meta.BuildPredicate(); resultingPredicate = str => toMatch(str) && exclpredicate(str); } return(_fileWatcher.Latest.Search(resultingPredicate)); }) .Switch() .Replay(1).RefCount(); return(new SearchInfo(meta.SearchText, meta.IsGlobal, latest, SearchType.User)); }); //combine the results into a single collection Searches = systemSearches.Connect() .Or(userSearches) .AsObservableCache(); _cleanUp = new CompositeDisposable(Searches, systemSearches); }
public TailViewModel([NotNull] ILogger logger, [NotNull] ISchedulerProvider schedulerProvider, [NotNull] IFileWatcher fileWatcher, [NotNull] ISelectionMonitor selectionMonitor, [NotNull] IClipboardHandler clipboardHandler, [NotNull] ISearchInfoCollection searchInfoCollection, [NotNull] IInlineViewerFactory inlineViewerFactory, [NotNull] GeneralOptionBindings generalOptionBindings, [NotNull] ICombinedSearchMetadataCollection combinedSearchMetadataCollection, [NotNull] IStateBucketService stateBucketService, [NotNull] ITailViewStateRestorer restorer, [NotNull] SearchHints searchHints, [NotNull] ITailViewStateControllerFactory tailViewStateControllerFactory, [NotNull] IThemeProvider themeProvider, [NotNull] SearchCollection searchCollection, [NotNull] ITextFormatter textFormatter, [NotNull] ILineMatches lineMatches, [NotNull] IObjectProvider objectProvider, [NotNull] IDialogCoordinator dialogCoordinator) { if (logger == null) { throw new ArgumentNullException(nameof(logger)); } if (schedulerProvider == null) { throw new ArgumentNullException(nameof(schedulerProvider)); } if (fileWatcher == null) { throw new ArgumentNullException(nameof(fileWatcher)); } if (clipboardHandler == null) { throw new ArgumentNullException(nameof(clipboardHandler)); } if (searchInfoCollection == null) { throw new ArgumentNullException(nameof(searchInfoCollection)); } if (inlineViewerFactory == null) { throw new ArgumentNullException(nameof(inlineViewerFactory)); } if (stateBucketService == null) { throw new ArgumentNullException(nameof(stateBucketService)); } if (themeProvider == null) { throw new ArgumentNullException(nameof(themeProvider)); } if (textFormatter == null) { throw new ArgumentNullException(nameof(textFormatter)); } if (lineMatches == null) { throw new ArgumentNullException(nameof(lineMatches)); } if (objectProvider == null) { throw new ArgumentNullException(nameof(objectProvider)); } if (dialogCoordinator == null) { throw new ArgumentNullException(nameof(dialogCoordinator)); } if (combinedSearchMetadataCollection == null) { throw new ArgumentNullException(nameof(combinedSearchMetadataCollection)); } Name = fileWatcher.FullName; SelectionMonitor = selectionMonitor ?? throw new ArgumentNullException(nameof(selectionMonitor)); GeneralOptionBindings = generalOptionBindings; SearchHints = searchHints ?? throw new ArgumentNullException(nameof(searchHints)); CopyToClipboardCommand = new Command(() => clipboardHandler.WriteToClipboard(selectionMonitor.GetSelectedText())); OpenFileCommand = new Command(() => Process.Start(fileWatcher.FullName)); OpenFolderCommand = new Command(() => Process.Start(fileWatcher.Folder)); CopyPathToClipboardCommand = new Command(() => clipboardHandler.WriteToClipboard(fileWatcher.FullName)); UnClearCommand = new Command(fileWatcher.Reset); ClearCommand = new Command(fileWatcher.Clear); KeyAutoTail = new Command(() => { AutoTail = true; }); OpenSearchOptionsCommand = new Command(async() => { await Task.Run(() => { var content = objectProvider.Get <SearchOptionsViewModel>(new Argument <ICombinedSearchMetadataCollection>(combinedSearchMetadataCollection)); dialogCoordinator.Show(this, content, x => content.Dispose()); }); }); var closeOnDeselect = this.WhenValueChanged(vm => vm.IsSelected, false) .Where(selected => !selected) .Subscribe(_ => dialogCoordinator.Close()); SearchCollection = searchCollection ?? throw new ArgumentNullException(nameof(searchCollection)); SearchMetadataCollection = combinedSearchMetadataCollection.Local; var horizonalScrollArgs = new ReplaySubject <TextScrollInfo>(1); HorizonalScrollChanged = args => horizonalScrollArgs.OnNext(args); _tailViewStateControllerFactory = tailViewStateControllerFactory; //this deals with state when loading the system at start up and at shut-down _persister = new TailViewPersister(this, restorer); FileStatus = fileWatcher.Status.ForBinding(); //command to add the current search to the tail collection var searchInvoker = SearchHints.SearchRequested.Subscribe(request => searchInfoCollection.Add(request.Text, request.UseRegEx)); //An observable which acts as a scroll command var autoChanged = this.WhenValueChanged(vm => vm.AutoTail); var scroller = _userScrollRequested.CombineLatest(autoChanged, (user, auto) => { var mode = AutoTail ? ScrollReason.Tail : ScrollReason.User; return(new ScrollRequest(mode, user.PageSize, user.FirstIndex)); }) .Do(x => logger.Info("Scrolling to {0}/{1}", x.FirstIndex, x.PageSize)) .DistinctUntilChanged(); //User feedback to show file size FileSizeText = fileWatcher.Latest.Select(fn => fn.Size) .Select(size => size.FormatWithAbbreviation()) .DistinctUntilChanged() .ForBinding(); //tailer is the main object used to tail, scroll and filter in a file var selectedProvider = SearchCollection.Latest.ObserveOn(schedulerProvider.Background); var lineScroller = new LineScroller(selectedProvider, scroller); MaximumChars = lineScroller.MaximumLines() .ObserveOn(schedulerProvider.MainThread) .ForBinding(); var lineProxyFactory = new LineProxyFactory(textFormatter, lineMatches, horizonalScrollArgs.DistinctUntilChanged(), themeProvider); var loader = lineScroller.Lines.Connect() .LogChanges(logger, "Received") .Transform(lineProxyFactory.Create) .LogChanges(logger, "Sorting") .Sort(SortExpressionComparer <LineProxy> .Ascending(proxy => proxy)) .ObserveOn(schedulerProvider.MainThread) .Bind(out _data, 100) .LogChanges(logger, "Bound") .DisposeMany() .LogErrors(logger) .Subscribe(); //monitor matching lines and start index, Count = searchInfoCollection.All.Select(latest => latest.Count).ForBinding(); CountText = searchInfoCollection.All.Select(latest => $"{latest.Count:##,###} lines").ForBinding(); LatestCount = SearchCollection.Latest.Select(latest => latest.Count).ForBinding(); ////track first visible index var firstIndexMonitor = lineScroller.Lines.Connect() .Buffer(TimeSpan.FromMilliseconds(25)).FlattenBufferResult() .ToCollection() .Select(lines => lines.Count == 0 ? 0 : lines.Select(l => l.Index).Max() - lines.Count + 1) .ObserveOn(schedulerProvider.MainThread) .Subscribe(first => { FirstIndex = first; }); //Create objects required for inline viewing var isUserDefinedChanged = SearchCollection.WhenValueChanged(sc => sc.Selected) .Where(selected => selected != null) .Select(selected => selected.IsUserDefined) .DistinctUntilChanged() .Replay(1) .RefCount(); var showInline = this.WhenValueChanged(vm => vm.ShowInline); var inlineViewerVisible = isUserDefinedChanged.CombineLatest(showInline, (userDefined, showInlne) => userDefined && showInlne); CanViewInline = isUserDefinedChanged.ForBinding(); InlineViewerVisible = inlineViewerVisible.ForBinding(); //return an empty line provider unless user is viewing inline - this saves needless trips to the file var inline = searchInfoCollection.All.CombineLatest(inlineViewerVisible, (index, ud) => ud ? index : EmptyLineProvider.Instance); InlineViewer = inlineViewerFactory.Create(combinedSearchMetadataCollection, inline, this.WhenValueChanged(vm => vm.SelectedItem)); _cleanUp = new CompositeDisposable(lineScroller, loader, firstIndexMonitor, FileStatus, Count, CountText, LatestCount, FileSizeText, CanViewInline, InlineViewer, InlineViewerVisible, SearchCollection, searchInfoCollection, searchHints, SelectionMonitor, closeOnDeselect, Disposable.Create(dialogCoordinator.Close), searchInvoker, MaximumChars, _stateMonitor, combinedSearchMetadataCollection, horizonalScrollArgs.SetAsComplete(), _userScrollRequested.SetAsComplete()); }
public SearchOptionsViewModel(ICombinedSearchMetadataCollection combinedSearchMetadataCollection, ISearchProxyCollectionFactory searchProxyCollectionFactory, ISearchMetadataFactory searchMetadataFactory, ISchedulerProvider schedulerProvider, SearchHints searchHints) { SearchHints = searchHints; var global = combinedSearchMetadataCollection.Global; var local = combinedSearchMetadataCollection.Local; Action <SearchMetadata> changeScopeAction = meta => { if (meta.IsGlobal) { //make global global.Remove(meta.SearchText); var newValue = new SearchMetadata(meta, local.NextIndex(), false); local.AddorUpdate(newValue); } else { //make local local.Remove(meta.SearchText); var newValue = new SearchMetadata(meta, global.NextIndex(), true); global.AddorUpdate(newValue); } }; Local = searchProxyCollectionFactory.Create(local, Id, changeScopeAction); Global = searchProxyCollectionFactory.Create(global, Id, changeScopeAction); //command to add the current search to the tail collection var searchInvoker = SearchHints.SearchRequested .ObserveOn(schedulerProvider.Background) .Subscribe(request => { var isGlobal = SelectedIndex == 1; var nextIndex = isGlobal ? global.NextIndex() : local.NextIndex(); var meta = searchMetadataFactory.Create(request.Text, request.UseRegEx, nextIndex, false, isGlobal); if (isGlobal) { global.AddorUpdate(meta); } else { local.AddorUpdate(meta); } }); _cleanUp = new CompositeDisposable(searchInvoker, searchInvoker, SearchHints, Global, Local); }
public SearchOptionsViewModel(ICombinedSearchMetadataCollection combinedSearchMetadataCollection, ISearchProxyCollectionFactory searchProxyCollectionFactory, ISearchMetadataFactory searchMetadataFactory, ISchedulerProvider schedulerProvider, SearchHints searchHints) { SearchHints = searchHints; var global = combinedSearchMetadataCollection.Global; var local = combinedSearchMetadataCollection.Local; Action<SearchMetadata> changeScopeAction = meta => { if (meta.IsGlobal) { //make global global.Remove(meta.SearchText); var newValue = new SearchMetadata(meta, local.NextIndex(),false); local.AddorUpdate(newValue); } else { //make local local.Remove(meta.SearchText); var newValue = new SearchMetadata(meta, global.NextIndex(), true); global.AddorUpdate(newValue); } }; Local = searchProxyCollectionFactory.Create(local, Id, changeScopeAction); Global = searchProxyCollectionFactory.Create(global, Id, changeScopeAction); //command to add the current search to the tail collection var searchInvoker = SearchHints.SearchRequested .ObserveOn(schedulerProvider.Background) .Subscribe(request => { var isGlobal = SelectedIndex == 1; var nextIndex = isGlobal ? global.NextIndex() : local.NextIndex(); var meta = searchMetadataFactory.Create(request.Text, request.UseRegEx, nextIndex, false, isGlobal); if (isGlobal) { global.AddorUpdate(meta); } else { local.AddorUpdate(meta); } }); _cleanUp = new CompositeDisposable(searchInvoker, searchInvoker, SearchHints, Global, Local); }
public SearchInfoCollection(ICombinedSearchMetadataCollection combinedSearchMetadataCollection, ISearchMetadataFactory searchMetadataFactory, IFileWatcher fileWatcher) { _localMetadataCollection = combinedSearchMetadataCollection.Local; _combinedSearchMetadataCollection = combinedSearchMetadataCollection; _searchMetadataFactory = searchMetadataFactory; _fileWatcher = fileWatcher; var exclusionPredicate = combinedSearchMetadataCollection.Combined.Connect() .IncludeUpdateWhen((current, previous) => !SearchMetadata.EffectsFilterComparer.Equals(current, previous)) .Filter(meta=> meta.IsExclusion) .ToCollection() .Select(searchMetadataItems => { Func<string, bool> predicate = null; if (searchMetadataItems.Count == 0) return predicate; var predicates = searchMetadataItems.Select(meta => meta.BuildPredicate()).ToArray(); predicate = str => { return !predicates.Any(item => item(str)); }; return predicate; }).StartWith((Func<string, bool>)null) .Replay(1).RefCount(); All = exclusionPredicate.Select(predicate => { if (predicate==null) return _fileWatcher.Latest.Index(); return _fileWatcher.Latest.Search(predicate); }).Switch().Replay(1).RefCount(); //create a collection with 1 item, which is used to show entire file var systemSearches = new SourceCache<SearchInfo, string>(t => t.SearchText); systemSearches.AddOrUpdate(new SearchInfo("<All>", false, All, SearchType.All)); //create a collection of all possible user filters var userSearches = combinedSearchMetadataCollection.Combined .Connect(meta => meta.Filter) .IgnoreUpdateWhen((current,previous)=> SearchMetadata.EffectsFilterComparer.Equals(current, previous)) .Transform(meta => { var latest = exclusionPredicate .Select(exclpredicate => { Func<string, bool> resultingPredicate; if (exclpredicate == null) { resultingPredicate = meta.BuildPredicate(); } else { var toMatch = meta.BuildPredicate(); resultingPredicate = str=> toMatch(str) && exclpredicate(str); } return _fileWatcher.Latest.Search(resultingPredicate); }) .Switch() .Replay(1).RefCount(); return new SearchInfo(meta.SearchText, meta.IsGlobal, latest, SearchType.User); }); //combine te results into a single collection Searches = systemSearches.Connect() .Or(userSearches) .AsObservableCache(); _cleanUp = new CompositeDisposable(Searches, systemSearches); }