#!/usr/bin/env perl
#
# Output as Format: read from stdin and output it as code in the given format
# https://github.com/sshaw/output-as-format
#
# Based off copy-as-format for Emacs: https://github.com/sshaw/copy-as-format
#
# Copyright (c) 2017-2019 Skye Shaw
#
# This library is free software; you can redistribute it and/or modify
# it under the same terms as Perl itself.
#
use strict;
use warnings;
package Util;
sub html_escape {
my $text = shift;
my %escapes = ('&' => '&', '<' => '<');
$$text =~ s/[&<]/$escapes{$&}/eg;
}
sub indent {
my ($text, $n) = @_;
$$text =~ s/^/' ' x $n/gme;
}
# Should just do Markdown->quote, Markdown->list, etc...
package Quote;
sub html {
my $lines = shift;
print '
',"\n";
for (@$lines) {
Util::html_escape(\$_);
print $_,"\n";
}
print '
',"\n";
}
sub jira {
my ($lines, $multiline) = @_;
if ($multiline) {
print '{quote}',"\n";
printf "%s\n", $_ for @$lines;
print '{quote}',"\n";
}
else {
printf "bq. %s\n", join ' ', @$lines
}
}
sub orgmode {
my $lines = shift;
print "#+BEGIN_QUOTE\n";
print $_,"\n" for @$lines;
print "#+END_QUOTE\n";
}
sub markdown {
my $lines = shift;
printf "> %s\n", $_ for @$lines;
}
sub output {
shift;
my ($format, $multiline) = @_;
my $lines = [];
die "unsupported quote format: $format\n" unless $Quote::{$format};
while () {
chomp;
s/^\s*//g;
push @$lines, $_;
}
$multiline = @$lines > 1 ? 1 : 0 unless defined($multiline);
$Quote::{$format}->($lines, $multiline);
}
$Quote::{$_} = *markdown for qw(github slack);
package List;
sub html {
my ($lines, $numbered) = @_;
my $li = sub {
for (@$lines) {
Util::html_escape(\$_);
printf '%s', $_;
}
};
if ($numbered) {
print '';
$li->();
print '
';
}
else {
print '';
}
}
# mediawiki too
sub markdown {
my ($lines, $numbered) = @_;
my $prefix = $numbered ? '1.' : '*';
printf "%s %s\n", $prefix, $_ for @$lines;
}
sub jira {
my ($lines, $numbered) = @_;
my $prefix = $numbered ? '#' : '*';
printf "%s %s\n", $prefix, $_ for @$lines;
}
sub orgmode {
my ($lines, $numbered) = @_;
my $n = 1;
printf "%s %s\n", $numbered ? $n++ . '.' : '-', $_ for @$lines;
}
sub rst {
# Bullet lists are the same as Markdown
return markdown(shift) unless pop;
my $lines = shift;
printf "#. %s\n", $_ for @$lines;
}
sub pod {
# Numbered
my $lines = shift;
print '=over 2',"\n";
printf "=item %s\n" for @$lines;
print '=back';
}
sub output {
shift;
my ($format, $numbered) = @_;
my $lines = [];
die "unsupported list format: $format\n" unless $List::{$format};
while () {
chomp;
s/^\s*//g;
push @$lines, $_;
}
$List::{$format}->($lines, $numbered);
}
$List::{$_} = *markdown for qw(github slack);
package Code;
sub output {
shift;
my ($format, $lang, $multiline) = @_;
my @indents;
my $snippet = '';
while () {
s/\t/' ' x 8/eg;
$snippet .= $_;
push @indents, /^(\s*)/ && length $1;
}
exit 1 unless @indents;
# Remove the minimum amount of leading white space
my $trim = (sort { $a <=> $b } @indents)[0];
$snippet =~ s/^\s{$trim}//mg;
# Remove newline for single line input
chomp $snippet if @indents == 1;
$multiline = @indents > 1 ? 1 : 0 unless defined($multiline);
$Code::{$format}->($snippet, $multiline, $lang);
}
sub disqus {
my ($snippet, undef, $lang) = @_;
Util::html_escape(\$snippet);
printf "\n%s
\n", $lang || '', $snippet;
}
sub github {
my ($snippet, $multiline, $lang) = @_;
if ($multiline) {
printf "```%s\n%s```\n", $lang || '', $snippet;
} else {
printf '`%s`', $snippet;
}
}
sub hipchat {
my $snippet = shift;
printf '/code %s', $snippet;
}
sub html {
my ($snippet, $multiline, $lang) = @_;
Util::html_escape(\$snippet);
if ($multiline) {
printf "\n%s
", $snippet;
}
else {
printf '%s', $snippet;
}
}
sub jira {
my ($snippet, $multiline, $lang) = @_;
if ($multiline) {
printf "{code%s}\n%s{code}\n", $lang ? ":$lang" : '', $snippet;
}
else {
printf '{{%s}}', $snippet;
}
}
sub markdown {
my ($snippet, $multiline) = @_;
if ($multiline) {
Util::indent(\$snippet, 4);
print $snippet;
}
else {
printf '`%s`', $snippet;
}
}
sub mediawiki {
my ($snippet, $multiline, $lang) = @_;
my $markup = sprintf '\n%s", $snippet;
print $markup;
}
sub orgmode {
my ($snippet, $multiline, $lang) = @_;
if ($multiline) {
printf "#+BEGIN_SRC %s\n%s\n#+END_SRC\n", $lang || '', $snippet;
}
else {
# Cannot escape '~' within Org-mode's code formatting
# entities and empty space don't work.
printf '~%s~', $snippet;
}
}
sub pod {
my ($snippet, $multiline, $lang) = @_;
if ($multiline) {
Util::indent(\$snippet, 2);
print $snippet;
}
else {
$snippet =~ s/<E/g;
printf 'C<< %s >>', $snippet;
}
}
sub rst {
my ($snippet, $multiline, $lang) = @_;
if ($multiline) {
Util::indent(\$snippet, 4);
printf ".. code::\n\n%s\n", $snippet;
}
else {
printf '``%s``', $snippet;
}
}
sub slack {
my ($snippet, $multiline) = @_;
if ($multiline) {
printf "```\n%s```\n", $snippet;
} else {
$snippet =~ s/^\s+|\s+$//g;
printf '`%s`', $snippet;
}
}
package main;
use Getopt::Long qw(:config no_ignore_case);
our $VERSION = '0.04';
sub HELP_MESSAGE {
my $out = shift;
print $out < 'github',
disqus => 'disqus',
github => 'github',
gitlab => 'github',
hipchat => 'hipchat',
html => 'html',
jira => 'jira',
markdown => 'markdown',
mediawiki => 'mediawiki',
orgmode => 'orgmode',
pod => 'pod',
rst => 'rst',
slack => 'slack');
GetOptions('f=s' => \$options{f},
'h|help' => sub { HELP_MESSAGE(*STDOUT) and exit },
'l=s' => \$options{l},
'list|L' => \$options{list},
'list1|1' => \$options{list1},
'm' => \$options{m},
'p' => \$options{p},
'quote|Q' => \$options{quote},
's' => \$options{s},
'v|version' => sub { VERSION_MESSAGE(*STDOUT) and exit }
) or HELP_MESSAGE(*STDERR) and exit 1;
if ($options{p}) {
local $\= "\n";
print for sort keys %formats;
exit;
}
my $lang = $options{l} || $ENV{OAF_LANG};
my $multiline = $options{m};
$multiline = 0 if $options{s};
# FIXME: use GetOptions for this..?
my $format = $options{f} || $ENV{OAF_FORMAT} || 'markdown';
die "Unknown format: $format\n" unless $formats{$format};
# ======
if ($options{list} || $options{list1}) {
my $numbered = $options{list1} || 0;
List->output($formats{$format}, $numbered);
}
elsif ($options{quote}) {
Quote->output($formats{$format}, $multiline);
}
else {
Code->output($formats{$format}, $lang, $multiline);
}