Allow underscores in function argument names
authorGreg Hurrell <greg@hurrell.net>
Wed, 6 Mar 2019 11:33:04 +0000 (12:33 +0100)
committerGreg Hurrell <greg@hurrell.net>
Wed, 6 Mar 2019 11:33:04 +0000 (12:33 +0100)
lib/Text/Docvim/Parse.hs
tests/fixtures/integration/command-t/golden/ast.golden
tests/fixtures/integration/command-t/golden/markdown.golden
tests/fixtures/integration/command-t/golden/plaintext.golden
tests/fixtures/integration/scalpel/golden/ast.golden
tests/fixtures/integration/scalpel/golden/markdown.golden
tests/fixtures/integration/scalpel/golden/plaintext.golden
tests/fixtures/integration/scalpel/input/autoload/scalpel.vim
tests/fixtures/integration/scalpel/input/plugin/scalpel.vim

index 63517f63b333ae2d0c3da0cc121d5cc599ac9c1a..555186fb4a7963b870c0625f1bdd6278b6dbffdc 100644 (file)
@@ -53,7 +53,7 @@ function =   FunctionDeclaration
     arguments  =  (char '(' >> optional wsc)
                *> (ArgumentList <$> argument `sepBy` (char ',' >> optional wsc))
                <* (optional wsc >> char ')' >> optional wsc)
-    argument   = Argument <$> (string "..." <|> many1 alphaNum) <* optional wsc
+    argument   = Argument <$> (string "..." <|> many1 (oneOf identifier)) <* optional wsc
     attributes = choice [string "abort", string "range", string "dict"] `sepEndBy` wsc
 
 -- Disambiguate `:endf[unction]` and `:endfo[r]`
