Files
atom/bundles/ruby-on-rails.tmbundle/Support/plugins/footnotes/lib/textmate_footnotes.rb
2012-09-28 17:28:25 -06:00

264 lines
8.6 KiB
Ruby

class String
def line_from_index(index)
lines = self.to_a
running_length = 0
lines.each_with_index do |line, i|
running_length += line.length
if running_length > index
return i
end
end
end
end
class FootnoteFilter
cattr_accessor :no_style, :abs_root, :textmate_prefix
self.no_style = false
self.textmate_prefix = "txmt://open?url=file://"
attr_accessor :body, :abs_root
def self.filter(controller)
return if controller.render_without_footnotes
filter = FootnoteFilter.new(controller)
filter.add_footnotes!
end
def initialize(controller)
@controller = controller
@template = controller.instance_variable_get("@template")
@body = controller.response.body
@extra_html = ""
self.abs_root = File.expand_path(RAILS_ROOT)
end
def add_footnotes!
if performed_render? and first_render?
if ["rhtml", "rxhtml"].include?(template_extension) && (content_type =~ /html/ || content_type.nil?) && !xhr?
# If the user would like to be responsible for the styles, let them opt out of the styling here
insert_styles unless FootnoteFilter.no_style
insert_footnotes
end
end
rescue Exception => e
# Discard footnotes if there are any problems
RAILS_DEFAULT_LOGGER.error "Textmate Footnotes Exception: #{e}\n#{e.backtrace.join("\n")}"
end
# Some controller classes come with the Controller:: module and some don't
# (anyone know why? -- Duane)
def controller_filename
File.join(abs_root, "app", "controllers", "#{@controller.class.to_s.underscore}.rb").
sub('/controllers/controllers/', '/controllers/')
end
def controller_text
@controller_text ||= IO.read(controller_filename)
end
def index_of_method
(controller_text =~ /def\s+#{@controller.action_name}[\s\(]/)
end
def controller_line_number
controller_text.line_from_index(index_of_method)
end
def performed_render?
@controller.instance_variable_get("@performed_render")
end
def first_render?
@template.respond_to?(:first_render) and @template.first_render
end
def xhr?
@controller.request.xhr?
end
def template_path
@template.first_render.sub(/\.(rhtml|rxhtml|rxml|rjs)$/, "")
end
def template_extension
@template.first_render.scan(/\.(rhtml|rxhtml|rxml|rjs)$/).flatten.first ||
@template.pick_template_extension(template_path).to_s
end
def template_file_name
File.expand_path(@template.send(:full_template_path, template_path, template_extension))
end
def layout_file_name
File.expand_path(@template.send(:full_template_path, @controller.active_layout, "rhtml"))
end
def content_type
@controller.response.headers['Content-Type']
end
def stylesheet_files
@stylesheet_files ||= @body.scan(/<link[^>]+href\s*=\s*['"]([^>?'"]+)/im).flatten
end
def javascript_files
@javascript_files ||= @body.scan(/<script[^>]+src\s*=\s*['"]([^>?'"]+)/im).flatten
end
def controller_url
escape(
textmate_prefix +
controller_filename +
(index_of_method ? "&line=#{controller_line_number + 1}&column=3" : "")
)
end
def view_url
escape(textmate_prefix + template_file_name)
end
def layout_url
escape(textmate_prefix + layout_file_name)
end
def insert_styles
insert_text :before, /<\/head>/i, <<-HTML
<!-- TextMate Footnotes Style -->
<style type="text/css">
#tm_footnotes_debug {margin-top: 0.5em; text-align: center; color: #999;}
#tm_footnotes_debug a {text-decoration: none; color: #bbb;}
#tm_footnotes_debug pre {overflow: scroll;}
fieldset.tm_footnotes_debug_info {text-align: left; border: 1px dashed #aaa; padding: 1em; margin: 1em 2em 1em 2em; color: #777;}
</style>
<!-- End TextMate Footnotes Style -->
HTML
end
def insert_footnotes
def tm_footnotes_toggle(id)
"s = document.getElementById('#{id}').style; if(s.display == 'none') { s.display = '' } else { s.display = 'none' }"
end
footnotes_html = <<-HTML
<!-- TextMate Footnotes -->
<div style="clear:both"></div>
<div id="tm_footnotes_debug">
#{textmate_links}
Show:
<a href="#" onclick="#{tm_footnotes_toggle('session_debug_info')};return false">Session</a> |
<a href="#" onclick="#{tm_footnotes_toggle('cookies_debug_info')};return false">Cookies</a> |
<a href="#" onclick="#{tm_footnotes_toggle('params_debug_info')};return false">Params</a> |
<a href="#" onclick="#{tm_footnotes_toggle('log_debug_info')};return false">Log</a> |
<a href="#" onclick="#{tm_footnotes_toggle('general_debug_info')};return false">General Debug</a>
<br/>(<a href="http://blog.inquirylabs.com/2006/09/28/textmate-footnotes-v16-released/"><b>TextMate Footnotes</b></a>)
#{@extra_html}
<fieldset id="session_debug_info" class="tm_footnotes_debug_info" style="display: none">
<legend>Session</legend>
#{escape(@controller.session.instance_variable_get("@data").inspect)}
</fieldset>
<fieldset id="cookies_debug_info" class="tm_footnotes_debug_info" style="display: none">
<legend>Cookies</legend>
<code>#{escape(@controller.send(:cookies).inspect)}</code>
</fieldset>
<fieldset id="params_debug_info" class="tm_footnotes_debug_info" style="display: none">
<legend>Params</legend>
<code>#{escape(@controller.params.inspect)}</code>
</fieldset>
<fieldset id="log_debug_info" class="tm_footnotes_debug_info" style="display: none">
<legend>Log</legend>
<code><pre>#{escape(log_tail)}</pre></code>
</fieldset>
<fieldset id="general_debug_info" class="tm_footnotes_debug_info" style="display: none">
<legend>General (id="tm_debug")</legend>
<div id="tm_debug"></div>
</fieldset>
</div>
<!-- End TextMate Footnotes -->
HTML
if @body =~ %r{<div[^>]+id=['"]tm_footnotes['"][^>]*>}
# Insert inside the "tm_footnotes" div if it exists
insert_text :after, %r{<div[^>]+id=['"]tm_footnotes['"][^>]*>}, footnotes_html
else
# Otherwise, try to insert as the last part of the html body
insert_text :before, /<\/body>/i, footnotes_html
end
end
def textmate_links
html = ""
if ::MAC_OS_X
html = <<-HTML
Edit:
<a href="#{controller_url}">Controller</a> |
<a href="#{view_url}">View</a> |
<a href="#{layout_url}">Layout</a>
HTML
html += asset_file_links("Stylesheets", stylesheet_files) unless stylesheet_files.blank?
html += asset_file_links("Javascripts", javascript_files) unless javascript_files.blank?
html += "<br/>"
end
html
end
def log_tail
ansi = `tail -n 200 #{RAILS_DEFAULT_LOGGER.instance_variable_get("@logdev").filename}`
html = ansi.gsub(/\e\[.+?m/, '')
end
def asset_file_links(link_text, files)
return '' if files.size == 0
links = files.map do |filename|
if filename =~ %r{^/}
full_filename = File.join(abs_root, "public", filename)
%{<a href="#{textmate_prefix}#{full_filename}">#{filename}</a>}
else
%{<a href="#{filename}">#{filename}</a>}
end
end
@extra_html << <<-HTML
<fieldset id="tm_footnotes_#{link_text.underscore.gsub(' ', '_')}" class="tm_footnotes_debug_info" style="display: none">
<legend>#{link_text}</legend>
<ul><li>#{links.join("</li><li>")}</li></ul>
</fieldset>
HTML
# Return the link that will open the 'extra html' div
%{ | <a href="#" onclick="#{tm_footnotes_toggle('tm_footnotes_' + link_text.underscore.gsub(' ', '_') )}; return false">#{link_text}</a>}
end
def indent(indentation, text)
lines = text.to_a
initial_indentation = lines.first.scan(/^(\s+)/).flatten.first
lines.map do |line|
if initial_indentation.nil?
" " * indentation + line
elsif line.index(initial_indentation) == 0
" " * indentation + line[initial_indentation.size..-1]
else
" " * indentation + line
end
end.join
end
# Inserts text in to the body of the document
# +pattern+ is a Regular expression which, when matched, will cause +new_text+
# to be inserted before or after the match. If no match is found, +new_text+ is appended
# to the body instead. +position+ may be either :before or :after
def insert_text(position, pattern, new_text, indentation = 4)
index = case pattern
when Regexp
if match = @body.match(pattern)
match.offset(0)[position == :before ? 0 : 1]
else
@body.size
end
else
pattern
end
@body.insert index, indent(indentation, new_text)
end
def escape(text)
text.gsub("&", "&amp;").gsub("<", "&lt;").gsub(">", "&gt;")
end
end