예제 #1
0
 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();
 }
예제 #2
0
 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();
 }
예제 #3
0
 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();
 }
예제 #4
0
 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);
 }
예제 #5
0
        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));
        }
예제 #6
0
        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);
        }
예제 #7
0
        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());
        }
예제 #8
0
        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);
        }