index 2546828c63d05c4f659b8ca0845cd33707667478..022564202b717084eaa9d9a6cf24295371523e6d 100644 (file)
@@ -1,3 +1,579 @@
-"(eval)" (line 134, column 38):
-unexpected "_"
-expecting letter or digit, "\n", "," or ")"
+Project
+  [ Project
+      [ Unit
+          [ GenericStatement "if exists('g:command_t_autoloaded') || &cp"
+          , GenericStatement "finish"
+          , GenericStatement "endif"
+          , LetStatement
+              { letLexpr = "g:command_t_autoloaded " , letValue = "1" }
+          , FunctionDeclaration
+              { functionBang = True
+              , functionName = "s:RubyWarning"
+              , functionArguments = ArgumentList []
+              , functionAttributes = [ "abort" ]
+              , functionBody =
+                  [ GenericStatement "echohl WarningMsg"
+                  , GenericStatement
+                      "echo 'command-t.vim requires Vim to be compiled with Ruby support'"
+                  , GenericStatement
+                      "echo 'For more information type: :help command-t'"
+                  , GenericStatement "echohl none"
+                  ]
+              }
+          , FunctionDeclaration
+              { functionBang = True
+              , functionName = "commandt#BufferFinder"
+              , functionArguments = ArgumentList []
+              , functionAttributes = [ "abort" ]
+              , functionBody =
+                  [ GenericStatement "if has('ruby')"
+                  , GenericStatement "ruby $command_t.show_buffer_finder"
+                  , GenericStatement "else"
+                  , GenericStatement "call s:RubyWarning()"
+                  , GenericStatement "endif"
+                  ]
+              }
+          , FunctionDeclaration
+              { functionBang = True
+              , functionName = "commandt#CommandFinder"
+              , functionArguments = ArgumentList []
+              , functionAttributes = [ "abort" ]
+              , functionBody =
+                  [ GenericStatement "if has('ruby')"
+                  , GenericStatement "ruby $command_t.show_command_finder"
+                  , GenericStatement "else"
+                  , GenericStatement "call s:RubyWarning()"
+                  , GenericStatement "endif"
+                  ]
+              }
+          , FunctionDeclaration
+              { functionBang = True
+              , functionName = "commandt#FileFinder"
+              , functionArguments = ArgumentList [ Argument "arg" ]
+              , functionAttributes = [ "abort" ]
+              , functionBody =
+                  [ GenericStatement "if has('ruby')"
+                  , GenericStatement "ruby $command_t.show_file_finder"
+                  , GenericStatement "else"
+                  , GenericStatement "call s:RubyWarning()"
+                  , GenericStatement "endif"
+                  ]
+              }
+          , FunctionDeclaration
+              { functionBang = True
+              , functionName = "commandt#JumpFinder"
+              , functionArguments = ArgumentList []
+              , functionAttributes = [ "abort" ]
+              , functionBody =
+                  [ GenericStatement "if has('ruby')"
+                  , GenericStatement "ruby $command_t.show_jump_finder"
+                  , GenericStatement "else"
+                  , GenericStatement "call s:RubyWarning()"
+                  , GenericStatement "endif"
+                  ]
+              }
+          , FunctionDeclaration
+              { functionBang = True
+              , functionName = "commandt#MRUFinder"
+              , functionArguments = ArgumentList []
+              , functionAttributes = [ "abort" ]
+              , functionBody =
+                  [ GenericStatement "if has('ruby')"
+                  , GenericStatement "ruby $command_t.show_mru_finder"
+                  , GenericStatement "else"
+                  , GenericStatement "call s:RubyWarning()"
+                  , GenericStatement "endif"
+                  ]
+              }
+          , FunctionDeclaration
+              { functionBang = True
+              , functionName = "commandt#HelpFinder"
+              , functionArguments = ArgumentList []
+              , functionAttributes = [ "abort" ]
+              , functionBody =
+                  [ GenericStatement "if has('ruby')"
+                  , GenericStatement "ruby $command_t.show_help_finder"
+                  , GenericStatement "else"
+                  , GenericStatement "call s:RubyWarning()"
+                  , GenericStatement "endif"
+                  ]
+              }
+          , FunctionDeclaration
+              { functionBang = True
+              , functionName = "commandt#HistoryFinder"
+              , functionArguments = ArgumentList []
+              , functionAttributes = [ "abort" ]
+              , functionBody =
+                  [ GenericStatement "if has('ruby')"
+                  , GenericStatement "ruby $command_t.show_history_finder"
+                  , GenericStatement "else"
+                  , GenericStatement "call s:RubyWarning()"
+                  , GenericStatement "endif"
+                  ]
+              }
+          , FunctionDeclaration
+              { functionBang = True
+              , functionName = "commandt#LineFinder"
+              , functionArguments = ArgumentList []
+              , functionAttributes = [ "abort" ]
+              , functionBody =
+                  [ GenericStatement "if has('ruby')"
+                  , LetStatement
+                      { letLexpr = "g:CommandTCurrentBuffer" , letValue = "bufnr('%')" }
+                  , GenericStatement "ruby $command_t.show_line_finder"
+                  , GenericStatement "else"
+                  , GenericStatement "call s:RubyWarning()"
+                  , GenericStatement "endif"
+                  ]
+              }
+          , FunctionDeclaration
+              { functionBang = True
+              , functionName = "commandt#SearchFinder"
+              , functionArguments = ArgumentList []
+              , functionAttributes = [ "abort" ]
+              , functionBody =
+                  [ GenericStatement "if has('ruby')"
+                  , GenericStatement "ruby $command_t.show_search_finder"
+                  , GenericStatement "else"
+                  , GenericStatement "call s:RubyWarning()"
+                  , GenericStatement "endif"
+                  ]
+              }
+          , FunctionDeclaration
+              { functionBang = True
+              , functionName = "commandt#TagFinder"
+              , functionArguments = ArgumentList []
+              , functionAttributes = [ "abort" ]
+              , functionBody =
+                  [ GenericStatement "if has('ruby')"
+                  , GenericStatement "ruby $command_t.show_tag_finder"
+                  , GenericStatement "else"
+                  , GenericStatement "call s:RubyWarning()"
+                  , GenericStatement "endif"
+                  ]
+              }
+          , FunctionDeclaration
+              { functionBang = True
+              , functionName = "commandt#Flush"
+              , functionArguments = ArgumentList []
+              , functionAttributes = [ "abort" ]
+              , functionBody =
+                  [ GenericStatement "if has('ruby')"
+                  , GenericStatement "ruby $command_t.flush"
+                  , GenericStatement "else"
+                  , GenericStatement "call s:RubyWarning()"
+                  , GenericStatement "endif"
+                  ]
+              }
+          , FunctionDeclaration
+              { functionBang = True
+              , functionName = "commandt#Load"
+              , functionArguments = ArgumentList []
+              , functionAttributes = [ "abort" ]
+              , functionBody =
+                  [ GenericStatement "if !has('ruby')"
+                  , GenericStatement "call s:RubyWarning()"
+                  , GenericStatement "endif"
+                  ]
+              }
+          , FunctionDeclaration
+              { functionBang = True
+              , functionName = "commandt#ActiveFinder"
+              , functionArguments = ArgumentList []
+              , functionAttributes = [ "abort" ]
+              , functionBody =
+                  [ GenericStatement "if has('ruby')"
+                  , GenericStatement
+                      "ruby ::VIM::command \"return '#{$command_t.active_finder}'\""
+                  , GenericStatement "else"
+                  , GenericStatement "return ''"
+                  , GenericStatement "endif"
+                  ]
+              }
+          , FunctionDeclaration
+              { functionBang = True
+              , functionName = "commandt#Path"
+              , functionArguments = ArgumentList []
+              , functionAttributes = [ "abort" ]
+              , functionBody =
+                  [ GenericStatement "if has('ruby')"
+                  , GenericStatement
+                      "ruby ::VIM::command \"return '#{($command_t.path || '').gsub(/'/, \"''\")}'\""
+                  , GenericStatement "else"
+                  , GenericStatement "return ''"
+                  , GenericStatement "endif"
+                  ]
+              }
+          , FunctionDeclaration
+              { functionBang = True
+              , functionName = "commandt#CheckBuffer"
+              , functionArguments = ArgumentList [ Argument "buffer_number" ]
+              , functionAttributes = [ "abort" ]
+              , functionBody =
+                  [ GenericStatement "if has('ruby')"
+                  , GenericStatement
+                      "execute 'ruby $command_t.return_is_own_buffer' a:buffer_number"
+                  , GenericStatement "else"
+                  , GenericStatement "return 0"
+                  , GenericStatement "endif"
+                  ]
+              }
+          , GenericStatement "if !has('ruby')"
+          , GenericStatement "finish"
+          , GenericStatement "endif"
+          , GenericStatement "augroup CommandTMRUBuffer"
+          , GenericStatement "autocmd!"
+          , GenericStatement "autocmd BufEnter * ruby CommandT::MRU.touch"
+          , GenericStatement "autocmd BufDelete * ruby CommandT::MRU.delete"
+          , GenericStatement "augroup END"
+          , GenericStatement "ruby << EOF"
+          , GenericStatement "# require Ruby files"
+          , GenericStatement "begin"
+          , GenericStatement "require 'command-t'"
+          , GenericStatement
+              "# Make sure we're running with the same version of Ruby that Command-T was"
+          , GenericStatement "# compiled with."
+          , GenericStatement
+              "patchlevel = defined?(RUBY_PATCHLEVEL) ? RUBY_PATCHLEVEL : nil"
+          , GenericStatement "if CommandT::Metadata::UNKNOWN == true || ("
+          , GenericStatement
+              "CommandT::Metadata::EXPECTED_RUBY_VERSION == RUBY_VERSION &&"
+          , GenericStatement
+              "CommandT::Metadata::EXPECTED_RUBY_PATCHLEVEL == patchlevel"
+          , GenericStatement ")"
+          , GenericStatement
+              "require 'command-t/ext' # eager load, to catch compilation problems early"
+          , GenericStatement "$command_t = CommandT::Controller.new"
+          , GenericStatement "else"
+          , GenericStatement "$command_t = CommandT::Stub.new"
+          , GenericStatement "end"
+          , GenericStatement "rescue LoadError"
+          , GenericStatement "load_path_modified = false"
+          , GenericStatement
+              "::VIM::evaluate('&runtimepath').to_s.split(',').each do |path|"
+          , GenericStatement "ext = \"#{path}/ruby/command-t/ext\""
+          , GenericStatement
+              "if !$LOAD_PATH.include?(ext) && File.exist?(ext)"
+          , GenericStatement "$LOAD_PATH << ext"
+          , GenericStatement "load_path_modified = true"
+          , GenericStatement "end"
+          , GenericStatement "lib = \"#{path}/ruby/command-t/lib\""
+          , GenericStatement
+              "if !$LOAD_PATH.include?(lib) && File.exist?(lib)"
+          , GenericStatement "$LOAD_PATH << lib"
+          , GenericStatement "load_path_modified = true"
+          , GenericStatement "end"
+          , GenericStatement "end"
+          , GenericStatement "retry if load_path_modified"
+          , GenericStatement "$command_t = CommandT::Stub.new"
+          , GenericStatement "end"
+          , GenericStatement "EOF"
+          ]
+      , Unit
+          [ LetStatement
+              { letLexpr = "s:script_directory"
+              , letValue = "expand('<sfile>:p:h')"
+              }
+          , FunctionDeclaration
+              { functionBang = True
+              , functionName = "commandt#isengard#init"
+              , functionArguments = ArgumentList []
+              , functionAttributes = [ "abort" ]
+              , functionBody =
+                  [ LetStatement
+                      { letLexpr = "l:daemon_path"
+                      , letValue =
+                          "resolve(s:script_directory . '/../../ruby/command-t/bin/commandtd')"
+                      }
+                  , LetStatement
+                      { letLexpr = "l:client_log_file"
+                      , letValue = "get(g:, 'CommandTClientLog', '')"
+                      }
+                  , LetStatement
+                      { letLexpr = "l:server_log_file"
+                      , letValue = "get(g:, 'CommandTServerLog', '')"
+                      }
+                  , GenericStatement "if !empty(l:client_log_file)"
+                  , GenericStatement "call ch_logfile(l:client_log_file, 'w')"
+                  , GenericStatement "endif"
+                  , GenericStatement "if !empty(l:server_log_file)"
+                  , LetStatement
+                      { letLexpr = "s:job"
+                      , letValue =
+                          "job_start([l:daemon_path, '--logfile=' . l:server_log_file, '--vim-pid=' . getpid()])"
+                      }
+                  , GenericStatement "else"
+                  , LetStatement
+                      { letLexpr = "s:job"
+                      , letValue = "job_start([l:daemon_path, '--vim-pid=' . getpid()])"
+                      }
+                  , GenericStatement "endif"
+                  , LetStatement
+                      { letLexpr = "s:channel" , letValue = "job_getchannel(s:job)" }
+                  , GenericStatement
+                      "call ch_evalraw(s:channel, json_encode({'cd': getcwd()}) . \"\\n\")"
+                  , LetStatement
+                      { letLexpr = "g:CommandTResult"
+                      , letValue =
+                          "ch_evalraw(s:channel, json_encode({'match': 'commandt'}) . \"\\n\")"
+                      }
+                  ]
+              }
+          ]
+      , Unit
+          [ FunctionDeclaration
+              { functionBang = True
+              , functionName = "commandt#mirkwood#init"
+              , functionArguments = ArgumentList []
+              , functionAttributes = [ "abort" ]
+              , functionBody =
+                  [ GenericStatement
+                      "command! CommandTBuffer call commandt#BufferFinder()"
+                  , GenericStatement
+                      "command! CommandTCommand call commandt#CommandFinder()"
+                  , GenericStatement
+                      "command! CommandTHelp call commandt#HelpFinder()"
+                  , GenericStatement
+                      "command! CommandTHistory call commandt#HistoryFinder()"
+                  , GenericStatement
+                      "command! CommandTJump call commandt#JumpFinder()"
+                  , GenericStatement
+                      "command! CommandTLine call commandt#LineFinder()"
+                  , GenericStatement "command! CommandTMRU call commandt#MRUFinder()"
+                  , GenericStatement
+                      "command! CommandTSearch call commandt#SearchFinder()"
+                  , GenericStatement "command! CommandTTag call commandt#TagFinder()"
+                  , GenericStatement
+                      "command! -nargs=? -complete=dir CommandT call commandt#FileFinder(<q-args>)"
+                  , GenericStatement "command! CommandTFlush call commandt#Flush()"
+                  , GenericStatement "command! CommandTLoad call commandt#Load()"
+                  , GenericStatement
+                      "if !hasmapto('<Plug>(CommandT)') && maparg('<Leader>t', 'n') ==# ''"
+                  , GenericStatement "nmap <unique> <Leader>t <Plug>(CommandT)"
+                  , GenericStatement "endif"
+                  , GenericStatement
+                      "nnoremap <silent> <Plug>(CommandT) :CommandT<CR>"
+                  , GenericStatement
+                      "if !hasmapto('<Plug>(CommandTBuffer)') && maparg('<Leader>b', 'n') ==# ''"
+                  , GenericStatement "nmap <unique> <Leader>b <Plug>(CommandTBuffer)"
+                  , GenericStatement "endif"
+                  , GenericStatement
+                      "nnoremap <silent> <Plug>(CommandTBuffer) :CommandTBuffer<CR>"
+                  , GenericStatement
+                      "nnoremap <silent> <Plug>(CommandTHelp) :CommandTHelp<CR>"
+                  , GenericStatement
+                      "nnoremap <silent> <Plug>(CommandTHistory) :CommandTHistory<CR>"
+                  , GenericStatement "if has('jumplist')"
+                  , GenericStatement
+                      "if !hasmapto('<Plug>(CommandTJump)') && maparg('<Leader>j', 'n') ==# ''"
+                  , GenericStatement "nmap <unique> <Leader>j <Plug>(CommandTJump)"
+                  , GenericStatement "endif"
+                  , GenericStatement
+                      "nnoremap <silent> <Plug>(CommandTJump) :CommandTJump<CR>"
+                  , GenericStatement "endif"
+                  , GenericStatement
+                      "nnoremap <silent> <Plug>(CommandTCommand) :CommandTCommand<CR>"
+                  , GenericStatement
+                      "nnoremap <silent> <Plug>(CommandTLine) :CommandTLine<CR>"
+                  , GenericStatement
+                      "nnoremap <silent> <Plug>(CommandTMRU) :CommandTMRU<CR>"
+                  , GenericStatement
+                      "nnoremap <silent> <Plug>(CommandTSearch) :CommandTSearch<CR>"
+                  , GenericStatement
+                      "nnoremap <silent> <Plug>(CommandTTag) :CommandTTag<CR>"
+                  ]
+              }
+          ]
+      , Unit
+          [ FunctionDeclaration
+              { functionBang = True
+              , functionName = "commandt#private#ListMatches"
+              , functionArguments = ArgumentList []
+              , functionAttributes = [ "abort" ]
+              , functionBody =
+                  [ GenericStatement "ruby $command_t.list_matches" ]
+              }
+          , FunctionDeclaration
+              { functionBang = True
+              , functionName = "commandt#private#HandleKey"
+              , functionArguments = ArgumentList [ Argument "arg" ]
+              , functionAttributes = [ "abort" ]
+              , functionBody = [ GenericStatement "ruby $command_t.handle_key" ]
+              }
+          , FunctionDeclaration
+              { functionBang = True
+              , functionName = "commandt#private#Backspace"
+              , functionArguments = ArgumentList []
+              , functionAttributes = [ "abort" ]
+              , functionBody = [ GenericStatement "ruby $command_t.backspace" ]
+              }
+          , FunctionDeclaration
+              { functionBang = True
+              , functionName = "commandt#private#Delete"
+              , functionArguments = ArgumentList []
+              , functionAttributes = [ "abort" ]
+              , functionBody = [ GenericStatement "ruby $command_t.delete" ]
+              }
+          , FunctionDeclaration
+              { functionBang = True
+              , functionName = "commandt#private#AcceptSelection"
+              , functionArguments = ArgumentList []
+              , functionAttributes = [ "abort" ]
+              , functionBody =
+                  [ GenericStatement "ruby $command_t.accept_selection" ]
+              }
+          , FunctionDeclaration
+              { functionBang = True
+              , functionName = "commandt#private#AcceptSelectionTab"
+              , functionArguments = ArgumentList []
+              , functionAttributes = [ "abort" ]
+              , functionBody =
+                  [ GenericStatement
+                      "ruby $command_t.accept_selection :command => $command_t.tab_command"
+                  ]
+              }
+          , FunctionDeclaration
+              { functionBang = True
+              , functionName = "commandt#private#AcceptSelectionSplit"
+              , functionArguments = ArgumentList []
+              , functionAttributes = [ "abort" ]
+              , functionBody =
+                  [ GenericStatement
+                      "ruby $command_t.accept_selection :command => $command_t.split_command"
+                  ]
+              }
+          , FunctionDeclaration
+              { functionBang = True
+              , functionName = "commandt#private#AcceptSelectionVSplit"
+              , functionArguments = ArgumentList []
+              , functionAttributes = [ "abort" ]
+              , functionBody =
+                  [ GenericStatement
+                      "ruby $command_t.accept_selection :command => $command_t.vsplit_command"
+                  ]
+              }
+          , FunctionDeclaration
+              { functionBang = True
+              , functionName = "commandt#private#Quickfix"
+              , functionArguments = ArgumentList []
+              , functionAttributes = [ "abort" ]
+              , functionBody = [ GenericStatement "ruby $command_t.quickfix" ]
+              }
+          , FunctionDeclaration
+              { functionBang = True
+              , functionName = "commandt#private#Refresh"
+              , functionArguments = ArgumentList []
+              , functionAttributes = [ "abort" ]
+              , functionBody = [ GenericStatement "ruby $command_t.refresh" ]
+              }
+          , FunctionDeclaration
+              { functionBang = True
+              , functionName = "commandt#private#ToggleFocus"
+              , functionArguments = ArgumentList []
+              , functionAttributes = [ "abort" ]
+              , functionBody =
+                  [ GenericStatement "ruby $command_t.toggle_focus" ]
+              }
+          , FunctionDeclaration
+              { functionBang = True
+              , functionName = "commandt#private#Cancel"
+              , functionArguments = ArgumentList []
+              , functionAttributes = [ "abort" ]
+              , functionBody = [ GenericStatement "ruby $command_t.cancel" ]
+              }
+          , FunctionDeclaration
+              { functionBang = True
+              , functionName = "commandt#private#SelectNext"
+              , functionArguments = ArgumentList []
+              , functionAttributes = [ "abort" ]
+              , functionBody = [ GenericStatement "ruby $command_t.select_next" ]
+              }
+          , FunctionDeclaration
+              { functionBang = True
+              , functionName = "commandt#private#SelectPrev"
+              , functionArguments = ArgumentList []
+              , functionAttributes = [ "abort" ]
+              , functionBody = [ GenericStatement "ruby $command_t.select_prev" ]
+              }
+          , FunctionDeclaration
+              { functionBang = True
+              , functionName = "commandt#private#Clear"
+              , functionArguments = ArgumentList []
+              , functionAttributes = [ "abort" ]
+              , functionBody = [ GenericStatement "ruby $command_t.clear" ]
+              }
+          , FunctionDeclaration
+              { functionBang = True
+              , functionName = "commandt#private#ClearPrevWord"
+              , functionArguments = ArgumentList []
+              , functionAttributes = [ "abort" ]
+              , functionBody =
+                  [ GenericStatement "ruby $command_t.clear_prev_word" ]
+              }
+          , FunctionDeclaration
+              { functionBang = True
+              , functionName = "commandt#private#CursorLeft"
+              , functionArguments = ArgumentList []
+              , functionAttributes = [ "abort" ]
+              , functionBody = [ GenericStatement "ruby $command_t.cursor_left" ]
+              }
+          , FunctionDeclaration
+              { functionBang = True
+              , functionName = "commandt#private#CursorRight"
+              , functionArguments = ArgumentList []
+              , functionAttributes = [ "abort" ]
+              , functionBody =
+                  [ GenericStatement "ruby $command_t.cursor_right" ]
+              }
+          , FunctionDeclaration
+              { functionBang = True
+              , functionName = "commandt#private#CursorEnd"
+              , functionArguments = ArgumentList []
+              , functionAttributes = [ "abort" ]
+              , functionBody = [ GenericStatement "ruby $command_t.cursor_end" ]
+              }
+          , FunctionDeclaration
+              { functionBang = True
+              , functionName = "commandt#private#CursorStart"
+              , functionArguments = ArgumentList []
+              , functionAttributes = [ "abort" ]
+              , functionBody =
+                  [ GenericStatement "ruby $command_t.cursor_start" ]
+              }
+          , FunctionDeclaration
+              { functionBang = True
+              , functionName = "commandt#private#RunAutocmd"
+              , functionArguments = ArgumentList [ Argument "cmd" ]
+              , functionAttributes = [ "abort" ]
+              , functionBody =
+                  [ GenericStatement
+                      "if v:version > 703 || v:version == 703 && has('patch438')"
+                  , GenericStatement
+                      "execute 'silent doautocmd <nomodeline> User ' . a:cmd"
+                  , GenericStatement "else"
+                  , GenericStatement "execute 'silent doautocmd User ' . a:cmd"
+                  , GenericStatement "endif"
+                  ]
+              }
+          ]
+      , Unit
+          [ GenericStatement "if exists('g:command_t_loaded') || &compatible"
+          , GenericStatement "finish"
+          , GenericStatement "endif"
+          , LetStatement
+              { letLexpr = "g:command_t_loaded " , letValue = "1" }
+          , GenericStatement
+              "if has('patch-7-4-1829') && get(g:, 'CommandTEngine', 'mirkwood') ==? 'isengard'"
+          , GenericStatement "call commandt#isengard#init()"
+          , GenericStatement "endif"
+          , GenericStatement "call commandt#mirkwood#init()"
+          , GenericStatement "finish"
+          , GenericStatement
+              "if has('patch-7-4-1829') && get(g:, 'CommandTEngine', 'isengard') ==? 'isengard'"
+          , GenericStatement "call commandt#isengard#init()"
+          , GenericStatement "else"
+          , GenericStatement "call commandt#mirkwood#init()"
+          , GenericStatement "endif"
+          ]
+      ]
+  ]
index 2546828c63d05c4f659b8ca0845cd33707667478..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 100644 (file)
@@ -1,3 +0,0 @@
-"(eval)" (line 134, column 38):
-unexpected "_"
-expecting letter or digit, "\n", "," or ")"
index 2546828c63d05c4f659b8ca0845cd33707667478..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 100644 (file)
@@ -1,3 +0,0 @@
-"(eval)" (line 134, column 38):
-unexpected "_"
-expecting letter or digit, "\n", "," or ")"
index b6cddba42c3d029096dff1ab51ef4a634a278fe7..a372887537763a516945096bd2ece8f76351a2d4 100644 (file)
@@ -4,6 +4,7 @@ Project
       , TOC
           [ "Intro"
           , "Installation"
+          , "FAQ"
           , "Website"
           , "License"
           , "Development"
@@ -217,6 +218,58 @@ Project
       , Whitespace
       , Plaintext "file."
       ]
