<commit_before>package Wordbath::Transcript;
use Moose;
use Modern::Perl;

#
# This is intended to serve as the model for the document, and provide
# the scrolled text widget.
#
# Maybe this should've subclassed textview, but it's encompassed
# in a scrolled window, and it uses Moose.

has scrolled_widget => (
  is => 'rw',
  isa => 'Object',
  builder => '_build_scrolled_widget',
);
has _text_widget => (
  is => 'rw',
  isa => 'Object',
);

sub _buf{
  my $self = shift;
  my $txt = $self->_text_widget;
  my $buf = $txt->get_buffer;
  return $buf;
}

sub _build_scrolled_widget{
  my $self = shift;
  my $scrolled = Gtk3::ScrolledWindow->new();
  $scrolled->set_vexpand(1);
  $scrolled->set_hexpand(0);
  my $wordbox = Gtk3::TextView->new();
  $wordbox->set_wrap_mode('word');
  $scrolled->add($wordbox);
  my $fontdesc = Pango::FontDescription->from_string('monospace 10');
  $wordbox->modify_font($fontdesc);
  $self->_text_widget($wordbox);

  $wordbox->signal_connect('move-cursor', \&_on_txt_move, $self);
  $wordbox->signal_connect('insert-at-cursor', \&_on_txt_insert, $self);
  $wordbox->signal_connect('delete-from-cursor', \&_on_txt_delete, $self);
  $wordbox->get_buffer->signal_connect('mark-set', \&_on_buf_mark_set, $self);
  $wordbox->get_buffer->signal_connect('changed', \&_on_buf_changed, $self);
  return $scrolled;
}

sub current_text{
  my $self = shift;
  my $buf = $self->_buf;
  my ($start, $end) = $buf->get_bounds();
  $end->forward_char();
  my $txt = $buf->get_text($start, $end, 1);
  return $txt;
}
sub grab_focus{
  my $self = shift;
  $self->_text_widget->grab_focus();
}

#misc callbacks. mark-set seems to be kind of a catch-all.
sub _on_txt_move{
  my ($txt,$step_size, $count, $extend_selection, $self) = @_;
}
sub _on_txt_insert{
  my ($txt,$string, $self) = @_;
  say "insertion event. txt: $string";
}
sub _on_txt_delete{
  my ($txt,$deltype, $count, $self) = @_;
  say "deletion event. deltype: $deltype, count: $count";
}
sub _on_buf_mark_set{
  my ($txt,$iter, $mark, $self) = @_;
  $self->_on_pos_change;
}
sub _on_buf_changed{
  my ($txt, $self) = @_;
  $self->_on_pos_change;
  my ($line, $col) = $self->get_text_pos();
  say "buf 'changed' event. Cursor: line $line, col $col";
}

# $data will be the first argument,
#  so the handler can be a method of whatever $data is.
has _signals => (
  is => 'rw',
  isa => 'HashRef',
  default => sub{{pos_change => [],}},
);
sub whenever {
  my ($self, $signal_name, $cb, $data) = @_;
  my $sigs = $self->_signals->{$signal_name};
  return unless $sigs;
  my $handler = {cb => $cb, data => $data};
  push @$sigs, $handler
}
sub blurp{
  my $self = shift;
  my $signal_name = shift;
  my $sigs = $self->_signals->{$signal_name};
  die "no such sig: $signal_name" unless $sigs;
  # @_ now contains signal-specific stuff,
  #  like cursor position or whatever.
  for my $handler (@$sigs){
    my $cb   = $handler->{cb};
    my $data = $handler->{data};
    $cb->($data, @_);
  }
}
# return number of signal handlers for a specific signal.
sub blurps{
  my ($self, $signal_name) = @_;
  my $sigs = $self->_signals->{$signal_name};
  return scalar @$sigs
}
sub _on_pos_change{
  my $self = shift;
  return unless $self->blurps('pos_change');
  my ($line, $col) = $self->get_text_pos();
  $self->blurp(pos_change => $line,$col);
}

<commit_msg>blurp mechanism for change signals<commit_after>package Wordbath::Transcript;
use Moose;
use Modern::Perl;

#
# This is intended to serve as the model for the document, and provide
# the scrolled text widget.
#
# Maybe this should've subclassed textview, but it's encompassed
# in a scrolled window, and it uses Moose.

has scrolled_widget => (
  is => 'rw',
  isa => 'Object',
  builder => '_build_scrolled_widget',
);
has _text_widget => (
  is => 'rw',
  isa => 'Object',
);

sub _buf{
  my $self = shift;
  my $txt = $self->_text_widget;
  my $buf = $txt->get_buffer;
  return $buf;
}

