Dotfiles, utilities, and other apparatus.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

217 lines
4.5 KiB

#!/usr/bin/env perl
=pod
=head1 NAME
notes-links - work with links within my notes
=head1 SYNOPSIS
Currently this is vimwiki-dependent.
# List pages linking to foo:
notes links --target foo
# Pages linking to foo.wiki:
notes links --file ./foo.wiki
# Pages with a date (i.e. logs and diaries) linking to foo:
notes links --with-date --target foo
# Links in foo (experimental, probably broken):
notes links --source foo
# Format (defaults to location):
notes links --format target
=head1 OUTPUT FORMATS
B<--format> options include C<vimwiki>, C<tsv>, C<location>, C<target>,
C<full>, and C<name>.
Formats may be glitchy.
=head1 AUTHOR
Brennen Bearnes
=cut
use warnings;
use strict;
use 5.10.0;
use Cwd;
use Data::Dumper;
use DBI;
use File::Basename;
use File::Spec;
use Getopt::Long;
use Pod::Usage;
use SQL::Abstract;
use Sys::Hostname;
# Display formats:
my %FORMATS = (
vimwiki => sub {
my ($data) = @_;
my $timestamp = '';
if (defined $data->{timestamp}) {
$timestamp = $data->{timestamp} . " ";
}
return ' - '
. $timestamp
. '[[/' . $data->{page} . '|'
. $data->{title}
. "]]\n";
},
tsv => sub {
my ($data) = @_;
my $timestamp = '(null)';
if (defined $data->{timestamp}) {
$timestamp = $data->{timestamp};
}
return (join "\t", ($timestamp, $data->{page}, $data->{title})) . "\n";
},
# vim location list
#
# TODO: Note that the "1" here is, theoretically, a line number.
# Unfortunately, there's no obvious way to extract that from Pandoc's parsing
# of the original file, and thus no non-hacky way for notes-collect-metadata to
# store an approximate value in the database. We _could_ read the file
# line-by-line, or run grep, looking for the file's basename, but this is
# tricky to get right.
#
# If I keep using this long-term, I should solve this problem.
location => sub {
my ($data) = @_;
my $result = "$ENV{HOME}/notes/vimwiki/$data->{page}.wiki";
if ($data->{title}) {
$result .= ":1:$data->{title}";
} else {
$result .= print ":1:$data->{page}"
}
return $result . "\n";
},
# List of bare link _target_ names
target => sub {
my ($data) = @_;
return $data->{target} . "\n";
},
# Fulltext of vimwiki pages:
full => sub {
my ($data) = @_;
my $result = '';
my $pagepath = "$ENV{HOME}/notes/vimwiki/$data->{page}.wiki";
$result .= "%% $pagepath {{{\n\n";
$result .= file_get_contents($pagepath);
$result .= "\n%% }}}\n\n";
return $result;
},
# Bare names of wiki pages (no extension):
name => sub {
my ($data) = @_;
return $data->{page} . "\n";
},
);
# Handle options, including help generated from the POD above.
my $debug = 0;
my $with_date = 0;
my $target;
my $source;
my $file;
my $format = 'location';
GetOptions(
'debug' => \$debug,
'target=s' => \$target,
'file=s' => \$file,
'format=s' => \$format,
'with-date' => \$with_date,
'help' => sub { pod2usage(0) },
) or pod2usage(2);
if (defined $file)
{
my ($name, $path, $suffix) = fileparse($file, '.wiki');
# XXX: This is a dirty fucking hack - get rid of the leading path stuff, but
# keep subdirs within the vimwiki directory:
$path =~ s{^.*?notes/vimwiki/}{};
$target = $path . $name;
}
my %where = ();
# Require that the link point to a given page:
if (defined $target) {
$where{target} = $target;
}
# Get only links from pages that have a datetime:
if ($with_date) {
# NOT NULL:
$where{'pages.datetime'} = { '!=', undef };
}
my $dbfile = $ENV{HOME} . "/notes/metadata.db";
my $dbh = DBI->connect("dbi:SQLite:dbname=$dbfile", "", "");
my $sql = SQL::Abstract->new;
my ($query, @bind) = $sql->select(
'links JOIN pages ON links.page = pages.page',
# timestamp will be null if datetime is
"*, DATETIME(datetime, 'localtime') AS timestamp",
\%where,
{-desc => [ 'datetime', 'page' ] }
);
if ($debug) {
say STDERR $query;
say STDERR Dumper(%where);
}
my $sth = $dbh->prepare($query);
$sth->execute(@bind);
while (my $data = $sth->fetchrow_hashref())
{
if ($debug) {
print STDERR Dumper($data);
}
print $FORMATS{$format}->($data);
}
$sth->execute();
# PHP-style file-content grabbing:
sub file_get_contents {
my ($file) = @_;
open my $fh, '<', $file
or die "Couldn't open $file: $!\n";
my $contents;
{
# line separator:
local $/ = undef;
$contents = <$fh>;
}
close $fh or die "Couldn't close $file: $!";
return $contents;
}
1;