How to enable ruby methods visualizing in vim - ruby

a have googled the question, but found nothing - maybe I do not know how to define the search keywords properly in this case.
I like to use folding in vim when I'm developing Ruby on Rails applications. And my foldcolumn is set to 4. But its visualizing of the start and the end of the ruby method is not so simple and obvious ("-" - "def", "|" - "end"):
-def foo
bar = 1
|end
The question is - is there any plugin for vim, that will show markers (arrows or stmh) near every "def" and "end" like it is done in TextMate (1)?
v def foo
bar = 1
^ end
Also, as I do not have much experience in vim/ruby, maybe there is another, more elegant way to check that all def-end pairs are closed in a particular file? (matchit.vim is not very comfortable for this need)
I hope there is more convenient way to catch lost "ends" than to read "Syntax error" in the console :)

I'm not sure whether it's quite what you need, but have you tried the 'foldcolumn' option? For example, with:
:set foldcolumn=4
You'll get something like this:
- def foo
| bar = 1
| end
- def foo2
| bar = 2
|- if x == 1
|| bar = 3
|| end
| end
See :help 'foldcolumn' for more information. Note that you can click on the - signs to close the folds if your Vim is mouse-enabled.
Edit
If you don't like the fold method, you could use signs (assuming your Vim is signs enabled). Try something like this:
command! RubySigns call RubySigns()
" Optional:
au BufReadPost *.rb call RubySigns()
function! RubySigns()
sign define ruby_end text=^
sign define ruby_def text=v
sign unplace *
g/^\s*\(def\|class\|begin\)\>/exe 'sign place '.line('.').' line='.line('.').' name=ruby_def buffer='.bufnr('%')
g/^\s*end\>/exe 'sign place '.line('.').' line='.line('.').' name=ruby_end buffer='.bufnr('%')
endfunction
It's probably not perfect (I don't know ruby), but it might give you something to get started.

Related

Copy paste from irb excluding prompt at start of line?

When copy/pasting from irb to an .rb file the start of each line should be removed.
A short example, say you copy this from irb:
irb(main):049:1* def thing(x, y)
irb(main):050:1* out = x * y
irb(main):051:1* puts out
irb(main):052:0> end
=> :thing
irb(main):053:0> thing(2, 3)
6
=> nil
it would be handy to paste only this:
def thing(x, y)
out = x * y
puts out
end
=> :thing
thing(2, 3)
6
=> nil
Is there any special copy/paste command to achieve this, either already built into irb, available through some sort of extension, or even operating system shortcut (running macOS)?
What I tried
A regular expression could be used to remove the prompts, but it's not ideal since it assumes the paste will be into a text editor with find and replace with regex.
Although completely unrelated to rails, Google Docs, has a convention of cmd + shift + paste to 'paste without formatting'. I'm after something similar for irb like 'copy without prompt'.
Note: I'm using the macOS terminal (zsh)
You can hold the ⌥ Option key and drag for a rectangular selection:
You can also customize IRB's prompt. Here's an example of the built-in --noprompt option which provides a blank prompt:

Command-line interface helpers?

I am working on a command-line interface to make my code more user-friendly. It currently looks like this:
loop do
print "> "
cmd = gets.chomp
break if cmd == "quit"
run_command(cmd)
puts
end
I would like to extend it a little bit to save typing. For example, I'd like to allow using the "Up Arrow" key to repeat the last command, and the "Tab" key to auto-match command etc.
Is there any tool to ease the job?
You're probably looking for readline, here's an example:
require "readline"
while cmd = Readline.readline("> ", true)
break if cmd == "quit"
run_command(cmd)
puts
end
Tab-completion is a little trickier, though. Have a look at the example on their documentation page.
The library you are looking for is readline

How to implement a /dev/listener device in Ruby?