+  , Paragraph
+      [ Plaintext "Screencasts"
+      , Whitespace
+      , Plaintext "that"
+      , Whitespace
+      , Plaintext "show"
+      , Whitespace
+      , Plaintext "Scalpel"
+      , Whitespace
+      , Plaintext "in"
+      , Whitespace
+      , Plaintext "action:"
+      ]
+  , List
+      [ ListItem
+          [ Plaintext "https://youtu.be/YwMgnmZNWXA:"
+          , Whitespace
+          , Plaintext "\"Vim"
+          , Whitespace
+          , Plaintext "screencast"
+          , Whitespace
+          , Plaintext "#13:"
+          , Whitespace
+          , Plaintext "Multiple"
+          , Whitespace
+          , Plaintext "Cursors\""
+          ]
+      , ListItem
+          [ Plaintext "https://youtu.be/7Bx_mLDBtRc:"
+          , Whitespace
+          , Plaintext "\"Vim"
+          , Whitespace
+          , Plaintext "screencast"
+          , Whitespace
+          , Plaintext "#14:"
+          , Whitespace
+          , Plaintext "*Ncgn\""
+          ]
+      , ListItem
+          [ Plaintext "https://youtu.be/iNVyCPPYFzc:"
+          , Whitespace
+          , Plaintext "\"Vim"
+          , Whitespace
+          , Plaintext "screencast"
+          , Whitespace
+          , Plaintext "#21:"
+          , Whitespace
+          , Plaintext "Scalpel"
+          , Whitespace
+          , Plaintext "update\""
+          ]
+      ]
   , Paragraph
       [ Plaintext "Note"
       , Whitespace
@@ -785,6 +838,202 @@ Project
       , Plaintext "with:"
       ]
   , Fenced [ ":call pathogen#helptags()" ]
