Add vimoutliner v0.4.0

This commit is contained in:
Anthony Rose 2019-06-19 13:42:02 +01:00
parent 4efe31ffd7
commit 855198fe1f
106 changed files with 19741 additions and 0 deletions

16
pack/acp/start/vimoutliner/.gitignore vendored Normal file
View file

@ -0,0 +1,16 @@
# Vim
*.sw?
*.un?
tags
# archives
*.zip
*.gz
*.gzip
# osx noise
.DS_Store
# Python
*.pyc
*~

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,137 @@
Installing and Testing VimOutliner
Automatic Method |vo-auto-install|
Updating |vo-updating|
Manual Method |vo-manual-install|
Testing |vo-testing|
Automatic Method
The automatic installation targets Unix-compatible platforms:
From tar archive
$ tar xzvf vimoutliner-0.3.x.tar.gz
$ cd vimoutliner
$ sh install.sh
From zip archive
$ unzip vimoutliner-0.3.x.zip
$ cd vimoutliner-0.3.x
$ sh install.sh
Installing with pathogen
If you have pathogen installed as your package manager you can clone the git repository in you
pathogen directory:
$ cd ~/.vim/bundle && git clone https://github.com/vimoutliner/vimoutliner.git
Installing using install.sh:
First you can decide whether to install the VimOutliner files or abort the
process leaving everything unchanged. Assuming you confirmed the
installation, the script creates the necessary directory tree and copies the
files which provide the core functionality and documentation.
With the second question you decide whether you want to install some brand new
add-ons, currently implementing checkboxes(enabled), hoisting(disabled)
and smart paste(disabled), as well as several useful external scripts in the vimoutliner/scripts folder.
Updating
Updating an existing installation might require some manual work.
If you are already working with a previous VimOutliner release, there is a
slight chance that the current directory tree is different from your current
one. In this case, you will have to manually migrate your files to the new
locations.
The installation script creates unique backups of files being replaced with
newer versions. So if you put some local customisations into, say
$HOME/.vim/vimoutliner/vimoutlinerrc, you'll probably have to merge the backup
with the new file by hand.
Manual Method
You can also copy the files from the unpacked distribution tarball into their
destination folders by yourself. The following steps are a description of what
has to go where and assume some knowledge of your vim setup.
If you encounter problems, please contact the mailing list for an immediate
solution and more complete future documentation. www.lists.vimoutliner.org
If you want to setup VimOutliner on a system running Microsoft Windows, the
directory $HOME denotes the base folder of the vim installation. If you're on
Unix based system, $HOME is as usual.
You need the following sub trees in your $HOME directory:
$HOME/.vim/
doc/
ftdetect/
ftplugin/
syntax/
vimoutliner/
plugins/
scripts/
The distribution tarball unpacks into a directory vimoutliner with the
following contents:
vimoutliner/
plugins/ (1)
scripts/ (1)
doc/ (1)
ftdetect/ (1)
ftplugin/ (1)
install.sh*
syntax/ (1)
syntax/ (1)
vimoutlinerrc (1)
(1) The content of these folders should be copied to their namesakes in the
$HOME/.vim folder
Your $HOME/.vimrc file should contain the lines
filetype plugin indent on
syntax on
Finally, you need to integrate the online help provided with VimOutliner into
the vim help system. Start vim and execute the following command:
:helptags $HOME/.vim/doc
At this point, VimOutliner should be functional. Type ":help vo" to get
started. To get a quick overview of all VimOutliner commands you can view the
cheatsheet with ":help votl_cheatsheet".
Testing Base Functionality
Create a text file and save it as a .otl file like vo_test.otl. Enter
some text and verify the following:
- Tabs indent the text
- Different indent levels are different colors
- Lines starting with a colon and space word-wrap
Lines starting with colons are body text. They should word wrap and
should be a special color (typically green, but it can vary). Verify
that paragraphs of body text can be reformatted with the Vim gq
commands.
You can test the different VimOutliner commands by looking at the examples
in the cheatsheet. "help votl_cheatsheet".
Debian Installation
Debian does include VimOutliner as a package. However some Debian versions
require this line to be added to your .vimrc file:
syntax on

View file

@ -0,0 +1,11 @@
LICENSE
VimOutliner
Copyright (C) 2001, 2003 by Steve Litt
Copyright (C) 2004 by Noel Henson
Licensed under the GNU General Public License (GPL), version 3
Absolutely no warranty.
If your distro doesn't come with the GPL file you can find it here:
HTML: http://www.gnu.org/copyleft/gpl.html
Text: http://www.gnu.org/copyleft/gpl.txt

View file

@ -0,0 +1,81 @@
VimOutliner README file
Introduction
VimOutliner is an outline processor with many of the same features
as Grandview, More, Thinktank, Ecco, etc. Features include tree
expand/collapse, tree promotion/demotion, level sensitive colors,
interoutline linking, and body text.
What sets VimOutliner apart from the rest is that it's been constructed
from the ground up for fast and easy authoring. Keystrokes are quick and
easy, especially for someone knowing the Vim editor. VimOutliner can be
used without the mouse (but is supported to the extent that Vim supports
the mouse).
Many of the VimOutliner commands start with a double comma ",," because
that's very quick to type. All VimOutliner files have the ".otl"
extension. For help on VimOutliner type ":h vo". For an overview of
all the most important VimOutliner commands you can type ":h votl_cheatsheet"
when you have opened an otl file.
* Usage
VimOutliner has been reported to help with the following tasks:
- Project management
- Password wallet
- To-do lists
- Account and cash book
- 'Plot device' for writing novels
- Inventory control
- Hierarchical database
- Web site management
* Characteristics
- Fast and effective
- Fully integrated with Vim
- Extensible through plugins
- Many post-processing scripts allow exporting to multiple formats
- Extensive documentation
For more information on the use of VimOutliner see the README.detailed
file in this directory.
Installation details
1. Zip file
To install from the zip file do the following (exchange "x" with
version number from the package you have downloaded):
$ unzip vimoutliner-0.3.x.zip $ cd vimoutliner-0.3.x $ sh install.sh
2. Gzip archive
To install from the gzip archive follow these steps:
$ tar -xzvf vimoutliner-0.3.x.tgz $ cd vimoutliner-0.3.x $ sh install.sh
Downloads
zip archives are available here:
https://github.com/vimoutliner/vimoutliner/downloads
Download of all packages can also be done from the Freshmeat site:
http://freecode.com/projects/vimoutliner
For more detailed installation instructions see the INSTALL file in
this directory.

View file

@ -0,0 +1 @@
doc/votl.txt

View file

@ -0,0 +1,27 @@
TODO LIST
Updated: 20140916
:This is just a preliminary roadmap, use the issue list at
:https://github.com/vimoutliner/vimoutliner/issues for suggesting new
:features and register issues or bugs
Work planned for next version:
Cleanup code in plugins to more efficient vim script
Make plug mappings for all commands
Decide on common script language, and write scripts in new language
Maybe/probably:
Include a list of vim plugins that are useful with VimOutliner
Templates
More sort functionality
Wiki plugin and documentation
Documentation on how to convert VO files to mindmap programs, Grapviz and Lyx
More colorschemes
vimoutliner-tutor
Documentation on how to use VO for writing
MetaData?
Done:
Remove included RCS,logs etc.

View file

@ -0,0 +1,81 @@
let g:colors_name="VO Dark"
hi normal guifg=white guibg=black ctermfg=gray ctermbg=black
hi StatusLine guifg=white guibg=black ctermfg=gray ctermbg=black
hi StatusLineNC guifg=white guibg=black ctermfg=gray ctermbg=black
hi VertSplit guifg=white guibg=black ctermfg=gray ctermbg=black
hi OL1 guifg=white ctermfg=gray
hi OL2 guifg=red ctermfg=darkred
hi OL3 guifg=lightblue ctermfg=darkblue
hi OL4 guifg=violet ctermfg=darkmagenta
hi OL5 guifg=white ctermfg=gray
hi OL6 guifg=red ctermfg=darkred
hi OL7 guifg=lightblue ctermfg=darkblue
hi OL8 guifg=violet ctermfg=darkmagenta
hi OL9 guifg=white ctermfg=gray
" colors for tags
hi outlTags guifg=darkred ctermfg=darkred
" color for body text
hi BT1 guifg=green ctermfg=darkgreen
hi BT2 guifg=green ctermfg=darkgreen
hi BT3 guifg=green ctermfg=darkgreen
hi BT4 guifg=green ctermfg=darkgreen
hi BT5 guifg=green ctermfg=darkgreen
hi BT6 guifg=green ctermfg=darkgreen
hi BT7 guifg=green ctermfg=darkgreen
hi BT8 guifg=green ctermfg=darkgreen
hi BT9 guifg=green ctermfg=darkgreen
" color for pre-formatted text
hi PT1 guifg=blue ctermfg=darkcyan
hi PT2 guifg=blue ctermfg=darkcyan
hi PT3 guifg=blue ctermfg=darkcyan
hi PT4 guifg=blue ctermfg=darkcyan
hi PT5 guifg=blue ctermfg=darkcyan
hi PT6 guifg=blue ctermfg=darkcyan
hi PT7 guifg=blue ctermfg=darkcyan
hi PT8 guifg=blue ctermfg=darkcyan
hi PT9 guifg=blue ctermfg=darkcyan
" color for tables
hi TA1 guifg=darkviolet ctermfg=darkcyan
hi TA2 guifg=darkviolet ctermfg=darkcyan
hi TA3 guifg=darkviolet ctermfg=darkcyan
hi TA4 guifg=darkviolet ctermfg=darkcyan
hi TA5 guifg=darkviolet ctermfg=darkcyan
hi TA6 guifg=darkviolet ctermfg=darkcyan
hi TA7 guifg=darkviolet ctermfg=darkcyan
hi TA8 guifg=darkviolet ctermfg=darkcyan
hi TA9 guifg=darkviolet ctermfg=darkcyan
" color for user text (wrapping)
hi UT1 guifg=darkred ctermfg=darkcyan
hi UT2 guifg=darkred ctermfg=darkcyan
hi UT3 guifg=darkred ctermfg=darkcyan
hi UT4 guifg=darkred ctermfg=darkcyan
hi UT5 guifg=darkred ctermfg=darkcyan
hi UT6 guifg=darkred ctermfg=darkcyan
hi UT7 guifg=darkred ctermfg=darkcyan
hi UT8 guifg=darkred ctermfg=darkcyan
hi UT9 guifg=darkred ctermfg=darkcyan
" color for user text (non-wrapping)
hi UB1 guifg=darkgray ctermfg=darkcyan
hi UB2 guifg=darkgray ctermfg=darkcyan
hi UB3 guifg=darkgray ctermfg=darkcyan
hi UB4 guifg=darkgray ctermfg=darkcyan
hi UB5 guifg=darkgray ctermfg=darkcyan
hi UB6 guifg=darkgray ctermfg=darkcyan
hi UB7 guifg=darkgray ctermfg=darkcyan
hi UB8 guifg=darkgray ctermfg=darkcyan
hi UB9 guifg=darkgray ctermfg=darkcyan
" colors for folded sections
hi Folded guifg=darkcyan guibg=bg ctermfg=darkcyan ctermbg=black
hi FoldColumn guifg=darkcyan guibg=bg ctermfg=darkcyan ctermbg=black
" colors for experimental spelling error highlighting
" this only works for spellfix.vim with will be cease to exist soon
hi spellErr gui=underline guifg=yellow cterm=underline ctermfg=darkyellow
hi BadWord gui=underline guifg=yellow cterm=underline ctermfg=darkyellow

View file

@ -0,0 +1,81 @@
let g:colors_name="VO Light"
hi normal guifg=black guibg=white ctermfg=white ctermbg=black
hi StatusLine guifg=black guibg=white ctermfg=white ctermbg=black
hi StatusLineNC guifg=black guibg=white ctermfg=white ctermbg=black
hi VertSplit guifg=black guibg=white ctermfg=white ctermbg=black
hi OL1 guifg=black ctermfg=black
hi OL2 guifg=red ctermfg=red
hi OL3 guifg=blue ctermfg=blue
hi OL4 guifg=darkviolet ctermfg=magenta
hi OL5 guifg=black ctermfg=black
hi OL6 guifg=red ctermfg=red
hi OL7 guifg=blue ctermfg=blue
hi OL8 guifg=darkviolet ctermfg=magenta
hi OL9 guifg=black ctermfg=black
" colors for tags
hi outlTags guifg=darkred ctermfg=darkred
" color for body text
hi BT1 guifg=darkgreen ctermfg=green
hi BT2 guifg=darkgreen ctermfg=green
hi BT3 guifg=darkgreen ctermfg=green
hi BT4 guifg=darkgreen ctermfg=green
hi BT5 guifg=darkgreen ctermfg=green
hi BT6 guifg=darkgreen ctermfg=green
hi BT7 guifg=darkgreen ctermfg=green
hi BT8 guifg=darkgreen ctermfg=green
hi BT9 guifg=darkgreen ctermfg=green
" color for pre-formatted text
hi PT1 guifg=darkblue ctermfg=cyan
hi PT2 guifg=darkblue ctermfg=cyan
hi PT3 guifg=darkblue ctermfg=cyan
hi PT4 guifg=darkblue ctermfg=cyan
hi PT5 guifg=darkblue ctermfg=cyan
hi PT6 guifg=darkblue ctermfg=cyan
hi PT7 guifg=darkblue ctermfg=cyan
hi PT8 guifg=darkblue ctermfg=cyan
hi PT9 guifg=darkblue ctermfg=cyan
" color for tables
hi TA1 guifg=darkviolet ctermfg=cyan
hi TA2 guifg=darkviolet ctermfg=cyan
hi TA3 guifg=darkviolet ctermfg=cyan
hi TA4 guifg=darkviolet ctermfg=cyan
hi TA5 guifg=darkviolet ctermfg=cyan
hi TA6 guifg=darkviolet ctermfg=cyan
hi TA7 guifg=darkviolet ctermfg=cyan
hi TA8 guifg=darkviolet ctermfg=cyan
hi TA9 guifg=darkviolet ctermfg=cyan
" color for user text (wrapping)
hi UT1 guifg=darkred ctermfg=cyan
hi UT2 guifg=darkred ctermfg=cyan
hi UT3 guifg=darkred ctermfg=cyan
hi UT4 guifg=darkred ctermfg=cyan
hi UT5 guifg=darkred ctermfg=cyan
hi UT6 guifg=darkred ctermfg=cyan
hi UT7 guifg=darkred ctermfg=cyan
hi UT8 guifg=darkred ctermfg=cyan
hi UT9 guifg=darkred ctermfg=cyan
" color for user text (non-wrapping)
hi UB1 guifg=darkgray ctermfg=cyan
hi UB2 guifg=darkgray ctermfg=cyan
hi UB3 guifg=darkgray ctermfg=cyan
hi UB4 guifg=darkgray ctermfg=cyan
hi UB5 guifg=darkgray ctermfg=cyan
hi UB6 guifg=darkgray ctermfg=cyan
hi UB7 guifg=darkgray ctermfg=cyan
hi UB8 guifg=darkgray ctermfg=cyan
hi UB9 guifg=darkgray ctermfg=cyan
" colors for folded sections
hi Folded guifg=darkcyan guibg=bg ctermfg=cyan ctermbg=white
hi FoldColumn guifg=darkcyan guibg=bg ctermfg=cyan ctermbg=white
" colors for experimental spelling error highlighting
" this only works for spellfix.vim with will be cease to exist soon
hi spellErr gui=underline guifg=darkred cterm=underline ctermfg=darkred
hi BadWord gui=underline guifg=darkred cterm=underline ctermfg=darkred

View file

@ -0,0 +1,81 @@
let g:colors_name="VO Light"
hi normal guifg=black guibg=white ctermfg=white ctermbg=black
hi StatusLine guifg=black guibg=white ctermfg=white ctermbg=black
hi StatusLineNC guifg=black guibg=white ctermfg=white ctermbg=black
hi VertSplit guifg=black guibg=white ctermfg=white ctermbg=black
hi OL1 guifg=black ctermfg=black
hi OL2 guifg=red ctermfg=red
hi OL3 guifg=blue ctermfg=blue
hi OL4 guifg=darkviolet ctermfg=magenta
hi OL5 guifg=black ctermfg=black
hi OL6 guifg=red ctermfg=red
hi OL7 guifg=blue ctermfg=blue
hi OL8 guifg=darkviolet ctermfg=magenta
hi OL9 guifg=black ctermfg=black
" colors for tags
hi outlTags guifg=darkred ctermfg=darkred
" color for body text
hi BT1 guifg=darkviolet ctermfg=magenta
hi BT2 guifg=black ctermfg=black
hi BT3 guifg=red ctermfg=red
hi BT4 guifg=blue ctermfg=blue
hi BT5 guifg=darkviolet ctermfg=magenta
hi BT6 guifg=black ctermfg=black
hi BT7 guifg=red ctermfg=red
hi BT8 guifg=blue ctermfg=blue
hi BT9 guifg=darkviolet ctermfg=magenta
" color for pre-formatted text
hi PT1 guifg=darkblue ctermfg=cyan
hi PT2 guifg=darkblue ctermfg=cyan
hi PT3 guifg=darkblue ctermfg=cyan
hi PT4 guifg=darkblue ctermfg=cyan
hi PT5 guifg=darkblue ctermfg=cyan
hi PT6 guifg=darkblue ctermfg=cyan
hi PT7 guifg=darkblue ctermfg=cyan
hi PT8 guifg=darkblue ctermfg=cyan
hi PT9 guifg=darkblue ctermfg=cyan
" color for tables
hi TA1 guifg=darkviolet ctermfg=cyan
hi TA2 guifg=darkviolet ctermfg=cyan
hi TA3 guifg=darkviolet ctermfg=cyan
hi TA4 guifg=darkviolet ctermfg=cyan
hi TA5 guifg=darkviolet ctermfg=cyan
hi TA6 guifg=darkviolet ctermfg=cyan
hi TA7 guifg=darkviolet ctermfg=cyan
hi TA8 guifg=darkviolet ctermfg=cyan
hi TA9 guifg=darkviolet ctermfg=cyan
" color for user text (wrapping)
hi UT1 guifg=darkred ctermfg=cyan
hi UT2 guifg=darkred ctermfg=cyan
hi UT3 guifg=darkred ctermfg=cyan
hi UT4 guifg=darkred ctermfg=cyan
hi UT5 guifg=darkred ctermfg=cyan
hi UT6 guifg=darkred ctermfg=cyan
hi UT7 guifg=darkred ctermfg=cyan
hi UT8 guifg=darkred ctermfg=cyan
hi UT9 guifg=darkred ctermfg=cyan
" color for user text (non-wrapping)
hi UB1 guifg=darkgray ctermfg=cyan
hi UB2 guifg=darkgray ctermfg=cyan
hi UB3 guifg=darkgray ctermfg=cyan
hi UB4 guifg=darkgray ctermfg=cyan
hi UB5 guifg=darkgray ctermfg=cyan
hi UB6 guifg=darkgray ctermfg=cyan
hi UB7 guifg=darkgray ctermfg=cyan
hi UB8 guifg=darkgray ctermfg=cyan
hi UB9 guifg=darkgray ctermfg=cyan
" colors for folded sections
hi Folded guifg=darkcyan guibg=bg ctermfg=cyan ctermbg=white
hi FoldColumn guifg=darkcyan guibg=bg ctermfg=cyan ctermbg=white
" colors for experimental spelling error highlighting
" this only works for spellfix.vim with will be cease to exist soon
hi spellErr gui=underline guifg=darkred cterm=underline ctermfg=darkred
hi BadWord gui=underline guifg=darkred cterm=underline ctermfg=darkred

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,85 @@
*votl_cheatsheet.txt* Last change: 2013-04-06
VIMOUTLINER CHEAT SHEET~
This overview has Vimoutliner specific commands for the available operations.
Remember that all Vim commands can be performed in Vimoutliner files. This is
especially useful for copying, pasting, moving around and similar commands.
For more extensive descriptions of command uses in Vimoutliner do ':h vo-command'. |vo-command|
List format explained: [command] [mode] [description]
CHECKBOXES~
,,cb normal Insert a check box on the current line/range
,,cx normal Toggle check box state (percentage aware)
,,cd normal Delete check boxes
,,c% normal Create a check box with percentage placeholder
,,cp normal Create a check box with percentage placeholder on all
headings
,,cz normal Compute completion for the tree below the current
heading.
EXECUTABLE LINES~
,,e normal Execute the executable tag line under cursor
FOLDING~
,,1 all set foldlevel=0
,,2 all set foldlevel=1
,,3 all set foldlevel=2
,,4 all set foldlevel=3
,,5 all set foldlevel=4
,,6 all set foldlevel=5
,,7 all set foldlevel=6
,,8 all set foldlevel=7
,,9 all set foldlevel=8
,,0 all set foldlevel=99999
FORMATTING~
,,- all Draw dashed line
,,s normal Sort sub-tree under cursor ascending
,,S normal Sort sub-tree under cursor descending
,,B normal Make body text start with a space
,,b normal Make body text start with a colon and space
>> normal Demote headline
<< normal Promote headline
<C-T> insert Demote headline
<C-D> insert Promote headline
Q normal Reformat (Synonym for gq)
OTHER~
,,f normal Directory listing of the current directory
,,w insert Save changes and return to insert mode
,,D all VimOutliner reserved command
TAGGING/INTEROUTLINE~
<C-K> normal Follow tag/interoutline (Synonym for Ctrl+])
<C-N> normal Return from tag (Synonym for Ctrl+T)
TIME AND DATE~
,,t normal Append timestamp (HH:MM:SS) to heading
,,T normal Prepend timestamp (HH:MM:SS) to heading
,,t insert Insert timestamp (HH:MM:SS) at cursor
,,d normal Append datestamp (YYYY-MM-DD) to heading
,,d insert Insert datestamp (YYYY-MM-DD) at cursor
,,D normal Prepend datestamp (YYYY-MM-DD) to heading
vim:set filetype=help textwidth=78:

View file

@ -0,0 +1,26 @@
"# #######################################################################
"# filetype.vim: filetype loader
"# version 0.4.0
"# Copyright (C) 2001,2003 by Steve Litt (slitt@troubleshooters.com)
"#
"# This program is free software; you can redistribute it and/or modify
"# it under the terms of the GNU General Public License as published by
"# the Free Software Foundation; either version 2 of the License, or
"# (at your option) any later version.
"#
"# This program is distributed in the hope that it will be useful,
"# but WITHOUT ANY WARRANTY; without even the implied warranty of
"# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
"# GNU General Public License for more details.
"#
"# You should have received a copy of the GNU General Public License
"# along with this program; if not, see <http://www.gnu.org/licenses/>.
"#
"# Steve Litt, slitt@troubleshooters.com, http://www.troubleshooters.com
"# #######################################################################
augroup filetypedetect
au! BufRead,BufNewFile *.otl setfiletype votl
au! BufRead,BufNewFile *.oln setfiletype xoutliner
augroup END

View file

@ -0,0 +1,679 @@
"#########################################################################
"# ftplugin/votl.vim: VimOutliner functions, commands and settings
"# version 0.4.0
"# Copyright (C) 2001,2003 by Steve Litt (slitt@troubleshooters.com)
"# Copyright (C) 2004,2014 by Noel Henson (noelwhenson@gmail.com)
"#
"# This program is free software; you can redistribute it and/or modify
"# it under the terms of the GNU General Public License as published by
"# the Free Software Foundation; either version 2 of the License, or
"# (at your option) any later version.
"#
"# This program is distributed in the hope that it will be useful,
"# but WITHOUT ANY WARRANTY; without even the implied warranty of
"# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
"# GNU General Public License for more details.
"#
"# You should have received a copy of the GNU General Public License
"# along with this program; if not, see <http://www.gnu.org/licenses/>.
"#
"# Steve Litt, slitt@troubleshooters.com, http://www.troubleshooters.com
"#########################################################################
" Load the plugin {{{1
" Prevent the plugin from being loaded twice
"if exists("b:did_ftplugin")
" finish
"endif
"let b:did_ftplugin = 1
let b:current_syntax = "outliner"
" Default Preferences {{{1
let use_space_colon=0
" End User Preferences
" VimOutliner Standard Settings {{{1
setlocal autoindent
"setlocal backspace=2
setlocal wrapmargin=5
setlocal wrap
setlocal tw=78
setlocal noexpandtab
setlocal tabstop=4 " tabstop and shiftwidth must match
setlocal shiftwidth=4 " values from 2 to 8 work well
"setlocal nosmarttab
"setlocal softtabstop=0
setlocal foldlevel=20
setlocal foldcolumn=1 " turns on "+" at the beginning of close folds
setlocal foldmethod=expr
setlocal foldexpr=MyFoldLevel(v:lnum)
setlocal indentexpr=
setlocal nocindent
setlocal iskeyword=@,39,45,48-57,_,129-255
" Vim Outliner Functions {{{1
if !exists("loaded_vimoutliner_functions")
let loaded_vimoutliner_functions=1
" Sorting {{{2
" IsParent(line) {{{3
" Return 1 if this line is a parent
function! IsParent(line)
return (Ind(a:line)+1) == Ind(a:line+1)
endfunction
"}}}3
" FindParent(line) {{{3
" Return line if parent, parent line if not
function! FindParent(line)
if IsParent(a:line)
return a:line
else
let l:parentindent = Ind(a:line)-1
let l:searchline = a:line
while (Ind(l:searchline) != l:parentindent) && (l:searchline > 0)
let l:searchline = l:searchline-1
endwhile
return l:searchline
endif
endfunction
"}}}3
" FindLastChild(line) {{{3
" Return the line number of the last decendent of parent line
function! FindLastChild(line)
let l:parentindent = Ind(a:line)
let l:searchline = a:line+1
while Ind(l:searchline) > l:parentindent
let l:searchline = l:searchline+1
endwhile
return l:searchline-1
endfunction
"}}}3
" MoveDown() {{{3
" Move a heading down by one
" Used for sorts and reordering of headings
function! MoveDown()
call cursor(line("."),0)
del x
put x
endfunction
"}}}3
" DelHead() {{{3
" Delete a heading
" Used for sorts and reordering of headings
function! DelHead(line)
let l:fstart = foldclosed(a:line)
if l:fstart == -1
let l:execstr = a:line . "del x"
else
let l:fend = foldclosedend(a:line)
let l:execstr = l:fstart . "," . l:fend . "del x"
endif
exec l:execstr
endfunction
" PutHead() {{{3
" Put a heading
" Used for sorts and reordering of headings
function! PutHead(line)
let l:fstart = foldclosed(a:line)
if l:fstart == -1
let l:execstr = a:line . "put x"
exec l:execstr
else
let l:fend = foldclosedend(a:line)
let l:execstr = l:fend . "put x"
exec l:execstr
endif
endfunction
"}}}3
" NextHead(line) {{{3
" Return line of next heading
" Used for sorts and reordering of headings
function! NextHead(line)
let l:fend = foldclosedend(a:line)
if l:fend == -1
return a:line+1
else
return l:fend+1
endif
endfunction
"}}}3
" CompHead(line) {{{3
" Compare this heading and the next
" Return 1: next is greater, 0 next is same, -1 next is less
function! CompHead(line)
let nexthead = NextHead(a:line)
let l:thisline=getline(a:line)
let l:nextline=getline(nexthead)
if indent(a:line) != indent(nexthead)
return 0
elseif l:thisline <# l:nextline
return 1
elseif l:thisline ># l:nextline
return -1
else
return 0
endif
endfunction
"}}}3
" Sort1Line(line) {{{3
" Compare this heading and the next and swap if out of order
" Dir is 0 for forward, 1 for reverse
" Return a 1 if a change was made
function! Sort1Line(line,dir)
if (CompHead(a:line) == -1) && (a:dir == 0)
call DelHead(a:line)
call PutHead(a:line)
return 1
elseif (CompHead(a:line) == 1) && (a:dir == 1)
call DelHead(a:line)
call PutHead(a:line)
return 1
else
return 0
endif
endfunction
"}}}3
" Sort1Pass(start,end,dir) {{{3
" Compare this heading and the next and swap if out of order
" Dir is 0 for forward, 1 for reverse
" Return a 0 if no change was made, other wise return the change count
function! Sort1Pass(fstart,fend,dir)
let l:i = a:fstart
let l:changed = 0
while l:i < a:fend
let l:changed = l:changed + Sort1Line(l:i,a:dir)
let l:i = NextHead(l:i)
endwhile
return l:changed
endfunction
"}}}3
" Sort(start,end,dir) {{{3
" Sort this range of headings
" dir: 0 = ascending, 1 = decending
function! SortRange(fstart,fend,dir)
let l:changed = 1
while l:changed != 0
let l:changed = Sort1Pass(a:fstart,a:fend,a:dir)
endwhile
endfunction
"}}}3
" SortChildren(dir) {{{3
" Sort the children of a parent
" dir: 0 = ascending, 1 = descending
function! SortChildren(dir)
let l:oldcursor = line(".")
let l:fstart = FindParent(line("."))
let l:fend = FindLastChild(l:fstart)
let l:fstart = l:fstart
if l:fend <= l:fstart + 1
return
endif
call append(line("$"),"Temporary last line for sorting")
mkview
let l:execstr = "set foldlevel=" . foldlevel(l:fstart)
exec l:execstr
call SortRange(l:fstart + 1,l:fend,a:dir)
call cursor(line("$"),0)
del x
loadview
call cursor(l:oldcursor,0)
endfunction
"}}}3
"}}}2
" MakeChars() {{{2
" Make a string of characters
" Used for strings of repeated characters
function MakeChars(count,char)
let i = 0
let l:chars=""
while i < a:count
let l:chars = l:chars . a:char
let i = i + 1
endwhile
return l:chars
endfunction
"}}}2
" MakeSpaces() {{{2
" Make a string of spaces
function MakeSpaces(count)
return MakeChars(a:count," ")
endfunction
"}}}2
" MakeDashes() {{{2
" Make a string of dashes
function MakeDashes(count)
return MakeChars(a:count,"-")
endfunction
"}}}2
" MyFoldText() {{{2
" Create string used for folded text blocks
function MyFoldText()
if exists('g:vo_fold_length') && g:vo_fold_length == "max"
let l:foldlength = winwidth(0) - 1 - &numberwidth - &foldcolumn
elseif exists('g:vo_fold_length')
let l:foldlength = g:vo_fold_length
else
let l:foldlength = 58
endif
" I have this as an option, if the user wants to set "…" as the padding
" string, or some other string, like "(more)"
if exists('g:vo_trim_string')
let l:trimstr = g:vo_trim_string
else
let l:trimstr = "..."
endif
let l:MySpaces = MakeSpaces(&sw)
let l:line = getline(v:foldstart)
let l:bodyTextFlag=0
if l:line =~ "^\t* \\S" || l:line =~ "^\t*\:"
let l:bodyTextFlag=1
let l:MySpaces = MakeSpaces(&sw * (v:foldlevel-1))
let l:line = l:MySpaces."[TEXT]"
elseif l:line =~ "^\t*\;"
let l:bodyTextFlag=1
let l:MySpaces = MakeSpaces(&sw * (v:foldlevel-1))
let l:line = l:MySpaces."[TEXT BLOCK]"
elseif l:line =~ "^\t*\> "
let l:bodyTextFlag=1
let l:MySpaces = MakeSpaces(&sw * (v:foldlevel-1))
let l:line = l:MySpaces."[USER]"
elseif l:line =~ "^\t*\>"
let l:ls = stridx(l:line,">")
let l:le = stridx(l:line," ")
if l:le == -1
let l:l = strpart(l:line, l:ls+1)
else
let l:l = strpart(l:line, l:ls+1, l:le-l:ls-1)
endif
let l:bodyTextFlag=1
let l:MySpaces = MakeSpaces(&sw * (v:foldlevel-1))
let l:line = l:MySpaces."[USER ".l:l."]"
elseif l:line =~ "^\t*\< "
let l:bodyTextFlag=1
let l:MySpaces = MakeSpaces(&sw * (v:foldlevel-1))
let l:line = l:MySpaces."[USER BLOCK]"
elseif l:line =~ "^\t*\<"
let l:ls = stridx(l:line,"<")
let l:le = stridx(l:line," ")
if l:le == -1
let l:l = strpart(l:line, l:ls+1)
else
let l:l = strpart(l:line, l:ls+1, l:le-l:ls-1)
endif
let l:bodyTextFlag=1
let l:MySpaces = MakeSpaces(&sw * (v:foldlevel-1))
let l:line = l:MySpaces."[USER BLOCK ".l:l."]"
elseif l:line =~ "^\t*\|"
let l:bodyTextFlag=1
let l:MySpaces = MakeSpaces(&sw * (v:foldlevel-1))
let l:line = l:MySpaces."[TABLE]"
endif
let l:sub = substitute(l:line,'\t',l:MySpaces,'g')
let l:sublen = strdisplaywidth(l:sub)
let l:end = " (" . ((v:foldend + l:bodyTextFlag)- v:foldstart)
if ((v:foldend + l:bodyTextFlag)- v:foldstart) == 1
let l:end = l:end . " line)"
else
let l:end = l:end . " lines)"
endif
let l:endlen = strdisplaywidth(l:end)
" Multiple cases:
" (1) Full padding with ellipse (...) or user defined string,
" (2) No point in padding, pad would just obscure the end of text,
" (3) Don't pad and use dashes to fill up the space.
if l:endlen + l:sublen > l:foldlength
let l:sub = strpart(l:sub, 0, l:foldlength - l:endlen - strdisplaywidth(l:trimstr))
let l:sub = l:sub . l:trimstr
let l:sublen = strdisplaywidth(l:sub)
let l:sub = l:sub . l:end
elseif l:endlen + l:sublen == l:foldlength
let l:sub = l:sub . l:end
else
let l:sub = l:sub . " " . MakeDashes(l:foldlength - l:endlen - l:sublen - 1) . l:end
endif
return l:sub.repeat(' ', winwidth(0)-strdisplaywidth(l:sub))
endfunction
"}}}2
" InsertDate() {{{2
" Insert today's date.
function InsertDate(ba)
let @x = strftime("%Y-%m-%d")
if a:ba == "0"
normal! "xp
else
normal! "xP
endif
endfunction
"}}}2
" InsertSpaceDate() {{{2
" Insert a space, then today's date.
function InsertSpaceDate()
let @x = " "
let @x = @x . strftime("%Y-%m-%d")
normal! "xp
endfunction
"}}}2
" InsertTime() {{{2
" Insert the time.
function InsertTime(ba)
let @x = strftime("%H:%M:%S")
if a:ba == "0"
normal! "xp
else
normal! "xP
endif
endfunction
"}}}2
" InsertSpaceTime() {{{2
" Insert a space, then the time.
function InsertSpaceTime()
let @x = " "
let @x = @x . strftime("%H:%M:%S")
normal! "xp
endfunction
"}}}2
" Ind(line) {{{2
" Determine the indent level of a line.
" Courtesy of Gabriel Horner
function! Ind(line)
return indent(a:line)/&tabstop
endfunction
"}}}2
" BodyText(line) {{{2
" Determine the indent level of a line.
function! BodyText(line)
return (match(getline(a:line),"^\t*:") == 0)
endfunction
"}}}2
" PreformattedBodyText(line) {{{2
" Determine the indent level of a line.
function! PreformattedBodyText(line)
return (match(getline(a:line),"^\t*;") == 0)
endfunction
"}}}2
" PreformattedUserText(line) {{{2
" Determine the indent level of a line.
function! PreformattedUserText(line)
return (match(getline(a:line),"^\t*<") == 0)
endfunction
"}}}2
" PreformattedUserTextLabeled(line) {{{2
" Determine the indent level of a line.
function! PreformattedUserTextLabeled(line)
return (match(getline(a:line),"^\t*<\S") == 0)
endfunction
"}}}2
" PreformattedUserTextSpace(line) {{{2
" Determine the indent level of a line.
function! PreformattedUserTextSpace(line)
return (match(getline(a:line),"^\t*< ") == 0)
endfunction
"}}}2
" UserText(line) {{{2
" Determine the indent level of a line.
function! UserText(line)
return (match(getline(a:line),"^\t*>") == 0)
endfunction
"}}}2
" UserTextSpace(line) {{{2
" Determine the indent level of a line.
function! UserTextSpace(line)
return (match(getline(a:line),"^\t*> ") == 0)
endfunction
"}}}2
" UserTextLabeled(line) {{{2
" Determine the indent level of a line.
function! UserTextLabeled(line)
return (match(getline(a:line),"^\t*>\S") == 0)
endfunction
"}}}2
" PreformattedTable(line) {{{2
" Determine the indent level of a line.
function! PreformattedTable(line)
return (match(getline(a:line),"^\t*|") == 0)
endfunction
"}}}2
" MyFoldLevel(Line) {{{2
" Determine the fold level of a line.
function MyFoldLevel(line)
let l:myindent = Ind(a:line)
let l:nextindent = Ind(a:line+1)
if BodyText(a:line)
if (BodyText(a:line-1) == 0)
return '>'.(l:myindent+1)
endif
if (BodyText(a:line+1) == 0)
return '<'.(l:myindent+1)
endif
return (l:myindent+1)
elseif PreformattedBodyText(a:line)
if (PreformattedBodyText(a:line-1) == 0)
return '>'.(l:myindent+1)
endif
if (PreformattedBodyText(a:line+1) == 0)
return '<'.(l:myindent+1)
endif
return (l:myindent+1)
elseif PreformattedTable(a:line)
if (PreformattedTable(a:line-1) == 0)
return '>'.(l:myindent+1)
endif
if (PreformattedTable(a:line+1) == 0)
return '<'.(l:myindent+1)
endif
return (l:myindent+1)
elseif PreformattedUserText(a:line)
if (PreformattedUserText(a:line-1) == 0)
return '>'.(l:myindent+1)
endif
if (PreformattedUserTextSpace(a:line+1) == 0)
return '<'.(l:myindent+1)
endif
return (l:myindent+1)
elseif PreformattedUserTextLabeled(a:line)
if (PreformattedUserTextLabeled(a:line-1) == 0)
return '>'.(l:myindent+1)
endif
if (PreformattedUserText(a:line+1) == 0)
return '<'.(l:myindent+1)
endif
return (l:myindent+1)
elseif UserText(a:line)
if (UserText(a:line-1) == 0)
return '>'.(l:myindent+1)
endif
if (UserTextSpace(a:line+1) == 0)
return '<'.(l:myindent+1)
endif
return (l:myindent+1)
elseif UserTextLabeled(a:line)
if (UserTextLabeled(a:line-1) == 0)
return '>'.(l:myindent+1)
endif
if (UserText(a:line+1) == 0)
return '<'.(l:myindent+1)
endif
return (l:myindent+1)
else
if l:myindent < l:nextindent
return '>'.(l:myindent+1)
endif
if l:myindent > l:nextindent
"return '<'.(l:nextindent+1)
return (l:myindent)
"return '<'.(l:nextindent-1)
endif
return l:myindent
endif
endfunction
"}}}2
" Spawn(line) {{{2
" Execute an executable line
" Courtesy of Steve Litt
if !exists("loaded_steveoutliner_functions")
let loaded_steveoutliner_functions=1
function Spawn()
let theline=getline(line("."))
let idx=matchend(theline, "_exe_\\s*")
if idx == -1
echo "Not an executable line"
else
let command=strpart(theline, idx)
let command="!".command
exec command
endif
endfunction
endif
"}}}2
" This should be a setlocal but that doesn't work when switching to a new .otl file
" within the same buffer. Using :e has demonstrated this.
set foldtext=MyFoldText()
"setlocal fillchars=|,
endif " if !exists("loaded_vimoutliner_functions")
" End Vim Outliner Functions
" Menu Entries {{{1
" VO menu
amenu &VO.Expand\ Level\ &1 :set foldlevel=0<cr>
amenu &VO.Expand\ Level\ &2 :set foldlevel=1<cr>
amenu &VO.Expand\ Level\ &3 :set foldlevel=2<cr>
amenu &VO.Expand\ Level\ &4 :set foldlevel=3<cr>
amenu &VO.Expand\ Level\ &5 :set foldlevel=4<cr>
amenu &VO.Expand\ Level\ &6 :set foldlevel=5<cr>
amenu &VO.Expand\ Level\ &7 :set foldlevel=6<cr>
amenu &VO.Expand\ Level\ &8 :set foldlevel=7<cr>
amenu &VO.Expand\ Level\ &9 :set foldlevel=8<cr>
amenu &VO.Expand\ Level\ &All :set foldlevel=99999<cr>
amenu &VO.-Sep1- :
"Tools sub-menu
let s:path2scripts = expand('<sfile>:p:h:h').'/vimoutliner/scripts'
" otl2html
exec 'amenu &VO.&Tools.otl2&html\.py\ (otl2html\.py\ thisfile\ -S\ html2otl_nnnnnn\.css\ >\ thisfile\.html) :!'.s:path2scripts.'/otl2html.py -S html2otl_nnnnnn.css % > %.html<CR>'
" otl2docbook
exec 'amenu &VO.&Tools.otl2&docbook\.pl\ (otl2docbook\.pl\ thisfile\ >\ thisfile\.dbk) :!'.s:path2scripts.'/otl2docbook.pl % > %.dbk<CR>'
" otl2table
exec 'amenu &VO.&Tools.otl2&table\.py\ (otl2table\.py\ thisfile\ >\ thisfile\.txt) :!'.s:path2scripts.'/otl2table.py % > %.txt<CR>'
" otl2tags => FreeMind
exec 'amenu &VO.&Tools.otl2tags\.py\ =>\ &FreeMind\ (otl2tags\.py\ \-c\ otl2tags_freemind\.conf\ thisfile\ >\ thisfile\.mm) :!'.s:path2scripts.'/otl2tags.py -c '.s:path2scripts.'/otl2tags_freemind.conf % > %.mm<CR>'
" otl2tags => Graphviz
exec 'amenu &VO.&Tools.otl2tags\.py\ =>\ &Graphviz\ (otl2tags\.py\ \-c\ otl2tags_graphviz\.conf\ thisfile\ >\ thisfile\.gv) :!'.s:path2scripts.'/otl2tags.py -c '.s:path2scripts.'/otl2tags_graphviz.conf % > %.gv<CR>'
amenu &VO.&Tools.&myotl2thml\.sh\ (myotl2html\.sh\ thisfile) :!myotl2html.sh %<CR>
amenu &VO.-Sep2- :
amenu &VO.&Color\ Scheme :popup Edit.Color\ Scheme<cr>
amenu &VO.-Sep3- :
amenu &VO.&Help.&Index :he vo<cr>
amenu &VO.&Help.&,,\ Commands :he votl-command<cr>
amenu &VO.&Help.&Checkboxes :he votl-checkbox<cr>
amenu &VO.&Help.&Hoisting :he votl-hoisting<cr>
amenu &Help.-Sep1- :
" Help menu additions
amenu &Help.&Vim\ Outliner.&Index :he votl<cr>
amenu &Help.&Vim\ Outliner.&,,\ Commands :he votl-command<cr>
amenu &Help.&Vim\ Outliner.&Checkboxes :he votl-checkbox<cr>
amenu &Help.&Vim\ Outliner.&Hoisting :he votl-hoisting<cr>
"}}}1
" Auto-commands {{{1
if !exists("autocommand_vo_loaded")
let autocommand_vo_loaded = 1
au BufNewFile,BufRead *.otl setf votl
" au CursorHold *.otl syn sync fromstart
"set updatetime=500
endif
"}}}1
" this command needs to be run every time so Vim doesn't forget where to look
setlocal tags^=$HOME/.vim/vimoutliner/vo_tags.tag
" Added an indication of current syntax as per Dillon Jones' request
let b:current_syntax = "outliner"
" Directory where VO is located now
let vo_dir = expand("<sfile>:p:h:h")
" Load rc file, only the first found.
let rcs = split(globpath('$HOME,$HOME/.vimoutliner','.vimoutlinerrc'), "\n") +
\ split(globpath('$HOME,$HOME/.vimoutliner,$HOME/.vim', 'vimoutlinerrc'), "\n") +
\ split(globpath(vo_dir, 'vimoutlinerrc'), "\n")
if len(rcs) > 0
exec 'source '.rcs[0]
else
runtime vimoutliner/vimoutlinerrc
endif
" Load modules
if exists('g:vo_modules_load')
for vo_module in split(g:vo_modules_load, '\s*:\s*')
exec "runtime! vimoutliner/plugin/votl_" . vo_module . ".vim"
endfor
unlet! vo_module
endif
" Vim Outliner Key Mappings {{{1
" insert the date
nmap <silent><buffer> <localleader>d $:call InsertSpaceDate()<cr>
imap <silent><buffer> <localleader>d ~<esc>x:call InsertDate(0)<cr>a
nmap <silent><buffer> <localleader>D ^:call InsertDate(1)<cr>a <esc>
" insert the time
nmap <silent><buffer> <localleader>t $:call InsertSpaceTime()<cr>
imap <silent><buffer> <localleader>t ~<esc>x:call InsertTime(0)<cr>a
nmap <silent><buffer> <localleader>T ^:call InsertTime(1)<cr>a <esc>
" sort a list naturally
map <silent> <buffer> <localleader>s :silent call SortChildren(0)<cr>
" sort a list, but you supply the options
map <silent> <buffer> <localleader>S :silent call SortChildren(1)<cr>
" invoke the file explorer
map <silent><buffer> <localleader>f :e .<cr>
imap <silent><buffer> <localleader>f :e .<cr>
" Insert a fence for segmented lists.
" this divider is used by otl2html.py to create '<hr>'
map <silent><buffer> <localleader>- o----------------------------------------0
imap <silent><buffer> <localleader>- ----------------------------------------<cr>
" switch document between the two types of bodytext styles
if use_space_colon == 1
" First, convert document to the marker style
map <silent><buffer><localleader>b :%s/\(^\t*\) :/\1/e<cr>:%s/\(^\t*\) /\1 : /e<cr>:let @/=""<cr>
" Now, convert document to the space style
map <silent><buffer><localleader>B :%s/\(^\t*\) :/\1/e<cr>:let @/=""<cr>
else
" First, convert document to the marker style
map <silent><buffer><localleader>b :%s/\(^\t*\):/\1/e<cr>:%s/\(^\t*\) /\1: /e<cr>:let @/=""<cr>
" Now, convert document to the space style
map <silent><buffer><localleader>B :%s/\(^\t*\):/\1/e<cr>:let @/=""<cr>
endif
" Steve's additional mappings start here
map <silent><buffer> <C-K> <C-]>
map <silent><buffer> <C-N> <C-T>
map <silent><buffer> <localleader>0 :set foldlevel=99999<CR>
map <silent><buffer> <localleader>9 :set foldlevel=8<CR>
map <silent><buffer> <localleader>8 :set foldlevel=7<CR>
map <silent><buffer> <localleader>7 :set foldlevel=6<CR>
map <silent><buffer> <localleader>6 :set foldlevel=5<CR>
map <silent><buffer> <localleader>5 :set foldlevel=4<CR>
map <silent><buffer> <localleader>4 :set foldlevel=3<CR>
map <silent><buffer> <localleader>3 :set foldlevel=2<CR>
map <silent><buffer> <localleader>2 :set foldlevel=1<CR>
map <silent><buffer> <localleader>1 :set foldlevel=0<CR>
"next line commented out due to hard-coded nature and ancient, nonexistent file
"map <silent><buffer> <localleader>,, :runtime vimoutliner/vimoutlinerrc<CR>
map! <silent><buffer> <localleader>w <Esc>:w<CR>a
nmap <silent><buffer> <localleader>e :call Spawn()<cr>
" Steve's additional mappings end here
" End of Vim Outliner Key Mappings }}}1
" The End
" vim600: set foldmethod=marker foldlevel=0:

View file

@ -0,0 +1,173 @@
#!/bin/bash
homedir=$HOME
vimdir=$homedir/.vim
vodir=$vimdir/vimoutliner
OS=`uname`
backupargs=""
#SOME FUNCTIONS
sure () {
read REPLY
echo test $REPLY = "y" || test $REPLY = "Y"
}
make_dir () {
test -d $1 || {
echo " creating: $1"
mkdir $1
created=1
}
}
copyfile () {
echo " installing: $2/$1"
install $backupargs $1 $2/$1
}
copydir () {
files=`ls $1`
for i in $files; do
echo " installing: $2/$i"
if [ -d $1/$i ]; then
mkdir -p $2/$i
copydir $1/$i $2/$i
else
install $backupargs $1/$i $2
fi
done
}
#START THE INSTALLATION
cat <<EOT
Vim Outliner Installation
This script is safe for installing Vim Outliner and for upgrading an
existing Vim Outliner installation.
EOT
echo -n "Would you like to continue (y/N) ? "
sure || exit
#CREATE NECESSARY DIRECTORIES
created=0
echo checking/creating needed directories
make_dir $vimdir
make_dir $vimdir/syntax
make_dir $vimdir/ftplugin
make_dir $vimdir/ftdetect
make_dir $vimdir/doc
make_dir $vimdir/colors
make_dir $vodir
make_dir $vodir/plugin
make_dir $vodir/scripts
if [ $created -eq 0 ]; then echo " none created"; fi
#TWEAK $HOME/.vimrc
modified=0
echo checking/creating/modifying $homedir/.vimrc
test -f $homedir/.vimrc || { echo " creating $homedir/.vimrc"
touch $homedir/.vimrc
}
egrep -lq "filetype[[:space:]]+plugin[[:space:]]+indent[[:space:]]+on" $homedir/.vimrc || \
{ modified=1
echo "filetype plugin indent on" >> $homedir/.vimrc
}
egrep -lq "syntax[[:space:]]+on" $homedir/.vimrc || \
{ modified=1
echo "syntax on" >> $homedir/.vimrc
}
if [ $modified -eq 0 ] ; then
echo " not modified";
else
echo " modifying $homedir/.vimrc"
fi
#TWEAK $HOME/.vim/filetype.vim
modified=0
echo checking/creating/modifying $homedir/.vim/filetype.vim
test -f $homedir/.vim/filetype.vim || \
{ echo " creating $homedir/.vim/filetype.vim"
touch $homedir/.vim/filetype.vim
}
egrep -lq "runtime\! ftdetect/\*.vim" $homedir/.vim/filetype.vim || \
{ echo " modifying $homedir/.vim/filetype.vim"
modified=1
echo "runtime! ftdetect/*.vim" >> $homedir/.vim/filetype.vim
}
if [ $modified -eq 0 ] ; then echo " not modified"; fi
#CLEANUP OLD INSTALLATIONS
echo "cleaning up old (<0.3.5) installations"
files=`find $vimdir -iname "vo_*"`
for file in $files; do
echo "removing $file"
rm -v $file
done
#CLEANUP OLD BACKUPS
if [ -z $backupargs ]; then
echo "cleaning up old backups"
files=`find $vimdir -iname "vo*.old"`
for file in $files; do
echo "removing $file"
rm -v $file
done
files2=`find $vodir -iname "*.old"`
for file in $files; do
echo "removing $file"
rm -v $file
done
fi
#COPY FILES
echo "installing files"
copyfile syntax/votl.vim $vimdir
copyfile ftplugin/votl.vim $vimdir
copyfile ftdetect/votl.vim $vimdir
copyfile colors/vo_light.vim $vimdir
copyfile colors/vo_dark.vim $vimdir
copyfile doc/votl.txt $vimdir
copyfile doc/votl_cheatsheet.txt $vimdir
copyfile vimoutlinerrc $vodir
copyfile vimoutliner/scripts/votl_maketags.pl $vimdir
#INCORPORATE HELP DOCUMENTATION
echo "Installing vimoutliner documentation"
vim -c "helptags $HOME/.vim/doc" -c q
#INSTALL THE ADD-ONS
cat <<EOT
Add-ons
There are optional Vim Outliner plugins to handle checkboxes, hoisting and
smartpaste. There is also a script to convert a Vim Outliner .otl file
to a html file, as well as many other external scripts included.
The plugins will be stored in $vodir/plugin and the scripts will be installed in
$vodir/scripts.
EOT
echo -n "Would you like to install these (y/N) "
if sure; then
echo installing add-ons
copydir vimoutliner/plugin $vodir/plugin
copydir vimoutliner/scripts $vodir/scripts
fi
#ALL DONE
echo ""
echo "Installation of Vimoutliner is now complete"
cat <<EOT
**********************************************************************
* For help with using VimOutliner simply execute ":help vo" within *
* vim. For a quick overview of all commands execute: *
* ":help votl_cheatsheet" *
* *
* Additional useful scripts are available in the scripts folder, *
* see $HOME/.vim/vimoutliner/scripts *
**********************************************************************
EOT

View file

@ -0,0 +1,248 @@
"#########################################################################
"# syntax/votl.vim: VimOutliner syntax highlighting
"# version 0.4.0
"# Copyright (C) 2001,2003 by Steve Litt (slitt@troubleshooters.com)
"#
"# This program is free software; you can redistribute it and/or modify
"# it under the terms of the GNU General Public License as published by
"# the Free Software Foundation; either version 2 of the License, or
"# (at your option) any later version.
"#
"# This program is distributed in the hope that it will be useful,
"# but WITHOUT ANY WARRANTY; without even the implied warranty of
"# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
"# GNU General Public License for more details.
"#
"# You should have received a copy of the GNU General Public License
"# along with this program; if not, see <http://www.gnu.org/licenses/>.
"#
"# Steve Litt, slitt@troubleshooters.com, http://www.troubleshooters.com
"#########################################################################
" HISTORY {{{1
"#########################################################################
"# V0.1.0 Pre-alpha
"# Set of outliner friendly settings
"# Steve Litt, 5/28/2001
"# End of version 0.1.0
"#
"# V0.1.1 Pre-alpha
"# No change
"#
"# Steve Litt, 5/28/2001
"# End of version 0.1.1
"#
"# V0.1.2 Pre-alpha
"# No Change
"# Steve Litt, 5/30/2001
"# End of version 0.1.2
"# V0.1.3 Pre-alpha
"# No Change
"# Steve Litt, 5/30/2001
"# End of version 0.1.3
"# V0.2.0
"# Noel Henson adds code for outliner-friendly expand and
"# collapse, comma comma commands, color coding, hooks for a
"# spellchecker, sorting, and date insertion.
"# Noel Henson, 11/24/2002
"# End of version 0.2.0
"# END OF HISTORY
"#
"#########################################################################
" Colors linked {{{1
" Bill Powell, http://www.billpowellisalive.com
" Linked colors to normal groups. Different schemes will need tweaking.
" Occasionally certain groups will be rendered invisible. ;)
"
" Changelog {{{2
"2007 Jan 23, 21:23 Tue - 0.3.0, Modified version 0.1
" Linked syntax groups to standard Vim color groups, intsead of to
" particular colors. Now each colorscheme can work its own magic on
" a VO file.
"2007 Apr 30, 9:36 Mon - 0.3.0, Modified version 0.2
" Changed a few linked groups to reduce chances of groups being invisible.
" No longer use Ignore group for anything.
" Still a little redundancy; different groups might linked to same color group.
" E.g., PT1 and UT1. But some color schemes (e.g. astronout) will differentiate between
" Special and Debug. Others will use the same colors for, say, Identifier and Debug.
" It just depends.
" To tweak these groups, try :h syntax and go to group-name.
" This shows the color groups, highlighted in your current colorscheme.
" }}}
hi link OL1 Statement
hi link OL2 Identifier
hi link OL3 Constant
hi link OL4 PreProc
hi link OL5 Statement
hi link OL6 Identifier
hi link OL7 Constant
hi link OL8 PreProc
hi link OL9 Statement
"colors for tags
"hi link outlTags Tag
hi link outlTags Todo
"color for body text
hi link BT1 Comment
hi link BT2 Comment
hi link BT3 Comment
hi link BT4 Comment
hi link BT5 Comment
hi link BT6 Comment
hi link BT7 Comment
hi link BT8 Comment
hi link BT9 Comment
"color for pre-formatted text
hi link PT1 Special
hi link PT2 Special
hi link PT3 Special
hi link PT4 Special
hi link PT5 Special
hi link PT6 Special
hi link PT7 Special
hi link PT8 Special
hi link PT9 Special
"color for tables
hi link TA1 Type
hi link TA2 Type
hi link TA3 Type
hi link TA4 Type
hi link TA5 Type
hi link TA6 Type
hi link TA7 Type
hi link TA8 Type
hi link TA9 Type
"color for user text (wrapping)
hi link UT1 Debug
hi link UT2 Debug
hi link UT3 Debug
hi link UT4 Debug
hi link UT5 Debug
hi link UT6 Debug
hi link UT7 Debug
hi link UT8 Debug
hi link UT9 Debug
"color for user text (non-wrapping)
hi link UB1 Underlined
hi link UB2 Underlined
hi link UB3 Underlined
hi link UB4 Underlined
hi link UB5 Underlined
hi link UB6 Underlined
hi link UB7 Underlined
hi link UB8 Underlined
hi link UB9 Underlined
"colors for folded sections
"hi link Folded Special
"hi link FoldColumn Type
"colors for experimental spelling error highlighting
"this only works for spellfix.vim with will be cease to exist soon
hi link spellErr Error
hi link BadWord Todo
" Syntax {{{1
syn clear
syn sync fromstart
syn match outlTags '_tag_\w*' contained
" Noel's style of body text {{{2
syntax region BT1 start=+^ \S+ skip=+^ \S+ end=+^\S+me=e-1 end=+^\(\t\)\{1}\S+me=e-2 contains=spellErr,SpellErrors,BadWord contained
syntax region BT2 start=+^\(\t\)\{1} \S+ skip=+^\(\t\)\{1} \S+ end=+^\(\t\)*\S+me=s-1 contains=spellErr,SpellErrors,BadWord contained
syntax region BT3 start=+^\(\t\)\{2} \S+ skip=+^\(\t\)\{2} \S+ end=+^\(\t\)*\S+me=s-1 contains=spellErr,SpellErrors,BadWord contained
syntax region BT4 start=+^\(\t\)\{3} \S+ skip=+^\(\t\)\{3} \S+ end=+^\(\t\)*\S+me=s-1 contains=spellErr,SpellErrors,BadWord contained
syntax region BT5 start=+^\(\t\)\{4} \S+ skip=+^\(\t\)\{4} \S+ end=+^\(\t\)*\S+me=s-1 contains=spellErr,SpellErrors,BadWord contained
syntax region BT6 start=+^\(\t\)\{5} \S+ skip=+^\(\t\)\{5} \S+ end=+^\(\t\)*\S+me=s-1 contains=spellErr,SpellErrors,BadWord contained
syntax region BT7 start=+^\(\t\)\{6} \S+ skip=+^\(\t\)\{6} \S+ end=+^\(\t\)*\S+me=s-1 contains=spellErr,SpellErrors,BadWord contained
syntax region BT8 start=+^\(\t\)\{7} \S+ skip=+^\(\t\)\{7} \S+ end=+^\(\t\)*\S+me=s-1 contains=spellErr,SpellErrors,BadWord contained
syntax region BT9 start=+^\(\t\)\{8} \S+ skip=+^\(\t\)\{8} \S+ end=+^\(\t\)*\S+me=s-1 contains=spellErr,SpellErrors,BadWord contained
"comment-style bodytext as per Steve Litt {{{2
syntax region BT1 start=+^:+ skip=+^:+ end=+^\S+me=e-1 end=+^\(\t\)\{1}\S+me=s-1 contains=spellErr,SpellErrors,BadWord contained
syntax region BT2 start=+^\(\t\)\{1}:+ skip=+^\(\t\)\{1}:+ end=+^\(\t\)*\S+me=s-1 contains=spellErr,SpellErrors,BadWord contained
syntax region BT3 start=+^\(\t\)\{2}:+ skip=+^\(\t\)\{2}:+ end=+^\(\t\)*\S+me=s-1 contains=spellErr,SpellErrors,BadWord contained
syntax region BT4 start=+^\(\t\)\{3}:+ skip=+^\(\t\)\{3}:+ end=+^\(\t\)*\S+me=s-1 contains=spellErr,SpellErrors,BadWord contained
syntax region BT5 start=+^\(\t\)\{4}:+ skip=+^\(\t\)\{4}:+ end=+^\(\t\)*\S+me=s-1 contains=spellErr,SpellErrors,BadWord contained
syntax region BT6 start=+^\(\t\)\{5}:+ skip=+^\(\t\)\{5}:+ end=+^\(\t\)*\S+me=s-1 contains=spellErr,SpellErrors,BadWord contained
syntax region BT7 start=+^\(\t\)\{6}:+ skip=+^\(\t\)\{6}:+ end=+^\(\t\)*\S+me=s-1 contains=spellErr,SpellErrors,BadWord contained
syntax region BT8 start=+^\(\t\)\{7}:+ skip=+^\(\t\)\{7}:+ end=+^\(\t\)*\S+me=s-1 contains=spellErr,SpellErrors,BadWord contained
syntax region BT9 start=+^\(\t\)\{8}:+ skip=+^\(\t\)\{8}:+ end=+^\(\t\)*\S+me=s-1 contains=spellErr,SpellErrors,BadWord contained
"Preformatted body text {{{2
syntax region PT1 start=+^;+ skip=+^;+ end=+^\S+me=e-1 end=+^\(\t\)\{1}\S+me=s-1 contains=spellErr,SpellErrors,BadWord contained
syntax region PT2 start=+^\(\t\)\{1};+ skip=+^\(\t\)\{1};+ end=+^\(\t\)*\S+me=s-1 contains=spellErr,SpellErrors,BadWord contained
syntax region PT3 start=+^\(\t\)\{2};+ skip=+^\(\t\)\{2};+ end=+^\(\t\)*\S+me=s-1 contains=spellErr,SpellErrors,BadWord contained
syntax region PT4 start=+^\(\t\)\{3};+ skip=+^\(\t\)\{3};+ end=+^\(\t\)*\S+me=s-1 contains=spellErr,SpellErrors,BadWord contained
syntax region PT5 start=+^\(\t\)\{4};+ skip=+^\(\t\)\{4};+ end=+^\(\t\)*\S+me=s-1 contains=spellErr,SpellErrors,BadWord contained
syntax region PT6 start=+^\(\t\)\{5};+ skip=+^\(\t\)\{5};+ end=+^\(\t\)*\S+me=s-1 contains=spellErr,SpellErrors,BadWord contained
syntax region PT7 start=+^\(\t\)\{6};+ skip=+^\(\t\)\{6};+ end=+^\(\t\)*\S+me=s-1 contains=spellErr,SpellErrors,BadWord contained
syntax region PT8 start=+^\(\t\)\{7};+ skip=+^\(\t\)\{7};+ end=+^\(\t\)*\S+me=s-1 contains=spellErr,SpellErrors,BadWord contained
syntax region PT9 start=+^\(\t\)\{8};+ skip=+^\(\t\)\{8};+ end=+^\(\t\)*\S+me=s-1 contains=spellErr,SpellErrors,BadWord contained
"Preformatted tables {{{2
syntax region TA1 start=+^|+ skip=+^|+ end=+^\S+me=e-1 end=+^\(\t\)\{1}\S+me=s-1 contains=spellErr,SpellErrors,BadWord contained
syntax region TA2 start=+^\(\t\)\{1}|+ skip=+^\(\t\)\{1}|+ end=+^\(\t\)*\S+me=s-1 contains=spellErr,SpellErrors,BadWord contained
syntax region TA3 start=+^\(\t\)\{2}|+ skip=+^\(\t\)\{2}|+ end=+^\(\t\)*\S+me=s-1 contains=spellErr,SpellErrors,BadWord contained
syntax region TA4 start=+^\(\t\)\{3}|+ skip=+^\(\t\)\{3}|+ end=+^\(\t\)*\S+me=s-1 contains=spellErr,SpellErrors,BadWord contained
syntax region TA5 start=+^\(\t\)\{4}|+ skip=+^\(\t\)\{4}|+ end=+^\(\t\)*\S+me=s-1 contains=spellErr,SpellErrors,BadWord contained
syntax region TA6 start=+^\(\t\)\{5}|+ skip=+^\(\t\)\{5}|+ end=+^\(\t\)*\S+me=s-1 contains=spellErr,SpellErrors,BadWord contained
syntax region TA7 start=+^\(\t\)\{6}|+ skip=+^\(\t\)\{6}|+ end=+^\(\t\)*\S+me=s-1 contains=spellErr,SpellErrors,BadWord contained
syntax region TA8 start=+^\(\t\)\{7}|+ skip=+^\(\t\)\{7}|+ end=+^\(\t\)*\S+me=s-1 contains=spellErr,SpellErrors,BadWord contained
syntax region TA9 start=+^\(\t\)\{8}|+ skip=+^\(\t\)\{8}|+ end=+^\(\t\)*\S+me=s-1 contains=spellErr,SpellErrors,BadWord contained
"wrapping user text {{{2
syntax region UT1 start=+^>+ skip=+^>+ end=+^\S+me=e-1 end=+^\(\t\)\{1}\S+me=s-1 contains=spellErr,SpellErrors,BadWord contained
syntax region UT2 start=+^\(\t\)\{1}>+ skip=+^\(\t\)\{1}>+ end=+^\(\t\)*\S+me=s-1 contains=spellErr,SpellErrors,BadWord contained
syntax region UT3 start=+^\(\t\)\{2}>+ skip=+^\(\t\)\{2}>+ end=+^\(\t\)*\S+me=s-1 contains=spellErr,SpellErrors,BadWord contained
syntax region UT4 start=+^\(\t\)\{3}>+ skip=+^\(\t\)\{3}>+ end=+^\(\t\)*\S+me=s-1 contains=spellErr,SpellErrors,BadWord contained
syntax region UT5 start=+^\(\t\)\{4}>+ skip=+^\(\t\)\{4}>+ end=+^\(\t\)*\S+me=s-1 contains=spellErr,SpellErrors,BadWord contained
syntax region UT6 start=+^\(\t\)\{5}>+ skip=+^\(\t\)\{5}>+ end=+^\(\t\)*\S+me=s-1 contains=spellErr,SpellErrors,BadWord contained
syntax region UT7 start=+^\(\t\)\{6}>+ skip=+^\(\t\)\{6}>+ end=+^\(\t\)*\S+me=s-1 contains=spellErr,SpellErrors,BadWord contained
syntax region UT8 start=+^\(\t\)\{7}>+ skip=+^\(\t\)\{7}>+ end=+^\(\t\)*\S+me=s-1 contains=spellErr,SpellErrors,BadWord contained
syntax region UT9 start=+^\(\t\)\{8}>+ skip=+^\(\t\)\{8}>+ end=+^\(\t\)*\S+me=s-1 contains=spellErr,SpellErrors,BadWord contained
"non-wrapping user text {{{2
syntax region UB1 start=+^<+ skip=+^<+ end=+^\S+me=e-1 end=+^\(\t\)\{1}\S+me=s-1 contains=spellErr,SpellErrors,BadWord contained
syntax region UB2 start=+^\(\t\)\{1}<+ skip=+^\(\t\)\{1}<+ end=+^\(\t\)*\S+me=s-1 contains=spellErr,SpellErrors,BadWord contained
syntax region UB3 start=+^\(\t\)\{2}<+ skip=+^\(\t\)\{2}<+ end=+^\(\t\)*\S+me=s-1 contains=spellErr,SpellErrors,BadWord contained
syntax region UB4 start=+^\(\t\)\{3}<+ skip=+^\(\t\)\{3}<+ end=+^\(\t\)*\S+me=s-1 contains=spellErr,SpellErrors,BadWord contained
syntax region UB5 start=+^\(\t\)\{4}<+ skip=+^\(\t\)\{4}<+ end=+^\(\t\)*\S+me=s-1 contains=spellErr,SpellErrors,BadWord contained
syntax region UB6 start=+^\(\t\)\{5}<+ skip=+^\(\t\)\{5}<+ end=+^\(\t\)*\S+me=s-1 contains=spellErr,SpellErrors,BadWord contained
syntax region UB7 start=+^\(\t\)\{6}<+ skip=+^\(\t\)\{6}<+ end=+^\(\t\)*\S+me=s-1 contains=spellErr,SpellErrors,BadWord contained
syntax region UB8 start=+^\(\t\)\{7}<+ skip=+^\(\t\)\{7}<+ end=+^\(\t\)*\S+me=s-1 contains=spellErr,SpellErrors,BadWord contained
syntax region UB9 start=+^\(\t\)\{8}<+ skip=+^\(\t\)\{8}<+ end=+^\(\t\)*\S+me=s-1 contains=spellErr,SpellErrors,BadWord contained
"comment-style bodytext formatting as per Steve Litt
syntax match Comment "^\s*:.*$"
setlocal fo-=t fo+=crqno
setlocal com=sO:\:\ -,mO:\:\ \ ,eO:\:\:,:\:,sO:\>\ -,mO:\>\ \ ,eO:\>\>,:\>
" Headings {{{2
syntax region OL1 start=+^[^:\t]+ end=+^[^:\t]+me=e-1 contains=outlTags,BT1,BT2,PT1,PT2,TA1,TA2,UT1,UT2,UB1,UB2,spellErr,SpellErrors,BadWord,OL2 keepend
syntax region OL2 start=+^\t[^:\t]+ end=+^\t[^:\t]+me=s-1 contains=outlTags,BT2,BT3,PT2,PT3,TA2,TA3,UT2,UT3,UB2,UB3,spellErr,SpellErrors,BadWord,OL3 keepend
syntax region OL3 start=+^\(\t\)\{2}[^:\t]+ end=+^\(\t\)\{2}[^:\t]+me=e-3 contains=outlTags,BT3,BT4,PT3,PT4,TA3,TA4,UT3,UT4,UB3,UB4,spellErr,SpellErrors,BadWord,OL4 keepend
syntax region OL4 start=+^\(\t\)\{3}[^:\t]+ end=+^\(\t\)\{3}[^:\t]+me=e-4 contains=outlTags,BT4,BT5,PT4,PT5,TA4,TA5,UT4,UT5,UB4,UB5,spellErr,SpellErrors,BadWord,OL5 keepend
syntax region OL5 start=+^\(\t\)\{4}[^:\t]+ end=+^\(\t\)\{4}[^:\t]+me=e-5 contains=outlTags,BT5,BT6,PT5,PT6,TA5,TA6,UT5,UT6,UB5,UB6,spellErr,SpellErrors,BadWord,OL6 keepend
syntax region OL6 start=+^\(\t\)\{5}[^:\t]+ end=+^\(\t\)\{5}[^:\t]+me=e-6 contains=outlTags,BT6,BT7,PT6,PT7,TA6,TA7,UT6,UT7,UB6,UB7,spellErr,SpellErrors,BadWord,OL7 keepend
syntax region OL7 start=+^\(\t\)\{6}[^:\t]+ end=+^\(\t\)\{6}[^:\t]+me=e-7 contains=outlTags,BT7,BT8,PT7,PT8,TA7,TA8,UT7,UT8,UB7,UB8,spellErr,SpellErrors,BadWord,OL8 keepend
syntax region OL8 start=+^\(\t\)\{7}[^:\t]+ end=+^\(\t\)\{7}[^:\t]+me=e-8 contains=outlTags,BT8,BT9,PT8,PT9,TA8,TA9,UT8,UT9,UB8,UB9,spellErr,SpellErrors,BadWord,OL9 keepend
syntax region OL9 start=+^\(\t\)\{8}[^:\t]+ end=+^\(\t\)\{8}[^:\t]+me=e-9 contains=outlTags,BT9,PT9,TA9,UT9,UB9,spellErr,SpellErrors,BadWord keepend
" Auto-commands {{{1
if !exists("autocommand_vo_loaded")
let autocommand_vo_loaded = 1
au BufNewFile,BufRead *.otl setf outliner
" au CursorHold *.otl syn sync fromstart
" set updatetime=500
endif
" The End
" vim600: set foldmethod=marker foldlevel=0:

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 318 B

View file

@ -0,0 +1,77 @@
Hoisting
What is it?
: Hoisting is a way to focus on a particular area of an outline. Only the
: offspring from a selected parent will be shown. The offspring are
: left-justified just as if they were the top level headings and had no
: parent.
Installation
The hoist plugin is included in the VimOutliner plugin directory
This plugin is disabled by default
See information in the ~/.vimoutlinerrc on how to enable it
Optionally set the hoistParanoia variable
: If you are really paranoid about loosing your data during shakedown
: of vo_hoist.vim, you can add this to your ~/.vimoutlinerrc file. It
: will prevent VO from automatically removing the temporary hoist
: files. This will have a tendency to hugely increase the number of
: files in your working directory (but if you're paranoid... ;) )
let g:hoistParanoia = 1
How do I use it?
Hoisting
: Whenever you open a VO document, hoisting will be available.
Invocation
Place the cursor on the parent to be hoisted
,,h
You will need to hit <ENTER> to accept some informational messages
You should now see the children of the selected parent ready for editing
Treat this document just like a normal .otl file
De-hoisting
: From within the hoisted document you simply need to quit. The
: hoisted data will be saved in place of the old children. The cursor
: will be returned to the hoisted parent.
Invocation
The :q, :wq, :x and ZZ perform a de-hoist operation
A write will automatically be done to save any changes
Manual De-hoisting
: Should the de-hoisting ever fail or should an operator quit Vim (by
: closing the window with the mouse, say), a manual de-hoisting will
: need to be performed.
You can find a currently hoisted parent by searching for __hoist
Place the cursor on the parent with the __hoist tag
,,H
The edited, hoisted offspring will replace the current offspring
The __hoist tag will be removed
The Hoist Tag
Example Tag
: The hoist tag is added to a hoisted parent for error recovery. It
: includes a filename, a line number and a timestamp.
Example Tag
__hoist:vo_hoist.46.20030816124249.otl
Tag Components
Tag Marker
__hoist:
Easy search and replace
Filename Prefix
vo_hoist.
Needed to comply with VO standards for file naming
Parent Line Number (at time of hoisting)
46.
Timestamp
20030816124249
YYYYMMDDhhmmss
Filename Suffix
.otl
Duh
The Log Files
: Log files of the hoists are created to aid in error recovery should that
: become necessary. They are created in the same directory as the working
: .otl file.
Log Filename
.vo_hoist.<otlfilename>.log
Example Log Filename
.vo_hoist.test.otl.log
Contents
There is one line per hoist operation
Each is comprised of a parent with a __hoist tag
Limitations
1 Level Deep
: Until debug is complete, hoisting is limited to one level.

View file

@ -0,0 +1,251 @@
"######################################################################
"# VimOutliner Hoisting
"# Copyright (C) 2003 by Noel Henson noel@noels-lab.com
"# The file is currently an experimental part of Vim Outliner.
"#
"# This program is free software; you can redistribute it and/or modify
"# it under the terms of the GNU General Public License as published by
"# the Free Software Foundation; either version 2 of the License, or
"# (at your option) any later version.
"#
"# This program is distributed in the hope that it will be useful,
"# but WITHOUT ANY WARRANTY; without even the implied warranty of
"# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
"# GNU General Public License for more details.
"######################################################################
" Load the plugin {{{1
" mappings {{{1
map <silent> <buffer> <localleader>h :call Hoist(line("."))<cr>
map <silent> <buffer> <localleader>H :call DeHoistThis(line("."))<cr>
"}}}1
if exists("g:did_vo_hoist")
finish
endif
if !exists("g:hoistParanoia")
let g:hoistParanoia=0
endif
let g:did_vo_hoist = 1
" Functions {{{1
" RemoveTabsLine(line,tabs) {{{2
" remove specified number of tabs from the beginning of line
function! RemoveTabsLine(line,tabs)
return substitute(getline(a:line),"^\\(\\t\\)\\{".a:tabs."}", "", "")
endfunction
"}}}2
" MakeTempFilename(line) {{{2
" return a string to use as the temporary filename for the hoisted area
function! MakeTempFilename(line)
return "vo_hoist.".a:line.strftime(".%Y%m%d%H%M%S").".otl"
endfunction
"}}}2
" AddHoistFilename(line) {{{2
" Add a temporary filename to a parent line to indicate hoisting
function! AddHoistFilename(line)
let l:newparent = getline(a:line)." __hoist:".MakeTempFilename(a:line)
call setline(a:line,l:newparent)
endfunction
"}}}2
"}}}2
" DeleteHoistFilename(line) {{{2
" Delete a temporary filename from a parent line
function! DeleteHoistFilename(line)
call setline(a:line,substitute(getline(a:line)," __hoist:.*","",""))
endfunction
"}}}2
" ExtractHoistFilename(line) {{{2
" Extract a filename from a hoisted parent
function! ExtractHoistFilename(line)
return substitute(getline(a:line),".* __hoist:","","")
endfunction
"}}}2
" IsParent(line) {{{2
" Return 1 if this line is a parent
function! IsParent(line)
return (Ind(a:line)+1) == Ind(a:line+1)
endfunction
"}}}2
" IsHoistedParent(line) {{{2
" Return 1 if this line is a parent with hoisted children
function! IsHoistParent(line)
return match(getline(a:line)," __hoist:","") != -1
endfunction
"}}}2
" FindParent(line) {{{2
" Return line if parent, parent line if not
function! FindParent(line)
if IsParent(a:line)
return a:line
else
let l:parentindent = Ind(a:line)-1
let l:searchline = a:line
while (Ind(l:searchline) != l:parentindent) && (l:searchline > 0)
let l:searchline = l:searchline-1
endwhile
return l:searchline
endif
endfunction
"}}}2
" FindLastChild(line) {{{2
" Return the line number of the last descendent of parent line
function! FindLastChild(line)
let l:parentindent = Ind(a:line)
let l:searchline = a:line+1
while Ind(l:searchline) > l:parentindent
let l:searchline = l:searchline+1
endwhile
return l:searchline-1
endfunction
"}}}2
"}}}2
" Hoist(line) {{{2
" Write the offspring of a parent to a new file, open it and remove the
" leading tabs.
function! Hoist(line)
let l:parent = FindParent(a:line)
if l:parent == 0
return
endif
call cursor(l:parent,1)
let l:firstline = l:parent+1
let l:childindent = Ind(l:firstline)
let l:lastline = FindLastChild(l:parent)
let l:filename = MakeTempFilename(l:parent)
echo l:firstline.",".l:lastline."w! ".l:filename
let l:folded = foldclosed(l:parent)
call cursor(l:parent,1)
normal zo
exe l:firstline.",".l:lastline."w! ".l:filename
call AddHoistFilename(l:parent)
silent write
" log what we did in case we need to recover manually
let l:doit = l:parent."write! >> .vo_hoist.".bufname(bufnr("%")).".log"
exe l:doit
let l:parentbuffer = bufnr("%")
"WARNING: switching files
let l:doit = "silent e +%s/^\\\\(\\\t\\\\)\\\\{"
let l:doit = l:doit.l:childindent."}// ".l:filename." | "
let l:doit = l:doit."let b:myParentBuffer = ".l:parentbuffer." | "
let l:doit = l:doit."let b:myParentLine = ".l:parent." | "
let l:doit = l:doit."call cursor(1,1)|"
let l:doit = l:doit."let b:hoisted = 1"
exe l:doit
silent write
endfunction
"}}}2
" DeleteChildren(line) {{{2
" Delete the existing offspring of a parent
function! DeleteChildren(line)
let l:parent = FindParent(a:line)
let l:firstline = l:parent+1
let l:lastline = FindLastChild(l:parent)
exe l:firstline.",".l:lastline."d"
endfunction
"}}}2
" MakeTabString(n) {{{2
" Return a string of n tabs
function! MakeTabString(n)
let l:string = ""
let l:i = 0
while l:i < a:n
let l:string = l:string."\t"
let l:i = l:i +1
endwhile
return l:string
endfunction
"}}}2
" AddChildren(line) {{{2
" Add left-justified children to parent. The filename is extracted from the
" end of the parent line. The parent is assumed to have no children at this
" point.
function! AddChildren(line)
let l:filename = ExtractHoistFilename(a:line)
if filereadable(l:filename) == 1
if a:line == line("$")
exe "read ".l:filename
if a:line != line("$")
exe a:line+1.",$"." s/^/".MakeTabString(Ind(a:line)+1)."/"
endif
else
exe a:line+1."ma v"
call cursor(a:line,1)
exe "read ".l:filename
if a:line+1 != line("'v")
exe a:line+1.",'v-1"." s/^/".MakeTabString(Ind(a:line)+1)."/"
endif
endif
endif
endfunction
"}}}2
" DeleteHoistFile(line) {{{2
" Delete a temporary filename from a parent line
function! DeleteHoistFile(line)
if g:hoistParanoia
return
endif
let l:filename = ExtractHoistFilename(a:line)
call delete(l:filename)
let l:filename = l:filename."~"
call delete(l:filename)
endfunction
"}}}2
" DeHoistThis(line) {{{2
" Remove the old children, add the new children and remove the __hoist data
" leading tabs from this file.
function! DeHoistThis(line)
let l:parent = FindParent(a:line)
let l:folded = foldclosed(l:parent)
call cursor(l:parent,1)
if l:folded == l:parent
normal zo
endif
call DeleteChildren(l:parent)
call AddChildren(l:parent)
call DeleteHoistFile(l:parent)
call DeleteHoistFilename(l:parent)
if l:folded == l:parent
normal zc
endif
endfunction
"}}}2
" DeHoist() {{{2
" Remove the old children, add the new children and remove the __hoist data
" leading tabs from the calling file.
function! DeHoist()
silent write
if bufexists(b:myParentBuffer) == 0
return
endif
let l:myParentBuffer = b:myParentBuffer
let l:myParentLine = b:myParentLine
bdelete
" Warning switching files
exe "buffer ".l:myParentBuffer
call cursor(l:myParentLine,1)
let l:parent = FindParent(l:myParentLine)
let l:folded = foldclosed(l:parent)
call cursor(l:parent,1)
" if l:folded == l:parent
" normal zo
" endif
normal zv
silent call DeleteChildren(l:parent)
silent call AddChildren(l:parent)
silent call DeleteHoistFile(l:parent)
silent call DeleteHoistFilename(l:parent)
if l:folded == l:parent
call cursor(l:parent,1)
normal zc
endif
silent write
endfunction
"}}}2
"}}}1
" Autocommands {{{1
au BufReadPost vo_hoist.*.otl cmap <buffer> wq call DeHoist()
au BufReadPost vo_hoist.*.otl cmap <buffer> qa call DeHoist()
au BufReadPost vo_hoist.*.otl cmap <buffer> q call DeHoist()
au BufReadPost vo_hoist.*.otl cmap <buffer> x call DeHoist()
au BufReadPost vo_hoist.*.otl nmap <buffer> ZZ :call DeHoist()<cr>
"}}}1
" vim600: set foldlevel=0 foldmethod=marker:

View file

@ -0,0 +1,310 @@
Checkboxes: votl_checkbox
What is it?
: Checkboxes is a plugin for project, task and list managment. It add an
: understanding of check boxes and percentage of task completion to Vim
: Outliner. It adds just three tags and six commands.
Installation
This should already be included in your Vim Outliner package
The newest, stable version is included with this package in the plugin directory
The pluging is already enabled in your ~/.vimoutlinerrc
Tag Syntax
[_] an unchecked item or incomplete task
[X] a checked item or complete task
[-] a canceled item - removes this item/branch from completion calculations
% a placeholder for percentage of completion
n% a percentage of completion, used in calculating parent completion
n%w a percentage with a weighting factor - e.g.: 10%2
[tag] a tag than can be replaced from a list of tags
[tag] also the default tag name - will be replaced by tag[0] of tag list[0]
[tag] [tag] tags must be separated by whitespace
Example g:cbTags (put in .vimoutlinerrc)
let g:cbTags = [
\ ['TODO','FEEDBACK','VERIFY','DELEGATED','HOLDING']
\,['Feature','Enhancement','Bug']
\,['Low','Normal','High','URGENT']
\,['Home','Lab','Work','Shopping']
\]
Checkbox Commands
: The default <localleader> for VimOutliner is ,, so we will use this
: leader in the command discussions.
,,cb Create a check box
: This works for the current heading or selected range of lines
: including folds. Visual selection of the range of headings works
: nicely. This command is currently not aware of body text. This
: limited awareness needs to be fixed before this plugin can be
: included in the standard Vim Outliner plugins.
,,cB Create a check box (even if one exists)
,,c% Create a checkbox and % symbol
: This works just like ,,cb but add a % symbol for use in completion
: calculations.
,,cp Create a checkbox and % symbol
: This works just like ,,cb but add a % symbol for use in completion
: calculations; even on childless headings.
,,cx Change check box state
: If there is a checkbox on the line the cursor is on, change its
: state. If it's checked, uncheck it and vice-versa. Then recompute
: the completion of the entire branch starting from the root parent.
,,cd Delete a checkbox
: Delete the left-most check box on the selected heading(s).
,,cz Compute completion
: Starting at the heading the cursor is on, recursively compute the
: completion level of all sub-headings.
,,c1 Set completion to 10%
: Set the completion to 10%, if a percentage already exists.
,,c2 Set completion to 20%
: Set the completion to 20%, if a percentage already exists.
,,c3 Set completion to 30%
: Set the completion to 30%, if a percentage already exists.
,,c4 Set completion to 40%
: Set the completion to 40%, if a percentage already exists.
,,c5 Set completion to 50%
: Set the completion to 50%, if a percentage already exists.
,,c6 Set completion to 60%
: Set the completion to 60%, if a percentage already exists.
,,c7 Set completion to 70%
: Set the completion to 70%, if a percentage already exists.
,,c8 Set completion to 80%
: Set the completion to 80%, if a percentage already exists.
,,c9 Set completion to 90%
: Set the completion to 90%, if a percentage already exists.
,,c+ Increment the completion by 10%
: Increment the completion by 10%, if a percentage already exists.
,,c- Decrement the completion by 10%
: Decrement the completion by 10%, if a percentage already exists.
,,ct Set tag to next tag in current tag list
: Set the tag under the cursor to the next tag in the list that
: contains the current tag. This command is not mapped if g:cbTags
: does not exist.
,,cT Set tag to next tag list
: Set the tag under the cursor to the first tag in the next tag list.
: This command is not mapped if g:cbTags does not exist.
How do I use it?
Start with a simple example
: Let's start with planning a small party; say a barbeque.
Make the initial outline
Barbeque
Guests
Bill and Barb
Larry and Louise
Marty and Mary
Food
Chicken
Ribs
Corn on the cob
Beverages
Soda
Iced Tea
Beer
Party Favors
Squirt guns
Hats
Name tags
Materials
Paper Plates
Napkins
Trash Containers
Add the check boxes
: This can be done by visually selecting them and typing ,,cb.
: When done, you should see this:
[_] Barbeque
[_] Guests
[_] Bill and Barb
[_] Larry and Louise
[_] Marty and Mary
[_] Food
[_] Chicken
[_] Ribs
[_] Corn on the cob
[_] Beverages
[_] Soda
[_] Iced Tea
[_] Beer
[_] Party Favors
[_] Squirt guns
[_] Hats
[_] Name tags
[_] Materials
[_] Paper Plates
[_] Napkins
[_] Trash Containers
Now check off what's done
: Checking off what is complete is easy with the ,,cx command.
: Just place the cursor on a heading and ,,cx it. Now you can see
: what's done as long as the outline is fully expanded.
[_] Barbeque
[X] Guests
[X] Bill and Barb
[X] Larry and Louise
[X] Marty and Mary
[_] Food
[X] Chicken
[X] Ribs
[_] Corn on the cob
[_] Beverages
[_] Soda
[X] Iced Tea
[X] Beer
[_] Party Favors
[_] Squirt guns
[_] Hats
[_] Name tags
[_] Materials
[X] Paper Plates
[_] Napkins
[X] Trash Containers
Getting more advanced
Now summarize what's done
: You can summarize what is done with the ,,cz command. Place the
: cursor on the 'Barbeque' heading and ,,cz it. The command will
: recursively process the outline and update the check boxes of
: the parent headlines. You should see:
: (Note: the only change is on the 'Guests' heading. It changed
: because all of its children are complete.)
[_] Barbeque
[X] Guests
[X] Bill and Barb
[X] Larry and Louise
[X] Marty and Mary
[_] Food
[X] Chicken
[X] Ribs
[_] Corn on the cob
[_] Beverages
[_] Soda
[X] Iced Tea
[X] Beer
[_] Party Favors
[_] Squirt guns
[_] Hats
[_] Name tags
[_] Materials
[X] Paper Plates
[_] Napkins
[X] Trash Containers
Add percentages for a better view
: You can get a much better view of what's going on, especially
: with collapsed headings, if you add percentages. Place a 0% on
: each heading that has children like this:
[_] 59% Barbeque
[X] 100% Guests
[X] Bill and Barb
[X] Larry and Louise
[X] Marty and Mary
[_] 66% Food
[X] Chicken
[X] Ribs
[_] Corn on the cob
[_] 66% Beverages
[_] Soda
[X] Iced Tea
[X] Beer
[_] 0% Party Favors
[_] Squirt guns
[_] Hats
[_] Name tags
[_] 66% Materials
[X] Paper Plates
[_] Napkins
[X] Trash Containers
Now compute the percentage of completion
: After adding the 0% symbols, place the cursor on the 'Barbeque'
: heading and execute ,,cz as before. Keep in mind that the
: recursive percentages are weighted. You should see:
[_] 59% Barbeque
[X] 100% Guests
[X] Bill and Barb
[X] Larry and Louise
[X] Marty and Mary
[_] 66% Food
[X] Chicken
[X] Ribs
[_] Corn on the cob
[_] 66% Beverages
[_] Soda
[X] Iced Tea
[X] Beer
[_] 0% Party Favors
[_] Squirt guns
[_] Hats
[_] Name tags
[_] 66% Materials
[X] Paper Plates
[_] Napkins
[X] Trash Containers
Complete a few more just for fun
: Mark Salad and Soda and recompute with ,,cz and you should
: see the ouline below.
:
: Try playng around with zc and zo to see the effects of opening
: and closing folds. Even if you place the cursor on 'Barbeque'
: and zo it, you still have a good understanding of how complete
: the project is.
[_] 66% Barbeque
[X] 100% Guests
[X] Bill and Barb
[X] Larry and Louise
[X] Marty and Mary
[_] 66% Food
[X] Chicken
[X] Ribs
[_] Corn on the cob
[X] 100% Beverages
[X] Soda
[X] Iced Tea
[X] Beer
[_] 0% Party Favors
[_] Squirt guns
[_] Hats
[_] Name tags
[_] 66% Materials
[X] Paper Plates
[_] Napkins
[X] Trash Containers
Getting way more advanced
Weighted Tasks
Here is a simple outline to track a project
[_] 22% Garage Upgrade
[_] 66% Clean out old junk
[X] 100% empty garage
[X] 100% sort junk
[_] 0% dispose of junk
[_] 0% Build Shelves
[_] 0% Oranize Junk
[_] 0% oranize saved junk
[_] 0% put junk on shelves
What if some tasks take more effort?
: Assume that you know that it will take twice as long to sort
: junk as it does to remove it from the garage or to dispose
: of it. This can be represented with weights. Notice the
: completion percentage of 'Clean out old junk' once a weight
: has been added. Each percentage can be weighted.
[_] 25% Garage Upgrade
[_] 75% Clean out old junk
[X] 100% empty garage
[X] 100%2 sort junk
[_] 0% dispose of junk
[_] 0% Build Shelves
[_] 0% Oranize Junk
[_] 0% oranize saved junk
[_] 0% put junk on shelves
Complex weighting
[_] 83% Garage Upgrade
[_] 75%2 Clean out old junk
[X] 100% empty garage
[X] 100%2 sort junk
[_] 0% dispose of junk
[X] 100%2 Build Shelves
[_] 66% Oranize Junk
[X] 100%2 oranize saved junk
[_] 0% put junk on shelves
Tags: Add more information to a heading
: Additional information and workflow information can easily
: be added to headings with tags. The command ,,ct can be used
: to cycle a tag among it sibling tags in g:cbTags. ,,cT can
: be be used to cycle a tag to a different list of sibling
: tags.
My Software Project
[_] 33% To Do
[X] Misspellings in documention [Bug] [High]
[_] Installation infects all networked systems with app [Feature] [Normal]
[_] Clean garage [Low] [@Home]

View file

@ -0,0 +1,475 @@
"######################################################################
"# VimOutliner Checkboxes
"# Copyright (C) 2003 by Noel Henson noel@noels-lab.com
"# The file is currently an experimental part of Vim Outliner.
"#
"# This program is free software; you can redistribute it and/or modify
"# it under the terms of the GNU General Public License as published by
"# the Free Software Foundation; either version 2 of the License, or
"# (at your option) any later version.
"#
"# This program is distributed in the hope that it will be useful,
"# but WITHOUT ANY WARRANTY; without even the implied warranty of
"# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
"# GNU General Public License for more details.
"######################################################################
" mappings {{{1
" insert a chechbox
noremap <silent><buffer> <localleader>cb :call SafelyInsertCheckBox()<cr>
noremap <silent><buffer> <localleader>c% :call SafelyInsertCheckBoxPercent()<cr>
noremap <silent><buffer> <localleader>cp :call SafelyInsertCheckBoxPercentAlways()<cr>
noremap <silent><buffer> <localleader>cB :call InsertCheckBox()<cr>
" delete a chechbox
noremap <silent><buffer> <localleader>cd :call DeleteCheckbox()<cr>
" switch the status of the box and adjust percentages
if !exists('g:vo_checkbox_fast_calc') || g:vo_checkbox_fast_calc == 1
" Use new and faster method
noremap <silent><buffer> <localleader>cx :call SwitchBox() <bar>call CalculateMyBranch(line("."))<cr>
noremap <silent><buffer> <localleader>c+ :call IncPercent(".") <bar>call CalculateMyBranch(line("."))<cr>
noremap <silent><buffer> <localleader>c- :call DecPercent(".") <bar>call CalculateMyBranch(line("."))<cr>
noremap <silent><buffer> <localleader>c1 :call SetPercent(".",10)<bar>call CalculateMyBranch(line('.'))<cr>
noremap <silent><buffer> <localleader>c2 :call SetPercent(".",20)<bar>call CalculateMyBranch(line('.'))<cr>
noremap <silent><buffer> <localleader>c3 :call SetPercent(".",30)<bar>call CalculateMyBranch(line('.'))<cr>
noremap <silent><buffer> <localleader>c4 :call SetPercent(".",40)<bar>call CalculateMyBranch(line('.'))<cr>
noremap <silent><buffer> <localleader>c5 :call SetPercent(".",50)<bar>call CalculateMyBranch(line('.'))<cr>
noremap <silent><buffer> <localleader>c6 :call SetPercent(".",60)<bar>call CalculateMyBranch(line('.'))<cr>
noremap <silent><buffer> <localleader>c7 :call SetPercent(".",70)<bar>call CalculateMyBranch(line('.'))<cr>
noremap <silent><buffer> <localleader>c8 :call SetPercent(".",80)<bar>call CalculateMyBranch(line('.'))<cr>
noremap <silent><buffer> <localleader>c9 :call SetPercent(".",90)<bar>call CalculateMyBranch(line('.'))<cr>
else
" Use the old method
noremap <silent><buffer> <localleader>cx :call SwitchBox() <bar>call NewHMD(FindRootParent(line(".")))<cr>
noremap <silent><buffer> <localleader>c+ :call IncPercent(".") <bar>call NewHMD(FindRootParent(line(".")))<cr>
noremap <silent><buffer> <localleader>c- :call DecPercent(".") <bar>call NewHMD(FindRootParent(line(".")))<cr>
noremap <silent><buffer> <localleader>c1 :call SetPercent(".",10)<bar>call NewHMD(FindRootParent(line(".")))<cr>
noremap <silent><buffer> <localleader>c2 :call SetPercent(".",20)<bar>call NewHMD(FindRootParent(line(".")))<cr>
noremap <silent><buffer> <localleader>c3 :call SetPercent(".",30)<bar>call NewHMD(FindRootParent(line(".")))<cr>
noremap <silent><buffer> <localleader>c4 :call SetPercent(".",40)<bar>call NewHMD(FindRootParent(line(".")))<cr>
noremap <silent><buffer> <localleader>c5 :call SetPercent(".",50)<bar>call NewHMD(FindRootParent(line(".")))<cr>
noremap <silent><buffer> <localleader>c6 :call SetPercent(".",60)<bar>call NewHMD(FindRootParent(line(".")))<cr>
noremap <silent><buffer> <localleader>c7 :call SetPercent(".",70)<bar>call NewHMD(FindRootParent(line(".")))<cr>
noremap <silent><buffer> <localleader>c8 :call SetPercent(".",80)<bar>call NewHMD(FindRootParent(line(".")))<cr>
noremap <silent><buffer> <localleader>c9 :call SetPercent(".",90)<bar>call NewHMD(FindRootParent(line(".")))<cr>
endif
" calculate the proportion of work done on the subtree
noremap <silent><buffer> <localleader>cz :call NewHMD(FindRootParent(line(".")))<cr>
" tag list key mappings
if exists("g:cbTags")
noremap <silent><buffer> <localleader>ct :call SetNextTag()<cr>
noremap <silent><buffer> <localleader>cT :call SetNextList()<cr>
endif
"}}}1
" Load guard for functions {{{1
if exists('s:loaded')
finish
endif
let s:loaded = 1
" InsertCheckBox() {{{1
" Insert a checkbox at the beginning of a header without disturbing the
" current folding.
function! InsertCheckBox()
let @x = "[_] "
normal! ^"xP
endfunction
"}}}1
" Safely InsertCheckBox() {{{1
" Insert a checkbox at the beginning of a header without disturbing the
" current folding only if there is no checkbox already.
function! SafelyInsertCheckBox()
if match(getline("."),"^\t\t*\[<>:;|\]") != -1
return
endif
if match(getline("."),"[\[X_\]]") == -1
let @x = "[_] "
normal! ^"xP
endif
endfunction
"}}}1
" Safely InsertCheckBoxPercent() {{{1
" Insert a checkbox and % sign at the beginning of a header without disturbing
" the current folding only if there is no checkbox already.
function! SafelyInsertCheckBoxPercent()
if match(getline("."),"^\t\t*\[<>:;|\]") != -1
return
endif
if match(getline("."), "[\[X_\]]") == -1
if Ind(line(".")+1) > Ind(line("."))
let @x = "[_] % "
else
let @x = "[_] "
endif
normal! ^"xP
endif
endfunction
"}}}1
" Safely InsertCheckBoxPercentAlways() {{{1
" Insert a checkbox and % sign at the beginning of a header without disturbing
" the current folding only if there is no checkbox already. Include the
" checkbox even on childless headings.
function! SafelyInsertCheckBoxPercentAlways()
if match(getline("."),"^\t\t*\[<>:;|\]") != -1
return
endif
if match(getline("."), "[\[X_\]]") == -1
let @x = "[_] % "
normal! ^"xP
endif
endfunction
"}}}1
" SetBox(char) {{{1
" Switch the state of the checkbox on the current line.
function! SetBox(char)
substitute/\[.\]/\="[".expand(a:char)."]"/
if a:char == 'X'
call SetPercent(".",100)
elseif a:char == '_'
call SetPercent(".",0)
endif
endfunction
"}}}1
" SwitchBox() {{{1
" Switch the state of the checkbox on the current line.
function! SwitchBox()
let l:line = getline(".")
let questa = strridx(l:line,"[_]")
let questb = strridx(l:line,"[X]")
if (questa != -1) || (questb != -1)
if (questa != -1)
call SetBox('X')
else
call SetBox('_')
endif
endif
endfunction
"}}}1
" DeleteCheckbox() {{{1
" Delete a checkbox if one exists
function! DeleteCheckbox()
let questa = strridx(getline("."),"[_]")
let questb = strridx(getline("."),"[X]")
if (questa != -1) || (questb != -1)
if (questa != -1)
substitute/\(^\s*\)\[_\] \(.*\)/\1\2/
else
substitute/\(^\s*\)\[X\] \(.*\)/\1\2/
endif
endif
endfunction
"}}}1
" Ind(line) {{{1
" Return the index of the line.
" Remove it when using the new version of VO
function! Ind(line)
return indent(a:line) / &tabstop
endf
" FindMyParent(line) {{{1
" returns the line number of the parent of the current node
function! FindMyParent(line)
let l:mylevel = Ind(a:line)
if l:mylevel == 0
return (a:line)
endif
let l:i = a:line
while Ind(l:i) >= l:mylevel
let l:i -= 1
endwhile
return l:i
endf
" FindRootParent(line) {{{1
" returns the line number of the root parent for any child
function! FindRootParent(line)
if Ind(a:line) == 0
return (a:line)
endif
let l:i = a:line
while l:i > 1 && Ind(l:i) > 0
let l:i = l:i - 1
endwhile
return l:i
endf
" LimitPercent(percent) {{{1
" Limits percentage values to between 0 and 100
function! LimitPercent(val)
if a:val > 100
return 100
elseif a:val < 0
return 0
else
return a:val
endif
endf
" GetPercent(line) {{{1
" Get the percent complete from a line
function! GetPercent(line)
let l:proportion = 0
let mbegin=match(getline(a:line), " [0-9]*%")
if mbegin
let mend=matchend(getline(a:line), " [0-9]*%")
let l:proportion=getline(a:line)[mbegin+1 : mend-1]
let l:proportion=str2nr(l:proportion)
endif
return l:proportion
endf
" SetPercent(line,proportion) {{{1
" Set the percent complete for a line
function! SetPercent(line,proportion)
let mbegin=match(getline(a:line), " [0-9]*%")
if mbegin
call setline(a:line,substitute(getline(a:line)," [0-9]*%"," ".a:proportion."%",""))
endif
endf
" IncPercent(line) {{{1
" Increments the percent doneness by 10%
function! IncPercent(line)
if match(getline(a:line), " [0-9]*%")
call SetPercent(a:line,LimitPercent(GetPercent(a:line)+10))
endif
endf
" DecPercent(line) {{{1
" Decrements the percent doneness by 10%
function! DecPercent(line)
if match(getline(a:line), " [0-9]*%")
let l:percent = GetPercent(a:line)
call setline(a:line,substitute(getline(a:line),"\\[X\\]","[_]",""))
call SetPercent(a:line,LimitPercent(l:percent-10))
endif
endf
" ComputePW(line,count,done) {{{1
" Computes proportion and weight of a node
" Returns (proportion,weight) proportion could be a flag of -1
function! ComputePW(line,count,done)
let l:proportion=0
let l:haspercent = 0
" get the percent
let mbegin=match(getline(a:line), " [0-9]*%")
if mbegin != -1
let l:haspercent = 1
let mend=matchend(getline(a:line), " [0-9]*%")
let l:proportion=str2nr(getline(a:line)[mbegin+1 : mend-1])
endif
" get the weight
let l:weight=1
let mbegin=match(getline(a:line), "%[0-9]\\+")
if mbegin != -1
let mend=matchend(getline(a:line), "%[0-9]\\+\s")
let l:weight=str2nr(getline(a:line)[mbegin+1 : mend-1])
endif
" compute the proportion
if a:count>0
let l:proportion = ((a:done*100)/a:count)/100
elseif match(getline(a:line),"\\[X\\]") != -1
let l:proportion = 100
elseif match(getline(a:line),"\\[-\\]") != -1
let l:weight = 0
endif
" update non-ignored items
let l:questa = strridx(getline(a:line),"[-]")
if l:questa == -1
call setline(a:line,substitute(getline(a:line)," [0-9]*%"," ".l:proportion."%",""))
endif
" Limit proportion to 0 or 100 if there is not a percentage sign
if !haspercent && (!exists('g:vo_checkbox_fast_calc') || g:vo_checkbox_fast_calc == 1)
let l:proportion = l:proportion == 100 ? l:proportion : 0
endif
" update the completion
if l:questa != -1
return [100,l:weight]
elseif l:proportion == 100
call setline(a:line,substitute(getline(a:line),"\\[_\\]","[X]",""))
return [100,l:weight]
elseif l:proportion == 0 && a:count == 0
if match(getline(a:line),"\\[X\\]") != -1
return [100,l:weight]
elseif match(getline(a:line),"\\[_\\]") != -1
return [0,l:weight]
else
return [-1,l:weight]
endif
else
call setline(a:line,substitute(getline(a:line),"\\[X\\]","[_]",""))
return [l:proportion,l:weight]
endif
endf
" CalculateMyChildren(line) {{{1
" Calculates percent completion only on the immediate children of the
" parent specified by line.
function! CalculateMyChildren(line)
let l:done = 0
let l:count = 0
let l:line = a:line + 1
let l:mylevel = Ind(a:line)
let l:childlevel = l:mylevel+1
while l:mylevel < Ind(l:line) " I have children
if l:childlevel == Ind(l:line)
let l:childstat = ComputePW(l:line,0,0)
let l:childdoneness = l:childstat[0] * l:childstat[1]
if l:childdoneness >= 0
let l:done += l:childdoneness
let l:count += l:childstat[1]
endif
endif
let l:line += 1
endwhile
return ComputePW(a:line,l:count,l:done) " returns with (proportion,weight)
endf
" CalculateMyBranch(line) {{{1
" Calculate from the leaf, up unlke NewHMD
function! CalculateMyBranch(line)
call NewHMD(a:line) " compute and adjust my children, if I have any
let l:line = a:line
while Ind(l:line) > 0
let l:line = FindMyParent(l:line)
call CalculateMyChildren(l:line)
endwhile
endf
" NewHMD(line) {{{1
" (New How Many Done)
" Calculates proportion of already done work in the subtree
" Recursive, but slow because it computes an entire branch of an outline
" from level 1.
" Returns (proportion,weight) proportion could be a flag of -1
function! NewHMD(line)
let l:done = 0
let l:count = 0
let l:line = a:line+1
let l:mylevel = Ind(a:line)
let l:childlevel = l:mylevel+1
while l:mylevel < Ind(l:line) " I have children
if l:childlevel == Ind(l:line)
let l:childstat = NewHMD(l:line)
let l:childdoneness = l:childstat[0] * l:childstat[1]
if l:childdoneness >= 0
let l:done += l:childdoneness
let l:count += l:childstat[1]
endif
endif
let l:line += 1
endwhile
return ComputePW(a:line,l:count,l:done) " returns with (proportion,weight)
endf
" Experimental Heading Tags {{{1
if !exists('g:cbTags')
finish
endif
" GetTag() {{{2
" return the tag word under the cursor
function! GetTag()
let word = expand("<cWORD>")
if word[0] == '[' && word[-1:] == ']'
return word[1:-2]
endif
return ""
endfunction
" WhereInLists(word) {{{2
" return a single-entry list with a pair of values [listIndex,tagIndex]
" return -1,-1 if tag word not found
function! WhereInLists(word)
let lidx = 0
for list in g:cbTags
let tidx = index(list,a:word)
if tidx >= 0
return [lidx,tidx]
endif
let lidx += 1
endfor
return [-1,-1]
endfunction
" NextTagIdx(lidx,tidx) {{{2
" return the index of the next tag in the current list
function! NextTagIdx(lidx,tidx)
if a:tidx >= 0
let llen = len(g:cbTags[a:lidx])
let tidx = (a:tidx + 1)%llen
endif
return tidx
endfunction
" GetNextTag(word) {{{2
" return the next tag word (from a:word) in the list
function! GetNextTag(word)
if a:word == 'tag'
return g:cbTags[0][0]
endif
let liti = WhereInLists(a:word)
if liti[1] == -1
return ""
endif
let liti[1] = NextTagIdx(liti[0],liti[1])
let nextword = g:cbTags[liti[0]][liti[1]]
return nextword
endfunction
" SetNextTag() {{{2
" set the current tag to the next tag in the same list
" this is circular, the last tag will roll to the first tag
function! SetNextTag()
let oldtag = GetTag()
let newtag = GetNextTag(oldtag)
if newtag == ""
return
endif
let sub = "normal!ci[".newtag
exec sub
endfunction
" NextListIdx(lidx) {{{2
" return the index of the next list
function! NextListIdx(lidx)
if a:lidx >= 0
let llen = len(g:cbTags)
let lidx = (a:lidx + 1)%llen
endif
return lidx
endfunction
" GetNextList(word) {{{2
" return the next tag word (from a:word) in the list
function! GetNextList(word)
if a:word == 'tag'
return g:cbTags[0][0]
endif
let liti = WhereInLists(a:word)
if liti[1] == -1
return ""
endif
let liti[0] = NextListIdx(liti[0])
let nextword = g:cbTags[liti[0]][0]
return nextword
endfunction
" SetNextList() {{{2
" set the current tag to the first tag in the next list
" this is circular, the last list will roll to the first list
function! SetNextList()
let oldtag = GetTag()
let newtag = GetNextList(oldtag)
if newtag == ""
return
endif
let sub = "normal!ci[".newtag
exec sub
endfunction
" vim600: set foldlevel=0 foldmethod=marker:

View file

@ -0,0 +1,139 @@
"######################################################################
"# VimOutliner Clock
"# Copyright (C) 2011 by Daniel Carl
"# The file is currently an experimental part of Vim Outliner.
"#
"# This program is free software; you can redistribute it and/or modify
"# it under the terms of the GNU General Public License as published by
"# the Free Software Foundation; either version 2 of the License, or
"# (at your option) any later version.
"#
"# This program is distributed in the hope that it will be useful,
"# but WITHOUT ANY WARRANTY; without even the implied warranty of
"# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
"# GNU General Public License for more details.
"######################################################################
" Shortlog{{{1
"
" This plugin of vimoutliner allow the simple tracking of times and the
" calculation of them in seconds, minutes, hours or days
" Exmaple :
" May -> 64.75 h
" Working time week 51 -> 46.00 h
" Working time week 52 -> 18.75 h
" 2010-05-03 [08:00:00 -- 17:45:00] -> 9.75 h
" 2010-05-04 [09:00:00 -- 18:00:00] -> 9.00 h
"
" TODO: Use better date calculation to track also time around 00:00
" [23:00:00 -- 03:00:00] that will at the time lead to negative
" hours.
" TODO: change the clocking format so that times over several days could
" be calculated - [2010-05-03 08:00:00 -- 2010-05-23 26:30:45] or
" a shorter dateformat - this seems to need a more complex
" datehandling
" TODO: write a helppage for the vimoutliner that describes the votd_clock
" TODO: allow the user to select his own dateformat
"}}}1
" ClockStart(space) {{{1
" Insert a space, then the datetime.
function! ClockStart(space)
let @x = ""
if a:space == 1
let @x = " "
endif
let @x = @x . strftime("%Y-%m-%d [%T -- %T] ->")
normal! "xp
endfunction
"}}}1
" ClockStop() {{{1
" Insert a space, then the datetime.
function! ClockStop()
if match(getline("."), "\\[.* -- .*\\]\\s*-\>") != -1
call setline(".",substitute(getline(".")," -- .*]\\s*-\>"," -- ".strftime("%T] ->"),""))
endif
endfunction
"}}}1
" CalculateSeconds(str) {{{1
" Calculates the seconds between the start and the end time.
function! CalculateSeconds(str)
let l:parts = split(a:str,"\ --\ ")
let l:startparts = split(l:parts[0],":")
let l:endparts = split(l:parts[1],":")
let l:seconds = (str2nr(l:endparts[2]) - str2nr(l:startparts[2]))
let l:seconds = (str2nr(l:endparts[1]) - str2nr(l:startparts[1])) * 60 + l:seconds
let l:seconds = (str2nr(l:endparts[0]) - str2nr(l:startparts[0])) * 3600 + l:seconds
return l:seconds
endfunction
" }}}1
" CalculateDuration() {{{1
" insert date time
function! CalculateDuration(line)
let l:seconds=0
let l:count=0
let l:i = 1
while Ind(a:line) < Ind(a:line+l:i)
if (Ind(a:line)+1) == (Ind(a:line+l:i))
let l:childseconds = CalculateDuration(a:line+l:i)
if l:childseconds >= 0
let l:seconds = l:seconds + l:childseconds
let l:count = l:count+1
endif
endif
let l:i = l:i+1
endwhile
" if no childs found calculate the seconds for the line
let l:lineString = getline(a:line)
if match(l:lineString,"\\s*-\>") != -1
let l:times = matchstr(l:lineString,"\\[.* -- .*\\]\\s*-\>")
if l:times != ""
" calculate the real time difference
let l:seconds = CalculateSeconds(substitute(l:times,"\\[\\(.*\\)\\]","\\1",""))
endif
" don't add summarized time to text lines
if match(l:lineString,"^\t*[;:<>]") == -1
if match(l:lineString," -\> [0-9 .]*s") != -1
call setline(a:line,substitute(l:lineString," -\>.*s"," -> ".l:seconds." s",""))
elseif match(getline(a:line)," -\> [0-9 .]*m") != -1
call setline(a:line,substitute(l:lineString," -\>.*m"," -> ".printf("%.2f",l:seconds/60.0)." m",""))
elseif match(getline(a:line)," -\> [0-9 .]*d") != -1
call setline(a:line,substitute(l:lineString," -\>.*"," -> ".printf("%.2f",(l:seconds/86400.0))." d",""))
else
call setline(a:line,substitute(l:lineString," -\>.*"," -> ".printf("%.2f",(l:seconds/3600.0))." h",""))
endif
endif
" else
" return -1
endif
return l:seconds
endf
"}}}1
" FindRootParent(line) {{{1
" returns the line number of the root parent for any child
function! FindRootParent(line)
if Ind(a:line) == 0
return (a:line)
endif
let l:i = a:line
while l:i > 1 && Ind(l:i) > 0
let l:i = l:i - 1
endwhile
return l:i
endf
"}}}1
" UpdateTimes() {{{1
" initiates the update of all times in the tree where the cursur is located
function! UpdateTimes()
call CalculateDuration(FindRootParent(line(".")))
endf
"}}}1
" Mappings {{{1
nmap <silent> <buffer> <localleader>cs $:call ClockStart(1)<cr>
imap <silent> <buffer> <localleader>cs ~<esc>x:call ClockStart(0)<cr>a
nmap <silent> <buffer> <localleader>cS $:call ClockStop()<cr>:call UpdateTimes()<cr>
imap <silent> <buffer> <localleader>cS ~<esc>x:call ClockStop()<cr>:call UpdateTimes()<cr>i
nmap <silent> <buffer> <localleader>cu $:call UpdateTimes()<cr>
"}}}1
" The End
" vim600: set foldmethod=marker foldlevel=0:

View file

@ -0,0 +1,157 @@
"######################################################################
"# VimOutliner Format plugin
"# Copyright (C) 2011 by Jostein Berntsen
"# The file is currently an experimental part of Vim Outliner.
"#
"# This program is free software; you can redistribute it and/or modify
"# it under the terms of the GNU General Public License as published by
"# the Free Software Foundation; either version 2 of the License, or
"# (at your option) any later version.
"#
"# This program is distributed in the hope that it will be useful,
"# but WITHOUT ANY WARRANTY; without even the implied warranty of
"# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
"# GNU General Public License for more details.
"######################################################################
" Documentation{{{1
"
" This script inserts bullets, dashes, and arrows in front of lines, including
" VO body text. To insert markers for several lines, select the lines with V
" and execute the mapping. Indents will be kept as they are.
" You can also use the MakeText function to make body text from headers or
" lists.
"
" There are also functions for
" 1) Aligning text in a paragraph to a level 1 header
" 2) Insert checkboxes for all headings in a paragraph
" 3) Indent text in a paragraph/branch to the right
" 4) Indent text in a paragraph/branch to the left
"
"}}}1
" Load guard for functions {{{1
if exists("g:loaded_votl_format") || &cp
finish
endif
let g:loaded_votl_format= 1
let s:keepcpo = &cpo
set cpo&vim
" Mappings {{{1
""" Command mappings
"
" Insert bullets on selected text
map <buffer><localleader><F1> :call InsertBullet()<cr>
" Insert dashes on selected text
map <buffer><localleader><F2> :call InsertDash()<cr>
" Insert arrows on selected text
map <buffer><localleader><F3> :call InsertArrow()<cr>
" Insert colons before selected text
map <buffer><localleader><F4> :call MakeText()<cr>
" Align text in a paragraph and indent 1 level
map <buffer><localleader><F5> V}k:le<cr>V}>
" Insert checkboxes for text lines in a paragraph
map <buffer><localleader><F6> V}k,,cb
" Indent text in a paragraph 1 level to the right and keep indentation
map <buffer><localleader><F7> :call VOindentright()<cr>
" Indent text in a paragraph 1 level to the level and keep indentation
map <buffer><localleader><F8> :call VOindentleft()<cr>
"}}}1
" InsertBullet() {{{1
" Insert bullets on selected text.
function! InsertBullet()
if match(getline("."),"^[\t]*:") != -1
let @x = ": * "
normal! ^"xPex
else
let @x = "* "
normal! ^"xP
endif
endfunction
"}}}1
" InsertDash() {{{1
" Insert dashes on selected text.
function! InsertDash()
if match(getline("."),"^[\t]*:") != -1
let @x = ": - "
normal! ^"xPex
else
let @x = "- "
normal! ^"xP
endif
endfunction
"}}}1
" InsertArrow() {{{1
" Insert arrows on selected text.
function! InsertArrow()
if match(getline("."),"^[\t]*:") != -1
let @x = ": --> "
normal! ^"xPex
else
let @x = "--> "
normal! ^"xP
endif
endfunction
"}}}1
" MakeText() {{{1
" Make selected lines body text.
function! MakeText()
let @x = ":"
normal! ^"xP
endfunction
"}}}1
" VOindentright() {{{1
" Indent branch 1 level to the right.
function! VOindentright()
let thisLine = line(".")
if (foldclosed(thisLine) == -1) && IsParent(thisLine)
normal! zc
let fold_cursor = getpos(".")
normal! >>
let get_cursor = getpos(".")
call setpos('.',fold_cursor)
normal! zo
call setpos('.',get_cursor)
set foldlevel=3
else
normal! >>
endif
endfunction
"}}}1
" VOindentleft() {{{1
" Indent branch 1 level to the left.
function! VOindentleft()
let thisLine = line(".")
if (foldclosed(thisLine) == -1) && IsParent(thisLine)
normal! zc
let fold_cursor = getpos(".")
normal! <<
let get_cursor = getpos(".")
call setpos('.',fold_cursor)
normal! zo
call setpos('.',get_cursor)
set foldlevel=3
else
normal! <<
endif
endfunction
"}}}1
" The End
" vim600: set foldmethod=marker foldlevel=0:

View file

@ -0,0 +1,219 @@
"######################################################################
"# VimOutliner GTD
"# Copyright (C) 2003 by Noel Henson noel@noels-lab.com
"# The file is currently an experimental part of Vim Outliner.
"#
"# This program is free software; you can redistribute it and/or modify
"# it under the terms of the GNU General Public License as published by
"# the Free Software Foundation; either version 2 of the License, or
"# (at your option) any later version.
"#
"# This program is distributed in the hope that it will be useful,
"# but WITHOUT ANY WARRANTY; without even the implied warranty of
"# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
"# GNU General Public License for more details.
"######################################################################
" InsertElem() {{{1
" Insert a elem without disturbing the current folding.
function! InsertElem(elem)
let @x = a:elem
normal! ^"xP
endfunction
"}}}1
" DeleteCheckbox() {{{1
" Delete a checkbox with all its marks (team/completion)
" if one exists
function! DeleteCheckbox()
:-1/\[[X_?-]\%(,[<>=] [^]]*\)\?\] \%(\d*%\)\?/s///
endfunction
"}}}1
let g:reOutline = "^\t\+\[<>:;|\]"
let g:reBox = "[\[X_?-\]"
let g:reTeam = ",\[<>=\] \[^]\]*"
let g:rePercent = "\\d*% "
" Safe Insert *Box() {{{1
" Insert a element at the beginning of a header without disturbing the
" current folding only if there is no element already.
function! InsertSwitch(elem)
if match(getline("."),g:reOutline) != -1
return
endif
" if match(getline("."),"[".a:elem."\\%(".g:reTeam."\\)\\?"."] ") != -1
" return
" endif
if match(getline("."),g:reBox."\\%(".g:reTeam."\\)\\?"."] ") != -1
substitute/\[./\="[".a:elem/
else
call InsertElem("[".a:elem."] ")
endif
endfunction
"}}}1
" Safe Add Team Indicator() {{{1
" Insert a work with("="), for (">"), waiting for ("<")
" in a checkbox (created if needed) at the beginning of a header
" without disturbing the current folding.
function! SafeAddTeam(elem)
if match(getline("."),g:reOutline) != -1
return
endif
if match(getline("."),g:reBox.g:reTeam."] ") != -1
return
endif
if match(getline("."),g:reBox."] ") != -1
substitute/\[[X_?-]/\=submatch(0).",".a:elem." "/
else
call InsertElem("[_".",".a:elem." "."] ")
endif
endfunction
"}}}1
" Safe InsertCheckBoxPercent() {{{1
" Insert a checkbox and % sign at the beginning of a header without disturbing
" the current folding
function! SafeAddPercent()
" if Ind(line(".")+1) <= Ind(line("."))
" return
" endif
if match(getline("."),g:reOutline) != -1
return
endif
if match(getline("."),g:reBox."\\%(".g:reTeam."\\)\\?"."] ".g:rePercent) != -1
return
endif
if match(getline("."),g:reBox."\\%(".g:reTeam."\\)\\?"."] ") != -1
substitute/\[[^]]\+\] /&% /
else
call InsertElem("[_] % ")
endif
endfunction
"}}}1
" Ind(line) {{{1
" Return the index of the line.
" Remove it when using the new version of VO
function! Ind(line)
return indent(a:line) / &tabstop
endf
"}}}1
" FindRootParent(line) {{{1
" returns the line number of the root parent for any child
function! FindRootParent(line)
let l:i = a:line
while l:i > 1 && Ind(l:i) > 0
let l:i -= 1
endwhile
return l:i
endf
"}}}1
" NewHMD(line) {{{1
" (How Many Done)
" Calculates proportion of already done work in the subtree
function! NewHMD(line)
let l:done = 0 " checkboxes
let l:count = 0 " number of elems : for %
let l:i = 1 " line counting
let l:proportion = 0 " % : for checkboxes (<100 or 100 ?) and %
let l:lineindent = Ind(a:line)
" look recursively
while Ind(a:line+l:i) > l:lineindent
if Ind(a:line+l:i) == l:lineindent + 1
let l:childdoneness = NewHMD(a:line+l:i)
if l:childdoneness >= 0
let l:done += l:childdoneness
let l:count += 1
endif
" echomsg "->".a:line."/".(a:line+l:i)."/ [".l:childdoneness."]-[".l:count."]"
else
" echomsg "(skip) ->".a:line."/".(a:line+l:i)
endif
let l:i += 1
endwhile
" update %
if l:count > 0
" echomsg "->".a:line." proportion ".l:proportion
let l:proportion = ((l:done * 100)/l:count)/100
endif
call setline(a:line,substitute(getline(a:line)," [0-9]*%"," ".l:proportion."%",""))
"
" update checkboxes
"
" everything under is done, toggle
if l:proportion == 100
" echomsg "->".a:line." proportion 100."
call setline(a:line,substitute(getline(a:line),"[.","[X",""))
return 100
endif
if l:proportion == 0 && l:count == 0
" done or skipped
if match(getline(a:line),"\[[X-][\],]") != -1
" echomsg "->".a:line." proportion is X or -."
return 100
endif
" not done or questionnable
if match(getline(a:line),"\[[_\?][\],]") != -1
" echomsg "->".a:line." proportion is _ or ?."
return 0
endif
" unknown status for line
" echomsg "->".a:line." proportion is unknown."
return -1
endif
" we have not done tasks, undo 'mark as done'
if match(getline(a:line),"\[[X][\],]") != -1
call setline(a:line,substitute(getline(a:line),"[.","[_",""))
endif
" echomsg "->".a:line." proportion is revert?. [".l:proportion."] / [".l:count."]"
return l:proportion
endf
"}}}1
" mappings {{{1
" gtd addings
" work alone
noremap <buffer> <localleader>cb :call InsertSwitch("_")<cr>
noremap <buffer> <localleader>cq :call InsertSwitch("?")<cr>
noremap <buffer> <localleader>cD :call InsertSwitch("-")<cr>
noremap <buffer> <localleader>cx :call InsertSwitch("X")<cr>:call NewHMD(FindRootParent(line(".")))<cr>
" noremap <buffer> <localleader>cx :call InsertSwitch("X")<cr>
" team work
noremap <buffer> <localleader>cw :call SafeAddTeam("<")<cr>
noremap <buffer> <localleader>cf :call SafeAddTeam(">")<cr>
noremap <buffer> <localleader>c= :call SafeAddTeam("=")<cr>
" completion
noremap <buffer> <localleader>c% :call SafeAddPercent()<cr>
" forced mapping
noremap <buffer> <localleader>gb :call InsertElem("[_] ")<cr>
noremap <buffer> <localleader>gq :call InsertElem("[?] ")<cr>
noremap <buffer> <localleader>gD :call InsertElem("[-] ")<cr>
noremap <buffer> <localleader>gx :call InsertElem("[X] ")<cr>
noremap <buffer> <localleader>gw :call InsertElem("[_,< ] ")<cr>
noremap <buffer> <localleader>gf :call InsertElem("[_,> ] ")<cr>
noremap <buffer> <localleader>g= :call InsertElem("[_,= ] ")<cr>
noremap <buffer> <localleader>g% :call InsertElem("[_] % ")<cr>
" delete a chechbox
noremap <buffer> <localleader>cd :call DeleteCheckbox()<cr>
" calculate the proportion of work done on the subtree
noremap <buffer> <localleader>cz :call NewHMD(FindRootParent(line(".")))<cr>
"}}}1
" vim600: set foldlevel=0 foldmethod=marker:

View file

@ -0,0 +1,56 @@
VO Math
Syntax
{} contains formalae to compute
{f0;f1} semicolon separates formulae
There is a respective order between the formulae and variables in a heading
{math0;math1;math2} var0=result0 var1=result1 var2=result2
ordering
Math can come before or after variables
{3+5;3*5} sum=8 product=15
sum=8 product=15 {3+5;3*5}
name=n name become a named result for passing to a parent
A=n B=n space separates variables
A=n test B=n space separates variables, interspersed words are allowed
Math Examples
Example 1: one-line math {3*5} result=15
Example 2: one-line floating-point math {3*5.0} result=15.0
Example 3: Simple math with child variables {A*B+C} result=5.0
Item 1 A=2
Item 2 B=2
Item 3 C=3.0
Example 4: Automatic summing {bonk} total=6.0
Item 1 bonk=1
Item 2 bonk=2
Item 3 bonk=3.0
Example 5: Multiple equations {A*B;B*C;C*A;A+B+C} AB=2.0 BC=6.0 CA=3.0 sumABC=6.0
Item 1 A=1
Item 2 B=2
Item 3 C=3.0
Example 6: Multiple trees {Labor;Materials;Total} Labor=1222.5 Materials=225.0 Total=1447.5
Project 1 {Labor;Materials;Labor+Materials} Labor=747.5 Materials=110.0 Total=857.5
Task 1 {Hours*Rate;Materials} Labor=500.0 Materials=100.0
Hours=10
Rate=50
Materials=100
Task 2 {Hours*Rate;Materials} Labor=247.5 Materials=10.0
Hours=4.5
Rate=55
Materials=10
Project 2 {Labor;Materials;Labor+Materials} Labor=475.0 Materials=115.0 Total=590.0
Task 1 {Hours*Rate;Materials} Labor=300.0 Materials=70.0
Hours=6
Rate=50
Materials=70
Task 2 {Hours*Rate;Materials} Labor=175.0 Materials=45.0
Hours=3.5
Rate=50
Materials=45
Example 7: Reversal of results and maths AB=2.0 BC=6.0 CA=3.0 sumABC=6.0 {A*B;B*C;C*A;A+B+C}
Item 1 A=1
Item 2 B=2
Item 3 C=3.0
Example 8: Scientific notation: {10000.0*100000} result=1.0e9
Example 9: Trigonometry: {sin(pi/4)} theta=0.707107
pi=3.1415926
Example 10: Base conversion: {printf("0x%x",65)} hex='0x41'
Example 11: Vim internal state: {&ts;&foldlevel} tabstop=4 foldlevel=99999

View file

@ -0,0 +1,286 @@
"######################################################################
"# VimOutliner Outline Math
"# Copyright (C) 2014 by Noel Henson noelwhenson@gmail.com
"# The file is currently an experimental part of Vim Outliner.
"#
"# This program is free software; you can redistribute it and/or modify
"# it under the terms of the GNU General Public License as published by
"# the Free Software Foundation; either version 2 of the License, or
"# (at your option) any later version.
"#
"# This program is distributed in the hope that it will be useful,
"# but WITHOUT ANY WARRANTY; without even the implied warranty of
"# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
"# GNU General Public License for more details.
"######################################################################
" Naming Conventions ################################################## {{{1
" lnum: line number
" line: string from a line
" vars: dictionary of variables, key:value pairs
"
" Common Functions #################################################### {{{1
" These functions have broader application scope than those specific to
" performing math on outline trees. Perhaps even adding math to VO tables.
"
" MyLineage(lnum) {{{2
" return a list of ancestors in order of youngest-first
" eg:
" 9 A
" 10 B
" 11 C
" 12 D
" MyLineage(12)
" [11,10,9]
function! MyLineage(lnum)
let lineage = []
let lnum = a:lnum
let indent = Ind(lnum)
if indent == 0
return lineage
endif
let parentIndent = indent - 1
while (parentIndent >= 0) && (lnum >= 0)
while (indent > parentIndent) && (lnum >= 0)
let lnum -= 1
let indent = Ind(lnum)
endwhile
let lineage += [lnum]
let parentIndent -= 1
endwhile
return lineage
endfunction
" MyChildren(lnum) {{{2
" return a list of immediate children from the specificed line
function! MyChildren(lnum)
let children = []
let parentInd = Ind(a:lnum)
let childInd = parentInd + 1
let last = line("$")
let lnum = a:lnum + 1
let lnumInd = Ind(lnum)
while (lnum <= last) && (parentInd < lnumInd)
if lnumInd == childInd
let children += [lnum]
endif
let lnum += 1
let lnumInd = Ind(lnum)
endwhile
return children
endfunction
" RootParents() {{{2
" return a list of all root nodes (indent level 0)
function! RootParents()
let parents = []
let lnum = 1
let lines = line("$")
while lnum <= lines
let ind = Ind(lnum)
if ind == 0
let parents += [lnum]
endif
let lnum += 1
endwhile
return parents
endfunction
" FindMath(string) {{{2
" location of first character of match, -1 if not
" notation: ...{...}...=number...
" function! FindMath(string)
" return match(a:string,'{.*}.*=-\?[0-9]\+\(.[0-9]\+\)\+\([eE][-+]\?[0-9]\+\)\?')
" endfunction
" the below is faster!
" function! FindMath(string)
" return match(a:string,'{.*}.*=-\?[0-9]')
" endfunction
" the below is even faster
" and allows for formulae to be placed at the end of a heading
function! FindMath(string)
if match(a:string,'=') != -1
return match(a:string,'{.*}')
else
return -1
endif
endfunction
" GetMathFromString(string) {{{2
" returns a list of formulae in a string, in the order they were listed
" returns an empty list if none
" notation: {formula} or {formula1;formula2;...;formulan}
function! GetMathFromString(string)
let mstart = FindMath(a:string)
if mstart == -1
return []
endif
let mstart += 1
let mend = match(a:string,'}',mstart)
if mend == -1
return []
endif
let mend -= 1
return split(a:string[mstart : mend],';')
endfunction
" MarkValues(string) {{{2
" mark Values in a string for replacement by formula results
" turns each number into '= voMathResult'
function! MarkValues(string)
return substitute(a:string,'=-\?[0-9]\+\(.[0-9]\+\)\?\([eE][-+]\?[0-9]\+\)\?','=voMathResult','g')
endfunction
" GetVarsFromString(string,vars) {{{2
" add key:value pairs from a string to the passed dictionary
" create new entries if key does not exist
" add values to existing entries
" vars is a dictionary of key:value pairs
" notation: name=number
function! GetVarsFromString(string,vars)
" quick return if no potential variables
if match(a:string,'=') == -1
return
endif
let tokens = split(a:string)
for token in tokens
if match(token,'=') == -1
continue
endif
let [key,value] = split(token,"=")
" read values are cast to floats to prevent
" auto-casting to integers in the first case
" and strings in the second
if has_key(a:vars,key)
let a:vars[key] += str2float(value)
else
let a:vars[key] = str2float(value)
endif
endfor
endfunction
" ReplaceVars(formula,vars) {{{2
" replace variables with their values from the supplied dictionary
" vars is a dictionary of key:value pairs
" key:value pairs are first sorted by key length, longest-first
" this prevents name collisions when similar key names are used like:
" Total and Totals -or- X1 and X12
function! ReplaceVars(formula,vars)
let formula = a:formula
let vars = []
for [key,val] in items(a:vars)
let vars += [[len(key),key,val]]
endfor
let vars = reverse(sort(vars))
for [len,key,val] in vars
let formula = substitute(formula,key,string(val),"g")
endfor
return formula
endfunction
" ComputeString(string,vars) {{{2
" compute a string using its math and a dictionary of variables
" return the computed, modified string
" string is a string containing math and result variable names
" vars is a dictionary of key:value pairs used in the computation
function! ComputeString(string,vars)
let string = a:string
let maths = GetMathFromString(string)
if len(maths)
let string = MarkValues(string)
for math in maths
let math = ReplaceVars(math,a:vars)
let result = string(eval(math))
let string = substitute(string,'voMathResult',result,"")
endfor
endif
return string
endfunction
" Math Functions on Outlines ########################################## {{{1
" MyChildrensVars(lnum) {{{2
" return a dictionary of variable from immediate children
function! MyChildrensVars(lnum)
let children = MyChildren(a:lnum)
let vars = {}
for child in children
call GetVarsFromString(getline(child),vars)
endfor
return vars
endfunction
" ComputeLine(lnum) {{{2
" compute a line's maths using variables from it's children
" replace the line with the newly computed line
function! ComputeLine(lnum)
let vars = MyChildrensVars(a:lnum)
let line = ComputeString(getline(a:lnum),vars)
call setline(a:lnum,line)
endfunction
" ComputeUp(lnum) {{{2
" compute 'up' a tree towards level 1
" the line (lnum) itself is computed first
" this is intended to be a fast compute method to update a branch of nodes
" it assumes that all other calculations in a tree are correct
function! ComputeUp(lnum)
call ComputeLine(a:lnum)
let lineage = MyLineage(a:lnum)
if len(lineage)
for lnum in lineage
call ComputeLine(lnum)
endfor
endif
endfunction
" ComputeDown(lnum) {{{2
" compute 'down' a tree from the current node
" the line (lnum) itself is computed last
function! ComputeDown(lnum)
let children = MyChildren(a:lnum)
if len(children)
for lnum in children
call ComputeDown(lnum)
endfor
endif
call ComputeLine(a:lnum)
endfunction
" ComputeTree(lnum) {{{2
" compute down an entire tree
function! ComputeTree(lnum)
let parents = MyLineage(a:lnum)
if len(parents)
let topparent = parents[-1]
else
let topparent = a:lnum
endif
call ComputeDown(topparent)
endfunction
" ComputeDocument() {{{2
" compute down all trees
function! ComputeDocument(lnum)
let parents = RootParents()
for parent in parents
call ComputeDown(parent)
endfor
endfunction
" Concealings {{{1
" BadWord is a very old VO region that is no longer used.
" It can be used now for plugins :)
" This should probably be fixed at some point in the future
syntax match BadWord "{.\+}" conceal transparent cchar=µ
set conceallevel=1
" mappings {{{1
map <silent><buffer> <localleader>== :call ComputeUp(line("."))<cr>
map <silent><buffer> <localleader>=t :call ComputeTree(line("."))<cr>
map <silent><buffer> <localleader>=d :call ComputeDocument()<cr>
map <silent><buffer> <localleader>=h :set conceallevel=1<cr>
map <silent><buffer> <localleader>=H :set conceallevel=0<cr>

View file

@ -0,0 +1,456 @@
"######################################################################
"# VimOutliner Hoisting
"# Copyright (C) 2003 by Noel Henson noel@noels-lab.com
"# The file is currently an experimental part of Vim Outliner.
"#
"# This program is free software; you can redistribute it and/or modify
"# it under the terms of the GNU General Public License as published by
"# the Free Software Foundation; either version 2 of the License, or
"# (at your option) any later version.
"#
"# This program is distributed in the hope that it will be useful,
"# but WITHOUT ANY WARRANTY; without even the implied warranty of
"# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
"# GNU General Public License for more details.
"######################################################################
" Load the plugin {{{1
if exists("g:did_vo_hoist")
"finish
endif
if !exists("g:hoistParanoia")
let g:hoistParanoia=0
endif
if !exists('hlevel')
let hlevel = 20
endif
let g:did_vo_hoist = 1
" mappings {{{1
map <silent> <buffer> <localleader>hh :call Hoist(line("."))<cr>
map <silent> <buffer> <localleader>hd :call DeHoist()<cr>
map <silent> <buffer> <localleader>hD :call DeHoistAll()<cr>
"}}}1
" syntax {{{1
" Hoisted {{{2
"syntax match Invis +^\~\zs.*$+ containedin=ALL conceal cchar=~
""hi Invis guifg=bg ctermfg=bg
""hi Invis guifg=bg
"hi link Invis Conceal
"}}}2
"}}}1
" MyFoldText() {{{1
" Create string used for folded text blocks
function! MyFoldText()
let l:MySpaces = MakeSpaces(&sw)
let l:line = getline(v:foldstart)
let l:bodyTextFlag=0
if l:line =~'^\~'
let l:line = '~'.repeat(' ', winwidth(0)-1)
elseif l:line =~ "^\t* \\S" || l:line =~ "^\t*\:"
let l:bodyTextFlag=1
let l:MySpaces = MakeSpaces(&sw * (v:foldlevel-1))
let l:line = l:MySpaces."[TEXT]"
elseif l:line =~ "^\t*\;"
let l:bodyTextFlag=1
let l:MySpaces = MakeSpaces(&sw * (v:foldlevel-1))
let l:line = l:MySpaces."[TEXT BLOCK]"
elseif l:line =~ "^\t*\> "
let l:bodyTextFlag=1
let l:MySpaces = MakeSpaces(&sw * (v:foldlevel-1))
let l:line = l:MySpaces."[USER]"
elseif l:line =~ "^\t*\>"
let l:ls = stridx(l:line,">")
let l:le = stridx(l:line," ")
if l:le == -1
let l:l = strpart(l:line, l:ls+1)
else
let l:l = strpart(l:line, l:ls+1, l:le-l:ls-1)
endif
let l:bodyTextFlag=1
let l:MySpaces = MakeSpaces(&sw * (v:foldlevel-1))
let l:line = l:MySpaces."[USER ".l:l."]"
elseif l:line =~ "^\t*\< "
let l:bodyTextFlag=1
let l:MySpaces = MakeSpaces(&sw * (v:foldlevel-1))
let l:line = l:MySpaces."[USER BLOCK]"
elseif l:line =~ "^\t*\<"
let l:ls = stridx(l:line,"<")
let l:le = stridx(l:line," ")
if l:le == -1
let l:l = strpart(l:line, l:ls+1)
else
let l:l = strpart(l:line, l:ls+1, l:le-l:ls-1)
endif
let l:bodyTextFlag=1
let l:MySpaces = MakeSpaces(&sw * (v:foldlevel-1))
let l:line = l:MySpaces."[USER BLOCK ".l:l."]"
elseif l:line =~ "^\t*\|"
let l:bodyTextFlag=1
let l:MySpaces = MakeSpaces(&sw * (v:foldlevel-1))
let l:line = l:MySpaces."[TABLE]"
endif
let l:sub = substitute(l:line,'\t',l:MySpaces,'g')
let l:len = strlen(l:sub)
let l:sub = l:sub . " " . MakeDashes(58 - l:len)
let frange = (v:foldend + l:bodyTextFlag)- v:foldstart
let l:sub = l:sub . " (" . frange
if frange == 1
let l:sub = l:sub . " line)"
else
let l:sub = l:sub . " lines)"
endif
return l:sub.repeat(' ', winwidth(0)-len(l:sub))
endfunction
"}}}1
" New Fold Function (will be put into vo_base later {{{1
function! MyHoistableFoldLevel(line)
let l:myindent = Ind(a:line)
let l:nextindent = Ind(a:line+1)
if HoistFold(a:line)
" if (a:line == 1)
" return g:hlevel
" elseif (HoistFold(a:line-1) == 0)
" return ">".0
" else
" return g:hlevel
" endif
return g:hlevel
elseif BodyText(a:line)
if (BodyText(a:line-1) == 0)
return '>'.(l:myindent+1)
endif
if (BodyText(a:line+1) == 0)
return '<'.(l:myindent+1)
endif
return (l:myindent+1)
elseif PreformattedBodyText(a:line)
if (PreformattedBodyText(a:line-1) == 0)
return '>'.(l:myindent+1)
endif
if (PreformattedBodyText(a:line+1) == 0)
return '<'.(l:myindent+1)
endif
return (l:myindent+1)
elseif PreformattedTable(a:line)
if (PreformattedTable(a:line-1) == 0)
return '>'.(l:myindent+1)
endif
if (PreformattedTable(a:line+1) == 0)
return '<'.(l:myindent+1)
endif
return (l:myindent+1)
elseif PreformattedUserText(a:line)
if (PreformattedUserText(a:line-1) == 0)
return '>'.(l:myindent+1)
endif
if (PreformattedUserTextSpace(a:line+1) == 0)
return '<'.(l:myindent+1)
endif
return (l:myindent+1)
elseif PreformattedUserTextLabeled(a:line)
if (PreformattedUserTextLabeled(a:line-1) == 0)
return '>'.(l:myindent+1)
endif
if (PreformattedUserText(a:line+1) == 0)
return '<'.(l:myindent+1)
endif
return (l:myindent+1)
elseif UserText(a:line)
if (UserText(a:line-1) == 0)
return '>'.(l:myindent+1)
endif
if (UserTextSpace(a:line+1) == 0)
return '<'.(l:myindent+1)
endif
return (l:myindent+1)
elseif UserTextLabeled(a:line)
if (UserTextLabeled(a:line-1) == 0)
return '>'.(l:myindent+1)
endif
if (UserText(a:line+1) == 0)
return '<'.(l:myindent+1)
endif
return (l:myindent+1)
else
if l:myindent < l:nextindent
return '>'.(l:myindent+1)
endif
if l:myindent > l:nextindent
return (l:myindent)
endif
return l:myindent
endif
endfunction
set foldexpr=MyHoistableFoldLevel(v:lnum)
"}}}2
"}}}1
" Functions {{{1
" RemoveTabs(line,tabs) {{{2
" remove specified number of tabs from the begining of line
function! RemoveTabs(start,end,tabs)
if a:tabs > 0
let l:doit = "silent ".a:start.",".a:end."s/^\\(\\t\\)\\{".a:tabs."}/"
exe l:doit
endif
endfunction
"}}}2
" IsParent(line) {{{2
" Return 1 if this line is a parent
function! IsParent(line)
return (Ind(a:line)+1) == Ind(a:line+1)
endfunction
"}}}2
" FindParent(line) {{{2
" Return line if parent, parent line if not
function! FindParent(line)
if IsParent(a:line)
return a:line
else
let l:parentindent = Ind(a:line)-1
let l:searchline = a:line
while (Ind(l:searchline) != l:parentindent) && (l:searchline > 0)
let l:searchline = l:searchline-1
endwhile
return l:searchline
endif
endfunction
"}}}2
" HoistFold() {{{2
" Return a flag indicating that there is a valid hoist
function! HoistFold(line)
if getline(a:line) =~ '^\~'
return 1
else
return 0
endif
endfunction
"}}}2
" Hoisted() {{{2
" Return a flag indicating that there is a valid hoist
function! Hoisted()
if getline(1) =~ '^\~'
return 1
else
return 0
endif
endfunction
"}}}2
" FindTopHoist(line) {{{2
" Return line number of the nearest (last line) top hoist tag
function! FindTopHoist(line)
let l:line = a:line
while (match(getline(l:line),"^\\~") == -1) && (l:line > 0)
let l:line -= 1
endwhile
return l:line
endfunction
"}}}2
" FindBottomHoist(line) {{{2
" Return line number of the nearest (last line) top hoist tag
function! FindBottomHoist(line)
let l:line = a:line
let l:lastline = line('$')
while getline(l:line) !~ "^\\~" && (l:line > 0) && l:line <= l:lastline
let l:line += 1
endwhile
return l:line
endfunction
"}}}2
" FindLastChild(line) {{{2
" Return the line number of the last decendent of parent line
function! FindLastChild(line)
let l:parentindent = Ind(a:line)
let l:searchline = a:line+1
while Ind(l:searchline) > l:parentindent
let l:searchline = l:searchline+1
endwhile
return l:searchline-1
endfunction
"}}}2
" GetHoistedIndent(line) {{{2
" line is the line number containing the indent
" Returns the original indent of the hoisted region
function! GetHoistedIndent(line)
return str2nr(strpart(getline(a:line),1,2))
endfunction
"}}}2
" HoistTagBefore(line,indent) {{{2
function! HoistTagBefore(line,indent)
let l:doit = "silent 1,".(a:line-1)."s/^/\\~".a:indent." /"
exe l:doit
"call setline(1, map(getline(1,a:line-1), '"~".a:indent.v:val'))
endfunction
"}}}2
" HoistDeTagBefore(line) {{{2
function! HoistDeTagBefore(line)
let l:doit = "silent 1,".a:line."s/^\\~\\d* //"
exe l:doit
endfunction
"}}}2
" HoistTagAfter(line) {{{2
function! HoistTagAfter(line)
if a:line > line('$')
return
endif
let l:doit = "silent ".a:line.",$s/^/\\~/"
exe l:doit
endfunction
"}}}2
" HoistDeTagAfter(line) {{{2
function! HoistDeTagAfter(line)
if a:line > line('$')
return
endif
let l:doit = "silent ".a:line.",$s/^\\~//"
exe l:doit
endfunction
"}}}2
" HoistWrite(file) {{{2
" Write the clean file if hoisted
function! HoistWrite(file,...) range
"let lines = getline(1,line('$'))
let saved = 0
mkview
call DeHoistAll()
if v:cmdbang
let bang = '!'
else
let bang = ''
endif
try
if a:0
echom a:firstline.','.a:lastline."w".bang.fnameescape(v:cmdarg).' '.a:file
exe a:firstline.','.a:lastline."w".bang.fnameescape(v:cmdarg).' '.a:file
else
exe "w".bang.fnameescape(v:cmdarg).' '.a:file
endif
let saved = 1
catch
echohl ErrorMsg
echom substitute(v:exception,'^Vim(.\{-}):','','')
echohl None
endtry
"call setline(1,lines)
silent earlier
silent loadview
if saved
silent setlocal nomodified
endif
endfunction "HoistWrite
" Hoist(line) {{{2
" Write the offspring of a parent to a new file, open it and remove the
" leading tabs.
function! Hoist(line)
let l:parent = FindParent(a:line)
if l:parent == 0
return
endif
"call cursor(l:parent,1)
"let l:firstline = l:parent+1
let l:firstline = l:parent
let l:childindent = Ind(l:firstline)
let l:lastline = FindLastChild(l:parent)
setlocal foldlevel=20
call HoistTagBefore(l:firstline,l:childindent)
call HoistTagAfter(l:lastline+1)
" call RemoveTabs(l:firstline,l:lastline,l:childindent)
call cursor(l:firstline,1)
setlocal foldlevel=19
augroup VO_HOIST
au!
au CursorMoved,CursorMovedI <buffer>
\ if getline('.') =~ '^\~\d* ' |
\ call HoistKeepCursor(1) |
\ elseif getline('.') =~ '^\~' |
\ call HoistKeepCursor(0) |
\ endif
"au BufWriteCmd <buffer> call HoistWrite(fnameescape(expand("<amatch>")))
"au FileWriteCmd <buffer> '[,']call HoistWrite(fnameescape(expand("<amatch>")), 1)
augroup END
if exists('+conceallevel')
syntax match Invis +^\~.*$+ conceal cchar=~
hi link Invis Conceal
setlocal conceallevel=2
else
syntax match Invis +^\~.*$+ containedin=ALL
let i = 1
while synIDtrans(i) != 0
if synIDattr(i, 'name') == 'Normal'
if synIDattr(i, 'bg') >= 0
" bg is set
hi Invis guifg=bg ctermfg=bg
else
hi Invis guifg=NONE ctermfg=NONE
endif
break
endif
let i += 1
endwhile
endif
endfunction
" }}}2
" HoistKeepCursor(top) {{{2
" Keep cursor out of non-hoisted area.
function! HoistKeepCursor( top )
if a:top
while getline('.') =~ '^\~\d* '
normal! j
endwhile
else
while getline('.') =~ '^\~'
normal! k
endwhile
endif
endfunction "HoistKeepCursor }}}2
" MakeTabs(n) {{{2
" return a string of n tabs
function! MakeTabs(n)
let l:tabs = ""
let l:n = a:n
while l:n > 0
let l:tabs = l:tabs."\t"
let l:n -= 1
endwhile
return l:tabs
endfunction
"}}}2
"}}}2
" DeHoist() {{{2
" Write the offspring of a parent to a new file, open it and remove the
" leading tabs.
function! DeHoist()
if !Hoisted()
return
endif
let l:line = line(".")
let l:top = FindTopHoist(l:line)
let l:bottom = FindBottomHoist(l:line)
echom 1
let l:indent = GetHoistedIndent(l:top)
" let l:tabs = MakeTabs(l:indent)
" let l:doit = "silent ".(l:top+1).",".(l:bottom-1)."s/^/".l:tabs."/"
" exe l:doit
call HoistDeTagBefore(l:top)
call HoistDeTagAfter(l:bottom)
if !Hoisted()
augroup VO_HOIST
au!
augroup! VO_HOIST
augroup END
endif
call cursor(l:line,l:indent)
endfunction
"}}}2
" DeHoistAll() {{{2
" Write the offspring of a parent to a new file, open it and remove the
" leading tabs.
function! DeHoistAll()
while Hoisted()
call DeHoist()
endwhile
endfunction
"}}}2
"}}}1
" vim600: set foldlevel=0 foldmethod=marker:

View file

@ -0,0 +1,117 @@
" Here is a small script that remaps the p and P normal commands such that VO
" will do what one would expect when pasting cut/copied nodes into another
" section of an outline. It will adjust the indents and not paste into the
" middle of a branch.
" Added 2011-03-01(JB): This script will now also copy an outline correctly by
" using \\y, copy to the register with \\r, cut an outline by using \\d, and
" paste from the register using \\b.
" http://www.lists.vimoutliner.org/pipermail/vimoutliner/2008-October/002366.html
map <buffer>p :call VOput()<cr>
map <buffer>\\b :call VOputreg()<cr>
map <buffer>\\y :call VOcop()<cr>
map <buffer>\\r :call VOreg()<cr>
map <buffer>\\d :call VOcut()<cr>
map <buffer>P ]P
if exists('s:loaded')
finish
endif
let s:loaded = 1
function! IsParent(line)
if a:line == line("$")
return 0
elseif Ind(a:line) < Ind(a:line+1)
return 1
else
return 0
endif
endfunction
function! VOcop()
let thisLine = line(".")
if (foldclosed(thisLine) == -1) && IsParent(thisLine)
normal! zc
let fold_cursor = getpos(".")
normal! yy
let get_cursor = getpos(".")
call setpos('.',fold_cursor)
normal! zo
call setpos('.',get_cursor)
else
normal! yy
endif
endfunction
function! VOreg()
let thisLine = line(".")
if (foldclosed(thisLine) == -1) && IsParent(thisLine)
normal! zc
let fold_cursor = getpos(".")
normal! V"+y
let get_cursor = getpos(".")
call setpos('.',fold_cursor)
normal! zo
call setpos('.',get_cursor)
else
normal! V"+y
endif
endfunction
function! VOcut()
let thisLine = line(".")
if (foldclosed(thisLine) == -1) && IsParent(thisLine)
normal! zc
let fold_cursor = getpos(".")
normal! dd
let get_cursor = getpos(".")
call setpos('.',fold_cursor)
normal! zo
call setpos('.',get_cursor)
else
normal! dd
endif
endfunction
function! VOput()
let thisLine = line(".")
if (foldclosed(thisLine) == -1) && IsParent(thisLine)
normal! zc
let fold_cursor = getpos(".")
normal! ]p
let put_cursor = getpos(".")
call setpos('.',fold_cursor)
normal! zo
call setpos('.',put_cursor)
else
normal! ]p
endif
endfunction
function! VOputreg()
let thisLine = line(".")
if (foldclosed(thisLine) == -1) && IsParent(thisLine)
normal! zc
let fold_cursor = getpos(".")
normal! "+]p
let put_cursor = getpos(".")
call setpos('.',fold_cursor)
normal! zo
call setpos('.',put_cursor)
else
normal! "+]p
endif
endfunction

View file

@ -0,0 +1,246 @@
" File: plugin/votl_tags.vim
" Version: 1.0
" Modified: 2011-02-28
" Description: This plugin provides inter-outline links for vimoutliner.
" Maintainer: Israel Chauca F. <israelchauca@gmail.com>
" Manual: The following mappings are added:
" - <C-K> : Follow a link.
" - <C-N> : Jump back in the link-history.
" - <localleader>l : Create a link.
" ============================================================================
if v:version < 700
echom 'VimOutliner: votl_tags.vim requires Vim 7.0 or later.'
finish
endif
" Create outlines' jump-list.
if !exists('w:vo_jump_list')
let w:vo_jump_list = []
endif
" Mappings {{{1
" Follow inter-outline link.
noremap <buffer> <Plug>VO_FollowLink :call <SID>follow_link()<CR>
if !hasmapto('<Plug>VO_FollowLink')
"map <unique> <buffer> <C-K> <Plug>VO_FollowLink
map <silent> <buffer> <C-K> <Plug>VO_FollowLink
endif
" Go back to previous outline.
noremap <buffer> <Plug>VO_JumpBack :call <SID>jump_back()<CR>
if !hasmapto('<Plug>VO_JumpBack')
"map <unique> <buffer> <C-N> <Plug>VO_JumpBack
map <silent> <buffer> <C-N> <Plug>VO_JumpBack
endif
" Create a link from a word in normal mode.
noremap <buffer> <Plug>VO_CreateLink :call <SID>create_link()<CR>
if !hasmapto('<Plug>VO_CreateLink')
silent! map <silent> <unique> <buffer> <localleader>l <Plug>VO_CreateLink
endif
" Create a link from a word in insert mode.
inoremap <buffer> <Plug>VO_CreateLinkI <C-O>:call <SID>create_link()<CR>
if !hasmapto('<Plug>VO_CreateLinkI')
silent! imap <silent> <unique> <buffer> <localleader>l <Plug>VO_CreateLinkI
endif
" Functions {{{1
let s:checkboxpat = '\%(\[[^[\]]\+\]\s\+\%(\d*%\d*\s\+\)\?\)\?'
" Don't re-load functions.
if exists('s:loaded')
finish
endif
let s:loaded = 1
" s:get_link() {{{2
" Get link data.
function! s:get_link(linenr)
" Check if it's a valid link.
let line = getline(a:linenr)
if line =~? '\m^\t*'.s:checkboxpat.'_tag_\w\+\s*$'
" Don't remember where this bit came from, please let me know if you do.
let [_,file,row,col;m0] = matchlist(getline(a:linenr + 1), '\m^\t*'.s:checkboxpat.'\([^:]\+\)\%(:\(\d\+\)\)\?\%(:\(\d\+\)\)\?$')
elseif line =~? '\m^\t*'.s:checkboxpat.'_ilink_\(.\{-}:\s\)\?\s*\S.*$'
let pat = '\m^\t*'.s:checkboxpat.'_ilink_\%([^:\\/]\{-}:\s\)\?\s*\(.\+\)\%(:\(\d\+\)\)\?\%(:\(\d\+\)\)\?$'
let [_,file,row,col;m0] = matchlist(line, pat)
else
return ['',0,0,0]
endif
let is_inner_link = 0
if file == '%'
let file = expand('%:p')
let is_inner_link = 1
endif
let row = (row == '' ? 0 : row * 1)
let col = (col == '' ? 0 : col * 1)
return [file, row, col, is_inner_link]
endfunction
" s:follow_link() {{{2
" Follow an interoutline link.
function! s:follow_link()
" Get link data.
let [file, row, col, is_inner_link] = s:get_link(line('.'))
if file == ''
echom 'Vimoutliner: "'.substitute(getline('.'), '\m^\t*'.s:checkboxpat, '', '').'" doesn''t not look like an inter-outline link.'
return
endif
" Check if file path exists.
let file = s:get_absolute_path(expand('%:h'), file)
let file = fnamemodify(file,':p')
let baseDir = fnamemodify(file,':h')
let dirconfirm = 0
" Check if directories exists. {{{3
if glob(baseDir) == ''
if exists('*confirm')
let dirconfirm = confirm('The linked file "'.file.'" and one or more directories do not exist, do you want to create them now?', "&Yes\n&No", '2', 'Question')
else
" Can't ask, asume a yes for answer.
let dirconfirm = 1
endif
if dirconfirm == 1
" Create dir(s):
if exists('*mkdir')
call mkdir(baseDir,'p')
elseif executable('mkdir')
call system('`which mkdir` -p '.shellescape(baseDir))
else
echom 'Vimoutliner: Vim can not create the required directories, please create them yourself.'
return
endif
else
return
endif
endif
" Check if file exists. {{{3
if glob(file) == ''
if exists('*confirm') && dirconfirm == 0
let confirm = confirm('The linked file "'.file.'" does not exist, do you want to create it now?', "&Yes\n&No", '2', 'Question')
else
" Can't ask, asume a yes for answer.
let confirm = 1
endif
if confirm == 1
call writefile([], file)
else
return
endif
endif
" }}}3
" Now let's jump to that outline.
try
call s:update_jump_list()
if !is_inner_link
exec "buffer ".bufnr(substitute(file, '\m^'.getcwd().'/','',''), 1)
endif
if row > 0
call setpos('.',[0,row,col,0])
endif
setlocal buflisted
catch
" Prevent reporting that the error ocurred inside this function.
echoh ErrorMsg
echom substitute(v:exception,'\m^Vim(.\{-}):','','')
echoh None
endtry
return ''
endfunction
" s:get_absolute_path(baseDir, fileName) {{{2
" Guess an absolute path
function! s:get_absolute_path(baseDir, fileName)
let baseDir = a:baseDir
if baseDir !~ '/$'
let baseDir = baseDir . '/'
endif
if a:fileName =~ '^/'
let absFileName = a:fileName
else
let absFileName = baseDir . a:fileName
endif
let absFileName = substitute(absFileName, '\m/\./', '/', 'g')
while absFileName =~ '/\.\./'
absFileName = substitute(absFileName, '\m/[^/]*\.\./', '', '')
endwhile
return absFileName
endfunction
" s:update_jump_list() {{{2
" Add current outline to list.
function! s:update_jump_list()
call add(w:vo_jump_list, [bufnr('%')] + getpos('.'))
endfunction
" s:remove_buf(buf) {{{2
" Remove outline from list.
function! s:remove_buf()
if !exists('w:vo_jump_list') || len(w:vo_jump_list) == 0
return
endif
" Remove last outline.
call remove(w:vo_jump_list, -1)
endfunction
" s:jump_back() {{{2
" Jump back to the previous outline.
function! s:jump_back()
if len(w:vo_jump_list) == 0
echom 'This is the first outline.'
return
endif
exec "buffer ".w:vo_jump_list[-1][0]
call setpos('.', w:vo_jump_list[-1][1 : ])
call s:remove_buf()
endfunction
" s:create_link() {{{2
" Create an interoutline link with the current keyword under the cursor.
function! s:create_link()
let line = getline('.')
" Create link on a header only
if line =~ '\m^\t\+[^ :;<>|]'
echom 'Vimoutliner: Links have to be on a header.'
return
endif
" Check if the there's is some content in the current line and a current
" link doesn't exists.
if line =~# '\m^\t*'.s:checkboxpat.'_ilink_\%([^:]\{-}:\s\)\?\s*\S\+.*$'
echom 'Vimoutliner: Looks like "'.substitute(line,'^\t*'.s:checkboxpat.'\(\S.*$\)','\1','').'" is already a link.'
return
endif
call inputsave()
let path = input('Linked outline''s path: ', '', 'file')
call inputrestore()
if path == ''
" User canceled.
return ''
endif
let path = matchstr(path, '\m^\t*'.s:checkboxpat.'\zs\S.\{-}\ze\s*$')
"if path !~ '\.otl$'
"" Add extension.
"let path = path.'.otl'
"endif
let tag = '_ilink_'
let [_,indent,checkbox,label;m0] = matchlist(line, '\m^\(\t*\)\('.s:checkboxpat.'\)\%(_ilink_\)\?\s*\(\S\%(.\{-1,}\S\)\?\)\?\s*\%(:\s\)\?\s*$')
"echom indent.'-'.checkbox.'-'.label
if indent == ''
let indent = matchstr(getline(line('.')-1), '\m^\(\t*\)')
endif
if label !~ ':\s*$'
let label = substitute(label, '\m\s*$', ': ', '')
else
let label = substitute(label, '\m:\s*$', ': ', '')
endif
call setline(line('.'), indent.checkbox.tag.' '.label.path)
echo ''
endfunction
" Autocommands {{{1
augroup vo_links
au!
au BufWinEnter * if !exists('w:vo_jump_list') | let w:vo_jump_list = [] | endif
augroup END
"{{{1 vim:foldmethod=marker

View file

@ -0,0 +1,17 @@
"delete trailing white space
:%s/^[ \t][ \t]*\(.*\)$/\1/
"delete one line from each block of empty lines
:g/^\_$\n\_^..*$/d
"add leading : to body text
:%s/^[^=]..*[^=]$/:&/
"add leading : to dividers made entirely of equal signs
:%s/^==*$/:&/
"delete one leading = and space from heading
"delete trailing space and = from heading
"\1 is leading equal signs. \2 is heading.
:%s/^=\(=*\) \(.*\) =\+$/\1\2/
"indent body text lines (lines starting with ":")
"how to indent body text lines?
"indent headings
"substitute each = with tab
:g/^=/s/=/\t/g

View file

@ -0,0 +1,37 @@
This email has the attachment.
Hello Vim Outliners,
I want to migrate some word processor documents to otl.  If you know of a formatter to convert "MediaWiki to otl" or "odt to otl" or "doc to otl" please let me know.  I did not find one.
I am attempting to write ex script to convert MediaWiki to otl.  This would be useful because LibreOffice Writer can export to MediaWiki format, and then the script would convert it to otl.
The heading format in MediaWiki is simple; one equal sign for each level in the heading hierarchy e.g.:
= head1 =
body text
== head2 ==
body text
=== head3 ===
body text
The above MediaWiki text should convert to otl like this:
head1
:body text
head2
:body text
head3
:body text
The attached MediaWiki2otl script converts MediaWiki to otl except for one thing.  I can not figure out how to indent the body text lines (indent lines that start with “:”). Here is the pseudo code:
if current line has “=”
set count to number of “=”
else  
insert count “\t”
Can this be done in ex script? i.e. Is there an ex command that can set a variable (or register) and an ex command to read that variable in the following lines?
Thank you,
wolfv

View file

@ -0,0 +1,118 @@
The Litt Perl Development Tools License
Version 1.0, 3/14/2003
---------------------------------------
The Litt Perl Development Tools License (LPDTL)
consists of the GNU GPL plus an exception, plus an
exception to that exception.
This program is free software; you can
redistribute it and/or modify it under the terms
of version 2 of the GNU General Public License as
published by the Free Software Foundation.
This program is distributed in the hope that it
will be useful, but WITHOUT ANY WARRANTY; without
even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General
Public License along with this software; see the
file COPYING.GPL. If not, write to the Free
Software Foundation, Inc., 59 Temple Place, Suite
330, Boston, MA 02111-1307 USA. Other locations
for the GNU General Public License include:
http://www.troubleshooters.com/licenses/LPDTL/COPYING.GPL
and
http://www.gnu.org/licenses/gpl.txt
THE EXCEPTION
As a special exception, the author of this Perl
Development Tool gives permission for additional
uses of the text contained in this Perl
Development Tool.
The exception is that, if you reference the LPDTL
licensed software, by use of a Perl "use" or
"require" statement, from other files, this does
not by itself cause the files referencing this
Perl Development Tool to be covered by the GNU
General Public License. Your use of the
referencing files is in no way restricted on
account of their referencing the code in this Perl
Development Tool.
Likewise, if you link the LPDTL licensed software
with other files to produce an executable, this
does not by itself cause the resulting executable
to be covered by the GNU General Public License.
Your use of that executable is in no way
restricted on account of linking the LPDTL
licensed software's code into it.
EXCEPTION LIMITATIONS
This exception does not however invalidate any
other reasons why the referencing files, or any
produced executables, might be covered by the GNU
General Public License.
This exception applies only to the code released
by its author under the Litt Perl Development
Tools License. Such code must clearly state that
it's covered by the Litt Perl Development Tools
License. If you copy code from other GNU-GPL
compatible software, as the General Public License
permits, the exception does not apply to the code
that you add in this way. To avoid misleading
anyone as to the status of such modified files,
you must delete this exception notice from them.
If you write modifications of your own for
software licensed under the Litt Perl Development
Tools License, it is your choice whether to permit
this exception to apply to your modifications. If
you do not wish that, delete this exception
notice.
This exception cannot be applied to software whose
license, End User License Agreement, or Terms Of
Use limit reverse engineering or discussion about
the software. This exception cannot be applied to
software incorporating any type of software
patents.
THE EXCEPTION TO THE EXCEPTION
The intent of the Litt Perl Development Tools
License (LPDTL) is to produce a free software
development environment enabling a software
developer to create free software, nonfree
software, or proprietary software. Therefore, all
of the resulting program's software development
features must be contained in the LPDTL licensed
software, or in GPL licensed software. It is a
violation of the LPDTL to place any software
development features in any file not licensed
under either the GPL or the LPDTL.
A "software development feature" is a feature
enabling creation or modification of software.
DISCUSSION OF INTENT OF THIS LICENSE
You can find a discussion of the intent of this
license in the LPDTL_discuss.txt file. If you
haven't been given a copy of this file, you can
find it in the
http://www.troubleshooters.com/licenses/LPDTL/
directory.

View file

@ -0,0 +1,340 @@
GNU GENERAL PUBLIC LICENSE
Version 2, June 1991
Copyright (C) 1989, 1991 Free Software Foundation, Inc.
59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The licenses for most software are designed to take away your
freedom to share and change it. By contrast, the GNU General Public
License is intended to guarantee your freedom to share and change free
software--to make sure the software is free for all its users. This
General Public License applies to most of the Free Software
Foundation's software and to any other program whose authors commit to
using it. (Some other Free Software Foundation software is covered by
the GNU Library General Public License instead.) You can apply it to
your programs, too.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
this service if you wish), that you receive source code or can get it
if you want it, that you can change the software or use pieces of it
in new free programs; and that you know you can do these things.
To protect your rights, we need to make restrictions that forbid
anyone to deny you these rights or to ask you to surrender the rights.
These restrictions translate to certain responsibilities for you if you
distribute copies of the software, or if you modify it.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must give the recipients all the rights that
you have. You must make sure that they, too, receive or can get the
source code. And you must show them these terms so they know their
rights.
We protect your rights with two steps: (1) copyright the software, and
(2) offer you this license which gives you legal permission to copy,
distribute and/or modify the software.
Also, for each author's protection and ours, we want to make certain
that everyone understands that there is no warranty for this free
software. If the software is modified by someone else and passed on, we
want its recipients to know that what they have is not the original, so
that any problems introduced by others will not reflect on the original
authors' reputations.
Finally, any free program is threatened constantly by software
patents. We wish to avoid the danger that redistributors of a free
program will individually obtain patent licenses, in effect making the
program proprietary. To prevent this, we have made it clear that any
patent must be licensed for everyone's free use or not licensed at all.
The precise terms and conditions for copying, distribution and
modification follow.
GNU GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. This License applies to any program or other work which contains
a notice placed by the copyright holder saying it may be distributed
under the terms of this General Public License. The "Program", below,
refers to any such program or work, and a "work based on the Program"
means either the Program or any derivative work under copyright law:
that is to say, a work containing the Program or a portion of it,
either verbatim or with modifications and/or translated into another
language. (Hereinafter, translation is included without limitation in
the term "modification".) Each licensee is addressed as "you".
Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope. The act of
running the Program is not restricted, and the output from the Program
is covered only if its contents constitute a work based on the
Program (independent of having been made by running the Program).
Whether that is true depends on what the Program does.
1. You may copy and distribute verbatim copies of the Program's
source code as you receive it, in any medium, provided that you
conspicuously and appropriately publish on each copy an appropriate
copyright notice and disclaimer of warranty; keep intact all the
notices that refer to this License and to the absence of any warranty;
and give any other recipients of the Program a copy of this License
along with the Program.
You may charge a fee for the physical act of transferring a copy, and
you may at your option offer warranty protection in exchange for a fee.
2. You may modify your copy or copies of the Program or any portion
of it, thus forming a work based on the Program, and copy and
distribute such modifications or work under the terms of Section 1
above, provided that you also meet all of these conditions:
a) You must cause the modified files to carry prominent notices
stating that you changed the files and the date of any change.
b) You must cause any work that you distribute or publish, that in
whole or in part contains or is derived from the Program or any
part thereof, to be licensed as a whole at no charge to all third
parties under the terms of this License.
c) If the modified program normally reads commands interactively
when run, you must cause it, when started running for such
interactive use in the most ordinary way, to print or display an
announcement including an appropriate copyright notice and a
notice that there is no warranty (or else, saying that you provide
a warranty) and that users may redistribute the program under
these conditions, and telling the user how to view a copy of this
License. (Exception: if the Program itself is interactive but
does not normally print such an announcement, your work based on
the Program is not required to print an announcement.)
These requirements apply to the modified work as a whole. If
identifiable sections of that work are not derived from the Program,
and can be reasonably considered independent and separate works in
themselves, then this License, and its terms, do not apply to those
sections when you distribute them as separate works. But when you
distribute the same sections as part of a whole which is a work based
on the Program, the distribution of the whole must be on the terms of
this License, whose permissions for other licensees extend to the
entire whole, and thus to each and every part regardless of who wrote it.
Thus, it is not the intent of this section to claim rights or contest
your rights to work written entirely by you; rather, the intent is to
exercise the right to control the distribution of derivative or
collective works based on the Program.
In addition, mere aggregation of another work not based on the Program
with the Program (or with a work based on the Program) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.
3. You may copy and distribute the Program (or a work based on it,
under Section 2) in object code or executable form under the terms of
Sections 1 and 2 above provided that you also do one of the following:
a) Accompany it with the complete corresponding machine-readable
source code, which must be distributed under the terms of Sections
1 and 2 above on a medium customarily used for software interchange; or,
b) Accompany it with a written offer, valid for at least three
years, to give any third party, for a charge no more than your
cost of physically performing source distribution, a complete
machine-readable copy of the corresponding source code, to be
distributed under the terms of Sections 1 and 2 above on a medium
customarily used for software interchange; or,
c) Accompany it with the information you received as to the offer
to distribute corresponding source code. (This alternative is
allowed only for noncommercial distribution and only if you
received the program in object code or executable form with such
an offer, in accord with Subsection b above.)
The source code for a work means the preferred form of the work for
making modifications to it. For an executable work, complete source
code means all the source code for all modules it contains, plus any
associated interface definition files, plus the scripts used to
control compilation and installation of the executable. However, as a
special exception, the source code distributed need not include
anything that is normally distributed (in either source or binary
form) with the major components (compiler, kernel, and so on) of the
operating system on which the executable runs, unless that component
itself accompanies the executable.
If distribution of executable or object code is made by offering
access to copy from a designated place, then offering equivalent
access to copy the source code from the same place counts as
distribution of the source code, even though third parties are not
compelled to copy the source along with the object code.
4. You may not copy, modify, sublicense, or distribute the Program
except as expressly provided under this License. Any attempt
otherwise to copy, modify, sublicense or distribute the Program is
void, and will automatically terminate your rights under this License.
However, parties who have received copies, or rights, from you under
this License will not have their licenses terminated so long as such
parties remain in full compliance.
5. You are not required to accept this License, since you have not
signed it. However, nothing else grants you permission to modify or
distribute the Program or its derivative works. These actions are
prohibited by law if you do not accept this License. Therefore, by
modifying or distributing the Program (or any work based on the
Program), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Program or works based on it.
6. Each time you redistribute the Program (or any work based on the
Program), the recipient automatically receives a license from the
original licensor to copy, distribute or modify the Program subject to
these terms and conditions. You may not impose any further
restrictions on the recipients' exercise of the rights granted herein.
You are not responsible for enforcing compliance by third parties to
this License.
7. If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues),
conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot
distribute so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you
may not distribute the Program at all. For example, if a patent
license would not permit royalty-free redistribution of the Program by
all those who receive copies directly or indirectly through you, then
the only way you could satisfy both it and this License would be to
refrain entirely from distribution of the Program.
If any portion of this section is held invalid or unenforceable under
any particular circumstance, the balance of the section is intended to
apply and the section as a whole is intended to apply in other
circumstances.
It is not the purpose of this section to induce you to infringe any
patents or other property right claims or to contest validity of any
such claims; this section has the sole purpose of protecting the
integrity of the free software distribution system, which is
implemented by public license practices. Many people have made
generous contributions to the wide range of software distributed
through that system in reliance on consistent application of that
system; it is up to the author/donor to decide if he or she is willing
to distribute software through any other system and a licensee cannot
impose that choice.
This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License.
8. If the distribution and/or use of the Program is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Program under this License
may add an explicit geographical distribution limitation excluding
those countries, so that distribution is permitted only in or among
countries not thus excluded. In such case, this License incorporates
the limitation as if written in the body of this License.
9. The Free Software Foundation may publish revised and/or new versions
of the General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the Program
specifies a version number of this License which applies to it and "any
later version", you have the option of following the terms and conditions
either of that version or of any later version published by the Free
Software Foundation. If the Program does not specify a version number of
this License, you may choose any version ever published by the Free Software
Foundation.
10. If you wish to incorporate parts of the Program into other free
programs whose distribution conditions are different, write to the author
to ask for permission. For software which is copyrighted by the Free
Software Foundation, write to the Free Software Foundation; we sometimes
make exceptions for this. Our decision will be guided by the two goals
of preserving the free status of all derivatives of our free software and
of promoting the sharing and reuse of software generally.
NO WARRANTY
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
REPAIR OR CORRECTION.
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
POSSIBILITY OF SUCH DAMAGES.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively
convey the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
<one line to give the program's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
Also add information on how to contact you by electronic and paper mail.
If the program is interactive, make it output a short notice like this
when it starts in an interactive mode:
Gnomovision version 69, Copyright (C) year name of author
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.
The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License. Of course, the commands you use may
be called something other than `show w' and `show c'; they could even be
mouse-clicks or menu items--whatever suits your program.
You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the program, if
necessary. Here is a sample; alter the names:
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
`Gnomovision' (which makes passes at compilers) written by James Hacker.
<signature of Ty Coon>, 1 April 1989
Ty Coon, President of Vice
This General Public License does not permit incorporating your program into
proprietary programs. If your program is a subroutine library, you may
consider it more useful to permit linking proprietary applications with the
library. If this is what you want to do, use the GNU Library General
Public License instead of this License.

View file

@ -0,0 +1,118 @@
The Litt Perl Development Tools License
Version 1.0, 3/14/2003
---------------------------------------
The Litt Perl Development Tools License (LPDTL)
consists of the GNU GPL plus an exception, plus an
exception to that exception.
This program is free software; you can
redistribute it and/or modify it under the terms
of version 2 of the GNU General Public License as
published by the Free Software Foundation.
This program is distributed in the hope that it
will be useful, but WITHOUT ANY WARRANTY; without
even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General
Public License along with this software; see the
file COPYING.GPL. If not, write to the Free
Software Foundation, Inc., 59 Temple Place, Suite
330, Boston, MA 02111-1307 USA. Other locations
for the GNU General Public License include:
http://www.troubleshooters.com/licenses/LPDTL/COPYING.GPL
and
http://www.gnu.org/licenses/gpl.txt
THE EXCEPTION
As a special exception, the author of this Perl
Development Tool gives permission for additional
uses of the text contained in this Perl
Development Tool.
The exception is that, if you reference the LPDTL
licensed software, by use of a Perl "use" or
"require" statement, from other files, this does
not by itself cause the files referencing this
Perl Development Tool to be covered by the GNU
General Public License. Your use of the
referencing files is in no way restricted on
account of their referencing the code in this Perl
Development Tool.
Likewise, if you link the LPDTL licensed software
with other files to produce an executable, this
does not by itself cause the resulting executable
to be covered by the GNU General Public License.
Your use of that executable is in no way
restricted on account of linking the LPDTL
licensed software's code into it.
EXCEPTION LIMITATIONS
This exception does not however invalidate any
other reasons why the referencing files, or any
produced executables, might be covered by the GNU
General Public License.
This exception applies only to the code released
by its author under the Litt Perl Development
Tools License. Such code must clearly state that
it's covered by the Litt Perl Development Tools
License. If you copy code from other GNU-GPL
compatible software, as the General Public License
permits, the exception does not apply to the code
that you add in this way. To avoid misleading
anyone as to the status of such modified files,
you must delete this exception notice from them.
If you write modifications of your own for
software licensed under the Litt Perl Development
Tools License, it is your choice whether to permit
this exception to apply to your modifications. If
you do not wish that, delete this exception
notice.
This exception cannot be applied to software whose
license, End User License Agreement, or Terms Of
Use limit reverse engineering or discussion about
the software. This exception cannot be applied to
software incorporating any type of software
patents.
THE EXCEPTION TO THE EXCEPTION
The intent of the Litt Perl Development Tools
License (LPDTL) is to produce a free software
development environment enabling a software
developer to create free software, nonfree
software, or proprietary software. Therefore, all
of the resulting program's software development
features must be contained in the LPDTL licensed
software, or in GPL licensed software. It is a
violation of the LPDTL to place any software
development features in any file not licensed
under either the GPL or the LPDTL.
A "software development feature" is a feature
enabling creation or modification of software.
DISCUSSION OF INTENT OF THIS LICENSE
You can find a discussion of the intent of this
license in the LPDTL_discuss.txt file. If you
haven't been given a copy of this file, you can
find it in the
http://www.troubleshooters.com/licenses/LPDTL/
directory.

View file

@ -0,0 +1,23 @@
INSTALLATION PROCEDURES FOR Node.pm
Version 0.2.0 released 5/13/2004
Downloading
Download from
http://www.troubleshooters.com/projects/Node/Node.#.#.#.tgz
Installing
The tarball extracts into a directory called Node
cd to the directory into which you want to place Node
Move any existing Node directory out of the way
tar xzvf Node.#.#.#.tgz
Testing
cd into the Node directory
./hello.pl
troubleshoot as necessary
Using
In any perl program using Node not in the Node directory...
Use following code at beginning
#!/usr/bin/perl -w -I/path/to/Node
use strict;
use Node;
If destination machine has unknown Node.pm directory...
See example_nodepath.pl

View file

@ -0,0 +1,26 @@
INTENT OF THE LITT PERL DEVELOPMENT TOOL LICENSE
The intent of the Litt Perl Development Tool License (LPDTL) is to provide
programmers with a development tool that is copylefted free software without
requiring applications built from this tool to be free software.
I (Steve Litt) have tried to craft the LPDTL to prevent "cute and
innovative" ways of taking this tool private. For instance, a person might
try to make the LPDTL licensed code dependent on subroutines in proprietary
subroutines. This is why the LPDTL has a clause stating that all
*development* features must reside in the LPDTL code, or in GPL code, but
not non-GPL-compatible code.
Although the LPDTL allows its use with software of almost any license, it
specifically forbids use with licenses doing any of the following:
1. Restricting reverse engineering
2. Restricting discussion about the software
3. Code that includes software patents
I feel that restrictions on reverse engineering and discussion, and software
patents, are so destructive to society, that I have included language
preventing use of LPDTL licensed code with any software incorporating any of
these three atrocities.

View file

@ -0,0 +1,475 @@
#!/usr/bin/perl -w
#######################################################################
# Copyright (C) 2003 by Steve Litt, all rights reserved.
# Licensed under version 1 of the
# Litt Perl Development Tools License
# See COPYING file
# Or COPYING.LPDTL.1.0
# Or see http://www.troubleshooters.com/licenses/LPDTL/COPYING.LPDTL.1.0
#
# ABSOLUTELY NO WARRANTY, USE AT YOUR OWN RISK!
#
# Version 0.2.0 released 5/13/2004
use strict;
package Node;
sub new($$$$)
{
my($typeOfClass) = $_[0];
my($self) = {};
bless($self, $typeOfClass);
$self->{'name'}=$_[1];
$self->{'type'}=$_[2];
$self->{'value'}=$_[3];
$self->{'nextsibling'}=undef;
$self->{'prevsibling'}=undef;
$self->{'parent'}=undef;
$self->{'firstchild'}=undef;
$self->{'lastchild'}=undef;
$self->{'attributes'}={};
return($self);
}
#### For single attribute
sub setAttribute()
{
$_[0]->{'attributes'}->{$_[1]} = $_[2];
}
sub removeAttribute()
{
delete $_[0]->{'attributes'}->{$_[1]};
}
sub getAttribute()
{
if($_[0]->hasAttributes())
{
return $_[0]->{'attributes'}->{$_[1]};
}
else
{
return(undef);
}
}
sub hasAttribute()
{
if($_[0]->hasAttributes())
{
return defined($_[0]->getAttribute($_[1]));
}
else
{
return(undef);
}
}
#### For attribute array
sub hasAttributes()
{
return defined($_[0]->getAttributes());
}
sub getAttributes()
{
return %{$_[0]->{'attributes'}};
}
sub setAttributes()
{
$_[0]->{'attributes'} = $_[1];
}
#### For traversing
sub getFirstChild() {return($_[0]->{'firstchild'});}
sub getNextSibling() {return($_[0]->{'nextsibling'});}
sub getParent() {return($_[0]->{'parent'});}
sub hasFirstChild() {return(defined($_[0]->{'firstchild'}));}
sub hasNextSibling() {return(defined($_[0]->{'nextsibling'}));}
sub hasParent() {return(defined($_[0]->{'parent'}));}
#### For reverse traversing
sub getLastChild() {return($_[0]->{'lastchild'});}
sub getPrevSibling() {return($_[0]->{'prevsibling'});}
sub hasLastChild() {return(defined($_[0]->{'lastchild'}));}
sub hasPrevSibling() {return(defined($_[0]->{'prevsibling'}));}
#### For content
sub getName() {return($_[0]->{'name'});}
sub getType() {return($_[0]->{'type'});}
sub getValue() {return($_[0]->{'value'});}
sub setName() {$_[0]->{'name'} = $_[1];}
sub setType() {$_[0]->{'type'} = $_[1];}
sub setValue() {$_[0]->{'value'} = $_[1];}
sub hasName() {return(defined($_[0]->{'name'}));}
sub hasType() {return(defined($_[0]->{'type'}));}
sub hasValue() {return(defined($_[0]->{'value'}));}
#### For setting pointers, should probably be private or protected
sub setFirstChild() {$_[0]->{'firstchild'} = $_[1];}
sub setNextSibling() {$_[0]->{'nextsibling'} = $_[1];}
sub setParent() {$_[0]->{'parent'} = $_[1];}
sub setLastChild() {$_[0]->{'lastchild'} = $_[1];}
sub setPrevSibling() {$_[0]->{'prevsibling'} = $_[1];}
#### For creation
sub insertSiblingBeforeYou()
{
my($self) = $_[0];
my($oldPrevSibling) = $self->getPrevSibling();
$self->setPrevSibling($_[1]);
$self->getPrevSibling()->setParent($self->getParent());
$self->getPrevSibling()->setNextSibling($self);
if(!defined($oldPrevSibling))
{
$self->getParent()->setFirstChild($self->getPrevSibling());
$self->getPrevSibling()->setPrevSibling(undef);
}
else
{
$self->getPrevSibling()->setPrevSibling($oldPrevSibling);
$oldPrevSibling->setNextSibling($self->getPrevSibling());
}
return($self->getPrevSibling());
}
sub insertSiblingAfterYou()
{
my($self) = $_[0];
my($oldNextSibling) = $self->getNextSibling();
$self->setNextSibling($_[1]);
$self->getNextSibling()->setParent($self->getParent());
$self->getNextSibling()->setPrevSibling($self);
if(!defined($oldNextSibling))
{
if(defined($self->getParent()))
{
$self->getParent()->setLastChild($self->getNextSibling());
}
$self->getNextSibling()->setNextSibling(undef);
}
else
{
$self->getNextSibling()->setNextSibling($oldNextSibling);
$oldNextSibling->setPrevSibling($self->getNextSibling());
}
return($self->getNextSibling());
}
sub insertFirstChild()
{
my($self) = $_[0];
my($oldFirstChild) = $self->getFirstChild();
if(defined($oldFirstChild))
{
$oldFirstChild->insertSiblingBeforeYou($_[1]);
}
else
{
$self->setFirstChild($_[1]);
$self->setLastChild($_[1]);
$self->getFirstChild()->setParent($self);
}
return($self->getFirstChild());
}
sub insertLastChild()
{
my($self) = $_[0];
my($oldLastChild) = $self->getLastChild();
if(defined($oldLastChild))
{
$oldLastChild->insertSiblingAfterYou($_[1]);
}
else
{
$self->setFirstChild($_[1]);
$self->setLastChild($_[1]);
$self->getFirstChild()->setParent($self);
}
return($self->getLastChild());
}
#### For cloning
sub clone()
{
my($self) = $_[0];
my($clone) = Node->new();
$clone->setName($self->getName());
$clone->setType($self->getType());
$clone->setValue($self->getValue());
$clone->setParent($self->getParent());
$clone->setFirstChild($self->getFirstChild());
$clone->setLastChild($self->getLastChild());
$clone->setPrevSibling($self->getPrevSibling());
$clone->setNextSibling($self->getNextSibling());
return($clone);
}
#### For deletion
sub deleteSelf()
{
my($self) = $_[0];
my($prev) = $self->getPrevSibling();
my($next) = $self->getNextSibling();
my($parent) = $self->getParent();
if((defined($self->getPrevSibling()))&&(defined($self->getNextSibling())))
{
$self->getNextSibling()->setPrevSibling($self->getPrevSibling());
$self->getPrevSibling()->setNextSibling($self->getNextSibling());
}
elsif((!defined($self->getPrevSibling()))&&(!defined($self->getNextSibling())))
{
$self->getParent()->setFirstChild(undef);
$self->getParent()->setLastChild(undef);
}
elsif(!defined($self->getPrevSibling()))
{
$self->getParent()->setFirstChild($self->getNextSibling());
$self->getNextSibling()->setPrevSibling(undef);
}
elsif(!defined($self->getNextSibling()))
{
$self->getParent()->setLastChild($self->getPrevSibling());
$self->getPrevSibling()->setNextSibling(undef);
}
$self->setFirstChild(undef);
$self->setLastChild(undef);
}
sub deleteTree()
{
my($self) = $_[0];
# #### Code to delete children and decendents here
$self->deleteSelf();
}
package OutlineParser;
sub new()
{
my($typeOfClass) = $_[0];
my($self) = {};
bless($self, $typeOfClass);
$self->{'head'} = Node->new("Header Node", "Head", "Inserted by OutlineParser");
$self->{'fromstdin'} = 1;
$self->{'zapblanks'} = 1;
return($self);
}
sub setCommentChar($$)
{
$_[0]->{'commentchar'} = $_[1];
}
sub getCommentChar($)
{
return($_[0]->{'commentchar'});
}
sub hasCommentChar($)
{
return(defined($_[0]->{'commentchar'}));
}
sub getFirstNonBlankChar($$)
{
my $self = shift;
my $line = shift;
chomp $line;
my @parts = split(/\s+/,$line, 2);
$line = join('', @parts);
my $firstchar = substr($line, 0, 1);
return $firstchar;
}
sub parse()
{
my($self) = $_[0];
my($fname) = $_[1];
my(@levelStack);
push(@levelStack, ($self->{'head'}));
my($checker) = $self->{'head'};
my($lineno) = 0;
my($prevLevel) = -1;
my($inf);
if($self->{'fromstdin'} == 0)
{
defined($fname) or die "OutlineParser::parse() requires a filename argument, terminating.\n";
open(INF, "<" . $fname) or die "OutlineParser::parse() could not open $fname for input, terminating.\n";
$inf = q(INF);
}
else
{
$inf = qw(STDIN);
}
while(<$inf>)
{
my($line) = $_;
chomp($line);
$lineno++;
my $zapFlag = 0;
my $firstNonBlankChar = $self->getFirstNonBlankChar($line);
if(($self->{'zapblanks'} != 0) && ($firstNonBlankChar eq ''))
{
$zapFlag = 1;
}
if($self->hasCommentChar() && ($self->getCommentChar() eq $firstNonBlankChar))
{
$zapFlag = 1;
}
unless($zapFlag)
{
my($level) = 0;
$line =~ m/^( *)(.*)/;
if(defined($1))
{
$level = length($1);
$line = $2;
}
else
{
$line = $2;
}
my $node = Node->new("", "Node", $line);
$node->setAttribute('_lineno', $lineno);
if($level == $prevLevel)
{
$levelStack[$prevLevel]->insertSiblingAfterYou($node);
$levelStack[$level] = $node;
}
elsif($level == $prevLevel + 1)
{
$levelStack[$prevLevel]->insertFirstChild($node);
$levelStack[$level] = $node;
}
elsif($level > $prevLevel + 1)
{
die "Multiple indent at line $lineno, \"$line\", terminating.\n";
}
elsif($level < $prevLevel)
{
my($dedent) = $prevLevel - $level;
while($level < $prevLevel)
{
pop(@levelStack);
$prevLevel--;
}
$levelStack[$prevLevel]->insertSiblingAfterYou($node);
$levelStack[$level] = $node;
}
$prevLevel = $level;
}
}
if($self->{'fromstdin'} == 0) {close(INF);}
return($self->getHead());
}
sub fromStdin() {$_[0]->{'fromstdin'} = 1;}
sub fromFile() {$_[0]->{'fromstdin'} = 0;}
sub zapBlanks() {$_[0]->{'zapblanks'} = 1;}
sub dontZapBlanks() {$_[0]->{'zapblanks'} = 0;}
sub getHead() {return($_[0]->{'head'});}
package Walker;
sub new()
{
my $typeOfClass = $_[0];
my $self = {};
bless($self, $typeOfClass);
$self->{'top'} = $_[1];
$self->{'entrycallback'} = $_[2];
$self->{'exitcallback'} = $_[3];
return($self);
}
sub walk()
{
my($self) = $_[0];
my($ascending) = 0;
my($checker)=$self->{'top'}; # like a checker you move around a board
my($level)=0;
my($continue) = 1;
my $counter = 0;
while($continue)
{
if($ascending == 0)
{
if(defined($self->{'entrycallback'}))
{
my @args = @{$self->{'entrycallback'}};
my $sub = shift(@args);
push(@args, ($checker, $level));
&{$sub}(@args);
}
if($level < 0) {$continue=0;} ## Callback sets negative to terminate
}
else
{
if(defined($self->{'exitcallback'}))
{
my @args = @{$self->{'exitcallback'}};
my $sub = shift(@args);
push(@args, ($checker, $level));
&{$sub}(@args);
}
if($level < 0) {$continue=0;} ## Callback sets negative to terminate
if($checker == $self->{'top'}) {$continue=0;}
}
if($continue == 0)
{
#skip this if/elsif/else entirely
}
elsif(($ascending == 0) && (defined($checker->getFirstChild())))
{
$ascending = 0;
$checker = $checker->getFirstChild();
$level++;
}
elsif((defined($checker->getNextSibling())) && ($checker != $self->{'top'}))
{
$ascending = 0;
$checker = $checker->getNextSibling();
}
elsif(defined($checker->getParent()))
{
$ascending = 1;
$checker = $checker->getParent();
$level--;
# if($level < 1) {$continue = 0;}
}
else
{
$continue = 0;
}
$counter++;
}
}
1;

View file

@ -0,0 +1,177 @@
MANUAL FOR THE Node.pm Tool
Version 0.2.0 released 5/13/2004
License
Litt Perl Development Tools License, version 1
See COPYING file
This license is the GNU GPL with an exception
See COPYING.GPL
NO WARRANTY!!!!! See COPYING.GPL
Purpose
Handling hierarchies in Perl
Implements a tree of nodes
Each node has a name, a type, a value, and optionally attributes
Each node can have zero, one or many attributes
Each attribute has a name and a value
Especially made to handle tab indented outlines in memory
Learning Node.pm
Learn from the example programs: Study them in this order:
example_hello.pl
example_parse.pl
example_otl2markup.pl
example_attribs.pl
example_bylevel.pl
example_delete.pl
example_insert.pl
example_nodepath.pl
That's the only way to learn this tool
Do each example program in order
Run
Read
Study
Example programs
example_hello.pl
Proof of concept
7 lines of perl
Instantiate and read from a Node object
example_parse.pl
Parse a tab indented outline file into a Node tree
Use a Walker object to walk the tree and print the Nodes
Create a Callback object and callback routine
example_otl2markup.pl
Use return callback to create end tags
example_attribs.pl
Set and get Node attributes
Observer "_lineno" attribute,
which was set by the Parser object
example_bylevel.pl
Read the Node tree recursively and output by level
Use the Callbacks object to store information
Use multiple callbacks to simplify a complex algorithm
Observe that the performance is still quite good
example_delete.pl
Delete nodes during a callback
Observe that the Walker object takes the deletion in stride
Observe that the Walker object does not "lose its place"
example_insert.pl
Insert nodes
Build a Node tree without an outline or Parser object
Create child nodes using an array
Create child nodes from a callback routine
Use a return callback to count occurrences within in its subtree
Access and use a Node's name, type and value
Move nodes within the tree
example_nodepath.pl
Load a Node.pm file in a different directory
Load a Node.pm file at runtime from an entry in a config file
Use an environment variable to change the name of the config file
File manifest
Documentation
INSTALL
README.otl
Licensing
COPYING
COPYING.GPL
COPYING.LPDTL.1.0
LPDTL_discuss.txt
Node.pm file
Node.pm
Example Programs
example_hello.pl
example_parse.pl
example_otl2markup.pl
example_attribs.pl
example_bylevel.pl
example_delete.pl
example_insert.pl
example_nodepath.pl
Sample node path config file (for example_nodepath.pl)
myapp.cfg
Sample outline (used for example_delete.pl)
deletetest.otl
Objects
Node.pm implements three object types:
Node
OutlineParser
Walker
Node
A single element in the hierarchy
All nodes connected
Parent pointer
(the pointer is a Perl reference, of course)
First child pointer
Last child pointer
Previous sibling pointer
Last sibling pointer
Each node has:
A name
hasName()
getName()
setName()
A type
hasType()
getType()
setType()
A value
hasValue()
getValue()
setValue()
Zero, one or many attributes
Each attribute has a name and a value
Single attribute methods:
hasAttribute()
getAttribute()
setAttribute()
removeAttribute()
Attribute array methods:
hasAttributes()
getAttributes()
setAttributes()
Node methods enable all hierarchy operations
Node insertion
insertFirstChild()
insertLastChild()
insertSiblingBeforeYou()
insertSiblingAfterYou()
Node deletion
deleteSelf()
Low level node traversal
hasFirstChild()
getFirstChild()
hasLastChild()
getLastChild()
hasPrevSibling()
getPrevSibling()
hasNextSibling()
getNextSibling()
hasParent()
getParent()
OutlineParser
Object to convert a tab indented outline to a tree of Node objects
Outline must be well formed...
Each line indented zero or one tab to the right of its parent
You can set and access properties of the parse
hasCommentChar()
getCommentChar()
setCommentChar()
fromStdin()
fromFile()
zapBlanks()
dontZapBlanks()
After setting parse properties, you perform the parse
my parser = OutlinerParser->new();
parser->setCommentChar('#');
parser->fromFile();
my $topNode = parser->parse("myoutline.txt");
Walker
Object to traverse entire Node hierarchy
Performs actions via callback routines
my $walker = Walker->new($topNode, $entryCallbackRef, $returnCallbackRef);
Entry callback occurs when node is first accessed
Return callback occurs when node is re-accessed after going through children
Callbacks should be object methods, not freestanding
Callbacks must take 3 arguments:
$self: The object containing the callback
[\&Callbacks::cbPrintNodeInfo, $callbacks]);
Installation
See INSTALL file

View file

@ -0,0 +1,18 @@
Top
Level2
Level2b
Level3
deleteme
gone
gone
deleteme
gone
gone
this should stay
deleteme
deleteme
gone
gone
Level3b
2level2
Top2

View file

@ -0,0 +1,162 @@
#!/usr/bin/perl -w
# Copyright (C) 2004 by Steve Litt
# Licensed with the GNU General Public License, Version 2
# ABSOLUTELY NO WARRANTY, USE AT YOUR OWN RISK
# See http://www.gnu.org/licenses/gpl.txt
#####################################################################
# This exercise demonstrates the use of attributes for each node.
# Attributes are facts about an entity, rather than an entity itself.
# In real practice, many times attributes can be substituted for nodes
# and vice versa. However, an attribute CANNOT have children.
#
# This is the first exercise using multiple Walker objects. The first
# Walker object counts each node's children, and if the node has
# children, it creates an attribute named "children" for that node.
# The value of the attribute is the number of direct children for
# that node.
#
# Nodes are accessed two ways in the cbPrintNode() callback. The entire
# attribute hash is accessed with hasAttributes and getAttributes(),
# while single named attributes are accessed with hasAttributes and
# getAttributes().
#
# One more action that's demonstrated is the use of secondary navigation
# within a callback routine. For each node, the callback routine
# navigates to the first child and then each successive sibling of that
# child in order to count the direct children. This is a common
# algorithm with Node.pm. It might look inefficient, and you might be
# tempted to perform the count during the callback that prints the
# information. Don't do it. Multiple walkers help keep Node.pm
# enabled programs easy to understand and modify. Because the
# entire node tree is in memory, the double navigation isn't
# particularly slow.
#
# Real world programs make heavy use of multiple walkers. For instance,
# the EMDL to UMENU program (not packaged here) has over 10 walkers.
#
#####################################################################
use strict; # prevent hard to find errors
use Node; # Use Note.pm tool
package Callbacks;
sub new()
{
my($type) = $_[0];
my($self) = {};
bless($self, $type);
$self->{'errors'} = 0;
$self->{'warnings'} = 0;
return($self);
}
sub getErrors(){return $_[0]->{'errors'};}
sub getWarnings(){return $_[0]->{'warnings'};}
sub cbCountChildren()
{
my($self, $checker, $level) = @_;
unless (defined($checker)) {return -999;}
my $childCount=0;
if($checker->hasFirstChild())
{
$childCount++;
my $checker2 = $checker->getFirstChild();
while($checker2->hasNextSibling())
{
$childCount++;
$checker2 = $checker2->getNextSibling();
}
$checker->setAttribute("children", $childCount);
}
}
sub cbPrintNode()
{
my($self, $checker, $level) = @_;
unless (defined($checker)) {return -999;} # don't process undef node
for(my $n=0; $n < $level; $n++) {print "\t";}
print "* ";
print $checker->getValue(); # print the text of the node
print "\n";
for(my $n=0; $n <= $level; $n++) {print "\t";}
print "(";
my %attribs = {};
%attribs = $checker->getAttributes() if $checker->hasAttributes();
my @keys = keys(%attribs);
foreach my $key (sort @keys)
{
print $key, "=", $attribs{$key}, "; ";
}
print ")\n";
if($checker->hasAttribute("children"))
{
for(my $n=0; $n <= $level; $n++) {print "\t";}
print "This node has ";
print $checker->getAttribute("children");
print " children.\n";
}
}
package Main;
sub main()
{
#### PARSE FROM FILE README.otl
my $parser = OutlineParser->new(); # instantiate parser
$parser->setCommentChar("#"); # ignore lines starting with #
$parser->fromFile(); # get input from file
my $topNode=$parser->parse("README.otl");
#====================================================================
# The preceding statement parses file README.otl into a node hierarchy
# and assigns the top level node of that hierarchy to $topNode. When
# you run the program you'll notice that the text in $topNode does
# not appear in README.otl, but instead has value
# "Inserted by OutlineParser".
#
# This is a feature, not a bug. In order to accommodate the typical
# case of an outline having several top level items, and yet still
# be able to represent the whole hierarchy as a single top node,
# the OutlineParser object creates a new node with value
# " Inserted by OutlineParser"
# and places all the outline's top level items under that newly
# created node.
#
# If the outline you're working on is guaranteed to have only
# a single top level item, and if you want that to be the top
# level node, you can simply do the following:
#
# $topNode=$topNode->getFirstChild();
#====================================================================
#### INSTANTIATE THE Callbacks OBJECT
my $callbacks = Callbacks->new();
#### WALK THE NODE TREE,
#### OUTPUTTING LEVEL AND TEXT
my $walker = Walker->new
(
$topNode,
[\&Callbacks::cbCountChildren, $callbacks]
);
$walker->walk();
my $walker = Walker->new
(
$topNode,
[\&Callbacks::cbPrintNode, $callbacks]
);
$walker->walk();
}
main();

View file

@ -0,0 +1,227 @@
#!/usr/bin/perl -w
# Copyright (C) 2004 by Steve Litt
# Licensed with the GNU General Public License, Version 2
# ABSOLUTELY NO WARRANTY, USE AT YOUR OWN RISK
# See http://www.gnu.org/licenses/gpl.txt
use strict; # prevent hard to find errors
use Node; # Use Node.pm tool
#####################################################################
# The Walker object walks the node hierarchy recursively. That is,
# it goes deep before going laterally. That's just what's needed for
# many applications. However, sometimes it's necessary to look at
# one level at a time.
#
# There are many ways to accomplish this. Some involve sorting and
# merging. Many involve arrays of nodes on a given level, and
# plunging one deep into each one.
#
# In this example we'll start with a walker that assigns the full
# path to each node as an attribute of that node. We'll then loop
# through all levels starting with 0, and for each one we'll print all
# children of nodes at that level. Every time there's a parent change,
# we'll print a header for that parent.
#
# This example also illustrates the use of variables within the
# Callback object. You might have wondered why callbacks must be
# part of an object rather than free floating functions. The answer
# is that the use of callbacks as object methods means that we can
# keep totals and break logic variables within the callback object,
# thereby eliminating the (nasty) necessity of global variables.
#
# We cannot simply pass variables into and out of callback
# routines because, by the very nature of a callback routine,
# its arguments and return type are strictly predefined. In the
# case of Node.pm the arguments are always $self, $checker and
# $level. To get any other information into or out of the callback
# routine, we must use a non-callback method of the same object.
#
# It should be noted that there's nothing wrong with having
# multiple callback objects. If there are numerous callback
# routines it might make sense to group them by functionality,
# or by totals and persistent variables they must keep track of.
#
# As you run, study and understand this code, be aware that converting
# a hierarchy to a list by levels is a very difficult and complex task.
# Imagine keeping a list of children, and for each level using those
# children to find the next generation, and rewriting the array. Or
# prepending a level number followed by a child index on each line,
# and then sorting the whole array by the level number and child
# index, and finally running a routine to output the formatted
# output, complete with break logic and headers.
#
# Now consider how easy Node.pm made this job. First, a trivial
# Walker to calculate full paths, then a level loop calling a
# Walker to print only children of nodes at the desired level. The
# code is short, and it's very readable and understandable. The
# callback routines are short enough that you can safely use non-
# structured techniques such as returning from the middle instead
# of using nested if statements. The result is even more readability.
#
# One could make the (very valid) point that nodes are visited many
# times to process each once, and that this is not efficient in
# terms of runtime performance. Absolutely true!
#
# However, the programming simplicity prevents truly collosal
# efficency problems, such as cascading intermediate files, sorts,
# and the various other CPU cycle grabbers that seem to crop up
# in complex algorithms. And remember, the entire tree is in memory,
# with navigation via simple pointers, so the environment of Node.pm
# favors runtime speed.
#
# Case in point. My original EMDL to UMENU converter was such an
# epic production that I needed to study it for 4 hours every time
# I made a minor improvement. I had developed it using informal OOP
# and structured techniques, and had paid close attention to
# efficiency. The resulting program took 15 seconds to convert a
# 2300 line EMDL file.
#
# I rewrote the converter using Node.pm. This was a complete
# rewrite -- all new code -- no salvage. It was so much simpler
# that I wrote it in 12 hours. But I was very concerned with
# runtime. If the 15 seconds doubled, this would be a hassle,
# and if it quadrupled it would be totally impractical. When
# I ran it, the program did everything the original did, but
# did it in 2 seconds. Node.pm had given me a 7 fold speed
# increase.
#
#####################################################################
package Callbacks;
sub new()
{
my($type) = $_[0];
my($self) = {};
bless($self, $type);
$self->{'errors'} = 0;
$self->{'warnings'} = 0;
$self->{'childrenatlevel'} = 0;
$self->{'currentlevel'} = 0;
$self->{'previousparentfullpath'} = "initialize";
return($self);
}
sub getErrors(){return $_[0]->{'errors'};}
sub getWarnings(){return $_[0]->{'warnings'};}
sub getChildrenAtLevel(){return $_[0]->{'childrenatlevel'};}
sub setChildrenAtLevel(){$_[0]->{'childrenatlevel'} = $_[1];}
sub incChildrenAtLevel(){$_[0]->{'childrenatlevel'}++;}
sub getCurrentLevel(){return $_[0]->{'currentlevel'};}
sub setCurrentLevel(){$_[0]->{'currentlevel'} = $_[1];}
sub cbCalculateFullPath()
{
my($self, $checker, $level) = @_;
unless (defined($checker)) {return -999;} # don't process undef node
if($checker->hasParent)
{
my $fullpath = $checker->getParent()->getAttribute("fullpath");
$fullpath .= "/";
$fullpath .= $checker->getValue();
$checker->setAttribute("fullpath", $fullpath);
}
else
{
$checker->setAttribute("fullpath", $checker->getValue());
}
}
sub cbPrintNode()
{
my($self, $checker, $level) = @_;
unless (defined($checker)) {return -999;} # don't process undef node
#### DO NOTHING UNLESS THIS NODE IS AT THE CURRENTLY SOUGHT LEVEL
return unless $level == $self->getCurrentLevel();
#### DO NOTHING UNLESS THIS NODE HAS CHILDREN
return unless $checker->hasFirstChild();
#### PRINT HEADER
print "\n", $checker->getAttribute("fullpath"), "\n";
#### PRINT CHILDREN AND COUNT CHILDREN AT LEVEL
my $checker2 = $checker->getFirstChild(); # We returned if there wasn't one
print "\t", $checker2->getValue(), "\n";
$self->incChildrenAtLevel();
while($checker2->hasNextSibling())
{
$checker2 = $checker2->getNextSibling();
print "\t", $checker2->getValue(), "\n";
$self->incChildrenAtLevel();
}
}
package Main;
sub main()
{
#### PARSE FROM FILE README.otl
my $parser = OutlineParser->new(); # instantiate parser
$parser->setCommentChar("#"); # ignore lines starting with #
$parser->fromFile(); # get input from file
my $topNode=$parser->parse("README.otl");
#### INSTANTIATE THE Callbacks OBJECT
my $callbacks = Callbacks->new();
#### WALK THE NODE TREE,
#### CALCULATING FULL PATHS AND PUTTING THEM IN AN ATTRIBUTE
my $walker = Walker->new
(
$topNode, # start with this node
[\&Callbacks::cbCalculateFullPath, $callbacks] # do this on entry to each node
);
$walker->walk();
#### PRINT LEVEL 0
print "\n\n********** BEGIN LEVEL ", "0", "\n";
print "\t", $topNode->getValue(), "\n";
#### SET STARTING PARENT LEVEL,
#### AND SET $childCount SO THE LOOP WILL FIRE THE FIRST TIME
my $level=0;
my $childCount=9999;
#==================================================================
# The main loop follows, level by level. At each level, nodes are
# queried for their children, which are then printed below the
# node's full path. The result is a list of nodes sorted by
# level.
#
# We add 1 to the level in the level header because we're referring
# to the level of the children, not of the current node. We keep
# looping to deeper levels until a level counts no children.
#
# This logic result in an empty level header at the bottom. If this
# were a big concern, we could print the level headers in the
# Callbacks::cbPrintNode() callback, with slightly altered logic.
# However, it's a minor point, so for simplicity we print the
# level header at the top of this loop in the main routine.
#==================================================================
while($childCount > 0)
{
print "\n\n********** BEGIN LEVEL ", $level + 1, "\n";
$callbacks->setChildrenAtLevel(0);
$callbacks->setCurrentLevel($level);
my $walker = Walker->new
(
$topNode,
[\&Callbacks::cbPrintNode, $callbacks]
);
$walker->walk();
$childCount = $callbacks->getChildrenAtLevel();
$level++;
}
}
main();

View file

@ -0,0 +1,107 @@
#!/usr/bin/perl -w
# Copyright (C) 2004 by Steve Litt
# Licensed with the GNU General Public License, Version 2
# ABSOLUTELY NO WARRANTY, USE AT YOUR OWN RISK
# See http://www.gnu.org/licenses/gpl.txt
use strict; # prevent hard to find errors
use Node; # Use Node.pm tool
#####################################################################
# This exercise demonstrates the deletion of nodes.
#
# Because Perl is a garbage collection language, node deletion
# DOES NOT deallocate memory and the like. However, in the absense
# of a copy of the node, it will be garbage collected and unavailable.
# Also, the deletion process specificly undef's the deleted node's
# first and last children.
#
# You noticed I mentioned keeping a copy. The algorithm of a Walker
# object moves a node around the tree like a checker. Calling
# $checker->deleteSelf() does not render $checker undefined. In fact,
# it still has its parent, nextSibling and previousSibling pointers
# intact. What this means is that the Walker's next iteration goes
# to exactly the same node as it would have if the deletion had not
# taken place. In other words, you do not need to "move the checker
# back one" after a deletion.
#
# This makes deletion algorithms very simple.
#
# There may come a time when you want to delete a node but keep its
# children. In that case, you must first attach its children to nodes
# that will not be deleted.
#
#####################################################################
package Callbacks;
sub new()
{
my($type) = $_[0];
my($self) = {};
bless($self, $type);
$self->{'errors'} = 0;
$self->{'warnings'} = 0;
return($self);
}
sub getErrors(){return $_[0]->{'errors'};}
sub getWarnings(){return $_[0]->{'warnings'};}
sub cbDelete()
{
my($self, $checker, $level) = @_;
unless (defined($checker)) {return -999;} # don't process undef node
#### DELETE THIS NODE IF ITS VALUE CONTAINS deleteme
my $text = "init";
$text = $checker->getValue() if $checker->hasValue();
if($text =~ m/deleteme/)
{
$checker->deleteSelf();
}
}
sub cbPrintNode()
{
my($self, $checker, $level) = @_;
unless (defined($checker)) {return -999;}
for(my $n=0; $n < $level; $n++) {print "\t";}
print $checker->getValue(), "\n";
}
package Main;
sub main()
{
#### PARSE FROM FILE README.otl
my $parser = OutlineParser->new();
$parser->setCommentChar("#");
$parser->fromFile();
my $topNode=$parser->parse("deletetest.otl");
#### INSTANTIATE THE Callbacks OBJECT
my $callbacks = Callbacks->new();
#### WALK THE NODE TREE,
#### DELETING NODES WITH "deleteme" IN THEM
my $walker = Walker->new
(
$topNode,
[\&Callbacks::cbDelete, $callbacks]
);
$walker->walk();
#### WALK THE NODE TREE,
#### OUTPUTTING LEVEL AND TEXT
$walker = Walker->new
(
$topNode,
[\&Callbacks::cbPrintNode, $callbacks]
);
$walker->walk();
}
main();

View file

@ -0,0 +1,15 @@
#!/usr/bin/perl -w
# Copyright (C) 2004 by Steve Litt
# Licensed with the GNU General Public License, Version 2
# ABSOLUTELY NO WARRANTY, USE AT YOUR OWN RISK
# See http://www.gnu.org/licenses/gpl.txt
use strict; # prevent hard to find errors
use Node;
my $topNode = Node->new("myname", "mytype", "myvalue");
print "\n::: ";
print $topNode->getName(), " ::: ";
print $topNode->getType(), " ::: ";
print $topNode->getValue(), " :::\n";

View file

@ -0,0 +1,550 @@
#!/usr/bin/perl -w
# Copyright (C) 2004 by Steve Litt
# Licensed with the GNU General Public License, Version 2
# ABSOLUTELY NO WARRANTY, USE AT YOUR OWN RISK
# See http://www.gnu.org/licenses/gpl.txt
use strict; # prevent hard to find errors
use Node; # Use Node.pm tool
#####################################################################
# WARNING: This is a difficult exercise. Do not attempt this exercise
# until you have completed the prerequisites listed in the README.otl
# file.
#
# I suggest you approach this example starting with the main routine
# at the bottom of the file, and then drilling down into subroutines
# and callbacks. Understand the big picture before drilling down.
#
# This exercise demonstrates insertion of nodes, and much, much more.
# Insertion is accomplished by the insertFirstChild(),
# insertSiblingBeforeYou(), insertSiblingAfterYou(), and
# insertLastChild() methods. The insertLastChild() method is not
# demonstrated.
#
# This exercise is VERY contrived. It is contrived to show techniques
# of building a node tree using insertions, and also how to switch
# two nodes. The switching of the two nodes is especially contrived,
# but I could think of no better way of demonstrating node moving.
#
# This exercise builds a tree that represents a date book type calendar.
# Level 0 is Calender, level 1 are the years, of which there is only 2004,
# Level 2 are the months, level 3 the days, and level 4 the hour long
# timeslots. There is no provision for weekends, nor after hours
# appointments. It is a demonstration only.
#
# Using an array of month names and an array of days per month, you build
# the month and day levels using a nested loop. The hour level is built
# using a Walker. Node names are things like "January" or 31 or
# "11:00-12:00". Node types are things like "Year", "Month", "Day" or
# "Hour". Node values are undefined unless an appointment is made, in
# which case the value is the node text.
#
# A special Walker is used to mark any day, month or year entities
# if they contain appointments. Specifically, all appointments in that
# day, month or year are counted, and that number of stars are placed
# beside the day, month or year. This is implemented by using an
# return callback so that by the time the callback is called, all children
# have been calculated.
#
#####################################################################
package Callbacks;
sub new()
{
my($type) = $_[0];
my($self) = {};
bless($self, $type);
$self->{'errors'} = 0;
$self->{'warnings'} = 0;
return($self);
}
sub getErrors(){return $_[0]->{'errors'};}
sub getWarnings(){return $_[0]->{'warnings'};}
#=================================================================
# The cbMakeMarks() callback is called on return to a node from
# its children (return callback). It executes only on year, month
# and day nodes. It iterates through all its immediate children,
# totalling up the "appointments" attribute and setting its
# own attribute to that total. Remember, because this is a
# callback triggered on return from children, it is guaranteed
# that all children have been counted, and that all those children
# have totalled their children, etc.
#
# In the case of a day node, instead of totalling the "appointments"
# attribute, it counts the number of hour nodes with defined values.
# A defined value on an hour node is an appointment.
#
# Last but not least, on non-zero counts, this callback sets the
# day, month or year node's value to a number of asterisks equal
# to the number of appointments in its subtree.
#
# Read this code carefully. Once you understand it, you'll have
# many insights to Node.pm.
#=================================================================
sub cbMakeMarks()
{
my($self, $checker, $level) = @_;
unless (defined($checker)) {return -999;} # don't process undef node
#### PROCESS ONLY DAY, MONTH OR YEAR NODES
unless (
$checker->getType() eq "Day" ||
$checker->getType() eq "Month" ||
$checker->getType() eq "Year"
)
{
return;
}
my $count = 0;
my $childNode = $checker->getFirstChild();
while(defined($childNode))
{
if($checker->getType() eq "Day")
{
if(defined($childNode->getValue()))
{
$count++;
}
}
else
{
if($childNode->hasAttribute("appointments"))
{
$count += $childNode->getAttribute("appointments");
}
}
$childNode = $childNode->getNextSibling();
}
$checker->setAttribute("appointments", $count);
if($count > 0)
{
my $string;
for(my $n=0; $n < $count; $n++){$string .= '*';}
$checker->setValue($string);
}
}
#=================================================================
# The cbInsertHours() callback operates ONLY on day nodes. When
# called from a day node, it inserts hourlong appointment slots
# starting at 8am and ending at 5pm. The code is pretty
# straightforward.
#=================================================================
sub cbInsertHours()
{
my($self, $checker, $level) = @_;
unless (defined($checker)) {return -999;} # don't process undef node
return unless $checker->getType() eq "Day"; # Insert hours under days only
my $checker2;
for(my $n=8; $n <= 16; $n++)
{
my $startHour = "$n:00";
my $n2 = $n + 1;
my $endHour = "$n2:00";
my $node = Node->new("$startHour" . "-" . "$endHour", "Hour", undef);
if($checker->hasFirstChild())
{
$checker2 = $checker2->insertSiblingAfterYou($node);
}
else
{
$checker2 = $checker->insertFirstChild($node);
}
}
}
#=================================================================
# The cbPrintNode() callback prints the name of the node,
# and its value if a value is defined. It's very straighforward.
#=================================================================
sub cbPrintNode()
{
my($self, $checker, $level) = @_;
unless (defined($checker)) {return -999;} # don't process undef node
#### DON'T PRINT LEVEL 0 (CALENDER)
return if $level == 0;
for(my $n=1; $n < $level; $n++) { print "\t";}
print $checker->getName() if $checker->hasName();
print ": ";
print $checker->getValue() if $checker->hasValue();
print "\n";
}
#=================================================================
# The cbPrintNodeDiagnosic() callback is not used, but provided
# for any necessary debugging.
#=================================================================
sub cbPrintNodeDiagnostic()
{
my($self, $checker, $level) = @_;
unless (defined($checker)) {return -999;} # don't process undef node
for(my $n=0; $n < $level; $n++) { print "\t";}
print ">";
print $checker->getName() if $checker->hasName();
print " ::: ";
print $checker->getType() if $checker->hasType();
print " ::: ";
print $checker->getValue() if $checker->hasValue();
print "<\n";
}
package Main;
###########################################################################
# The insertDays() subroutine handles insertion of days below all
# month nodes.
###########################################################################
sub makeAppointments($)
{
my $yearNode = shift;
#### MARCH 22 AT 8AM
my $monthNode = $yearNode->getFirstChild() -> #January
getNextSibling() -> #February
getNextSibling(); #March
my $dayNode = $monthNode->getFirstChild();
while($dayNode->getName() != 22)
{
$dayNode = $dayNode->getNextSibling();
unless(defined($dayNode))
{
die "No March 22\n";
}
}
my $hourNode = $dayNode->getFirstChild();
$hourNode->setValue("Spring Cleaning");
#### JUNE 22 AT 9AM
#### WRONGLY LABELED AS FALL FESTIVAL
#### INSTEAD OF SUMMER BREAK
$monthNode = $monthNode->getNextSibling() -> # April
getNextSibling() -> # May
getNextSibling(); # June
$dayNode = $monthNode->getFirstChild();
while($dayNode->getName() != 22)
{
$dayNode = $dayNode->getNextSibling();
unless(defined($dayNode))
{
die "No June 22\n";
}
}
$hourNode = $dayNode->getFirstChild()->getNextSibling();
$hourNode->setValue("Fall Festival");
#### SEPTEMBER 22 AT 10AM
#### WRONGLY LABELED AS FALL FESTIVAL
#### INSTEAD OF SUMMER BREAK
$monthNode = $monthNode->getNextSibling() -> # July
getNextSibling() -> # August
getNextSibling(); # September
$dayNode = $monthNode->getFirstChild();
while($dayNode->getName() != 22)
{
$dayNode = $dayNode->getNextSibling();
unless(defined($dayNode))
{
die "No September 22\n";
}
}
$hourNode = $dayNode -> getFirstChild() -> #8-9
getNextSibling() -> # 9-10
getNextSibling(); # 10-11
$hourNode->setValue("Summer Break");
#### DECEMBER 22 FROM 3PM TO 5PM (2 TIMESLOTS)
#### HAPPY HOLIDAYS PARTY
$monthNode = $monthNode->getNextSibling() -> # October
getNextSibling() -> # November
getNextSibling(); # December
$dayNode = $monthNode->getFirstChild();
while($dayNode->getName() != 22)
{
$dayNode = $dayNode->getNextSibling();
unless(defined($dayNode))
{
die "No December 22\n";
}
}
$hourNode = $dayNode->getFirstChild();
while($hourNode->getName() ne "15:00-16:00")
{
$hourNode = $hourNode->getNextSibling();
unless(defined($hourNode))
{
die "No 4pm slot\n";
}
}
$hourNode->setValue("Happy Holidays Party");
$hourNode = $hourNode->getNextSibling();
$hourNode->setValue("Happy Holidays Party");
#### DECEMBER 30 AT 9AM BUY PARTY SUPPLIES
while($dayNode->getName() != 30)
{
$dayNode = $dayNode->getNextSibling();
unless(defined($dayNode))
{
die "No December 30\n";
}
}
$hourNode = $dayNode->getFirstChild()->getNextSibling();
$hourNode->setValue("Buy Party Supplies");
}
###########################################################################
# The insertMonthsAndDays() subroutine handles insertion of months
# below the year, and days below every month. It works by iterating through
# an array of months, and finding number of days in an array of month
# lengths. It does NOT use the Node.pm navigational system to find months.
# Use of the Node.pm navigational system for this purpose is demonstrated
# in the insertion of hours in all days.
#
# Note that we could have avoided using a nested loop by using a Walker
# and associated callback to install the days under every month. In such
# a case the array of month lengths would have been placed in the Callback
# object. However, for the sake of variety, we chose to use a nested loop
# to load the months and days.
###########################################################################
sub insertMonthsAndDays($)
{
my $yearNode = shift;
my $checker = $yearNode;
my $checker2;
my @monthNames=("January", "February", "March", "April", "May",
"June", "July", "August", "September", "October",
"November", "December");
my @monthLengths=(31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31);
my $monthSS = 0;
foreach my $monthName (@monthNames)
{
my $node = Node->new($monthName, "Month", undef);
$node->setAttribute("days", $monthLengths[$monthSS]);
if($yearNode->hasFirstChild())
{
$checker = $checker->insertSiblingAfterYou($node);
}
else
{
$checker = $yearNode->insertFirstChild($node);
}
for(my $n=1; $n <= $monthLengths[$monthSS]; $n++)
{
$node = Node->new($n, "Day", undef);
if($checker->hasFirstChild())
{
$checker2 = $checker2->insertSiblingAfterYou($node);
}
else
{
$checker2 = $checker->insertFirstChild($node);
}
}
$monthSS++;
}
}
###########################################################################
# This subroutine switches the June 22 9am appointment and the
# September 22 10am appointment. In each case, both the appointment
# text and the time needed switching.
#
# The sane way to accomplish this task would have been to modify
# the nodes in place. However, this subroutine was created solely to
# demonstrate the movement of nodes, so that's what we did.
#
# Note that the fact that the two are at different times complicates the
# situation. It's not enough to just trade nodes -- the Sept 9am node
# must be placed after the existing June 10am node, which itself is after
# the erroneous June 9am node containing what should be September's
# appointment. After such placement, the original June 9am node must
# have its name updated so that it is a 10am node. A similar process
# takes place for September. The original nodes are also deleted.
#
# Please follow the (convoluted and contrived) logic:
# 1. Store the June hour node in $juneNode
# 2. Store the September hour node in $septNode
# 3. After the existing June 10am, place a CLONE of the Sept appointment
# 4. Before the existing Sept 9am, place a CLONE of the June appointment
# 5. Delete the original June appointment
# 6. Delete the original September appointment
# 7. On the original June 10am node, make it 9am
# 8. On the original September 9am node, make it 10am
###########################################################################
sub switchJuneAndSeptemberAppointments($)
{
my $yearNode = shift;
#### FIND NODE FOR JUNE 22 9AM APPOINTMENT
my $juneNode = $yearNode->getFirstChild();
while(defined($juneNode))
{
last if $juneNode->getName() eq "June";
$juneNode = $juneNode->getNextSibling();
}
die "Cannot find month of June\n" unless defined($juneNode);
$juneNode = $juneNode->getFirstChild();
while(defined($juneNode))
{
last if $juneNode->getName() eq "22";
$juneNode = $juneNode->getNextSibling();
}
die "Cannot find June 22\n" unless defined($juneNode);
$juneNode = $juneNode->getFirstChild();
while(defined($juneNode))
{
last if $juneNode->getName() eq "9:00-10:00";
$juneNode = $juneNode->getNextSibling();
}
die "Cannot find June 22 at 9am\n" unless defined($juneNode);
#### FIND NODE FOR SEPTEMBER 22 10AM APPOINTMENT
my $septNode = $yearNode->getFirstChild();
while(defined($septNode))
{
last if $septNode->getName() eq "September";
$septNode = $septNode->getNextSibling();
}
die "Cannot find month of September\n" unless defined($septNode);
$septNode = $septNode->getFirstChild();
while(defined($septNode))
{
last if $septNode->getName() eq "22";
$septNode = $septNode->getNextSibling();
}
die "Cannot find September 22\n" unless defined($septNode);
$septNode = $septNode->getFirstChild();
while(defined($septNode))
{
last if $septNode->getName() eq "10:00-11:00";
$septNode = $septNode->getNextSibling();
}
die "Cannot find September 22 at 9am\n" unless defined($septNode);
#### SWITCH THE NODES
my $newJune = $juneNode->getNextSibling()->insertSiblingAfterYou($septNode->clone());
my $newSept = $septNode->getPrevSibling()->insertSiblingBeforeYou($juneNode->clone());
$juneNode->deleteSelf();
$septNode->deleteSelf();
#### FIX NAMES OF SURROUNDING CLONES
$newJune->getPrevSibling()->setName("9:00-10:00");
$newSept->getNextSibling()->setName("10:00-11:00");
return;
}
###########################################################################
# In the main routine, you carry out or delegate the following tasks
# in order to create an appointment calendar:
# 1. Insert single level 0 and 1 nodes
# 2. Instantiate the Callbacks object
# 3. Insert all month and day nodes
# 4. Insert all hour nodes
# 5. Make appointments
# erroneously switching the june 22 & Sept 22 appointments
# 6. Mark days, months and years containing appointments
# 7. Output the calendar
# 8. Switch back June22 and Sept22
# 9. Re mark days, months and years
# 10. Output a separator between bad and good calendars
# 11. Re output the calendar
#
###########################################################################
sub main()
{
#### INSERT SINGLE LEVEL 0 AND 1 NODES
my $topNode=Node->new("Calender", "Calender", "Calender");
my $yearNode=$topNode->insertFirstChild(Node->new("2004", "Year", undef));
#### INSTANTIATE THE Callbacks OBJECT
my $callbacks = Callbacks->new();
#### INSERT MONTH AND DAY NODES
insertMonthsAndDays($yearNode);
#### INSERT THE HOURS USING A Walker
my $walker = Walker->new
(
$topNode,
[\&Callbacks::cbInsertHours, $callbacks]
);
$walker->walk();
#### MAKE A FEW APPOINTMENTS
#### ACCIDENTALLY SWITCHING SUMMER AND FALL
makeAppointments($yearNode);
#### MARK DAYS, MONTHS AND YEAR THAT HAVE APPOINTMENTS
#### USING A WALKER WITH ONLY A RETURN CALLBACK
$walker = Walker->new
(
$topNode,
undef,
[\&Callbacks::cbMakeMarks, $callbacks]
);
$walker->walk();
#### WALK THE NODE TREE,
#### OUTPUTTING THE CALENDER
$walker = Walker->new
(
$topNode, # start with this node
[\&Callbacks::cbPrintNode, $callbacks] # do this on entry to each node
);
$walker->walk();
#### CORRECT THE MISTAKE
#### SWITCH JUNE 22 AND SEPT 22
switchJuneAndSeptemberAppointments($yearNode);
#### RE-MARK DAYS, MONTHS AND YEAR THAT HAVE APPOINTMENTS
#### USING A WALKER WITH ONLY A RETURN CALLBACK
$walker = Walker->new
(
$topNode,
undef,
[\&Callbacks::cbMakeMarks, $callbacks]
);
$walker->walk();
#### OUTPUT A SEPARATOR BETWEEN ORIGINAL AND CORRECTED CALENDARS
for (my $n=0; $n<5; $n++)
{
print "######################################################\n";
}
#### RE-WALK THE NODE TREE,
#### RE-OUTPUTTING THE CALENDER
$walker = Walker->new
(
$topNode, # start with this node
[\&Callbacks::cbPrintNode, $callbacks] # do this on entry to each node
);
$walker->walk();
}
main();

View file

@ -0,0 +1,135 @@
#!/usr/bin/perl -w
# Copyright (C) 2004 by Steve Litt
# Licensed with the GNU General Public License, Version 2
# ABSOLUTELY NO WARRANTY, USE AT YOUR OWN RISK
# See http://www.gnu.org/licenses/gpl.txt
use strict; # prevent hard to find errors
#####################################################################
# Node.pm is a tool you will probably use in many projects located
# in varying directories. How do you enable those projects to
# include Node.pm? Here are some ways:
# 1. Place Node.pm in the project's directory
# 2. Place Node.pm on Perl's module path
# 3. Run the project as perl -I/path/to/Node project.pl
# 4. Shebang line #!/usr/bin/perl -w -I/path/to/Node
#
# Number 1 can become problematic as the number of apps using Node.pm
# increases. If you have 30 different copies in 30 different directories,
# how do you keep them all up to date.
#
# Number 2 is a much better option. It works. However, which @INC
# directory do you place it in? When you update Perl or your distribution,
# it goes away.
#
# Number 3 is great, except you need to create a shellscript to call
# Perl with your program as an argument. BE SURE not to leave a space
# between the -I and the directory, or that space actually becomes
# part of the directory.
#
# Number 4 is greater, because it doesn't require a shellscript. Once
# again, no space between -I and the directory. In all cases where
# you know what directory will contain Node.pm, number 4 is a great
# alternative.
#
# But what if you don't know in advance what directory
# will contain Node.pm? What if you're writing an application to be
# run at varying locations with varying setups? What if, in addition,
# you don't want the end user messing with the source code to change
# the shebang line? In that case, you can actually place the path
# to Node.pm in a configuration file. It takes several lines of code,
# but it's certainly nice to be able to accommodate the user's
# environment without requiring change to the source code.
#
# This exercise demonstrates how to set the Node.pm location from a
# configuration file. Once again, if you're the sole user it might be
# better to change the shebang line, but if you're distributing
# your program like the autumn leaves, a configuration file is the
# way to go.
#
#####################################################################
#####################################################################
# The loadNodeModule() subroutine is a complete substitute for:
# use Node
#
# It includes:
# require Node;
# import Node;
#
# The preceding two calls completely replace a use Node statement,
# and better still, unlike the use statement, they happen at
# runtime instead of compile time. Therefore, this subroutine reads
# the directory from a config file, then executes that directory
# with the proper require and import statements. Obviously, the
# loadNodeModule() subroutine must be executed before any code depending
# on the Node.pm module is executed.
#####################################################################
sub loadNodeModule()
{
#### CHANGE THE FOLLOWING TO CHANGE THE DEFAULT APP FILENAME
my $defaultConfFileName = "./myapp.cfg";
#### CHANGE THE FOLLOWING TO CHANGE APP FILENAME ENVIRONMENT VAR
my $envVarName = "MY_APP_CONFIG";
my($conffile) = $ENV{$envVarName};
print $conffile, "\n" if defined $conffile;
$conffile = $defaultConfFileName unless defined($conffile);
print "Using config file $conffile.\n";
open CONF, '<' . $conffile or die "FATAL ERROR: Could not open config file $conffile.";
my @lines = <CONF>;
close CONF;
my @nodedirs;
foreach my $line (@lines)
{
chomp $line;
if($line =~ m/^\s*nodedir\s*=\s*([^\s]*)/)
{
my $dir = $1;
if($dir =~ m/(.*)\$HOME(.*)/)
{
$dir = $1 . $ENV{'HOME'} . $2;
}
push @nodedirs, ($dir);
}
}
if(@nodedirs)
{
unshift @INC, @nodedirs;
}
require Node;
import Node;
}
#####################################################################
# The main() routine calls loadNodeModule to include Node.pm,
# and then runs a few lines of code to conclusively prove that
# Node.pm is loaded. It also prints out the @INC array to show that
# directory in which Node.pm resides is now in the @INC path.
#
# Note that in the absense of any change to the environment variable
# defined in loadNodeModule(), the configuration file will be ./myapp.cfg.
#####################################################################
sub main()
{
loadNodeModule();
my $topNode = Node->new("myname", "mytype", "myvalue");
print "\n::: ";
print $topNode->getName(), " ::: ";
print $topNode->getType(), " ::: ";
print $topNode->getValue(), " :::\n";
foreach my $line (@INC)
{
print $line, "\n";
}
}
main();

View file

@ -0,0 +1,123 @@
#!/usr/bin/perl -w
# Copyright (C) 2004 by Steve Litt
# Licensed with the GNU General Public License, Version 2
# ABSOLUTELY NO WARRANTY, USE AT YOUR OWN RISK
# See http://www.gnu.org/licenses/gpl.txt
#####################################################################
# This exercise demonstrates use of the return callback routine. The
# return callback routine occurs when node navigation returns to a
# node from its children. Therefore, the return callback routine is
# never executed by nodes without children.
#
# An obvious use of the return callback routine is to print end tags
# for nested markup. A node's end tag must follow all markup for all
# the node's children, so the return callback is perfect for that
# purpose.
#
# Because childless nodes never execute the return callback routine,
# in the case of childless nodes this program prints the end tags
# from the entry callback routine.
#
# This program prints the attributes of each Node object. You'll
# immediately note that the "children" attributes you set are printed.
# But you'll also observe that a "_lineno" attribute has been set for
# all nodes except the top one. That attribute was set by the Parser
# object, and corresponds to the line in the parsed outline file. This
# attribute is extremely helpful in printing error messages.
#####################################################################
use strict; # prevent hard to find errors
use Node; # Use Note.pm tool
package Callbacks;
sub new()
{
my($type) = $_[0];
my($self) = {};
bless($self, $type);
$self->{'errors'} = 0;
$self->{'warnings'} = 0;
return($self);
}
sub getErrors(){return $_[0]->{'errors'};}
sub getWarnings(){return $_[0]->{'warnings'};}
##############################################################
# cbPrintTag is the entry callback, and is called on first
# entry to each node. It prints the start tag and text. If
# the node is a leaf level node, it also prints the end tag
# on the same line.
##############################################################
sub cbPrintTag()
{
my($self, $checker, $level) = @_;
unless (defined($checker)) {return -999;}
#### PRINT START TAG AND CONTENT
for(my $n = 0; $n < $level; $n++) {print "\t";}
print "<node level=", $level, ">";
print $checker->getValue() if $checker->hasValue();
#### IF THIS IS A LEAF LEVEL ITEM, PRINT THE
#### END TAG IMMEDIATELY. OTHERWISE, THE
#### RETURN CALLBACK WILL TAKE CARE OF THE END TAG.
unless($checker->hasFirstChild())
{
print "</node>";
}
#### PRINT NEWLINE
print "\n";
}
##############################################################
# cbPrintEndTag is the return callback, and is called on reentry
# to the node, after all its children have been processed.
# It is not called by leaf level (childless) nodes. The purpose
# of this routine is to print the end tag.
#
# For nodes with children, the end tag must be printed after
# all information for the node's children has been printed,
# in order to preserve proper nesting.
##############################################################
sub cbPrintEndTag()
{
my($self, $checker, $level) = @_;
unless (defined($checker)) {return -999;}
#### PRINT END TAG FOR PARENT
for(my $n = 0; $n < $level; $n++) {print "\t";}
print "</node>";
print "\n";
}
package Main;
sub main()
{
#### PARSE FROM FILE README.otl
my $parser = OutlineParser->new();
$parser->setCommentChar("#");
$parser->fromFile();
my $topNode=$parser->parse("README.otl");
#### INSTANTIATE THE Callbacks OBJECT
my $callbacks = Callbacks->new();
#### WALK THE NODE TREE,
#### OUTPUTTING LEVEL AND TEXT
my $walker = Walker->new
(
$topNode, # start with this node
[\&Callbacks::cbPrintTag, $callbacks], # do this on entry to each node
[\&Callbacks::cbPrintEndTag, $callbacks]# do this on return from node's children
);
$walker->walk();
}
main();

View file

@ -0,0 +1,114 @@
#!/usr/bin/perl -w
# Copyright (C) 2004 by Steve Litt
# Licensed with the GNU General Public License, Version 2
# ABSOLUTELY NO WARRANTY, USE AT YOUR OWN RISK
# See http://www.gnu.org/licenses/gpl.txt
use strict; # prevent hard to find errors
use Node; # Use Node.pm tool
#####################################################################
# This exercise demonstrates the most elemental use of Node.pm.
# It does nothing more than read README.otl into a Node tree, and
# then print the tree.
#
# Here's the high level logic:
# Set up a Callback object to house the callback routines
# Instantiate and configure a Parser object to parse README.otl
# Instantiate a Walker object to walk the resulting node tree
# Link Callbacks::cbPrintNode() as the Walker's entry callback
#
#####################################################################
##############################################################
# You need an object to house callback routines. The object can
# be named anything, but it should have facilities to count up
# errors and warnings. Its new() method should always be something
# like what you see below, and there should have getErrors() and
# getWarnings() methods.
#
# The cbPrintNode() method is typical of a simple callback routine.
# All callback routines have exactly three arguments, $self,
# $checker and $level. $self refers to the object containing
# the callback routine, $checker is the node that called this
# callback routine, and $level is the level of that node in the
# hierarchy. Armed with these pieces of information, you can
# perform almost any operation on the current node ($checker).
#
# The callback routines are called by the Parser object during
# parsing. A callback routine can be called upon first entry
# into a node, or it can be called upon reentry into that node
# after processing all that node's children. The latter is
# an excellent way of outputting end tags at the proper time.
##############################################################
package Callbacks;
sub new()
{
my($type) = $_[0];
my($self) = {};
bless($self, $type);
$self->{'errors'} = 0;
$self->{'warnings'} = 0;
return($self);
}
sub getErrors(){return $_[0]->{'errors'};}
sub getWarnings(){return $_[0]->{'warnings'};}
sub cbPrintNode()
{
my($self, $checker, $level) = @_;
unless (defined($checker)) {return -999;} # don't process undef node
print $level, " ::: "; # print the level
print $checker->getValue(); # print the text of the node
print "\n";
}
package Main;
sub main()
{
#### PARSE FROM FILE README.otl
my $parser = OutlineParser->new(); # instantiate parser
$parser->setCommentChar("#"); # ignore lines starting with #
$parser->fromFile(); # get input from file
my $topNode=$parser->parse("README.otl");
#====================================================================
# The preceding statement parses file README.otl into a node hierarchy
# and assigns the top level node of that hierarchy to $topNode. When
# you run the program you'll notice that the text in $topNode does
# not appear in README.otl, but instead has value
# "Inserted by OutlineParser".
#
# This is a feature, not a bug. In order to accommodate the typical
# case of an outline having several top level items, and yet still
# be able to represent the whole hierarchy as a single top node,
# the OutlineParser object creates a new node with value
# " Inserted by OutlineParser"
# and places all the outline's top level items under that newly
# created node.
#
# If the outline you're working on is guaranteed to have only
# a single top level item, and if you want that to be the top
# level node, you can simply do the following:
#
# $topNode=$topNode->getFirstChild();
#====================================================================
#### INSTANTIATE THE Callbacks OBJECT
my $callbacks = Callbacks->new();
#### WALK THE NODE TREE,
#### OUTPUTTING LEVEL AND TEXT
my $walker = Walker->new
(
$topNode, # start with this node
[\&Callbacks::cbPrintNode, $callbacks] # do this on entry to each node
);
$walker->walk();
}
main();

View file

@ -0,0 +1,4 @@
#### THIS FILE CONFIGURES example_nodepath.pl.
#### IN ORDER TO USE example_nodepath.pl,
#### CHANGE FOLLOWING PATH TO THE DIRECTORY WHERE YOU PLACED Node.pm ####
nodedir=/path/to/nodemodule

View file

@ -0,0 +1,7 @@
#!/bin/sh
echo "$1"
echo "$1" | perl -pe 's/./-/g;'
cd "$1"
find . \
| sort \
| perl -pe 's|^\./||; s|[^/]+/|\t|g; s/([^\t])/[_] $1/;'

View file

@ -0,0 +1,284 @@
# *Title: otl2aft
# *Author: Todd Coram (http://maplefish.com/todd)
# *TOC
#
# ~Version 1.3~
#
# ~This source is hereby placed into the Public Domain.~
# ~What you do with it is your own business.~
# ~Just please do no harm.~
#
#------------------------------------------------------------------------
#
# * Introduction
#
# Otl2aft converts VimOutliner files into
# [AFT (http://www.maplefish.com/todd/aft.html)] documents. This file
# can be run with nawk, mawk or gawk.
#
# This tool was created upon the request/inspiration of David J Patrick
#(of http://linuxcaffe.ca fame).
#
# You can downloaded the most up to date
# source [here (http://www.maplefish.com/todd/otl2aft.awk)].
# A PDF version of this file resides
# [here (http://www.maplefish.com/todd/otl2aft.pdf)].
#
# AEY: Changed all # symbols within regular expressions to \043
# to avoid problems with # being the comment character.
#
# * Code
#
# In the beginning we want to print out the AFT title and author.
# We expect the otl file to contain two top level headlines. The first
# will be used as the title and the second as the author.
#
# We also print out some control bits and a table of contents placeholder.
#
BEGIN {
VERSION="v1.3 9/04/2009";
# AEY: Note first line is now for OTL use only; we ignore it here.
getline; # expect title
print "#---SET-CONTROL tableparser=new"
# AEY: Commented out following lines, since this info is now metadata.
#print "*Title: " $0;
#getline; # expect author
#print "*Author: " $0;
#print "\n*TOC\n"
}
# AEY: > now starts an inline comment. We ignore these.
/^[\t]+>/ {
next;
}
# AEY: < is now used for metadata. We only act on certain ones.
#/^[\t]+<[ \t]*title:[ \t]*/ {
/^[\t]+</ {
# "Munch" off the first part, which we don't care about
#sub(/^[\t]+<[ \t]*/,"");
# If there's no colon, there won't be a tag, so we don't care.
spec = rightpart($0,"<");
if (match(spec,/:/)) {
key = leftpart(spec,":");
value = rightpart(spec,":");
if (key == "title") {
print "*Title: " value;
} else if (key == "author") {
print "*Author: " value;
} else if (key == "aft") {
print value; # "aft:" is an all-purpose "aft"-code insertion tag
}
} else {
if (spec == "toc") {
print "*TOC";
}
}
next;
}
# AEY: Any other metadata line starting with < is currently ignored.
#/^[\t]+</ {
# next;
#}
# AEY: Stop processing after ---END--- line
#/^---END---/ {
/^\043--- END ---/ {
exit;
}
# AEY: If we find a VIM Outliner checkbox, get rid of it
/\[[_X]\][ ]/ {
gsub(/\[[_X]\][ ]/, "");
}
# Scan for one or more tabs followed by a colon (:). This is the outliner's
# markup for ''body text (wrapping)''.
# If we are not nested inside a list (subheaders), then [reset] before doing
# any work. This makes sure we properly terminatel tables and other modes.
#
# Our work here involves simply killing tabs and removing the colon.
# We then continue reading the rest of the file.
#
/^[\t]+:/ {
if (!list_level) reset();
gsub(/\t/,"");
sub(/[ ]*:/, "");
# AEY: Need to handle bulleted and numbered lists too,
# but not here. From our point of view, ": * " is now verboten.
#sub(/^[\t ]*\*/,"\t*");
#sub(/^[\t ]*\043\./,"\t#.");
# End changes
print $0; next;
}
# AEY: Support for our own style of bulleted and numbered lists (experimental).
/^[\t]+(\*|\043[\.\)])/ {
if (!list_level) reset();
gsub(/\t/,"");
if (list_level || $0 ~ /[ ]*(\*|\043[\.\)])/) {
handlelist();
}
print $0; next;
}
# AEY: * now handled like heading, but must add extra space to avoid confusing Aft
#/^[\t]+\*/ {
# gsub("*"," *");
# # Continue on and handle as normal
#}
# Scan for ''user defined text block (wrapping)''. If we get this, we
# kill the tabs, remove the |>| and if we discover a crosshatch |#|, we
# start a list. If we are already in a list, we continue the list. Both
# starting and continuing is handled by [handlelist].
#
# AEY: Removed this
#/^[\t]+>/ {
# if (!list_level) reset();
# gsub(/\t/,"");
# sub(/>/, "");
#
# if (list_level || $0 ~ /[ ]*[\043*]/) {
# handlelist();
# }
# print $0; next;
#}
# Scan for |;| or |<| which indicate ''preformatted body text'' and
# ''user-defined preformatted text block'' respectively. Both of these
# are non wrapping but we ignore that (for now). We handle lists just like
# the previous scan action.
#
# AEY: Removed < handling
/^[\t]+;/ { # Handle ";" and "<" (preformated text)
if (!list_level) reset();
gsub(/\t/,"");
sub(/;/, "");
if (list_level || $0 ~ /[ ]*\043/) { # Convert "< #" into numbered lists.
handlelist();
}
print $0; next;
}
# Scan for a table. This is tricky. We want to cast the Outliner table
# into the AFT ''new table'' format. AFT tables (especially as rendered
# by LaTeX) really want to have captions/headers. We fake that for now
# by using a |-| place holder. This should be fixed!
#
/^[\t]+\|/ {
if (!in_table) reset();
in_table = 1
gsub(/\t/,"");
if ($1 ~ /\|\|/) {
print "\t! _ !";
print "\t!----------!"
}
gsub(/\|\|/,"!");
gsub(/\|/,"!");
print "\t"$0
print "\t!----------!"
next;
}
# The default scan matches anything not matching the above scan. We simply
# go through and set the known indent level based on the number of tabs
# seen.
#
{ match($0,/^[\t]+/); indent = RLENGTH; if (indent == -1) indent = 0; }
# Given the iden level set by the default scan (above), we now determine
# what type of AFT output to do.
#
# Indent levels lower than 7 are represented directly
# using AFT sections.
#
# AEY: Added $0 = "*"$0; back in to ensure top-level headings remain headings!
# (This existed in earlier versions, but not in version 1.3.)
#indent < 7 { gsub(/\t/,"*"); print "";}
indent < 7 { gsub(/\t/,"*"); $0 = "*"$0; print "";}
# Indent levels greater than 6 are represented by AFT bullet lists.
# This is done by first killing some tabs (we don't want to start off
# nesting too deeply), and using the remaining tabs to adjust to the
# appropriate list nesting level.
#
indent > 6 {
gsub(/\t\t\t/, "");
match($0,/^[\t]+/);
remtabs = substr($0,RSTART,RLENGTH);
text = substr($0,RSTART+RLENGTH);
$0 = remtabs"* "text;
print "";
}
# After adjusting indentation, just print out the line.
#
{ print $0 }
# **handlelist
# Look at the indentation and produce lists accordingly.
#
function handlelist() {
if (!list_level) {
list_indent = length(indent) + 1;
}
list_level = list_indent - length(indent);
if ($0 ~ /[ ]*\043/) { # Convert " #" into numbered lists.
for (i=0; i < list_level; i++)
printf("\t");
gsub(/[ ]*\\043/,"#.");
} else if ($0 ~ /[ ]*\*/) { # Convert " *" into bullet lists.
for (i=0; i < list_level; i++)
printf("\t");
gsub(/[ ]*\*/,"*");
} else if (list_level) {
for (i=0; i < list_level; i++)
printf("\t");
}
}
# **reset
# Reset various parameters to get us out of various modes.
#
function reset() {
if (list_level) {
print " ";
list_level = 0;
}
if (in_table) {
print "\t!----------!\n"
in_table = 0;
}
}
# AEY: "Trim" function, added for sanity's sake.
function trim(str) {
sub(/^[ \t]*/,"",str);
sub(/[ \t]*$/,"",str);
return str;
}
# AEY: Get everything to left of specified regex, and trim it too.
function leftpart(str,regex) {
if (match(str,regex)) {
return trim(substr(str,1,RSTART-1));
} else {
return "";
}
}
# AEY: Get everything to right of specified regex, and trim it too.
function rightpart(str,regex) {
if (match(str,regex)) {
return trim(substr(str,RSTART+RLENGTH));
} else {
return "";
}
}
# That's all folks!

View file

@ -0,0 +1,270 @@
#!/usr/bin/perl
use strict;
use XML::Writer;
use vars qw($writer $section_has_contents $VERSION);
use constant DEBUG => 0;
$VERSION = '2.0';
sub debug {
if ( DEBUG )
{
print STDERR @_;
}
}
sub start_docbook {
$writer = XML::Writer->new(DATA_MODE => 1,
DATA_INDENT => 1);
debug(' 'x$writer->getDataIndent(), "starting new docbook\n");
$writer->xmlDecl();
# my $system = '/usr/share/sgml/docbook/xml-dtd-4.1/docbookx.dtd';
my $system = 'http://www.oasis-open.org/docbook/xml/4.0/docbookx.dtd';
$writer->doctype('article',
'-//OASIS//DTD DocBook XML V4.1//EN',
$system);
}
sub start_article {
my $id = shift;
debug(' 'x$writer->getDataIndent(), "starting new article\n");
my @attributes = (
'class' => 'whitepaper',
);
if ( $id )
{
push @attributes, ( 'id' => $id );
}
$writer->startTag('article', @attributes);
}
sub start_section {
my $title = shift;
debug(' 'x$writer->getDataIndent(), "starting new section\n");
$writer->startTag('section');
$section_has_contents = 0;
if ( $title )
{
$writer->dataElement('title', $title);
}
}
sub start_list {
debug(' 'x$writer->getDataIndent(), "starting new list\n");
$writer->startTag('itemizedlist');
}
sub append_list_item {
my $text = shift;
debug(' 'x$writer->getDataIndent(), "starting new listitem\n");
$writer->startTag('listitem');
$writer->dataElement('para', $text);
$writer->endTag('listitem');
}
sub end_list {
$writer->endTag('itemizedlist');
debug(' 'x$writer->getDataIndent(), "ending list\n");
}
sub append_code {
my $code = shift;
debug(' 'x$writer->getDataIndent(), "starting new programlisting\n");
$section_has_contents = 1;
$writer->dataElement('programlisting', $code, role=>'C');
}
sub append_para {
my $text = shift;
debug(' 'x$writer->getDataIndent(), "starting new para\n");
$section_has_contents = 1;
$writer->dataElement('para', $text);
}
sub end_section {
if ( ! $section_has_contents )
{
$writer->emptyTag('para');
$section_has_contents = 1;
}
$writer->endTag('section');
debug(' 'x$writer->getDataIndent(), "ending section\n");
}
sub end_article {
$writer->endTag('article');
debug(' 'x$writer->getDataIndent(), "ending article\n");
}
sub end_docbook {
$writer->end();
debug(' 'x$writer->getDataIndent(), "ending docbook\n");
}
####################################################
start_docbook();
start_article();
my $section_level = 0;
my $line;
my $para = '';
my $list_mode = 0;
my $code_mode = 0;
my $first_line = 1;
sub list_done {
if ( $list_mode ) {
end_list();
$list_mode = 0;
}
}
sub para_done {
if ( $para )
{
chomp $para;
if ( $code_mode )
{
append_code($para);
$code_mode = 0;
}
elsif ( $list_mode )
{
append_list_item($para);
}
else
{
append_para($para);
}
}
$para = '';
}
while ( defined ($line = <>) )
{
if ( $first_line and $line =~ /^-\*-/ )
{
next;
}
$first_line = 0;
if ( $line =~ /^\t*\* (.*)/ )
{
para_done();
$para = $1;
if ( ! $list_mode )
{
start_list();
$list_mode = 1;
}
next;
}
if ( $line =~ /^\t*[^\t: ]/ )
{
para_done();
list_done();
}
if ( $line =~ /^(\t*)([^\t\n: ].*)/ )
{
my $title = $2;
my $new_section_level = length($1) + 1;
para_done();
list_done();
for ( my $i = 0 ; $section_level - $new_section_level >= $i ; $i++ )
{
end_section();
}
chomp $title;
start_section($title);
$section_level = $new_section_level;
next;
}
# Code mode not supported yet
# if ( ! $list_mode and $line =~ /^\s+/ )
# {
# debug("enabling code mode\n");
# $code_mode = 1;
# }
$line =~ s/^\t*(\: ?| )//;
if ($line =~ /^$/) {
para_done();
list_done();
next;
}
$para .= $line;
}
para_done();
for ( my $i = 0 ; $section_level > $i ; $i++ )
{
end_section();
}
end_article();
end_docbook();
__END__
=head1 NAME
outline2dockbook - Generate DocBook XML from VimOutliner outline
=head1 SYNOPSIS
outline2docbook < input > output
=head1 DESCRIPTION
B<outline2docbook> reads an VimOutliner outline-mode type text file on
standard input and outputs DocBook XML on standard output.
The original version was written by Thomas R. Fullhart to convert from Emacs
outline mode. It is available at
http://genetikayos.com/code/repos/outline2docbook/distribution/.
This program uses the B<XML::Writer> perl module, which is available
on B<CPAN>.
=cut

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,269 @@
/* copyright notice and filename */
body {
font-family: helvetica,arial,sans-serif;
font-size: 10pt;
}
/* title at the top of the page */
H1 {
font-family: helvetica,arial,sans-serif;
font-size: 14pt;
font-weight: bold;
text-align: center;
color: black;
background-color: #ddddee;
padding-top: 20px;
padding-bottom: 20px;
}
H2 {
font-family: helvetica,arial,sans-serif;
font-size: 12pt;
font-weight: bold;
text-align: left;
color: black;
}
H3 {
font-family: helvetica,arial,sans-serif;
font-size: 12pt;
text-align: left;
color: black;
}
H4 {
font-family: helvetica,arial,sans-serif;
font-size: 12pt;
text-align: left;
color: black;
}
H5 {
font-family: helvetica,arial,sans-serif;
font-size: 10pt;
text-align: left;
color: black;
}
/* outline level spacing */
OL {
padding-bottom: 8pt;
counter-reset: section;
margin-left: 1.0em;
padding-left:0 ;
}
OL LI { counter-increment: section; }
OL LI:before { content: counters(section, ".") ". "; }
/* global heading settings */
LI {
font-family: helvetica,arial,sans-serif;
color: black;
font-weight: normal;
list-style: lower-alpha;
padding-top: 4px;
}
/* level 1 heading overrides */
LI.L1 {
font-size: 12pt;
font-weight: bold;
list-style: none;
}
/* level 2 heading overrides */
LI.L2 {
font-size: 10pt;
font-weight: bold;
list-style: none;
}
/* level 3 heading overrides */
LI.L3 {
font-size: 10pt;
list-style: none;
}
/* level 4 heading overrides */
LI.L4 {
font-size: 10pt;
list-style: none;
}
/* level 5 heading overrides */
LI.L5 {
font-size: 10pt;
list-style: none;
}
/* level 6 heading overrides */
LI.L6 {
font-size: 10pt;
list-style: none;
}
/* level 7 heading overrides */
LI.L7 {
font-size: 10pt;
list-style: none;
}
/* level 8 heading overrides */
LI.L8 {
font-size: 10pt;
list-style: none;
}
/* level 9 heading overrides */
LI.L9 {
font-size: 10pt;
list-style: none;
}
/* level 10 heading overrides */
LI.L10 {
font-size: 10pt;
list-style: none;
}
/* level 11 heading overrides */
LI.L11 {
font-size: 10pt;
list-style: none;
}
/* level 1 bullet heading overrides */
LI.LB1 {
font-size: 12pt;
font-weight: bold;
list-style: disc;
}
/* level 2 bullet heading overrides */
LI.LB2 {
font-size: 10pt;
font-weight: bold;
list-style: disc;
}
/* level 3 bullet heading overrides */
LI.LB3 {
font-size: 10pt;
list-style: disc;
}
/* level 4 bullet heading overrides */
LI.LB4 {
font-size: 10pt;
list-style: disc;
}
/* level 5 bullet heading overrides */
LI.LB5 {
font-size: 10pt;
list-style: disc;
}
/* level 6 bullet heading overrides */
LI.LB6 {
font-size: 10pt;
list-style: disc;
}
/* level 7 bullet heading overrides */
LI.LB7 {
font-size: 10pt;
list-style: disc;
}
/* level 1 numeric heading overrides */
LI.LN1 {
font-size: 12pt;
font-weight: bold;
list-style: decimal;
}
/* level 2 numeric heading overrides */
LI.LN2 {
font-size: 10pt;
font-weight: bold;
list-style: decimal;
}
/* level 3 numeric heading overrides */
LI.LN3 {
font-size: 10pt;
list-style: decimal;
}
/* level 4 numeric heading overrides */
LI.LN4 {
font-size: 10pt;
list-style: decimal;
}
/* level 5 numeric heading overrides */
LI.LN5 {
font-size: 10pt;
list-style: decimal;
}
/* level 6 numeric heading overrides */
LI.LN6 {
font-size: 10pt;
list-style: decimal;
}
/* level 7 numeric heading overrides */
LI.LN7 {
font-size: 10pt;
list-style: decimal;
}
/* body text */
P {
font-family: helvetica,arial,sans-serif;
font-size: 9pt;
font-weight: normal;
color: black;
}
/* preformatted text */
PRE {
font-family: fixed,monospace;
font-size: 9pt;
font-weight: normal;
color: darkblue;
}
TABLE {
margin-top: 1em;
font-family: helvetica,arial,sans-serif;
font-size: 12pt;
font-weight: normal;
border-collapse: collapse;
}
TH {
border: 1px solid black;
padding: 0.5em;
background-color: #eeddee;
}
TD {
border: 1px solid black;
padding: 0.5em;
background-color: #ddeeee;
}
CODE {
background-color: yellow;
}
TABLE.TAB1 {
margin-top: 1em;
font-family: helvetica,arial,sans-serif;
font-size: 12pt;
font-weight: normal;
border-collapse: collapse;
}
TABLE.TAB2 {
margin-top: 1em;
font-family: helvetica,arial,sans-serif;
font-size: 11pt;
font-weight: normal;
border-collapse: collapse;
}
TABLE.TAB3 {
margin-top: 1em;
font-family: helvetica,arial,sans-serif;
font-size: 10pt;
font-weight: normal;
border-collapse: collapse;
}
TABLE.TAB4 {
margin-top: 1em;
font-family: helvetica,arial,sans-serif;
font-size: 10pt;
font-weight: normal;
border-collapse: collapse;
}
TABLE.TAB5 {
margin-top: 1em;
font-family: helvetica,arial,sans-serif;
font-size: 10pt;
font-weight: normal;
border-collapse: collapse;
}
TABLE.TAB6 {
margin-top: 1em;
font-family: helvetica,arial,sans-serif;
font-size: 10pt;
font-weight: normal;
border-collapse: collapse;

View file

@ -0,0 +1,246 @@
/* copyright notice and filename */
body {
font-family: helvetica,arial,sans-serif;
font-size: 10pt;
}
/* title at the top of the page */
H1 {
font-family: helvetica,arial,sans-serif;
font-size: 14pt;
font-weight: bold;
text-align: center;
color: black;
background-color: #ddddee;
padding-top: 20px;
padding-bottom: 20px;
}
H2 {
font-family: helvetica,arial,sans-serif;
font-size: 12pt;
font-weight: bold;
text-align: left;
color: black;
}
H3 {
font-family: helvetica,arial,sans-serif;
font-size: 12pt;
text-align: left;
color: black;
}
H4 {
font-family: helvetica,arial,sans-serif;
font-size: 12pt;
text-align: left;
color: black;
}
H5 {
font-family: helvetica,arial,sans-serif;
font-size: 10pt;
text-align: left;
color: black;
}
/* outline level spacing */
OL {
margin-left: 1.0em;
padding-left: 0;
padding-bottom: 8pt;
}
/* global heading settings */
LI {
font-family: helvetica,arial,sans-serif;
color: black;
font-weight: normal;
list-style: lower-alpha;
padding-top: 4px;
}
/* level 1 heading overrides */
LI.L1 {
font-size: 12pt;
font-weight: bold;
list-style: none;
}
/* level 2 heading overrides */
LI.L2 {
font-size: 10pt;
font-weight: bold;
list-style: none;
}
/* level 3 heading overrides */
LI.L3 {
font-size: 10pt;
list-style: none;
}
/* level 4 heading overrides */
LI.L4 {
font-size: 10pt;
list-style: none;
}
/* level 5 heading overrides */
LI.L5 {
font-size: 10pt;
list-style: none;
}
/* level 6 heading overrides */
LI.L6 {
font-size: 10pt;
list-style: none;
}
/* level 7 heading overrides */
LI.L7 {
font-size: 10pt;
list-style: none;
}
/* level 1 bullet heading overrides */
LI.LB1 {
font-size: 12pt;
font-weight: bold;
list-style: disc;
}
/* level 2 bullet heading overrides */
LI.LB2 {
font-size: 10pt;
font-weight: bold;
list-style: disc;
}
/* level 3 bullet heading overrides */
LI.LB3 {
font-size: 10pt;
list-style: disc;
}
/* level 4 bullet heading overrides */
LI.LB4 {
font-size: 10pt;
list-style: disc;
}
/* level 5 bullet heading overrides */
LI.LB5 {
font-size: 10pt;
list-style: disc;
}
/* level 6 bullet heading overrides */
LI.LB6 {
font-size: 10pt;
list-style: disc;
}
/* level 7 bullet heading overrides */
LI.LB7 {
font-size: 10pt;
list-style: disc;
}
/* level 1 numeric heading overrides */
LI.LN1 {
font-size: 12pt;
font-weight: bold;
list-style: decimal;
}
/* level 2 numeric heading overrides */
LI.LN2 {
font-size: 10pt;
font-weight: bold;
list-style: decimal;
}
/* level 3 numeric heading overrides */
LI.LN3 {
font-size: 10pt;
list-style: decimal;
}
/* level 4 numeric heading overrides */
LI.LN4 {
font-size: 10pt;
list-style: decimal;
}
/* level 5 numeric heading overrides */
LI.LN5 {
font-size: 10pt;
list-style: decimal;
}
/* level 6 numeric heading overrides */
LI.LN6 {
font-size: 10pt;
list-style: decimal;
}
/* level 7 numeric heading overrides */
LI.LN7 {
font-size: 10pt;
list-style: decimal;
}
/* body text */
P {
font-family: helvetica,arial,sans-serif;
font-size: 9pt;
font-weight: normal;
color: darkgreen;
}
/* preformatted text */
PRE {
font-family: fixed,monospace;
font-size: 9pt;
font-weight: normal;
color: darkblue;
}
TABLE {
margin-top: 1em;
font-family: helvetica,arial,sans-serif;
font-size: 12pt;
font-weight: normal;
border-collapse: collapse;
}
TH {
border: 1px solid black;
padding: 0.5em;
background-color: #eeddee;
}
TD {
border: 1px solid black;
padding: 0.5em;
background-color: #ddeeee;
}
CODE {
background-color: yellow;
}
TABLE.TAB1 {
margin-top: 1em;
font-family: helvetica,arial,sans-serif;
font-size: 12pt;
font-weight: normal;
border-collapse: collapse;
}
TABLE.TAB2 {
margin-top: 1em;
font-family: helvetica,arial,sans-serif;
font-size: 11pt;
font-weight: normal;
border-collapse: collapse;
}
TABLE.TAB3 {
margin-top: 1em;
font-family: helvetica,arial,sans-serif;
font-size: 10pt;
font-weight: normal;
border-collapse: collapse;
}
TABLE.TAB4 {
margin-top: 1em;
font-family: helvetica,arial,sans-serif;
font-size: 10pt;
font-weight: normal;
border-collapse: collapse;
}
TABLE.TAB5 {
margin-top: 1em;
font-family: helvetica,arial,sans-serif;
font-size: 10pt;
font-weight: normal;
border-collapse: collapse;
}
TABLE.TAB6 {
margin-top: 1em;
font-family: helvetica,arial,sans-serif;
font-size: 10pt;
font-weight: normal;
border-collapse: collapse;

View file

@ -0,0 +1,44 @@
" local .vimrc for working with otl2latex
"
" Used to write notes in vim outlier file (.otl) and have dynamically
" generated beamer-latex files produced.
"
" requires the script otl2latex.py is in the same directory as this script.
" also requires that your user ~/.vimrc file has "set exrc" specified
"
" This can be added to as more functionality is desired.
"
" Author: Serge Rey <sjsrey@gmail.com>
" Last Revision: 2007-01-21
version 6.0
"get rid of blank lines
map ,n :g/^$/d
"make the next paragraph a text block (in Vim Outliner terms)
map ,t ma}k^mb'a'bI|
"make an itemized list out of the following contiguous lines (each line is an
"item)
map ,i ^ma}k^mb'a'bI\item 'aObegin{itemize}'aki\'bo\end{itemize}
"make an itemized list out of the following contiguous lines (each line is an
"item) and then mark block as otl text
map ,I ^ma}k^mb'a'bI\item 'aObegin{itemize}'aki\'bo\end{itemize}'akma}k^mb'a'bI|
map ,f ^Obegin{center}jo\end{center}k^i\includegraphics[width=.8\linewidth]{A}k^i\^jjmbkk'bI|
"process the otl file to produce a pdf presentation
map ,b :!python otl2latex.py -p % %<.tex;pdflatex %<.tex 
"pdflatex the current buffer
map ,p :!pdflatex %
"set up menus
amenu o&2l.&process<Tab>,b ,b
amenu o&2l.&delete\ blank\ lines<Tab>,n ,n
amenu o2l.-Sep- :
amenu o&2l.&itemize<Tab>,i ,i
amenu o&2l.&textualize<Tab>,t ,t
amenu o&2l.&itemize_and_textualize<Tab>,t ,I

View file

@ -0,0 +1,23 @@
otl2latex
Translate a Vim Outliner file to a LaTeX document.
Author: Serge Rey <sjsrey@gmail.com>
Version 0.1 (2007-01-21)
REQUIREMENTS
The Vim Outliner http://bike-nomad.com/vim/vimoutliner.html
beamer http://latex-beamer.sourceforge.net/
Python http://www.python.org
INSTRUCTIONS
The user's guide is in otl2latex.pdf
To reproduce it:
python otl2latex.py -p otl2latex.otl otl2latex.tex
pdflatex otl2latex
DEVELOPMENT
otl2latex development is hosted at:
http://http://code.google.com/p/otl2latex/

View file

@ -0,0 +1,95 @@
preamble
@author Serge Rey
@institute sjsrey@gmail.com
@instituteShort sjsrey@gmail.com
@title otl2latex
@subtitle User's Guide
@titleShort http://code.google.com/p/otl2latex/
@date November 3, 2007
@dateShort otl2latex
Introduction
What is otl2latex?
Translator
otl to tex
|otl2latex allows you to
|* prepare your document in a powerful outliner
|* generate \LaTeX\ markup of your content
Requirements
Operating Systems
Operating Systems Supported
|otl2latex has been used successfully on
|* Linux
|* Mac OS X
|* Windows
Software Required
Packages and Programs
|* Python http://www.python.org
|* \LaTeX
|* Beamer http://latex-beamer.sourceforge.net/
|* The Vim Outliner http://bike-nomad.com/vim/vimoutliner.html
Usage
Basics
Usage
From the command line
| \texttt{python otl2latex.py -p filename.otl filename.tex}
|
Notes
|* \texttt{filename.tex} will be generated, you don't edit that one.
|* You can run all this from withing Vim (see Vim Mappings below).
Basics
Presentations/Beamer
|* Level 1 in the outline become sections
|* Level 2 in the outline become subsections
|* Level 3 in the outline become frame titles
|* Level 4 in the outline become block titles
|* Text in the outline is treated as \LaTeX\ markup
Using Bullets
| Placing a '*' at the begining of a line will tell otl2latex to begin an itemize list. otl2latex currently supports 3 levels of Itemization.
|* First Level
|** Second Level
|*** Third Level
|** Second Level
Advanced
Tips
|* Level 4 can be omitted
|* You will have no blocks on that frame
Vim mappings
Vim Mappings: .vimrc
Processing
|* ,b will generate a pdf file from your outline
|* ,nb will remove all empty lines in your otl file
|* ,p will run the current vim buffer through pdflatex
Vim Mappings: .vimrc
Lists
|* ,i on the first line will create an itemized list of a block of lines
|* ,t will mark a block as otl text
|* ,I itemize and mark block as otl text
|You need to have a blank line at the end of the block to apply these.
Vim Mappings: .vimrc
Figures
|\begin{itemize}
|\item ,f (insert mode) will generate stub for figures
|\end{itemize}
A figure
| \begin{center}
| \includegraphics[width=.8\linewidth]{otl2latex.png}
| \end{center}
A figure
|\begin{center}
|\includegraphics[width=.8\linewidth]{otl2latex.png}
|\end{center}
Future Extensions
Move to vim script
.vimrc to otl2latex.vim
|\begin{itemize}
|\item Currently we are just embedding mappings in .vimrc
|\item Ok for testing, not very polished for end user
|\end{itemize}
Reverse Engineering
latex2otl
|\begin{itemize}
|\item take a tex file
|\item generate the otl file
|\end{itemize}

Binary file not shown.

After

Width:  |  Height:  |  Size: 41 KiB

View file

@ -0,0 +1,315 @@
usage="""
otl2latex.py
Translate a Vim Outliner file to a LaTeX document.
Usage:
otl2latex.py -[abp] file.otl [file.tex]
-a: Output to article class
-b: Output to book class
-p: Output to Beamer (presentation) class (default)
Author: Serge Rey <sjsrey@gmail.com>
Version 0.1 (2007-01-21)
"""
import os,sys
class Line:
"""Class for markup lines"""
def __init__(self, content):
ntabs=content.count("\t")
content=content.lstrip("\t")
level = ntabs - content.count("\t")
self.level=level
self.content = content
self.markup=0
if content[0]=="|":
self.markup=1
#3 lines added here
self.bullet=0
if len(content) > 2 and (content[2]=='*' or content[1]=='*'):
self.bullet=1
#print "%d: %s"%(self.bullet,content)
class Manager:
"""Abstract class for LaTeX document classes"""
def __init__(self, content, fileOut):
self.content=content
self.fileOut=open(fileOut,'w')
self.parse()
self.fileOut.write(self.markup)
self.fileOut.close()
def parse(self):
self.lines=[ Line(line) for line in self.content]
preambleStart=0
nl=len(self.lines)
id=zip(range(nl),self.lines)
level1=[i for i,line in id if line.level==0]
preambleEnd=level1[1]
preamble=self.lines[0:preambleEnd]
self.level1=level1
preambleMarkup=[]
for line in preamble:
if line.content.count("@"):
tmp=line.content.split("@")[1]
tmp=tmp.split()
env=tmp[0]
content=" ".join(tmp[1:])
mu="\\%s{%s}"%(env,content)
preambleMarkup.append(mu)
self.preamble=preambleMarkup
self.preambleLines=preamble
self.documentLines=self.lines[preambleEnd:]
class Beamer(Manager):
"""Manager for Beamer document class"""
def __init__(self, content,fileOut):
self.top1="""
\documentclass[nototal,handout]{beamer}
\mode<presentation>
{
\usetheme{Madrid}
\setbeamercovered{transparent}
}
\usepackage{verbatim}
\usepackage{fancyvrb}
\usepackage[english]{babel}
\usepackage[latin1]{inputenc}
\usepackage{times}
\usepackage{tikz}
\usepackage[T1]{fontenc}
\usepackage{graphicx} %sjr added
\graphicspath{{figures/}}
\usepackage{hyperref}"""
self.top2="""
% Delete this, if you do not want the table of contents to pop up at
% the beginning of each subsection:
\AtBeginSubsection[]
{
\\begin{frame}<beamer>
\\frametitle{Outline}
\\tableofcontents[currentsection,currentsubsection]
\end{frame}
}
% If you wish to uncover everything in a step-wise fashion, uncomment
% the following command:
\\beamerdefaultoverlayspecification{<+->}
\\begin{document}
\\begin{frame}
\\titlepage
\end{frame}
\\begin{frame}
\\frametitle{Outline}
\\tableofcontents[pausesections]
% You might wish to add the option [pausesections]
\end{frame}
"""
self.bulletLevel = 0
Manager.__init__(self, content, fileOut)
def itemize(self,line):
nstars=line.content.count("*")
content=line.content.lstrip("|").lstrip().lstrip("*")
self.currentBLevel = nstars - content.count("*")
stuff=[]
if self.currentBLevel == self.bulletLevel and line.bullet:
mu='\\item '+line.content.lstrip("|").lstrip().lstrip("*")
elif line.bullet and self.currentBLevel > self.bulletLevel:
self.bulletLevel += 1
stuff.append("\\begin{itemize}\n")
mu='\\item '+line.content.lstrip("|").lstrip().lstrip("*")
elif self.currentBLevel < self.bulletLevel and line.bullet:
self.bulletLevel -= 1
stuff.append("\\end{itemize}\n")
mu='\\item '+line.content.lstrip("|").lstrip().lstrip("*")
elif self.currentBLevel < self.bulletLevel:
self.bulletLevel -= 1
stuff.append("\\end{itemize}\n")
mu=line.content.lstrip("|")
else:
panic()
return stuff,mu
def parse(self):
Manager.parse(self)
#print self.content
#print self.lines
#print self.level1
#for info in self.preamble:
# print info
# do my own preamble
field=("author ","instituteShort ","dateShort ","date ","subtitle ",
"title ", "institute ", "titleShort ")
pattern=["@"+token for token in field]
f=zip(field,pattern)
d={}
for field,pattern in f:
t=[line.content for line in self.preambleLines if line.content.count(pattern)]
if t:
d[field]= t[0].split(pattern)[1].strip()
else:
d[field]=""
preamble="\n\n\\author{%s}\n"%d['author ']
preamble+="\\institute[%s]{%s}\n"%(d['instituteShort '],d['institute '])
preamble+="\\title[%s]{%s}\n"%(d['titleShort '],d['title '])
preamble+="\\subtitle{%s}\n"%(d['subtitle '])
preamble+="\\date[%s]{%s}\n"%(d['dateShort '],d['date '])
print self.preamble
self.preamble=preamble
body=[]
prev=0
frameOpen=0
blockOpen=0
frameCount=0
blockCount=0
for line in self.documentLines:
if line.level==0:
for i in range(0,self.bulletLevel):
self.bulletLevel -= 1
body.append("\\end{itemize}\n")
if blockOpen:
body.append("\\end{block}")
blockOpen=0
if frameOpen:
body.append("\\end{frame}")
frameOpen=0
mu="\n\n\n\\section{%s}"%line.content.strip()
elif line.level==1:
for i in range(0,self.bulletLevel):
self.bulletLevel -= 1
body.append("\\end{itemize}\n")
if blockOpen:
body.append("\\end{block}")
blockOpen=0
if frameOpen:
body.append("\\end{frame}")
frameOpen=0
mu="\n\n\\subsection{%s}"%line.content.strip()
elif line.level==2:
# check if this frame has blocks or is nonblocked
if line.markup:
if line.bullet or self.bulletLevel:
stuff,mu=self.itemize(line)
if len(stuff) > 0:
for i in stuff:
body.append(i)
else:
mu=line.content.lstrip("|")
else:
for i in range(0,self.bulletLevel):
self.bulletLevel -= 1
body.append("\\end{itemize}\n")
if blockOpen:
body.append("\\end{block}")
blockOpen=0
if frameOpen:
body.append("\\end{frame}")
else:
frameOpen=1
# check for verbatim here
tmp=line.content.strip()
if tmp.count("@vb"):
tmp=tmp.split("@")[0]
mu="\n\n\\begin{frame}[containsverbatim]\n\t\\frametitle{%s}\n"%tmp
else:
mu="\n\n\\begin{frame}\n\t\\frametitle{%s}\n"%tmp
frameCount+=1
elif line.level==3:
# check if it is a block or body content
if line.markup:
if line.bullet or self.bulletLevel:
stuff,mu=self.itemize(line)
if len(stuff) > 0:
for i in stuff:
body.append(i)
else:
mu=line.content.lstrip("\t")
mu=mu.lstrip("|")
else:
for i in range(0,self.bulletLevel):
self.bulletLevel -= 1
body.append("\\end{itemize}\n")
#block title
if blockOpen:
body.append("\\end{block}")
else:
blockOpen=1
mu="\n\\begin{block}{%s}\n"%line.content.strip()
blockCount+=1
else:
mu=""
body.append(mu)
for i in range(0,self.bulletLevel):
self.bulletLevel -= 1
body.append("\\end{itemize}\n")
if blockOpen:
body.append("\\end{block}")
if frameOpen:
body.append("\\end{frame}")
self.body=" ".join(body)
self.markup=self.top1+self.preamble+self.top2
self.markup+=self.body
self.markup+="\n\\end{document}\n"
print self.markup
# Process command line arguments
args = sys.argv
nargs=len(args)
dispatch={}
dispatch['beamer']=Beamer
inputFileName=None
outputFileName=None
def printUsage():
print usage
sys.exit()
if nargs==1:
printUsage()
else:
docType='beamer'
options=args[1]
if options.count("-"):
if options.count("a"):
docType='article'
elif options.count("b"):
docType='book'
if nargs==2:
printUsage()
elif nargs==3:
inputFileName=args[2]
elif nargs==4:
inputFileName=args[2]
outputFileName=args[3]
else:
printUsage()
elif nargs==2:
inputFileName=args[1]
elif nargs==3:
inputFileName=args[1]
outputFileName=args[3]
else:
printUsage()
# Dispatch to correct document class manager
fin=open(inputFileName,'r')
content=fin.readlines()
fin.close()
dispatch[docType](content,outputFileName)

View file

@ -0,0 +1,223 @@
\documentclass[nototal,handout]{beamer}
\mode<presentation>
{
\usetheme{Madrid}
\setbeamercovered{transparent}
}
\usepackage{verbatim}
\usepackage{fancyvrb}
\usepackage[english]{babel}
\usepackage[latin1]{inputenc}
\usepackage{times}
\usepackage{tikz}
\usepackage[T1]{fontenc}
\usepackage{graphicx} %sjr added
\graphicspath{{figures/}}
\usepackage{hyperref}
\author{Serge Rey}
\institute[sjsrey@gmail.com]{sjsrey@gmail.com}
\title[http://code.google.com/p/otl2latex/]{otl2latex}
\subtitle{User's Guide}
\date[otl2latex]{November 3, 2007}
% Delete this, if you do not want the table of contents to pop up at
% the beginning of each subsection:
\AtBeginSubsection[]
{
\begin{frame}<beamer>
\frametitle{Outline}
\tableofcontents[currentsection,currentsubsection]
\end{frame}
}
% If you wish to uncover everything in a step-wise fashion, uncomment
% the following command:
\beamerdefaultoverlayspecification{<+->}
\begin{document}
\begin{frame}
\titlepage
\end{frame}
\begin{frame}
\frametitle{Outline}
\tableofcontents[pausesections]
% You might wish to add the option [pausesections]
\end{frame}
\section{Introduction}
\subsection{What is otl2latex?}
\begin{frame}
\frametitle{Translator}
\begin{block}{otl to tex}
otl2latex allows you to
\begin{itemize}
\item prepare your document in a powerful outliner
\item generate \LaTeX\ markup of your content
\end{itemize}
\end{block} \end{frame}
\subsection{Requirements}
\begin{frame}
\frametitle{Operating Systems}
\begin{block}{Operating Systems Supported}
otl2latex has been used successfully on
\begin{itemize}
\item Linux
\item Mac OS X
\item Windows
\end{itemize}
\end{block} \end{frame}
\begin{frame}
\frametitle{Software Required}
\begin{block}{Packages and Programs}
\begin{itemize}
\item Python http://www.python.org
\item \LaTeX
\item Beamer http://latex-beamer.sourceforge.net/
\item The Vim Outliner http://bike-nomad.com/vim/vimoutliner.html
\end{itemize}
\end{block} \end{frame}
\section{Usage}
\subsection{Basics}
\begin{frame}
\frametitle{Usage}
\begin{block}{From the command line}
\texttt{python otl2latex.py -p filename.otl filename.tex}
\end{block}
\begin{block}{Notes}
\begin{itemize}
\item \texttt{filename.tex} will be generated, you don't edit that one.
\item You can run all this from withing Vim (see Vim Mappings below).
\end{itemize}
\end{block} \end{frame}
\begin{frame}
\frametitle{Basics}
\begin{block}{Presentations/Beamer}
\begin{itemize}
\item Level 1 in the outline become sections
\item Level 2 in the outline become subsections
\item Level 3 in the outline become frame titles
\item Level 4 in the outline become block titles
\item Text in the outline is treated as \LaTeX\ markup
\end{itemize}
\end{block}
\begin{block}{Using Bullets}
Placing a '*' at the begining of a line will tell otl2latex to begin an itemize list. otl2latex currently supports 3 levels of Itemization.
\begin{itemize}
\item First Level
\begin{itemize}
\item Second Level
\begin{itemize}
\item Third Level
\end{itemize}
\item Second Level
\end{itemize}
\end{itemize}
\end{block} \end{frame}
\begin{frame}
\frametitle{Advanced}
\begin{block}{Tips}
\begin{itemize}
\item Level 4 can be omitted
\item You will have no blocks on that frame
\end{itemize}
\end{block} \end{frame}
\subsection{Vim mappings}
\begin{frame}
\frametitle{Vim Mappings: .vimrc}
\begin{block}{Processing}
\begin{itemize}
\item ,b will generate a pdf file from your outline
\item ,nb will remove all empty lines in your otl file
\item ,p will run the current vim buffer through pdflatex
\end{itemize}
\end{block} \end{frame}
\begin{frame}
\frametitle{Vim Mappings: .vimrc}
\begin{block}{Lists}
\begin{itemize}
\item ,i on the first line will create an itemized list of a block of lines
\item ,t will mark a block as otl text
\item ,I itemize and mark block as otl text
\end{itemize}
You need to have a blank line at the end of the block to apply these.
\end{block} \end{frame}
\begin{frame}
\frametitle{Vim Mappings: .vimrc}
\begin{block}{Figures}
\begin{itemize}
\item ,f (insert mode) will generate stub for figures
\end{itemize}
\end{block} \end{frame}
\begin{frame}
\frametitle{A figure}
\begin{center}
\includegraphics[width=.8\linewidth]{otl2latex.png}
\end{center}
\end{frame}
\begin{frame}
\frametitle{A figure}
\begin{center}
\includegraphics[width=.8\linewidth]{otl2latex.png}
\end{center}
\end{frame}
\subsection{Future Extensions}
\begin{frame}
\frametitle{Move to vim script}
\begin{block}{.vimrc to otl2latex.vim}
\begin{itemize}
\item Currently we are just embedding mappings in .vimrc
\item Ok for testing, not very polished for end user
\end{itemize}
\end{block} \end{frame}
\begin{frame}
\frametitle{Reverse Engineering}
\begin{block}{latex2otl}
\begin{itemize}
\item take a tex file
\item generate the otl file
\end{itemize}
\end{block} \end{frame}
\section{}
\section{}
\end{document}

View file

@ -0,0 +1,9 @@
\beamer@endinputifotherversion {3.07pt}
\select@language {english}
\beamer@sectionintoc {1}{Introduction}{3}{0}{1}
\beamer@subsectionintoc {1}{1}{What is otl2latex?}{3}{0}{1}
\beamer@subsectionintoc {1}{2}{Requirements}{4}{0}{1}
\beamer@sectionintoc {2}{Usage}{6}{0}{2}
\beamer@subsectionintoc {2}{1}{Basics}{6}{0}{2}
\beamer@subsectionintoc {2}{2}{Vim mappings}{9}{0}{2}
\beamer@subsectionintoc {2}{3}{Future Extensions}{14}{0}{2}

View file

@ -0,0 +1,149 @@
#!/usr/bin/gawk -f
# Copyright (C) 2007 by Steve Litt, all rights reserved.
# Licensed under the GNU General Public License, version 2.
# otl2lyx.awk version 0.1.1 pre-alpha
# 4/23/2007
# Fixed insertion of other environments at bodytext to bodytext
# borders.
#
# USAGE: ./otl2lyx level-environment-table-file outline-file
#
# level-table-structure:
# 1: Top-level-environment-name
# 2: 2nd-level-environment-name
# 3: 3rd-level-environment-name
# 4: 4th-level-environment-name
# 5: 5th-level-environment-name
# 6: 6th-level-environment-name
# bodytext: environment-name-for-normal-text
#
# Example for a book:
# 1: Chapter
# 2: Section
# 3: Subsection
# 4: Subsubsection
# 5: Paragraph
# 6: Subparagraph
# 7: Garbage7
# bodytext: Standard
BEGIN{
FS=":[ \t]*"
OFS="\x09"
lastinbodytext=0
}
### BLOW OFF BLANKS OUTSIDE OF BODY TEXT
$0~/^[ \t]*$/ && inbodytext==0{
next
}
### FILL THE ENVIRONMENTS ARRAY ###
ARGIND==1{
FS=":[ \t]*";
sub(/[ \t]*$/,"",$2);
environments[$1] = $2;
next;
}
FNR==101{
for(i in environments) print "level=" i ", string=" environments[i];
}
### FIELD SEPARATOR IS TAB ON THE OUTLINE FILE ###
{FS="\x09"; }
### INCREMENT OUTLINE ID NUMBER
{ol_id++}
### CALCULATE LEVEL ###
{
for(i=1;i<=NF;i++)
if($i == ""){
} else {
break
}
this["level"] = i
if(ol_id == ol_id_first)
this["level"]--
}
### FIGURE TEXT ###
{
this["text"] = ""
for(i=1;i<=NF;i++){
if($i != ""){
if(this["text"] == ""){
this["text"] = this["text"] $i
} else {
this["text"] = this["text"] " " $i
}
}
}
sub(/^[ \t]+/, "", this["text"]);
sub(/[ \t]+$/, "", this["text"]);
}
### SET BODYTEXT FLAGS ###
{ inbodytext = 0; newbodytext = 0; endbodytext = 0; btblankline=0; }
this["text"] ~ /^:[ \t]+[^ \t]/{
inbodytext = 1;
sub(/^:[ \t]*/, "", this["text"]);
this["text"] = this["text"] " ";
}
this["text"] == "" || this["text"] == ":"{
this["text"] = "";
inbodytext = lastinbodytext;
if(inbodytext == 1){
endbodytext = 1;
newbodytext = 1;
btblankline = 1;
}
}
lastinbodytext == 1 && inbodytext == 0{
endbodytext = 1;
}
lastinbodytext == 0 && inbodytext == 1{
newbodytext = 1;
}
{ lastinbodytext = inbodytext; }
### QUOTE SINGLE BACKSLASHES FOR LATEX ###
{gsub(/\\/,"\r\\backslash\r", this["text"]);}
### PRINT LYX CONTENT ###
endbodytext == 1{
print "\\end_layout"
print ""
}
newbodytext == 1{
print "\\begin_layout " environments["bodytext"]
}
inbodytext == 1{
if(btblankline == 0) print this["text"]
}
inbodytext == 0{
print "\\begin_layout " environments[this["level"]]
print this["text"]
print "\\end_layout"
print ""
}
END{
if(inbodytext == 1){
print "\\end_layout"
print ""
}
}

View file

@ -0,0 +1,263 @@
#!/usr/bin/python2
# otl2ooimpress.py
# needs otl2ooimpress.sh to work in an automated way
#############################################################################
#
# Tool for Vim Outliner files to Open Office Impress files.
# Copyright (C) 2003 by Noel Henson, all rights reserved.
#
# This tool is free software; you can redistribute it and/or
# modify it under the terms of the GNU Library General Public
# License as published by the Free Software Foundation; either
# version 2 of the License, or (at your option) any later version.
#
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Library General Public
# License along with this library; if not, see
# <http://www.gnu.org/licenses/>.
#
#############################################################################
# ALPHA VERSION!!!
###########################################################################
# Basic function
#
# This program accepts VO outline files and converts them
# to the zipped XML files required by Open Office Impress.
#
# 10 outline levels are supported. These loosely correspond to the
# HTML H1 through H9 tags.
#
###########################################################################
# include whatever mdules we need
import sys
###########################################################################
# global variables
level = 0
inputFile = ""
outline = []
flatoutline = []
pageNumber = 0
inPage = 0
debug = 0
###########################################################################
# function definitions
# usage
# print the simplest form of help
# input: none
# output: simple command usage is printed on the console
def showUsage():
print
print "Usage:"
print "otl2ooimpress.py [options] inputfile > outputfile"
print ""
print "output is on STDOUT"
print
# getArgs
# Check for input arguments and set the necessary switches
# input: none
# output: possible console output for help, switch variables may be set
def getArgs():
global inputfile, debug
if (len(sys.argv) == 1):
showUsage()
sys.exit()()
else:
for i in range(len(sys.argv)):
if (i != 0):
if (sys.argv[i] == "-d"):
debug = 1 # test for debug flag
elif (sys.argv[i] == "-?"): # test for help flag
showUsage() # show the help
sys.exit() # exit
elif (sys.argv[i] == "--help"):
showUsage()
sys.exit()
elif (sys.argv[i] == "-h"):
showUsage()
sys.exit()
elif (sys.argv[i][0] == "-"):
print "Error! Unknown option. Aborting"
sys.exit()
else: # get the input file name
inputfile = sys.argv[i]
# getLineLevel
# get the level of the current line (count the number of tabs)
# input: linein - a single line that may or may not have tabs at the beginning
# output: returns a number 1 is the lowest
def getLineLevel(linein):
strstart = linein.lstrip() # find the start of text in line
x = linein.find(strstart) # find the text index in the line
n = linein.count("\t", 0, x) # count the tabs
return(n + 1) # return the count + 1 (for level)
# getLineTextLevel
# get the level of the current line (count the number of tabs)
# input: linein - a single line that may or may not have tabs at the beginning
# output: returns a number 1 is the lowest
def getLineTextLevel(linein):
strstart = linein.lstrip() # find the start of text in line
x = linein.find(strstart) # find the text index in the line
n = linein.count("\t", 0, x) # count the tabs
n = n + linein.count(" ", 0, x) # count the spaces
return(n + 1) # return the count + 1 (for level)
# colonStrip(line)
# stip a leading ':', if it exists
# input: line
# output: returns a string with a stipped ':'
def colonStrip(line):
if (line[0] == ":"):
return line[1:].lstrip()
else:
return line
# processLine
# process a single line
# input: linein - a single line that may or may not have tabs at the beginning
# level - an integer between 1 and 9 that show the current level
# (not to be confused with the level of the current line)
# output: through standard out
def processLine(linein):
global inPage, pageNumber
if (linein.lstrip() == ""):
print
return
if (getLineLevel(linein) == 1):
if (inPage == 1):
print '</draw:text-box></draw:page>'
inPage = 0
pageNumber += 1
outstring = '<draw:page draw:name="'
outstring += 'page'
outstring += str(pageNumber)
outstring += '" draw:style-name="dp1" draw:id="1" ' + \
'draw:master-page-name="Default" ' + \
'presentation:presentation-page-layout-name="AL1T0">'
print outstring
outstring = '<draw:text-box presentation:style-name="pr1" ' + \
'draw:layer="layout" svg:width="23.911cm" ' + \
'svg:height="3.508cm" svg:x="2.057cm" svg:y="1.0cm" ' + \
'presentation:class="title">'
print outstring
outstring = '<text:p text:style-name="P1">'
outstring += linein.lstrip()
outstring += "</text:p></draw:text-box>"
print outstring
outstring = '<draw:text-box presentation:style-name="pr1" ' + \
'draw:layer="layout" svg:width="23.911cm" ' + \
'svg:height="3.508cm" svg:x="2.057cm" svg:y="5.38cm" ' + \
'presentation:class="subtitle">'
print outstring
inPage = 1
else:
outstring = '<text:p text:style-name="P1">'
outstring += linein.lstrip()
outstring += '</text:p>'
print outstring
# flatten
# Flatten a subsection of an outline. The index passed is the outline section
# title. All sublevels that are only one level deeper are indcluded in the
# current subsection. Then there is a recursion for those items listed in the
# subsection. Exits when the next line to be processed is of the same or lower
# outline level.
# (lower means shallower)
# input: idx - the index into the outline. The indexed line is the title.
# output: adds reformatted lines to flatoutline[]
def flatten(idx):
if (outline[idx] == ""):
return
if (len(outline) <= idx):
return
titleline = outline[idx]
titlelevel = getLineLevel(titleline)
if (getLineLevel(outline[idx + 1]) > titlelevel):
if (titleline[titlelevel - 1] != " "):
flatoutline.append(titleline.lstrip())
exitflag = 0
while (exitflag == 0):
if (idx < len(outline) - 1):
idx = idx + 1
currlevel = getLineLevel(outline[idx])
if (currlevel == titlelevel + 1):
if (currlevel == outline[idx].find(" ") + 1):
flatoutline.append("\t " + outline[idx].lstrip())
else:
flatoutline.append("\t" + outline[idx].lstrip())
elif (currlevel <= titlelevel):
exitflag = 1
else:
exitflag = 1
return
def printHeader(linein):
print'''<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE office:document-content PUBLIC
"-//OpenOffice.org//DTD OfficeDocument 1.0//EN"
"office.dtd">
<office:document-content xmlns:office="http://openoffice.org/2000/office"
xmlns:style="http://openoffice.org/2000/style"
xmlns:text="http://openoffice.org/2000/text"
xmlns:table="http://openoffice.org/2000/table"
xmlns:draw="http://openoffice.org/2000/drawing"
xmlns:fo="http://www.w3.org/1999/XSL/Format"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:number="http://openoffice.org/2000/datastyle"
xmlns:presentation="http://openoffice.org/2000/presentation"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns:chart="http://openoffice.org/2000/chart"
xmlns:dr3d="http://openoffice.org/2000/dr3d"
xmlns:math="http://www.w3.org/1998/Math/MathML"
xmlns:form="http://openoffice.org/2000/form"
xmlns:script="http://openoffice.org/2000/script"
office:class="presentation" office:version="1.0">
<office:script/>
<office:body>'''
def printFooter():
print '</draw:text-box></draw:page>'
print'</office:body>'
def main():
getArgs()
file = open(inputFile, "r")
linein = file.readline().strip()
outline.append(linein)
linein = file.readline().strip()
while linein != "":
outline.append("\t" + linein)
linein = file.readline().rstrip()
for i in range(0, len(outline) - 1):
flatten(i)
printHeader(flatoutline[0])
for i in range(0, len(flatoutline)):
processLine(flatoutline[i])
printFooter()
file.close()
main()

View file

@ -0,0 +1,34 @@
#!/bin/bash
# otl2ooimpress.sh
# needs otl2ooimpress.py to work at all
#############################################################################
#
# Tool for Vim Outliner files to Open Office Impress files.
# Copyright (C) 2003 by Noel Henson, all rights reserved.
#
# This tool is free software; you can redistribute it and/or
# modify it under the terms of the GNU Library General Public
# License as published by the Free Software Foundation; either
# version 2 of the License, or (at your option) any later version.
#
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Library General Public
# License along with this library; if not, see
# <http://www.gnu.org/licenses/>.
#
#############################################################################
# Path to otl2ooimpress.py
MYPATH=$HOME/bin
# Path to rm
RMPATH=/bin
# Path to zip
ZIPPATH=/usr/bin
$MYPATH/otl2ooimpress.py $1 > content.xml
$ZIPPATH/zip $1.sxi content.xml
$RMPATH/rm content.xml

View file

@ -0,0 +1,199 @@
#!/usr/bin/python2
# otl2table.py
# convert a tab-formatted outline from VIM to tab-delimited table
#
# Copyright (c) 2004 Noel Henson All rights reserved
#
# ALPHA VERSION!!!
###########################################################################
# Basic function
#
# This program accepts text outline files and converts them
# the tab-delimited text tables.
# This:
# Test
# Dog
# Barks
# Howls
# Cat
# Meows
# Yowls
# Becomes this:
# Test Dog Barks
# Test Dog Howls
# Test Cat Meows
# Test Cat Yowls
#
# This will make searching for groups of data and report generation easier.
#
###########################################################################
# include whatever mdules we need
import sys
from string import *
#from time import *
###########################################################################
# global variables
level = 0
inputFile = ""
formatMode = "tab"
noTrailing = 0
columns = []
###########################################################################
# function definitions
# usage
# print the simplest form of help
# input: none
# output: simple command usage is printed on the console
def showUsage():
print
print "Usage:"
print "otl2table.py [options] inputfile > outputfile"
print "Options"
print " -n Don't include trailing columns."
print " -t type Specify field separator type."
print " Types:"
print " tab - separate fields with tabs (default)"
print " csv - separate fields with ,"
print " qcsv - separate fields with \",\""
print " bullets - uses HTML tags <ul> and <li>"
print "output is on STDOUT"
print
# getArgs
# Check for input arguments and set the necessary switches
# input: none
# output: possible console output for help, switch variables may be set
def getArgs():
global inputfile, debug, noTrailing, formatMode
if (len(sys.argv) == 1):
showUsage()
sys.exit()()
else:
for i in range(len(sys.argv)):
if (i != 0):
if (sys.argv[i] == "-d"): debug = 1 # test for debug flag
if (sys.argv[i] == "-n"): noTrailing = 1 # test for noTrailing flag
elif (sys.argv[i] == "-?"): # test for help flag
showUsage() # show the help
sys.exit() # exit
elif (sys.argv[i] == "--help"):
showUsage()
sys.exit()
elif (sys.argv[i] == "-h"):
showUsage()
sys.exit()
elif (sys.argv[i] == "-t"): # test for the type flag
formatMode = sys.argv[i+1] # get the type
i = i + 1 # increment the pointer
elif (sys.argv[i][0] == "-"):
print "Error! Unknown option. Aborting"
sys.exit()
else: # get the input file name
inputfile = sys.argv[i]
# getLineLevel
# get the level of the current line (count the number of tabs)
# input: linein - a single line that may or may not have tabs at the beginning
# output: returns a number 1 is the lowest
def getLineLevel(linein):
strstart = lstrip(linein) # find the start of text in line
x = find(linein,strstart) # find the text index in the line
n = count(linein,"\t",0,x) # count the tabs
return(n+1) # return the count + 1 (for level)
# getLineTextLevel
# get the level of the current line (count the number of tabs)
# input: linein - a single line that may or may not have tabs at the beginning
# output: returns a number 1 is the lowest
def getLineTextLevel(linein):
strstart = lstrip(linein) # find the start of text in line
x = find(linein,strstart) # find the text index in the line
n = count(linein,"\t",0,x) # count the tabs
n = n + count(linein," ",0,x) # count the spaces
return(n+1) # return the count + 1 (for level)
# closeLevels
# print the assembled line
# input: columns - an array of 10 lines (for 10 levels)
# level - an integer between 1 and 9 that show the current level
# (not to be confused with the level of the current line)
# noTrailing - don't print trailing, empty columns
# output: through standard out
def closeLevels():
global level,columns,noTrailing,formatMode
if noTrailing == 1 :
colcount = level
else:
colcount = 10
if formatMode == "tab":
for i in range(1,colcount+1):
print columns[i] + "\t",
print
elif formatMode == "csv":
output = ""
for i in range(1,colcount):
output = output + columns[i] + ","
output = output + columns[colcount]
print output
elif formatMode == "qcsv":
output = "\""
for i in range(1,colcount):
output = output + columns[i] + "\",\""
output = output + columns[colcount] + "\""
print output
for i in range(level+1,10):
columns[i] = ""
# processLine
# process a single line
# input: linein - a single line that may or may not have tabs at the beginning
# format - a string indicating the mode to use for formatting
# level - an integer between 1 and 9 that show the current level
# (not to be confused with the level of the current line)
# output: through standard out
def processLine(linein):
global level, noTrailing, columns
if (lstrip(linein) == ""): return
lineLevel = getLineLevel(linein)
if (lineLevel > level):
columns[lineLevel] = lstrip(rstrip(linein))
level = lineLevel
elif (lineLevel == level):
closeLevels()
columns[lineLevel] = lstrip(rstrip(linein))
else:
closeLevels()
level = lineLevel
columns[lineLevel] = lstrip(rstrip(linein))
def main():
global columns
getArgs()
file = open(inputfile,"r")
for i in range(11):
columns.append("")
linein = lstrip(rstrip(file.readline()))
while linein != "":
processLine(linein)
linein = file.readline()
closeLevels()
file.close()
main()

View file

@ -0,0 +1,713 @@
#!/usr/bin/python2
# otl2tags.py
# Convert an OTL file to any tags-based file using config user-
# definable configuration files. HTML, OPML, XML, LATEX and
# many, many others should be easily supportables.
#
# Copyright (c) 2005-2010 Noel Henson All rights reserved
###########################################################################
# Basic function
#
# This program accepts text outline files in Vim Outliners .otl format
# and converts them to a tags-based equivalent
###########################################################################
# include whatever mdules we need
import sys
from ConfigParser import ConfigParser
import re
###########################################################################
# global variables
config = ConfigParser() # configuration
linecount = 0 # outline size in lines
parents = [] # parent stack, (linenum, enum) enum is an order numer
v = {} # variable dictionary for substitution
outline = [] # line tuples (value, indent)
output = [] # output outline
escapeDict = {} # dictionary of character escape codes
debug = 0
inputfile = ""
###########################################################################
# arugment, help and debug functions
# usage
# print debug statements
# input: string
# output: string printed to standard out
def dprint(*vals):
global debug
if debug != 0:
print >> sys.stderr, vals
# usage
# print the simplest form of help
# input: none
# output: simple command usage is printed on the console
def showUsage():
print """
Usage:
otl2table.py [options] inputfile
Options
-c config-file
-d debug
--help show help
output filenames are based on the input file name and the config file
"""
# getArgs
# Check for input arguments and set the necessary switches
# input: none
# output: possible console output for help, switch variables may be set
def getArgs():
global inputfile, debug, noTrailing, formatMode, config
if (len(sys.argv) == 1):
showUsage()
sys.exit()()
else:
for i in range(len(sys.argv)):
if (i != 0):
if (sys.argv[i] == "-c"): # test for the type flag
config.read(sys.argv[i + 1]) # read the config
i = i + 1 # increment the pointer
elif (sys.argv[i] == "-d"):
debug = 1
elif (sys.argv[i] == "-?"): # test for help flag
showUsage() # show the help
sys.exit() # exit
elif (sys.argv[i] == "--help"):
showUsage()
sys.exit()
elif (sys.argv[i] == "-h"):
showUsage()
sys.exit()
elif (sys.argv[i][0] == "-"):
print "Error! Unknown option. Aborting"
sys.exit()
else: # get the input file name
inputfile = sys.argv[i]
# printConfig
# Debugging routine to print the parsed configuration file
# input: none
# output: configuration data printed to console
def printConfig():
global config
print >> sys.stderr, "Config ---------------------------------------------"
list = config.sections()
for i in range(len(list)):
print >> sys.stderr
print >> sys.stderr, list[i]
for x in config.options(list[i]):
if (x != "name") and (x != "__name__"):
print >> sys.stderr, x, ":", config.get(list[i], x)
print >> sys.stderr, "----------------------------------------------------"
print >> sys.stderr
###########################################################################
# low-level outline processing functions
# indentLevel
# get the level of the line specified by linenum
# input: line
# output: returns the level number, 1 is the lowest
def indentLevel(line):
strstart = line.lstrip() # find the start of text in line
x = line.find(strstart) # find the text index in the line
n = line.count("\t", 0, x) # count the tabs
n = n + line.count(" ", 0, x) # count the spaces
return(n + 1) # return the count + 1 (for level)
# stripMarker
# return a line without its marker and leading and trailing whitespace
# input: line, marker
# output: stripped line
def stripMarker(line, marker):
return line.lstrip(marker).strip()
# getLineType
# return the type of the line specified by linenum
# input: line
# output: returns text, usertext, table, preftext, etc.
def getLineType(line):
if (line[0] == ':'):
return 'text'
elif (line[0] == ';'):
return 'preftext'
elif (line[0] == '>'):
return 'usertext'
elif (line[0] == '<'):
return 'userpreftext'
elif (line[0] == '|'):
return 'table'
elif (line[0] == '-'):
return 'bulletheading'
elif (line[0] == '+'):
return 'numberheading'
# elif (line[0] == '['):
# return 'checkboxheading'
elif (line[0] == ''):
return 'blank'
else:
return 'heading'
# getChildren
# return a list of line numbers for children of the passed line number
# input: linenum
# output: a (possibly) empty list of children
def getChildren(linenum):
global outline, linecount
children = []
mylevel = outline[linenum][1]
childlevel = mylevel + 1
linenum = linenum + 1
while (linenum < linecount) and (outline[linenum][1] > mylevel):
if (outline[linenum][1] == childlevel):
children.append(linenum)
linenum = linenum + 1
return children
# subTags
# substitute variables in output expressions
# input: section - section from config
# input: type - object type (to look up in config)
# input: - substitution item (by name) from config array
# output: string - the substitution expression with variables inserted
def subTags(section, type):
global config, v, parents
varlist = v.keys()
pattern = config.get(section, type)
if len(parents) > 0:
v["%p"] = str(parents[len(parents) - 1])
for var in varlist:
x = ""
x = var
y = ""
y = v.get(var)
pattern = re.sub(x, y, pattern)
return pattern
#getBlock
#return a list of lines that match a mark (like : or ;)
#input: line number
#output: list of stripped lines
def getBlock(linenum, marker):
global outline, linecount
lines = []
line = outline[linenum][0]
while line[0] == marker:
lines.append(stripMarker(line, marker))
linenum = linenum + 1
if linenum == linecount:
break
line = outline[linenum][0]
return lines
#getUnstrippedBlock
#return a list of lines that match a mark (like : or ;)
#input: line number
#output: list of stripped lines
def getUnstrippedBlock(linenum, marker):
global outline, linecount
lines = []
line = outline[linenum][0]
while line[0] == marker:
lines.append(line)
linenum = linenum + 1
if linenum == linecount:
break
line = outline[linenum][0]
return lines
###########################################################################
# embedded object processing functions
# buildEscapes
# construct the dictionary for escaping special characters
# intput: config:escapes
# output: filled escapes dictionary
def buildEscapes():
escapes = config.get("Document", "escapes")
if len(escapes):
list = escapes.split(" ")
for pair in list:
key, value = pair.split(",")
escapeDict[key] = value
# charEscape
# escape special characters
# input: line
# output: modified line
def charEscape(line):
return "".join(escapeDict.get(c, c) for c in line)
# getURL
# if there is a url, [url text], return the extracted link, url and value
# input: line
# output: link, url, text
def getURL(line):
tags = []
for tag in line.split("]"):
tags.append(tag.split("["))
for tag in tags:
if len(tag) > 1 and re.search(" ", tag[1]):
link = tag[1]
url, text = link.split(" ", 1)
link = "[" + tag[1] + "]"
return link, url, text
# return link.group(0), url, text
# else:
# return None, None, None
return None, None, None
def handleURL(line):
link, url, text = getURL(line)
if link is None:
return re.replace(line, "[url]", "")
v["%u"] = url
v["%v"] = text
text = subTags("URLs", "url")
line = re.replace(line, link, text)
url = subTags("URLs", "url-attr")
line = re.replace(line, "[url]", url)
return line
###########################################################################
# outline header processing functions
# all outline object processors accept and output the following:
# input: linenum, enum
# output: print the output for each object
def handleHeading(linenum, enum):
global outline, parents
line = outline[linenum][0]
# url handling
# extract url data from line
# replace url object in line
# subTags line
# replace url attribute marker
v["%%"] = line
v["%l"] = str(outline[linenum][1])
v["%n"] = str(linenum)
v["%c"] = str(enum)
children = getChildren(linenum)
if enum == 1:
output.append(subTags("Headings", "before-headings"))
if children:
output.append(subTags("Headings", "branch-heading"))
parents.append([linenum, enum])
handleObjects(children)
parents.pop()
output.append(subTags("Headings", "after-headings"))
else:
output.append(subTags("Headings", "leaf-heading"))
def handleBulleted(linenum, enum):
global outline, parents
v["%%"] = outline[linenum][0]
v["%l"] = str(outline[linenum][1])
v["%n"] = str(linenum)
v["%c"] = str(enum)
children = getChildren(linenum)
if enum == 1:
output.append(subTags("Headings", "before-bulleted-headings"))
if children:
output.append(subTags("Headings", "bulleted-branch-heading"))
parents.append([linenum, enum])
handleObjects(children)
parents.pop()
output.append(subTags("Headings", "after-bulleted-headings"))
else:
output.append(subTags("Headings", "bulleted-leaf-heading"))
def handleNumbered(linenum, enum):
global outline, parents
v["%%"] = outline[linenum][0]
v["%l"] = str(outline[linenum][1])
v["%n"] = str(linenum)
v["%c"] = str(enum)
children = getChildren(linenum)
if enum == 1:
output.append(subTags("Headings", "before-numbered-headings"))
if children:
output.append(subTags("Headings", "numbered-branch-heading"))
parents.append([linenum, enum])
handleObjects(children)
parents.pop()
output.append(subTags("Headings", "after-numbered-headings"))
else:
output.append(subTags("Headings", "numbered-leaf-heading"))
###########################################################################
# outline text block processing functions
# all outline object processors accept and output the following:
# input: linenum, enum
# output: print the output for each object
def handleText(linenum, enum):
global outline, parents
if enum != 1:
return # only execute for first call
v["%l"] = str(outline[linenum][1])
v["%n"] = str(linenum)
v["%c"] = str(enum)
list = getBlock(linenum, ':')
output.append(subTags("Text", "before"))
lines = ""
for line in list:
if line == "":
lines = lines + config.get("Text", "paragraph-sep")
else:
lines = lines + line + config.get("Text", "line-sep")
v["%%"] = lines
output.append(subTags("Text", "text"))
output.append(subTags("Text", "after"))
def handleUserText(linenum, enum):
global outline, parents
if enum != 1:
return # only execute for first call
v["%l"] = str(outline[linenum][1])
v["%n"] = str(linenum)
v["%c"] = str(enum)
list = getBlock(linenum, '>')
output.append(subTags("UserText", "before"))
lines = ""
for line in list:
if line == "":
lines = lines + config.get("UserText", "paragraph-sep")
else:
lines = lines + line + config.get("UserText", "line-sep")
v["%%"] = lines.strip() # remove a possible extra separator
output.append(subTags("UserText", "text"))
output.append(subTags("UserText", "after"))
def handlePrefText(linenum, enum):
global outline, parents
if enum != 1:
return # only execute for first call
v["%l"] = str(outline[linenum][1])
v["%n"] = str(linenum)
v["%c"] = str(enum)
list = getBlock(linenum, ';')
output.append(subTags("PrefText", "before"))
lines = ""
for line in list:
if line == "":
lines = lines + config.get("PrefText", "paragraph-sep")
else:
lines = lines + line + config.get("PrefText", "line-sep")
v["%%"] = lines.strip() # remove a possible extra separator
output.append(subTags("PrefText", "text"))
output.append(subTags("PrefText", "after"))
def handleUserPrefText(linenum, enum):
global outline, parents
if enum != 1:
return # only execute for first call
v["%l"] = str(outline[linenum][1])
v["%n"] = str(linenum)
v["%c"] = str(enum)
list = getBlock(linenum, '<')
output.append(subTags("UserPrefText", "before"))
lines = ""
for line in list:
if line == "":
lines = lines + config.get("UserPrefText", "paragraph-sep")
else:
lines = lines + line + config.get("UserPrefText", "line-sep")
v["%%"] = lines.strip() # remove a possible extra separator
output.append(subTags("UserPrefText", "text"))
output.append(subTags("UserPrefText", "after"))
###########################################################################
# outline table processing functions
# isAlignRight
# return flag
# input: col, a string
def isAlignRight(col):
l = len(col)
if (col[0:2] == " ") and (col[l - 2:l] != " "):
return 1
else:
return 0
# isAlignLeft
# return flag
# input: col, a string
def isAlignLeft(col):
l = len(col)
if (col[0:2] != " ") and (col[l - 2:l] == " "):
return 1
else:
return 0
# isAlignCenter
# return flag
# input: col, a string
def isAlignCenter(col):
l = len(col)
if (col[0:2] == " ") and (col[l - 2:l] == " "):
return 1
else:
return 0
# handleHeaderRow
# process a non-header table row
# input: row
# output: print the output for each object
def handleHeaderRow(row):
global outline, parents
row = row.rstrip("|").lstrip("|")
columns = row.split("|")
output.append(subTags("Tables", "before-table-header"))
for col in columns:
v["%%"] = col.strip()
if isAlignCenter:
output.append(subTags("Tables", "table-header-column-center"))
elif isAlignCenter:
output.append(subTags("Tables", "table-header-column-center"))
elif isAlignCenter:
output.append(subTags("Tables", "table-header-column-center"))
else:
output.append(subTags("Tables", "table-header-column"))
output.append(subTags("Tables", "after-table-header"))
# handleRow
# process a non-header table row
# input: row
# output: print the output for each object
def handleRow(row):
global outline, parents
if row[0:2] == "||":
handleHeaderRow(row)
return
row = row.rstrip("|").lstrip("|")
columns = row.split("|")
output.append(subTags("Tables", "before-table-row"))
for col in columns:
v["%%"] = col.strip()
if isAlignCenter:
output.append(subTags("Tables", "table-column-center"))
elif isAlignLeft:
output.append(subTags("Tables", "table-column-left"))
elif isAlignRight:
output.append(subTags("Tables", "table-column-right"))
else:
output.append(subTags("Tables", "table-column"))
output.append(subTags("Tables", "after-table-row"))
# handleTable
# process a table
# input: linenum, enum
# output: print the output for each object
def handleTable(linenum, enum):
global outline, parents
if enum != 1:
return # only execute for first call
v["%l"] = str(outline[linenum][1])
v["%n"] = str(linenum)
v["%c"] = str(enum)
list = getUnstrippedBlock(linenum, '|')
output.append(subTags("Tables", "before"))
for row in list:
handleRow(row)
output.append(subTags("Tables", "after"))
###########################################################################
# outline wrapper processing functions
# addPreamble
# create the 'header' for the output document
# input: globals
# output: standard out
def addPreamble():
global outline, v
v["%%"] = ""
output.append(subTags("Document", "preamble"))
# addPostamble
# create the 'header' for the output document
# input: globals
# output: standard out
def addPostamble():
global outline, v
v["%%"] = ""
output.append(subTags("Document", "postamble"))
###########################################################################
# outline tree fuctions
# handleObject
# take an object and invoke the appropriate fuction to precess it
# input: linenum, enum (enum is the child order number of a parent)
# output: print the output of a object
def handleObject(linenum, enum):
global outline, linecount
obj = getLineType(outline[linenum][0])
if obj == 'heading':
handleHeading(linenum, enum)
elif obj == 'bulled':
handleBulleted(linenum, enum)
elif obj == 'numbered':
handleNumbered(linenum, enum)
elif obj == 'text':
handleText(linenum, enum)
elif obj == 'usertext':
handleUserText(linenum, enum)
elif obj == 'preftext':
handlePrefText(linenum, enum)
elif obj == 'userpreftext':
handleUserPrefText(linenum, enum)
elif obj == 'table':
handleTable(linenum, enum)
else:
print
print "Error: unknown line type @ ", linenum
sys.exit(1)
# handleObjects
# take an object list and invoke the appropriate fuctions to precess it
# input: linenum
# output: print the output of a object
def handleObjects(objs):
for i in range(len(objs)):
handleObject(objs[i], i + 1)
###########################################################################
# file functions
# readFile
# read the selected file into lines[]
# input: filename to be loaded
# output: a loaded-up lines[]
def readFile(inputfile):
global outline, linecount, config
file = open(inputfile, "r")
linein = file.readline()
while linein != "":
indent = indentLevel(linein)
line = charEscape(linein.strip())
outline.append([line, indent])
linein = file.readline()
file.close
outline[0][1] = 0 # set the first line to level 0
linecount = len(outline)
###########################################################################
# Main Program Loop
def main():
global outline, inputfile, linecount
# get the arguments
getArgs()
# constuct the escapes dictionary
buildEscapes()
# read the input file
readFile(inputfile)
# get the title
v["%t"] = outline[0][0].strip()
# construct the initial data
# parsing headings, text and tables
# but not parsing links or images
addPreamble()
if config.get("Document", "first-is-node") == "true":
objs = [0]
else:
objs = getChildren(0)
handleObjects(objs)
addPostamble()
# handle embeded objects
# parsing and constructing links, images and other embedded objects
for i in range(len(output)):
output[i] = handleURL(output[i])
# output the final data
for line in output:
if line.strip() != "":
print line.strip()
main()

View file

@ -0,0 +1,96 @@
# FreeMind configuration file
# Variables:
# %% - data
# %l - outline level
# %t - document title (the first line of the document)
# %n - line number (document title is 0)
# %N - line number of first line of block (for text blocks)
# %p - parent line number
# %c - my child number, a heading's order number under it's parent
# %u - URL link field: http://www.a.com in [http://www.a.com link to me]
# %v - URL text field: 'link to me' in [http://www.a.com link to me]
#
# Special Markers
# [url] - a marker for an optional url/link attribute: url-attr
#
# Flags
# first-is-node
# true for output to programs like FreeMind and GraphViz
# where the first line of the file is the top-most node
[Document]
escapes: &,&amp; ",&quot; ',&apos; >,&gt; <,&lt;
first-is-node: true
preamble: <map version="0.8.0">
postamble: </map>
[Headings]
before-headings:
branch-heading: <node [url] TEXT="%%">
leaf-heading: <node [url] TEXT="%%"/>
after-headings: </node>
before-bulleted-headings:
bulleted-branch-heading: <node [url] TEXT="*%%">
bulleted-leaf-heading: <node [url] TEXT="*%%"/>
after-bulleted-headings: </node>
before-numbered-headings:
numbered-branch-heading: <node [url] TEXT="%c %%">
numbered-leaf-heading: <node [url] TEXT="%c %%"/>
after-numbered-headings: </node>
[Text]
paragraph-sep: &#xa;
line-sep: &#xa;
before:
text: <node TEXT="%%"/>
after:
[PrefText]
paragraph-sep: \n
line-sep: \n
before: <node TEXT="<html><body><pre>
text: %%
after: </pre></body></html>"></node>
[UserPrefText]
user-pref-paragraph-sep:
before-user-preftext:
user-preftext:
after-user-preftext:
[UserText]
user-paragraph-sep:
before-user-text:
user-text:
after-user-text:
[Tables]
before: <node TEXT="<html><body><table>
before-table-header: <th>
table-header-column-left: <td>%%</td>
table-header-column-center: <td>%%</td>
table-header-column-right: <td>%%</td>
after-table-header: </th>
before-table-row: <tr>
table-column-left: <td>%%</td>
table-column-center: <td>%%</td>
table-column-right: <td>%%</td>
after-table-row: </tr>
after: </table></body></html>"/>
[URLs]
url: %v
url-attr: LINK="%u"

View file

@ -0,0 +1,104 @@
# otl2html - medium complexity
# Variables:
# %% - data
# %l - outline level
# %t - document title (the first line of the document)
# %n - line number (document title is 0)
# %N - line number of first line of block (for text blocks)
# %p - parent line number
# %c - my child number, a heading's order number under it's parent
#
# first-is-node = true for output to programs like FreeMind and GraphViz
# where the first line of the file is the top-most node.
[Document]
escapes:
first-is-node: true
preamble: digraph V {
node [shape=box style=rounded]
# comment out rankdir to use top-to-bottom placement
rankdir=LR
node0 [label="%t"]
postamble: }
[Headings]
before-headings:
branch-heading: node%n [label="%%"]
node%p -> node%n
leaf-heading: node%n [label="%%"]
node%p -> node%n
after-headings:
before-bulleted-headings:
bulleted-branch-heading: node%n [label="*%%"]
node%p -> node%n
bulleted-leaf-heading: node%n [label="*%%"]
node%p -> node%n
after-bulleted-headings:
before-numbered-headings:
numbered-branch-heading: node%n [label="%c %%"]
node%p -> node%n
numbered-leaf-heading: node%n [label="%c %%"]
node%p -> node%n
after-numbered-headings:
[Text]
paragraph-sep:
line-sep:
before: node%n [label="
text: %%
after: "]
node%p -> node%N
[PrefText]
pref-paragraph-sep:
before:
preftext: node%n [label="%%"]
after:
node%p -> node%N
[UserPrefText]
user-pref-paragraph-sep:
before-user-preftext:
user-preftext: node%n [label="%%"]
after-user-preftext:
node%p -> node%N
[UserText]
user-paragraph-sep:
before-user-text:
user-text node%n: [label="%%"]
after-user-text:
node%p -> node%N
[Tables]
before:
before-table-header:
table-header-column:
table-header-column-left:
table-header-column-center:
table-header-column-right:
after-table-header:
before-table-row:
table-column:
table-column-left:
table-column-center:
table-column-right:
after-table-row:
after:
[URLs]
url: %v
url-attr:

View file

@ -0,0 +1,324 @@
#
# VimOutliner (OTL) XHTML pretty printer for mod_perl2/apache2.
#
# Copyright (c) 2006-2009, Mahlon E. Smith <mahlon@martini.nu>
# All rights reserved.
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
#
# * Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# * Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in the
# documentation and/or other materials provided with the distribution.
# * Neither the name of Mahlon E. Smith nor the names of his
# contributors may be used to endorse or promote products derived
# from this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY
# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
# DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#
package Apache::OTL;
use strict;
use warnings;
use Apache2::Const qw/ DECLINED OK /;
use Apache2::Request;
use Apache2::RequestRec;
use Apache2::RequestUtil;
use Apache2::RequestIO;
use Apache2::Log;
use Time::HiRes 'gettimeofday';
sub handler
{
my $VERSION = '0.6';
my $ID = '$Id$';
my $r = shift;
my $t0 = Time::HiRes::gettimeofday;
my (
$file, # the absolute file path
$title, # the file's title
$uri, # the file uri
$data, # file contents
@blocks, # todo groupings
$mtime, # last modification time of otl file
$get, # get arguments (sorting, etc)
%opt, # options from otl file
);
# sanity checks
return DECLINED unless $r->method eq 'GET';
( $file, $uri ) = ( $r->filename, $r->uri );
return DECLINED unless -e $file;
$mtime = localtime( (stat(_))[9] );
my $req = Apache2::Request->new($r);
$get = $req->param || {};
my %re = (
title => qr/(?:.+)?\/(.+).otl$/i,
percent => qr/(\[.\]) (\d+)%/,
todo => qr/(\[_\]) /,
done => qr/(\[X\]) /,
user => qr/^(?:\t+)?\<(.+)/,
user_wrap => qr/^(?:\t+)?\>(.+)/,
body_wrap => qr/^(?:\t+)?:(.+)/,
body => qr/^(?:\t+)?;(.+)/,
time => qr/(\d{2}:\d{2}:\d{2})/,
date => qr/(\d{2,4}-\d{2}-\d{2})/,
subitem => qr/^\t(?!\t)/,
remove_tabs => qr/^(?:\t+)?(.+)/,
linetext => qr/^(?:\[.\] (?:\d+%)?)? (.+)/,
comma_sep => qr/(?:\s+)?\,(?:\s+)?/,
hideline => qr/(?:\t+)?\#/,
);
# snag file
open OTL, $file
or ( $r->log_error("Unable to read $file: $!") && return DECLINED );
do {
local $/ = undef;
$data = <OTL>; # shlorp
};
close OTL;
# just spit out the plain otl if requested.
if ( $get->{'show'} && $get->{show} eq 'source' ) {
$r->content_type('text/plain');
$r->print( $data );
return OK;
}
# divide each outline into groups
# skip blocks that start with a comment '#'
@blocks = grep { $_ !~ /^\#/ } split /\n\n+/, $data;
# get optional settings and otl title
{
my $settings = shift @blocks;
if ($settings =~ $re{user}) {
%opt = map { split /=/ } split /\s?:/, $1;
}
# if the first group wasn't a comment,
# we probably just aren't using a settings
# line. push the group back into place.
else {
unshift @blocks, $settings;
}
}
# Now that we have tried to detect settings,
# remove any level 0 blocks that are user data.
@blocks = grep { $_ !~ /^[\<\>]/ } @blocks;
# GET args override settings
$opt{$_} = $get->{$_} foreach keys %$get;
# set title (fallback to file uri)
$title =
$opt{title}
? $opt{title}
: $1 if $uri =~ $re{title};
# start html output
$r->content_type('text/html');
$r->print(<<EHTML);
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html>
<!--
generated by otl_handler $VERSION
Mahlon E. Smith <mahlon\@martini.nu>
http://projects.martini.nu/apache-otl/
Get VimOutliner at: http://www.vimoutliner.org/
-->
<head>
<title>$title</title>
EHTML
# optional styles
if ( $opt{style} ) {
foreach ( split /$re{'comma_sep'}/, $opt{style} ) {
my $media = $_ =~ /print/ ? 'print' : 'screen';
print qq{\t<link href="$_" rel="stylesheet" media="$media" type="text/css" />\n};
}
}
# optional javascript
if ( $opt{js} ) {
$r->print( "\t<script type=\"text/javascript\" src=\"$_\"></script>\n" )
foreach split /$re{'comma_sep'}/, $opt{js};
$r->print( ' ' x 4, "</head>\n" );
$r->print( ' ' x 4, "<body>\n" );
} else {
$r->print(<<EHTML);
</head>
<body>
EHTML
}
# title, last modification times
$r->print("<div class=\"header\">$opt{title}</div>\n") if $opt{title};
$r->print("<div class=\"last_mod\">Last modified: $mtime</div>\n") if $opt{last_mod};
if ($opt{legend}) {
$r->print(<<EHTML);
<div class="legend">
<span class="done">Item completed</span><br />
<span class="todo">Item is incomplete</span><br />
</div>
EHTML
}
# sorter
if ($opt{sort}) {
my %sorts = (
alpha => 'alphabetical',
percent => 'percentages',
);
$r->print("<div class=\"sort\">Sort: \n");
foreach (sort keys %sorts) {
if ($opt{sorttype} eq $_ && $opt{sortrev}) {
$r->print("<a href=\"$uri?sorttype=$_\">$sorts{$_}</a>&nbsp;");
} elsif ($opt{sorttype} eq $_ && ! $opt{sortrev}) {
$r->print("<a href=\"$uri?sorttype=$_&sortrev=1\">$sorts{$_}</a>&nbsp;");
} else {
$r->print("<a href=\"$uri?sorttype=$_\">$sorts{$_}</a>&nbsp;");
}
}
$r->print("</div>\n");
}
foreach my $block ( sort { sorter(\%opt, \%re) } @blocks ) {
# separate outline items
my @lines;
foreach my $line ( split /\n/, $block ) {
push @lines, $line unless $line =~ $re{hideline} ||
$line =~ $re{user} || $line =~ $re{user_wrap};
}
my $data = [];
# build structure and get item counts
my ( $subs, $comments, $subsubs ) = ( 0, 0, 0 );
foreach ( @lines ) {
if (/$re{body_wrap}/) {
$comments++;
}
elsif (/$re{subitem}/) {
$subs++;
}
my $level = 0;
$level = $1 =~ tr/\t/\t/ if /^(\t+)/;
$level++;
s#$re{remove_tabs}#$1# unless $opt{'debug'};
push @$data, [ $level, $_ ];
}
$subsubs = ( scalar @lines - 1 ) - $subs - $comments;
# begin parsing structure
$r->print("<div class=\"outline\">\n");
$r->print("<ul>\n") unless $opt{'debug'};
my $i = 0;
foreach ( @$data ) {
my ( $level, $line ) = @$_;
if ( $opt{'debug'} ) {
my $in = "&nbsp;" x $level x 4;
$r->print( "$level:$in $line<br />\n" );
next;
}
my $next_level = $data->[ $i+1 ] ? $data->[ $i+1 ]->[0] : 0;
my $in = "\t" x $level;
$line =~ s#$re{'time'}#<span class="time">$1</span>#g;
$line =~ s#$re{date}#<span class="date">$1</span>#g;
$line =~ s#$re{percent}#$1 <span class="percent">$2%</span>#;
# append counts
if ( $i == 0 && $opt{counts} && $line !~ $re{comment} ) {
my $itmstr = $subs == 1 ? 'item' : 'items';
my $sitmstr = $subsubs == 1 ? 'subitem' : 'subitems';
$line .= " <span class=\"counts\">$subs $itmstr, $subsubs $sitmstr</span>";
}
my $li_class = '>';
$li_class = ' class="todo">' if $line =~ s#$re{todo}##;
$li_class = ' class="done">' if $line =~ s#$re{done}##;
$li_class = ' class="comment_pre">' if $line =~ s#$re{body}#$1#;
$li_class = ' class="comment">' if $line =~ s#$re{body_wrap}#$1#;
if ( $next_level == $level || $next_level == 0 ) {
$r->print( "$in<li" . $li_class . "$line</li>\n" );
}
elsif ( $next_level < $level ) {
$r->print( "$in<li" . $li_class . "$line</li>\n" );
for (my $x = $level - 1; $x >= $next_level; $x--) {
my $in = "\t" x $x;
$r->print( "$in</ul>\n$in</li>\n" );
}
}
else {
# implicit: $next_level > $level AND $next_level != 0
$r->print("$in<li" . $li_class . "$line\n$in<ul>\n");
}
$i++;
}
unless ( $opt{'debug'} ) {
for (my $x = $data->[ scalar @$data - 1]->[0] - 1; $x > 0; $x--) {
my $in = "\t" x $x;
$r->print( "$in</ul>\n$in</li>\n" );
}
$r->print( "</ul>\n" );
}
$r->print( "</div>\n\n" );
}
my $t1 = Time::HiRes::gettimeofday;
my $td = sprintf("%0.3f", $t1 - $t0);
$r->print(" <div class=\"timer\">OTL parsed in $td secs</div>\n") if $opt{timer};
$r->print(<<EHTML);
</body>
</html>
EHTML
return OK;
}
sub sorter
{
my ($opt, $re) = @_;
return 0 unless $opt->{sorttype};
my ($sa, $sb);
if ($opt->{sorttype} eq 'percent') {
$sa = $2 if $a =~ $re->{percent};
$sb = $2 if $b =~ $re->{percent};
return $opt->{sortrev} ? $sb <=> $sa : $sa <=> $sb;
}
else {
$sa = $1 if $a =~ $re->{linetext};
$sb = $1 if $b =~ $re->{linetext};
return $opt->{sortrev} ? $sb cmp $sa : $sa cmp $sb;
}
}
1;

View file

@ -0,0 +1,155 @@
WHAT IS THIS?
---------------------------------------------------------------------
Vimoutliner already comes with some otl to HTML converters that work
quite well. I maintain a few different otl files, that are displayed
on a internal intranet - the step of converting to HTML on every little
change before upload was becoming mildly irritating, and countering my
near legendary laziness.
This mod_perl handler teaches apache how to pretty print otl natively.
Now, I can just edit the otl files directly - skip the conversion step
altogether, and let Apache make some delicious looking outlines.
INSTALLATION
---------------------------------------------------------------------
First of all, prerequisites!
- apache2
- mod_perl2
- libapreq2 (Apache2::Request)
Add the following lines in your httpd.conf, or in a
separate otl.conf in the apache Includes directory:
-------------------------
PerlSwitches -I/path/to/perl/libraries
PerlModule Apache::OTL
<FilesMatch ".*\.otl">
SetHandler perl-script
PerlResponseHandler Apache::OTL
</FilesMatch>
-------------------------
Doublecheck that your apreq2 module is setup to load, as well.
That's it. Apache will now pretty-print all your otl files.
SETTINGS
---------------------------------------------------------------------
Settings for the otl_handler are stored on the first line of the otl
files themselves, prefixed by the 'user no wrap' character, '<'. See
the sample.otl for an example settings line. All settings are entirely
optional.
title
Type: string
Default: filename
The title of the OTL. Used as a header, and the html title.
If this is not set, the html title is derived from the filename.
style
Type: string
Default: none
A path to css style(s).
Comma separated values load different files in order.
Media type defaults to 'screen', if the css name contains the
string 'print' anywhere, the media type is changed to print.
:style=/css/otl_style.css,/css/print_style.css
js
Type: string
Default: none
Use javascript? If set, loads an external javascript library.
Comma separated values load diff files in order.
last_mod
Type: boolean
Default: 0
Show modification time of the otl file?
legend
Type: boolean
Default: 0
Display small legend for todo and done items?
sort
Type: boolean
Default: 0
Show sort links?
sorttype
Type: string
Default: none
Default sorting method. Valid values are
percent
alpha
sortrev
Type: boolean
Default: 0
Should we default to reverse sorting?
counts
Type: boolean
Default: 0
Count and display sub items?
timer
Type: boolean
Default: 0
Display how long the parser took to generate the html?
INCLUDED FILES
---------------------------------------------------------------------
/Apache/OTL.pm
The mod_perl content handler.
/javascript/*
Example (but functional!) javascript. Create line numbers,
various eye candies, and clickable folds.
This requires the 'jquery.js' library, also included.
/sample.otl
An example vimoutliner file, with optional settings.
/styles/*
"Theme" examples for customizing OTL display.
ACKNOWLEDGEMENTS
---------------------------------------------------------------------
Thanks to Nathan Dabney <nathan.dabney@gmail.com> and
Michael Granger <ged@faeriemud.org> for their help and advice!

View file

@ -0,0 +1,5 @@
PerlModule Apache2::Reload
PerlInitHandler Apache2::Reload
PerlSetVar ReloadAll Off
PerlSetVar ReloadModules "Apache::OTL"
PerlSetVar ReloadConstantRedefineWarnings Off

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1,38 @@
$(document).ready(function(){
// slide everything open on new page
$(".outline:hidden").slideToggle("slow", function(){
$(".percent:hidden").fadeIn("slow");
});
// re-activate links (the event is stomped on by the li event)
$(".outline a").click(function(){ window.location.href = this });
// highlight clicked items
$("li").not("[ul]").click(function(){ $(this).toggleClass("selected") });
// add line numbers
var line_counter = 0;
$("li").each(function(){
var str = '<span class="linenum">' + ++line_counter + ':</span>';
$(this).prepend(str);
});
// attach folds
$(".outline ul li").toggle(
// hide
function(){
if ( $(this).children("ul").size() == 0 ) return;
$(this).children("ul").slideUp("slow");
$(this).find(".linenum").addClass("linenum-folded");
},
// show
function(){
$(this).children("ul").slideDown("slow");
$(this).find(".linenum").removeClass("linenum-folded");
}
);
});

View file

@ -0,0 +1,22 @@
$(document).ready(function(){
// append content div
$("body").prepend("<div id=\"content\">test</div>");
$("#content").hide();
// FIXME - document.width + document.height
$(".outline").click(function(){
$("#content").html( $(this).html() );
$("body").background("#7b7c8c");
$("#content").show();
});
$("#content").click(function(){
$(this).hide();
$("body").background("#acadc3");
});
// re-activate links (the event is stomped on by the li event)
$(".outline a").click(function(){ window.location.href = this; return false; });
});

View file

@ -0,0 +1,39 @@
<:title=Sample OTL list :counts=1 :timer=1 :style=styles/theme1.css :legend=1 :last_mod=1
: Theme examples:
: <a href="sample.otl">basic</a> <a href="sample.otl?counts=0&amp;style=styles/theme2.css&amp;legend=0&amp;js=/javascript/jquery.js,/javascript/theme2.js">advanced</a> <a href="sample.otl?counts=0&amp;style=styles/theme3.css&amp;legend=0&amp;timer=0&amp;js=/javascript/jquery.js,/javascript/theme3.js&amp;sort=1">advanced2</a>
[_] 29% Things to get for party
[_] 25% Food
[_] Chips
[_] Dips
[X] Honey roasted peanuts
[_] Sausage
[_] 33% Party favors
[_] Hats
[_] Whistles
[X] Beer bong
[_] 19% House projects
[_] 25% Paint
[_] 50% Buy supplies
[_] Paint
[X] Brushes
[X] Trays 2006-09-14
[_] Overalls
[_] 0% Rooms done
[_] Bathroom
[_] Bedroom
: Red?
[_] 13% Upgrade electrical
[_] 2 circuits to computer room
[_] 40% Get equipment
[X] Romex wire
[_] Entry feed wire
: How much of this do I really need? I should probably go out to the street and measure stuff.
: Make sure the inspector has access to examine stuff on side of house.
[_] Service meter
[X] Grounding rods
[_] Breakers
[_] Learn about electricity
[_] Don't die

View file

@ -0,0 +1,88 @@
body
{
width: 600px;
font-size: 0.9em;
font-family: sans;
}
ul
{
list-style-type: none;
line-height: 1.5em;
padding-left: 20px;
}
.date
{
font-size: 0.6em;
}
.outline
{
margin-bottom: 30px;
}
.percent
{
color: blue;
}
.comment, .counts
{
font-size: 0.7em;
line-height: 1em;
padding-top: 2px;
margin-bottom: 5px;
font-family: sans;
}
.comment_pre
{
font-style: normal;
font-family: courier;
white-space: pre;
}
.counts
{
margin-left: 10px;
}
.counts:before { content: "("; }
.counts:after { content: ")"; }
.todo
{
padding-left: 4px;
}
.done
{
background-color: #f4f4f4;
color: #777;
padding-left: 4px;
}
.done:before
{
font-size: 1.5em;
color: green;
content: "\2611 ";
}
.todo:before
{
font-size: 1.5em;
color: #777;
content: "\2610 ";
}
.legend .todo, .legend .done { border: 0 }
.legend
{
margin-bottom: 30px;
margin-top: 20px;
}

View file

@ -0,0 +1,152 @@
body
{
background-color: #444;
font: 11px/1.8em sans-serif;
margin: 0;
padding-bottom: 50px;
color: #ccc;
}
a, a:visited
{
text-decoration: none;
color: #7f9ab5;
}
.header
{
background-color: #aaa;
padding: 3px 0 3px 120px;
margin-top: 50px;
color: #3a5f85;
border-top: 5px solid #333;
font-size: 20px;
font-weight: bold;
}
.last_mod
{
padding-left: 120px;
background-color: #333;
border-bottom: 1px solid #000;
font-size: 0.85em;
color: #999;
}
.percent
{
display: none;
color: #7f9ab5;
font-weight: bold;
position: absolute;
left: 115px;
}
.linenum
{
color: #7f9ab5;
position: absolute;
left: 0;
font-size: 9px;
font-weight: normal;
font-style: normal;
width: 60px;
text-align: right;
}
.linenum-folded
{
color: #666;
}
.timer
{
color: #666;
position: absolute;
top: 5px;
right: 10px;
}
.outline
{
width: 400px;
margin: 20px 0 0 150px;
display: none;
cursor: pointer;
}
.outline ul
{
margin: 0;
padding: 0 10px 0 5px;
list-style-type: none;
}
.outline ul li
{
padding: 0 0 0 10px;
display: block;
color: #fff;
font-size: 14px;
font-weight: bold;
}
.outline ul li:hover
{
color: #fff;
}
.outline ul li ul li
{
border-left: 1px solid #666;
color: #ccc;
font-size: 11px;
font-weight: normal;
}
.outline ul li ul li:hover
{
border-left: 1px solid #aaa;
}
.done
{
color: #777 !important;
font-style: italic;
}
.done:after
{
font-size: 0.9em;
content: " (done)";
}
.comment
{
padding-right: 2px !important;
font-style: italic;
border-top: 1px solid #666;
border-bottom: 1px solid #666;
border-right: 1px solid #666;
}
.comment_pre
{
font-style: normal;
font-family: courier;
white-space: pre;
}
.comment:hover
{
border-top: 1px solid #aaa;
border-bottom: 1px solid #aaa;
border-right: 1px solid #aaa;
}
.selected
{
background-color: #3d3d3d;
}

View file

@ -0,0 +1,109 @@
body
{
background-color: #acadc3;
font: 12px Verdana, sans-serif;
color: #000;
text-align: center;
}
a, a:visited
{
text-decoration: none;
color: blue;
}
.header
{
margin-top: 40px;
font-size: 2em;
font-weight: bold;
}
.header:after { content: " ----|" }
.header:before { content: "|---- " }
.last_mod { font-size: .85em; }
.percent { font-weight: bold; }
.sort
{
margin-bottom: 30px;
font-size: .85em;
}
.outline
{
cursor: pointer;
float: left;
padding: 20px;
border: 1px solid #8082a9;
margin-left: 10px;
background-color: #ccc;
}
.outline:hover
{
border: 1px solid #000;
}
.outline ul, #content ul
{
padding: 0;
margin: 0;
list-style-type: none;
}
.outline ul li ul li { display: none; }
#content ul li ul { padding-left: 20px; }
#content ul li
{
font-weight: bold;
font-size: 1.5em;
text-align: center;
color: #3a3d85;
}
#content ul li ul li
{
font-weight: normal;
text-align: left;
font-size: 10px;
color: #000;
}
#content .done { color: #777; }
.comment { font-style: italic; }
#content ul li .percent { color: #ff7e00; }
#content ul li ul li .percent { color: #000; }
#content .comment
{
font-style: italic;
border-left: 1px solid #999;
padding-left: 5px;
margin-bottom: 3px;
}
#content .comment_pre
{
font-style: normal;
font-family: courier;
white-space: pre;
}
#content
{
position: absolute;
left: 15%;
width: 60%;
height: 80%;
cursor: pointer;
background-color: #fff;
border: 2px solid #3a3d85;
padding: 10px;
}

View file

@ -0,0 +1,212 @@
#!/usr/bin/python2
# otlgrep.py
# grep an outline for a regex and return the branch with all the leaves.
#
# Copyright 2005 Noel Henson All rights reserved
###########################################################################
# Basic function
#
# This program searches an outline file for a branch that contains
# a line matching the regex argument. The parent headings (branches)
# and the children (sub-branches and leaves) of the matching headings
# are returned.
#
# Examples
#
# Using this outline:
#
# Pets
# Indoor
# Cats
# Sophia
# Hillary
# Rats
# Finley
# Oliver
# Dogs
# Kirby
# Outdoor
# Dogs
# Kirby
# Hoover
# Goats
# Primrose
# Joey
#
# a grep for Sophia returns:
#
# Indoor
# Cats
# Sophia
#
# a grep for Dogs returns:
#
# Indoor
# Dogs
# Kirby
# Hoover
# Outdoor
# Dogs
# Kirby
# Hoover
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
###########################################################################
# include whatever mdules we need
import sys
import re
###########################################################################
# global variables
debug = 0
ignorecase = 0
pattern = ""
inputfiles = []
###########################################################################
# function definitions# usage
#
# print debug statements
# input: string
# output: string printed to standard out
def dprint(*vals):
global debug
if debug != 0:
print vals
# usage
# print the simplest form of help
# input: none
# output: simple command usage is printed on the console
def showUsage():
print """
Usage:
otlgrep.py [options] pattern [file...]
Options
-i Ignore case
--help Show help.
[file...] is zero or more files to search. Wildcards are supported.
if no file is specified, input is expected on stdin.
output is on STDOUT
"""
# getArgs
# Check for input arguments and set the necessary switches
# input: none
# output: possible console output for help, switch variables may be set
def getArgs():
global debug, pattern, inputfiles, ignorecase
if (len(sys.argv) == 1):
showUsage()
sys.exit()()
else:
for i in range(len(sys.argv)):
if (i != 0):
if (sys.argv[i] == "-d"):
debug = 1 # test for debug flag
elif (sys.argv[i] == "-i"):
ignorecase = 1 # test for debug flag
elif (sys.argv[i] == "-?"): # test for help flag
showUsage() # show the help
sys.exit() # exit
elif (sys.argv[i] == "--help"):
showUsage()
sys.exit()
elif (sys.argv[i][0] == "-"):
print "Error! Unknown option. Aborting"
sys.exit()
else: # get the input file name
if (pattern == ""):
pattern = sys.argv[i]
else:
inputfiles.append(sys.argv[i])
# getLineLevel
# get the level of the current line (count the number of tabs)
# input: linein - a single line that may or may not have tabs at the beginning
# output: returns a number 1 is the lowest
def getLineLevel(linein):
strstart = linein.lstrip() # find the start of text in line
x = linein.find(strstart) # find the text index in the line
n = linein.count("\t", 0, x) # count the tabs
return(n) # return the count + 1 (for level)
# processFile
# split an outline file
# input: file - the filehandle of the file we are splitting
# output: output files
def processFile(file):
global debug, pattern, ignorecase
parents = []
parentprinted = []
for i in range(10):
parents.append("")
parentprinted.append(0)
matchlevel = 0
line = file.readline() # read the outline title
# and discard it
line = file.readline() # read the first parent heading
while (line != ""):
level = getLineLevel(line)
parents[level] = line
parentprinted[level] = 0
if (ignorecase == 1):
linesearch = re.search(pattern, line.strip(), re.I)
else:
linesearch = re.search(pattern, line.strip())
if (linesearch is not None):
matchlevel = level
for i in range(level): # print my ancestors
if (parentprinted[i] == 0):
print parents[i][:-1]
parentprinted[i] = 1
print parents[level][:-1] # print myself
line = file.readline()
while (line != "") and (getLineLevel(line) > matchlevel):
print line[:-1]
line = file.readline()
else:
line = file.readline()
# main
# split an outline
# input: args and input file
# output: output files
def main():
global inputfiles, debug
getArgs()
if (len(inputfiles) == 0):
processFile(sys.stdin)
else:
for i in range(len(inputfiles)):
file = open(inputfiles[i], "r")
processFile(file)
file.close()
main()

View file

@ -0,0 +1,12 @@
#!/bin/bash
if [ "$#" -lt 1 ] ; then
echo " Usage: otlhead level < file"
echo " Keep the number of levels specified, remove the rest."
echo " Great for generating summaries."
echo " level - the number of levels to include"
echo " file - an otl file"
echo " input - standard in"
echo " output - standard out"
exit 0
fi
sed "/^\(\t\)\{$1\}.*$/ { D }"

View file

@ -0,0 +1,191 @@
#!/usr/bin/python2
# otlslit.py
# split an outline into several files.
#
# Copyright 2005 Noel Henson All rights reserved
###########################################################################
# Basic function
#
# This program accepts text outline files and splits them into
# several smaller files. The output file names are produced from the
# heading names of the parents.
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, see <http://www.gnu.org/licenses/>.
###########################################################################
# include whatever mdules we need
import sys
import re
###########################################################################
# global variables
debug = 0
subdir = ""
level = 1
title = 0
inputfile = ""
###########################################################################
# function definitions# usage
#
# print debug statements
# input: string
# output: string printed to standard out
def dprint(*vals):
global debug
if debug != 0:
print vals
# usage
# print the simplest form of help
# input: none
# output: simple command usage is printed on the console
def showUsage():
print """
Usage:
otlsplit.py [options] inputfile
Options
-l level The number of levels to split down to. The default is 1
-D dir Specifiy a target directory for the output files
-t Include a title line (the parerent heading) in split files
-h Show help.
output is on STDOUT
"""
# getArgs
# Check for input arguments and set the necessary switches
# input: none
# output: possible console output for help, switch variables may be set
def getArgs():
global debug, level, inputfile, title, subdir
if (len(sys.argv) == 1):
showUsage()
sys.exit()()
else:
for i in range(len(sys.argv)):
if (i != 0):
if (sys.argv[i] == "-d"):
debug = 1 # test for debug flag
elif (sys.argv[i] == "-?"): # test for help flag
showUsage() # show the help
sys.exit() # exit
elif (sys.argv[i] == "-l"): # test for the level flag
level = int(sys.argv[i + 1]) # get the level
i = i + 1 # increment the pointer
elif (sys.argv[i] == "-D"): # test for the subdir flag
subdir = sys.argv[i + 1] # get the subdir
i = i + 1 # increment the pointer
elif (sys.argv[i] == "-t"):
title = 1 # test for title flag
elif (sys.argv[i] == "--help"):
showUsage()
sys.exit()
elif (sys.argv[i] == "-h"):
showUsage()
sys.exit()
elif (sys.argv[i][0] == "-"):
print "Error! Unknown option. Aborting"
sys.exit()
else: # get the input file name
inputfile = sys.argv[i]
# getLineLevel
# get the level of the current line (count the number of tabs)
# input: linein - a single line that may or may not have tabs at the beginning
# output: returns a number 1 is the lowest
def getLineLevel(linein):
strstart = linein.lstrip() # find the start of text in line
x = linein.find(strstart) # find the text index in the line
n = linein.count("\t", 0, x) # count the tabs
return(n + 1) # return the count + 1 (for level)
# convertSensitiveChars
# get the level of the current line (count the number of tabs)
# input: line - a single line that may or may not have tabs at the beginning
# output: returns a string
def convertSensitiveChars(line):
line = re.sub('\W', '_', line.strip())
return(line)
# makeFileName
# make a file name from the string array provided
# input: line - a single line that may or may not have tabs at the beginning
# output: returns a string
def makeFileName(nameParts):
global debug, level, subdir
filename = ""
for i in range(level):
filename = filename + convertSensitiveChars(nameParts[i]).strip() + "-"
filename = filename[:-1] + ".otl"
if subdir != "":
filename = subdir + "/" + filename
return(filename.lower())
# processFile
# split an outline file
# input: file - the filehandle of the file we are splitting
# output: output files
def processFile(ifile):
global debug, level, title
nameparts = []
for i in range(10):
nameparts.append("")
outOpen = 0
line = ifile.readline() # read the outline title
# and discard it
line = ifile.readline() # read the first parent heading
dprint(level)
while (line != ""):
linelevel = getLineLevel(line)
if (linelevel < level):
if outOpen == 1:
ifile.close()
outOpen = 0
nameparts[linelevel] = line
dprint(level, linelevel, line)
else:
if outOpen == 0:
ofile = open(makeFileName(nameparts), "w")
outOpen = 1
if title == 1:
dprint("title:", title)
ofile.write(nameparts[level - 1])
ofile.write(line[level:])
line = file.readline()
# main
# split an outline
# input: args and input file
# output: output files
def main():
global inputfile, debug
getArgs()
file = open(inputfile, "r")
processFile(file)
file.close()
main()

View file

@ -0,0 +1,15 @@
#!/bin/bash
if [ "$#" -lt 1 ] ; then
echo " Usage: otltail level < file"
echo " Remove the specified number of parent headings."
echo " This is a way to promote children. It is"
echo " useful for converting a single outline into a"
echo " number of pages for a web site or chapters for"
echo " a book."
echo " level - the number of levels to include"
echo " file - an otl file"
echo " input - standard in"
echo " output - standard out"
exit 0
fi
sed "/^\(\t\)\{$1\}.*$/! { D }" | sed "s/^\(\t\)\{$1\}//"

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,31 @@
" add the calendar tag file to the tag search stack
set tags^=~/Outlines/outline_calendar/vo_calendar_tags.tag
" open the calendar at date
" a:1 is executed before the jump
function ToDate(date, ...)
let l:precmd = a:0>0 ? a:1 : ""
exec l:precmd
exec "tag " . a:date . "|normal zO"
endfunction
" open the calendar at a requested date
" default is today
" a:1 is executed before the jump
function ToSomeDay(...)
let l:precmd = a:0>0 ? a:1 : ""
let l:today = strftime("%Y-%m-%d")
let l:date = input('Date To Goto (yyyy-mm-dd): ', today)
call ToDate(l:date, l:precmd)
endfunction
" small case jumps use same window
nmap <leader>td :call ToDate(strftime("%Y-%m-%d"))<cr>
nmap <leader>ts :call ToSomeDay()<cr>
nmap <leader>tc :call ToDate(expand("<cWORD>"))<cr>
" upper case jumps open new window
nmap <leader>tD :call ToDate(strftime("%Y-%m-%d"),"new")<cr>
nmap <leader>tS :call ToSomeDay("new")<cr>
nmap <leader>tC :call ToDate(expand("<cWORD>"),"new")<cr>

View file

@ -0,0 +1,9 @@
--langdef=outline
--langmap=outline:.otl
--regex-outline=/_tag_ set ([^ ]+)/_tag_\1/
--regex-outline=/^\t+([1-9][0-9]{3})_([01][0-9])_([0-3][0-9])/\1-\2-\3/
--languages=-all,+outline
--recurse
--totals=yes

View file

@ -0,0 +1,110 @@
#!/usr/bin/ruby
# usage:
# calendar_generator.rb <calendar_folder> <years>
# indent for months, days are indented one more level
# both 0 and 1 make sense
MONTHINDENT = 0
# insert _tag_ todo and _tag_ done items under each day
# DIARY = ["Zu erledigen", "Tagebuch", "Abrechnen"]
# DIARY = [] # for empty days.
DIARY = ["To do", "Diary", "Timesheet"] # deluxe edition
# adapt to your preference
# Sunday should be first, Saturday last entry
# DAYNAMES = %w(So Mo Di Mi Do Fr Sa)
DAYNAMES = %w(Sun Mon Tue Wed Thu Fri Sat)
# January should be first, December last entry
# MONTHNAMES = %w(Januar Februar März April Mai Juni Juli August September Oktober November Dezember)
MONTHNAMES = %w(January February March April May June July August September October November December)
# you should not need to change anything below here
# but you are welcome to write equivalent functionality
# in your language of choice.
# ------------------------------------------------------------
require "date"
TAGFORMAT = "%.4d_%.2d_%.2d"
def indent(sublevel)
"\t"*(MONTHINDENT+sublevel)
end
def month(date)
MONTHNAMES[date.month - 1] +
(MONTHINDENT == 0 ? " " + date.year.to_s : "")
end
class Shelf
def initialize(path)
begin
@shelf = File.readlines(path)
rescue
@shelf = Array.new
end
@path = path
end
def parse
unless @books
@books = Hash.new
(0...@shelf.size).step(2) do |i|
@books[@shelf[i].strip] = @shelf[i+1].strip
end
end
end
def save()
File.open(@path, "w") do |out|
@books.sort.each do |key,value|
out.puts(key)
out.puts("\t" + value)
end if @books
end
end
def update(year)
re = /^_tag_calendar_#{year}/
unless @shelf.any? { |str| re.match(str) }
parse
@books["_tag_calendar_#{year}"] = "#{year}.otl"
save
end
end
end
def update_shelf(year)
shelf = Shelf.new(CALENDAR + "/vo_calendar_shelf.otl")
shelf.update(year)
end
CALENDAR = ARGV.shift
ARGV.each do |arg|
File.open(CALENDAR + "/" + arg + ".otl", "w") do |out|
update_shelf(arg)
year = arg.to_i
d = Date.new(year, 1, 1)
out.puts year if MONTHINDENT == 1
while d.year == year
out.puts indent(0) + month(d)
month = d.month
while d.month == month
out.puts indent(1) + (TAGFORMAT % [d.year, d.month, d.day]) +
" " + DAYNAMES[d.wday]
DIARY.each do |item|
out.puts indent(2) + item
end
d += 1
end
end
end
end

View file

@ -0,0 +1,195 @@
Outline Calendar 1.3
What's new
in 1.3
vimrc-script by Lee Bigelow for easy calendar access
small modifications to this outline
update from 1.2.2
edit tagfile path in outline_calendar/vimrc
add 'source .../outline_calendar/vimrc' to your ~/.vimrc
in 1.2.2
changed personal _tag_ set definition
: it may appear anywhere, but takes only next space-delimited
: word into account. The advantage is that I can set a tag
: behind a calendar date and jump to the current week via
: _tag_kalender
in 1.2.1
removed spurious underscores in td function
in 1.2
use ~/.vimoutlinerrc to avoid tagfile collisions
,,d-dates usable as links into calendar
calendar dates (almost) without syntactic noise
update to documentation
in 1.1
we are almost votl_maketags.pl-compatible again
you can use vo_calendar_shelf.otl to attach your calendar shelf to your outline tree
files follow vimoutliner naming convention
all installation files are kept within one directory
minor refinements to console user interface
defaults and samples are in English now
Prerequisites
ruby
used for generating the skeleton calendar and the shelf
could be replaced by perl, awk, python, ...
maybe even by a shell script or a small forth program ;-)
bash
use of special variable expansion in shell-frontend
ctags
create the tags-file for interoutline links and calendar access
regex support necessary
$ ctags --version
: Exuberant Ctags 5.5.1, Copyright (C) 1996-2003 Darren Hiebert
: Compiled: Aug 15 2003, 21:06:30
: Addresses: <dhiebert@users.sourceforge.net>, http://ctags.sourceforge.net
: Optional compiled features: +wildcards, +regex
vimoutliner
you should already have it
Known incompatibilities
none
Installation and Configuration
Installation
I recommend keeping all Outlines in one directory
it's easier on ctags
move the distribution folder there
: $ mv outline_calendar ~/Outlines
add another tag file to vim's lookup table
the following line should go into $HOME/.vimrc
: source ~/Outlines/outline_calendar/vimrc
configure the various scripts (see below)
source vo_calendar_ui.sh into your shell
$HOME/.bashrc is probably a good place for this
something like the following should do it
$ . $HOME/Outlines/outline_calendar/vo_calendar_ui.sh
you're ready to run
try
$ calgen 2005 2006 2007
: you get three yearly calendars plus a shelf to
: access them through _tag_xxx linking
$ tagvout
: this should create a tag file inside the outline
: calendar directory
$ td
: now vimoutliner should be positioned on today
$ vim
in normal mode type \tS
enter a date after the prompt and hit return
the calendar should open in a new window
Configure the calendar layout in vo_calendar_generator.rb
Month layout
with MONTHINDENT=1
2006
January
_2006-01-01_ Sun
_2006-01-02_ Mon
or with MONTHINDENT=0
January 2006
_2006-01-01_ Sun
_2006-01-02_ Mon
Optional diary headings for each day
with DIARY=[]
_2006-01-01_ Sun
_2006-01-02_ Mon
with DIARY=["Todo", "Billing", "Personal notes"]
_2006-01-01_ Sun
Todo
Billing
Personal notes
_2006-01-02_ Mon
Todo
Billing
Personal notes
change the language of the calendar
DAYNAMES
names of weekdays starting with Sunday
MONTHNAMES
names of months
calendar skeletons
need not be generated with the ruby script
but we do have an "API" to keep things compatible
: I suggest an approach that can be executed in
: the same way as my ruby script. If you did it
: with perl, you could call the script
: vo_calendar_generator.pl.
: Its first command line parameter should be the
: directory, where the calendar files are stored,
: the remaining parameters denote the years, for which
: calendar skeletons should be generated.
calendar shelf
do not edit manually, a really stupid parser (tm) is reading this
: it contains the _tag_ format links into the various
: yearly calendars. It is "parsed" (if you can call it
: that) to insert a newly created calendar in the correct
: position.
Configure the directory layout in vo_calendar_ui.sh
See the comments there for further explanations
Ideally you only have to set the variable OUTLINES, but YMMV
Configure tag harvesting in vo_calendar_ctags.conf
it should not need any changes
if it does, "man 1 ctags" is your friend
what it does, line by line
define a new language called outline
outline handling rules are applied only to .otl files
recognise headings starting with "_tag_ set "
everything after "_tag_ set " is considered as tag content
: this provides an alternative way of defining tags
: for both interoutline and intraoutline jumps.
: It can safely be removed without harming the
: outline calendar.
: If you keep it, your non-calendar tags will be
: stored in the calendar tagfile, too, but this should
: not be a problem.
recognise headings starting with a yyyy_mm_dd date
Underscore as date separator
are distinct from ,,d dates
date stamps will not be jumped to
date stamps will only be jumped from
they are mangled to hyphens as tag marker
plain ,,d dates can be used to enter the calendar from anywhere
all dates typed by you are in ISO-format yyyy-mm-dd
only tag outline files
handle files in subdirectories, too
show some impressive statistics
Usage
administrative
once: install and configure (see above)
rarely: generate a new calendar
$ calgen 2005 2006 2007
this will go a long way
often: refresh the tag file
$ tagvout
you might even want to put it in your crontab
don't rely on shell functions there, use plain commands
modulo local changes the crontab entry would look like this:
: */10 * * * * /usr/bin/ctags -f $HOME/.vimoutliner/vo_tags.tag --options=$HOME/Outlines/outline_calendar/vo_calendar_ctags.conf $HOME/Outlines &> /dev/null
refresh tag file every 10 minutes
remove the configuration line --totals, you won't need it there
open calendar from running shell
edit today's calendar entries
$ td
: vimoutliner should open on today
: if you're running X, you'll get a gvim, otherwise a vim
: you'll be on today's entry
move around, add some items to the outline
move cursor on a ISO-format date yyyy-mm-dd and <C-k> it
edit any available day
$ td 2005-05-04
: release day
open calendar from running vim session
mappings for normal mode
\td and \tD
jump to today
\tc and \tC
jump to the date at the cursor position
\ts and \tS
ask for date to jump to
Notes
the mappings are defined as <leader>t...
the uppercase variant opens a new window within the current session
Author(s)
(c) Stefan Schmiedl <s@xss.de> 2005-05-04
Contributors:
Lee Bigelow brought the \t? functions into existence
License
thou shalt neither sue nor blame me
it's worth what you paid for it
thou can do whatever else thou want to do with it
just remember to have fun on the way

View file

@ -0,0 +1,6 @@
_tag_calendar_2003
2003.otl
_tag_calendar_2004
2004.otl
_tag_calendar_2005
2005.otl

View file

@ -0,0 +1,39 @@
# parent folder of all outlines
OUTLINES=$HOME/Outlines
# folder for calendar files
# should be a subfolder of $OUTLINES
CALENDAR=$OUTLINES/outline_calendar
# file for calendar tags
CALENDARTAGS=$CALENDAR/vo_calendar_tags.tag
# folders to tag for interoutline links and calendar access
# if $CALENDAR is not below $OUTLINES, you need
# TAGFOLDERS=($OUTLINES $CALENDAR)
TAGFOLDERS=($OUTLINES)
# script to generate calendar skeletons
CALGENSCRIPT=$CALENDAR/vo_calendar_generator.rb
# option file for ctags
CTAGSOPTIONS=$CALENDAR/vo_calendar_ctags.conf
# you should not need to change anything below here
# that's what all the variables above are for
# ------------------------------------------------------------
function td() {
local date
date=${1:-`date +%Y-%m-%d`}
${DISPLAY:+g}vim -c ":ta $date" $CALENDAR/${date%%-*}.otl
}
function tagvout() {
ctags -f $CALENDARTAGS --options=$CTAGSOPTIONS ${TAGFOLDERS[*]}
}
function calgen() {
$CALGENSCRIPT $CALENDAR $*
}

View file

@ -0,0 +1,198 @@
#!/usr/bin/python2
'''
usage:
freemind.py -o [fmt] <files>, where ofmt selects output format: {otl,mm}
freemind.py -o otl <files>:
Read in an freemind XML .mm file and generate a outline file
compatable with vim-outliner.
freemind.py -o mm <files>:
Read in an otl file and generate an XML mind map viewable in freemind
NOTE:
Make sure that you check that round trip on your file works.
Author: Julian Ryde
'''
import sys
import getopt
import codecs
import otl
import xml.etree.ElementTree as et
from xml.etree.ElementTree import XMLParser
debug = False
class Outline: # The target object of the parser
depth = -1
indent = '\t'
current_tag = None
def start(self, tag, attrib): # Called for each opening tag.
self.depth += 1
self.current_tag = tag
# print the indented heading
if tag == 'node' and self.depth > 1:
#if 'tab' in attrib['TEXT']:
#import pdb; pdb.set_trace()
print (self.depth - 2) * self.indent + attrib['TEXT']
def end(self, tag): # Called for each closing tag.
self.depth -= 1
self.current_tag = None
def data(self, data):
if self.current_tag == 'p':
bodyline = data.rstrip('\r\n')
bodyindent = (self.depth - 5) * self.indent + ": "
#textlines = textwrap.wrap(bodytext, width=77-len(bodyindent),
# break_on_hyphens=False)
#for line in textlines:
print bodyindent + bodyline
def close(self): # Called when all data has been parsed.
pass
def mm2otl(*arg, **kwarg):
fname = arg[0][0]
file = codecs.open(fname, 'r', encoding='utf-8')
filelines = file.readlines()
outline = Outline()
parser = XMLParser(target=outline, encoding='utf-8')
parser.feed(filelines[0].encode('utf-8'))
parser.close()
# TODO body text with manual breaks
# TODO commandline arguments for depth, maxlength etc.
# TODO do not read whole file into memory?
# TODO handle decreasing indent by more than one tab
# TODO handle body text lines sometimes not ending with space
depth = 99
def attach_note(node, textlines):
et.ElementTree
# Format should look like
#<richcontent TYPE="NOTE">
#<html>
# <head> </head>
# <body>
# %s
# </body>
#</html>
#</richcontent>
notenode = et.SubElement(node, 'richcontent')
notenode.set('TYPE', 'NOTE')
htmlnode = et.SubElement(notenode, 'html')
bodynode = et.SubElement(htmlnode, 'body')
for line in textlines:
pnode = et.SubElement(bodynode, 'p')
pnode.text = line
def otl2mm(*arg, **kwarg):
fname = arg[0][0]
# node ID should be based on the line number of line in the
# otl file for easier debugging
#for lineno, line in enumerate(open(fname)):
# enumerate starts at 0 I want to start at 1
# FIXME freemind.py|107| W806 local variable 'lineno' is assigned to but never used
lineno = 0
mapnode = et.Element('map')
mapnode.set('version', '0.9.0')
topnode = et.SubElement(mapnode, 'node')
topnode.set('TEXT', fname)
parents = [mapnode, topnode]
#left_side = True # POSITION="right"
# read otl file into memory
filelines = codecs.open(fname, 'r', encoding='utf-8')
# first handle the body texts turn it into a list of headings
# with associated body text for each one this is because the
# body text especially multi-line is what makes it awkward.
headings = []
bodytexts = []
for line in filelines:
if otl.is_heading(line):
headings.append(line)
bodytexts.append([])
else:
# TODO this ': ' removal should go in otl.py?
bodytexts[-1].append(line.lstrip()[2:] + '\n')
#import pdb; pdb.set_trace()
oldheading = ''
for heading, bodytext in zip(headings, bodytexts):
if debug:
print heading, bodytext
level = otl.level(heading)
oldlevel = otl.level(oldheading)
if level == oldlevel:
pass
elif level > oldlevel:
# about to go down in the hierarchy so add this line
# as a parent to the stack
# FIXME freemind.py|149| W802 undefined name 'node'
parents.append(node)
elif level < oldlevel:
# about to go up in the hierarchy so remove parents from the stack
leveldiff = oldlevel - level
parents = parents[:-leveldiff]
node = et.SubElement(parents[-1], 'node')
node.set('TEXT', heading.lstrip().rstrip('\r\n'))
#if len(bodytext) > 0:
attach_note(node, bodytext)
oldheading = heading
xmltree = et.ElementTree(mapnode)
xmltree.write(sys.stdout, 'utf-8')
print
def usage():
print "usage: %s -[mo] <files>" % (sys.argv[0])
def main():
args = sys.argv
try:
opts, args = getopt.getopt(sys.argv[1:], 'moh', [""])
except getopt.GetoptError, err:
usage()
print str(err)
sys.exit(2)
for o, a in opts:
if o == "-m":
otl2mm(args)
elif o == "-o":
mm2otl(args)
elif o == "-h":
usage()
sys.exit(0)
else:
usage()
assert False, "unhandled option: %s" % o
return args
if __name__ == "__main__":
main()
# vim: set noet :

View file

@ -0,0 +1,48 @@
#!/usr/bin/python2
'''Converts a freemind xml .mm file to an outline file compatable with vim
outliner.
Make sure that you check that round trip on your file works.
Author: Julian Ryde
'''
import sys
from xml.etree.ElementTree import XMLParser
import textwrap
import codecs
class Outline: # The target object of the parser
depth = -1
indent = '\t'
current_tag = None
def start(self, tag, attrib): # Called for each opening tag.
self.depth += 1
self.current_tag = tag
# print the indented heading
if tag == 'node' and self.depth > 1:
#if 'tab' in attrib['TEXT']:
#import pdb; pdb.set_trace()
print (self.depth-2)*self.indent + attrib['TEXT']
def end(self, tag): # Called for each closing tag.
self.depth -= 1
self.current_tag = None
def data(self, data):
if self.current_tag == 'p':
bodyline = data.rstrip('\r\n')
bodyindent = (self.depth-5)*self.indent + ": "
#textlines = textwrap.wrap(bodytext, width=77-len(bodyindent), break_on_hyphens=False)
#for line in textlines:
print bodyindent + bodyline
def close(self): # Called when all data has been parsed.
pass
outline = Outline()
parser = XMLParser(target=outline, encoding='utf-8')
fname = sys.argv[1]
file = codecs.open(fname, 'r', encoding='utf-8')
filelines = file.readlines();
print "filelines", type(filelines[0]), filelines[0]
parser.feed(filelines[0].encode('utf-8'))
parser.close()

View file

@ -0,0 +1,26 @@
# Some integer IDs
# headings are 1, 2, 3, ....
bodynowrap = -1 # ;
bodywrap = 0 # :
def level(line):
'''return the heading level 1 for top level and down and 0 for body text'''
if line.lstrip().find(':')==0: return bodywrap
if line.lstrip().find(';')==0: return bodynowrap
strstart = line.lstrip() # find the start of text in lin
x = line.find(strstart) # find the text index in the line
n = line.count("\t",0,x) # count the tabs
return(n+1) # return the count + 1 (for level)
def is_bodywrap(line):
return level(line) == bodywrap
def is_bodynowrap(line):
return level(line) == bodynowrap
def is_heading(line):
return level(line) > 0
def is_body(line):
return not is_heading(line)

View file

@ -0,0 +1,114 @@
#!/usr/bin/python2
'''Read in an otl file and generate an xml mind map viewable in freemind
Make sure that you check that round trip on your file works.
Author: Julian Ryde
'''
import sys
import os
import xml.etree.ElementTree as et
import otl
import codecs
fname = sys.argv[1]
max_length = 40
depth = 99
debug = False
# TODO body text with manual breaks
# TODO commandline arguments for depth, maxlength etc.
# TODO do not read whole file into memory?
# TODO handle decreasing indent by more than one tab
# TODO handle body text lines sometimes not ending with space
otlfile = open(fname)
indent = ' '
def attach_note(node, textlines):
et.ElementTree
# Format should look like
#<richcontent TYPE="NOTE">
#<html>
# <head> </head>
# <body>
# %s
# </body>
#</html>
#</richcontent>
notenode = et.SubElement(node, 'richcontent')
notenode.set('TYPE', 'NOTE')
htmlnode = et.SubElement(notenode, 'html')
headnode = et.SubElement(htmlnode, 'head')
bodynode = et.SubElement(htmlnode, 'body')
for line in textlines:
pnode = et.SubElement(bodynode, 'p')
pnode.text = line
# node ID should be based on the line number of line in the otl file for easier
# debugging
#for lineno, line in enumerate(open(fname)):
# enumerate starts at 0 I want to start at 1
lineno = 0
mapnode = et.Element('map')
mapnode.set('version', '0.9.0')
topnode = et.SubElement(mapnode, 'node')
topnode.set('TEXT', fname)
parents = [mapnode, topnode]
#left_side = True # POSITION="right"
# read otl file into memory
filelines = codecs.open(fname, 'r', encoding='utf-8')
# remove those that are too deep or body text and pesky end of line characters
#filelines = [line.rstrip('\r\n') for line in filelines if otl.level(line) <= depth]
#filelines = [line for line in filelines if otl.is_heading(line)]
# first handle the body texts turn it into a list of headings with associated
# body text for each one this is because the body text especially multi-line is
# what makes it awkward.
headings = []
bodytexts = []
for line in filelines:
if otl.is_heading(line):
headings.append(line)
bodytexts.append([])
else:
# TODO this ': ' removal should go in otl.py?
bodytexts[-1].append(line.lstrip()[2:] + '\n')
#import pdb; pdb.set_trace()
oldheading = ''
for heading, bodytext in zip(headings, bodytexts):
if debug: print heading, bodytext
level = otl.level(heading)
oldlevel = otl.level(oldheading)
if level == oldlevel:
pass
elif level > oldlevel:
# about to go down in the hierarchy so add this line as a parent to the
# stack
parents.append(node)
elif level < oldlevel:
# about to go up in the hierarchy so remove parents from the stack
leveldiff = oldlevel - level
parents = parents[:-leveldiff]
node = et.SubElement(parents[-1], 'node')
node.set('TEXT', heading.lstrip().rstrip('\r\n'))
#if len(bodytext) > 0:
attach_note(node, bodytext)
oldheading = heading
xmltree = et.ElementTree(mapnode)
xmltree.write(sys.stdout, 'utf-8')
print

View file

@ -0,0 +1,27 @@
A
: This is some early multi-line body text which should wrap. This is some
: early multi-line body text which should wrap. This is some early
: multi-line body text which should wrap.
a
: single line body text
b
1
c
B
d
2
3
4
C
This is a very long heading line that should be longer than the wrap width and so tests this case
Heading with a tab in the middle
: body text with tab in it
Body text with empy line in it
: First line
:
: Third line
Difficult characters
[_] ' " &
Last line body text
: This is a last line of body text

View file

@ -0,0 +1,10 @@
#!/bin/sh
tmp=/tmp
dirname=`dirname $0`
fname=$dirname/test.otl
[ -n "$1" ] && fname=$1
$dirname/freemind.py -m $fname > $tmp/test.mm
$dirname/freemind.py -o $tmp/test.mm > $tmp/return.otl
diff -Nur $fname $tmp/return.otl

View file

@ -0,0 +1,329 @@
#! /usr/bin/ruby
# = Synopsis
#
# vo2xo: Converts VIM Outliner files to HTML slides.
#
# = Usage
#
# vo2html [OPTION] input-file
#
# -?, --help: show help
#
# -h, --head file-name:
# insert the contents of the named file within the HTML HEAD element.
#
# -b, --body file-name:
# insert the contents of the named file just after the start of the HTML BODY element.
#
# -t, --tail file-name:
# insert the contents of the named file just before the end the HTML BODY element.
#
# input-file: The VIM Outliner file to be converted.
# LICENSE
# Copyright (C) 2006 Bruce Perens
#
# This license incorporates by reference the terms of GPL Version 3 or, at
# your choice, any later version of the GPL published by the Free Software
# Foundation, Inc. of Boston, MA, USA.
#
# You may not modify this license. You must preserve it and the accompanying
# copyright declaration in any whole or fragmentary verisons of the software
# to which they apply.
#
require 'getoptlong'
require 'rdoc/usage'
require 'rubygems'
require 'builder'
gem 'ruby-mp3info'
require 'mp3info'
class OutlineParser
private
LeadingTabPattern = /^(\t*)(.*)$/.freeze
ColonPattern = /^:[ \t]*(.*)$/.freeze
protected
def initialize(file)
@file = file
@pushback = nil
@nesting = 0
end
# Get a line, with push-back.
def get_line
if (line = @pushback)
@pushback = nil
return line
else
return @file.gets
end
end
# Recursive parser for VIM Outliner.
#
# Meant to be called from itself when nesting increases - it calls its
# callers "nest" method, which calls "parse". This fits well with nesting
# output paradigms like that of Builder::XmlMarkup.
#
# Returns when nesting decreases, or if got_heading is set, just before the
# next top-level heading in the input stream. You should iteratively call
# parse() until more() returns false. This facilitates per-heading handling
# such as in the Xoxo converter, which uses first-level heading to demarcate
# the boundaries of slides.
#
# got_heading: If set, this will return just before the next top-level
# heading, leaving it in the push-back.
#
def parse(got_heading = false)
while (line = get_line())
m = line.match(LeadingTabPattern)
n = m[1].length # This line's nesting level.
after_tabs = m[2] # Content after zero or more leading tabs.
# Drop empty lines, and lines with only tabs.
next if after_tabs == ''
if n != @nesting # The nesting level changes with this line.
previous = @nesting
@nesting = n
@pushback = line
# If nesting increases, recursively parse it through nest().
# If nesting decreases, return to nest(), which will in turn
# return here. Both of these can be true in sequence! Nest()
# detects when a nesting level is closed by looking ahead one
# line and then pushing it back. That line can be one or MORE
# levels lesser than the current nesting level.
#
nest(n) if n > previous
return true if n < previous
elsif
if (p = after_tabs.match(ColonPattern)) and p[1].length > 0
text(p[1], n)
else
if got_heading and n == 0
@pushback = line
return true # Return before the next top-level heading.
end
got_heading = true
heading(after_tabs, n)
end
end
end
false
end
public
# Simple parser that returns true if there is any remaining content
# and leaves that content in the push-back.
# The return value is the content minus any leading tabs.
#
# Usage
# Return true if there is more content:
# if more
# Return the content of the next line to be read.
# more
#
# The second form is used to get the document title from the first line
# in the file.
#
def more
while (line = get_line())
if (m = line.match(LeadingTabPattern)) and m[2].length > 0
@pushback = line
return m[2]
end
end
false
end
end
class OutlineToHTML < OutlineParser
private
Type = [ :DOCTYPE,
:html,
:PUBLIC,
'-//W3C//DTD XHTML 1.0 Strict//EN',
'http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd'
].freeze
def wrap(nesting)
if nesting > 1
@x.li { yield }
else
yield
end
end
protected
def heading(text, nesting)
if nesting == 0
@x.h1(text)
else
@x.li(text)
end
end
def nest(nesting)
wrap(nesting) { @x.ul(:class => "list-#{nesting}") { parse(true) } }
end
def text(t, nesting)
@x.p(t)
end
public
def initialize(input_file, head_insert, body_insert, tail_insert)
@head_insert = head_insert.read if head_insert
@body_insert = body_insert.read if body_insert
@tail_insert = tail_insert.read if tail_insert
super(input_file)
end
def convert
n = 0
presentation_title = more
while (title = more)
audio_filename = "#{n + 1}.mp3"
next_audio_filename = "#{n + 2}.mp3"
@x = ::Builder::XmlMarkup.new(:indent => 2)
@x.declare!(*Type)
@x.html {
body_parameters = {}
@x.head {
if title != presentation_title
@x.title("#{presentation_title} - #{title}")
else
@x.title("#{presentation_title}")
end
@x << eval('"' + @head_insert + '"') if @head_insert
if File.exists?(audio_filename)
seconds = nil
Mp3Info.open(audio_filename) { |mp3|
seconds = mp3.length.ceil + 1
}
parameters = {
:'http-equiv' => 'refresh',
:content => "#{seconds};url=#{n + 2}.html"
}
@x.meta(parameters)
if File.exists?(next_audio_filename)
program = "function Preload() { a = new Image(); a.src = '#{next_audio_filename}'; p = new Image(); p.src = '#{n + 2}.html' }"
@x.script(program, :language => 'JavaScript')
body_parameters[:onload] = "Preload()"
end
end
}
@x.body(body_parameters) {
@x << eval('"' + @body_insert + '"') if @body_insert
attributes = { :class => 'content' }
# Fix: I don't yet know if this is the last slide, so I can't
# decide whether to do this onclick action or not.
# Oops. We have to generate the body tag after its contents.
# This might be awkward to do within Builder.
attributes[:onclick] = "document.location='#{n + 2}.html'"
@x.div(attributes) {
parse(false)
}
@x.div(:class => 'bottom') {
@x << eval('"' + @tail_insert + '"') if @tail_insert
@x.div(:class => 'navbar-wrapper') {
@x.ul(:class => 'navbar') {
@x.li(:class => 'previous') {
if n > 0
@x.a('Previous', :href => "#{n}.html")
end
}
@x.li(:class => 'top') {
@x.a(presentation_title, :href => "1.html")
}
@x.li(:class => 'next') {
if more
@x.a('Next', :href => "#{n + 2}.html")
end
}
}
}
if File.exists?(audio_filename)
@x.object(:type => 'audio/mpeg', :data => audio_filename, :width => "95%", :height => 42) {
message = "Your web browser isn't configured correctly to play the audio file #{audio_filename}, and thus you are missing the sound-track to this program."
@x.param(:name => 'autoplay', :value => true)
@x.param(:name => 'playcount', :value => 1)
@x.param(:name => 'showcontrols', :value => false)
@x.param(:name => 'showdisplay', :value => false)
@x.span(message, :class => 'error')
}
end
}
}
}
File.open("#{n += 1}.html", "w") { |f|
f.write(@x.target!)
}
@x = nil
end
end
end
opts = GetoptLong.new(
[ '--help', '-?', GetoptLong::NO_ARGUMENT ],
[ '--head', '-h', GetoptLong::REQUIRED_ARGUMENT ],
[ '--body', '-b', GetoptLong::REQUIRED_ARGUMENT ],
[ '--tail', '-t', GetoptLong::REQUIRED_ARGUMENT ]
)
head_insert = nil
body_insert = nil
tail_insert = nil
begin
opts.each do |opt, arg|
case opt
when '--help'
RDoc::usage
when '--head'
head_insert = File.new(arg, 'r')
when '--body'
body_insert = File.new(arg, 'r')
when '--tail'
tail_insert = File.new(arg, 'r')
end
end
if ARGV.length != 1
RDoc::usage
exit 0
end
input_file = File.new(ARGV[0], 'r')
rescue Exception => error
$stderr.print("Error: #{error}\n")
exit(1)
end
c = OutlineToHTML.new(input_file, head_insert, body_insert, tail_insert)
if not c.more
$stderr.write("Error: Input file contains no content.\n")
exit(1)
end
c.convert
exit(0)

Some files were not shown because too many files have changed in this diff Show more