How can I create a "listener" that would react like a /dev/something file, and all input redirect to it (like: cat /text.txt > /dev/something) will be read by the Ruby program?
In my eyes it looks like
dev = Device.new(/dev/something)
while dev.gets do
...
....
.....
end
What you want is either socket or named pipe. Check which one will be suitable for your application.
In my opinion the easiest way to do this is like this:
ARGF.each_with_index do |line, index|
puts "#{index}: #{line}"
end
And in your console you type type stdin_read1.rb | stdin_read1.rb where the first file can be any text-file.
This is the Windows syntax but I see you have no problem with the console part, I suppose the cat is the Linux equivalent.
You can just use each, I used each_with_index to demonstrate the text is not just piped.

Creating interactive ruby console application

I want make interactive application where user launches it and can do various task by typing commands (some kind of shell)
example:
./myapp.rb
App says Hi
Commands:
help - display help about command
open - open task
do - do action
Start>help open
open <TaskName>
opens specified task
Start>open Something
Something>do SomeAction
Success!
Something> (blinking cursor here)
I searched but couldn't find any ruby gems that I could use specially for console interaction, so I'm about to my make my own...
I looked at Thor, but that's not exactly as I want, maybe I could use it, but not sure...
it could look something like:
class Tasks
attr_reader :opened_task
desc "open <TaskName>", "opens specified task"
def open(params)
end
desc "do <ActionName>", "do specified action"
def do(params)
end
end
tasks = Tasks.new
# theoretical Console class
console = Console.new
console.addCommand("open",tasks.method(:open),"open task")
console.addCommand("do",tasks.method(:do),"do action")
console.start("%s>",[*tasks.opened_task])
so my question is, what gems I could use to make such console class? maybe someone have already made something similar?
I plan using HighLine for input/output, but any other suggestion what could I use?
What you want is a REPL – Read → Evaluate → Print Loop.
IRB, for example, implements a REPL for the Ruby language.
Here's a very simple implementation of your application's REPL:
loop do
Application::Console.prompt.display
input = gets.chomp
command, *params = input.split /\s/
case command
when /\Ahelp\z/i
puts Application::Console.help_text
when /\Aopen\z/i
Application::Task.open params.first
when /\Ado\z/i
Application::Action.perform *params
else puts 'Invalid command'
end
end
\A and \z match the start of the string and the end of the string, respectively.
You could also try ripl. (from the documentation):
Creating and starting a custom shell is as simple as:
require 'ripl'
# Define plugins, load files, etc...
Ripl.start
There is a comprehensive list of plugins for ripl as well as list of console applications using ripl on the projects website.
ok, so I made this library for creating console applications in ruby. Actually it was some while ago, but only just decided to release it. It does support auto-completion if used with HighLine and Readline.
When I wrote it there wasn't any documentation nor tests/specs, but now I made some. Still not much but for beginning should be ok.
So gem cli-console and
code is at GitHub, here's usage example
TTY is a really good gem for easily doing this sort of things. You have plenty of tools which can work alone or with the full toolKit. You can use colors, prompts, execute shell natives, interact with the screen, print tables, progressbars and many other useful elements of command lines whith the easy of a goop api.
Particularly tty-prompt is really useful for asking for user input.
A brief example for the case you proposed:
require 'tty-prompt'
require 'pastel'
prompt = TTY::Prompt.new
loop do
cmd, parms* = prompt.ask('user#machine$ ').split /\s/
case cmd
when "hola"
puts "Hola amigo " parms
when "exit"
break if prompt.yes?('Do you really want to exit?')
end
end
Take a look at cliqr ruby gem. It looks like exactly what you need. Here is the github link with a descriptive readme: https://github.com/anshulverma/cliqr
It can execute the commands directly or within a inbuilt shell.
Here is a test case from its git repo:
it 'can execute a sub action from shell' do
cli = Cliqr.interface do
name 'my-command'
handler do
puts 'base command executed'
end
action :foo do
handler do
puts 'foo executed'
end
action :bar do
handler do
puts 'bar executed'
end
end
end
end
with_input(['', 'my-command', 'foo', 'foo bar', 'foo bar help']) do
result = cli.execute %w(my-command shell), output: :buffer
expect(result[:stdout]).to eq <<-EOS
Starting shell for command "my-command"
my-command > .
base command executed
my-command > my-command.
base command executed
my-command > foo.
foo executed
my-command > foo bar.
bar executed
my-command > foo bar help.
my-command foo bar
USAGE:
my-command foo bar [actions] [options] [arguments]
Available options:
--help, -h : Get helpful information for action "my-command foo bar" along with its usage information.
Available actions:
[ Type "my-command foo bar help [action-name]" to get more information about that action ]
help -- The help action for command "my-command foo bar" which provides details and usage information on how to use the command.
my-command > exit.
shell exited with code 0
EOS
end
end
class MyAPI
def self.__is__(text)
#__is__ = text
end
def self.method_added(method)
#__help__ ||= {}
#__help__[method.to_s] = #__is__
#__is__ = nil
end
def self.help(of)
#__help__[of]
end
__is__ "open file <file>"
def open(file)
#...
end
__is__ "do X"
def do(*params)
#...
end
__is__ "calls help, use help <command>"
def help(*args, &block)
self.class.help(*args, &block)
end
end
MyAPI.new(...).pry
Or you could use pry commands, but that defeats the
turing-completeness. Help might be implemented using commands, as I'm
not sure how well my approach works out. Those methods need to be
coded defensive. I can't remember how to use class variables :-/