+  , HeadingAnnotation "FAQ"
+  , SubheadingAnnotation
+      "Why use Scalpel rather than a built-in alternative?"
+  , Paragraph
+      [ Plaintext "Scalpel"
+      , Whitespace
+      , Plaintext "is"
+      , Whitespace
+      , Plaintext "a"
+      , Whitespace
+      , Plaintext "lightweight"
+      , Whitespace
+      , Plaintext "plug-in"
+      , Whitespace
+      , Plaintext "that"
+      , Whitespace
+      , Plaintext "provides"
+      , Whitespace
+      , Plaintext "subtle"
+      , Whitespace
+      , Plaintext "but"
+      , Whitespace
+      , Plaintext "valuable"
+      , Whitespace
+      , Plaintext "improvements"
+      , Whitespace
+      , Plaintext "to"
+      , Whitespace
+      , Plaintext "the"
+      , Whitespace
+      , Plaintext "experience"
+      , Whitespace
+      , Plaintext "you'd"
+      , Whitespace
+      , Plaintext "get"
+      , Whitespace
+      , Plaintext "by"
+      , Whitespace
+      , Plaintext "using"
+      , Whitespace
+      , Plaintext "Vim's"
+      , Whitespace
+      , Plaintext "built-in"
+      , Whitespace
+      , Plaintext "functionality."
+      ]
+  , Paragraph
+      [ Plaintext "Compared"
+      , Whitespace
+      , Plaintext "to"
+      , Whitespace
+      , Plaintext "writing"
+      , Whitespace
+      , Plaintext "a"
+      , Whitespace
+      , Link ":substitute"
+      , Whitespace
+      , Plaintext "command"
+      , Whitespace
+      , Plaintext "manually:"
+      ]
+  , List
+      [ ListItem
+          [ Plaintext "Scalpel"
+          , Whitespace
+          , Plaintext "is"
+          , Whitespace
+          , Plaintext "quickly"
+          , Whitespace
+          , Plaintext "activated"
+          , Whitespace
+          , Plaintext "by"
+          , Whitespace
+          , Plaintext "a"
+          , Whitespace
+          , Plaintext "mapping."
+          ]
+      , ListItem
+          [ Plaintext "Scalpel"
+          , Whitespace
+          , Plaintext "prepopulates"
+          , Whitespace
+          , Plaintext "the"
+          , Whitespace
+          , Plaintext "search"
+          , Whitespace
+          , Plaintext "pattern"
+          , Whitespace
+          , Plaintext "with"
+          , Whitespace
+          , Plaintext "the"
+          , Whitespace
+          , Plaintext "word"
+          , Whitespace
+          , Plaintext "currently"
+          , Whitespace
+          , Plaintext "under"
+          , Whitespace
+          , Plaintext "the"
+          , Whitespace
+          , Plaintext "cursor."
+          ]
+      , ListItem
+          [ Plaintext "Scalpel"
+          , Whitespace
+          , Plaintext "avoids"
+          , Whitespace
+          , Plaintext "a"
+          , Whitespace
+          , Plaintext "jarring"
+          , Whitespace
+          , Plaintext "jump"
+          , Whitespace
+          , Plaintext "to"
+          , Whitespace
+          , Plaintext "the"
+          , Whitespace
+          , Plaintext "top"
+          , Whitespace
+          , Plaintext "of"
+          , Whitespace
+          , Plaintext "the"
+          , Whitespace
+          , Plaintext "file,"
+          , Whitespace
+          , Plaintext "instead"
+          , Whitespace
+          , Plaintext "starting"
+          , Whitespace
+          , Plaintext "replacements"
+          , Whitespace
+          , Plaintext "at"
+          , Whitespace
+          , Plaintext "the"
+          , Whitespace
+          , Plaintext "current"
+          , Whitespace
+          , Plaintext "location."
+          ]
+      ]
+  , Paragraph
+      [ Plaintext "Compared"
+      , Whitespace
+      , Plaintext "to"
+      , Whitespace
+      , Plaintext "a"
+      , Whitespace
+      , Plaintext "mapping"
+      , Whitespace
+      , Plaintext "such"
+      , Whitespace
+      , Plaintext "as"
+      , Whitespace
+      , Plaintext "\"*Ncgn\":"
+      ]
+  , List
+      [ ListItem
+          [ Plaintext "Scalpel"
+          , Whitespace
+          , Plaintext "allows"
+          , Whitespace
+          , Plaintext "you"
+          , Whitespace
+          , Plaintext "to"
+          , Whitespace
+          , Plaintext "preview"
+          , Whitespace
+          , Plaintext "the"
+          , Whitespace
+          , Plaintext "location"
+          , Whitespace
+          , Plaintext "at"
+          , Whitespace
+          , Plaintext "which"
+          , Whitespace
+          , Plaintext "each"
+          , Whitespace
+          , Plaintext "change"
+          , Whitespace
+          , Plaintext "will"
+          , Whitespace
+          , Plaintext "occur"
+          , Whitespace
+          , Plaintext "instead"
+          , Whitespace
+          , Plaintext "of"
+          , Whitespace
+          , Plaintext "performing"
+          , Whitespace
+          , Plaintext "the"
+          , Whitespace
+          , Plaintext "change"
+          , Whitespace
+          , Plaintext "blindly."
+          ]
+      ]
   , HeadingAnnotation "Website"
   , Paragraph
       [ Plaintext "The"
@@ -1337,7 +1586,118 @@ Project
       , Plaintext "<greg@hurrell.net>."
       ]
   , HeadingAnnotation "History"
