summaryrefslogtreecommitdiff
path: root/.vim/plugin/shim.vim
diff options
context:
space:
mode:
Diffstat (limited to '.vim/plugin/shim.vim')
-rw-r--r--.vim/plugin/shim.vim273
1 files changed, 273 insertions, 0 deletions
diff --git a/.vim/plugin/shim.vim b/.vim/plugin/shim.vim
new file mode 100644
index 0000000..18d7a31
--- /dev/null
+++ b/.vim/plugin/shim.vim
@@ -0,0 +1,273 @@
+" Superior Haskell Interaction Mode (SHIM) {{{
+" ==============================================================================
+" Copyright: Lars Kotthoff <lars@larsko.org> 2008,2009
+" Released under the terms of the GPLv3
+" (http://www.gnu.org/copyleft/gpl.html)
+" Name Of File: shim.vim
+" Version: 0.3.4
+" Description: GHCi integration for VIM
+" Requirements: VIM or gVIM with Ruby support, Ruby, and GHCi.
+" Installation: Copy this file into the plugin/ subdirectory of your vim
+" installation -- /etc/vim/ for system-wide installation,
+" .vim/ for user installation. Alternatively, you can manually
+" source the file with ":source shim.vim".
+" Usage: The file exposes 3 commands, GhciFile, GhciRange, and GhciReload. The
+" first takes no arguments and loads the current buffer into GHCi (":l
+" <file>"). If autowrite is set and the buffer has been modified, the
+" buffer is written before it is loaded. The second command takes a range
+" as an argument and pastes these lines into GHCi without any
+" interpretation. The third command kills GHCi, clears the ghci buffer,
+" and restarts GHCi. Note that it doesn't reload any files.
+"
+" You can bind these functions to key combinations for quicker access,
+" e.g. put something like
+"
+" autocmd FileType haskell nmap <C-c><C-l> :GhciRange<CR>
+" autocmd FileType haskell vmap <C-c><C-l> :GhciRange<CR>
+" autocmd FileType haskell nmap <C-c><C-f> :GhciFile<CR>
+" autocmd FileType haskell nmap <C-c><C-r> :GhciReload<CR>
+"
+" into your .vimrc or .gvimrc.
+"
+" The first time you run any of these functions, GHCi will be started
+" automatically in a split window below the current one. You can
+" customize the following options:
+"
+" g:shim_ghciInterp -- the name of the GHCi executable, default "ghci"
+" g:shim_ghciArgs -- extra arguments passed to GHCi, default ""
+" g:shim_ghciPrompt -- a regular expression matching the GHCi prompt
+" g:shim_ghciTimeout -- the timeout for waiting for GHCi to return after
+" sending commands, default 10 seconds
+" g:shim_jumpToGhci -- whether to jump to the GHCi window after executing
+" a GHCi command, default "false"
+" g:shim_quickfix -- whether to integrate SHIM with quickfix (more on
+" that below), default "true"
+" g:shim_defaultWindowSize -- the height of the GHCi window, default 15
+"
+" When quickfix integration is turned on, the output received from GHCi
+" is passed through quickfix to find any errors. This means that you get
+" a list of errors and the cursor is automatically put to the position of
+" the first error. See :help quickfix for more details.
+" If this feature is not working properly for you, you probably need to
+" set the error format for GHC output; you can find Haskell mode for VIM,
+" which includes an error format, at
+" http://www.cs.kent.ac.uk/people/staff/cr3/toolbox/haskell/Vim/.
+" Note that this only applies if a file is loaded into GHCi, as there is
+" no possibility of pinpointing the error position in a file if only a
+" part of the file is passed to GHCi with the GhciRange function.
+"
+" The "ghci" buffer behaves much like an actual GHCi prompt. The cursor
+" is always positioned after the last prompt and <CR> is remapped to
+" GhciRange for the current line, i.e. you can start typing after the
+" prompt and hit <Enter> and the line you typed is sent to GHCi, the
+" output appended to the buffer. The prompt text is stripped off before
+" passing the text on to GHCi.
+"
+" GHCi is quit automatically when you quit VIM or destroy the "ghci"
+" buffer.
+" ==============================================================================
+" }}}
+
+if has("ruby")
+
+if !exists('g:shim_ghciInterp')
+ let g:shim_ghciInterp = "ghci"
+endif
+if !exists('g:shim_ghciPrompt')
+ let g:shim_ghciPrompt = "^[\*A-Z][A-Za-z0-9\. ]+>"
+endif
+if !exists('g:shim_ghciTimeout')
+ let g:shim_ghciTimeout = 10
+endif
+if !exists('g:shim_jumpToGhci')
+ let g:shim_jumpToGhci = "false"
+endif
+if !exists('g:shim_quickfix')
+ let g:shim_quickfix = "true"
+endif
+if !exists('g:shim_defaultWindowSize')
+ let g:shim_defaultWindowSize = 15
+endif
+if !exists('g:shim_ghciArgs')
+ let g:shim_ghciArgs = ""
+endif
+
+command! GhciReload ruby ghci.reloadGhci
+command! GhciFile ruby ghci.ghciSourceFile
+command! -range GhciRange ruby ghci.writeRangeToGhci(<line1>, <line2>)
+
+ruby << EOF
+module VIM
+ class Buffer
+ class << self
+ def getForName(name)
+ (0...self.count).each { |i|
+ return self[i] if self[i].name =~ name
+ }
+ end
+ end
+ end
+
+ class Window
+ class << self
+ def forBufferNumber(bufferNumber)
+ (0...self.count).each { |i|
+ return self[i] if self[i].buffer.number == bufferNumber
+ }
+ return nil
+ end
+
+ def number(window)
+ (0...self.count).each { |i|
+ return i + 1 if self[i] == window
+ }
+ end
+ end
+ end
+end
+EOF
+
+ruby << EOF
+require 'expect'
+
+class Ghci
+ def initialize
+ @ghciInterp = VIM::evaluate("g:shim_ghciInterp")
+ @ghciArgs = VIM::evaluate("g:shim_ghciArgs")
+ @ghciPrompt = Regexp.new(VIM::evaluate("g:shim_ghciPrompt"))
+ @ghciTimeout = VIM::evaluate("g:shim_ghciTimeout").to_i
+ @jumpToGhci = VIM::evaluate("g:shim_jumpToGhci") == "true" ? true : false
+ @quickfix = VIM::evaluate("g:shim_quickfix") == "true" ? true : false
+ @defaultWindowSize = VIM::evaluate("g:shim_defaultWindowSize").to_i
+
+ @buffer = nil
+ @pipe = nil
+ end
+
+ def setupWindow
+ originatorWin = VIM::Window.current
+ VIM::command("below split +e ghci")
+ VIM::command("res " + @defaultWindowSize.to_s)
+ VIM::command("setlocal buftype=nofile noswapfile filetype=haskell")
+ VIM::command("imap <buffer> <CR> <Esc>:GhciRange<CR>a")
+
+ @buffer = VIM::Buffer.getForName(Regexp.new(Regexp.escape(File::Separator) + "ghci$"))
+
+ VIM::command(VIM::Window.number(originatorWin).to_s + "wincmd w") unless @jumpToGhci
+ end
+
+ def initGhciBuffer
+ setupWindow
+ openGhci
+ end
+
+ def openGhci
+ @ghciArgs = VIM::evaluate("g:shim_ghciArgs")
+ # ghci writes some stuff to stderr...
+ @pipe = IO.popen(@ghciInterp + " " + @ghciArgs + " 2>&1", File::RDWR)
+ readFromGhci
+ end
+
+ def closeGhci
+ if(!@pipe.nil?)
+ @pipe.syswrite(":q\n")
+ @pipe.close
+ @pipe = nil
+ end
+ end
+
+ def reloadGhci
+ if(@pipe.nil?)
+ openGhci
+ else
+ closeGhci
+ @buffer.length.times { @buffer.delete(1) }
+ openGhci
+ end
+ end
+
+ def readFromGhci
+ if(!@buffer.nil? && !@pipe.nil?)
+ output = @pipe.expect(@ghciPrompt, @ghciTimeout)
+ break if output.nil?
+ text = output.join("\n").strip + " "
+
+ text.split(/\r?\n/).each { |line|
+ @buffer.append(@buffer.count, line)
+ }
+
+ originatorWin = VIM::Window.current
+ window = VIM::Window.forBufferNumber(@buffer.number)
+
+ if(@quickfix)
+ VIM::command("cex " + text.inspect) unless text =~ /<interactive>/
+ end
+
+ window.cursor = [ @buffer.count, @buffer[@buffer.count].length ] unless window.nil?
+
+ if(@buffer.number != VIM::Window.current.buffer.number && !window.nil?)
+ # switch to the ghci window and refresh it to
+ # make sure the new output is visible, then switch
+ # back unless we wanted to go there anyway
+ VIM::command(VIM::Window.number(window).to_s + "wincmd w")
+ VIM::command("redraw")
+ VIM::command(VIM::Window.number(originatorWin).to_s + "wincmd w") unless @jumpToGhci
+ end
+ else
+ VIM::message("Ghci buffer or pipe isn't open!")
+ end
+ end
+
+ def writeToGhci(text)
+ text.strip!
+ if(text.length > 0)
+ initGhciBuffer if @buffer.nil?
+
+ if(!@buffer.nil? && !@pipe.nil?)
+ # if the input matches the prompt, it was typed
+ # in the interpreter window and we don't need to
+ # echo it; also remove it before passing it on to ghci
+ if(!(text =~ @ghciPrompt).nil?)
+ text.gsub!(@ghciPrompt, "")
+ else
+ text.split(/\r?\n/).each { |line|
+ @buffer.append(@buffer.count, line)
+ }
+ end
+
+ begin
+ @pipe.syswrite(text + "\n")
+ rescue SystemCallError
+ VIM::message("Restarting Ghci, write failed: " + $!)
+ openGhci
+ retry
+ end
+ readFromGhci
+ else
+ VIM::message("Ghci buffer or pipe isn't open!")
+ end
+ end
+ end
+
+ def ghciSourceFile
+ autowrite = VIM::evaluate("&autowrite")
+ modified = VIM::evaluate("&mod")
+ VIM::command("w") if((modified == "1") && (autowrite == "1"))
+ writeToGhci(":l " + VIM::Buffer.current.name)
+ end
+
+ def writeRangeToGhci(line1, line2)
+ text = []
+ (line1..line2).each { |i|
+ text << VIM::Buffer.current[i]
+ }
+ writeToGhci(text.join("\n"))
+ end
+end
+ghci = Ghci.new
+EOF
+
+autocmd BufDelete ghci ruby ghci.closeGhci
+autocmd VimLeavePre * ruby ghci.closeGhci
+
+endif