diff --git a/README.markdown b/README.markdown index 2b8b7a9..463fa27 100644 --- a/README.markdown +++ b/README.markdown @@ -16,9 +16,9 @@ smooth the path to writing prose. * For editing files in _text_, _markdown_, _textile_, and other prose-oriented file types -* Agnostic on soft line wrapping _versus_ hard line breaks, supporting +* Agnostic on soft line wrap _versus_ hard line breaks, supporting both -* Auto-detects wrap mode via modeline if present +* Auto-detects wrap mode via modeline and sampling * Adjusts navigation key mappings to suit the wrap mode * Creates undo points on common punctuation * When using hard line breaks, enables autoformat while inserting text @@ -61,7 +61,7 @@ the basics of Vim._ ## Configuration -### Hard line breaks or soft line wrapping? +### Hard line breaks or soft line wrap? Coders will have the most experience with the former, and writers the latter. But whatever your background, chances are that you must contend @@ -82,16 +82,16 @@ 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* initialize with hard line -break mode. +plugin will auto-detect the line wrap approach, with `hard` as the +default. But for files of type `text`, it will *always* initialize with +hard line break mode. ### Commands Because auto-detect might not work as intended, you can invoke a command to set the behavior for the current buffer: -* `SoftPencil` - mode for soft line wrapping +* `SoftPencil` - mode for soft line wrap * `HardPencil` - mode for hard line breaks * `DropPencil` - removes navigation mappings and restores buffer to global settings * `TogglePencil` - if off, enables with detection; if on, turns off @@ -111,7 +111,7 @@ _This ‘autoformat’ feature affects **HardPencil** mode only._ When in **HardPencil** mode, Vim’s autoformat feature will be enabled by default in Insert mode and can offer many of the same benefits as soft -line wrapping. But autoformat will cause havoc when editing anything but +line wrap. But autoformat will cause havoc when editing anything but paragraphs of words, such as a code block or table. In these cases you will need to disable it, at least temporarily, via a command: @@ -198,42 +198,73 @@ a hard break. If you wish to retain the default Vim behavior, set the let g:pencil#cursorwrap = 1 " 0=disable, 1=enable ``` -## Auto-detection via modeline +## Auto-detecting wrap mode -Will the wrapping mode be detected accurately? Maybe. But you can improve -its chances by giving it a hint. +If you provided no explicit wrap mode during initialization, _pencil_ will +attempt to automatically detect the wrap mode. -At the bottom of this document is a strange code: +It will first look for a `textwidth` (or `tw`) specified in a modeline. +Failing that _pencil_ will then sample lines from the start of the file. + +### Detect via modeline + +Will the wrap mode be detected accurately? Maybe. But you can improve its +chances by giving it an explicit hint. + +At the bottom of this document is a odd-looking code: ``` ``` This is a ‘modeline’ that tells Vim to run the following command upon -loading this file into a buffer: +loading the 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’. - -If it’s `0`, then pencil assumes you want soft line wrapping. +That’s a strong hint to _pencil_ that it should assume hard line breaks, +regardless of whether or not soft line wrap is the default editing mode +for files of type ‘markdown’. ``` ``` -For more details: - -```vim -:help modeline -``` +If textwidth is `0` as shown above, then _pencil_ assumes you want soft +line wrap. Note that even if the modelines feature is disabled (such as for security -reasons) the textwidth may nevertheless be set by this plugin. +reasons) the textwidth will still be set by this plugin. + +### Detect via sampling + +If no modeline with a textwidth is found, _pencil_ will sample the initial +lines from the file looking for excessively-long lines. + +There are two settings you can add to your `.vimrc` to tweak this behavior. + +The maximum number of lines to sample from the start of the file: + +```vim +let g:pencil#softDetectSample = 10 +``` + +Set that value to `0` to disable detection via line sampling. + +When the number of bytes on a sampled line per exceeds this next value, +then _pencil_ assumes soft line wrap. + +```vim +let g:pencil#softDetectThreshold = 130 +``` + +If no such lines found, _pencil_ falls back to the default: + +```vim +let g:pencil#wrapModeDefault = 'hard' " or 'soft' +``` ## See also @@ -241,7 +272,6 @@ reasons) the textwidth may nevertheless be set by this plugin. * [Vim Training Class - Basic motions and commands](https://www.youtube.com/watch?v=Nim4_f5QUxA) - video tutorial by Shawn Biddle * [Vim for Writers](http://therandymon.com/woodnotes/vim-for-writers/vimforwriters.html) - guide to the basics geared to writers * [Vim-related books](http://iccf-holland.org/click5.html) - collection of books on learning Vim - * [pencil at vim.org](http://www.vim.org/scripts/script.php?script_id=4824) If you like this plugin, you might like these others from the same author: diff --git a/autoload/pencil.vim b/autoload/pencil.vim index 5af4067..dbed902 100644 --- a/autoload/pencil.vim +++ b/autoload/pencil.vim @@ -15,30 +15,32 @@ let s:WRAP_MODE_HARD = 1 let s:WRAP_MODE_SOFT = 2 " Wrap-mode detector -" attempt to determine user's intent from modeline +" Scan lines at end and beginning of file to determine the wrap mode. +" Modelines has priority over long lines found. 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 + let b:max_textwidth = -1 " assume no relevant modeline 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 + + if b:max_textwidth == 0 + " modeline(s) found only with zero textwidth, so it's soft wrapped 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 + + if b:max_textwidth > 0 + " modelines(s) found with positive textwidth, so hard line breaks + return s:WRAP_MODE_HARD + endif + + " scan initial lines in an attempt to detect long lines + for l:line in getline(1, g:pencil#softDetectSample) + if len(l:line) > g:pencil#softDetectThreshold + return s:WRAP_MODE_SOFT + endif + endfor + + " punt + return s:WRAP_MODE_DEFAULT endfunction function! pencil#setAutoFormat(mode) @@ -140,7 +142,10 @@ function! pencil#init(...) abort " clean out stuff we likely don't want setlocal formatoptions-=2 setlocal formatoptions-=v - setlocal formatoptions-=w " trailing whitespace continues paragraph (will enable in insert mode) + + " trailing whitespace continues paragraph + " makes autoformat behave oddly where spaces aren't present + setlocal formatoptions-=w else setlocal autoindent< noautoindent< setlocal list< nolist< @@ -224,15 +229,6 @@ fun! s:doOne(item) abort if l:matches[1] =~ 'textwidth\|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\|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 @@ -254,6 +250,8 @@ fun! s:doModeline(line) abort endif endfun +" sample lines for detection, capturing both +" modeline(s) and max line length " Hat tip to https://github.com/ciaranm/securemodelines fun! s:doModelines() abort if line("$") > &modelines diff --git a/plugin/pencil.vim b/plugin/pencil.vim index 85ec68f..64dd06c 100644 --- a/plugin/pencil.vim +++ b/plugin/pencil.vim @@ -41,6 +41,19 @@ if !exists('g:pencil#cursorwrap') let g:pencil#cursorwrap = 1 endif +if !exists('g:pencil#softDetectSample') + " if no modeline, read as many as this many lines at + " start of file in attempt to detect at least one line + " whose byte count exceeds g:pencil#softDetectThreshold + let g:pencil#softDetectSample = 10 +endif + +if !exists('g:pencil#softDetectThreshold') + " if the byte count of at least one sampled line exceeds + " this number, then pencil assumes soft line wrapping + let g:pencil#softDetectThreshold = 130 +endif + " # Commands command -nargs=0 HardPencil call pencil#init({'wrap': 'hard'}) command -nargs=0 SoftPencil call pencil#init({'wrap': 'soft'})