-  , SubheadingAnnotation "0.2 (not yet released)"
+  , SubheadingAnnotation "1.0 (3 January 2019)"
+  , List
+      [ ListItem
+          [ Plaintext "Perform"
+          , Whitespace
+          , Plaintext "multiple"
+          , Whitespace
+          , Plaintext "replacements"
+          , Whitespace
+          , Plaintext "per"
+          , Whitespace
+          , Plaintext "line"
+          , Whitespace
+          , Plaintext "even"
+          , Whitespace
+          , Plaintext "when"
+          , Whitespace
+          , Link "'gdefault'"
+          , Whitespace
+          , Plaintext "is"
+          , Whitespace
+          , Plaintext "on."
+          ]
+      ]
+  , SubheadingAnnotation "0.5 (28 July 2018)"
+  , List
+      [ ListItem
+          [ Plaintext "Fix"
+          , Whitespace
+          , Plaintext "problem"
+          , Whitespace
+          , Plaintext "with"
+          , Whitespace
+          , Link "Visual"
+          , Whitespace
+          , Plaintext "mode"
+          , Whitespace
+          , Plaintext "operation"
+          , Whitespace
+          , Plaintext "on"
+          , Whitespace
+          , Plaintext "older"
+          , Whitespace
+          , Plaintext "versions"
+          , Whitespace
+          , Plaintext "of"
+          , Whitespace
+          , Plaintext "Vim"
+          , Whitespace
+          , Plaintext "(GitHub"
+          , Whitespace
+          , Plaintext "issue"
+          , Whitespace
+          , Plaintext "#8)."
+          ]
+      ]
+  , SubheadingAnnotation "0.4 (23 July 2018)"
+  , List
+      [ ListItem
+          [ Plaintext "Fix"
+          , Whitespace
+          , Plaintext "problem"
+          , Whitespace
+          , Plaintext "with"
+          , Whitespace
+          , Plaintext "replacement"
+          , Whitespace
+          , Plaintext "patterns"
+          , Whitespace
+          , Plaintext "containing"
+          , Whitespace
+          , Plaintext "the"
+          , Whitespace
+          , Plaintext "number"
+          , Whitespace
+          , Plaintext "1"
+          , Whitespace
+          , Plaintext "(GitHub"
+          , Whitespace
+          , Plaintext "issue"
+          , Whitespace
+          , Plaintext "#7)."
+          ]
+      ]
+  , SubheadingAnnotation "0.3 (10 May 2018)"
+  , List
+      [ ListItem
+          [ Plaintext "Fix"
+          , Whitespace
+          , Plaintext "compatibility"
+          , Whitespace
+          , Plaintext "with"
+          , Whitespace
+          , Plaintext "older"
+          , Whitespace
+          , Plaintext "versions"
+          , Whitespace
+          , Plaintext "of"
+          , Whitespace
+          , Plaintext "Vim"
+          , Whitespace
+          , Plaintext "that"
+          , Whitespace
+          , Plaintext "don't"
+          , Whitespace
+          , Plaintext "implement"
+          , Whitespace
+          , Link "getcurpos()"
+          , Plaintext "."
+          ]
+      ]
+  , SubheadingAnnotation "0.2 (13 June 2016)"
   , List
       [ ListItem
           [ Plaintext "Support"
@@ -1346,6 +1706,25 @@ Project
           , Whitespace
           , Plaintext "mode."
           ]
+      , ListItem
+          [ Plaintext "Do"
+          , Whitespace
+          , Plaintext "not"
+          , Whitespace
+          , Plaintext "show"
+          , Whitespace
+          , Plaintext "\"N"
+          , Whitespace
+          , Plaintext "substitutions"
+          , Whitespace
+          , Plaintext "on"
+          , Whitespace
+          , Plaintext "N"
+          , Whitespace
+          , Plaintext "lines\""
+          , Whitespace
+          , Plaintext "messages."
+          ]
       ]
   , SubheadingAnnotation "0.1 (29 April 2016)"
   , List
