Пример #1
0
        public void ScrollToSpecificLine()
        {
            var file = Path.GetTempFileName();
            var info = new FileInfo(file);

            var textMatch = Observable.Return((string)null);

            var autoTailer = new ReplaySubject <ScrollRequest>(1);

            autoTailer.OnNext(new ScrollRequest(10, 15));

            File.AppendAllLines(file, Enumerable.Range(1, 100).Select(i => $"{i}").ToArray());

            using (var tailer = new FileTailer(info, textMatch, autoTailer))
            {
                tailer.Lines.Items.Select(l => l.Number).ShouldAllBeEquivalentTo(Enumerable.Range(15, 10));


                autoTailer.OnNext(new ScrollRequest(15, 50));

                File.Delete(file);

                tailer.Lines.Items.Select(l => l.Number).ShouldAllBeEquivalentTo(Enumerable.Range(50, 15));
            }
        }
Пример #2
0
        public void AutoTailWithFilter()
        {
            var file       = Path.GetTempFileName();
            var info       = new FileInfo(file);
            var scheduler  = new TestScheduler();
            var textMatch  = Observable.Return((string)"1");
            var autoTailer = Observable.Return(new ScrollRequest(10));


            File.AppendAllLines(file, Enumerable.Range(1, 100).Select(i => i.ToString()).ToArray());

            using (var tailer = new FileTailer(info, textMatch, autoTailer, scheduler))
            {
                //lines which contain "1"
                var expectedLines = Enumerable.Range(1, 100)
                                    .Select(i => i.ToString())
                                    .Where(s => s.Contains("1"))
                                    .Reverse()
                                    .Take(10)
                                    .Reverse()
                                    .Select(int.Parse)
                                    .ToArray();


                tailer.Lines.Items.Select(l => l.Number).ShouldAllBeEquivalentTo(expectedLines);


                File.AppendAllLines(file, Enumerable.Range(101, 10).Select(i => i.ToString()));


                //lines which contain "1"
                expectedLines = Enumerable.Range(1, 110)
                                .Select(i => i.ToString())
                                .Where(s => s.Contains("1"))
                                .Reverse()
                                .Take(10)
                                .Reverse()
                                .Select(int.Parse)
                                .ToArray();


                scheduler.AdvanceByMilliSeconds(250);
                File.Delete(file);
                tailer.Lines.Items.Select(l => l.Number).ShouldAllBeEquivalentTo(expectedLines);
            }
        }
Пример #3
0
        public void AutoTail()
        {
            var file       = Path.GetTempFileName();
            var info       = new FileInfo(file);
            var scheduler  = new TestScheduler();
            var textMatch  = Observable.Return((string)null);
            var autoTailer = Observable.Return(new ScrollRequest(10));

            File.AppendAllLines(file, Enumerable.Range(1, 100).Select(i => i.ToString()).ToArray());

            using (var tailer = new FileTailer(info, textMatch, autoTailer, scheduler))
            {
                tailer.Lines.Items.Select(l => l.Number).ShouldAllBeEquivalentTo(Enumerable.Range(91, 10));
                File.AppendAllLines(file, Enumerable.Range(101, 10).Select(i => i.ToString()));

                scheduler.AdvanceByMilliSeconds(250);
                File.Delete(file);
                tailer.Lines.Items.Select(l => l.Number).ShouldAllBeEquivalentTo(Enumerable.Range(101, 10));
            }
        }
Пример #4
0
        public void AutoTailWithFilter()
        {
            var file = Path.GetTempFileName();
            var info = new FileInfo(file);

            var textMatch  = Observable.Return("1");
            var autoTailer = Observable.Return(new ScrollRequest(10));

            File.AppendAllLines(file, Enumerable.Range(1, 100).Select(i => $"{i}").ToArray());

            using (var tailer = new FileTailer(info, textMatch, autoTailer))
            {
                // lines that contain "1"
                var expectedLines = Enumerable.Range(1, 100)
                                    .Select(i => $"{i}")
                                    .Where(s => s.Contains("1"))
                                    .Reverse()
                                    .Take(10)
                                    .Select(int.Parse)
                                    .ToArray();

                tailer.Lines.Items.Select(l => l.Number).ShouldAllBeEquivalentTo(expectedLines);

                File.AppendAllLines(file, Enumerable.Range(101, 10).Select(i => $"{i}"));

                Thread.Sleep(TimeSpan.FromSeconds(1));

                File.Delete(file);

                // lines that contain "1"
                expectedLines = Enumerable.Range(1, 110)
                                .Select(i => $"{i}")
                                .Where(s => s.Contains("1"))
                                .Reverse()
                                .Take(10)
                                .Select(int.Parse)
                                .ToArray();

                tailer.Lines.Items.Select(l => l.Number).ShouldAllBeEquivalentTo(expectedLines);
            }
        }
Пример #5
0
        public void AutoTail()
        {
            var file = Path.GetTempFileName();
            var info = new FileInfo(file);

            var textMatch  = Observable.Return((string)null);
            var autoTailer = Observable.Return(new ScrollRequest(10));

            File.AppendAllLines(file, Enumerable.Range(1, 100).Select(i => $"{i}").ToArray());

            using (var tailer = new FileTailer(info, textMatch, autoTailer))
            {
                tailer.Lines.Items.Select(l => l.Number).ShouldAllBeEquivalentTo(Enumerable.Range(91, 10));

                File.AppendAllLines(file, Enumerable.Range(101, 10).Select(i => $"{i}"));

                Thread.Sleep(TimeSpan.FromSeconds(1));

                File.Delete(file);

                tailer.Lines.Items.Select(l => l.Number).ShouldAllBeEquivalentTo(Enumerable.Range(101, 10));
            }
        }