sub _build_scrolled_widget{
  my $self = shift;
  my $scrolled = Gtk3::ScrolledWindow->new();
  $scrolled->set_vexpand(1);
  $scrolled->set_hexpand(0);
  my $wordbox = Gtk3::TextView->new();
  $wordbox->set_wrap_mode('word');
  $scrolled->add($wordbox);
  my $fontdesc = Pango::FontDescription->from_string('monospace 10');
  $wordbox->modify_font($fontdesc);
  $self->_text_widget($wordbox);

  $wordbox->signal_connect('move-cursor', \&_on_txt_move, $self);
  $wordbox->signal_connect('insert-at-cursor', \&_on_txt_insert, $self);
  $wordbox->signal_connect('delete-from-cursor', \&_on_txt_delete, $self);
  $wordbox->get_buffer->signal_connect('mark-set', \&_on_buf_mark_set, $self);
  $wordbox->get_buffer->signal_connect('changed', \&_on_buf_changed, $self);
  return $scrolled;
}

sub current_text{
  my $self = shift;
  my $buf = $self->_buf;
  my ($start, $end) = $buf->get_bounds();
  $end->forward_char();
  my $txt = $buf->get_text($start, $end, 1);
  return $txt;
}
sub grab_focus{
  my $self = shift;
  $self->_text_widget->grab_focus();
}

#misc callbacks. mark-set seems to be kind of a catch-all.
sub _on_txt_move{
  my ($txt,$step_size, $count, $extend_selection, $self) = @_;
}
sub _on_txt_insert{
  my ($txt,$string, $self) = @_;
  say "insertion event. txt: $string";
}
sub _on_txt_delete{
  my ($txt,$deltype, $count, $self) = @_;
  say "deletion event. deltype: $deltype, count: $count";
}
sub _on_buf_mark_set{
  my ($txt,$iter, $mark, $self) = @_;
  $self->_on_pos_change;
}
sub _on_buf_changed{
  my ($txt, $self) = @_;
  $self->_on_pos_change;
  my ($line, $col) = $self->get_text_pos();
  say "buf 'changed' event. Cursor: line $line, col $col";
}

use Array::Compare;
# $data will be the first argument,
#  so the handler can be a method of whatever $data is.
has _signals => (
  is => 'ro',
  isa => 'HashRef',
  default => sub{{pos_change => [],}},
);
has _last_blurps =>(
  is => 'ro',
  isa => 'HashRef',
  default => sub{{}},
);
sub whenever {
  my ($self, $signal_name, $cb, $data) = @_;
  my $sigs = $self->_signals->{$signal_name};
  return unless $sigs;
  my $handler = {cb => $cb, data => $data};
  push @$sigs, $handler
}
sub blurp{
  my $self = shift;
  my $signal_name = shift;
  my $sigs = $self->_signals->{$signal_name};
  die "no such sig: $signal_name" unless $sigs;
  # @_ now contains signal-specific stuff,
  #  like cursor position or whatever.
  for my $handler (@$sigs){
    my $cb   = $handler->{cb};
    my $data = $handler->{data};
    $cb->($data, @_);
  }
  $self->_last_blurps->{$signal_name} = [@_];
}
# return number of signal handlers for a specific signal.
sub blurps{
  my ($self, $signal_name) = @_;
  my $sigs = $self->_signals->{$signal_name};
  return scalar @$sigs
}
#returns an arrayref. Or undef if this signal hasn't been blurped.
sub last_blurp{
  my ($self, $signal_name) = @_;
  return $self->_last_blurps->{$signal_name};
}
#takes a signal & arrayref.
sub last_blurp_matches{
  my ($self, $signal_name, $blurp) = @_;
  die "no such signal $signal_name"
     unless $self->blurps($signal_name);
  my $last_blurp = $self->last_blurp($signal_name);
  return 0 unless defined $last_blurp;
  my $comp = Array::Compare->new();
  return $comp->simple_compare ($blurp, $last_blurp);
}
sub _on_pos_change{
  my $self = shift;
  #use Array::Compare.
  return unless $self->blurps('pos_change');
  my ($line, $col) = $self->get_text_pos();

  #only blurp on changes. It's a change signal.
  return if $self->last_blurp_matches(pos_change => [$line,$col]);

  $self->blurp(pos_change => $line,$col);
}


{'fraction_non_alphanumeric': 0.17868589743589744, 'fraction_numerical': 0.002003205128205128, 'mean_word_length': 4.360773085182534, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 125, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3277782', 'n_tokens_mistral': 2891, 'n_tokens_neox': 2831, 'n_words': 709}