@@ -1366,14 +1745,14 @@ Project
                   , LetStatement
                       { letLexpr = "l:chars" , letValue = "split(l:line, '\\zs')" }
                   , LetStatement { letLexpr = "l:word" , letValue = "[]" }
-                  , GenericStatement "for l:char in l:chars[l:col:]"
+                  , GenericStatement "for l:char in l:chars[(l:col):]"
                   , GenericStatement "if match(l:char, '\\k') != -1"
                   , GenericStatement "call add(l:word, l:char)"
                   , GenericStatement "else"
                   , GenericStatement "break"
                   , GenericStatement "endif"
                   , GenericStatement "endfor"
-                  , GenericStatement "for l:char in reverse(l:chars[:l:col - 1])"
+                  , GenericStatement "for l:char in reverse(l:chars[:(l:col) - 1])"
                   , GenericStatement "if match(l:char, '\\k') != -1"
                   , GenericStatement "call insert(l:word, l:char, 0)"
                   , GenericStatement "else"
@@ -1383,6 +1762,43 @@ Project
                   , GenericStatement "return join(l:word, '')"
                   ]
               }
+          , FunctionDeclaration
+              { functionBang = False
+              , functionName = "s:g"
+              , functionArguments = ArgumentList []
+              , functionAttributes = []
+              , functionBody = [ GenericStatement "return &gdefault ? '' : 'g'" ]
+              }
+          , FunctionDeclaration
+              { functionBang = False
+              , functionName = "s:replacements"
+              , functionArguments =
+                  ArgumentList
+                    [ Argument "currentline"
+                    , Argument "_lastline"
+                    , Argument "patterns"
+                    , Argument "g"
+                    ]
+              , functionAttributes = []
+              , functionBody =
+                  [ LetStatement { letLexpr = "s:report" , letValue = "&report" }
+                  , GenericStatement "try"
+                  , GenericStatement "set report=10000"
+                  , GenericStatement
+                      "execute a:currentline . ',' . a:_lastline . 's' . a:patterns . a:g . 'ce#'"
+                  , GenericStatement "catch /^Vim:Interrupt$/"
+                  , GenericStatement "execute 'set report=' . s:report"
+                  , GenericStatement "return"
+                  , GenericStatement "finally"
+                  , GenericStatement "normal! q"
+                  , LetStatement
+                      { letLexpr = "s:transcript" , letValue = "getreg('s')" }
+                  , GenericStatement "if exists('s:register')"
+                  , GenericStatement "call setreg('s', s:register)"
+                  , GenericStatement "endif"
+                  , GenericStatement "endtry"
+                  ]
+              }
           , FunctionDeclaration
               { functionBang = True
               , functionName = "scalpel#substitute"
@@ -1412,8 +1828,9 @@ Project
                   , LetStatement
                       { letLexpr = "l:currentline" , letValue = "l:firstline" }
                   , GenericStatement "endif"
+                  , LetStatement { letLexpr = "l:g" , letValue = "s:g()" }
                   , GenericStatement
-                      "if match(a:patterns, '\\v^/[^/]*/[^/]*/$') != 0"
+                      "if match(a:patterns, '\\v^([^\"\\\\|A-Za-z0-9 ]).*\\1.*\\1$') != 0"
                   , GenericStatement "echomsg 'Invalid patterns: ' . a:patterns"
                   , GenericStatement
                       "echomsg 'Expected patterns of the form \"/foo/bar/\".'"
@@ -1421,28 +1838,27 @@ Project
                   , GenericStatement "endif"
                   , GenericStatement "if getregtype('s') != ''"
                   , LetStatement
-                      { letLexpr = "l:register" , letValue = "getreg('s')" }
+                      { letLexpr = "s:register" , letValue = "getreg('s')" }
+                  , GenericStatement "elseif exists('s:register')"
+                  , UnletStatement { unletBang = False , unletBody = "s:register" }
                   , GenericStatement "endif"
                   , GenericStatement "normal! qs"
+                  , GenericStatement "if exists('*execute')"
+                  , LetStatement
+                      { letLexpr = "l:replacements"
+                      , letValue =
+                          "execute('call s:replacements(l:currentline, l:lastline, a:patterns, l:g)', '')"
+                      }
+                  , GenericStatement "else"
                   , GenericStatement "redir => l:replacements"
-                  , GenericStatement "try"
                   , GenericStatement
-                      "execute l:currentline . ',' . l:lastline . 's' . a:patterns . 'gce#'"
-                  , GenericStatement "catch /^Vim:Interrupt$/"
-                  , GenericStatement "return"
-                  , GenericStatement "finally"
-                  , GenericStatement "normal! q"
-                  , LetStatement
-                      { letLexpr = "l:transcript" , letValue = "getreg('s')" }
-                  , GenericStatement "if exists('l:register')"
-                  , GenericStatement "call setreg('s', l:register)"
-                  , GenericStatement "endif"
-                  , GenericStatement "endtry"
+                      "call s:replacements(l:currentline, l:lastline, a:patterns, l:g)"
                   , GenericStatement "redir END"
+                  , GenericStatement "endif"
                   , GenericStatement "if len(l:replacements) > 0"
                   , LetStatement
                       { letLexpr = "l:last"
-                      , letValue = "strpart(l:transcript, len(l:transcript) - 1)"
+                      , letValue = "strpart(s:transcript, len(s:transcript) - 1)"
                       }
                   , GenericStatement
                       "if l:last ==# 'l' || l:last ==# 'q' || l:last ==# '\ESC'"
@@ -1450,14 +1866,15 @@ Project
                   , GenericStatement "elseif l:last ==# 'a'"
                   , GenericStatement "if l:currentline > l:firstline"
                   , GenericStatement
-                      "execute l:firstline . ',' . l:currentline . '-&gce'"
+                      "execute l:firstline . ',' . l:currentline . '-&' . l:g . 'ce'"
                   , GenericStatement "endif"
                   , GenericStatement "return"
                   , GenericStatement "endif"
                   , GenericStatement "endif"
                   , GenericStatement "if l:currentline > l:firstline"
                   , GenericStatement
-                      "execute l:firstline . ',' . l:currentline . '-&gce'"
+                      "execute l:firstline . ',' . l:currentline . '-&' .l:g . 'ce'"
+                  , GenericStatement "execute 'set report=' . s:report"
                   , GenericStatement "endif"
                   ]
               }
@@ -1485,10 +1902,25 @@ Project
           , GenericStatement "endif"
           , GenericStatement
               "execute 'command! -nargs=1 -range ' . s:command . ' call scalpel#substitute(<q-args>, <line1>, <line2>, <count>)'"
-          , LetStatement { letLexpr = "s:curpos" , letValue = "getcurpos()" }
+          , FunctionDeclaration
+              { functionBang = True
+              , functionName = "s:GetCurposCompat"
+              , functionArguments = ArgumentList []
+              , functionAttributes = []
+              , functionBody =
+                  [ GenericStatement "if exists('*getcurpos')"
+                  , GenericStatement "return getcurpos()"
+                  , GenericStatement "else"
+                  , GenericStatement "return getpos('.')"
+                  , GenericStatement "endif"
+                  ]
+              }
+          , LetStatement
+              { letLexpr = "s:curpos" , letValue = "s:GetCurposCompat()" }
           , GenericStatement "augroup Scalpel"
           , GenericStatement "autocmd!"