Пример #6
0
        public FileTailerViewModel(ILogger logger, ISchedulerProvider schedulerProvider, FileInfo fileInfo)
        {
            if (logger == null)
            {
                throw new ArgumentNullException(nameof(logger));
            }
            if (schedulerProvider == null)
            {
                throw new ArgumentNullException(nameof(schedulerProvider));
            }
            if (fileInfo == null)
            {
                throw new ArgumentNullException(nameof(fileInfo));
            }

            var filterRequest = this.WhenValueChanged(vm => vm.SearchText).Throttle(TimeSpan.FromMilliseconds(125));
            var autoChanged   = this.WhenValueChanged(vm => vm.AutoTail);
            var scroller      = _userScrollRequested
                                .CombineLatest(autoChanged, (user, auto) =>
            {
                var mode = AutoTail ? ScrollingMode.Tail : ScrollingMode.User;
                return(new ScrollRequest(mode, user.PageSize, user.FirstIndex));
            })
                                .Sample(TimeSpan.FromMilliseconds(150))
                                .DistinctUntilChanged();

            var tailer = new FileTailer(fileInfo, filterRequest, scroller);


            //create user display for count line count
            var lineCounter = tailer.TotalLines.CombineLatest(tailer.MatchedLines, (total, matched) =>
            {
                return(total == matched
                    ? $"File has {total.ToString("#,###")} lines"
                    : $"Showing {matched.ToString("#,###")} of {total.ToString("#,###")} lines");
            })
                              .Subscribe(text => LineCountText = text);


            //load lines into observable collection
            var loader = tailer.Lines.Connect()
                         .Transform(line => new LineProxy(line))
                         .Sort(SortExpressionComparer <LineProxy> .Ascending(proxy => proxy.Number))
                         .ObserveOn(schedulerProvider.MainThread)
                         .Bind(out _data)
                         .Subscribe(changes => logger.Info($"Rows changed {changes.Adds} adds, {changes.Removes} removed"),
                                    ex => logger.Error(ex, "There is a problem with bind data"));


            //monitor matching lines and start index,
            var matchedLinesMonitor = tailer.MatchedLines
                                      .Subscribe(matched => MatchedLineCount = matched);

            //track first visible index
            var firstIndexMonitor = tailer.Lines.Connect()
                                    .QueryWhenChanged(lines => lines.Count == 0 ? 0 : lines.Select(l => l.Index).Min())
                                    .Subscribe(first => FirstIndex = first);


            _cleanUp = new CompositeDisposable(tailer,
                                               lineCounter,
                                               loader,
                                               firstIndexMonitor,
                                               matchedLinesMonitor,
                                               Disposable.Create(() =>
            {
                _userScrollRequested.OnCompleted();
            }));
        }
        public FileTailerViewModel(ILogger logger, ISchedulerProvider schedulerProvider, FileInfo fileInfo)
        {
            if (logger == null)
            {
                throw new ArgumentNullException(nameof(logger));
            }
            if (schedulerProvider == null)
            {
                throw new ArgumentNullException(nameof(schedulerProvider));
            }
            if (fileInfo == null)
            {
                throw new ArgumentNullException(nameof(fileInfo));
            }

            File     = fileInfo.FullName;
            AutoTail = true;

            var filterRequest = this.WhenValueChanged(vm => vm.SearchText).Throttle(TimeSpan.FromMilliseconds(125));
            var autoTail      = this.WhenValueChanged(vm => vm.AutoTail)
                                .CombineLatest(_userScrollRequested, (auto, user) =>
                                               auto
                        ? new ScrollRequest(user.Rows)
                        : new ScrollRequest(user.Rows, user.FirstIndex + 1))
                                .DistinctUntilChanged();

            var tailer = new FileTailer(fileInfo, filterRequest, autoTail);

            var lineCounter = tailer
                              .TotalLinesCount
                              .CombineLatest(tailer.MatchedLinesCount, (total, matched) =>
                                             total == matched
                        ? $"File has {total:#,###} lines"
                        : $"Showing {matched:#,###} of {total:#,###} lines")
                              .Subscribe(text => LineCountText = text);

            // load lines into observable collection
            var loader = tailer.Lines.Connect()
                         .Buffer(TimeSpan.FromMilliseconds(125)).FlattenBufferResult()
                         .Transform(line => new LineProxy(line))
                         .Sort(SortExpressionComparer <LineProxy> .Ascending(proxy => proxy.Number))
                         .ObserveOn(schedulerProvider.MainThread)
                         .Bind(out _data)
                         .Do(_ => AutoScroller.ScrollToEnd())
                         .Subscribe(a => logger.Info(a.Adds.ToString()), ex => logger.Error(ex, "Opps"));

            // monitor matching lines and start index
            // update local values so the virtual scroll panel can bind to them
            var matchedLinesMonitor = tailer.MatchedLinesCount
                                      .Subscribe(matched => MatchedLinesCount = matched);

            var firstIndexMonitor = tailer.Lines.Connect()
                                    .QueryWhenChanged(lines =>
            {
                // use zero based index rather than line number
                return(lines.Count == 0 ? 0 : lines.Select(l => l.Number).Min() - 1);
            }).Subscribe(first => FirstRow = first - 1);

            _cleanup = new CompositeDisposable(
                tailer,
                lineCounter,
                loader,
                matchedLinesMonitor,
                firstIndexMonitor,
                Disposable.Create(() => _userScrollRequested.OnCompleted()));
        }