From f04fbf911a49cb2c4342555ad801cfb157afccce Mon Sep 17 00:00:00 2001 From: Sam Saffron Date: Tue, 25 Apr 2017 09:12:39 -0700 Subject: [PATCH] FEATURE: in vim dev you can focus on spec line in autospec instructions in bin/notify_file_change --- bin/notify_file_change | 37 +++++++++++++++++++ lib/autospec/manager.rb | 45 ++++++++++++++++------- lib/socket_server.rb | 80 +++++++++++++++++++++++++++++++++++++++++ lib/stats_socket.rb | 63 ++------------------------------ 4 files changed, 152 insertions(+), 73 deletions(-) create mode 100755 bin/notify_file_change create mode 100644 lib/socket_server.rb diff --git a/bin/notify_file_change b/bin/notify_file_change new file mode 100755 index 0000000000..c4721d4e4c --- /dev/null +++ b/bin/notify_file_change @@ -0,0 +1,37 @@ +#!/bin/bash + + +DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && cd ../tmp && pwd )" + +SOCKET="$DIR"/file_change.sock + +if [[ -e "$SOCKET" ]]; then + echo "$1 $2" | socat - UNIX-CONNECT:$SOCKET >/dev/null 2>/dev/null + if [ $? != 0 ]; then + rm $SOCKET + fi +fi + +# To enable: +# +# 1. Install socat +# 2. Add VIM_AUTOSPEC=1 to your environment +# 3. Add the following to your .vimrc +# +# function s:notify_file_change() +# let git_root = fugitive#extract_git_dir(expand("%:p")) +# let root = substitute(git_root, '.git', '', 'g') +# let notify = root . "bin/notify_file_change" +# if executable(notify) +# if executable('socat') +# execute "!" . notify . ' ' . expand("%:p") . " " . line(".") +# end +# end +# " redraw! +# endfunction + +# autocmd BufWritePost * silent! call s:notify_file_change() + +# What this does? +# +# bin/rake autospec will now automatically try running specs where the actual cursor is located first, then fall back to running spec file diff --git a/lib/autospec/manager.rb b/lib/autospec/manager.rb index 450b215855..4f9089c9dc 100644 --- a/lib/autospec/manager.rb +++ b/lib/autospec/manager.rb @@ -3,6 +3,7 @@ require "thread" require "fileutils" require "autospec/reload_css" require "autospec/base_runner" +require "socket_server" module Autospec; end @@ -173,6 +174,25 @@ class Autospec::Manager path = File.expand_path(File.dirname(__FILE__) + "../../..") + if ENV['VIM_AUTOSPEC'] + STDERR.puts "Using VIM file listener" + + socket_path = (Rails.root + "tmp/file_change.sock").to_s + FileUtils.rm_f(socket_path) + server = SocketServer.new(socket_path) + server.start do |line| + file,line = line.split(' ') + file = file.sub(Rails.root.to_s << "/", "") + # process_change can aquire a mutex and block + # the acceptor + Thread.new do + process_change([[file,line]]) + end + "OK" + end + return + end + # to speed up boot we use a thread ["spec", "lib", "app", "config", "test", "vendor", "plugins"].each do |watch| @@ -204,7 +224,7 @@ class Autospec::Manager specs = [] hit = false - files.each do |file| + files.each do |file, line| @runners.each do |runner| # reloaders runner.reloaders.each do |k| @@ -220,22 +240,21 @@ class Autospec::Manager puts "@@@@@@@@@@@@ #{file} matched a watcher for #{runner}" if @debug hit = true spec = v ? (v.arity == 1 ? v.call(m) : v.call) : file - specs << [file, spec, runner] if File.exists?(spec) || Dir.exists?(spec) + with_line = spec + if spec == file && line + with_line = spec + ":" << line.to_s + end + if File.exists?(spec) || Dir.exists?(spec) + if with_line != spec + specs << [file, spec, runner] + end + specs << [file, with_line, runner] + end end end end - - # special watcher for styles/templates - # now handled via libass integration - # Autospec::ReloadCss::WATCHERS.each do |k, _| - # matches = [] - # matches << file if k.match(file) - # Autospec::ReloadCss.run_on_change(matches) if matches.present? - # end end - queue_specs(specs) if hit - rescue => e fail(e, "failed in watcher") end @@ -260,7 +279,7 @@ class Autospec::Manager puts "@@@@@@@@@@@@ #{@queue}" if @debug specs.each do |file, spec, runner| # make sure there's no other instance of this spec in the queue - @queue.delete_if { |_, s, r| s.strip == spec.strip && r == runner } + @queue.delete_if { |_, s, r| s.strip.start_with?(spec.strip) && r == runner } # deal with focused specs if @queue.first && @queue.first[0] == "focus" focus = @queue.shift diff --git a/lib/socket_server.rb b/lib/socket_server.rb new file mode 100644 index 0000000000..9a2e0df54b --- /dev/null +++ b/lib/socket_server.rb @@ -0,0 +1,80 @@ +require 'socket' + +class SocketServer + + def initialize(socket_path) + @socket_path = socket_path + @server = nil + end + + def start(&blk) + @server = UNIXServer.new(@socket_path) + @accept_thread = new_accept_thread + if blk + @blk = blk + end + end + + def stop + @server.close if @server + @server = nil + @blk = nil + end + + protected + + def new_accept_thread + server = @server + Thread.new do + done = false + while !done + done = !accept_connection(server) + end + end + end + + def accept_connection(server) + socket = nil + begin + socket = server.accept + rescue IOError + # socket was shut down or something catastrophic like that happened + return false + end + + start = Time.now + line = "" + + while Time.now - start < 10 + if IO.select([socket], nil, nil, 10) + begin + line << socket.read_nonblock(1000) + rescue IO::WaitReadable + sleep 0.001 + end + end + break if line.include?("\n") + end + + if line.include?("\n") + socket.write get_response(line.strip) + end + + true + rescue IOError => e + # nothing to do here, case its normal on shutdown + rescue => e + Rails.logger.warn("Failed to handle connection in stats socket #{e}") + ensure + socket&.close rescue nil + end + + def get_response(command) + if @blk + @blk.call(command) + else + raise "Must be implemented by child" + end + end + +end diff --git a/lib/stats_socket.rb b/lib/stats_socket.rb index ad39d12a47..2b81db678f 100644 --- a/lib/stats_socket.rb +++ b/lib/stats_socket.rb @@ -1,70 +1,13 @@ -require 'socket' +require 'socket_server' -class StatsSocket +class StatsSocket < SocketServer def initialize(socket_path) - @socket_path = socket_path - @server = nil - end - - def start - @server = UNIXServer.new(@socket_path) - @accept_thread = new_accept_thread - end - - def stop - @server.close if @server - @server = nil + super(socket_path) end protected - def new_accept_thread - server = @server - Thread.new do - done = false - while !done - done = !accept_connection(server) - end - end - end - - def accept_connection(server) - socket = nil - begin - socket = server.accept - rescue IOError - # socket was shut down or something catastrophic like that happened - return false - end - - start = Time.now - line = "" - - while Time.now - start < 10 - if IO.select([socket], nil, nil, 10) - begin - line << socket.read_nonblock(1000) - rescue IO::WaitReadable - sleep 0.001 - end - end - break if line.include?("\n") - end - - if line.include?("\n") - socket.write get_response(line.strip) - end - - true - rescue IOError => e - # nothing to do here, case its normal on shutdown - rescue => e - Rails.logger.warn("Failed to handle connection in stats socket #{e}") - ensure - socket&.close rescue nil - end - def get_response(command) result = case command