-          , GenericStatement "autocmd CursorMoved * let s:curpos=getcurpos()"
+          , GenericStatement
+              "autocmd CursorMoved * let s:curpos=s:GetCurposCompat()"
           , GenericStatement "augroup END"
           , FunctionDeclaration
               { functionBang = True
index c829eabda1ccd7b213b9f607edb796f21a9b762b..2dca5967d08fad2768fbee43fd76a85863a30c38 100644 (file)
@@ -16,6 +16,12 @@ Press `<Enter>` and Scalpel will prompt to confirm each substitution, starting a
 
 Scalpel works similarly in visual mode, except that it scopes itself to the current visual selection rather than operating over the entire file.
 
+Screencasts that show Scalpel in action:
+
+- https://youtu.be/YwMgnmZNWXA: &quot;Vim screencast #13: Multiple Cursors&quot;
+- https://youtu.be/7Bx_mLDBtRc: &quot;Vim screencast #14: *Ncgn&quot;
+- https://youtu.be/iNVyCPPYFzc: &quot;Vim screencast #21: Scalpel update&quot;
+
 Note that `:Scalpel` just calls through to an underlying `scalpel#substitute` function that does the real work, ultimately calling Vim's own `:substitute`. As such, be aware that whatever changes you make to the command-line prior to pressing `<Enter>` must keep it a valid pattern, or bad things will happen.
 
 The mapping can be suppressed by setting:
@@ -86,6 +92,24 @@ To generate help tags under Pathogen, you can do so from inside Vim with:
 ```
 
 
+## FAQ<a name="scalpel-faq" href="#user-content-scalpel-faq"></a>
+
+
+### Why use Scalpel rather than a built-in alternative?<a name="scalpel-why-use-scalpel-rather-than-a-built-in-alternative" href="#user-content-scalpel-why-use-scalpel-rather-than-a-built-in-alternative"></a>
+
+Scalpel is a lightweight plug-in that provides subtle but valuable improvements to the experience you'd get by using Vim's built-in functionality.
+
+Compared to writing a <strong>`:substitute`</strong> command manually:
+
+- Scalpel is quickly activated by a mapping.
+- Scalpel prepopulates the search pattern with the word currently under the cursor.
+- Scalpel avoids a jarring jump to the top of the file, instead starting replacements at the current location.
+
+Compared to a mapping such as &quot;*Ncgn&quot;:
+
+- Scalpel allows you to preview the location at which each change will occur instead of performing the change blindly.
+
+
 ## Website<a name="scalpel-website" href="#user-content-scalpel-website"></a>
 
 The official Scalpel source code repo is at:
@@ -170,9 +194,30 @@ Scalpel is written and maintained by Greg Hurrell &lt;greg@hurrell.net&gt;.
 ## History<a name="scalpel-history" href="#user-content-scalpel-history"></a>
 
 
-### 0.2 (not yet released)<a name="scalpel-02-not-yet-released" href="#user-content-scalpel-02-not-yet-released"></a>
+### 1.0 (3 January 2019)<a name="scalpel-10-3-january-2019" href="#user-content-scalpel-10-3-january-2019"></a>
+
+- Perform multiple replacements per line even when <strong>`'gdefault'`</strong> is on.
+
+
+### 0.5 (28 July 2018)<a name="scalpel-05-28-july-2018" href="#user-content-scalpel-05-28-july-2018"></a>
+
+- Fix problem with <strong>`Visual`</strong> mode operation on older versions of Vim (GitHub issue #8).
+
+
+### 0.4 (23 July 2018)<a name="scalpel-04-23-july-2018" href="#user-content-scalpel-04-23-july-2018"></a>
+
+- Fix problem with replacement patterns containing the number 1 (GitHub issue #7).
+
+
+### 0.3 (10 May 2018)<a name="scalpel-03-10-may-2018" href="#user-content-scalpel-03-10-may-2018"></a>
+
+- Fix compatibility with older versions of Vim that don't implement <strong>`getcurpos()`</strong>.
+
+
+### 0.2 (13 June 2016)<a name="scalpel-02-13-june-2016" href="#user-content-scalpel-02-13-june-2016"></a>
 
 - Support visual mode.
+- Do not show &quot;N substitutions on N lines&quot; messages.
 
 
 ### 0.1 (29 April 2016)<a name="scalpel-01-29-april-2016" href="#user-content-scalpel-01-29-april-2016"></a>
index febb27760a9635d6d4798d224818fb53704f576b..d873e717f45a4bab3398ea14c10ff02a3659a0ab 100644 (file)
@@ -4,11 +4,12 @@ CONTENTS                                                      *scalpel-contents*
 
 1. Intro            |scalpel-intro|
 2. Installation     |scalpel-installation|
-3. Website          |scalpel-website|
-4. License          |scalpel-license|
-5. Development      |scalpel-development|
-6. Authors          |scalpel-authors|
-7. History          |scalpel-history|
+3. FAQ              |scalpel-faq|
+4. Website          |scalpel-website|
+5. License          |scalpel-license|
+6. Development      |scalpel-development|
+7. Authors          |scalpel-authors|
+8. History          |scalpel-history|
 
 INTRO                                                            *scalpel-intro*
 
@@ -28,6 +29,12 @@ the file).
 Scalpel works similarly in visual mode, except that it scopes itself to the
 current visual selection rather than operating over the entire file.
 
+Screencasts that show Scalpel in action:
+
+- https://youtu.be/YwMgnmZNWXA: "Vim screencast #13: Multiple Cursors"
+- https://youtu.be/7Bx_mLDBtRc: "Vim screencast #14: *Ncgn"
+- https://youtu.be/iNVyCPPYFzc: "Vim screencast #21: Scalpel update"
+
 Note that `:Scalpel` just calls through to an underlying `scalpel#substitute`
 function that does the real work, ultimately calling Vim's own `:substitute`.
 As such, be aware that whatever changes you make to the command-line prior
@@ -92,6 +99,27 @@ To generate help tags under Pathogen, you can do so from inside Vim with:
 >
     :call pathogen#helptags()
 <
+FAQ                                                                *scalpel-faq*
+
+Why use Scalpel rather than a built-in alternative? ~
+
+Scalpel is a lightweight plug-in that provides subtle but valuable
+improvements to the experience you'd get by using Vim's built-in
+functionality.
+
+Compared to writing a |:substitute| command manually:
+
+- Scalpel is quickly activated by a mapping.
+- Scalpel prepopulates the search pattern with the word currently under the
+  cursor.
+- Scalpel avoids a jarring jump to the top of the file, instead starting
+  replacements at the current location.
+
+Compared to a mapping such as "*Ncgn":
+
+- Scalpel allows you to preview the location at which each change will occur
+  instead of performing the change blindly.
+
 WEBSITE                                                        *scalpel-website*
 
 The official Scalpel source code repo is at:
@@ -173,9 +201,29 @@ Scalpel is written and maintained by Greg Hurrell <greg@hurrell.net>.
 
 HISTORY                                                        *scalpel-history*
 
-0.2 (not yet released) ~
+1.0 (3 January 2019) ~
+
+- Perform multiple replacements per line even when |'gdefault'| is on.
+
+0.5 (28 July 2018) ~
+
+- Fix problem with |Visual| mode operation on older versions of Vim (GitHub
+  issue #8).
+
+0.4 (23 July 2018) ~
+
+- Fix problem with replacement patterns containing the number 1 (GitHub
+  issue #7).
+
+0.3 (10 May 2018) ~
+
+- Fix compatibility with older versions of Vim that don't implement
+  |getcurpos()|.
+
+0.2 (13 June 2016) ~
 
 - Support visual mode.
+- Do not show "N substitutions on N lines" messages.
 
 0.1 (29 April 2016) ~
 
index f633c99ec2e03a9d038d0309c53bb987a06dd2c4..34abc117cecfde44c21c30bf5069a4059d58c82b 100644 (file)
@@ -10,7 +10,7 @@ function! scalpel#cword(curpos) abort
   let l:word=[]
 
   " Look for keyword characters rightwards.
-  for l:char in l:chars[l:col:]
+  for l:char in l:chars[(l:col):]
     if match(l:char, '\k') != -1
       call add(l:word, l:char)
     else
@@ -19,7 +19,7 @@ function! scalpel#cword(curpos) abort
   endfor
 
   " Look for keyword characters leftwards.
-  for l:char in reverse(l:chars[:l:col - 1])
+  for l:char in reverse(l:chars[:(l:col) - 1])
     if match(l:char, '\k') != -1
       call insert(l:word, l:char, 0)
     else
@@ -30,6 +30,29 @@ function! scalpel#cword(curpos) abort
   return join(l:word, '')
 endfunction
 
+function s:g()
+  return &gdefault ? '' : 'g'
+endfunction
+
+" a:lastline is effectively reserved (see `:h a:lastline`), so use _lastline
+" instead.
+function s:replacements(currentline, _lastline, patterns, g)
+  let s:report=&report
+  try
+    set report=10000
+    execute a:currentline . ',' . a:_lastline . 's' . a:patterns . a:g . 'ce#'
+  catch /^Vim:Interrupt$/
+    execute 'set report=' . s:report
+    return
+  finally
+    normal! q
+    let s:transcript=getreg('s')
+    if exists('s:register')
+      call setreg('s', s:register)
+    endif
+  endtry
+endfunction
+
 function! scalpel#substitute(patterns, line1, line2, count) abort
   if a:count == -1
     " No range supplied, operate on whole buffer.
@@ -41,31 +64,34 @@ function! scalpel#substitute(patterns, line1, line2, count) abort
     let l:lastline=a:line2 >= a:line2 ? a:line2 : a:line1
     let l:currentline=l:firstline
   endif
-  if match(a:patterns, '\v^/[^/]*/[^/]*/$') != 0
+
+  let l:g=s:g()
+
+  " As per `:h E146`, can use any single-byte non-alphanumeric character as
+  " delimiter except for backslash, quote, and vertical bar.
+  if match(a:patterns, '\v^([^"\\|A-Za-z0-9 ]).*\1.*\1$') != 0
     echomsg 'Invalid patterns: ' . a:patterns
     echomsg 'Expected patterns of the form "/foo/bar/".'
     return
   endif
   if getregtype('s') != ''
-    let l:register=getreg('s')
+    let s:register=getreg('s')
+  elseif exists('s:register')
+    unlet s:register
   endif
   normal! qs
-  redir => l:replacements
-  try
-    execute l:currentline . ',' . l:lastline . 's' . a:patterns . 'gce#'
-  catch /^Vim:Interrupt$/
-    return
-  finally
-    normal! q
-    let l:transcript=getreg('s')
-    if exists('l:register')
-      call setreg('s', l:register)
-    endif
-  endtry
-  redir END
+
+  if exists('*execute')
+    let l:replacements=execute('call s:replacements(l:currentline, l:lastline, a:patterns, l:g)', '')
+  else
+    redir => l:replacements
+    call s:replacements(l:currentline, l:lastline, a:patterns, l:g)
+    redir END
+  endif
+
   if len(l:replacements) > 0
     " At least one instance of pattern was found.
-    let l:last=strpart(l:transcript, len(l:transcript) - 1)
+    let l:last=strpart(s:transcript, len(s:transcript) - 1)
     if l:last ==# 'l' || l:last ==# 'q' || l:last ==# '\e'
       " User bailed.
       return
@@ -74,7 +100,7 @@ function! scalpel#substitute(patterns, line1, line2, count) abort
       " Avoid unwanted "Backwards range given, OK to swap (y/n)?" messages.
       if l:currentline > l:firstline
         " Drop c flag.
-        execute l:firstline . ',' . l:currentline . '-&gce'
+        execute l:firstline . ',' . l:currentline . '-&' . l:g . 'ce'
       endif
      return
     endif
@@ -83,6 +109,7 @@ function! scalpel#substitute(patterns, line1, line2, count) abort
   " Loop around to top of range/file and continue.
   " Avoid unwanted "Backwards range given, OK to swap (y/n)?" messages.
   if l:currentline > l:firstline
-    execute l:firstline . ',' . l:currentline . '-&gce'
+    execute l:firstline . ',' . l:currentline . '-&' .l:g . 'ce'
+    execute 'set report=' . s:report
   endif
 endfunction
index c84f6ab552eaa438e8c28fc931e5bc30aa9162fc..920946ba7485fb4a75307d99c81dbcd5e7212d31 100644 (file)
 " Scalpel works similarly in visual mode, except that it scopes itself to the
 " current visual selection rather than operating over the entire file.
 "
+" Screencasts that show Scalpel in action:
+"
+" - https://youtu.be/YwMgnmZNWXA: "Vim screencast #13: Multiple Cursors"
+" - https://youtu.be/7Bx_mLDBtRc: "Vim screencast #14: *Ncgn"
+" - https://youtu.be/iNVyCPPYFzc: "Vim screencast #21: Scalpel update"
+"
 " Note that `:Scalpel` just calls through to an underlying `scalpel#substitute`
 " function that does the real work, ultimately calling Vim's own `:substitute`.
 " As such, be aware that whatever changes you make to the command-line prior to
 " :call pathogen#helptags()
 " ```
 "
+" # FAQ
+"
+" ## Why use Scalpel rather than a built-in alternative?
+"
+" Scalpel is a lightweight plug-in that provides subtle but valuable
+" improvements to the experience you'd get by using Vim's built-in
+" functionality.
+"
+" Compared to writing a |:substitute| command manually:
+"
+" - Scalpel is quickly activated by a mapping.
+" - Scalpel prepopulates the search pattern with the word currently under the
+"   cursor.
+" - Scalpel avoids a jarring jump to the top of the file, instead starting
+"   replacements at the current location.
+"
+" Compared to a mapping such as "*Ncgn":
+"
+" - Scalpel allows you to preview the location at which each change will occur
+"   instead of performing the change blindly.
+"
 " # Website
 "
 " The official Scalpel source code repo is at:
 "
 " # History
 "
-" ## 0.2 (not yet released)
+" ## 1.0 (3 January 2019)
+"
+" - Perform multiple replacements per line even when |'gdefault'| is on.
+"
+" ## 0.5 (28 July 2018)
+"
+" - Fix problem with |Visual| mode operation on older versions of Vim
+"   (GitHub issue #8).
+"
+" ## 0.4 (23 July 2018)
+"
+" - Fix problem with replacement patterns containing the number 1
+"   (GitHub issue #7).
+"
+" ## 0.3 (10 May 2018)
+"
+" - Fix compatibility with older versions of Vim that don't implement
+"   |getcurpos()|.
+"
+" ## 0.2 (13 June 2016)
 "
 " - Support visual mode.
+" - Do not show "N substitutions on N lines" messages.
 "
 " ## 0.1 (29 April 2016)
 "
@@ -227,12 +274,20 @@ execute 'command! -nargs=1 -range '
       \ . s:command
       \ . ' call scalpel#substitute(<q-args>, <line1>, <line2>, <count>)'
 
+function! s:GetCurposCompat()
+  if exists('*getcurpos')
+    return getcurpos()
+  else
+    return getpos('.')
+  endif
+endfunction
+
 " Need to remember last-seen cursor position because `getcurpos()` is not useful
 " in VISUAL modes.
-let s:curpos=getcurpos()
+let s:curpos=s:GetCurposCompat()
 augroup Scalpel
   autocmd!
-  autocmd CursorMoved * let s:curpos=getcurpos()
+  autocmd CursorMoved * let s:curpos=s:GetCurposCompat()
 augroup END
 
 " Local accessor so that we can reference the script-local variable from inside