commit e1bddbc3b5e66c823a7adedff13751b036b9cfd5 Author: Reed Esau Date: Sat Jan 4 18:31:56 2014 -0700 first cut diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..44fe50a --- /dev/null +++ b/LICENSE @@ -0,0 +1,23 @@ +License: The MIT License (MIT) + +Copyright (c) 2013,2014 Reed Esau + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + + diff --git a/README.markdown b/README.markdown new file mode 100644 index 0000000..d226523 --- /dev/null +++ b/README.markdown @@ -0,0 +1,213 @@ +# vim-pencil + +> “Extending Vim to better support writing prose and documentation” + +Features of this plugin: + +* Configures Vim for word processing, with key mappings, undo points, etc. +* Agnostic on hard line endings _versus_ soft wrapping +* Use for editing markdown, textile, documentation, etc. +* Can detect wrapping approach via modeline + +Why such a minimalistic approach? There are several Vim plugins for +writing that take a comprehensive approach, including changing not only +the functional behavior of the editor, but also visual aspects such as +colorscheme and font. This plugin heads in the opposite direction, +focusing narrowly on the few tweaks needed to smooth the path to writing +prose in Vim. + +Where you need more features, you can tailor your environment +by installing those plugins that meet your specific needs. + +## Why use Vim for writing? + +While programmers will extol the many virtues of Vim in writing code, few +will appreciate its powerful text manipulation capabilities for writing +documentation and prose. + +But with plenty of word processing tools available, including those which +specifically cater to writers, why use a programmer’s editor like Vim for +writing? + +There are good reasons NOT to use Vim for writing: + +* Primitive in certain respects (no WYSIWYG or proportionally spaced + characters, e.g.) +* A modal editor with a steep learning curve +* Time and effort to configure to your needs + +But then again Vim offers a unique editing environment not matched by +other writing tools: + +* Your hands rest in a neutral ‘home’ position, only rarely straying to + reach for mouse, track pad, or arrow keys +* Minimal chording, with many mnemonic-friendly commands +* Sophisticated capabilities for navigating and manipulating text +* Highly configurable to suit your needs, with many plugins available + +## Installation + +Install using Pathogen, Vundle, Neobundle, or your favorite Vim package manager. + +(Suggestion for those who are new to Vim: you should first work through +one of the Vim tutorials listed at the bottom of this document. Then, once +you are comfortable with the basics of Vim, consider installing this +plugin.) + +## Configuration + +### Hard breaks or soft wrapping? + +Where do you stand on hard line breaks versus soft line wrapping? + +Typically, coders will have the most experience with the former, and +writers the latter. But whatever your background, chances are that you +will be living in a mixed environment where you must contend with both. +This plugin doesn't force you to choose a side—each buffer is configured +independently. + +In most cases you can set a default to suit your preference and let +auto-detection figure out what to do. Add to your `.vimrc`: + +```vim +let g:pencil#wrapModeDefault = 'hard' " or 'soft' +augroup pencil + autocmd! + autocmd FileType markdown call pencil#init() + autocmd FileType textile call pencil#init() + autocmd FileType text call pencil#init({'wrap': 'hard'}) +augroup END +``` + +In the example above, for files of type `markdown` and `textile`, this +plugin will auto-detect the wrapping approach, with `hard` as the default. +But for files of type `text`, it will *always* use hard line endings. + +### Commands + +Because auto-detect doesn’t always work correctly, you can invoke commands +to set the behavior for the current buffer: + +* `PencilSoft` - configure for soft wrapping +* `PencilHard` - configure for hard line endings +* `PencilToggle` - if off, enables with detection; if on, turns off +* `PencilOff` - removing mappings and restore global settings + +Optionally, you can map to keys in your `.vimrc`: + +```vim +nmap ws :PencilSoft +nmap wh :PencilHard +nmap ww :PencilToggle +nmap w0 :PencilOff +``` + +Also, more commands in ‘Automatic Formatting’ below. + +### Additional settings + +You can configure the default `textwidth` for Hard mode, when none is set +or available via modeline: + +```vim +let g:pencil#textwidth = 74 +``` + +`joinspaces` determines number of spaces after period (`0`=1 space, `1`=2 spaces) + +```vim +let g:pencil#joinspaces=0 +``` + +## Automatic formatting + +(This feature affects ‘hard’ line break mode only.) + +When using hard line breaks, Vim’s autoformat feature can offer many of +the same benefits as soft wrapping lines. But autoformat can cause havoc +when editing outside of paragraphs of sentences. Occasionally, you will +need to disable it. + +To set the default behavior, add to your `.vimrc`: + +```vim +let g:pencil#autoformat = 1 " 1=enable, 0=disable +``` + +You can override this default during initialization, as in: + +```vim +let g:pencil#wrapModeDefault = 'soft' +augroup pencil + autocmd! + autocmd FileType text call pencil#init({'wrap': 'hard', 'autoformat': 0}) + ... +augroup END +``` + +You can also toggle it as needed with a command: + +* `PencilFormatAuto` - enables autoformat +* `PencilFormatManual` - disables autoformat +* `PencilFormatToggle` + +Or bind to keys in your `.vimrc`: + +```vim +nmap wa :PencilFormatAuto +nmap wm :PencilFormatManual +nmap wf :PencilFormatToggle +``` + +Again, when using soft line wrapping, Vim’s autoformat feature does not +apply. + +## Auto-detection via modeline + +At the bottom of this document is a strange code: + +``` + +``` + +This is a ‘modeline’ that tells Vim to run the following command upon +loading this file into a buffer: + +```vim +:set textwidth=74 +``` + +That’s a strong hint to this plugin that we should assume hard line +endings, regardless of whether or not soft wrapping is the default editing +mode for files of type ‘markdown’. + +To provide a hint for detection, you can add a modeline to the last line +of your documents. For more details: + +```vim +:help modeline +``` + +Note that even if the modelines feature is disabled (such as for security +reasons) the textwidth will still be set by this plugin. + +## See also + +* [Vim for Writers](http://therandymon.com/woodnotes/vim-for-writers/vimforwriters.html) +* [Vim-related books](http://iccf-holland.org/click5.html) + +If you like this plugin, you might like these others from the same author: + +* [vim-lexical](http://github.com/reedes/vim-lexical) - Building on Vim’s spell-check and thesaurus/dictionary completion +* [vim-litecorrect](http://github.com/reedes/vim-litecorrect) - Lightweight auto-correction for Vim +* [vim-quotable](http://github.com/reedes/vim-quotable) - extends Vim to support typographic (‘curly’) quotes +* [vim-thematic](http://github.com/reedes/vim-thematic) — Conveniently manage Vim’s appearance to suit your task and environment + +## Future development + +If you’ve spotted a problem or have an idea on improving this plugin, +please post it to the github project issue page. + +``` + +``` diff --git a/autoload/pencil.vim b/autoload/pencil.vim new file mode 100644 index 0000000..ab7ccfc --- /dev/null +++ b/autoload/pencil.vim @@ -0,0 +1,257 @@ +" ============================================================================ +" File: pencil.vim +" Description: autoload functions for vim-pencil plugin +" Maintainer: Reed Esau +" Last Change: December 28, 2013 +" License: The MIT License (MIT) +" ============================================================================ + +if exists("autoloaded_pencil") | finish | endif +let autoloaded_pencil = 1 + +let s:WRAP_MODE_DEFAULT = -1 +let s:WRAP_MODE_OFF = 0 +let s:WRAP_MODE_HARD = 1 +let s:WRAP_MODE_SOFT = 2 + +" Wrap-mode detector +" attempt to determine user's intent from modeline +function! s:detect_mode() abort + let b:max_textwidth = -1 + let b:min_textwidth = 9999 + let b:max_wrapmargin = -1 + let b:min_wrapmargin = 9999 + + call s:doModelines() + if b:max_textwidth == -1 && + \ b:min_textwidth == 9999 && + \ b:max_wrapmargin == -1 && + \ b:min_wrapmargin == 9999 + " no relevant modeline params present + return s:WRAP_MODE_DEFAULT + elseif b:max_textwidth <= 0 && b:max_wrapmargin <= 0 + " no textwidth or wrapmargin were gt 0 + return s:WRAP_MODE_SOFT + elseif b:min_textwidth > 0 || b:min_wrapmargin > 0 + " at least one textwidth or wrapmargin was gt 0 + return s:WRAP_MODE_HARD + else + " unsure what to do! + return s:WRAP_MODE_DEFAULT + endif +endfunction + +function! pencil#setAutoFormat(mode) + " 1=enable, 0=disable, -1=toggle + if !exists('b:lastAF') + let b:lastAF = 0 + endif + let b:lastAF = a:mode == -1 ? !b:lastAF : a:mode + if b:lastAF + augroup pencil_autoformat + autocmd InsertEnter set formatoptions+=a + autocmd InsertLeave set formatoptions-=a + augroup END + else + silent! autocmd! pencil_autoformat * + endif +endfunction + +" Create mappings for word processing +" args: +" 'wrap': 'detect|off|hard|soft|toggle' +function! pencil#init(...) abort + let l:args = a:0 ? a:1 : {} + + if !exists('b:wrap_mode') + let b:wrap_mode = s:WRAP_MODE_OFF + endif + + " If user explicitly requested wrap_mode thru args, go with that. + let l:wrap_arg = get(l:args, 'wrap', 'detect') + + if (b:wrap_mode && l:wrap_arg ==# 'toggle') || + \ l:wrap_arg =~# '^\(off\|disable\|false\)$' + let b:wrap_mode = s:WRAP_MODE_OFF + elseif l:wrap_arg ==# 'hard' + let b:wrap_mode = s:WRAP_MODE_HARD + elseif l:wrap_arg ==# 'soft' + let b:wrap_mode = s:WRAP_MODE_SOFT + elseif l:wrap_arg ==# 'default' + let b:wrap_mode = s:WRAP_MODE_DEFAULT + else + " this can return s:WRAP_MODE_ for soft, hard or default + let b:wrap_mode = s:detect_mode() + endif + + " translate default(-1) to soft(1) or hard(2) or off(0) + if b:wrap_mode == s:WRAP_MODE_DEFAULT + if g:pencil#wrapModeDefault =~# '^\(off\|disable\|false\)$' + let b:wrap_mode = s:WRAP_MODE_OFF + elseif g:pencil#wrapModeDefault ==# 'soft' + let b:wrap_mode = s:WRAP_MODE_SOFT + else + let b:wrap_mode = s:WRAP_MODE_HARD + endif + endif + + " autoformat is only used in Hard mode, and then only during + " Insert mode + call pencil#setAutoFormat( + \ b:wrap_mode == s:WRAP_MODE_HARD && + \ get(l:args, 'autoformat', g:pencil#autoformat)) + + if b:wrap_mode == s:WRAP_MODE_HARD + if &modeline == 0 && b:max_textwidth > 0 + " Compensate for disabled modeline + execute 'setlocal textwidth=' . b:max_textwidth + elseif &textwidth == 0 + execute 'setlocal textwidth=' . g:pencil#textwidth + else + setlocal textwidth< + endif + setlocal nowrap + elseif b:wrap_mode == s:WRAP_MODE_SOFT + setlocal textwidth=0 + setlocal wrap + setlocal linebreak + setlocal colorcolumn=0 + else + setlocal textwidth< + setlocal wrap< nowrap< + setlocal linebreak< nolinebreak< + setlocal colorcolumn< + endif + + if b:wrap_mode + setlocal nolist + setlocal wrapmargin=0 + setlocal display+=lastline + setlocal formatoptions+=1 " don't break line before 1 letter word + setlocal formatoptions+=t + else + setlocal list< nolist< + setlocal wrapmargin< + setlocal display< + setlocal formatoptions< + endif + + if b:wrap_mode + if g:pencil#joinspaces + setlocal joinspaces " two spaces after .!? + else + setlocal nojoinspaces " only one space after a .!? (default) + endif + else + setlocal joinspaces< nojoinspaces< + endif + + if b:wrap_mode == s:WRAP_MODE_SOFT + nnoremap $ g$ + nnoremap 0 g0 + vnoremap $ g$ + vnoremap 0 g0 + noremap g + noremap g + else + silent! nunmap $ + silent! nunmap 0 + silent! vunmap $ + silent! vunmap 0 + silent! nunmap + silent! nunmap + endif + + if b:wrap_mode + inoremap g + inoremap g + noremap gk + noremap gj + nnoremap j gj + nnoremap k gk + vnoremap j gj + vnoremap k gk + else + silent! iunmap + silent! iunmap + silent! unmap + silent! unmap + silent! nunmap j + silent! nunmap k + silent! vunmap j + silent! vunmap k + endif + + " set undo points around common punctuation + if b:wrap_mode + inoremap . .u + inoremap ! !u + inoremap ? ?u + inoremap , ,u + inoremap ; ;u + else + silent! iunmap . + silent! iunmap ! + silent! iunmap ? + silent! iunmap , + silent! iunmap ; + endif +endfunction + +" attempt to find a non-zero textwidth, etc. +fun! s:doOne(item) abort + let l:matches = matchlist(a:item, '^\([a-z]\+\)=\([a-zA-Z0-9_\-.]\+\)$') + if len(l:matches) > 1 + if l:matches[1] ==# 'textwidth' || + \ l:matches[1] ==# 'tw' + if l:matches[2] > b:max_textwidth + let b:max_textwidth = l:matches[2] + elseif l:matches[2] < b:min_textwidth + let b:min_textwidth = l:matches[2] + endif + endif + if l:matches[1] ==# 'wrapmargin' || + \ l:matches[1] ==# 'wm' + if l:matches[2] > b:max_wrapmargin + let b:max_wrapmargin = l:matches[2] + elseif l:matches[2] < b:min_wrapmargin + let b:min_wrapmargin = l:matches[2] + endif + endif + endif +endfun + +" attempt to find a non-zero textwidth, etc. +fun! s:doModeline(line) abort + let l:matches = matchlist(a:line, '\%(\S\@=]\?\)\([0-9]\+\)\?\)\|\sex\):\s*\%(set\s\+\)\?\([^:]\+\):\S\@!') + if len(l:matches) > 0 + for l:item in split(l:matches[3]) + call s:doOne(l:item) + endfor + endif + let l:matches = matchlist(a:line, '\%(\S\@=]\?\)\([0-9]\+\)\?\)\|\sex\):\(.\+\)') + if len(l:matches) > 0 + for l:item in split(l:matches[3], '[ \t:]') + call s:doOne(l:item) + endfor + endif +endfun + +" Hat tip to https://github.com/ciaranm/securemodelines +fun! s:doModelines() abort + if line("$") > &modelines + let l:lines={ } + call map(filter(getline(1, &modelines) + + \ getline(line("$") - &modelines, "$"), + \ 'v:val =~ ":"'), 'extend(l:lines, { v:val : 0 } )') + for l:line in keys(l:lines) + call s:doModeline(l:line) + endfor + else + for l:line in getline(1, "$") + call s:doModeline(l:line) + endfor + endif +endfun + +" vim:ts=2:sw=2:sts=2 diff --git a/plugin/pencil.vim b/plugin/pencil.vim new file mode 100644 index 0000000..6f340f7 --- /dev/null +++ b/plugin/pencil.vim @@ -0,0 +1,51 @@ +" ============================================================================ +" File: pencil.vim +" Description: vim-pencil plugin +" Maintainer: Reed Esau +" Last Change: December 28, 2013 +" License: The MIT License (MIT) +" ============================================================================ +" +if exists('g:loaded_pencil') || &cp | finish | endif +let g:loaded_pencil = 1 + +" Save 'cpoptions' and set Vim default to enable line continuations. +let s:save_cpo = &cpo +set cpo&vim + +if !exists('g:pencil#wrapModeDefault') + " user-overridable default, if detection fails + " should be 'soft' or 'hard' or 'off' + let g:pencil#wrapModeDefault = 'hard' +endif + +if !exists('g:pencil#textwidth') + " textwidth used when in hard linebreak mode + let g:pencil#textwidth = 74 +endif + +if !exists('g:pencil#autoformat') + " by default, automatically format text when in Insert mode + " with hard wrap. + let g:pencil#autoformat = 1 +endif + +if !exists('g:pencil#joinspaces') + " by default, only one space after full stop (.) + let g:pencil#joinspaces = 0 +endif + +" # Commands +command -nargs=0 PencilHard call pencil#init({'wrap': 'hard'}) +command -nargs=0 PencilSoft call pencil#init({'wrap': 'soft'}) +command -nargs=0 PencilOff call pencil#init({'wrap': 'off' }) +command -nargs=0 PencilToggle call pencil#init({'wrap': 'toggle' }) + +command -nargs=0 PencilFormatAuto call pencil#setAutoFormat(1) +command -nargs=0 PencilFormatManual call pencil#setAutoFormat(0) +command -nargs=0 PencilFormatToggle call pencil#setAutoFormat(-1) + +let &cpo = s:save_cpo +unlet s:save_cpo + +" vim:ts=2:sw=2:sts=2