Highlight under-cursor code block?

I'm looking for a way to highlight the code block currently under the cursor while in normal mode.
This would work kinda like the set showmatch config options does, but the detection and highlighting would stretch over the entire block.
Any way to achieve this functionality either with config options or a (preferably existing) script ?
Short answer: no.
Long answer: there are ways to achieve this, but you'll have to sacrifice a lot of other fairly essential highlighting features.
There is certainly no way to do it with a simple option. The problem that you may come across is that Vim doesn't allow overlapping regions, so if you have rainbow.vim installed or anything else that makes a region in the area of your block, it's lost. It's also difficult (although I'd welcome any corrections) to have multiple highlighted groups, so one that sets the background colour and another that sets the foreground. This is very limiting as you'll see.
However, if fancy playing around, read on.
I'm making the assumption here that you're using C code in the same coding style as me, but this could easily be changed...
Here's a simple function that should help show you what's involved:
function! HighlightBlock()
" Search backwards and forwards for an opening and closing brace
" at the start of the line (change this according to your coding
" style or how you define a block).
let startmatch = search('^{', 'bcnW')
let endmatch = search('^}', 'cnW')
" Search in the other direction for each to catch the situation
" where we're in between blocks.
let checkstart = search('^{', 'cnW')
let checkend = search('^}', 'bcnW')
" Clear BlockRegion if it exists already (requires Vim 7 probably)
try
syn clear BlockRegion
catch
endtry
" If we're not in a block, give up
if ((startmatch < checkstart) && (endmatch > checkstart))
\ || ((startmatch < checkend) && (endmatch > checkend))
\ || (startmatch == 0)
\ || (endmatch == 0)
return
endif
" Create a new syntax region called "BlockRegion" that starts
" on the specific lines found above (see :help \%l for more
" information).
exec 'syn region BlockRegion'
\ 'start=' . '"\%' . startmatch . 'l"'
\ 'end=' . '"\%' . (endmatch+1) . 'l"'
" Add "contains=ALL" onto the end for a different way of
" highlighting, but it's not much better...
" Define the colours - not an ideal place to do this,
" but good for an example
if &background == 'light'
hi default BlockRegion guibg='#AAAAAA'
else
hi default BlockRegion guibg='#333333'
endif
endfunction
To use the function, source it from somewhere and then create an autocmd to call it whenever something changes, e.g.
au CursorMoved *.c call HighlightBlock()
See the following for some autocommands you may want to consider:
:help CursorHold
:help CursorHoldI
:help CursorMoved
:help CursorMovedI

Resources