| 6 | | # use XXX; |
|---|
| 7 | | # use diagnostics; |
|---|
| 8 | | |
|---|
| 9 | | our $VERSION = '0.10'; |
|---|
| 10 | | |
|---|
| 11 | | use IO::All; |
|---|
| 12 | | use YAML::XS; |
|---|
| 13 | | use Class::Field 'field', 'const'; |
|---|
| 14 | | use Getopt::Long; |
|---|
| 15 | | use Carp; |
|---|
| 16 | | |
|---|
| 17 | | field input => 'slides.vroom'; |
|---|
| 18 | | field stream => ''; |
|---|
| 19 | | field ext => ''; |
|---|
| 20 | | field clean => 0; |
|---|
| 21 | | field vroom => 0; |
|---|
| 22 | | field digits => 0; |
|---|
| 23 | | field config => { |
|---|
| 24 | | title => 'Unnamed Presentation', |
|---|
| 25 | | height => 24, |
|---|
| 26 | | width => 80, |
|---|
| 27 | | list_indent => 10, |
|---|
| 28 | | }; |
|---|
| 29 | | |
|---|
| 30 | | sub new { |
|---|
| 31 | | return bless {}, shift; |
|---|
| 32 | | } |
|---|
| 33 | | |
|---|
| 34 | | sub run { |
|---|
| 35 | | my $self = shift; |
|---|
| 36 | | |
|---|
| 37 | | local @ARGV = @_; |
|---|
| 38 | | |
|---|
| 39 | | $self->getOptions; |
|---|
| 40 | | |
|---|
| 41 | | $self->cleanUp; |
|---|
| 42 | | return if $self->clean; |
|---|
| 43 | | |
|---|
| 44 | | $self->makeAll; |
|---|
| 45 | | |
|---|
| 46 | | $self->startUp if $self->vroom; |
|---|
| 47 | | } |
|---|
| 48 | | |
|---|
| 49 | | sub getOptions { |
|---|
| 50 | | my $self = shift; |
|---|
| 51 | | GetOptions( |
|---|
| 52 | | "clean" => \$self->{clean}, |
|---|
| 53 | | "input=s" => \$self->{input}, |
|---|
| 54 | | "vroom" => \$self->{vroom}, |
|---|
| 55 | | ) or die $self->usage; |
|---|
| 56 | | |
|---|
| 57 | | do { delete $self->{$_} unless defined $self->{$_} } |
|---|
| 58 | | for qw(clean input vroom); |
|---|
| 59 | | |
|---|
| 60 | | my @files = <*.vroom>; |
|---|
| 61 | | |
|---|
| 62 | | $self->input($files[0]) |
|---|
| 63 | | if @files; |
|---|
| 64 | | } |
|---|
| 65 | | |
|---|
| 66 | | sub cleanUp { |
|---|
| 67 | | unlink(glob "0*"); |
|---|
| 68 | | unlink(".vimrc"); |
|---|
| 69 | | } |
|---|
| 70 | | |
|---|
| 71 | | sub makeAll { |
|---|
| 72 | | my $self = shift; |
|---|
| 73 | | $self->getInput; |
|---|
| 74 | | $self->buildSlides; |
|---|
| 75 | | $self->writeVimrc; |
|---|
| 76 | | } |
|---|
| 77 | | |
|---|
| 78 | | sub getInput { |
|---|
| 79 | | my $self = shift; |
|---|
| 80 | | my $stream = io($self->input)->all |
|---|
| 81 | | or croak "No input provided. Make a file called 'slides.vroom'"; |
|---|
| 82 | | $self->stream($stream); |
|---|
| 83 | | } |
|---|
| 84 | | |
|---|
| 85 | | sub buildSlides { |
|---|
| 86 | | my $self = shift; |
|---|
| 87 | | my @split = grep length, split /^(----\ *.*)\n/m, $self->stream; |
|---|
| 88 | | push @split, '----' if $split[0] =~ /\n/; |
|---|
| 89 | | my (@raw_configs, @raw_slides); |
|---|
| 90 | | while (@split) { |
|---|
| 91 | | my ($config, $slide) = splice(@split, 0, 2); |
|---|
| 92 | | $config =~ s/^----\s*(.*?)\s*$/$1/; |
|---|
| 93 | | push @raw_configs, $config; |
|---|
| 94 | | push @raw_slides, $slide; |
|---|
| 95 | | } |
|---|
| 96 | | $self->{digits} = int(log(@raw_slides)/log(10)) + 2; |
|---|
| 97 | | |
|---|
| 98 | | my $number = 1; |
|---|
| 99 | | |
|---|
| 100 | | for my $raw_slide (@raw_slides) { |
|---|
| 101 | | my $config = $self->parseSlideConfig(shift @raw_configs); |
|---|
| 102 | | next if $config->{skip}; |
|---|
| 103 | | |
|---|
| 104 | | $raw_slide = $self->applyOptions($raw_slide, $config) |
|---|
| 105 | | or next; |
|---|
| 106 | | |
|---|
| 107 | | $raw_slide = $self->padVertical($raw_slide); |
|---|
| 108 | | |
|---|
| 109 | | my @slides; |
|---|
| 110 | | my $slide = ''; |
|---|
| 111 | | for (split /^\+/m, $raw_slide) { |
|---|
| 112 | | $slide .= $_; |
|---|
| 113 | | push @slides, $slide; |
|---|
| 114 | | } |
|---|
| 115 | | |
|---|
| 116 | | my $base_name = $self->formatNumber($number++); |
|---|
| 117 | | |
|---|
| 118 | | my $suffix = 'a'; |
|---|
| 119 | | for (my $i = 1; $i <= @slides; $i++) { |
|---|
| 120 | | my $slide = $self->padFullScreen($slides[$i - 1]); |
|---|
| 121 | | $slide =~ s{^\ *==\ *(.*?)\ *$} |
|---|
| 122 | | {' ' x (($self->config->{width} - length($1)) / 2) . $1}gem; |
|---|
| 123 | | my $suf = $suffix++; |
|---|
| 124 | | $suf = $suf eq 'a' |
|---|
| 125 | | ? '' |
|---|
| 126 | | : $i == @slides |
|---|
| 127 | | ? 'z' |
|---|
| 128 | | : $suf; |
|---|
| 129 | | io("$base_name$suf" . $self->ext)->print($slide); |
|---|
| 130 | | } |
|---|
| 131 | | } |
|---|
| 132 | | } |
|---|
| 133 | | |
|---|
| 134 | | sub formatNumber { |
|---|
| 135 | | my $self = shift; |
|---|
| 136 | | my $number = shift; |
|---|
| 137 | | my $digits = $self->digits; |
|---|
| 138 | | return sprintf "%0${digits}d", $number; |
|---|
| 139 | | } |
|---|
| 140 | | |
|---|
| 141 | | sub parseSlideConfig { |
|---|
| 142 | | my $self = shift; |
|---|
| 143 | | my $string = shift; |
|---|
| 144 | | my $config = {}; |
|---|
| 145 | | for my $option (split /\s*,\s*/, $string) { |
|---|
| 146 | | $config->{$1} = 1 |
|---|
| 147 | | if $option =~ /^(config|skip|center|perl|yaml)$/; |
|---|
| 148 | | $config->{indent} = $1 |
|---|
| 149 | | if $option =~ /i(\d+)/; |
|---|
| 150 | | } |
|---|
| 151 | | return $config; |
|---|
| 152 | | } |
|---|
| 153 | | |
|---|
| 154 | | sub applyOptions { |
|---|
| 155 | | my $self = shift; |
|---|
| 156 | | my ($slide, $config) = @_; |
|---|
| 157 | | |
|---|
| 158 | | $config = { |
|---|
| 159 | | %{$self->config}, |
|---|
| 160 | | %$config, |
|---|
| 161 | | }; |
|---|
| 162 | | |
|---|
| 163 | | $self->ext(""); |
|---|
| 164 | | if ($config->{config}) { |
|---|
| 165 | | $config = { |
|---|
| 166 | | %{$self->config}, |
|---|
| 167 | | %{(YAML::XS::Load($slide))}, |
|---|
| 168 | | }; |
|---|
| 169 | | $self->config($config); |
|---|
| 170 | | return ''; |
|---|
| 171 | | } |
|---|
| 172 | | if ($config->{center}) { |
|---|
| 173 | | $slide =~ s{^(\+?)\ *(.*?)\ *$} |
|---|
| 174 | | {$1 . ' ' x (($self->config->{width} - length($2)) / 2) . $2}gem; |
|---|
| 175 | | $slide =~ s{^\s*$}{}gm; |
|---|
| 176 | | } |
|---|
| 177 | | if (defined $config->{indent}) { |
|---|
| 178 | | my $indent = $config->{indent}; |
|---|
| 179 | | $slide =~ s{^(\+?)}{$1 . ' ' x $indent}gem; |
|---|
| 180 | | } |
|---|
| 181 | | elsif ($slide =~ /^\+?\*/m) { |
|---|
| 182 | | my $indent = $config->{list_indent}; |
|---|
| 183 | | $slide =~ s{^(\+?)}{$1 . ' ' x $indent}gem; |
|---|
| 184 | | } |
|---|
| 185 | | if ($config->{perl}) { |
|---|
| 186 | | $self->ext(".pl"); |
|---|
| 187 | | } |
|---|
| 188 | | if ($config->{yaml}) { |
|---|
| 189 | | $self->ext(".yaml"); |
|---|
| 190 | | } |
|---|
| 191 | | return $slide; |
|---|
| 192 | | } |
|---|
| 193 | | |
|---|
| 194 | | sub padVertical { |
|---|
| 195 | | my $self = shift; |
|---|
| 196 | | my $slide = shift; |
|---|
| 197 | | $slide =~ s/\A\s*\n//; |
|---|
| 198 | | $slide =~ s/\n\s*\z//; |
|---|
| 199 | | my @lines = split /\n/, $slide; |
|---|
| 200 | | my $lines = @lines; |
|---|
| 201 | | my $before = int(($self->config->{height} - $lines) / 2) - 1; |
|---|
| 202 | | return "\n" x $before . $slide; |
|---|
| 203 | | } |
|---|
| 204 | | |
|---|
| 205 | | sub padFullScreen { |
|---|
| 206 | | my $self = shift; |
|---|
| 207 | | my $slide = shift; |
|---|
| 208 | | chomp $slide; |
|---|
| 209 | | my @lines = split /\n/, $slide; |
|---|
| 210 | | my $lines = @lines; |
|---|
| 211 | | my $after = $self->config->{height} - $lines + 1; |
|---|
| 212 | | return $slide . "\n" x $after; |
|---|
| 213 | | } |
|---|
| 214 | | |
|---|
| 215 | | sub writeVimrc { |
|---|
| 216 | | my $self = shift; |
|---|
| 217 | | my $title = "%f " . $self->config->{title}; |
|---|
| 218 | | $title =~ s/\s/_/g; |
|---|
| 219 | | io(".vimrc")->print(<<"..."); |
|---|
| 220 | | map <SPACE> :n<CR>:<CR>gg |
|---|
| 221 | | map <BACKSPACE> :N<CR>:<CR>gg |
|---|
| 222 | | map R :!perl %<CR> |
|---|
| 223 | | map Q :q!<CR> |
|---|
| 224 | | set laststatus=2 |
|---|
| 225 | | set statusline=$title |
|---|
| 226 | | ... |
|---|
| 227 | | } |
|---|
| 228 | | |
|---|
| 229 | | sub startUp { |
|---|
| 230 | | exec "vim 0*"; |
|---|
| 231 | | } |
|---|
| 232 | | |
|---|
| 239 | | > vim slides.vroom # Write Some Slides |
|---|
| 240 | | > vroom --vroom # Show Your Slides |
|---|
| 241 | | |
|---|
| 242 | | =head1 DESCRIPTION |
|---|
| 243 | | |
|---|
| 244 | | Ever given a Slide Show and needed to switch over to the shell? |
|---|
| 245 | | |
|---|
| 246 | | Now you don't ever have to switch again. You're already there. |
|---|
| 247 | | |
|---|
| 248 | | Vroom lets you create your slides in a single file using a Wiki-like |
|---|
| 249 | | style, much like Spork and Sporx do. The difference is that your slides |
|---|
| 250 | | don't compile to HTML or JavaScript or XUL. They get turned into a set |
|---|
| 251 | | of files in a directory called C<slides>. |
|---|
| 252 | | |
|---|
| 253 | | The slides are named in alpha order. That means you can bring them all |
|---|
| 254 | | into a Vim session with the command: C<vim *>. |
|---|
| 255 | | |
|---|
| 256 | | Vroom creates a file called C<slides/.vimrc> with many helpful key mappings |
|---|
| 257 | | for navigating a slideshow. See L<KEY MAPPINGS> below. |
|---|
| 258 | | |
|---|
| 259 | | Vroom takes advantage of Vim's syntax highlighting. It also lets you run |
|---|
| 260 | | slides that contain code. |
|---|
| 261 | | |
|---|
| 262 | | Since Vim is an editor, you can change your slides during the show. |
|---|
| 263 | | |
|---|
| 264 | | =head1 COMMAND USAGE |
|---|
| 265 | | |
|---|
| 266 | | Vroom has a few command line options: |
|---|
| 267 | | |
|---|
| 268 | | =over |
|---|
| 269 | | |
|---|
| 270 | | =item vroom |
|---|
| 271 | | |
|---|
| 272 | | Just running vroom will compiles 'slides.vroom' into slide files. |
|---|
| 273 | | |
|---|
| 274 | | =item vroom --vroom |
|---|
| 275 | | |
|---|
| 276 | | Compile and start vim show. |
|---|
| 277 | | |
|---|
| 278 | | =item vroom --clean |
|---|
| 279 | | |
|---|
| 280 | | Clean up all the compiled output files. |
|---|
| 281 | | |
|---|
| 282 | | =back |
|---|
| 283 | | |
|---|
| 284 | | =head1 INPUT FORMAT |
|---|
| 285 | | |
|---|
| 286 | | Here is an example slides.vroom: |
|---|
| 287 | | |
|---|
| 288 | | ---- config |
|---|
| 289 | | title: My Spiffy Slideshow |
|---|
| 290 | | height: 84 |
|---|
| 291 | | width: 20 |
|---|
| 292 | | ---- center |
|---|
| 293 | | My Presentation |
|---|
| 294 | | |
|---|
| 295 | | by Ingy |
|---|
| 296 | | ---- |
|---|
| 297 | | == Stuff I care about: |
|---|
| 298 | | |
|---|
| 299 | | * Foo |
|---|
| 300 | | +* Bar |
|---|
| 301 | | +* Baz |
|---|
| 302 | | ---- perl |
|---|
| 303 | | use Vroom; |
|---|
| 304 | | |
|---|
| 305 | | print "Hello World"; |
|---|
| 306 | | ---- center |
|---|
| 307 | | THE END |
|---|
| 308 | | |
|---|
| 309 | | =head1 KEY MAPPINGS |
|---|
| 310 | | |
|---|
| 311 | | =over |
|---|
| 312 | | |
|---|
| 313 | | =item <SPACE> |
|---|
| 314 | | |
|---|
| 315 | | Advance one slide |
|---|
| 316 | | |
|---|
| 317 | | =item <BACKSPACE> |
|---|
| 318 | | |
|---|
| 319 | | Go back one slide |
|---|
| 320 | | |
|---|
| 321 | | =item <R> |
|---|
| 322 | | |
|---|
| 323 | | Run current slide as Perl |
|---|
| 324 | | |
|---|
| 325 | | =item <Q> |
|---|
| 326 | | |
|---|
| 327 | | Quit Vroom |
|---|
| 328 | | |
|---|
| 329 | | =back |
|---|
| 330 | | |
|---|
| 331 | | =head1 AUTHOR |
|---|
| 332 | | |
|---|
| 333 | | Ingy döt Net <ingy@cpan.org> |
|---|
| 334 | | |
|---|
| 335 | | =head1 COPYRIGHT |
|---|
| 336 | | |
|---|
| 337 | | Copyright (c) 2008. Ingy döt Net. |
|---|
| 338 | | |
|---|
| 339 | | This program is free software; you can redistribute it and/or modify it |
|---|
| 340 | | under the same terms as Perl itself. |
|---|
| 341 | | |
|---|
| 342 | | See http://www.perl.com/perl/misc/Artistic.html |
|---|
| | 12 | Vroom was moved to Vroom::Vroom because the CPAN shell thought this |
|---|
| | 13 | module was Tim Vroom, who is currently uninstallable. |
|---|