Class: SL::SearchLink
- Inherits:
-
Object
- Object
- SL::SearchLink
- Defined in:
- lib/searchlink/config.rb,
lib/searchlink/help.rb,
lib/searchlink/parse.rb,
lib/searchlink/search.rb
Overview
Main SearchLink class
Instance Attribute Summary collapse
-
#clipboard ⇒ Object
readonly
Returns the value of attribute clipboard.
-
#originput ⇒ Object
readonly
Returns the value of attribute originput.
-
#output ⇒ Object
readonly
Returns the value of attribute output.
Instance Method Summary collapse
-
#add_plugin_configs(config) ⇒ Object
Add plugin configurations to config object.
-
#config_file ⇒ Object
Values found in ~/.searchlink will override defaults in this script.
-
#confirmed?(url) ⇒ Boolean
Confirm a URL with a popup if requested.
-
#get_plugin_config(cfg) ⇒ String
Get a single plugin configuration.
-
#get_plugin_configs(default_config) ⇒ String
Get plugin configs.
- #help_cli ⇒ Object
- #help_css ⇒ Object
- #help_dialog ⇒ Object
- #help_html ⇒ Object
- #help_js ⇒ Object
- #help_text ⇒ Object
-
#initialize(opt = {}) ⇒ SearchLink
constructor
A new instance of SearchLink.
-
#parse(input) ⇒ Object
Parse the input string and perform searches.
-
#restore_prev_config ⇒ Object
Reset configuration.
-
#write_new_plugin_config ⇒ Object
Add new keys to config if don’t exist.
Constructor Details
#initialize(opt = {}) ⇒ SearchLink
Returns a new instance of SearchLink.
33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 |
# File 'lib/searchlink/config.rb', line 33 def initialize(opt = {}) SL.printout = opt[:echo] || false if File.exist? config_file write_new_plugin_config else default_config = <<~ENDCONFIG # set to true to have an HTML comment included detailing any errors # Can be disabled per search with `--d`, or enabled with `++d`. debug: true # set to true to have an HTML comment included reporting results report: true # use Notification Center to display progress notifications: false # when running on a file, back up original to *.bak backup: true # Time limit for searches. Increase if your searches are regularly # timing out timeout: 15 # change this to set a specific country for search (default US) country_code: US # set to true to force inline Markdown links. Can be disabled # per search with `--i`, or enabled with `++i` inline: false # set to true to include a random string in reference titles. # Avoids conflicts if you're only running on part of a document # or using SearchLink multiple times within a document prefix_random: true # set to true to add titles to links based on the page title # of the search result. Can be disabled per search with `--t`, # or enabled with `++t`. include_titles: false # set to true to attempt to remove SEO elements from page titles, # such that "Regular expressions for beginners | Brett Terpstra.com" # becomes "Regular expressions for beginners" remove_seo: false # confirm existence (200) of generated links. Can be disabled # per search with `--v`, or enabled with `++v`. validate_links: false # If the link text is left empty, always insert the page title # E.g. [](!g Search Text) empty_uses_page_title: false # If confirm is true, then a popup dialog will be displayed # showing the destination of each found link. Hitting cancel # will leave the link unchanged. confirm: false # To create custom abbreviations for Google Site Searches, # add to (or replace) the hash below. # "abbreviation" => "site.url", # This allows you, for example to use [search term](!bt) # as a shortcut to search brettterpstra.com (using a site-specific # Google search). Keys in this list can override existing # search trigger abbreviations. # # If a custom search starts with "http" or "/", it becomes # a simple replacement. Any instance of "$term" is replaced # with a URL-escaped version of your search terms. # Use $term1, $term2, etc. to replace in sequence from # multiple search terms. No instances of "$term" functions # as a simple shortcut. "$term" followed by a "d" lowercases # the replacement. Use "$term1d," "$term2d" to downcase # sequential replacements (affected individually). # Long flags (e.g. --no-validate_links) can be used after # any url in the custom searches. # # Use $terms to slugify all search terms, turning # "Markdown Service Tools" into "markdown-service-tools" custom_site_searches: bt: brettterpstra.com btt: https://brettterpstra.com/topic/$term1d bts: /search/$term --no-validate_links md: www.macdrifter.com ms: macstories.net dd: www.leancrew.com spark: macsparky.com man: http://man.cx/$term dev: developer.apple.com nq: http://nerdquery.com/?media_only=0&query=$term&search=1&category=-1&catid=&type=and&results=50&db=0&prefix=0 gs: http://scholar.google.com/scholar?btnI&hl=en&q=$term&btnG=&as_sdt=80006 ENDCONFIG default_config = get_plugin_configs(default_config) File.open(config_file, "w") do |f| f.puts default_config end end config = YAML.load_file(config_file) # set to true to have an HTML comment inserted showing any errors config["debug"] ||= false # set to true to get a verbose report at the end of multi-line processing config["report"] ||= false config["backup"] = true unless config.key? "backup" config["timeout"] ||= 15 # set to true to force inline links config["inline"] ||= false # set to true to add titles to links based on site title config["include_titles"] ||= false # set to true to remove SEO elements from page titles config["remove_seo"] ||= false # set to true to use page title as link text when empty config["empty_uses_page_title"] ||= false # change this to set a specific country for search (default US) config["country_code"] ||= "US" # set to true to include a random string in ref titles # allows running SearchLink multiple times w/out conflicts config["prefix_random"] = false unless config["prefix_random"] config["social_template"] ||= "%service%/%user%" # append affiliate link info to iTunes urls, empty quotes for none # example: # $itunes_affiliate = "&at=10l4tL&ct=searchlink" config["itunes_affiliate"] ||= "&at=10l4tL&ct=searchlink" # to create Amazon affiliate links, set amazon_partner to your amazon # affiliate tag # amazon_partner: "bretttercom-20" config["amazon_partner"] ||= "" # display a popup dialog confirmation config["confirm"] ||= false # To create custom abbreviations for Google Site Searches, # add to (or replace) the hash below. # "abbreviation" => "site.url", # This allows you, for example to use [search term](!bt) # as a shortcut to search brettterpstra.com. Keys in this # hash can override existing search triggers. config["custom_site_searches"] ||= { "bt" => "brettterpstra.com", "imdb" => "imdb.com" } # confirm existence of links generated from custom search replacements config["validate_links"] ||= false # use notification center to show progress config["notifications"] ||= false SL.line_num = nil SL.match_column = nil SL.match_length = nil SL.config = config add_plugin_configs(config) end |
Instance Attribute Details
#clipboard ⇒ Object (readonly)
Returns the value of attribute clipboard.
8 9 10 |
# File 'lib/searchlink/search.rb', line 8 def clipboard @clipboard end |
#originput ⇒ Object (readonly)
Returns the value of attribute originput.
8 9 10 |
# File 'lib/searchlink/search.rb', line 8 def originput @originput end |
#output ⇒ Object (readonly)
Returns the value of attribute output.
8 9 10 |
# File 'lib/searchlink/search.rb', line 8 def output @output end |
Instance Method Details
#add_plugin_configs(config) ⇒ Object
applies configurations to SL.config
Add plugin configurations to config object
210 211 212 213 214 215 216 217 218 |
# File 'lib/searchlink/config.rb', line 210 def add_plugin_configs(config) SL::Searches.plugins[:search].each_value do |plugin| next unless plugin.key?(:config) && !plugin[:config].nil? && !plugin[:config].empty? plugin[:config].each do |cfg| SL.config[cfg[:key]] = config[cfg[:key]] if config.key?(cfg[:key]) end end end |
#config_file ⇒ Object
Values found in ~/.searchlink will override defaults in this script
22 23 24 25 26 27 28 29 30 31 |
# File 'lib/searchlink/config.rb', line 22 def config_file old_style = File.("~/.searchlink") new_style = File.("~/.config/searchlink/config.yaml") if File.exist?(old_style) && !File.exist?(new_style) old_style else FileUtils.mkdir_p(File.dirname(new_style)) new_style end end |
#confirmed?(url) ⇒ Boolean
Confirm a URL with a popup if requested
6 7 8 9 10 |
# File 'lib/searchlink/parse.rb', line 6 def confirmed?(url) return true if !SL.config["confirm"] || NO_CONFIRM SL::Shortener.confirm?(url) end |
#get_plugin_config(cfg) ⇒ String
Get a single plugin configuration
266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 |
# File 'lib/searchlink/config.rb', line 266 def get_plugin_config(cfg) key = cfg[:key] value = cfg[:value] required = cfg[:required] description = cfg[:description] description = "\n#{description}" if description description = description.word_wrap(60, "# ") if description key = required ? key : "# #{key}" if value.is_a?(Array) array_value = "\n" value.each do |v| array_value += required ? "- #{v.yaml_val}" : "# - #{v.yaml_val}\n" end value = array_value elsif value.is_a?(Hash) hash_value = "\n" value.each do |k, v| hash_value += required ? " #{k}: #{v.yaml_val}" : "# #{k}: #{v.yaml_val}" end value = hash_value else value = value.yaml_val end new_config = "" new_config += description if description new_config + "#{key}: #{value}" end |
#get_plugin_configs(default_config) ⇒ String
Get plugin configs
246 247 248 249 250 251 252 253 254 255 256 257 |
# File 'lib/searchlink/config.rb', line 246 def get_plugin_configs(default_config) SL::Searches.plugins[:search].each_value do |plugin| next unless plugin.key?(:config) && !plugin[:config].nil? && !plugin[:config].empty? plugin[:config].each do |cfg| new_config = get_plugin_config(cfg) default_config += new_config end end default_config end |
#help_cli ⇒ Object
105 106 107 |
# File 'lib/searchlink/help.rb', line 105 def help_cli $stdout.puts help_text end |
#help_css ⇒ Object
5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
# File 'lib/searchlink/help.rb', line 5 def help_css <<~ENDCSS body{-webkit-font-smoothing:antialiased;font-family:"Avenir Next",Avenir,"Helvetica Neue",Helvetica,Arial,Verdana,sans-serif; margin:30px 0 0;padding:0;background:#fff;color:#303030;font-size:16px;line-height:1.5;text-align:center}h1{color:#000} h2{color:#111}p,td,div{color:#111;font-family:"Avenir Next",Avenir,"Helvetica Neue",Helvetica,Arial,Verdana,sans-serif; word-wrap:break-word}a{color:#de5456;text-decoration:none;-webkit-transition:color .2s ease-in-out; -moz-transition:color .2s ease-in-out;-o-transition:color .2s ease-in-out;-ms-transition:color .2s ease-in-out; transition:color .2s ease-in-out}a:hover{color:#3593d9}h1,h2,h3,h4,h5{margin:2.75rem 0 2rem;font-weight:500;line-height:1.15} h1{margin-top:0;font-size:2em}h2{font-size:1.7em}ul,ol,pre,table,blockquote{margin-top:2em;margin-bottom:2em} caption,col,colgroup,table,tbody,td,tfoot,th,thead,tr{border-spacing:0}table{border:1px solid rgba(0,0,0,0.25); border-collapse:collapse;display:table;empty-cells:hide;margin:-1px 0 1.3125em;padding:0;table-layout:fixed;margin:0 auto} caption{display:table-caption;font-weight:700}col{display:table-column}colgroup{display:table-column-group} tbody{display:table-row-group}tfoot{display:table-footer-group}thead{display:table-header-group} td,th{display:table-cell}tr{display:table-row}table th,table td{font-size:1.2em;line-height:1.3;padding:.5em 1em 0} table thead{background:rgba(0,0,0,0.15);border:1px solid rgba(0,0,0,0.15);border-bottom:1px solid rgba(0,0,0,0.2)} table tbody{background:rgba(0,0,0,0.05)}table tfoot{background:rgba(0,0,0,0.15);border:1px solid rgba(0,0,0,0.15); border-top:1px solid rgba(0,0,0,0.2)}p{font-size:1.1429em;line-height:1.72em;margin:1.3125em 0}dt,th{font-weight:700} table tr:nth-child(odd),table th:nth-child(odd),table td:nth-child(odd){background:rgba(255,255,255,0.06)} table tr:nth-child(even),table td:nth-child(even){background:rgba(200,200,200,0.25)} input[type=text] {padding: 5px;border-radius: 5px;border: solid 1px #ccc;font-size: 20px;} ENDCSS end |
#help_dialog ⇒ Object
93 94 95 96 97 98 99 100 101 102 103 |
# File 'lib/searchlink/help.rb', line 93 def help_dialog text = ["<html><head><style>#{help_css}</style><script>#{help_js}</script></head><body>"] text << "<h1>SearchLink Help</h1>" text << "<p>[#{SL.version_check}] [<a href='https://github.com/ttscoff/searchlink/wiki'>Wiki</a>]</p>" text << help_html text << '<p><a href="https://github.com/ttscoff/searchlink/wiki">Visit the wiki</a> for additional information</p>' text << "</body>" html_file = File.("~/.searchlink_searches.html") File.open(html_file, "w") { |f| f.puts text.join("\n") } `open #{html_file}` end |
#help_html ⇒ Object
77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 |
# File 'lib/searchlink/help.rb', line 77 def help_html out = ['<input type="text" id="filter" onkeyup="filterTable()" placeholder="Filter searches">'] out << "<h2>Available Searches</h2>" out << SL::Searches.available_searches_html out << "<h2>Custom Searches</h2>" out << '<table id="custom">' out << "<thead><td>Shortcut</td><td>Search Type</td></thead>" out << "<tbody>" SL.config["custom_site_searches"].each do |label, site| out << "<tr><td><code>!#{label}</code></td><td>#{site}</td></tr>" end out << "</tbody>" out << "</table>" out.join("\n") end |
#help_js ⇒ Object
28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 |
# File 'lib/searchlink/help.rb', line 28 def help_js <<~EOJS function filterTable() { let input, filter, table, tr, i, txtValue; input = document.getElementById("filter"); filter = input.value.toUpperCase(); table = document.getElementById("searches"); table2 = document.getElementById("custom"); tr = table.getElementsByTagName("tr"); for (i = 0; i < tr.length; i++) { txtValue = tr[i].textContent || tr[i].innerText; if (txtValue.toUpperCase().indexOf(filter) > -1) { tr[i].style.display = ""; } else { tr[i].style.display = "none"; } } tr = table2.getElementsByTagName("tr"); for (i = 0; i < tr.length; i++) { txtValue = tr[i].textContent || tr[i].innerText; if (txtValue.toUpperCase().indexOf(filter) > -1) { tr[i].style.display = ""; } else { tr[i].style.display = "none"; } } } EOJS end |
#help_text ⇒ Object
62 63 64 65 66 67 68 69 70 71 72 73 74 75 |
# File 'lib/searchlink/help.rb', line 62 def help_text text = <<~EOHELP -- [Available searches] ------------------- #{SL::Searches.available_searches} EOHELP if SL.config["custom_site_searches"] text += "\n-- [Custom Searches] ----------------------\n" SL.config["custom_site_searches"].sort_by do |l, _s| l end.each { |label, site| text += "!#{label}#{label.spacer} #{site}\n" } end text end |
#parse(input) ⇒ Object
Parse the input string and perform searches
13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 |
# File 'lib/searchlink/parse.rb', line 13 def parse(input) SL.output = [] return false if input.empty? parse_arguments(input, { only_meta: true }) SL.originput = input.dup parse_commands(input) SL.config["inline"] = true if input.scan("](").length == 1 && input.split("\n").length == 1 SL.errors = {} SL.report = [] SL.shortener = :none # Check for new version latest_version = SL.new_version? SL.add_output("<!-- v#{latest_version} available, run SearchLink on the word 'update' to install. -->") if latest_version SL. = [] input.sub!(/\n?<!-- Report:.*?-->\n?/m, "") input.sub!(/\n?<!-- Errors:.*?-->\n?/m, "") @links = input.scan_links @prefix = determine_prefix(input) @highest_marker = 0 input.scan(/^\s{,3}\[(?:#{@prefix})?(\d+)\]: /).each do m = Regexp.last_match @highest_marker = m[1].to_i if m[1].to_i > @highest_marker end @footnote_counter = 0 input.scan(/^\s{,3}\[\^(?:#{@prefix})?fn(\d+)\]: /).each do m = Regexp.last_match @footnote_counter = m[1].to_i if m[1].to_i > @footnote_counter end if SL.config["complete_bare"] # match all URLs not preceded by a ( or : or <, and are followed by a space or newline rx = %r{(?ix-m)(?<!\(|:\s|<)(?: (?:https?://)(?:[\da-z.-]+)\.(?:[a-z.]{2,6}) (?:[/\w\d.\-()_+=?&%]*?(?=[\s\n]|$)) )} # replace with [%](url) input.gsub!(rx) do url_match = Regexp.last_match url_match.pre_match =~ /!\S+ +$/ ? url_match[0] : "[%](#{url_match[0]})" end end # Handle multi-line links in the form of [\ntext\n](url) if input =~ /\[\n(.*?\n)+\]\((.*?)?\)/ input.gsub!(/\[\n(((\s*(?:[-+*]|\d+\.)?\s+)*(!\S+ +)?(.*?))\n)+\]\((!\S+.*?)?\)/) do m = Regexp.last_match lines = m[0].split("\n") lines = lines[1..-2] lines.map do |l| el_rx = /(\s*(?:[-+*]|\d+\.)?\s+)?(!\S+ )?(\w.*?)$/ if l =~ el_rx els = l.match(el_rx) search = if els[2] els[2].strip else m[6] || "!g" end "#{els[1]}[#{els[3].strip}](#{search})" else l end end.join("\n") end end # Handle links in the form of [text](url) or [text](url "title") if input =~ /\[(.*?)\]\((.*?)\)/ parse_brackets(input) else # Assume single line input parse_single_line(input) end end |
#restore_prev_config ⇒ Object
Reset configuration
296 297 298 299 300 301 302 |
# File 'lib/searchlink/config.rb', line 296 def restore_prev_config @prev_config&.each do |k, v| SL.config[k] = v $stderr.print "\r\033[0KReset config: #{k} = #{SL.config[k]}\n" unless SILENT end @prev_config = {} end |
#write_new_plugin_config ⇒ Object
Add new keys to config if don’t exist
221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 |
# File 'lib/searchlink/config.rb', line 221 def write_new_plugin_config default_config = IO.read(config_file) new_config = "" SL::Searches.plugins[:search].each_value do |plugin| next unless plugin.key?(:config) && !plugin[:config].nil? && !plugin[:config].empty? plugin[:config].each do |cfg| next if default_config =~ /^(# *)?#{cfg[:key]}:/ new_config += get_plugin_config(cfg) end end return if new_config.empty? File.open(config_file, "w") { |f| f.puts default_config + new_config } end |