Class: Pygments::Popen
- Inherits:
-
Object
- Object
- Pygments::Popen
- Defined in:
- lib/pygments/popen.rb
Instance Method Summary collapse
-
#alive? ⇒ Boolean
Check for a @pid variable, and then hit ‘kill -0` with the pid to check if the pid is still in the process table.
-
#css(klass = '', opts = {}) ⇒ Object
Public: Return css for highlighted code.
-
#filters ⇒ Object
Public: Return an array of all available filters.
-
#find_python_binary ⇒ Object
Detect a suitable Python binary to use.
-
#formatters ⇒ Object
Public: Get an array of available Pygments formatters.
-
#highlight(code, opts = {}) ⇒ Object
Public: Highlight code.
-
#lexer_name_for(*args) ⇒ Object
Public: Return the name of a lexer.
-
#lexers ⇒ Object
Public: Get all lexers from a serialized array.
-
#lexers! ⇒ Object
Public: Get back all available lexers from mentos itself.
- #popen4(cmd) ⇒ Object
- #python_binary ⇒ Object
- #python_binary=(python_bin) ⇒ Object
-
#start(pygments_path = File.expand_path('../../../vendor/pygments-main/', __FILE__)) ⇒ Object
Get things started by opening a pipe to mentos (the freshmaker), a Python process that talks to the Pygments library.
-
#stop(reason) ⇒ Object
Stop the child process by issuing a kill -9.
-
#styles ⇒ Object
Public: Return an array of all available styles.
-
#which(command) ⇒ Object
Cross platform which command from stackoverflow.com/a/5471032/284795.
- #windows? ⇒ Boolean
Instance Method Details
#alive? ⇒ Boolean
Check for a @pid variable, and then hit ‘kill -0` with the pid to check if the pid is still in the process table. If this function gives us an ENOENT or ESRCH, we can also safely return false (no process to worry about). Defensively, if EPERM is raised, in a odd/rare dying process situation (e.g., mentos is checking on the pid of a dead process and the pid has already been re-used) we’ll want to raise that as a more informative Mentos exception.
Returns true if the child is alive.
115 116 117 118 119 120 121 122 |
# File 'lib/pygments/popen.rb', line 115 def alive? return true if defined?(@pid) && @pid && Process.kill(0, @pid) false rescue Errno::ENOENT, Errno::ESRCH false rescue Errno::EPERM raise MentosError, "EPERM checking if child process is alive." end |
#css(klass = '', opts = {}) ⇒ Object
Public: Return css for highlighted code
189 190 191 192 193 194 195 |
# File 'lib/pygments/popen.rb', line 189 def css(klass='', opts={}) if klass.is_a?(Hash) opts = klass klass = '' end mentos(:css, ['html', klass], opts) end |
#filters ⇒ Object
Public: Return an array of all available filters
179 180 181 |
# File 'lib/pygments/popen.rb', line 179 def filters mentos(:get_all_filters) end |
#find_python_binary ⇒ Object
Detect a suitable Python binary to use. Or return $PYGMENTS_RB_PYTHON if it’s exists.
60 61 62 63 64 65 66 67 |
# File 'lib/pygments/popen.rb', line 60 def find_python_binary if ENV['PYGMENTS_RB_PYTHON'] return which(ENV['PYGMENTS_RB_PYTHON']) elsif windows? && which('py') return 'py -2' end return which('python2') || which('python') end |
#formatters ⇒ Object
Public: Get an array of available Pygments formatters
Returns an array of formatters.
127 128 129 130 131 132 133 134 135 136 137 138 |
# File 'lib/pygments/popen.rb', line 127 def formatters mentos(:get_all_formatters).inject(Hash.new) do | hash, (name, desc, aliases) | # Remove the long-winded and repetitive 'Formatter' suffix name.sub!(/Formatter$/, '') hash[name] = { :name => name, :description => desc, :aliases => aliases } hash end end |
#highlight(code, opts = {}) ⇒ Object
Public: Highlight code.
Takes a first-position argument of the code to be highlighted, and a second-position hash of various arguments specifying highlighting properties.
Returns the highlighted string or nil when the request to the Python process timed out.
222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 |
# File 'lib/pygments/popen.rb', line 222 def highlight(code, opts={}) # If the caller didn't give us any code, we have nothing to do, # so return right away. return code if code.nil? || code.empty? # Callers pass along options in the hash opts[:options] ||= {} # Default to utf-8 for the output encoding, if not given. opts[:options][:outencoding] ||= 'utf-8' # Get back the string from mentos and force encoding if we can str = mentos(:highlight, nil, opts, code) str.force_encoding(opts[:options][:outencoding]) if str.respond_to?(:force_encoding) str end |
#lexer_name_for(*args) ⇒ Object
Public: Return the name of a lexer.
198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 |
# File 'lib/pygments/popen.rb', line 198 def lexer_name_for(*args) # Pop off the last arg if it's a hash, which becomes our opts if args.last.is_a?(Hash) opts = args.pop else opts = {} end if args.last.is_a?(String) code = args.pop else code = nil end mentos(:lexer_name_for, args, opts, code) end |
#lexers ⇒ Object
Public: Get all lexers from a serialized array. This avoids needing to spawn mentos when it’s not really needed (e.g., one-off jobs, loading the Rails env, etc).
Should be preferred to #lexers!
Returns an array of lexers.
147 148 149 150 151 152 153 154 155 |
# File 'lib/pygments/popen.rb', line 147 def lexers begin lexer_file = File.('../../../lexers', __FILE__) raw = File.open(lexer_file, "rb").read Marshal.load(raw) rescue Errno::ENOENT raise MentosError, "Error loading lexer file. Was it created and vendored?" end end |
#lexers! ⇒ Object
Public: Get back all available lexers from mentos itself
Returns an array of lexers.
160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 |
# File 'lib/pygments/popen.rb', line 160 def lexers! mentos(:get_all_lexers).inject(Hash.new) do |hash, lxr| name = lxr[0] hash[name] = { :name => name, :aliases => lxr[1], :filenames => lxr[2], :mimetypes => lxr[3] } hash["dasm16"] = {:name=>"dasm16", :aliases=>["DASM16"], :filenames=>["*.dasm16", "*.dasm"], :mimetypes=>['text/x-dasm16']} hash["Puppet"] = {:name=>"Puppet", :aliases=>["puppet"], :filenames=>["*.pp"], :mimetypes=>[]} hash["Augeas"] = {:name=>"Augeas", :aliases=>["augeas"], :filenames=>["*.aug"], :mimetypes=>[]} hash["TOML"] = {:name=>"TOML", :aliases=>["toml"], :filenames=>["*.toml"], :mimetypes=>[]} hash["Slash"] = {:name=>"Slash", :aliases=>["slash"], :filenames=>["*.sl"], :mimetypes=>[]} hash end end |
#popen4(cmd) ⇒ Object
17 18 19 20 |
# File 'lib/pygments/popen.rb', line 17 def popen4(cmd) stdin, stdout, stderr, wait_thr = Open3.popen3(cmd) [wait_thr[:pid], stdin, stdout, stderr] end |
#python_binary ⇒ Object
50 51 52 |
# File 'lib/pygments/popen.rb', line 50 def python_binary @python_binary ||= find_python_binary end |
#python_binary=(python_bin) ⇒ Object
54 55 56 |
# File 'lib/pygments/popen.rb', line 54 def python_binary=(python_bin) @python_bin = python_bin end |
#start(pygments_path = File.expand_path('../../../vendor/pygments-main/', __FILE__)) ⇒ Object
Get things started by opening a pipe to mentos (the freshmaker), a Python process that talks to the Pygments library. We’ll talk back and forth across this pipe.
25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 |
# File 'lib/pygments/popen.rb', line 25 def start(pygments_path = File.('../../../vendor/pygments-main/', __FILE__)) begin @log = Logger.new(ENV['MENTOS_LOG'] ||= File::NULL) @log.level = Logger::INFO @log.datetime_format = "%Y-%m-%d %H:%M " rescue @log = Logger.new(File::NULL) end ENV['PYGMENTS_PATH'] = pygments_path # Make sure we kill off the child when we're done at_exit { stop "Exiting" } # A pipe to the mentos python process. #popen4 gives us # the pid and three IO objects to write and read. script = "#{python_binary} #{File.('../mentos.py', __FILE__)}" @pid, @in, @out, @err = popen4(script) @log.info "Starting pid #{@pid} with fd #{@out.to_i} and python #{python_binary}." end |
#stop(reason) ⇒ Object
Stop the child process by issuing a kill -9.
We then call waitpid() with the pid, which waits for that particular child and reaps it.
kill() can set errno to ESRCH if, for some reason, the file is gone; regardless the final outcome of this method will be to set our @pid variable to nil.
Technically, kill() can also fail with EPERM or EINVAL (wherein the signal isn’t sent); but we have permissions, and we’re not doing anything invalid here.
94 95 96 97 98 99 100 101 102 103 104 |
# File 'lib/pygments/popen.rb', line 94 def stop(reason) if @pid begin Process.kill('KILL', @pid) Process.waitpid(@pid) rescue Errno::ESRCH, Errno::ECHILD end end @log.info "Killing pid: #{@pid.to_s}. Reason: #{reason}" @pid = nil end |
#styles ⇒ Object
Public: Return an array of all available styles
184 185 186 |
# File 'lib/pygments/popen.rb', line 184 def styles mentos(:get_all_styles) end |
#which(command) ⇒ Object
Cross platform which command from stackoverflow.com/a/5471032/284795
71 72 73 74 75 76 77 78 79 80 |
# File 'lib/pygments/popen.rb', line 71 def which(command) exts = ENV['PATHEXT'] ? ENV['PATHEXT'].split(';') : [''] ENV['PATH'].split(File::PATH_SEPARATOR).each do |dir| exts.each { |ext| path = File.join(dir, "#{command}#{ext}") return path if File.executable?(path) && !File.directory?(path) } end return nil end |
#windows? ⇒ Boolean
46 47 48 |
# File 'lib/pygments/popen.rb', line 46 def windows? RUBY_PLATFORM =~ /mswin|mingw/ end |