TODO don’t write unchanged config to disk
[six.git] / plugins-extra / textmate.rb
blobc826b69f2b10d43050f0c3cd81e8db0646722947
2 # TextMate related stuff.
5 require 'async'
6 require 'net/http'
7 require 'set'
9 class Textmate < PluginBase
11   def phrase_to_keywords(phrase)
12     phrase.gsub(/\b(\w+)s\b/, '\1').downcase.split(/\W/).to_set
13   end
15   def parse_toc(text)
16     res = []
17     text.grep(%r{<li>\s*([\d.]+)\s*<a href='(.*?)'>(.*?)</a>}) do |line|
18       res << {
19         :title    => "#{$1} #{$3}",
20         :link     => "http://macromates.com/textmate/manual/#{$2}",
21         :keywords => phrase_to_keywords($3)
22       }
23     end
24     res
25   end
27   def cmd_doc(irc, line)
28     if line.to_s.empty?
29       irc.reply 'USAGE: doc <search string or regex>'
30     else
31       Async.run(irc) do
32         Net::HTTP.start('macromates.com') do |http|
33           re = http.get('/textmate/manual/',  { 'User-Agent' => 'CyBrowser' })
34           if re.code == '200'
36             search_keywords = phrase_to_keywords(line)
37             entries = parse_toc re.body
38             matches = entries.find_all { |m| search_keywords.subset? m[:keywords] }
39             if matches.empty?
40               irc.reply 'No matches found.'
41             else
42               ranked = matches.map { |m| m.merge({ :rank => search_keywords.length.to_f / m[:keywords].length }) }
43               hit = ranked.max { |a, b| a[:rank] <=> b[:rank] }
44               irc.respond "\x02#{hit[:title]}\x0f #{hit[:link]}"
45             end
47           else
48             irc.reply "Documentation site returned an error: #{re.code} #{re.message}"
49           end
50         end
51       end
52     end
53   end
54   help :doc, 'Searches the TextMate manual for the given string or regex.'
56   def parse_faq(text)
57     res = [ ]
58     text.grep(%r{<a name=["'](.*?)["'][^>]*>.*?Keywords:(.*?)</span>}) do |line|
59       res << {
60         :link     => 'http://macromates.com/wiki/Main/FAQ#' + $1,
61         :keywords => phrase_to_keywords($2)
62       }
63     end
64     res
65   end
67   def cmd_faq(irc, line)
68     if line.to_s.empty?
69       irc.reply 'USAGE: faq <search keyword(s)>'
70     else
71       Async.run(irc) do
72         Net::HTTP.start('macromates.com') do |http|
73           re = http.get('/wiki/Main/FAQ',  { 'User-Agent' => 'CyBrowser' })
74           if re.code == '200'
76             search_keywords = phrase_to_keywords(line)
77             entries = parse_faq re.body
78             matches = entries.find_all { |m| search_keywords.subset? m[:keywords] }
79             if matches.empty?
80               irc.reply 'No matches found.'
81             else
82               ranked = matches.collect { |m| m.merge({ :rank => search_keywords.length.to_f / m[:keywords].length }) }
83               hit = ranked.max { |a, b| a[:rank] <=> b[:rank] }
84               irc.respond hit[:link]
85             end
87           end
88         end
89       end
90     end
91   end
92   help :faq, 'Searches the TextMate FAQ for the given keyword(s).'
94   def cmd_calc(irc, line)
95     if line.to_s.empty?
96       irc.reply 'USAGE: calc <expression>. The expression must be in the bc language.'
97     else
98       Async.run(irc) do
99         result = nil
100         IO.popen('bc -l 2>&1', 'r+') do |f|
101           f.puts line
102           result = f.gets.strip
103         end
104         irc.reply result
105       end
106     end
107   end
108   help :calc, "Calculates the expression given and returns the answer. The expression must be in the bc language."